/* =====================================================================
   WYSELE PUSHKARAM — DESIGN SYSTEM (pilgrim + field surfaces)
   public/shared/design-system.css

   Single source of truth for the visual language of every surface a
   PERSON uses standing outside: pilgrim.html, lostfound.html,
   helpdesk.html, staff.html, ghatofficer.html, medic.html,
   collector.html, login.html. Tokens (color / type / space / radius /
   elevation / motion / z-index) + a framework-free component kit
   (vanilla JS app, no build step).

   The ICCC is NOT one of these surfaces. It loads ../tokens.css +
   admin/styles.css and stays on a dark ground on purpose — read the
   header of tokens.css for why. The two files share one brand, one band
   ladder and one rule; they do not share a ground.

   ─────────────────────────────────────────────────────────────────────
   PALETTE — "Marigold on the sand"

   The old palette here was "River at four in the morning": a green-black
   ground and a verdigris accent, chosen because the muhurtham peak at
   Rajahmundry is 04:30 and sunrise is not until 05:31. That reasoning is
   still correct — for the ICCC, whose operator really is working a
   pre-dawn watch in a dim room, and which keeps a dark ground.

   It was never correct for the pilgrim. A pilgrim does not open this app
   in a control room at 04:30. They open it standing on a ghat step in
   direct sun, on a mid-range Android, with wet hands, having queued for
   two hours. A dark UI loses that fight: the screen is competing with
   thousands of lux of ambient light, and a dark screen's own emitted
   luminance is the only thing fighting back, while a near-white screen
   reflects and adds. Every official Kumbh app a pilgrim has already used
   is saffron on light. That is not fashion; it is the same constraint,
   and looking like nothing else in the category is one more thing to
   learn in a crowd.

   So the ground is the pale warm sand at the waterline (#fbf4ea), white
   cards, soft shadows, generous radii. The brand is marigold (#ea8412) —
   the flower actually sold in baskets on the approach road. The old
   Godavari green is not deleted, it is DEMOTED FROM GROUND TO INK
   (--accent-text, #0b443e): links, emphasis and selected labels are set
   in it. That demotion is what makes the whole thing work — see below.

   ─────────────────────────────────────────────────────────────────────
   THE RULE THAT GOVERNS EVERYTHING BELOW — and why it had to be rewritten

   The old rule was one line: "warm colour always means risk; yellow,
   orange and red belong to the band ladder and to nothing else." It
   bought its legibility with HUE EXCLUSIVITY — the rest of the app was
   cold, so any warm pixel was a signal. A saffron brand spends exactly
   that, and an orange warning on an orange app is not a warning. So the
   rule had to be replaced, not quietly dropped.

   First, what does NOT work: finding a hue gap for the brand. There
   isn't one. Measured hue angles — watch h39, action h17, critical h351,
   marigold h32. The brand lands between watch and action, ~15° from
   each, and 15° of hue at this saturation is not a distinction anybody
   makes at a glance, let alone in glare. The warm arc is fully occupied
   by the ladder. Hue is dead as a separator.

   THE NEW RULE — WARM FILL MEANS THE BRAND. WARM INK MEANS RISK.

     CLAUSE A — THE BRAND IS A FILL, NEVER INK.
       Marigold paints the app bar, the brand mark, the one primary
       button on a screen, the active-tab indicator, the focus ring. It
       is never the colour of a word or a number, and there is no
       marigold status anywhere. On this ground the clause is enforced by
       physics rather than discipline: #ea8412 on the page measures
       2.46:1 and cannot be legible text at any size. The palette makes
       the violation impossible to ship. (On the ICCC's charcoal it
       reaches 7.12:1 and WOULD be legible, so over there the clause is
       enforced by the token instead — tokens.css points --accent-text at
       the ink. Same rule, different enforcement.)

     CLAUSE B — RISK IS THE ONLY WARM INK.
       Every band is dark warm ink on a pale tint of its own hue, always
       small type, always with its word. So the only warm ink on the
       screen is a risk statement — which is the old rule, narrowed from
       "colour" to "ink", and therefore still true.

     CLAUSE C — WARM IN THE FURNITURE IS THE BRAND; WARM IN THE CONTENT
       IS RISK.
       Furniture is the fixed chrome in the same place on every screen:
       app bar, tab bar, primary action, brand mark. Content is
       everything between them. Nothing in the content is warm unless it
       is a band. This is the clause that makes the other two auditable
       on a page you have never seen.

     COLOUR NEVER TRAVELS ALONE. Unchanged, and still the clause that
     actually carries the meaning: every band ships with a text label.

   WHY THIS SURVIVES A 70-YEAR-OLD IN GLARE, WHICH IS THE ONLY TEST THAT
   COUNTS:
     · VALUE, NOT HUE, IS THE CHANNEL. The brand is bright (l49) and
       always a large filled area; every band ink is dark (l25-l37) and
       always small type on a pale patch. Nobody is asked to tell
       marigold from ochre — they are asked to tell a big bright block
       from small dark type. Hue discrimination in the yellow-orange
       region is among the first things an ageing eye loses; it is the
       last channel to hang a warning on.
     · THE CHROME IS EVERYWHERE, SO IT IS NOT A SIGNAL. A colour pops out
       only when it is the odd one out. Marigold is on every screen, all
       day, on the furniture — the eye stops attending to it, which is
       precisely why it cannot be mistaken for an alert.
     · THE COOL INK IS THE RELEASE VALVE. Because --accent-text is deep
       Godavari (#0b443e) and not a shade of the brand, none of the
       ordinary emphasis in the app — links, the selected label, the
       helpline number — has to borrow a warm tone. That is what vacates
       the entire warm-dark register for the ladder.

   THE HONEST LIMIT: watch (#7e5200) is the weakest pair against the
   brand, because it is the one band sharing marigold's hue family. So
   watch never appears as a bare swatch — always inside a bordered chip
   or card with its word beside it. The two rungs that matter most were
   moved further away on purpose: action is vermilion (h17) and critical
   is crimson (h351), both unmistakably not the brand.

   The one sanctioned exception, stated so it cannot become a loophole:
   warm decoration is allowed on a surface that can never carry a band —
   the login door, the riverfront banner, the snana certificate. Those
   have no crowding state, no severity and no alert, so there is nothing
   for a warm area to be confused with. Everything that can carry a band
   is bound by clauses A-C.

   ─────────────────────────────────────────────────────────────────────
   WHAT WE VERIFIED, INCLUDING WHERE THE BRIEF'S HYPOTHESIS WAS WRONG

   The reskin brief's working assumption was "a light ground lets
   SATURATED risk chips read strongly, which is how the reference apps
   work." We built that first and measured it, and it is only half true.
   Saturated fills of the ladder against this page:

       ok       #127A45  4.97:1     reads strongly
       critical #C81028  5.43:1     reads strongly
       action   #F26D0C  2.78:1     adequate, carried by chroma not value
       watch    #FFC93C  1.42:1  ←  effectively no edge at all

   A yellow chip on warm sand is the same value as the page: a strong
   colour with no outline. Worse, a saturated warm FILL is exactly what
   clause A reserves for the brand, so the saturated-chip version put the
   ladder and the brand in the same visual register — the very collision
   the reskin was supposed to solve. Both problems disappear with the
   ink-on-tint chip that shipped: dark warm ink (5.5-5.8:1 over its own
   tint) inside a bordered pill. So the light ground DOES let risk read
   strongly, but as ink, not as fill.

   The corollary decided the control room, and it is the reverse of what
   you would guess: THE DARK GROUND GIVES THE LADDER MORE POP THAN SAND
   DOES. On charcoal the ladder runs 6.2-12.9:1 against the page as bare
   ink; here the same statements run 5.5-5.8:1 and need a tint and a
   border to hold. A cream ICCC would have made the band ladder quieter
   on the one screen where a missed band is a crowd crush. That is the
   evidence behind keeping the control room dark.

   ─────────────────────────────────────────────────────────────────────
   CONTRAST — every text/background pair, measured, not eyeballed
   (WCAG 2.1; AA is 4.5:1 body, 3:1 large/UI. Computed from the literal
   token values in this file.)

   LIGHT (default) — page #fbf4ea · card #ffffff · inset #efe1ca
                          page   card   inset
     ink        #241a0e  15.65  17.09  13.26   PASS
     ink-2      #57483a   8.04   8.77   6.81   PASS
     ink-muted  #73604d   5.48   5.98   4.64   PASS
     accent-text/river
                #0b443e  10.06  10.99   8.53   PASS
     good       #0a6b3e   6.04   6.59   5.11   PASS
     watch      #7e5200   6.22   6.79   5.27   PASS
     action     #a83408   6.09   6.65   5.16   PASS
     critical   #ab1026   6.79   7.41   5.75   PASS
   band ink over its OWN pale tint (the chip's worst case)
     good  #0a6b3e on #dcefe4  5.50   watch #7e5200 on #fae7bd  5.57
     action#a83408 on #fbe2d5  5.36   crit  #ab1026 on #fadcde  5.77
   fills
     ink #241704 ON the marigold fill #ea8412            6.51   PASS
     --on-critical white ON critical #ab1026 (the SOS)   7.41   PASS
     --on-accent-text white ON river #0b443e            10.99   PASS
     hairline rgba(58,38,12,.22) on white                1.55   (a rule,
       not text; cards also carry a shadow — a white card on this page is
       1.09:1 on its own, which is a boundary you cannot see)
     marigold #ea8412 AS TEXT on the page                2.46
       ← FAILS BY DESIGN. This is clause A being enforced by physics.

   DARK ([data-theme="dark"]) — page #100f0d · panel #191815 · card #201e1a
                          page   panel  card
     ink        #f5f1e9  17.01  15.76  14.77   PASS
     ink-2      #b9b2a6   9.11   8.44   7.91   PASS
     ink-muted  #a19889   6.72   6.23   5.84   PASS
     ink-faint  #928a7c   5.61   5.20   4.87   PASS
     accent-text#6fd3c0  10.72  10.00   9.31   PASS
     good       #3ddc97  10.84  10.05   9.42   PASS
     watch      #ffce4a  12.94  11.99  11.24   PASS
     action     #ff7a45   7.41   6.87   6.44   PASS
     critical   #ff5570   6.19   5.74   5.38   PASS
     ink #241704 ON the marigold fill #ea8412            6.51   PASS
     --on-accent-text #100f0d ON river #6fd3c0          10.72   PASS
     --on-critical    #2a0207 ON critical #ff5570        6.11   PASS

   ─────────────────────────────────────────────────────────────────────
   MIRROR: pushkaram-mobile/src/theme.ts carries the same values —
   page, surfaces, ink tiers, hairline, accent, onAccent, accentSoft,
   river/riverMid/riverDeep and all four bands with their soft tints are
   identical literals. A pilgrim moves between this PWA and that app
   inside one event and must not feel handed to a different product. If
   you change a colour here, change it there in the same commit.
   Two tokens exist here and not there, both additive:
     --action-soft #fbe2d5 — mobile ships goodSoft/amberSoft/criticalSoft
       but no action tint; it should adopt this literal.
     the whole [data-theme="dark"] block — the mobile app has no control
       room and no night surface, so there is nothing to mirror.

   TABLE OF CONTENTS
     1.  Tokens — color primitives (brand marigold + river + band hues)
     2.  Tokens — semantic (surfaces / ink / status / accent), LIGHT default
     3.  Tokens — type scale, spacing, radius, elevation, motion, z-index
     4.  Dark theme overrides             [data-theme="dark"]
     5.  Reset + base document styles
     6.  Typography utilities
     7.  Layout shell (mobile-first single column + safe-area)
     8.  Appbar + bottom tabbar (mobile nav)
     9.  Sidebar (desktop nav) + segmented tabs (desktop content tabs)
     10. Buttons
     11. Forms — fields, inputs, validation states
     12. Chips / badges / STATUS SCALE (the domain's soul)
     13. Cards / panels / lists
     14. Stat tiles (KPIs) + meters
     15. Tables
     16. Sheets (mobile bottom sheet) + modal (desktop dialog)
     17. Toasts / inline alerts
     18. Skeleton loaders + empty states
     19. Tooltips
     20. Timeline / stars / misc
     21. Focus-visible, selection, scrollbar, reduced motion
     22. Theme-specific corrections
   ===================================================================== */


