/* NagaBosti — core stylesheet
   Brand rules from BRAND.md, applied LIGHT since the 30 August 2026 conversion:
     Off-white #f5f1e8 is the page ground (the home screen's colour became the
     app's colour); black #0d0d0d is the ink; white cards on the cream ground.
     Warrior red #b3202c accent + buttons — unchanged (6.6:1 under white text,
     5.8:1 against the cream ground, re-checked in the turn-2 design review).
     Gold #d9a441 is a BADGE FILL with ink text, never text on its own: gold as
     text on cream measures 1.9:1 and is illegible in sunlight. PRICES ARE INK.
   The source of the numbers: "Browse design system review/NagaBosti
   Browse.dc.html", TURN 2 ONLY (turn 1 invented a different brand — do not
   read colours out of it).
   UI rule: radically simple, big touch targets, low-literacy friendly. */

/* ---------- The typeface: Mukta, SELF-HOSTED, TWO COMBINED FILES ----------

   WHY SELF-HOSTED, AND NEVER fonts.googleapis.com. Two hard reasons, both read
   from this project's own code rather than from preference:

   1. sw.js's fetch handler returns early for any origin that is not ours
      ("Only handle our own GET pages/assets"). A Google-hosted font would be
      ABSENT OFFLINE and re-fetched on every cold start, on an app whose whole
      point is working on a village signal. That file's own comment already
      says exactly this about the Firebase SDK.

   2. privacy.html -- Maya's final text, and the page the Play Store data-safety
      section points at -- discloses exactly TWO third-party contacts, Firebase
      in Mumbai and Google reCAPTCHA, and states that we run no advertising
      trackers. A fonts.googleapis.com link would send every seller's IP to an
      undisclosed Google endpoint on every page load and make that legal page
      untrue. One CSS line, one false legal document.

   ONE FILE PER WEIGHT, CARRYING BOTH SCRIPTS -- AND WHY THE unicode-range
   SPLIT THAT USED TO BE HERE IS GONE. This block first declared FOUR faces,
   Latin and Devanagari separately, each with its own unicode-range, so that an
   English or Nagamese reader -- both Latin scripts -- never downloaded the
   Devanagari at all. THE SPLIT FILES TURNED OUT NOT TO BE AVAILABLE by any
   route Avi could follow safely: the packaging tool combines Devanagari and
   Latin into one file per weight, and the only source of split files is the
   Google Fonts stylesheet API, which means hand-downloading four files with
   long hashed names that nobody could verify afterwards. A WRONG-BUT-PRESENT
   font file answers 200 and fails silently, which is worse than a slightly
   larger one that is certainly right.

   WHAT THAT COSTS, MEASURED RATHER THAN ESTIMATED: 116 KB + 122 KB = 238 KB,
   and a Latin reader does now fetch Devanagari they will never see. It is paid
   ONCE PER DEVICE, not once per release: the host sends
   "Cache-Control: public, max-age=0, must-revalidate" with an ETag, so a later
   release revalidates these files and gets 304 Not Modified -- a few hundred
   bytes, not 238 KB -- even though sw.js deletes the old cache on activate.
   For scale, the app shell was already 1,210 KB before these were added, and
   each of these files is about the size of the shop banner we have shipped all
   along.

   font-display: swap is REQUIRED, not a preference. On a weak signal the text
   paints IMMEDIATELY in the system stack below and re-paints when the font
   arrives. The worst case is exactly the appearance we had before this change.
   Never blank text, never a wait.

   Licence: SIL Open Font License 1.1. assets/fonts/OFL.txt ships beside the
   font files and must not be removed -- it is the licence condition.

   IF THE FILES ARE ABSENT the app falls back to the system stack and looks
   exactly as it did before. Nothing breaks -- the desktop view was built and
   walked that way, with these files missing. */

@font-face {
  font-family: 'Mukta';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../assets/fonts/mukta-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Mukta';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../assets/fonts/mukta-700.woff2') format('woff2');
}

:root {
  /* THE LIGHT CONVERSION (30 August 2026). --black is the INK, --offwhite is
     the GROUND — the same two brand colours, swapped round, exactly as the
     walked home screen already had them. --panel was the dark card (#1a1a1a)
     and is now the white card; --panel-line was #2e2e2e and is now the cream
     hairline. --grey was #8a857c, which measures 3.3:1 on cream — too pale
     for reading text — so muted text is now the home screen's own #5c574e
     (6.3:1). Every name kept its MEANING (ink/ground/card/border/muted), so
     nothing that reads these variables needed to know the theme changed. */
  --black: #0d0d0d;
  --offwhite: #f5f1e8;
  --red: #b3202c;
  --red-dark: #8c1922;
  --gold: #d9a441;
  --grey: #5c574e;
  --panel: #ffffff;
  --panel-line: #e2dccd;

  /* Input and chip borders are one step darker than card borders (#D9D0BC vs
     #E2DCCD, from the turn-2 sheet) so a control reads as a control. */
  --field-line: #d9d0bc;

  /* Semantic colours, REBUILT FOR LIGHT (turn-2 block 2e). The old sets were
     tuned against black and fail on cream. */
  --err-bg:   #fbeaea;  --err-text:  #8c1922;  --err-line:  #e5b8bb;
  --ok-bg:    #eaf3e6;  --ok-text:   #275d22;  --ok-line:   #b7d3ae;
  --warn-bg:  #fbf3df;  --warn-text: #7a5a14;  --warn-line: #e4cd9a;
  --dis-bg:   #e9e3d5;  --dis-text:  #8a8578;

  /* Link blue: the old #9dc4ff was tuned against black; #1d5fa8 is 4.9:1 on
     white. Also the colour of "way out" options (Anywhere in Phek…). */
  --link: #1d5fa8;

  /* The readable member of the gold family, for the few lines that must stay
     gold-flavoured but are TEXT (gold itself is banned as text — 1.9:1). */
  --gold-text: #7a5a14;

  /* The warm block behind photographs and the no-photo tile (turn-2 2e/2f):
     never a dark hole behind a picture. */
  --photo-bg: #efe9dc;
  /* A SYMBOL TILE'S GROUND on the sub-category wall (v110): a shade paler
     than --offwhite so a buyer reads "photograph coming", not "broken"
     (Rivet's proposal, 13 September 2026; Avi rules on the walk). */
  --tile-wait: #fbf9f4;

  /* Minimum touch target. 56px is comfortably above the 48px accessibility
     floor — this app is used one-handed, outdoors, sometimes by older users. */
  --tap: 56px;
  --pad: 16px;
  /* SQUARE CORNERS — AVI'S RULING, 31 August 2026: rectangular, not
     pill-shaped, throughout. Until that day this token was 14px AND
     eighteen components carried their own hard-coded radii (8/10/12/20/
     22/999px) — the same value written in many places, the drift pattern.
     All eighteen now read THIS token, so the app's corner is one decision
     in one line. DELIBERATELY NOT SQUARED, each for a reason: the Call and
     WhatsApp buttons (Avi's own exceptions — their shape is part of how
     they are told apart, see .contact-btn below); true circles
     (border-radius 50% — the qty +/- buttons, the switch knob, remove ×,
     the emoji circles: shapes, not corner treatments); and the switch
     track (a pill knob needs a pill track to run in). */
  --radius: 0;

  /* Defined ONCE so the typeface is changed in one place, never per screen.
     The fallback IS the stack the app used before Mukta, so a font that has
     not arrived yet renders exactly the old design rather than something
     unintended. */
  --font-ui: 'Mukta', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
             'Helvetica Neue', Arial, sans-serif;

  /* The widest the content column is allowed to grow on a desktop screen.
     Used by main, the header gutter and the footer gutter so all three stay
     aligned. Below 900px nothing reads this. */
  --shell: 1080px;
}

* { box-sizing: border-box; }

/* ---------------------------------------------------------------------------
 * THE `hidden` ATTRIBUTE MUST ALWAYS WIN. Do not remove this rule.
 *
 * The browser's own stylesheet says [hidden] { display: none }, but that is a
 * USER-AGENT rule and ANY author rule beats it. So `.chooser { display: flex }`
 * silently defeated `overlay.hidden = true`, and the place chooser appeared
 * over the browse page — empty, because nothing had filled it, and impossible
 * to close, because close() also just set `hidden` again.
 *
 * One deploy went out like that. This rule makes the whole class of fault
 * impossible: any element with the attribute is hidden, whatever its class
 * says. It is the reason `!important` is justified here and almost nowhere
 * else.
 * --------------------------------------------------------------------------- */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--offwhite);
  color: var(--black);
  /* Mukta, self-hosted, falling back to the system stack it replaced.
     See the @font-face block above for why this is never a Google fetch. */
  font-family: var(--font-ui);
  font-size: 17px;
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}

/* ---------- Network banner (always the very top of the page) ---------- */

#net-banner {
  display: none;
  padding: 12px var(--pad);
  font-weight: 700;
  text-align: center;
  font-size: 15px;
}
#net-banner.show { display: block; }
#net-banner.offline { background: var(--red); color: #fff; }
#net-banner.slow    { background: var(--gold); color: var(--black); }
#net-banner.back    { background: #1f6b34; color: #fff; }

/* ---------- Header ---------- */

/* White bar on the cream ground, as turn-2's 2a and 2d both draw it. The home
   screen keeps its own cream header (the walked design) via .home-light. */
.site-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px var(--pad);
  border-bottom: 1px solid var(--panel-line);
  background: var(--panel);
}
/* The mark and the wordmark are one tap target, not two.
 *
 * ITS BOX MUST NOT GROW DOWNWARDS. It used to carry `padding: 6px 4px` with
 * `margin: -6px -4px`, which pushed its hit area 6px below the header and left
 * only a 6px gap above the "Back" link directly underneath — two links, both at
 * the left edge, horizontally overlapping. A thumb aiming at Back and landing a
 * few pixels high hit the logo instead and was thrown to the home screen.
 *
 * That was invisible to testing because a scripted click always lands dead
 * centre; only a real thumb misses. Horizontal padding is kept for comfort;
 * vertical padding is not, so the gap below stays honest. */
.site-header .brand {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 4px;
  margin: 0 -4px;
  text-decoration: none;
  color: inherit;
  border-radius: var(--radius);
}
/* Gold hover text on a light bar is 1.9:1 — the home screen's red-dark hover
   is the pattern, applied everywhere. */
.site-header .brand:hover .wordmark,
.site-header .brand:focus-visible .wordmark { color: var(--red-dark); }
.site-header .brand:focus-visible { outline: 2px solid var(--red); }

.site-header .logo { height: 38px; width: auto; display: block; }
.site-header .wordmark {
  font-size: 20px;
  font-weight: 800;
  letter-spacing: 0.3px;
  margin: 0;
}
.site-header .wordmark .bosti { color: var(--red); }
.site-header .spacer { flex: 1; }

.lang-toggle {
  background: var(--panel);
  color: var(--black);
  border: 1px solid var(--field-line);
  border-radius: var(--radius);
  min-height: 40px;
  padding: 0 12px;
  font-size: 15px;
  font-weight: 600;
}

/* THE OPEN LIST MUST BE READABLE, AND IT WAS NOT.
 *
 * The closed control is our dark header, so the options inherited off-white
 * text — but the browser draws the OPEN list with its own white background.
 * White on white: "English" was barely visible and "हिन्दी" was invisible
 * entirely. Only the hovered row could be read, because Windows paints that one
 * blue. A language chooser nobody can read is worse than the cycling button it
 * replaced.
 *
 * Both colours are set explicitly, so the list does not depend on the system
 * theme guessing correctly — this must hold on a light Windows desktop and on a
 * dark Android phone alike. Kept native: the fault was colour, not the control.
 */
.lang-toggle option {
  background-color: #ffffff;
  color: #0d0d0d;
  font-weight: 600;
}
/* EVERY SELECT'S POPUP (v120, 20 September 2026 — the register's "dark
   select popups on Android", session 130): the lang-toggle's colours
   generalised to every option on the site — category, sub-category,
   district, sub-division, unit, rent period, the ledger's district filter.
   The open list is drawn by the PHONE, not the page; some Androids honour
   these colours (the lang-toggle proved it), some ignore them (OS chrome,
   like the keyboard). PROVEN ONLY ON AVI'S ANDROID (fault #18); if his
   phone ignores it, the item closes honestly as OS chrome. */
select option {
  background-color: #ffffff;
  color: #0d0d0d;
}
/* Some Androids honour the dark scheme for the popup; give it matching colours
   rather than leaving it to inherit half of each. */
@media (prefers-color-scheme: dark) {
  .lang-toggle option,
  select option {
    background-color: #1a1a1a;
    color: #f5f1e8;
  }
}

/* THE BACK CONTROL — ONE IMPLEMENTATION EVERYWHERE (D116, Avi's ruling,
   1 September 2026): bold capitals inside a thin dark outlined box, the
   arrow before the word, the whole box a real tap target. It was body-weight
   text in white space, and on his own phone it did not read as tappable
   beside the red Search block. NOT filled red or gold — D109: red is THE
   ACTION (Search, Post); Back is the way out, and the outline is what makes
   it read as a button without competing. The capitals come from
   text-transform, so the packs keep their natural words ("Pichete",
   "वापस" — a no-op for Devanagari, which has no capitals). The box GROWS
   with a longer word — max-content and nowrap — it never truncates and
   never wraps. One class serves <a> and <button> alike (font: inherit,
   cursor) so the header link, the legal-page foot buttons and the founding
   flow's step-back are the SAME control.
   THE FILL (v84, Avi's ruling, 1 September 2026): pale grey so the box
   lifts off the cream page — white-on-cream was ignorable. The token is
   --field-line, chosen because its own documented job is "so a control
   reads as a control" and the switch track already uses it as a neutral
   control surface. NOT ink (ink fill + light text is the SELECTED
   signature — the place chips, the done steps; ruled un-shareable),
   NOT --dis-bg (that fill means EXPIRED/disabled — .pill-expired — and a
   live control must never wear it), NOT red or gold (D109). Word and
   outline stay ink — a chosen chip (ink fill, white text) and this box
   (pale fill, ink text) can never be confused. */
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: var(--tap);
  width: max-content;
  padding: 0 18px;
  border: 1px solid var(--black);
  border-radius: var(--radius);
  background: var(--field-line);
  color: var(--black);
  text-decoration: none;
  font: inherit;
  font-weight: 700;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
}
.back-link:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }

/* The header's Back sits in its own bar under the logo; the bar carries the
   page inset, and the top margin is the clear air between this and the logo
   above it, which goes to the home screen — both sit at the left edge and
   overlap horizontally, so this gap is what separates "go back" from "lose
   my place entirely" (D50). */