/* =====================================================================
   1. TOKENS — color primitives
   Raw hues only. Nothing below should be used directly in components —
   always go through the semantic tokens in section 2. Every literal in
   this block is byte-identical to pushkaram-mobile/src/theme.ts.
   ===================================================================== */
:root {
  /* Sand — warm, low chroma on purpose. The page is NOT washed marigold:
     a warm page would make clause C collapse, because everything would
     be warm. */
  --sand-50:  #ffffff;   /* card. Opaque, not a wash: a translucent card
                            over a light page has no edge at all. */
  --sand-100: #fbf4ea;   /* page — the waterline */
  --sand-200: #efe1ca;   /* inset controls — inputs, chips, ghost buttons */
  --sand-300: #dfceb3;   /* the track a fill runs along */

  /* Charcoal — the night/ICCC ground. Deliberately ACHROMATIC (about ten
     points of saturation off pure grey). A ground with a hue of its own
     tints every colour placed on it; a hue-free ground means every hue
     on the screen is carrying information. */
  --char-950: #100f0d;
  --char-900: #191815;
  --char-850: #201e1a;
  --char-800: #2a2721;
  --char-750: #35312a;
  --char-700: #423d34;

  /* Brand — marigold. A FILL. See clause A. */
  --marigold-400: #ffa733;
  --marigold-500: #ea8412;   /* THE marigold */
  --marigold-600: #c96c05;   /* hover / pressed */
  --marigold-soft: #fce6c4;  /* pale wash — selected rows, chips, cells */
  --marigold-ink: #241704;   /* the ink that goes ON the fill (6.51:1) */

  /* River — the brand's INK form, and deliberately NOT a shade of the
     marigold. This is the old palette's Godavari green, demoted from
     ground to ink. It is what vacates the warm-dark register entirely
     for the band ladder. */
  --river-500: #0b443e;
  --river-400: #17594f;
  --river-600: #0b3733;
  --river-300: #6fd3c0;   /* the same idea inverted, for the dark ground */

  /* Band hues — the domain vocabulary. Two tiers per band because a tone
     that reads on sand does not read on charcoal and vice versa:
       -ink  : dark warm ink, for the light ground
       -lite : the same band inverted, for the dark ground
       -soft : the pale tint the ink sits on (light ground only) */
  --ok-ink:  #0a6b3e;  --ok-lite:  #3ddc97;  --ok-soft:  #dcefe4;
  --wa-ink:  #7e5200;  --wa-lite:  #ffce4a;  --wa-soft:  #fae7bd;
  --ac-ink:  #a83408;  --ac-lite:  #ff7a45;  --ac-soft:  #fbe2d5;
  --cr-ink:  #ab1026;  --cr-lite:  #ff5570;  --cr-soft:  #fadcde;
  /* fire — a hotter rung than critical, used by the waste/gas views only */
  --fi-ink:  #8e2600;  --fi-lite:  #ff5540;  --fi-soft:  #fbdcd2;
}