.back-bar { padding: 0 var(--pad); }
.back-bar .back-link { margin-top: 8px; }

/* ---------- Layout ---------- */

main {
  flex: 1;
  padding: var(--pad);
  max-width: 560px;
  width: 100%;
  margin: 0 auto;
}

h1 { font-size: 26px; font-weight: 800; margin: 8px 0 4px; }
h2 { font-size: 20px; font-weight: 700; margin: 24px 0 8px; }
h3 { font-size: 17px; font-weight: 700; margin: 18px 0 6px; }
p  { margin: 0 0 14px; }

/* Was gold text — banned on cream (1.9:1). Muted, like the home tagline. */
.tagline { color: var(--grey); font-weight: 600; margin: 0 0 24px; }
.muted   { color: var(--grey); font-size: 15px; }
.center  { text-align: center; }

/* ---------- Big home buttons ---------- */

.big-buttons { display: grid; gap: 16px; margin: 8px 0 28px; }

.big-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 132px;
  border-radius: var(--radius);
  text-decoration: none;
  font-size: 24px;
  font-weight: 800;
  letter-spacing: 0.4px;
  border: 2px solid transparent;
  padding: var(--pad);
  text-align: center;
}
.big-btn .icon { font-size: 40px; line-height: 1; }
.big-btn .sub  { font-size: 15px; font-weight: 600; opacity: 0.9; letter-spacing: 0; }

.big-btn.sell { background: var(--red); color: #fff; }
.big-btn.buy  { background: var(--panel); color: var(--black); border-color: var(--field-line); }

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: var(--tap);
  padding: 0 20px;
  border: none;
  border-radius: var(--radius);
  font-size: 18px;
  font-weight: 700;
  font-family: inherit;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
}
.btn-primary   { background: var(--red); color: #fff; }
/* Secondary = white with the control border and ink text (turn-2 sheet):
   on a cream page a transparent button would have no body at all. */
.btn-secondary { background: var(--panel); color: var(--black); border: 2px solid var(--field-line); }
.btn-plain     { background: var(--panel); color: var(--black); border: 2px solid var(--field-line); }

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}

.btn-row { display: grid; gap: 12px; margin-top: 20px; }

/* Spinner shown inside a button while an action is running. */
.btn .spinner {
  width: 18px; height: 18px;
  margin-right: 10px;
  border: 3px solid rgba(255,255,255,0.35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .btn .spinner { animation-duration: 2.4s; }
}

/* ---------- Forms ---------- */

.field { margin-bottom: 20px; }

label {
  display: block;
  font-weight: 700;
  margin-bottom: 6px;
  font-size: 16px;
}
label .req { color: var(--red); }
.hint { display: block; font-weight: 400; color: var(--grey); font-size: 14px; margin-bottom: 6px; }

/* input[type="search"] WAS MISSING FROM THIS LIST until 29 August 2026, and
   browse.html has always used type="search" for the buyer search box. The
   result, measured on the live site before the fix: WHITE background, BLACK
   text, a browser-default 2px INSET border, SQUARE corners and a 13.3px font
   -- in an app whose panels are #1a1a1a, whose radius is 14px and whose text
   is 18px. The one control a buyer uses most was the only one in the app
   wearing the browser default clothes, and at a size this app rejects
   everywhere else because its readers include people who read with
   difficulty. Its 56px height survived only by accident: .search-row is a
   flex row with align-items: stretch, so the button next to it dragged the
   box to the right height. Nothing was holding it there on purpose.
   THIS FIX CHANGES THE PHONE AS WELL AS THE DESKTOP, deliberately. */
input[type="text"],
input[type="tel"],
input[type="email"],
input[type="search"],
select,
textarea {
  width: 100%;
  min-height: var(--tap);
  padding: 12px 14px;
  background: var(--panel);
  color: var(--black);
  border: 2px solid var(--field-line);
  border-radius: var(--radius);
  font-size: 18px;
  font-family: inherit;
}
/* WebKit draws its own rounded search field over ours unless told not to.
   The native CLEAR (x) button is deliberately LEFT ALONE: it is a control
   that works, and this project does not remove working controls. */
input[type="search"] { appearance: none; -webkit-appearance: none; }

/* Focus was gold, which on a white field is 1.9:1 — invisible in sunlight.
   Ink says "you are here" without borrowing red from errors. */
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--black);
}
input.invalid, select.invalid { border-color: var(--red); }

select {
  /* Keep the native dropdown — it is the most familiar control on Android. */
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--black) 50%),
                    linear-gradient(135deg, var(--black) 50%, transparent 50%);
  background-position: calc(100% - 22px) 24px, calc(100% - 16px) 24px;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

.phone-input { display: flex; gap: 8px; align-items: stretch; }
.phone-input .cc {
  display: flex;
  align-items: center;
  padding: 0 14px;
  background: var(--panel);
  border: 2px solid var(--field-line);
  border-radius: var(--radius);
  font-size: 18px;
  font-weight: 700;
  white-space: nowrap;
}
.phone-input input { flex: 1; }

/* OTP boxes */
.otp-input {
  letter-spacing: 12px;
  text-align: center;
  font-size: 26px;
  font-weight: 800;
}

/* Radio group — one question, one answer. Used for the WhatsApp question so a
   contradictory answer is impossible to give. Same tappable-row idea as the
   checkbox: the whole strip is the target, not the little circle. */
.radio-group { display: grid; gap: 10px; }
.radio-group.invalid .check-row { border-color: var(--red); }

.check-row input[type="radio"] {
  width: 26px;
  height: 26px;
  min-width: 26px;
  margin: 1px 0 0;
  accent-color: var(--red);
}
.check-row:has(input:checked) {
  border-color: var(--red);
  background: #fbeaea;   /* the light red tint; was the dark tint #201314 */
}

/* Checkbox row — the whole row is tappable, not just the little square. */
.check-row {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 14px;
  background: var(--panel);
  border: 2px solid var(--field-line);
  border-radius: var(--radius);
  cursor: pointer;
}
.check-row.invalid { border-color: var(--red); }
.check-row input[type="checkbox"] {
  width: 28px;
  height: 28px;
  min-width: 28px;
  margin: 2px 0 0;
  accent-color: var(--red);
}
.check-row span { font-size: 16px; font-weight: 400; }
.check-row a { color: var(--link); }

/* ---------- Messages ---------- */

.msg {
  display: none;
  padding: 14px;
  border-radius: var(--radius);
  margin-bottom: 16px;
  font-weight: 600;
  font-size: 16px;
}
.msg.show { display: block; }
/* The semantic sets, rebuilt for light (turn-2 2e): the old error and success
   colours were tuned against black and fail on cream. */
.msg.error   { background: var(--err-bg); color: var(--err-text); border: 1px solid var(--err-line); }
.msg.success { background: var(--ok-bg); color: var(--ok-text); border: 1px solid var(--ok-line); }
.msg.info    { background: var(--panel); color: var(--black); border: 1px solid var(--panel-line); }
.msg .retry-btn {
  margin-top: 12px;
  background: var(--red);
  color: #fff;
  border: none;
  border-radius: var(--radius);
  min-height: 48px;
  width: 100%;
  font-size: 16px;
  font-weight: 700;
  font-family: inherit;
  cursor: pointer;
}

.field-error {
  display: none;
  color: var(--err-text);
  font-size: 15px;
  font-weight: 600;
  margin-top: 6px;
}
.field-error.show { display: block; }

/* ---------- Panels / placeholders ---------- */

.panel {
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: var(--radius);
  padding: var(--pad);
  margin-bottom: 16px;
}

.coming-soon {
  border: 2px dashed var(--panel-line);
  border-radius: var(--radius);
  padding: 24px var(--pad);
  text-align: center;
  color: var(--grey);
  margin-bottom: 16px;
}
.coming-soon .badge {
  display: inline-block;
  background: var(--gold);
  color: var(--black);
  font-size: 13px;
  font-weight: 800;
  padding: 4px 10px;
  border-radius: var(--radius);
  margin-bottom: 10px;
  letter-spacing: 0.5px;
}

/* Terms / prohibited list */
.legal { font-size: 16px; }
.legal ol { padding-left: 22px; }
.legal li { margin-bottom: 10px; }
.legal .warn {
  background: var(--err-bg);
  border: 1px solid var(--err-line);
  border-radius: var(--radius);
  padding: 14px;
  margin-bottom: 18px;
}
.draft-note {
  background: var(--panel);
  border-left: 4px solid var(--gold);
  padding: 12px 14px;
  margin-bottom: 18px;
  font-size: 15px;
}

/* ---------- Contact buttons (addendum §13) ---------- */

.contact-buttons { display: grid; gap: 12px; }

/* Demo listings must look IDENTICAL to real ones (addendum §11), so there is
   deliberately no styling hook here that distinguishes them. */
.contact-btn {
  flex-direction: column;
  gap: 2px;
  line-height: 1.25;
  /* AVI'S TWO NAMED EXCEPTIONS to the square-corner ruling (31 Aug 2026):
     the Call and WhatsApp buttons keep the shape they have always had —
     it is part of how a buyer tells them apart at a glance. 14px is the
     value --radius held before the ruling. Do not square these. */
  border-radius: 14px;
}
.contact-call     { background: var(--red); color: #fff; }
.contact-whatsapp { background: #1f6b34; color: #fff; }

.contact-btn.revealed { padding-top: 8px; padding-bottom: 8px; }
.contact-btn-label  { font-size: 15px; font-weight: 700; opacity: 0.9; }
.contact-btn-number { font-size: 21px; font-weight: 800; letter-spacing: 0.5px; }

.contact-msg {
  display: none;
  margin: 12px 0 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--grey);
}
.contact-msg.show  { display: block; }
.contact-msg.demo  { color: var(--warn-text); }  /* was gold text — 1.9:1 on cream */
.contact-msg.error { color: var(--err-text); }

/* ---------- Setting switch ---------- */

.setting-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
}
.setting-row .setting-text { flex: 1; }
.setting-row h3 { margin: 0 0 4px; }

.switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  width: 68px;
  height: 38px;
  cursor: pointer;
}
.switch input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.switch .track {
  width: 68px;
  height: 38px;
  border-radius: 19px;
  /* One step darker than the card hairline, so the white knob reads on it. */
  background: var(--field-line);
  transition: background 0.15s ease;
}
.switch .knob {
  position: absolute;
  top: 4px;
  left: 4px;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #ffffff;
  transition: transform 0.15s ease;
}
.switch input:checked ~ .track { background: var(--red); }
.switch input:checked ~ .knob  { transform: translateX(30px); }
.switch input:focus-visible ~ .track { outline: 2px solid var(--red); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .switch .track, .switch .knob { transition: none; }
}

/* ---------- Location picker (addendum §4) ---------- */

.picker { position: relative; }

.picker-results {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
  background: var(--panel);
  /* Was a 2px gold border — near-invisible against white. A floating list
     earns its elevation from a shadow; cards elsewhere still carry none. */
  border: 1px solid var(--field-line);
  box-shadow: 0 6px 16px rgba(13, 13, 13, 0.18);
  border-radius: var(--radius);
  overflow: hidden;
  position: absolute;
  left: 0; right: 0;
  z-index: 20;
  max-height: 320px;
  overflow-y: auto;
}
.picker-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  min-height: 52px;
  padding: 10px 14px;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--panel-line);
  color: var(--black);
  font-family: inherit;
  font-size: 16px;
  text-align: left;
  cursor: pointer;
}
.picker-option:hover, .picker-option:focus { background: var(--offwhite); }
.picker-label { font-weight: 600; }
.picker-kind  { color: var(--grey); font-size: 13px; white-space: nowrap; }
/* The "way out" options were gold text; on light they take the link blue. */
.picker-free .picker-label { color: var(--link); }

.picker-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
/* SELECTED = INK, NOT RED (turn-2, and D109's own logic): red is the action
   colour, and a chosen chip is a state, not an action. Ink background, white
   text — the same signature as the design sheet's selected chips. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--black);
  color: #fff;
  border: 1.5px solid var(--black);
  border-radius: var(--radius);
  padding: 8px 8px 8px 14px;
  font-size: 15px;
  font-weight: 600;
}
.chip-free { background: var(--gold); border-color: var(--gold); color: var(--black); }
.chip-x {
  width: 26px; height: 26px;
  border-radius: 50%;
  border: none;
  background: rgba(255,255,255,0.25);
  color: inherit;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.chip-free .chip-x { background: rgba(13,13,13,0.15); }

/* ---------- Ad form ---------- */

.form-section {
  border-top: 1px solid var(--panel-line);
  padding-top: 20px;
  margin-top: 28px;
}
.form-section:first-of-type { border-top: none; margin-top: 0; }

.inline-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 380px) { .inline-fields { grid-template-columns: 1fr; } }

/* ---------- Photos ---------- */

.photo-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  margin-bottom: 14px;
}
/* TWO WAYS IN (v117, 18 September 2026): the camera button and the gallery
   button SIDE BY SIDE at every width, equal halves, both layouts (D120) -
   Avi's ruling. No stacking breakpoint: most Android phones are 360px wide,
   so the 380px rule .inline-fields uses would have stacked them on nearly
   every phone. A long label wraps to two lines inside its half instead; the
   button's min-height holds the tap target. */
.photo-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 14px; }
.photo-actions .btn { padding-left: 8px; padding-right: 8px; text-align: center; }
.photo-slot {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--panel);
  border: 1px solid var(--panel-line);
}
.photo-slot img { display: block; width: 100%; height: 130px; object-fit: cover; }
.photo-slot .photo-note {
  padding: 6px 8px;
  font-size: 12px;
  color: var(--grey);
}
.photo-remove {
  position: absolute;
  top: 6px; right: 6px;
  width: 36px; height: 36px;
  border-radius: 50%;
  border: none;
  background: rgba(0,0,0,0.7);
  color: #fff;
  font-size: 20px;
  cursor: pointer;
}

/* ---------- Ad cards ---------- */

.ad-card {
  display: flex;
  gap: 12px;
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: var(--radius);
  padding: 12px;
  margin-bottom: 12px;
}
/* Lists use the small stored thumbnail, never the full photo. */
.ad-card img {
  width: 84px; height: 84px;
  object-fit: cover;
  border-radius: var(--radius);
  flex-shrink: 0;
  background: var(--photo-bg);
}
.ad-card .ad-body { flex: 1; min-width: 0; }
.ad-card h3 { margin: 0 0 4px; font-size: 16px; }
/* PRICES ARE INK (turn 2). Gold as text on light is 1.9:1 — the one thing
   from the dark design that does not survive the move. Gold retires to
   badges. */