/* =====================================================================
   2. TOKENS — semantic. LIGHT IS THE DEFAULT.
   Light is :root (not an override) so that any page which forgets the
   theme bootstrap still lands on the palette its users actually need.
   The old file had this the other way round, and every page that had not
   been converted silently inherited a dark control-room skin.
   Components reference ONLY these. Legacy names kept as aliases where
   older markup/JS across the app already reads them (documented inline).
   ===================================================================== */
:root {
  color-scheme: light;

  /* Elevation ladder — paper on sand, separated by shadow, not by value */
  --page: var(--sand-100);
  --surface-0: var(--sand-100);
  --surface-1: var(--sand-50);      /* panel */
  --surface-2: var(--sand-50);      /* card */
  --surface-3: var(--sand-50);      /* raised: sheet / modal / popover */
  --surface-4: var(--sand-200);     /* topmost: tooltip / toast / track */
  --surface-inset: var(--sand-200); /* controls: input, chip, ghost button */
  --surface-hover: var(--sand-200);

  /* Hairlines are warm-brown, not grey: a neutral rule on sand reads
     slightly blue and dirties the page. 1.55:1 — a rule, not a border. */
  --hairline: rgba(58, 38, 12, 0.22);
  --hairline-strong: rgba(58, 38, 12, 0.34);

  --ink: #241a0e;
  --ink-2: #57483a;
  --ink-muted: #73604d;
  --ink-faint: #8d7a66;   /* decorative only — placeholders, disabled */

  /* Read the accent names as roles:
       --accent       a FILL. Never assign this to `color:`.
       --accent-ink   the ink that goes on that fill.
       --accent-text  what the brand looks like when it must be TEXT —
                      and the answer is river, not a lighter marigold.
                      That substitution is the whole trick. */
  --accent: var(--marigold-500);
  --accent-strong: var(--marigold-600);
  --accent-soft: var(--marigold-soft);
  --accent-ring: rgba(234, 132, 18, 0.45);
  --accent-ink: var(--marigold-ink);
  --accent-text: var(--river-500);
  --accent-outline: var(--marigold-500);
  /* The ink that goes ON a river fill or a critical fill. These MUST be
     tokens and not literals: --accent-text and --status-critical both flip
     value between themes, so a hardcoded white that measures 10.99:1 on the
     light river measures 1.79:1 on the dark one. Both of those shipped in
     the first cut of this reskin and were caught by looking at the page in
     dark mode, not by reading the CSS. */
  --on-accent-text: #ffffff;   /* on #0b443e — 10.99:1 */
  --on-critical: #ffffff;      /* on #ab1026 —  7.41:1 */
  --grad: linear-gradient(135deg, var(--marigold-400) 0%, var(--marigold-600) 100%);
  --grad-text: none;   /* marigold letterforms are clause-A illegal */

  /* ── STATUS SCALE — ok · watch · action · critical · fire ──────────
     Three tokens per band, one job each:
       --status-X      the band as INK (chip text, bare text, dots, meters)
       --status-X-soft the pale tint that ink sits on
       --status-X-fg   legacy alias for the ink (older JS reads this name)
     Never use colour alone — always pair with a text label (the chip
     components below already do). */
  --status-ok: var(--ok-ink);             --status-ok-soft: var(--ok-soft);
  --status-watch: var(--wa-ink);          --status-watch-soft: var(--wa-soft);
  --status-action: var(--ac-ink);         --status-action-soft: var(--ac-soft);
  --status-critical: var(--cr-ink);       --status-critical-soft: var(--cr-soft);
  --status-fire: var(--fi-ink);           --status-fire-soft: var(--fi-soft);
  --status-ok-fg: var(--status-ok);
  --status-watch-fg: var(--status-watch);
  --status-action-fg: var(--status-action);
  --status-critical-fg: var(--status-critical);
  --status-fire-fg: var(--status-fire);

  /* A "glow" is a dark-ground device; on sand it is a smudge. Critical
     emphasis on this ground is a ring. */
  --glow-critical: 0 0 0 3px rgba(171, 16, 38, 0.16);
  --glow-fire: 0 0 0 3px rgba(142, 38, 0, 0.18);

  /* Legacy status aliases — tokens.css and the admin JS reference these
     exact names directly (e.g. `var(--good)` inside an injected <style>).
     Keep them mapped 1:1 so nothing silently loses its colour. */
  --good: var(--status-ok);
  --watch: var(--status-watch);
  --action: var(--status-action);
  --critical: var(--status-critical);

  /* River as a DEPICTION — the ghat ribbon and the pilgrim hero. Allowed
     to be cool on a warm page because it is a picture of water, not
     brand colour. */
  --river-deep: var(--river-600);
  --river-mid: var(--river-400);
  --river-line: rgba(11, 68, 62, 0.30);
}

/* =====================================================================
   3. TOKENS — type scale, spacing, radius, elevation, motion, z-index
   Type scale: a major-third-derived progression (×1.25), tuned down at
   the small end for mobile density and up at the large end for KPIs.
   All numeric/metric UI (.num, .tnum, table figures, meters) gets
   tabular-nums so digits never jitter column widths.
   ===================================================================== */