.ad-card .ad-price { font-weight: 800; color: var(--black); }
.ad-card .ad-meta { font-size: 13px; color: var(--grey); }

/* ---- the posting overlay — D102 (28 August 2026) ----
   Fills the screen while a post runs: a seller on weak 4G must never wonder
   whether anything is happening, and a slow post must not be tappable twice.
   Never a trap: every path through posting ends in the success state or the
   error state with its way-out button — enforced in post-ad.html, asserted
   by the build harness. */
.post-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(0, 0, 0, 0.82);
}
.post-overlay-panel {
  width: 100%;
  max-width: 420px;
  background: var(--panel, #ffffff);
  border: 1px solid var(--panel-line, #e2dccd);
  border-radius: var(--radius, 10px);
  padding: 28px 22px;
  text-align: center;
}
.post-overlay-title { font-size: 22px; font-weight: 800; margin: 14px 0 6px; }
.post-overlay-photo { font-size: 17px; margin: 0 0 12px; }
.post-overlay-wait  { font-size: 14px; color: var(--grey, #5c574e); margin: 14px 0 0; }
/* The advertisements-left line under the success tick (Avi, 31 Aug 2026:
   "make it bigger, bold it — it must be clearly visible" in the moment it
   shows). INK, not red — D109: this is information, not an action. */
.post-overlay-count {
  font-size: 19px;
  font-weight: 800;
  color: var(--black, #0d0d0d);
}
.post-overlay-track {
  height: 14px;
  border-radius: var(--radius);
  background: rgba(13, 13, 13, 0.12);   /* on a white panel now, not a dark one */
  overflow: hidden;
  margin: 6px 0 0;
}
.post-overlay-bar {
  height: 100%;
  width: 0%;
  background: var(--red);
  border-radius: var(--radius);
  transition: width 0.3s ease;
}
.post-overlay-spinner {
  width: 46px;
  height: 46px;
  margin: 0 auto;
  border: 5px solid rgba(13, 13, 13, 0.15);
  border-top-color: var(--red);
  border-radius: 50%;
  animation: nb-spin 0.9s linear infinite;
}
@keyframes nb-spin { to { transform: rotate(360deg); } }
.post-overlay-tick {
  width: 64px;
  height: 64px;
  margin: 0 auto;
  border-radius: 50%;
  background: #2f6b2a;
  color: #fff;
  font-size: 38px;
  line-height: 64px;
  font-weight: 800;
}
.post-overlay .btn { margin-top: 18px; }

/* D100 — the FOR RENT / FOR LEASE tag on a card title. Gold outline rather
   than a filled block, so it reads as information beside the title instead of
   competing with the price, which is the gold that matters. */
.ad-badge {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 7px;
  /* Was a gold outline with gold text — 1.9:1 on a white card. Turn 2's rule:
     gold is a BADGE FILL with ink text (7.2:1 inside the badge). */
  border: 1px solid var(--gold);
  background: var(--gold);
  border-radius: var(--radius);
  color: var(--black);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  vertical-align: 2px;
  white-space: nowrap;
}

/* v123 (Avi's ruling, 21 September 2026) — the SAMPLE label on a
   demonstration listing: the first line of the card body and the first
   line of the ad page. Plain grey text with no fill and no box, so it is
   none of the app's other signals — not a price (ink, 800), not a badge
   (gold fill), not the Verified mark (gold), not a top-level control (pale
   fill + capitals in a box), not the action (red), not EXPIRED (--dis-bg).
   Small capitals with letter-spacing so it reads as a stamp, not a title;
   12px on a 360px card is the size of the meta lines and read without
   zooming. --grey on the panel is about 6.5:1. A real listing never draws
   it (js/browse.js and listing.html add it only when demo === true). */
.ad-sample {
  margin: 0 0 3px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--grey);
}
/* The explaining line under a demonstration listing's title on its page. */
.detail-sample-note {
  margin: 0 0 10px;
  font-size: 15px;
  color: var(--grey);
}
/* v125 — the same label on the DEMONSTRATION SHOP: a block line on its
   tile in the shops list (between the picture and the name), above the
   name in the shop page's identity block, and above "Sold by" on its
   products; the explaining line sits under the identity block. Same grey,
   same size, no fill, no box — a real shop draws none of it. */
.shop-tile .ad-sample { display: block; padding: 0 8px; margin: 0 0 2px; }
.shop-identity-text .ad-sample { margin: 0 0 2px; }
.shop-identity-text .detail-sample-note { margin: 8px 0 0; }

.ad-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}
.ad-action {
  min-height: 44px;
  padding: 0 14px;
  border-radius: var(--radius);
  border: 1px solid var(--field-line);
  background: var(--panel);
  color: var(--black);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}
.ad-action-primary { background: var(--red); border-color: var(--red); color: #fff; }

/* Delete (D95). DELIBERATELY NOT the solid red of the primary action — solid
   red here is "Post again", the thing a seller is meant to reach for, and
   painting the irreversible action the same way would invite the mis-tap. An
   outline instead: findable when looked for, not eye-catching. */
.ad-action-danger {
  background: transparent;
  border-color: var(--red);
  color: var(--red);
}
.ad-action-danger:hover, .ad-action-danger:focus-visible {
  background: var(--red);
  color: #fff;
}

.ad-action:disabled { opacity: 0.5; cursor: not-allowed; }

.pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius);
  font-size: 12px;
  font-weight: 700;
}
.pill-active  { background: #1f6b34; color: #fff; }
.pill-expired { background: var(--dis-bg); color: var(--dis-text); }
.pill-sold    { background: var(--gold); color: var(--black); }
/* EXPIRY WARNINGS (v120): the live pill turns amber at ≤ 5 days and wears
   the error family at ≤ 1 day (never the action red, D109); the line under
   the card says renew. */
.pill-warn    { background: var(--warn-bg); color: var(--warn-text); border: 1px solid var(--warn-line); }
.pill-urgent  { background: var(--err-bg);  color: var(--err-text);  border: 1px solid var(--err-line); }
.expiry-warn  { margin: 6px 0 0; font-weight: 700; color: var(--warn-text); }
.expiry-warn.urgent { color: var(--err-text); }

/* ---------- Category grid (buyer view) ---------- */

.cat-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  margin-bottom: 20px;
}
@media (min-width: 480px) { .cat-grid { grid-template-columns: repeat(3, 1fr); } }

/* Turn-2 block 2c: icon-first tiles, keeping the existing emoji. White card,
   a 3px top edge alternating red / gold (the home screen's signature, thinner),
   and the emoji inside a cream circle. The tile SHELL is identical to the
   photo-first destination (2a), so each square swaps to a photograph later
   with no layout change. */
/* THE PHOTOGRAPH FILLS THE TILE (v95, Avi's ruling from his own phone,
   6 September 2026): the 52px circle was the emoji's, and two subjects in
   a 52px dot could not be read without zooming. Now the tile is a SQUARE
   photograph at the tile's full width, flush under the coloured top edge,
   with the label BELOW it (a label over a photograph would need a scrim
   and hide part of the picture; below, the word stays plain and readable
   for a buyer who reads slowly). One photo fills the square; two are split
   VERTICALLY down the middle, left and right (Avi's sketch). An emoji tile
   keeps the SAME square — the cream ground with the emoji centred, larger —
   so every tile in the wall is the same shape and the contrast between a
   picture and a symbol is deliberate, not a broken tile. The wall grows
   taller (a phone's two columns give ~165px squares); if it reads too
   long on his phone, three columns is the one-line fallback. */
.cat-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 116px;
  padding: 12px 8px;
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-top: 3px solid var(--red);
  border-radius: var(--radius);
  color: var(--black);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
  cursor: pointer;
}
.cat-grid > .cat-tile:nth-child(even) { border-top-color: var(--gold); }
.cat-tile:hover, .cat-tile:focus { border-color: var(--grey); border-top-color: var(--red); }
.cat-grid > .cat-tile:nth-child(even):hover,
.cat-grid > .cat-tile:nth-child(even):focus { border-top-color: var(--gold); }
/* The base .cat-icon stays the 52px cream circle: rent.html's kind-chooser
   draws these tiles too and was not part of the v95 ruling. */
.cat-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--offwhite);
  font-size: 28px;
  line-height: 1;
  overflow: hidden;
}
.cat-icon .cat-photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* BROWSE'S CATEGORY WALL ONLY (#cat-grid): v95 supersedes D113's "52px circle
   by design". The tile is a full-width SQUARE photograph flush under the
   coloured top edge, label below; an emoji tile keeps the same square with
   the emoji centred and larger. A 600×600 tile serves a ~165px phone square
   at 2× (330 device px) and a ~280px tablet square (560) sharp enough;
   object-fit keeps the centre-crop or the vertical pair the build made. */
#cat-grid .cat-tile {
  align-items: stretch;
  justify-content: flex-start;
  gap: 0;
  min-height: 0;
  padding: 0;
}
#cat-grid .cat-icon {
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  border-radius: 0;
  font-size: 48px;
}
#cat-grid .cat-name {
  display: block;
  padding: 8px 8px 10px;
}

/* ---------- THE SUB-CATEGORY TILE WALL (#sub-grid, v110, 13 September 2026) ----------
   Tap a category → every sub-category it has as a tile: the main wall's
   shape (photograph filling a square, label below, the red/gold top edge
   alternating), scaled down. THREE ACROSS on the phone — Rivet's proposal,
   Avi rules on the walk: at 360px that is a ~110px square, the label under
   it holds "Pet animals (domestic only)" in two lines, and livestock's nine
   tiles fill one screen; four across would be an 82px square with labels
   wrapping to four lines. The laptop shows the same wall in the content
   column beside the drawer (five across, six from 1400px). A tile whose
   photograph has not arrived shows the sub-category's emoji on --tile-wait,
   a shade paler than the category wall's cream, so "coming" and
   "photograph" read differently on purpose. NO animation. Same painter
   (js/cat-photos.js paintSub), same one-retry fallback. */
#sub-grid { grid-template-columns: repeat(3, 1fr); gap: 8px; }
#sub-grid .cat-tile {
  align-items: stretch;
  justify-content: flex-start;
  gap: 0;
  min-height: 0;
  padding: 0;
  font-size: 13px;
}
#sub-grid .cat-icon {
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  border-radius: 0;
  font-size: 40px;
}
#sub-grid .cat-icon.is-emoji { background: var(--tile-wait); }
#sub-grid .cat-name { display: block; padding: 6px 4px 8px; }
@media (min-width: 600px) { #sub-grid { grid-template-columns: repeat(4, 1fr); gap: 10px; } }
@media (min-width: 900px) {
  #sub-grid { grid-template-columns: repeat(5, 1fr); gap: 12px; }
  #sub-grid .cat-icon { font-size: 48px; }
  #sub-grid .cat-tile { font-size: 14px; }
}
@media (min-width: 1400px) { #sub-grid { grid-template-columns: repeat(6, 1fr); } }
/* The chip row stands aside while the wall is up — ON THE PHONE ONLY (the
   tiles are the navigation; the header's Back goes up a level). The laptop
   keeps drawer and chips throughout (Avi's ruling 7, D120). */
@media (max-width: 899px) { #sub-chips.on-wall { display: none; } }

/* THE CHIPS ABOVE THE AD (v111, Avi's ruling 6): the same row as browse's,
   drawn by js/cat-nav.js from the ad's own category, sitting between the
   Back bar and the photograph. A little air below it before the gallery. */
.ad-chips { margin: 0 0 12px; }
/* THE LAPTOP AD PAGE GAINS THE DRAWER (v111, D120) — the same 300px column
   browse has; the ad's own two-column layout (#listing) sits inside the
   content column. The gallery column shrinks when the content column is
   narrow (at exactly 900px it has ~570px), so the two rules cannot fight. */
@media (min-width: 900px) {
  main.listing-main {
    display: grid;
    grid-template-columns: 300px minmax(0, 1fr);
    column-gap: 28px;
    align-items: start;
  }
  main.listing-main > :not(#cat-drawer) { grid-column: 2; }
  /* Inside the content column the ad's two columns are EQUAL FRACTIONS, not
     560px + the rest: at 1280px the column is 720px wide, and the fixed
     560px gallery left the title, price and buttons 130px (caught in the
     pane, session 177). The photographs follow the column (max-width, the
     global img rule); the gallery keeps its own aspect. */
  main.listing-main #listing { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  main.listing-main #listing > .gallery img { width: 100%; height: auto; }
}

/* ---------- THE SELLER'S CATEGORY PICKER (v99, 8 September 2026) ----------
   post-ad.html's native category dropdown, drawn as the browse wall's own
   squares (js/cat-picker.js; the ONE painter in js/cat-photos.js). Both
   layouts from ONE rule (D120): auto-fill columns of at least 104px give a
   phone three columns of ~110px squares (six rows for eighteen tiles) and
   the 560px form four or five. The chosen tile wears the SELECTED signature
   - the label on ink with light text, the selected chip's mark - never a
   colour of its own. After a choice the grid collapses to .cat-picker-chosen
   (a 64px square, the drawer's size, beside the name) with the "Change
   category" control, so the sub-category dropdown sits right under it. */
.cat-picker { margin-top: 6px; }
.cat-picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 8px;
}
.cat-pick {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  min-height: 0;
  padding: 0;
  background: var(--panel);
  border: 1px solid var(--field-line);
  border-radius: var(--radius);
  color: var(--black);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
  overflow: hidden;
}
.cat-pick .cat-icon {
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  border-radius: 0;
  font-size: 40px;
}
.cat-pick .cat-name {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2.9em;
  padding: 4px 4px 6px;
}
.cat-pick.is-chosen { border-color: var(--black); box-shadow: inset 0 0 0 2px var(--black); }
.cat-pick.is-chosen .cat-name { background: var(--black); color: var(--panel); }
.cat-pick:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.cat-picker-chosen {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 8px;
  padding: 8px;
  background: var(--panel);
  border: 1px solid var(--field-line);
  border-radius: var(--radius);
}
.cat-picker-chosen .cat-icon { flex: 0 0 auto; width: 64px; height: 64px; border-radius: 0; font-size: 34px; }
.cat-picker-chosen-name { flex: 1 1 auto; min-width: 0; font-size: 16px; font-weight: 700; }
.cat-picker-change {
  min-height: 44px;
  padding: 0 14px;
  background: var(--panel);
  color: var(--black);
  border: 2px solid var(--field-line);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.cat-picker-change:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

/* The Shops tile is an <a>, not a <button>, so it needs the link defaults
   removed to match the eighteen tiles beside it. A gold edge marks it as a
   different KIND of destination — a shop rather than a slice of listings —
   without making it shout. */
.cat-tile-shops {
  text-decoration: none;
  border-color: var(--gold);
}

.chip-quiet { background: var(--panel); color: var(--black); border: 1.5px solid var(--field-line); }
/* THE ALL-CATEGORIES CHIP (v92, 6 Sept 2026): the phone's "← All categories"
   is the same control as the desktop drawer's ALL CATEGORIES head — the way
   back to everything — and D120 gives both layouts one look: bold capitals,
   0.06em tracking, the arrow kept. text-transform, so the packs keep their
   natural words (a no-op for Devanagari — the BACK asymmetry, accepted).
   ONLY this chip: sub-category chips and every named-parent level-up chip
   stay sentence case — that contrast is the point (D116). */
.chip-all {
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
  /* v93 (Avi, 6 Sept 2026): the PRIMARY control wears the family's pale
     fill — --field-line, the drawer head's and the Back box's — so pale
     fill + capitals = top-level control on both layouts; the white chips
     beside it are the things you pick. The border stays the family's
     1.5px field-line, now the same tone as the fill. */
  background: var(--field-line);
}

/* ---------- Search row ---------- */

.search-row {
  display: flex;
  gap: 10px;
  align-items: stretch;
  margin-bottom: 16px;
}
.search-row input { flex: 1; min-width: 0; }
/* The button is visible and thumb-sized. A search box with no button leaves a
   phone user guessing whether anything will happen. */
.search-btn {
  width: auto;
  flex: 0 0 auto;
  padding: 0 20px;
  font-size: 17px;
}

/* Two lines per suggestion: the place, then where it is. */
.picker-option { align-items: flex-start; flex-direction: column; gap: 2px; }
.picker-kind { white-space: normal; }

/* Hints and the "not in our list" block inside the picker dropdown. */
.picker-hint {
  padding: 10px 14px;
  color: var(--grey);
  font-size: 14px;
  border-bottom: 1px solid var(--panel-line);
}
.picker-hint-quiet { border-bottom: none; padding-bottom: 2px; }
.picker-wide .picker-label { color: var(--link); }

/* ---------- Choose from a list (district → area → village) ---------- */

.chooser {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(0, 0, 0, 0.72);
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.chooser-panel {
  width: 100%;
  max-width: 560px;
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: var(--radius) var(--radius) 0 0;
}
@media (min-width: 600px) {
  .chooser { align-items: center; }
  .chooser-panel { border-radius: var(--radius); }
}

.chooser-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px var(--pad);
  border-bottom: 1px solid var(--panel-line);
}
.chooser-head h3 { flex: 1; margin: 0; font-size: 18px; text-align: center; }
.chooser-back, .chooser-close {
  min-height: 44px;
  min-width: 44px;
  padding: 0 10px;
  background: transparent;
  border: none;
  color: var(--black);
  font-family: inherit;
  font-size: 16px;
  cursor: pointer;
}
.chooser-back[hidden] { display: none; }

.chooser-list { overflow-y: auto; -webkit-overflow-scrolling: touch; }

.chooser-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  min-height: var(--tap);
  padding: 12px var(--pad);
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--panel-line);
  color: var(--black);
  font-family: inherit;
  font-size: 17px;
  text-align: left;
  cursor: pointer;
}
.chooser-row:hover, .chooser-row:focus { background: var(--offwhite); }
.chooser-row-label { font-weight: 600; }
.chooser-row-sub { color: var(--grey); font-size: 13px; white-space: nowrap; }

/* "Anywhere in Phek" — the way out at every level. Was gold text. */
.chooser-row-wide .chooser-row-label { color: var(--link); }

.chooser-empty { padding: 20px var(--pad); color: var(--grey); text-align: center; }

/* The delivery offer that turns a dead end into the best line in the demo. */
.delivery-offer { border-color: var(--gold); color: var(--black); }

/* ---------- Filter box ---------- */

/* THE PLACE CONTROL (D115, Avi's fault report, 1 September 2026): the
   summary read as a HEADING — bold text in a faint panel (display:flex
   suppresses even the native disclosure triangle), nothing saying "tap me",
   no sign of what is currently chosen. Now it reads as a control and shows
   its state: the pin, the label, the CURRENT VALUE under it ("All Nagaland"
   when nothing is chosen — the filter-current span, painted by browse.html —
   or the chosen places when some are), and a chevron that says it opens and
   turns when it has. The border is the control-grade line (--field-line,
   one step darker than card borders — that distinction exists for exactly
   this). The MECHANISM is unchanged: the same native <details>/<summary>
   his phone has been opening since C5 — only the look moved (fault #18:
   never swap a mechanism to fix a look). */
.filter-box {
  background: var(--panel);
  border: 2px solid var(--field-line);
  border-radius: var(--radius);
  margin-bottom: 16px;
}
.filter-box > summary {
  min-height: var(--tap);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px var(--pad);
  font-weight: 700;
  cursor: pointer;
}
.filter-q { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.filter-current {
  font-weight: 400;
  color: var(--grey);
  font-size: 15px;
  margin-top: 2px;
}
.filter-chevron {
  font-size: 26px;
  line-height: 1;
  font-weight: 400;
  transition: transform 0.15s;
}
.filter-box[open] .filter-chevron { transform: rotate(90deg); }
.filter-body { padding: 0 var(--pad) var(--pad); }

/* ---------- Listing detail ---------- */

.gallery {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  margin: 0 calc(var(--pad) * -1) 16px;
  padding: 0 var(--pad);
  scroll-snap-type: x mandatory;
}
.gallery img {
  flex: 0 0 auto;
  width: 88%;
  max-width: 420px;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radius);
  background: var(--photo-bg);
  scroll-snap-align: center;
}

/* The price is INK, never gold (turn 2) — see the .ad-price note. */
.detail-price { font-size: 26px; font-weight: 800; color: var(--black); margin: 0 0 4px; }
.detail-neg   { font-size: 16px; font-weight: 600; color: var(--black); }
.detail-age   { color: var(--grey); font-size: 14px; margin: 0 0 18px; }

/* A whole card is tappable on the buyer side. */
a.ad-card { text-decoration: none; color: inherit; }
a.ad-card:hover { border-color: var(--grey); }

/* ---------- "No photo yet" tile (D36) ----------
   Replaces any image that fails to load. Calm and obviously deliberate: it must
   read as "this seller has not added a picture", never as a fault in the app.
   Deliberately quieter than a real photo — muted, no border, no red. */

.no-photo {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: var(--photo-bg);   /* warm, never dark (turn 2) */
  color: var(--grey);
  border-radius: var(--radius);
  flex-shrink: 0;
}
.no-photo-icon { font-size: 24px; line-height: 1; opacity: 0.45; }
.no-photo-text { font-size: 14px; font-weight: 600; }

/* Browse card: exactly the footprint the thumbnail would have had, so a card
   with no picture is the same height as one with a picture. */
.no-photo-thumb { width: 84px; height: 84px; }
.no-photo-thumb .no-photo-icon { font-size: 26px; }

/* Listing page: fills the space the photo would have taken, so the page never
   opens with a large empty block above the title. */
.no-photo-detail {
  width: 88%;
  max-width: 420px;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  scroll-snap-align: center;
}
.no-photo-detail .no-photo-icon { font-size: 44px; }

/* ---------- Enterprise shop page (§12) ---------- */

/* Banner runs to the edges, as a shop front should. */
.shop-banner {
  margin: 0 calc(var(--pad) * -1) 0;
  background: var(--photo-bg);
}
.shop-banner img,
.shop-banner .no-photo-banner {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 1;
  object-fit: cover;
}

/* The identity block (v104, Avi's ruling from the live DZÜDO page,
   9 September 2026): the logo keeps its lift over the banner, but the NAME
   no longer sits beside it, small and half-lost against the banner's edge —
   it is one readable block BELOW the logo: name (larger), tagline, place,
   badge. The same template for every shop, phone and desktop alike (D120);
   every rule here is scoped to the shop page's own classes (fault #21).
   Before v104 this was a flex row with the name at 24px beside a 76px logo. */
.shop-identity {
  display: block;
  /* Lift the logo so it overlaps the banner, the way a shop front reads. */
  margin: -34px 0 16px;
  position: relative;
}
.shop-logo {
  width: 76px;
  height: 76px;
  margin: 0 0 10px;
  border-radius: var(--radius);
  overflow: hidden;
  /* The ring was page-black; it is page-cream now — same trick, the logo
     looks cut into the banner. */
  border: 3px solid var(--offwhite);
  background: var(--panel);
}
.shop-logo img,
.shop-logo .no-photo-logo {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.shop-identity-text { min-width: 0; }
.shop-identity-text h1 {
  margin: 0 0 4px;
  font-size: 30px;
  line-height: 1.15;
  overflow-wrap: anywhere;   /* a long shop name wraps; it never runs off a phone */
}
.shop-tagline { margin: 0 0 4px; color: var(--grey); font-size: 16px; }
.shop-where { margin: 0 0 6px; }
.shop-identity-text .pill { display: inline-block; margin: 4px 0 0; }
@media (min-width: 900px) {
  .shop-identity-text h1 { font-size: 36px; }
  .shop-tagline { font-size: 17px; }
}

.shop-section { margin-bottom: 28px; }
.shop-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  border-bottom: 1px solid var(--panel-line);
  padding-bottom: 8px;
  margin-bottom: 12px;
}
.shop-section-head h3 { margin: 0; font-size: 18px; }
.shop-section-head .muted { font-size: 14px; white-space: nowrap; }

/* The bulk rate is the point of an Enterprise shop, so it is not muted away.
   It was gold text; it is a price statement, so it takes the price treatment:
   ink, bold. */
.shop-bulk { color: var(--black); font-weight: 700; margin: 0; }
/* THE STRUCK MRP (v117, 18 September 2026): a shop's printed MRP beside its
   own price, struck through - the second number of "₹40 ₹49". Drawn only
   when the CSV names an mrp_inr HIGHER than price_inr (Shop.struckMrp), on
   the product tile (.ad-price) and the product page (.detail-price). Grey and
   smaller so the selling price stays the number the eye lands on; the ink
   price itself is untouched. */
/* THE ADMIN MENU (v121, 20 September 2026): one big button per section,
   one column on a phone, two at laptop width; each carries its title, what
   is behind it, and a count where one is cheap. Pale fill + outline: the
   top-level-control family (D116), never the action red. */
.admin-menu { display: grid; gap: 12px; margin-top: 16px; }
@media (min-width: 700px) { .admin-menu { grid-template-columns: 1fr 1fr; } }
.admin-btn { display: block; padding: 14px 16px; background: var(--panel); color: var(--black); border: 2px solid var(--field-line); border-radius: var(--radius); text-decoration: none; min-height: var(--tap); }
.admin-btn:active { border-color: var(--black); }
.admin-btn-title { display: block; font-weight: 800; font-size: 20px; }
.admin-btn-desc { display: block; color: var(--grey); margin-top: 4px; }
.admin-btn-count { display: block; font-weight: 700; margin-top: 6px; }

/* THE COUNTS TABLES on the owner ledger (v119, 19 September 2026): plain
   rows, right-aligned tabular numbers, a bold total under a rule. Owner-only
   page; both layouts (D120). */
.counts-table { width: 100%; border-collapse: collapse; margin: 8px 0 16px; font-size: 15px; }
.counts-table th, .counts-table td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--panel-line); vertical-align: top; }
.counts-table th { font-weight: 700; }
.counts-table th.num, .counts-table td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.counts-table tr.total td { font-weight: 800; border-top: 2px solid var(--black); border-bottom: none; }
.price-mrp { text-decoration: line-through; color: var(--grey); font-weight: 400; font-size: 0.78em; margin-left: 6px; white-space: nowrap; }

/* ---------- Shop section tiles ----------
   The marketplace .cat-tile, with the emoji swapped for a photograph. Taller
   than a category tile because a picture needs room to be recognisable; a
   96px-wide basket is a smudge. */
.shop-tile {
  min-height: 0;
  padding: 0 0 12px;
  gap: 0;
  overflow: hidden;
  text-decoration: none;
}
.shop-tile-pic {
  width: 100%;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  margin-bottom: 10px;
  background: var(--photo-bg);
}
.shop-tile-pic img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The no-photo tile is sized for an 84px thumbnail, so it has to be told to
   fill the picture area instead. Still never blank (D36). */
.shop-tile-pic .no-photo {
  width: 100%;
  height: 100%;
  border-radius: 0;
}
.shop-tile .cat-name { padding: 0 8px; }
.shop-tile-count {
  display: block;
  margin-top: 4px;
  padding: 0 8px;
  font-size: 13px;
  font-weight: 400;
  color: var(--grey);
}

.shop-back-chip { display: inline-block; margin-bottom: 14px; text-decoration: none; }

/* Banner and logo reuse the D36 tile, at their own sizes. */
.no-photo-banner { aspect-ratio: 3 / 1; border-radius: 0; }
.no-photo-logo   { border-radius: 0; }
.no-photo-logo .no-photo-icon { font-size: 26px; }

/* ---------- Product page: quantity ---------- */

.product-section { margin: 0 0 6px; font-size: 14px; }
.product-bulk-offer { margin: 0 0 16px; }

.qty-box { margin-top: 20px; }
.qty-row {
  display: flex;
  align-items: stretch;
  gap: 10px;
  max-width: 240px;
}
/* Big round targets — this is the control a buyer taps repeatedly, and it has
   to work with a thumb on a phone held one-handed. */
.qty-btn {
  width: var(--tap);
  height: var(--tap);
  flex-shrink: 0;
  border-radius: 50%;
  border: 2px solid var(--field-line);
  background: var(--panel);
  color: var(--black);
  font-size: 26px;
  font-family: inherit;
  line-height: 1;
  cursor: pointer;
}
.qty-btn:hover, .qty-btn:focus { border-color: var(--grey); }

.qty-input {
  flex: 1;
  min-width: 0;
  text-align: center;
  font-size: 22px;
  font-weight: 800;
}

.qty-total { margin: 14px 0 0; font-size: 18px; font-weight: 700; }

.qty-bulk-note { margin: 6px 0 0; font-size: 15px; color: var(--grey); }
/* "Bulk rate applied" is good news — the success green, not gold text. */
.qty-bulk-note.is-active { color: var(--ok-text); font-weight: 700; }

/* ---------- Basket (§12 step 3) ---------- */

/* The link that makes the basket reachable at all. Full width so it cannot be
   missed on a phone — a basket nobody finds is the same as no basket. */