:root {
  --font-text: system-ui, -apple-system, 'Segoe UI', Roboto, 'Noto Sans Telugu', 'Helvetica Neue', Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --text-2xs: 11px;  --text-xs: 12px;  --text-sm: 13px;  --text-base: 15px;
  --text-md: 17px;   --text-lg: 19px;  --text-xl: 24px;  --text-2xl: 30px;
  --text-3xl: 38px;
  --leading-tight: 1.2; --leading-snug: 1.35; --leading-normal: 1.5;

  /* Spacing — 4px base unit */
  --space-1: 4px;  --space-2: 8px;  --space-3: 12px; --space-4: 16px;
  --space-5: 20px; --space-6: 24px; --space-7: 28px; --space-8: 32px;
  --space-10: 40px; --space-12: 48px; --space-16: 64px;

  /* Radius — rounder than the dark kit was, and matched to theme.ts. On a
     dark ground a card is defined by being lighter than what is behind
     it; on a light one it is defined by its edge, and a generous radius
     is what makes that edge read as a card rather than as a box. */
  --radius-sm: 12px; --radius: 16px; --radius-lg: 24px; --radius-xl: 28px;
  --radius-full: 999px;

  /* Elevation — on sand the shadow IS the separation (a white card on
     this page is 1.09:1 on its own), so these are softer and wider than
     a dark theme's and warm-tinted rather than blue-black, which is what
     stops a light UI looking like a screenshot of a different product. */
  --shadow-1: 0 1px 2px rgba(74, 46, 10, 0.06), 0 4px 12px rgba(74, 46, 10, 0.10);
  --shadow-2: 0 10px 26px rgba(74, 46, 10, 0.13);
  --shadow-3: 0 22px 56px rgba(74, 46, 10, 0.20);
  --shadow-card: var(--shadow-1);   /* legacy alias (tokens.css) */
  --shadow-pop: var(--shadow-3);    /* legacy alias (tokens.css) */

  /* Translucent chrome background (appbar/tabbar) — plain rgba, not
     color-mix(), so it renders correctly on older Android WebViews */
  --chrome-bg: rgba(253, 248, 240, 0.92);

  /* Motion — tasteful, fast, purposeful */
  --duration-fast: 120ms;
  --duration-base: 160ms;
  --duration-slow: 220ms;
  --ease-standard: cubic-bezier(.2, .7, .2, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);   /* legacy alias (tokens.css) */
  --ease-in: cubic-bezier(.4, 0, 1, 1);

  --z-sticky: 10; --z-popover: 60; --z-overlay: 100; --z-modal: 200;
  --z-toast: 300; --z-tooltip: 400;

  --appbar-h: 56px; --tabbar-h: 62px;
  --track-eyebrow: 0.14em;
}

/* =====================================================================
   4. DARK THEME — [data-theme="dark"]
   Same variable names; only values change. This is the SAME charcoal the
   ICCC runs on (tokens.css), so a supervisor who flips their phone to
   dark at 4am sees the control room's ground, not a second dark theme.
   The ladder inverts from "-ink" to "-lite" because a tone that reads on
   sand fails on charcoal; --accent-text inverts from deep river to a
   light verdigris for the same reason. The BRAND does not invert — a
   marigold fill with its dark ink works on both grounds, which is what
   keeps the two surfaces recognisably one product.
   ===================================================================== */
[data-theme="dark"] {
  color-scheme: dark;

  --page: var(--char-950);
  --surface-0: var(--char-950);
  --surface-1: var(--char-900);
  --surface-2: var(--char-850);
  --surface-3: var(--char-800);
  --surface-4: var(--char-750);
  --surface-inset: var(--char-800);
  --surface-hover: var(--char-700);

  --hairline: rgba(245, 241, 233, 0.11);
  --hairline-strong: rgba(245, 241, 233, 0.20);

  --ink: #f5f1e9;
  --ink-2: #b9b2a6;
  --ink-muted: #a19889;
  --ink-faint: #928a7c;

  /* Clause A is NOT enforced by physics here — marigold reaches 7.12:1
     on this ground and would be perfectly legible as text — so it is
     enforced by the token. If you write `color: var(--accent)` on a dark
     surface you are writing a bug. */
  --accent-text: var(--river-300);
  --on-accent-text: var(--char-950);   /* on #6fd3c0 — 10.72:1 */
  --on-critical: #2a0207;              /* on #ff5570 —  6.11:1 */
  --accent-soft: rgba(234, 132, 18, 0.18);
  --accent-ring: rgba(234, 132, 18, 0.45);

  --status-ok: var(--ok-lite);          --status-ok-soft: rgba(61, 220, 151, 0.12);
  --status-watch: var(--wa-lite);       --status-watch-soft: rgba(255, 206, 74, 0.12);
  --status-action: var(--ac-lite);      --status-action-soft: rgba(255, 122, 69, 0.12);
  --status-critical: var(--cr-lite);    --status-critical-soft: rgba(255, 85, 112, 0.14);
  --status-fire: var(--fi-lite);        --status-fire-soft: rgba(255, 85, 64, 0.16);

  --glow-critical: 0 0 18px rgba(255, 85, 112, 0.35);
  --glow-fire: 0 0 22px rgba(255, 85, 64, 0.5);

  --river-deep: #0b3733;
  --river-mid: #17594f;
  --river-line: rgba(111, 211, 192, 0.30);

  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.5), 0 6px 16px rgba(0, 0, 0, 0.35);
  --shadow-2: 0 10px 28px rgba(0, 0, 0, 0.45);
  --shadow-3: 0 24px 64px rgba(0, 0, 0, 0.6);
  --shadow-card: var(--shadow-1);
  --shadow-pop: var(--shadow-3);

  --chrome-bg: rgba(22, 21, 18, 0.92);
}

/* =====================================================================
   5. RESET + BASE DOCUMENT STYLES
   ===================================================================== */
* { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  /* No marigold wash. The old body carried two accent-tinted radials;
     under this palette that would paint a permanent warm haze across the
     content area — exactly what clause C forbids. The page is sand, and
     the only warmth on it is furniture or a band. The one gradient kept
     is achromatic: a barely-there deepening at the bottom so a short page
     does not end on a flat stop. */
  background:
    linear-gradient(180deg, transparent 70%, rgba(74, 46, 10, 0.05)),
    var(--page);
  background-attachment: fixed;
  color: var(--ink);
  font-family: var(--font-text);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  min-height: 100vh;
  transition: background-color var(--duration-slow) var(--ease-standard), color var(--duration-slow) var(--ease-standard);
}
[data-theme="dark"] body {
  background:
    radial-gradient(1200px 760px at 50% 118%, rgba(255, 255, 255, 0.035), transparent 62%),
    var(--page);
}

/* =====================================================================
   6. TYPOGRAPHY UTILITIES
   ===================================================================== */
h1, h2, h3 { line-height: var(--leading-tight); font-weight: 700; letter-spacing: -0.01em; }
h1 { font-size: var(--text-2xl); }
h2 { font-size: var(--text-lg); }
h3 { font-size: var(--text-md); }
/* Links are set in river, not marigold — clause A. This also makes them
   survive greyscale and the common colour-vision deficiencies, which a
   warm-on-warm link would not have. */