/* The "go to your basket" bar. The arrow is pushed to the right edge rather
   than sitting against the words, so the bar reads as a control that leads
   somewhere even before the words are read — which is the whole point for a
   buyer whose language the label is not yet translated into. `gap` keeps the
   two apart when the label is short enough that they would otherwise touch. */
.cart-bar { margin: 0 0 18px; gap: 10px; }
.cart-bar .cart-bar-label { min-width: 0; }
.cart-bar .cart-bar-arrow { flex: 0 0 auto; font-size: 22px; line-height: 1; }

.cart-count { margin: 0 0 14px; }

.cart-line {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 14px;
  margin-bottom: 12px;
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: var(--radius);
}

/* Same 84px footprint as a browse card thumbnail, so the no-photo tile drops
   straight in at the right size (D36). */
.cart-thumb { flex: 0 0 84px; width: 84px; }
.cart-thumb img {
  display: block;
  width: 84px;
  height: 84px;
  object-fit: cover;
  border-radius: var(--radius);
  background: var(--photo-bg);
}

.cart-body { flex: 1; min-width: 0; }
.cart-title-link { text-decoration: none; color: var(--black); }
.cart-title-link h3 { margin: 0 0 4px; font-size: 17px; }
.cart-each { margin: 0; color: var(--grey); font-size: 15px; }

.cart-bulk-pill { margin-top: 6px; }

.cart-line-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 12px;
}

/* Smaller than the product page's stepper: there can be several of these on one
   screen, and the full 56px circles would push the line total onto its own row
   on a narrow phone. Still 44px, above the accessibility floor. */
.qty-row-sm { max-width: 168px; gap: 8px; }
.qty-row-sm .qty-btn { width: 44px; height: 44px; font-size: 22px; }
.qty-row-sm .qty-input { font-size: 19px; padding: 8px; min-height: 44px; }

.cart-line-total {
  margin: 0;
  font-size: 20px;
  font-weight: 800;
  color: var(--black);   /* a price — ink, never gold (turn 2) */
  white-space: nowrap;
}

/* Deliberately a quiet text button, not a red block. Removing is easy to hit by
   accident and there is no undo, so it should not be the loudest thing on the
   row. */
.cart-remove {
  margin-top: 10px;
  padding: 8px 0;
  min-height: 44px;
  background: none;
  border: none;
  color: var(--grey);
  font-family: inherit;
  font-size: 15px;
  text-decoration: underline;
  cursor: pointer;
}
.cart-remove:hover, .cart-remove:focus { color: var(--red); }

.cart-summary { margin-top: 18px; }
.cart-total-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-size: 18px;
}
.cart-total-row strong { font-size: 22px; color: var(--black); }

.cart-free-note { margin: 10px 0 0; font-size: 15px; }
.cart-free-note.is-earned { color: var(--ok-text); font-weight: 700; }

.cart-subtotal-value { font-weight: 700; }

/* "Free" was gold like a rupee figure; rupee figures are ink now, and good
   news takes the success green — the moment ₹150 becomes "Free" still reads. */
.cart-delivery-free { color: var(--ok-text); font-weight: 800; }

.cart-empty { text-align: center; padding: 28px 18px; }
.cart-empty-title { margin: 0 0 6px; font-size: 19px; font-weight: 700; }
.cart-empty .muted { margin: 0; }

/* ---------- Checkout (§12 step 4) ---------- */

/* Collection and delivery are the same size and weight on purpose. Most buyers
   in Nagaland will collect, so collection must not read as the fallback for
   people who could not manage the address form. */
#method-group .check-row strong {
  display: block;
  font-size: 17px;
  font-weight: 700;
  margin-bottom: 2px;
}
.method-detail {
  display: block;
  font-size: 14px;
  color: var(--grey);
  line-height: 1.4;
}
.method-detail.is-good { color: var(--ok-text); }

/* The refusal. Bordered in red because it stops the order, but it is a panel
   rather than a field error: nothing was typed wrongly, and the alternative has
   to sit inside the same box as the problem. */
.no-delivery {
  border: 2px solid var(--red);
  background: var(--err-bg);
  margin-bottom: 20px;
}
.no-delivery-title {
  margin: 0 0 6px;
  font-size: 17px;
  font-weight: 700;
  color: var(--err-text);
}
.no-delivery .muted { margin: 0 0 14px; }
.no-delivery .btn { margin: 0; }

/* The total is the one number an officer will look for, so it is the one number
   that is bigger than everything around it. */
.cart-total-row.is-total {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--panel-line);
  font-weight: 700;
}
.checkout-total-value {
  font-size: 24px;
  font-weight: 800;
  color: var(--black);   /* the total is a price — ink (turn 2) */
}

/* Not ink-bold like a price: "Not available" is not a number. */
.checkout-unavailable { color: var(--err-text); font-weight: 700; }

/* ---------- Payment details (§12 step 5) ---------- */

.pay-banner { margin-bottom: 20px; font-weight: 700; }
.pay-line { font-size: 16px; padding: 4px 0; }
.pay-detail { margin: 0 0 4px; }
.pay-box { margin-top: 18px; }

.pay-upi-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 14px;
  word-break: break-all;
  text-align: right;
}

.pay-qr-heading { margin: 20px 0 10px; font-size: 16px; }

/* White plate behind the drawn code, because a QR is read dark-on-light and a
   sample should look like the real thing it is standing in for. */
.pay-qr {
  display: flex;
  justify-content: center;
  padding: 14px;
  background: #ffffff;
  border-radius: var(--radius);
}
.pay-qr svg { width: 100%; max-width: 220px; height: auto; display: block; }

/* Loud on purpose. This is the sentence that keeps the platform out of the
   payments business, so it is not a footnote. */
.pay-qr-warning {
  margin: 12px 0 0;
  padding: 12px;
  background: var(--err-bg);
  border: 1px solid var(--err-line);
  border-radius: var(--radius);
  color: var(--err-text);
  font-size: 15px;
  font-weight: 700;
}
.pay-qr-note { margin: 10px 0 0; font-size: 14px; }

/* The gate the order cannot get past. Given room of its own rather than tucked
   under the buttons, because it is a statement the buyer makes, not fine print. */
.pay-gate { margin-top: 22px; }

/* ---------- Confirmation (§12 step 6) ---------- */

.done-tick {
  width: 64px;
  height: 64px;
  margin: 10px auto 14px;
  border-radius: 50%;
  background: var(--ok-bg);
  border: 2px solid var(--ok-line);
  color: var(--ok-text);
  font-size: 34px;
  line-height: 60px;
  text-align: center;
}
.done-title { text-align: center; margin: 0 0 6px; }
.done-lead { text-align: center; font-size: 19px; margin: 0 0 4px; }
.done-next {
  text-align: center;
  font-size: 17px;
  margin: 0 0 20px;
  color: var(--ok-text);   /* was gold text */
  font-weight: 700;
}
.done-demo { margin-bottom: 18px; }
.done-demo-line { margin: 0 0 8px; font-size: 15px; }
.done-demo-line:last-child { margin-bottom: 0; }
.done-where { margin: 14px 0 0; }

/* ---------- About page ---------- */

.owner-block { display: flex; flex-direction: column; gap: 14px; }

/* The owner's text is four paragraphs separated by blank lines inside ONE
   string. I18N.apply() sets textContent, and a literal newline in textContent
   draws nothing without this — the four paragraphs would run together into a
   single wall of text. Caught by Warden in a comment in js/strings.en.js before
   it ever shipped.
   pre-line, not pre: it honours the line breaks but still wraps long lines to
   the width of a phone, which `pre` would not. */
.owner-block p { white-space: pre-line; margin: 0; }
.owner-photo {
  width: 140px;
  height: 140px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--photo-bg);
}

/* The credit lines. Monospace-ish sizing and generous spacing because these are
   filenames and usernames, where a missed character matters — and because they
   must stay legible rather than decorative. */
.credit-list {
  margin: 0;
  padding-left: 20px;
  color: var(--black);
  font-size: 15px;
}
.credit-list li { margin-bottom: 10px; line-height: 1.5; word-break: break-word; }

.about-terms { margin-top: 24px; }
.about-terms a { color: var(--link); }

/* ---------- A control locked behind the Verified Badge (D08) ----------
   Dimmed enough to read as deliberately off, not broken — and the reason beside
   it stays at full strength, because the reason is the part that sells the
   badge. */
.setting-row.is-locked .switch { opacity: 0.4; cursor: not-allowed; }
.verified-prompt {
  margin: 0;
  color: var(--gold-text);   /* the readable gold — gold itself fails as text */
  font-size: 15px;
  font-weight: 600;
}

/* Whose basket this is. Every shop keeps its own, so the shop is named rather
   than assumed. */
.cart-for-shop {
  margin: 0 0 4px;
  color: var(--gold-text);
  font-weight: 700;
}

/* ---------- Share a listing ---------- */

.share-box { margin-top: 20px; }

/* Wraps rather than scrolls, so nothing is hidden off the right edge on a
   narrow phone. Two per row at 375px. */
.share-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 12px;
}
.share-btn {
  flex: 1 1 46%;
  min-width: 130px;
  margin: 0;
  padding: 0 12px;
  font-size: 16px;
}
/* Each service in its own colour, because on a small screen people find these
   by colour before they read the word. The text colours were tuned against
   black; on white each takes the darkest readable member of its own family. */
/* share-whatsapp deliberately carries NO brand colour (Maya's trademark
   rule, 2 Sept 2026: the word WhatsApp is never coloured on its own — the
   green FILLED contact button is different, D109's named exception and
   Avi's permanent ruling). It draws as a plain secondary button; the
   resulting asymmetry with the coloured Facebook/Copy pair is the rule's
   accepted cost, Avi's to restyle if it bothers him. */
.share-facebook { border-color: #1877f2; color: var(--link); }
.share-copy     { border-color: var(--gold); color: var(--gold-text); }

/* ---------- Footer ---------- */

.site-footer {
  border-top: 1px solid var(--panel-line);
  padding: 18px var(--pad) 26px;
  text-align: center;
  color: var(--grey);
  font-size: 14px;
}
.site-footer a { color: var(--grey); }

/* ---------- Dev test panel (REMOVED before public launch) ---------- */

#dev-panel {
  position: fixed;
  right: 10px;
  bottom: 10px;
  z-index: 50;
  font-size: 13px;
}
#dev-toggle {
  width: 44px; height: 44px;
  border-radius: 50%;
  background: var(--gold);
  color: var(--black);
  border: none;
  font-size: 20px;
  font-weight: 800;
  cursor: pointer;
  opacity: 0.55;
}
#dev-body {
  display: none;
  background: var(--panel);
  border: 1px solid var(--gold);
  border-radius: var(--radius);
  padding: 12px;
  margin-bottom: 8px;
  width: 210px;
}
#dev-body.show { display: block; }
#dev-body p { margin: 0 0 8px; font-weight: 700; color: var(--gold-text); }
#dev-body button {
  display: block;
  width: 100%;
  margin-bottom: 6px;
  min-height: 40px;
  border-radius: var(--radius);
  border: 1px solid var(--field-line);
  background: var(--panel);
  color: var(--black);
  font-family: inherit;
  font-size: 14px;
  cursor: pointer;
}
#dev-body button.active { background: var(--red); color: #fff; border-color: var(--red); }

/* ---------- Small screens ---------- */

@media (max-width: 360px) {
  html, body { font-size: 16px; }
  .big-btn { min-height: 112px; font-size: 21px; }
  h1 { font-size: 23px; }
}

/* ===========================================================================
 * THE HOME SCREEN — four tiles, light. Added 24 August 2026.
 *
 * Asked for by KVK, the ADC's office and NRLM staff. Every rule below is
 * scoped to `.home-light` / `.home-page`, so NOTHING on any other screen can
 * be changed by this block. Delete the class from index.html and the page
 * returns to the app's dark theme with no other edit.
 *
 * WHY LIGHT, when BRAND.md says black: a phone held at a market stall in
 * daylight turns a dark screen into a mirror, and this is the screen a
 * first-time user meets outdoors. The palette is still BRAND.md's — the
 * background IS the brand off-white and the text IS the brand black, swapped
 * round. No new colour was invented.
 * =========================================================================== */

.home-light {
  /* Only these four are new, and each is a darker companion to a brand colour
     that a light background needs: pure white for the tiles themselves, a
     hairline, a readable muted grey (--grey #8a857c is too pale on cream), and
     a soft shadow. */
  --home-bg:     #f5f1e8;   /* brand off-white, now the background */
  --home-ink:    #0d0d0d;   /* brand black, now the text */
  --home-card:   #ffffff;
  --home-line:   #e2dccd;
  --home-muted:  #5c574e;
  --home-shadow: 0 2px 6px rgba(13, 13, 13, 0.10);
}

.home-light,
.home-light body {
  background: var(--home-bg);
  color: var(--home-ink);
}

/* ---------- the banner ----------
   Same header App.init() builds on every page, and the same working language
   list — only bigger, and light. The mark must read as the app's identity, not
   as a small corner logo: it is the first thing a government audience sees. */
.home-light .site-header {
  background: var(--home-bg);
  border-bottom: 1px solid var(--home-line);
  padding: 18px var(--pad) 14px;
  gap: 14px;
}
.home-light .site-header .logo { height: 60px; }
.home-light .site-header .wordmark {
  font-size: 30px;
  letter-spacing: 0.2px;
  color: var(--home-ink);
}
.home-light .site-header .wordmark .bosti { color: var(--red); }
.home-light .site-header .brand:hover .wordmark,
.home-light .site-header .brand:focus-visible .wordmark { color: var(--red-dark); }
.home-light .site-header .brand:focus-visible { outline: 2px solid var(--red); }

/* The language list sits beside the logo, and must be legible on cream. */
.home-light .lang-toggle {
  color: var(--home-ink);
  background: var(--home-card);
  border: 1px solid var(--home-line);
  min-height: 44px;
}

.home-light .home-tagline {
  margin: 14px 0 18px;
  text-align: center;
  font-size: 17px;
  font-weight: 600;
  color: var(--home-muted);
}

/* ---------- the four tiles ----------
   Two rows of two with the explanation between them, not one 2x2 block, so the
   explanation lands in the middle of the screen where it is read on the way
   past. Reading order is BUY, SELL, then HIRE, RENT. */
.home-page .tile-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
/* The explanation used to sit between the rows and supplied this gap. It was
   moved below all four tiles on 24 August, so the second row needs its own. */
.home-page .tile-row + .tile-row { margin-top: 14px; }