a { color: var(--accent-text); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }
button { font-family: inherit; color: inherit; }
.muted { color: var(--ink-muted); }
.faint { color: var(--ink-faint); }
.small { font-size: var(--text-xs); }
.tnum, .num, .stat .num, .kpi-value { font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1; }
/* .grad-text used to paint the brand gradient into letterforms. Under
   clause A that is illegal, and at 2.46:1 it was never legible anyway.
   The class stays — markup across eight pages emits it — but the brand
   is now carried by weight and by river, not by a warm gradient. */
.grad-text { background: none; color: var(--accent-text); font-weight: 800; }
.sep { border: 0; border-top: 1px solid var(--hairline); margin: var(--space-4) 0; }
/* Eyebrow: wide-tracked small caps. Used for section kickers and for the
   Telugu/English pairing, which is structural here rather than a toggle —
   this event is bilingual by nature, not by translation. */
.eyebrow {
  font-size: var(--text-2xs); font-weight: 700;
  letter-spacing: var(--track-eyebrow); text-transform: uppercase;
  color: var(--ink-muted);
}
.eyebrow-te { text-transform: none; letter-spacing: 0; color: var(--accent-text); }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* =====================================================================
   7. LAYOUT SHELL — mobile-first single column, safe-area aware
   ===================================================================== */
.app-shell {
  max-width: 520px; margin: 0 auto;
  padding: calc(var(--appbar-h) + var(--space-4)) var(--space-4)
           calc(var(--tabbar-h) + var(--space-6) + env(safe-area-inset-bottom, 0px));
}
.view { display: none; }
.view.active { display: block; animation: dsViewIn var(--duration-slow) var(--ease-standard); }
@keyframes dsViewIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* Desktop-dense variant for admin/exec surfaces (Wave 2) */
.app-shell-wide { max-width: 1280px; margin: 0 auto; padding: calc(var(--appbar-h) + var(--space-6)) var(--space-6) var(--space-8); }

/* =====================================================================
   8. APPBAR + BOTTOM TABBAR (mobile nav chrome)
   Blur is intentionally reserved for this floating chrome layer.
   ===================================================================== */
.appbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-sticky);
  height: var(--appbar-h);
  display: flex; align-items: center; gap: 10px;
  padding: 0 var(--space-4);
  border-bottom: 1px solid var(--hairline);
  background: var(--chrome-bg);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
}
.brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
.brand-mark {
  width: 32px; height: 32px; flex: none; display: grid; place-items: center;
  background: var(--grad); color: var(--accent-ink); font-weight: 800; font-size: 16px;
  border-radius: 10px; box-shadow: 0 4px 14px rgba(120, 66, 4, 0.28);
}
.brand-title { font-size: 15px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.brand-sub { font-size: var(--text-2xs); color: var(--ink-muted); }
.spacer { flex: 1; }
.icon-btn {
  height: 36px; min-width: 36px; padding: 0 8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface-inset); border: 1px solid var(--hairline); color: var(--ink);
  border-radius: var(--radius-sm); cursor: pointer; font-size: var(--text-sm);
  transition: border-color var(--duration-fast) var(--ease-standard), background var(--duration-fast) var(--ease-standard);
}
.icon-btn:hover { border-color: var(--accent-outline); background: var(--surface-hover); }
select.icon-btn { appearance: none; -webkit-appearance: none; font-weight: 600; }

.tabbar {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: var(--z-sticky);
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  display: flex; justify-content: space-around; align-items: stretch;
  border-top: 1px solid var(--hairline);
  background: var(--chrome-bg);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
}
.tab {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; background: none; border: 0; cursor: pointer; color: var(--ink-muted);
  min-height: 44px; /* touch target */
  transition: color var(--duration-fast) var(--ease-standard);
}
.tab .ico { font-size: 19px; line-height: 1; }
.tab .ico svg { width: 21px; height: 21px; display: block; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.tab .lbl { font-size: var(--text-2xs); font-weight: 650; }
/* The tab bar is furniture, so the marigold is allowed — but as the 3px
   indicator FILL, not as the word (clause A). The word is river. */
.tab { position: relative; }
.tab.active { color: var(--accent-text); font-weight: 750; }
.tab.active .ico { transform: translateY(-1px); }
.tab.active::before {
  content: ''; position: absolute; top: 0; left: 22%; right: 22%; height: 3px;
  border-radius: 0 0 3px 3px; background: var(--accent);
}

/* =====================================================================
   9. SIDEBAR (desktop nav, Wave 2) + SEGMENTED TABS (content tabs)
   Namespaced .seg-tabs so it never collides with the mobile .tab class.
   ===================================================================== */
.sidebar {
  position: fixed; top: 0; left: 0; bottom: 0; width: 248px; z-index: var(--z-sticky);
  background: var(--surface-1); border-right: 1px solid var(--hairline);
  display: flex; flex-direction: column; padding: var(--space-4);
}
.sidebar-section { margin-top: var(--space-5); }
.sidebar-label {
  font-size: var(--text-2xs); font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-faint); padding: 0 var(--space-3); margin-bottom: var(--space-2);
}
.sidebar-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px var(--space-3); border-radius: var(--radius-sm); border: 0; background: none;
  color: var(--ink-2); font-size: var(--text-sm); font-weight: 600; cursor: pointer; text-align: left;
  min-height: 40px;
  transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}
.sidebar-item:hover { background: var(--surface-hover); color: var(--ink); }
.sidebar-item.active { background: var(--accent-soft); color: var(--ink); }
.sidebar-item.active::before {
  content: ''; position: absolute; /* pair with position:relative on parent row if used as a rail */
}

.seg-tabs { display: flex; gap: 2px; padding: 3px; border-radius: var(--radius); background: var(--surface-inset); border: 1px solid var(--hairline); width: max-content; }
.seg-tabs button {
  border: 0; background: none; color: var(--ink-muted); font: inherit; font-weight: 650; font-size: var(--text-sm);
  padding: 7px 16px; border-radius: var(--radius-sm); cursor: pointer; min-height: 36px;
  transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}
.seg-tabs button:hover { color: var(--ink); }
.seg-tabs button.active { background: var(--surface-1); color: var(--ink); box-shadow: var(--shadow-1); }

/* =====================================================================
   10. BUTTONS — primary / secondary / ghost / danger, sizes, states
   ===================================================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 11px 18px; border-radius: var(--radius); cursor: pointer;
  font-size: var(--text-base); font-weight: 650; font-family: inherit;
  background: var(--surface-inset); color: var(--ink);
  border: 1px solid var(--hairline);
  transition: transform var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard),
              background var(--duration-fast) var(--ease-standard),
              filter var(--duration-fast) var(--ease-standard);
}
.btn:hover { border-color: var(--accent-outline); }
.btn:active { transform: scale(.98); }
.btn:disabled, .btn[disabled] { opacity: .5; cursor: not-allowed; transform: none; }

.btn-primary { background: var(--accent); border: 1px solid transparent; color: var(--accent-ink); box-shadow: 0 6px 18px rgba(120, 66, 4, 0.26); }
.btn-primary:hover { background: var(--accent-strong); filter: none; }

/* Secondary — solid surface, more presence than ghost, less than primary */
.btn-secondary { background: var(--surface-inset); border-color: var(--hairline-strong); color: var(--ink); }
.btn-secondary:hover { background: var(--surface-hover); }

.btn-ghost { background: transparent; border-color: transparent; }
.btn-ghost:hover { background: var(--surface-inset); border-color: var(--hairline); }

/* Danger is a band hue doing a control's job. It is the ONE crimson-
   filled control in the system (the SOS), it carries its word, and white
   on #ab1026 measures 7.41:1. Everything else danger-flavoured stays ink
   on its tint like any other band. */
.btn-danger { background: var(--status-critical-soft); border-color: var(--status-critical); color: var(--status-critical); }
.btn-danger:hover { background: var(--status-critical); border-color: var(--status-critical); color: var(--on-critical); }
.btn-danger.btn-solid { background: var(--status-critical); border-color: var(--status-critical); color: var(--on-critical); }

.btn-sm { min-height: 36px; padding: 6px 12px; font-size: var(--text-sm); border-radius: var(--radius-sm); gap: 6px; }
.btn-lg { min-height: 52px; padding: 14px 22px; font-size: var(--text-md); }
.btn-block { display: flex; width: 100%; }
.btn-icon { min-width: 44px; padding: 0; }

/* Hero CTA — the one big mobile action (e.g. "Report an issue") */
.cta-hero {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; min-height: 56px; padding: 16px; margin: var(--space-4) 0;
  background: var(--accent); border: 0; border-radius: var(--radius-lg);
  color: var(--accent-ink); font-size: var(--text-md); font-weight: 750; cursor: pointer;
  box-shadow: 0 12px 28px rgba(120, 66, 4, 0.26);
  transition: transform var(--duration-fast) var(--ease-standard), background var(--duration-fast) var(--ease-standard);
}
.cta-hero:hover { background: var(--accent-strong); }
.cta-hero:active { transform: scale(.985); }

/* Loading state utility — pair with disabled + swap label text in JS */
.btn.is-loading { position: relative; color: transparent !important; pointer-events: none; }
.btn.is-loading::after {
  content: ''; position: absolute; width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent; color: var(--ink);
  animation: dsSpin .7s linear infinite;
}
@keyframes dsSpin { to { transform: rotate(360deg); } }

/* =====================================================================
   11. FORMS — fields, inputs, validation states
   ===================================================================== */
.field { margin-bottom: var(--space-4); }
.field > label {
  display: block; font-size: var(--text-2xs); font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-muted); margin-bottom: 7px;
}
.input, input.input, select.input, textarea.input {
  width: 100%; min-height: 44px; padding: 12px 14px; font: inherit; font-size: var(--text-base); color: var(--ink);
  background: var(--surface-inset); border: 1px solid var(--hairline);
  border-radius: var(--radius); outline: none;
  transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}
.input:hover { border-color: var(--hairline-strong); }
.input:focus { border-color: var(--accent-outline); box-shadow: 0 0 0 3px var(--accent-ring); }
.input::placeholder { color: var(--ink-faint); }
.input:disabled { opacity: .55; cursor: not-allowed; }
textarea.input { min-height: 96px; resize: vertical; }
select.input {
  appearance: none; -webkit-appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-muted) 50%), linear-gradient(135deg, var(--ink-muted) 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px; background-repeat: no-repeat;
}

/* Validation states */
.field-help { font-size: var(--text-2xs); color: var(--ink-muted); margin-top: 6px; }
.field-error { font-size: var(--text-2xs); color: var(--status-critical-fg); margin-top: 6px; font-weight: 650; }
.field.has-error .input,
.input.invalid { border-color: var(--status-critical); box-shadow: 0 0 0 3px rgba(171, 16, 38, 0.18); }
.field.has-success .input,
.input.valid { border-color: var(--status-ok); box-shadow: 0 0 0 3px rgba(10, 107, 62, 0.16); }

/* Phone / OTP auth pattern (login.html + any Wave-2 phone-auth reuse) */
.phone-row { display: flex; gap: 8px; }
.phone-row .cc {
  display: flex; align-items: center; gap: 6px; padding: 0 13px; flex: none; min-height: 44px;
  background: var(--surface-inset); border: 1px solid var(--hairline);
  border-radius: var(--radius); font-weight: 650; font-size: var(--text-sm);
}
.otp-row { display: flex; gap: 8px; justify-content: space-between; }
.otp-box {
  width: 15%; aspect-ratio: 1 / 1; max-width: 52px; min-height: 44px; text-align: center;
  font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--ink); background: var(--surface-inset);
  border: 1px solid var(--hairline); border-radius: var(--radius); outline: none;
  transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}
.otp-box:focus { border-color: var(--accent-outline); box-shadow: 0 0 0 3px var(--accent-ring); }
.otp-meta { display: flex; justify-content: space-between; font-size: var(--text-xs); color: var(--ink-muted); margin-top: 10px; }
.otp-meta .resend { color: var(--ink-muted); }
.otp-meta .resend.is-ready { color: var(--accent-text); cursor: pointer; font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }

/* =====================================================================
   12. CHIPS / BADGES / STATUS SCALE — the domain's soul
   Five states — ok · watch · action · critical · fire — used for
   bin.status, grievance SLA state, fleet/plant health, everywhere.
   Legacy class names (band-good/watch/action/critical, sev-*, m-good…)
   are kept because citizen.js / admin JS already emit them literally.
   ===================================================================== */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-2xs); padding: 6px 12px; border-radius: var(--radius-full);
  border: 1px solid var(--hairline); color: var(--ink-2);
  background: var(--surface-inset); white-space: nowrap; min-height: 30px;
  transition: border-color var(--duration-fast) var(--ease-standard), background var(--duration-fast) var(--ease-standard);
}
button.chip { cursor: pointer; }
button.chip:hover { border-color: var(--accent-outline); }
/* Selected state is river, not marigold: a filter chip sits in the
   content, and clause C keeps the content free of brand warmth. White on
   river is 10.99:1. */
.chip.active { background: var(--accent-text); border-color: var(--accent-text); color: var(--on-accent-text); font-weight: 700; }
.chip-row { display: flex; flex-wrap: wrap; gap: 8px; }

.badge {
  display: inline-block; font-size: var(--text-2xs); padding: 4px 10px;
  border-radius: var(--radius-full); border: 1px solid var(--hairline);
  color: var(--ink-2); background: var(--surface-inset); white-space: nowrap;
}
.badge-mock { color: var(--status-watch); border-color: var(--status-watch); background: var(--status-watch-soft); }