.home-page .tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* Tapped by working hands, outdoors, sometimes wet: the whole tile is the
     target, and it is far above the 56px floor in --tap. */
  min-height: 148px;
  padding: 18px 12px;
  gap: 4px;
  background: var(--home-card);
  color: var(--home-ink);
  border: 1px solid var(--home-line);
  border-top: 5px solid var(--red);
  border-radius: var(--radius);
  box-shadow: var(--home-shadow);
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.home-page .tile:active { transform: translateY(1px); }
.home-page .tile:focus-visible { outline: 3px solid var(--red); outline-offset: 2px; }

/* BUY and SELL are the two main actions and carry the red edge. HIRE and RENT
   are the secondary pair and carry gold — BRAND.md keeps gold for small
   accents, and a 5px edge is exactly that. */
.home-page .tile-hire,
.home-page .tile-rent { border-top-color: var(--gold); }

.home-page .tile-icon { font-size: 40px; line-height: 1; }
.home-page .tile-word {
  font-size: 26px;
  font-weight: 800;
  letter-spacing: 0.5px;
}
.home-page .tile-sub {
  font-size: 13.5px;
  line-height: 1.35;
  color: var(--home-muted);
}

/* ---------- what NagaBosti is ---------- */
.home-page .home-explain {
  margin: 18px 0;
  padding: 16px;
  background: var(--home-card);
  border: 1px solid var(--home-line);
  border-left: 5px solid var(--gold);
  border-radius: var(--radius);
  font-size: 15.5px;
  line-height: 1.5;
  color: var(--home-ink);
}

/* ---------- tutorials ---------- */
.home-page .tutorial-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin: 22px 0 0;
  min-height: var(--tap);
  padding: 0 18px;
  background: var(--red);
  color: #ffffff;
  border-radius: var(--radius);
  font-size: 18px;
  font-weight: 700;
  text-decoration: none;
}
.home-page .tutorial-btn:active { background: var(--red-dark); }
.home-page .tutorial-btn:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.home-page .tutorial-note {
  margin: 10px 0 0;
  text-align: center;
  font-size: 14.5px;
  color: var(--home-muted);
}

/* ---------- the live count ----------
   The only proof a first-time visitor gets that the app is not empty, so it is
   not a footnote. It stays empty until the real number is known. */
.home-page .home-count {
  margin: 22px 0 6px;
  text-align: center;
  font-size: 16px;
  font-weight: 700;
  color: var(--home-ink);
}
.home-page .home-count:empty { margin: 0; }

/* ---------- home: the shop invitation and the social line (v99, 8 September 2026) ----------
   .home-strip is browse's own .banner-strip (same keys, same destination),
   placed under the explain line; the control is an <a> here, so it needs
   the link defaults removed. .home-social is Maya's text-only rule for the
   Meta names: nouns in ordinary text, the neighbours' size, no logo, no
   brand colour - the links are ink, underlined, nothing more. */
.home-page .home-strip { margin: 14px 0 0; }
.home-page .home-strip .strip-btn { display: inline-flex; align-items: center; text-decoration: none; }
.home-page .home-social {
  margin: 18px 0 4px;
  text-align: center;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--home-muted);
}
.home-page .home-social a { color: var(--home-ink); font-weight: 600; text-decoration: underline; }
.home-page .home-social a:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

.home-light .site-footer {
  border-top: 1px solid var(--home-line);
  color: var(--home-muted);
}
.home-light .site-footer a { color: var(--home-muted); }

/* ---------- the owner's test gear does not belong on this screen ----------
 * `#dev-panel` is the owner test panel — a gold gear fixed to the bottom-right
 * corner, drawn only on the fake build and already marked for removal before
 * public launch. On the home screen it landed on top of the RENT tile's words,
 * and to anyone who does not know what it is it reads as a fault. This is the
 * screen going to the KVK head, so it is hidden HERE ONLY.
 *
 * It is not removed and not disabled: it is still on browse, login, the
 * dashboard, post-ad and every other screen, exactly as before. To bring it
 * back on home, delete these three lines.
 *
 * The alternative considered and rejected was moving it to the bottom-left,
 * which keeps a floating gear on the one screen that must look finished. */
.home-page #dev-panel { display: none; }

/* HINDI TILE WORDS NEED MORE ROOM THAN ENGLISH ONES.
 *
 * Measured on the real tile, 24 August 2026, not estimated from a table:
 * Hindi RENT — "किराए पर लें" — is 138px wide against 141px of usable tile at
 * 375px, and the same 3px clearance at 360px and 320px. It FITS, but three
 * pixels is not a margin. Devanagari metrics differ between the fonts Android
 * ships, the Hindi word for HIRE may be longer, and any rewording breaks it.
 * Warden's flag was right.
 *
 * The tile word is dropped to 21px on Hindi only, which turns 3px of clearance
 * into about 30px. English and Nagamese are untouched — Nagamese uses short
 * Latin words ("Bhara Lobo" / "Bhara Dibo"). `html[lang]` is set by
 * I18N.apply() on every language change, so this follows the picker live.
 *
 * RE-MEASURE when the real Hindi HIRE string arrives: it has not been seen yet,
 * and it is the one most likely to be longer than RENT. */
html[lang="hi"] .home-page .tile-word { font-size: 21px; }
@media (max-width: 340px) {
  html[lang="hi"] .home-page .tile-word { font-size: 18px; }
}

/* NAGAMESE NEEDS THE SAME GUARD, AND FOR A DIFFERENT REASON.
 *
 * Found when the real table arrived, 24 August 2026: HIRE and RENT are TWO
 * words in Nagamese — "Bhara Lobo" and "Bhara Dibo" — and at 26px each measured
 * 147px against 149px of usable tile. Both overflowed and BROKE ACROSS TWO
 * LINES, while BUY ("Kina") and SELL ("Bikiri") stayed on one. That is the
 * uneven tile Avi objected to on 24 August, arriving through the translation
 * rather than through the English.
 *
 * 20px brings both to about 113px, on one line, with real clearance. English is
 * untouched at 26px. This was measured on the tile after the table was wired,
 * not predicted from the word length. */
html[lang="nm"] .home-page .tile-word { font-size: 20px; }
@media (max-width: 340px) {
  html[lang="nm"] .home-page .tile-word { font-size: 17px; }
}

/* THE SUB-LINES CANNOT BE ONE LINE IN EVERY LANGUAGE, AND SHOULD NOT BE FORCED.
 *
 * English fits one line each (24 August). Nagamese and Hindi do not: "Free te
 * dikhibo pare · Login nalage" would need roughly 8px type to fit 149px, which
 * is unreadable — the layout would have eaten the message, which is the trade
 * refused when BUY's English line was kept intact.
 *
 * So they wrap, and the fix is to make the wrapping EVEN rather than to prevent
 * it: two lines of space are reserved in both languages, so a one-line sub and
 * a two-line sub occupy the same block and the four tiles read as a set. */
html[lang="nm"] .home-page .tile-sub,
html[lang="hi"] .home-page .tile-sub { min-height: 2.7em; }

/* ONE LINE EACH, ON A NARROW PHONE TOO.
 *
 * The four sub-lines were shortened on 24 August so each fits on one line and
 * the tiles look even. At 375px they do. At 360px — a very common budget
 * Android — "Browse free · No login" was still wrapping to two, which brings
 * back the unevenness the shortening was meant to remove.
 *
 * The words are NOT cut down further: "No login" is the one thing a first-time
 * buyer most needs to know, and dropping it to win 12 pixels would be trading
 * the message for the layout. A little horizontal room and half a point of type
 * buys the same line at no cost to meaning. */
@media (max-width: 380px) {
  .home-page .tile { padding-left: 8px; padding-right: 8px; }
  .home-page .tile-sub { font-size: 12.5px; }
}

/* A narrow phone (older Androids around 320px) must not squeeze the words. */
@media (max-width: 340px) {
  .home-page .tile { min-height: 132px; padding: 14px 8px; }
  .home-page .tile-word { font-size: 22px; }
  .home-page .tile-icon { font-size: 34px; }
  /* Holds "Browse free · No login" to one line at 320px too, so the tiles stay
     even on the oldest phones. The tile WORD stays 22px — it is the thing being
     read at arm's length; this is only the line under it. */
  .home-page .tile-sub { font-size: 11.5px; }
  .home-light .site-header .logo { height: 48px; }
  .home-light .site-header .wordmark { font-size: 24px; }
}

/* ===========================================================================
 * FIRST-RUN LANGUAGE CHOOSER. Added 24 August 2026.
 *
 * Shown once, on first open, then never again. It exists for the elder who is
 * handed back a phone set up by a younger relative: if the app opens in English
 * and the only way out is a list labelled "English", a person who reads no
 * English cannot read the word that would rescue them.
 *
 * NO `hidden` ATTRIBUTE AND NO `!important` HERE, ON PURPOSE. The rule at the
 * top of this file makes [hidden] beat every author rule, so using it would
 * have forced an !important to undo — and that rule is load-bearing. Plain
 * display:none, flipped by a class, needs neither.
 * =========================================================================== */

#first-run { display: none; }

html.nb-first-run #first-run {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 100;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 40px;
  padding: 24px;
  background: var(--home-bg, #f5f1e8);
  color: var(--home-ink, #0d0d0d);
  overflow-y: auto;
}

/* The page behind is hidden as well as covered, so a screen reader and the tab
   key cannot wander into a home screen the person has not reached yet. */
html.nb-first-run #app-header,
html.nb-first-run .home-main,
html.nb-first-run #app-footer { display: none; }

/* The owner's gold test gear must not float over this screen either. */
html.nb-first-run #dev-panel { display: none; }

#first-run .fr-brand { text-align: center; }
#first-run .fr-brand img { display: block; margin: 0 auto 10px; height: 96px; width: auto; }
#first-run .fr-wordmark {
  margin: 0;
  font-size: 34px;
  font-weight: 800;
  letter-spacing: 0.2px;
}
#first-run .fr-wordmark .bosti { color: var(--red); }

#first-run .fr-choices {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
  max-width: 340px;
}

/* Large targets, generous spacing — tapped by working hands, outdoors, and by
   people who may be unsure they are allowed to touch anything at all. 72px is
   well above the 56px floor in --tap, because this is the one screen where a
   mis-tap sets the wrong language and the person may not know how to undo it. */