/* Status chip — canonical new names */
.band-chip {
  display: inline-block; font-size: var(--text-2xs); font-weight: 700;
  padding: 3px 10px; border-radius: var(--radius-full); border: 1px solid;
  white-space: nowrap; line-height: 1.5;
}
/* A band is dark warm INK on a pale tint of its own hue, inside a border
   in the same ink. Clause B: this is the only warm ink in the system, so
   a warm word anywhere on the screen is a risk statement.
   We built the saturated-fill version first and measured it out: on this
   page the watch fill was 1.42:1 (a strong colour with no edge at all),
   and worse, a saturated warm fill is precisely what clause A reserves
   for the brand — it put the ladder and the marigold in the same visual
   register. Ink-on-tint is 5.4-5.8:1 in every band's worst case and is
   unmistakably not furniture. */
.band-ok, .band-good {
  background: var(--status-ok-soft); color: var(--status-ok); border-color: var(--status-ok);
}
.band-watch {
  background: var(--status-watch-soft); color: var(--status-watch); border-color: var(--status-watch);
}
.band-action {
  background: var(--status-action-soft); color: var(--status-action); border-color: var(--status-action);
}
.band-critical {
  background: var(--status-critical-soft); color: var(--status-critical); border-color: var(--status-critical);
  box-shadow: var(--glow-critical);
}
.band-fire {
  background: var(--status-fire-soft); color: var(--status-fire); border-color: var(--status-fire);
  box-shadow: var(--glow-fire); animation: dsEmberPulse 1.6s ease-in-out infinite;
}
@keyframes dsEmberPulse { 50% { filter: brightness(1.12); } }

/* Bare-text severity utility (no chip/background) — citizen.js already
   emits .sev-action / .sev-watch on stat numbers */
.sev-ok, .sev-good { color: var(--status-ok-fg); }
.sev-watch         { color: var(--status-watch-fg); }
.sev-action        { color: var(--status-action-fg); }
.sev-critical      { color: var(--status-critical-fg); }
.sev-fire          { color: var(--status-fire-fg); }

/* Status dot (map pins / compact indicators) — ALWAYS pair with a label */
.status-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; flex: none; }
.status-dot.ok, .status-dot.good { background: var(--status-ok); }
.status-dot.watch { background: var(--status-watch); }
.status-dot.action { background: var(--status-action); }
.status-dot.critical { background: var(--status-critical); box-shadow: var(--glow-critical); }
.status-dot.fire { background: var(--status-fire); box-shadow: var(--glow-fire); animation: dsEmberPulse 1.6s ease-in-out infinite; }

/* =====================================================================
   13. CARDS / PANELS / LISTS
   Solid layered surface + soft shadow + subtle top sheen. No blur.
   ===================================================================== */
.card {
  background: var(--surface-2);
  border: 1px solid var(--hairline); border-radius: var(--radius-lg);
  padding: var(--space-4); margin-bottom: var(--space-3);
  box-shadow: var(--shadow-card);
}
.panel {
  background: var(--surface-1); border: 1px solid var(--hairline);
  border-radius: var(--radius-lg); padding: var(--space-5);
}
.card-title { font-size: var(--text-md); font-weight: 700; margin-bottom: 2px; }
.card-sub { font-size: var(--text-xs); color: var(--ink-muted); }
.list { display: flex; flex-direction: column; gap: var(--space-2); }
.list-item {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; border-radius: var(--radius);
  background: var(--surface-1); border: 1px solid var(--hairline);
  min-height: 44px;
}
.kv { display: flex; justify-content: space-between; gap: 10px; font-size: var(--text-sm); padding: 3px 0; }
.kv b { font-variant-numeric: tabular-nums; }

/* =====================================================================
   14. STAT TILES (KPIs) + METERS
   ===================================================================== */
.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); gap: 10px; margin: var(--space-3) 0; }
.stat {
  background: var(--surface-2);
  border: 1px solid var(--hairline); border-radius: var(--radius);
  padding: 11px 13px; box-shadow: var(--shadow-1);
}
.stat .num { font-size: var(--text-xl); font-weight: 750; font-variant-numeric: tabular-nums; line-height: 1.15; }
.stat .lbl { font-size: var(--text-2xs); color: var(--ink-muted); margin-top: 2px; }

.meter { height: 8px; border-radius: var(--radius-full); background: var(--sand-300); overflow: hidden; }
/* Clause C: an unbanded meter is data with no state to report, so it
   runs in river, not in the brand. */
.meter > i { display: block; height: 100%; border-radius: inherit; background: var(--accent-text); transition: width var(--duration-slow) var(--ease-out); }
.meter.m-ok > i, .meter.m-good > i { background: var(--status-ok); }
.meter.m-watch > i { background: var(--status-watch); }
.meter.m-action > i { background: var(--status-action); }
.meter.m-critical > i { background: var(--status-critical); }
.meter.m-fire > i { background: var(--status-fire); }

/* =====================================================================
   15. TABLES (dense desktop views, Wave 2)
   ===================================================================== */
.table-wrap { overflow-x: auto; border: 1px solid var(--hairline); border-radius: var(--radius-lg); }
.table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); background: var(--surface-1); }
.table th {
  text-align: left; font-size: var(--text-2xs); font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-muted); padding: 10px 14px; border-bottom: 1px solid var(--hairline-strong);
  background: var(--surface-inset); position: sticky; top: 0;
}
.table td { padding: 11px 14px; border-bottom: 1px solid var(--hairline); vertical-align: middle; }
.table td.num, .table th.num { font-variant-numeric: tabular-nums; text-align: right; }
.table tbody tr { transition: background var(--duration-fast) var(--ease-standard); }
.table tbody tr:hover { background: var(--surface-hover); }
.table tbody tr:last-child td { border-bottom: 0; }

/* =====================================================================
   16. SHEETS (mobile bottom sheet) + MODAL (desktop centered dialog)
   ===================================================================== */
.sheet-backdrop {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  background: rgba(36, 23, 4, 0.48); backdrop-filter: blur(3px);
  opacity: 0; pointer-events: none; transition: opacity var(--duration-base) var(--ease-standard);
}
.sheet-backdrop.open { opacity: 1; pointer-events: auto; }
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-modal);
  max-width: 520px; margin: 0 auto;
  background: var(--surface-3);
  border: 1px solid var(--hairline); border-bottom: 0;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  padding: var(--space-5) var(--space-4) calc(var(--space-6) + env(safe-area-inset-bottom, 0px));
  box-shadow: var(--shadow-pop);
  transform: translateY(105%); transition: transform var(--duration-slow) var(--ease-out);
}
.sheet.open { transform: none; }
.sheet::before { content: ''; display: block; width: 40px; height: 4px; margin: -8px auto 14px; border-radius: 2px; background: var(--hairline-strong); }

/* Desktop centered modal — same backdrop, different panel shape */
.modal {
  position: fixed; z-index: var(--z-modal); top: 50%; left: 50%; transform: translate(-50%, -46%);
  width: min(520px, calc(100vw - 32px)); max-height: 86vh; overflow-y: auto;
  background: var(--surface-3); border: 1px solid var(--hairline); border-radius: var(--radius-lg);
  padding: var(--space-5); box-shadow: var(--shadow-pop);
  opacity: 0; pointer-events: none; transition: opacity var(--duration-base) var(--ease-standard), transform var(--duration-base) var(--ease-out);
}
.modal.open { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%); }