#first-run .fr-btn {
  min-height: 72px;
  padding: 12px 20px;
  background: var(--home-card, #fff);
  color: var(--home-ink, #0d0d0d);
  border: 1px solid var(--home-line, #e2dccd);
  border-left: 6px solid var(--red);
  border-radius: var(--radius);
  box-shadow: var(--home-shadow, 0 2px 6px rgba(13,13,13,0.10));
  font-family: inherit;
  font-size: 26px;
  font-weight: 700;
  line-height: 1.3;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
#first-run .fr-btn:active { transform: translateY(1px); }
#first-run .fr-btn:focus-visible { outline: 3px solid var(--red); outline-offset: 2px; }

@media (max-width: 340px) {
  html.nb-first-run #first-run { gap: 28px; }
  #first-run .fr-brand img { height: 76px; }
  #first-run .fr-wordmark { font-size: 28px; }
  #first-run .fr-btn { min-height: 64px; font-size: 22px; }
}

/* A SHORT SCREEN MUST SHOW ALL THREE BUTTONS WITHOUT SCROLLING.
 *
 * The overlay scrolls, so a button below the fold is reachable — but not on
 * THIS screen. It exists for someone who cannot read the language the app
 * opened in; expecting that person to guess that a fourth option is hidden
 * below the edge is the same failure as the dropdown this replaced. Measured on
 * a phone held sideways (640x360): हिन्दी, the last button, sat 38px under the
 * fold. Everything shrinks until all three fit.
 *
 * Buttons never go below 48px — the accessibility floor — whatever the height. */
@media (max-height: 520px) {
  html.nb-first-run #first-run { justify-content: flex-start; gap: 20px; padding: 16px; }
  #first-run .fr-brand img { height: 60px; margin-bottom: 6px; }
  #first-run .fr-wordmark { font-size: 24px; }
  #first-run .fr-choices { gap: 12px; }
  #first-run .fr-btn { min-height: 60px; font-size: 22px; }
}
@media (max-height: 430px) {
  html.nb-first-run #first-run { gap: 12px; padding: 12px; }
  #first-run .fr-brand img { height: 40px; margin-bottom: 4px; }
  #first-run .fr-wordmark { font-size: 19px; }
  #first-run .fr-choices { gap: 10px; }
  #first-run .fr-btn { min-height: 52px; font-size: 20px; }
}

/* ===========================================================================
 * THE TWO BROWSE BANNERS (C5). Added 25 August 2026.
 *
 * They stand where the full list of listings used to sit under the category
 * grid. Full width and STACKED, not side by side (Avi, 25 August): both get the
 * whole width, and the order says which matters more.
 *
 * Originally the DARK browse screen; since the 30 August 2026 light
 * conversion these use the app's normal (now light) palette.
 * =========================================================================== */

.banner {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  /* Taller than a category tile (116px), so it reads as a different kind of
     thing rather than a wide tile. */
  min-height: 132px;
  padding: 18px;
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--black);
  -webkit-tap-highlight-color: transparent;
}
.banner:active { transform: translateY(1px); }

.banner-icon {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 74px;
  height: 74px;
  border-radius: var(--radius);
  background: rgba(13, 13, 13, 0.05);
  font-size: 40px;
  line-height: 1;
}

.banner-text { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.banner-title {
  font-size: 28px;
  font-weight: 800;
  letter-spacing: 0.6px;
  line-height: 1.1;
}
.banner-blurb {
  font-size: 15px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--grey);   /* #5c574e reads under a 28px ink title on light */
}

/* ---- SHOPS: the more important of the two ----
   Gold edge, the same mark the old 19th tile used to carry, and a faint green
   cast so it is plainly the louder banner. */
.banner-shops {
  /* The faint green cast, relit: was #17240f into the dark panel. */
  background: linear-gradient(160deg, var(--ok-bg) 0%, var(--panel) 70%);
  border: 1px solid var(--gold);
  /* Joined to the strip below it — they are one block, not two. */
  border-bottom: none;
  border-radius: var(--radius) var(--radius) 0 0;
}
.banner-shops:hover, .banner-shops:focus-visible { border-color: #c9963a; }
.banner-shops:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* ---- the shopkeeper strip ----
   Attached under the Shops banner and deliberately subordinate to it: smaller
   type, quieter ground, no picture. It speaks to shopkeepers without competing
   for a buyer's attention. */
/* IT HAS TO STAY ON ONE ROW, AND THAT IS WHY THE TYPE IS SMALL.
   At the sizes first written it wrapped on a 375px phone and stood 116px tall —
   as tall as a category tile, which is not "subordinate to the banner", it is a
   second banner. The words are settled and were not cut; the type and padding
   came down until the row fits. */
.banner-strip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 16px;
  background: rgba(217, 164, 65, 0.07);
  border: 1px solid var(--gold);
  border-top: 1px solid rgba(217, 164, 65, 0.30);
  border-radius: 0 0 var(--radius) var(--radius);
}
.strip-ask { font-size: 13.5px; font-weight: 600; color: var(--grey); }
.strip-btn {
  /* 48px is the accessibility floor. The 56px in --tap is for primary actions;
     this is a secondary one, and the extra 8px is what made the row wrap. */
  min-height: 48px;
  padding: 0 14px;
  background: var(--gold);
  color: var(--black);
  border: none;
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
}
.strip-btn:active { background: #c9963a; }
.strip-btn:focus-visible { outline: 3px solid var(--black); outline-offset: 2px; }

.strip-note {
  margin: 10px 2px 0;
  font-size: 14.5px;
  line-height: 1.45;
  color: var(--gold-text);
}

/* ---- BROWSE ALL: deliberately quieter ----
   NEUTRAL, NOT RED (Avi, 25 August), so SHOPS stays the more important of the
   two. The sample picture had a red cast here; the instruction overrides the
   picture. */
.banner-all {
  margin-top: 16px;
  margin-bottom: 20px;
  background: var(--panel);
  border: 1px solid var(--panel-line);
}
.banner-all:hover, .banner-all:focus-visible { border-color: var(--grey); }
.banner-all:focus-visible { outline: 2px solid var(--grey); outline-offset: 2px; }
.banner-all .banner-title { font-size: 24px; }

/* A narrow phone must not squeeze the words against the icon. */
@media (max-width: 360px) {
  .banner { gap: 12px; padding: 14px; min-height: 118px; }
  .banner-icon { width: 58px; height: 58px; font-size: 32px; }
  .banner-title { font-size: 24px; }
  .banner-all .banner-title { font-size: 21px; }
  .banner-blurb { font-size: 14px; }
  .banner-strip { padding: 10px 14px; }
}

/* Only on a genuinely narrow phone does the strip give up on one row. Below
   about 344px the ask and the button cannot both fit, so instead of letting
   them wrap raggedly the button goes full width and the stack reads as
   deliberate. Measured: one row holds at 360px and at 375px. */
@media (max-width: 344px) {
  .strip-ask { width: 100%; }
  .strip-btn { width: 100%; }
}

/* NAGAMESE AND HINDI CANNOT HOLD ONE ROW, AND ARE NOT ASKED TO.
 *
 * Measured 25 August 2026, after Warden's table was wired: "Register your shop"
 * becomes "Apuni laga dukan register koribo" and "अपनी दुकान रजिस्टर करें" —
 * far longer. At 375px the row wrapped raggedly, the ask on one line and the
 * button half-hanging on the next.
 *
 * The words are NOT cut: they are a translator's, and shrinking the type to
 * around 11px to force one row would trade a readable button for a tidy row.
 * So the wrap is made DELIBERATE instead — exactly the trade Avi approved for
 * the home-screen tile subtitles on 24 August. English keeps its one row. */
html[lang="nm"] .strip-ask,
html[lang="hi"] .strip-ask,
html[lang="nm"] .strip-btn,
html[lang="hi"] .strip-btn { width: 100%; }
html[lang="nm"] .strip-btn,
html[lang="hi"] .strip-btn { text-align: center; }

/* ==========================================================================
   THE DESKTOP VIEW — 29 August 2026
   ==========================================================================

   WHY THIS EXISTS. Avi opened the site on a PC and found it read as generic:
   the content sat in a narrow column with large black areas either side, like
   a phone app pinned to the middle of a monitor rather than a website. A
   department, or the Governor's office, will open it on a PC.

   MEASURED ON THE LIVE SITE BEFORE THE FIX, at a 1440px viewport:
     main was 560px wide -- 880px of empty black, 61% of the screen;
     the category grid was 3 columns of 168px;
     22 listings stacked in ONE 528px column;
     each listing photograph was displayed at 84x84 from a thumbnail that is
     stored at 400px -- 21% of the pixels we already had on disk.

   THE ORDER MATTERS AND IT IS DELIBERATE: widen first, then let the
   photographs grow into the room that widening created. Enlarging pictures
   inside a 560px column would only have made the column feel more cramped,
   and decorating the empty sides would have covered the gap instead of
   closing it.

   EVERYTHING HERE IS min-width. NOTHING BELOW 900px MOVES. Phones and tablets
   render exactly as they did -- the sellers are on phones and this app is
   mobile-first. 900px is chosen so that a 768px or 800px tablet keeps the
   layout it has today; the desktop rules begin above every tablet width.

   THE PIXEL CEILING ON IMAGES, MEASURED, NOT ASSUMED. Every stored thumbnail
   is exactly 400px on its long side (all 94 checked; js/images.js THUMB_MAX_PX
   = 400). Full photographs are 1280px (84 of 94 checked; ten older ones are
   smaller -- one 437, four 516, then 547, 554, 570, 651 and 901). Sizes below
   are chosen to stay INSIDE those numbers on an ordinary 1x or 1.5x monitor,
   because a photograph enlarged past its own resolution looks worse than a
   small sharp one. Where a size sits close to the ceiling it is said so.
   ========================================================================== */

@media (min-width: 900px) {

  /* ---- 1. The column, widened ---- */

  main { max-width: var(--shell); }

  /* Widening main widens EVERY page, including the forms. A 1080px-wide text
     box, or a line of prose 1080px long, is worse than the narrow column we
     started with -- prose stops being readable somewhere around 75 characters
     and a stretched input looks broken. So the container grows and the things
     that must not grow are held back here, in CSS, without touching a single
     page. */
  /* DESCENDANT selectors, not child ones. The first version of this rule used
     "main > p" and matched almost nothing, because these pages wrap their
     content in <section> -- login.html left a 1168px line of prose on screen
     and the rule looked like it was working. Caught by measuring the page
     rather than by reading the stylesheet.
     68ch is the readable line length. In a narrow container -- a card, a
     tile, the listing page right-hand column -- the cap simply never binds,
     so this is safe everywhere. */
  main p,
  main li,
  .field-error,
  .setting-text { max-width: 68ch; }
  /* Anything deliberately centred must stay centred once it has a max-width,
     or it silently becomes left-aligned. */
  main .center { margin-left: auto; margin-right: auto; }

  input[type="text"],
  input[type="tel"],
  input[type="email"],
  select,
  textarea { max-width: 460px; }

  /* The search box is the exception: it SHOULD run the width of its row. */
  .search-row input[type="search"] { max-width: none; }

  /* .btn is width:100% because a phone button should be thumb-wide. Left
     alone, the same rule drew a 1175px-wide "Choose from a list" button on a
     1440px screen -- measured, not guessed. A button that wide stops reading
     as a button and starts reading as a bar. It is held to the same width as
     the inputs above so a form reads as one column, not a staircase.
     .search-btn is unaffected: it already sets width:auto and sizes to its
     own label. */
  .btn { max-width: 460px; }

  /* THE TWO PHOTO BUTTONS (v122, 21 September 2026 — Avi's laptop tidy): the
     phone's 1fr 1fr grid stretched across the widened column, so each button
     sat capped at 460px inside a 518px cell — a wide gap between them and a
     staircase against "Add more details" below. On the laptop the pair is held
     to the SAME 460px the inputs and buttons above are held to (224 + 12 + 224),
     so the form reads as one column. The phone rule is untouched. */
  .photo-actions { grid-template-columns: repeat(2, minmax(0, 224px)); }

  /* The language chooser is a select, so the rule above would leave it 460px
     wide to show the word "English". It is full-width on a phone because the
     header is narrow there; on a desktop it only needs to be as wide as the
     longest language name. */
  .lang-toggle { width: auto; min-width: 150px; max-width: 220px; }

  /* THE HOME SCREEN IS DELIBERATELY NOT WIDENED. It is a launcher, not a
     content page: four tiles in two rows of two, with the explanation below
     them, in a reading order (BUY, SELL, HIRE, RENT) that was chosen on
     purpose and walked outdoors in sunlight (D78). At 1240px each tile would
     be over 600px wide, which is not a bigger tile, it is a stretched one.
     Capping the column keeps the design and simply gives it a comfortable
     desktop size. */
  .home-page main { max-width: 760px; }

  /* The header and footer keep their full-width background and border, but
     their contents line up with the column instead of hugging the screen
     edge. Done with padding rather than a wrapper because this job is CSS
     only -- no page markup changes. */
  .site-header,
  .site-footer {
    padding-left:  max(var(--pad), calc((100% - var(--shell)) / 2 + var(--pad)));
    padding-right: max(var(--pad), calc((100% - var(--shell)) / 2 + var(--pad)));
  }
  /* The Back link is inserted by app.js as a sibling of the header, so it is
     full width too and would otherwise sit alone at the far left. */
  .back-link {
    padding-left: max(var(--pad), calc((100% - var(--shell)) / 2 + var(--pad)));
  }

  /* ---- 2. The photographs, grown into the room ---- */

  /* Listings sit two abreast instead of one. #results holds .ad-card children
     directly; anything else in there (the "nothing found" message) is told to
     span the full width so it never sits in a half-width cell. */
  #results {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
  }
  #results > :not(.ad-card) { grid-column: 1 / -1; }
  /* The cards carry their own bottom margin for the single-column phone
     layout; inside a grid that margin becomes a double gap. */
  #results > .ad-card { margin-bottom: 0; }

  /* 84px -> 160px. Nearly double the width, more than three times the area.
     SHARP EVERYWHERE: 160px asks for 320 device pixels on a 2x screen, and
     every thumbnail is 400. The no-photo tile MUST move with it or a card
     without a picture changes height -- the two are one rule in two places. */
  .ad-card img { width: 160px; height: 160px; }
  .no-photo-thumb { width: 160px; height: 160px; }
  .no-photo-thumb .no-photo-icon { font-size: 40px; }
  .ad-card h3 { font-size: 18px; }

  /* The category grid goes from 3 columns to 6, and the tiles get taller so
     the icon has room. */
  .cat-grid { grid-template-columns: repeat(6, 1fr); gap: 14px; }
  .cat-tile { min-height: 148px; font-size: 15px; }
  .cat-icon { width: 64px; height: 64px; font-size: 34px; }
  /* v95, browse's wall only: the square stays full-width; the emoji grows. */
  #cat-grid .cat-tile { min-height: 0; }
  #cat-grid .cat-icon { width: 100%; height: auto; font-size: 56px; }

  /* A SHOP tile is a photograph, not an emoji, so it takes two columns of that
     same grid -- roughly 350px wide, against 168px before. The stylesheet
     already says why: "a 96px-wide basket is a smudge."
     HONEST LIMIT: shop and section tiles are drawn from the 400px THUMBNAIL,
     so at ~350px they are sharp on a standard 1x or 1.25x monitor and
     slightly soft on a 2x high-DPI screen. Making them sharp there means
     pointing the tile at the 1280px full photograph, which is a change in
     shop.html, not in CSS, and is deliberately NOT done in this job. */
  /* A grid that holds SHOP tiles is a grid of PHOTOGRAPHS, not emoji, so it
     gets four columns rather than six -- about 290px a tile against 168px
     before. The stylesheet already says why: "a 96px-wide basket is a
     smudge."
     WHY 290px AND NOT WIDER, MEASURED: these tiles are drawn from the 400px
     THUMBNAIL, and 400px is the LONG side. A portrait photograph is only
     ~267px WIDE (checked: one shop tile source is 267x400), and the tile is
     landscape 4:3, so WIDTH is what has to cover. At six columns spanning two
     -- the first thing tried -- tiles came out 393px and stretched a 267px
     photograph by half again, which is visible. 290px asks 267px to stretch
     by 8%, which is not.
     To go bigger than this the tile must use the 1280px FULL photograph
     instead of the thumbnail. That is one line in shop.html, not CSS, and is
     deliberately left for a separate job.
     :has() is used to tell the two kinds of grid apart without touching the
     markup. Every current desktop browser supports it; one that does not
     simply keeps six columns, which is smaller and sharper, never broken. */
  .cat-grid:has(.shop-tile) { grid-template-columns: repeat(4, 1fr); }

  /* The listing page gallery: a swipe strip is a phone gesture. With the room
     to do it, two photographs sit side by side and a mouse user can see both
     at once. 560px asks for 840 device pixels at 1.5x, against a 1280px
     stored photograph -- comfortable. The TEN older listings whose full
     photograph is smaller than 900px will look soft here; they are listed at
     the top of this block. */
  .gallery img,
  .no-photo-detail { width: auto; max-width: 560px; }
  .gallery { scroll-snap-type: none; }

  /* THE LISTING PAGE BECOMES TWO COLUMNS: the photograph on the left, and
     everything a buyer decides with -- price, where it is, and the Call and
     WhatsApp buttons -- on the right, beside it rather than below it. In one
     column on a monitor the photograph sat alone with the whole right-hand
     half of the page empty, and the contact buttons fell below the fold.

     HOW, AND WHAT IT DEPENDS ON: #listing is built by listing.html as a flat
     list of children with .gallery FIRST. The gallery is pinned to column one
     and every other child is sent to column two, where they stack in their
     existing order. Nothing is reordered and no markup changes.
     THE FRAGILITY, NAMED: this depends on .gallery being a DIRECT child of
     #listing and on it being the only thing that belongs on the left. If a
     future change wraps the gallery, or adds a second full-width element,
     this rule needs revisiting -- it will not fail loudly, it will just look
     wrong on a desktop, so check it when listing.html changes.
     span 99 rather than 1 / -1 because these rows are implicit, and -1 does
     not reach the end of an implicit grid. */
  #listing {
    display: grid;
    grid-template-columns: 560px minmax(0, 1fr);
    column-gap: 30px;
    align-items: start;
  }
  #listing > .gallery { grid-column: 1; grid-row: 1 / span 99; }
  #listing > :not(.gallery) { grid-column: 2; }
  /* The gallery bleeds to the screen edges on a phone. Inside a column it
     must not. */
  #listing > .gallery { margin: 0; padding: 0; }
  /* The first heading in the right column should line up with the top of the
     photograph, not carry a phone-sized top margin. */
  #listing > h1 { margin-top: 0; }

  /* Photo slots on the posting form grow with the wider column. */
  .photo-slot img { height: 200px; }

  /* ---- 3. The search bar ---- */

  /* It is now wearing the app's clothes at every width (see the
     input[type="search"] note further up this file, which is the real fix).
     On a desktop it also gets the size the most-used control on the page
     deserves. */
  .search-row { margin-bottom: 22px; }
  .search-row input[type="search"] { min-height: 62px; font-size: 19px; }
  .search-btn { min-height: 62px; font-size: 18px; padding: 0 32px; }

  /* ---- 4. The margins ---- */

  /* NOTHING GOES IN THEM, AND THAT IS THE ANSWER, NOT AN OMISSION.
     At 1440px the column is 1080px, so ~180px remains each side: an ordinary
     page margin, not a void. The empty black Avi saw was a symptom of a
     560px column, and it is gone because the column grew. Repeated logo
     marks down the sides were considered and rejected -- they read as
     wallpaper covering a gap, and there is no longer a gap to cover. */
}

/* A very wide monitor gets a little more room and a third listing per row.
   Cards are then ~350px wide with a 160px photograph, which is still well
   inside the 400px the thumbnail holds. */
@media (min-width: 1400px) {
  /* 1200px, not more, and the number is not arbitrary: the shop banner is
     1200x400 and it runs the full width of the column. Any wider and the one
     image that fills the page edge to edge starts being stretched. The widest
     the content may grow is set by the widest picture we actually own. */
  :root { --shell: 1200px; }
  #results { grid-template-columns: repeat(3, 1fr); }
}

/* ==========================================================================
   THE DESKTOP CATEGORIES DRAWER — turn-2 block 2d, 30 August 2026
   ==========================================================================

   A true desktop layout for browse, not a stretched phone: the 18 top-level
   categories stand in a left-hand drawer and the tile wall stands down — the
   drawer IS the tile wall, rotated into a sidebar. The selected category
   takes the red left edge: the home-tile signature, rotated 90 degrees.

   THE DRAWER SHOWS TOP LEVEL ONLY, DELIBERATELY. The 18 category names are
   translated; the ~90 sub-category names are still English. A drawer that
   expanded to sub-categories would put English in front of a Nagamese
   seller. Sub-categories stay where they are today — chips in the content
   column, unchanged.

   This also SUPERSEDES ON PURPOSE the 29 August decision that the desktop
   side margins stay empty ("nothing goes in them"): the drawer is content,
   not wallpaper. Below 900px none of this exists — the phone browse screen
   is exactly what it was.

   The drawer is drawn by browse.html's own script from the same category
   data as the tiles (translated names, same click handling), so the two can
   never disagree. It rebuilds on every run(), which includes language
   changes. */

#cat-drawer { display: none; }

/* THE DRAWER GROWS (v89, Avi's ruling on seeing v88 on his laptop, 2 Sept
   2026): 28px photographs read as dots. Sub-rows now carry a 48px circle
   and 17px labels; the row's minimum is --tap (56px) so a 48px circle sits
   in 4px of air. "All categories" is THE MAIN BUTTON (.drawer-main): the
   tile's own 52px circle, 18px bold, a hairline beneath it, and every
   category row indented under it as a child (.drawer-sub) — distinct by
   SHAPE and WEIGHT only: red stays the action colour and ink-fill the
   selected signature (D109), so neither is borrowed here. Phone tiles are
   untouched — this whole block is desktop-only by the media query below. */
.drawer-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: var(--tap);
  padding: 4px 12px;
  background: transparent;
  border: 1px solid transparent;
  border-left: 3px solid transparent;
  border-radius: var(--radius);
  color: var(--black);
  font-family: inherit;
  font-size: 17px;
  text-align: left;
  cursor: pointer;
}
/* ALL CATEGORIES MADE UNIQUE (v90, Avi's ruling on the live v89 drawer:
   bold alone let a SELECTED sub-row — white card, red edge, bold — read
   exactly like All categories at rest). Distinct by STRUCTURE first: full
   width, NO indent (the eighteen sub-rows sit 24px in under it), its own
   64px block with the tile's 52px circle and 18px bold, a hairline
   closing it off. And by FILL: --field-line, the token whose documented
   job is "so a control reads as a control" and which already fills the
   Back box (v84) and the switch track — the drawer's own way-back-to-
   everything wears the same family as Back. NEVER white (white + red
   edge IS the selected sub-row), never red (the action), never gold (a
   badge), never ink (selected chips). A selected sub-row and this block
   cannot be confused side by side: pale-grey vs white ground, un-indented
   vs indented, 52px vs 48px circle, 18px vs 17px type.
   WHEN IT IS ITSELF ACTIVE (no category chosen) it keeps its pale fill
   and size and gains only the drawer's red "you are here" left edge —
   still pale, un-indented and large, so it never turns into the white
   selected-sub-row card. */
.drawer-row.drawer-main {
  font-size: 18px;
  font-weight: 700;
  min-height: 64px;
  padding: 8px 12px;
  background: var(--field-line);
  border-bottom: 1px solid var(--panel-line);
  border-radius: 0;
  margin-bottom: 6px;
}
.drawer-row.drawer-main:hover,
.drawer-row.drawer-main:focus-visible { background: var(--field-line); }
.drawer-row.drawer-main.is-active {
  background: var(--field-line);
  border-color: transparent;
  border-bottom-color: var(--panel-line);
  border-left: 3px solid var(--red);
}
/* No icon on the main row at all (Avi, v90) — text alone, so its very
   STRUCTURE differs from every icon+label sub-row: nothing to mistake. */
/* BOLD CAPITALS WITH LETTER-SPACING (v91, Avi's ruling on live v90, 5 Sept
   2026): the pale fill and no-icon were not enough — 18px/700 still spoke in
   the same VOICE as a bold selected sub-row (17px/700). This is the BACK
   button's voice: capitals mean "top-level control", sentence case means
   "a thing you pick". text-transform so the packs keep their natural words
   (a no-op for Devanagari — the same accepted asymmetry as BACK);
   0.06em tracking so the capitals breathe without widening the word past
   the 300px block in any language. Everything else is v90. */
.drawer-row.drawer-main {
  padding: 8px 16px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.drawer-row.drawer-sub { padding-left: 24px; }
.drawer-row:hover, .drawer-row:focus-visible { background: var(--panel); }
.drawer-row:focus-visible { outline: 2px solid var(--red); outline-offset: 1px; }

/* The selected category: white card, hairline, and the red left edge — the
   same signature the home tiles carry on their top edge. */
.drawer-row.is-active {
  background: var(--panel);
  border-color: var(--panel-line);
  border-left: 3px solid var(--red);
  font-weight: 700;
}

/* D120 (2 September 2026): the drawer row's icon is a small circle — the
   tile's 52px circle at drawer scale — so a category photograph draws here
   exactly as it does on the phone tile (the same assets/cat-<id>.jpg, the
   same paintCatIcon, the same emoji fallback). 28px sits inside the row's
   44px minimum, so no row grows; the label moves right by the circle's
   extra width only. The circle is a functional shape (the square-corner
   ruling's emoji-circle exemption). */
/* v95 (D120 — the same treatment on the desktop): the drawer is a LIST, not a
   wall, so its rows cannot "fill" with a photograph; what carries over is
   the SHAPE — the circle becomes a 64px SQUARE thumbnail of the very same
   composed tile (a pair shows its two halves side by side, as on the
   phone), on the same cream ground for emoji rows. The wall is where the
   photograph is read; the drawer's square is its index. Rows grow from 56
   to 72px so the square sits in 4px of air. */
.drawer-icon {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  border-radius: 0;
  background: var(--offwhite);
  font-size: 34px;
  line-height: 1;
  overflow: hidden;
}
.drawer-row.drawer-sub { min-height: 72px; }
.drawer-icon .cat-photo { width: 100%; height: 100%; object-fit: cover; display: block; }
.drawer-label { min-width: 0; }

@media (min-width: 900px) {
  /* Same pattern as #listing's two columns: the drawer is pinned to column
     one and spans every implicit row; everything else stacks in column two
     in its existing order. No markup moves. */
  main.browse-main {
    display: grid;
    /* 300px (was 240): the drawer claims the empty left margin so 48px
       circles and 17px labels sit comfortably; at exactly 900px the
       listings column still keeps ~540px, comfortably one card wide. */
    grid-template-columns: 300px minmax(0, 1fr);
    column-gap: 28px;
    align-items: start;
  }
  main.browse-main > :not(#cat-drawer) { grid-column: 2; }
  #cat-drawer {
    display: flex;
    flex-direction: column;
    gap: 2px;
    grid-column: 1;
    grid-row: 1 / span 99;
    /* Stays put while the listings scroll; scrolls itself if the screen is
       shorter than 18 rows. */
    position: sticky;
    top: 16px;
    max-height: calc(100vh - 32px);
    overflow-y: auto;
  }
  /* The drawer replaces the tile wall (2d: "sidebar replaces the tile
     wall"). #cat-grid is browse's grid only — the shop pages' grids carry
     the class but not the id, and keep their tiles. */
  #cat-grid { display: none; }
}

/* ==========================================================================
   THE FOUR-ITEM BOTTOM NAVIGATION — 31 August 2026, Avi's ruling
   ==========================================================================

   Home · Sell · My Ads · Account. Four, not five — NagaBosti has no in-app
   messaging by design, so there is no Chats to copy from OLX. Drawn by
   app.js's buildBottomNav for SIGNED-IN, REGISTERED sellers only; a
   signed-out buyer never gets the element at all.

   D109: this is navigation, not action — no red fills. The current page
   carries the app's selected-signature (a red top edge, the same mark the
   home tiles and the desktop drawer use for "you are here"); everything
   else is muted ink on the white surface with the cream hairline.

   NOTHING IS COVERED: body.has-bottom-nav pads the page bottom by the
   bar's full height (plus the phone's gesture inset), so the post-ad Save
   button, a listing's Call/WhatsApp buttons and the checkout total scroll
   clear of it. The place-chooser sheet (z-100) and the posting overlay
   (z-1000) sit above the bar deliberately.

   DESKTOP: hidden at ≥900px — the drawer's own breakpoint. A bottom bar is
   a phone pattern; the desktop (the surface officials judge) keeps the
   header and the categories drawer. */

#bottom-nav {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  display: flex;
  background: var(--panel);
  border-top: 1px solid var(--panel-line);
  /* Gesture-navigation phones: keep the items above the home indicator. */
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

.bnav-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 60px;             /* above the 48px floor, one-thumb outdoors */
  border-top: 3px solid transparent;
  color: var(--grey);
  text-decoration: none;
  font-size: 12px;
  font-weight: 600;
  -webkit-tap-highlight-color: transparent;
}
.bnav-icon { font-size: 22px; line-height: 1; }
.bnav-item.is-here {
  color: var(--black);
  border-top-color: var(--red);   /* state marker, not an action (D109) */
  font-weight: 700;
}
.bnav-item:focus-visible { outline: 2px solid var(--red); outline-offset: -2px; }

body.has-bottom-nav {
  padding-bottom: calc(66px + env(safe-area-inset-bottom, 0px));
}
/* The owner's gold test gear (fake builds) sits bottom-right; lift it clear. */
body.has-bottom-nav #dev-panel { bottom: 78px; }

@media (min-width: 900px) {
  #bottom-nav { display: none; }
  body.has-bottom-nav { padding-bottom: 0; }
  body.has-bottom-nav #dev-panel { bottom: 10px; }
}

/* ===========================================================================
   FOUNDING SHOPS — the shop registration flow (shop-request.html, v75).

   Layout follows the Claude Design export ("Shop registration flow" folder)
   but EVERY visual value is the app's own token, NOT the mock's: the mock
   shows 10px rounded corners and a 3px-rounded progress bar — Avi's square
   ruling made --radius the ONE corner token and set it to 0 (v74, Call and
   WhatsApp the only exceptions). DO NOT "fix" these rules to match the mock.
   Progress fill is INK, never red: red is the one action colour (D109), and
   a progress bar is state, not action. All heights are min-heights so
   translated labels (Nagamese and Hindi run longer than English) wrap and
   grow — the mock CLAIMS this; these rules are where it is made true.
   =========================================================================== */

.founding-badge {
  display: inline-block;
  background: var(--gold);           /* gold is a badge FILL only, ink text */
  color: var(--black);
  font-size: 14px;
  font-weight: 800;
  padding: 4px 12px;
  border-radius: var(--radius);
  margin: 0 0 4px;
}

.founding-panel { margin: 14px 0; }
.founding-panel-title { margin: 0 0 6px; font-size: 16px; }
.founding-tight { margin: 6px 0; line-height: 1.5; }
.founding-center { text-align: center; }

/* The keep-current CONDITION — plain ink, not a benefit checkmark and not a
   warning box: it is a term of the offer, stated before the button. */
.founding-ask {
  margin: 14px 0;
  padding: 12px 14px;
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: var(--radius);
  line-height: 1.5;
}

.founding-step-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 44px;
  gap: 10px;
}
/* The founding flow's step-back is the shared .back-link now (D116) — its
   own .founding-back styling was the third Back implementation and is gone. */

.founding-progress {
  display: flex;
  gap: 4px;
  margin: 6px 0 18px;
}
.founding-progress span {
  flex: 1;
  height: 6px;                        /* holds no text — a fixed height is fine */
  background: var(--panel-line);
  border-radius: var(--radius);
}
.founding-progress span.done { background: var(--black); }  /* ink, never red */

.founding-section-row { display: flex; gap: 8px; align-items: stretch; }
.founding-section-row input { flex: 1; min-width: 0; }
.founding-add { width: auto; flex: none; padding: 0 18px; }

.founding-empty {
  background: var(--panel);
  border: 1px dashed var(--field-line);
  border-radius: var(--radius);
  padding: 20px 16px;
  text-align: center;
  margin: 14px 0;
}
.founding-empty-title { font-weight: 600; color: var(--grey); }

.founding-count {
  text-align: center;
  font-size: 14px;
  font-weight: 600;
  color: var(--grey);
  margin: 10px 0;
}

.founding-resume-name { font-weight: 700; margin: 0 0 6px; }

/* The done tick — a true circle: a shape, not a corner treatment, so it is
   exempt from the square ruling the same way the qty +/- buttons are. */
.founding-tick {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--ok-bg);
  border: 2px solid var(--ok-line);
  color: var(--ok-text);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28px;
  margin: 8px auto 14px;
}

/* Desktop (the mock's 1j rule): the flow column never grows past 560px,
   centred — a form stretched to 1200px is the loan-application look. The
   app's global .btn max-width already keeps buttons sane. */
@media (min-width: 900px) {
  #scr-offer, #scr-resume, #scr-steps, #scr-done, #scr-full {
    max-width: 560px;
    margin-left: auto;
    margin-right: auto;
  }
}