/* =====================================================================
   17. TOASTS / INLINE ALERTS
   ===================================================================== */
.toast {
  position: fixed; left: 50%; bottom: calc(var(--tabbar-h) + 18px); z-index: var(--z-toast);
  transform: translateX(-50%) translateY(16px);
  max-width: 86vw; padding: 12px 18px; border-radius: var(--radius);
  background: var(--surface-4); border: 1px solid var(--hairline-strong);
  color: var(--ink); font-size: var(--text-sm); font-weight: 600; text-align: center;
  box-shadow: var(--shadow-pop); opacity: 0; pointer-events: none;
  transition: opacity var(--duration-base), transform var(--duration-base) var(--ease-out);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* Inline banner alert — pairs a status color with text, for persistent
   (non-toast) notices: SLA breaches, offline banners, etc. */
.alert { display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border-radius: var(--radius); border: 1px solid; background: var(--surface-1); }
/* An alert is a band doing a paragraph's job — clause B: it is the band
   hue as INK plus a left rule, never a warm wash across the content. */
.alert { border-left-width: 4px; }
.alert-ok       { border-color: var(--status-ok);       background: var(--status-ok-soft); }
.alert-watch    { border-color: var(--status-watch);    background: var(--status-watch-soft); }
.alert-action   { border-color: var(--status-action);   background: var(--status-action-soft); }
.alert-critical { border-color: var(--status-critical); background: var(--status-critical-soft); }
.alert-fire     { border-color: var(--status-fire); background: var(--status-fire-soft); animation: dsEmberPulse 1.6s ease-in-out infinite; }

/* =====================================================================
   18. SKELETON LOADERS + EMPTY STATES
   ===================================================================== */
.skeleton { position: relative; overflow: hidden; background: var(--surface-inset); border-radius: var(--radius-sm); }
.skeleton::after {
  content: ''; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(74, 46, 10, 0.08), transparent);
  animation: dsShimmer 1.4s infinite;
}
@keyframes dsShimmer { 100% { transform: translateX(100%); } }
.skeleton-line { height: 12px; margin: 6px 0; }
.skeleton-line.w-60 { width: 60%; } .skeleton-line.w-40 { width: 40%; } .skeleton-line.w-80 { width: 80%; }
.skeleton-tile { height: 64px; border-radius: var(--radius); }

.empty { text-align: center; color: var(--ink-muted); padding: var(--space-8) var(--space-4); font-size: var(--text-sm); }
.empty .empty-ico { font-size: 30px; margin-bottom: 8px; opacity: .7; }

/* =====================================================================
   19. TOOLTIPS — CSS-only, hover + keyboard focus
   Usage: <span class="tooltip" data-tip="Explains the thing" tabindex="0">
   Prefer a real aria-describedby for anything conveying non-decorative
   info (documented in the playbook).
   ===================================================================== */
.tooltip { position: relative; }
.tooltip::after {
  content: attr(data-tip); position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%) translateY(2px);
  background: var(--surface-4); color: var(--ink); border: 1px solid var(--hairline-strong);
  font-size: var(--text-2xs); font-weight: 600; padding: 6px 10px; border-radius: var(--radius-sm);
  white-space: nowrap; box-shadow: var(--shadow-2); opacity: 0; pointer-events: none; z-index: var(--z-tooltip);
  transition: opacity var(--duration-fast) var(--ease-standard), transform var(--duration-fast) var(--ease-standard);
}
.tooltip:hover::after, .tooltip:focus-visible::after { opacity: 1; transform: translateX(-50%) translateY(0); }

/* =====================================================================
   20. TIMELINE / STARS / MISC
   ===================================================================== */
.timeline { display: flex; flex-direction: column; gap: 0; margin: var(--space-2) 0; }
.t-step { position: relative; padding: 0 0 14px 22px; font-size: var(--text-sm); color: var(--ink-muted); }
.t-step::before { content: ''; position: absolute; left: 5px; top: 5px; width: 9px; height: 9px; border-radius: 50%; background: var(--sand-300); border: 1px solid var(--hairline); }
.t-step::after { content: ''; position: absolute; left: 9px; top: 16px; bottom: 0; width: 1px; background: var(--hairline); }
.t-step:last-child { padding-bottom: 0; } .t-step:last-child::after { display: none; }
.t-step.done { color: var(--ink); }
.t-step.done::before { background: var(--status-ok); border-color: var(--status-ok); }

.stars { display: inline-flex; gap: 6px; }
.star { background: none; border: 0; cursor: pointer; font-size: 24px; line-height: 1; color: var(--sand-300); transition: color .12s, transform .12s; min-width: 32px; min-height: 32px; }
.star:hover { transform: scale(1.15); }
.star.on { color: var(--status-watch); }

/* =====================================================================
   21. ACCESSIBILITY — focus-visible, selection, scrollbar, reduced motion
   ===================================================================== */
:focus-visible {
  outline: 2px solid var(--accent-outline); outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--accent-ring);
  border-radius: var(--radius-sm);
}
.btn:focus-visible, .chip:focus-visible, .icon-btn:focus-visible { outline-offset: 3px; }

::selection { background: var(--accent-soft); color: var(--ink); }
[data-theme="dark"] ::selection { background: rgba(234, 132, 18, 0.32); color: var(--ink); }

/* Thin, unobtrusive scrollbars for dense desktop views (Wave 2) */
* { scrollbar-width: thin; scrollbar-color: var(--hairline-strong) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: var(--hairline-strong); border-radius: 999px; border: 2px solid transparent; background-clip: padding-box; }
*::-webkit-scrollbar-track { background: transparent; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
}

/* =====================================================================
   22. THEME-SPECIFIC CORRECTIONS
   A handful of effects only work in one polarity. Kept at the end so the
   component sections above stay theme-agnostic and readable.
   ===================================================================== */
/* A white top-sheen and a white shimmer sweep are both invisible on a
   white card; they are dark-ground devices and belong only there. */
[data-theme="dark"] .card { box-shadow: var(--shadow-card), inset 0 1px 0 rgba(255, 255, 255, 0.04); }
[data-theme="dark"] .skeleton::after { background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.06), transparent); }
[data-theme="dark"] .sheet-backdrop { background: rgba(0, 0, 0, 0.62); }
/* Sand needs the shadow to do the separating, because a white card on
   this page has almost no value step of its own (1.09:1). */
.card, .stat, .panel { box-shadow: var(--shadow-1); }
[data-theme="dark"] .meter { background: rgba(245, 241, 233, 0.10); }
[data-theme="dark"] .star { color: var(--surface-4); }
[data-theme="dark"] .t-step::before { background: var(--surface-4); }
[data-theme="dark"] .panel { box-shadow: none; }
