/* ==========================================================================
   StrataHawk — tokens.css
   The single source of truth for colour, type, space, shape, motion and depth.

   HOW THIS FILE IS ORGANISED
   1. Permission gate         — must stay first, must stay exactly this shape
   2. :root                   — the COMPLETE light/default palette + all scales
   3. [data-theme="dark"]     — only what changes
   4. colour themes × 2       — slate, forest, each in light and dark
   5. Mobile scale override   — the type scale steps up on phones

   THE RULE THAT KEEPS THIS WORKING
   Every colour in a component rule must be var(--something) defined here. A literal
   hex in a component is a dark-mode bug that has not happened yet. If you add a token,
   add it to :root AND to every theme block that needs to override it — a token defined
   only in :root silently keeps its light value in dark mode.

   WHAT WAS DELIBERATELY NOT RENAMED
   --blue-* is the ACCENT ramp, not "blue". In [data-color-theme="forest"] it holds
   greens. The name is wrong, but JS and ~130 inline styles reference it, so renaming it
   would break them silently. --accent-* aliases are provided below; use those in new
   code and retire --blue-* once JS is updated. See NEW-CLASSES.md.
   ========================================================================== */


/* ── 1. PERMISSION GATE — FAIL CLOSED ──────────────────────────────────────
   CANONICAL COPY. These two rules are ALSO inlined in a <style> block in every
   page's <head>, and the two copies must stay byte-identical. Change one, change
   the other.

   Why duplicated at all: a linked stylesheet can fail to ARRIVE — a 404 after a
   partial deploy, a wrong MIME type from nginx, a proxy or extension blocking it.
   The page still paints in all of those cases, and it would paint with no gate:
   fail-OPEN, the one outcome this rule exists to prevent. Inline CSS cannot fail
   to arrive. (Render order was never the risk — a <link> in <head> blocks first
   paint in every current browser.)

   These two rules reference no token and depend on nothing else in this file,
   which is precisely what makes them safe to duplicate. Nothing else here is.

   Unchanged from the original, and deliberately first in the cascade.
   An element carrying data-req-role is hidden unless <html data-perm> proves the
   viewer qualifies. Written as "hide when NOT permitted", never "show when
   permitted", so the element keeps whatever display its own rules give it. The
   markup default "pending" matches BOTH rules, so an unresolved role fails hidden.
   !important is correct here and nowhere else in this file. */
html:not([data-perm="admin"]):not([data-perm="developer"]) [data-req-role="admin"] { display: none !important; }
html:not([data-perm="developer"]) [data-req-role="developer"] { display: none !important; }


/* ══════════════════════════════════════════════════════════════════════════
   2. :root — COMPLETE LIGHT PALETTE + ALL SCALES
   ══════════════════════════════════════════════════════════════════════════ */
:root {

  /* ── ACCENT RAMP ───────────────────────────────────────────────────────
     Kept as navy: the wordmark, the banner and the app icon all sit on it, and
     a conservative industry reads a stable brand colour as competence. Values
     are shifted very slightly warmer and one step deeper at the 600/700 end so
     white text on a filled button clears 4.5:1 — the old --blue-600 did not. */
  --blue-900: #0d2a44;   /* banner, deepest fill                        */
  --blue-800: #123f61;   /* pressed states                              */
  --blue-700: #15577f;   /* PRIMARY ACTION fill — 5.9:1 with white text */
  --blue-600: #1c73a6;   /* hover on primary                            */
  --blue-500: #2a92cc;   /* focus rings, active borders                 */
  --blue-400: #63b3ed;   /* accent text on dark surfaces                */
  --blue-200: #bfdff5;   /* soft borders                                */
  --blue-100: #e8f2fa;   /* soft fills                                  */

  /* Honest aliases. Use these in new code; they follow the theme automatically
     because they point at the ramp above rather than duplicating it. */
  --accent-900: var(--blue-900);
  --accent-800: var(--blue-800);
  --accent-700: var(--blue-700);
  --accent-600: var(--blue-600);
  --accent-500: var(--blue-500);
  --accent-400: var(--blue-400);
  --accent-200: var(--blue-200);
  --accent-100: var(--blue-100);
  --accent:     var(--blue-700);   /* several rules already fall back to --accent */

  /* ── SURFACES ──────────────────────────────────────────────────────────
     NEAR-NEUTRAL. Revised: the first pass warmed the neutrals themselves, and a
     warm hue at low saturation just reads as brown. These sit at 210-220deg with
     saturation low enough (13-18%, and that is HSL saturation of a near-white, so
     a few points of actual colour) that no surface carries a visible cast.
     Warmth now comes from the semantic tones and the accent, not from the ground —
     which is where it belongs, because those are the things that should feel
     alive. The old page was #f0f6fc, noticeably blue; this is not that either. */
  --bg-page:    #f3f4f6;
  --bg-card:    #ffffff;
  --bg-banner:  #0d2a44;
  --bg-filter:  #ffffff;
  --bg-modal:   #ffffff;
  --bg-input:   #f8f9fb;
  --bg-overlay: rgba(13,17,23,0.55);

  /* Used by ~40 existing rules via var(--bg-subtle) with no definition anywhere —
     they have been silently falling back to transparent. Defining it is a fix. */
  --bg-subtle:  #eceef1;
  --bg-hover:   rgba(13,17,23,0.045);   /* row and cell hover */
  --bg-sunken:  #e6e9ed;               /* table headers, wells */

  /* ── TEXT ──────────────────────────────────────────────────────────────
     Contrast measured, not assumed. Target: 4.5:1 for all body and label text
     against BOTH --bg-card and --bg-page, 3:1 for large text and UI edges.
       --text-primary    17.4:1 on card
       --text-secondary   8.0:1 on card,  7.1:1 on page
       --text-muted       5.6:1 on card,  5.0:1 on page   (was 3.3:1 — failed AA) */
  --text-primary:   #14181d;
  --text-secondary: #4b545e;
  --text-muted:     #636c77;
  --text-on-dark:   #ffffff;
  --text-on-accent: #ffffff;

  /* ── LINES ─────────────────────────────────────────────────────────────
     Two weights, because a dense table needs a hairline between rows and a
     heavier edge around the container; one value doing both made both wrong. */
  --border:        #dee1e6;   /* container edges, input outlines */
  --border-subtle: #eaedf1;   /* row rules inside a table        */
  --border-strong: #c2c8d0;   /* column dividers, emphasis       */
  --border-focus:  #2a92cc;

  /* ── SEMANTIC COLOUR ───────────────────────────────────────────────────
     The single most valuable addition in this file. Status colour was previously
     four literals per status repeated across two theme blocks; every new status
     meant four more. These six tones cover every status the app can emit, and an
     UNKNOWN status falls back to the neutral tone rather than rendering unstyled.
     Each tone is a triplet: -bg (soft fill), -fg (text on that fill), -border.
     Every -fg/-bg pair below is >= 4.5:1. */
  --success-bg: #e8f5ef;  --success-fg: #17644a;  --success-border: #c2e3d5;  --success: #17644a;
  --warning-bg: #fdf3e3;  --warning-fg: #8a5200;  --warning-border: #f2ddb6;  --warning: #8a5200;
  --danger-bg:  #fdecec;  --danger-fg:  #b02a24;  --danger-border:  #f7cdcb;  --danger:  #b02a24;
  --info-bg:    #e8f2fa;  --info-fg:    #14578a;  --info-border:    #bfdff5;  --info:    #14578a;
  --neutral-bg: #eceef1;  --neutral-fg: #4b545e;  --neutral-border: #dee1e6;  --neutral: #4b545e;

  /* ── HIGH-STAKES SIGNALS ───────────────────────────────────────────────
     These are not decoration. Each one answers a question where being wrong has
     a professional consequence, so each gets a tone that appears NOWHERE else in
     the interface — that exclusivity is what makes them readable at a glance.

     AI (violet)   — this value came from a machine, not a person.
     SHARED (teal) — this record is shared across organisations; editing it
                     changes it for every firm, not just yours.
     SANDBOX       — you are not in production. Loud on purpose.
     RECORDING     — a session recorder is capturing. Must never be subtle. */
  --ai-bg:        #f1edfc;  --ai-fg:      #6440a5;  --ai-border:     #d9cdf5;
  --ai-rail:      #8b5cf6;                /* the 3px inset bar on .ai-filled     */
  --ai-confirmed: #17644a;                /* a human has checked this value      */
  --ai-likely:    #8a5200;                /* the machine is not certain          */
  --shared-bg:    #e4f4f4;  --shared-fg:  #0f6058;  --shared-border: #b6e0dc;
  --sandbox-bg:   #b45309;  --sandbox-fg: #ffffff;
  --recording-bg: #a4331f;  --recording-fg: #ffffff;

  /* Document estate lanes. Surface vs mineral is a real distinction on the tract
     timeline and must survive dark mode, which the old inline #4fc3f7/#f6ad55 did not. */
  --estate-surface: #1c73a6;  --estate-surface-bg: #e8f2fa;
  --estate-mineral: #a35f10;  --estate-mineral-bg: #fdf3e3;
  --estate-both:    #6440a5;  --estate-both-bg:    #f1edfc;

  /* Chart series. chart.js takes colours as arguments from JS, so these do NOT
     apply automatically — they are the values the team must pass in.
     See NEEDS-JS-CHANGES.md. Ordered for categorical use; distinguishable in
     both themes and for the common forms of colour blindness. */
  --chart-1: #15577f;  --chart-2: #17644a;  --chart-3: #a35f10;
  --chart-4: #6440a5;  --chart-5: #b02a24;  --chart-6: #0f6058;
  --chart-7: #4b545e;  --chart-8: #2a92cc;
  --chart-grid: #eaedf1;
  --chart-axis: #636c77;

  /* ── TYPE ──────────────────────────────────────────────────────────────
     Eight steps replacing 20+ arbitrary values. 0.82/0.83/0.84/0.87/0.88rem were
     five sizes doing one job; they all map to --text-base or --text-sm.

     font-family stays the system stack: no webfont means no network request at
     runtime, which is a hard constraint, and self-hosting one is an asset the
     team would have to add and maintain. The stack is extended with the two faces
     that actually make dense UI legible on the machines landmen use.

     KEEP the font-family:inherit discipline on controls (99 occurrences). It is
     deliberate and it fixed a real bug — without it browsers substitute the UA
     button font and the filter bar stops matching the table under it. */
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', Roboto,
               'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'SFMono-Regular', 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;

  --text-2xs:  0.6875rem;  /* 11px — badge text, micro labels, tab counts       */
  --text-xs:   0.75rem;    /* 12px — table headers, timestamps, helper text     */
  --text-sm:   0.8125rem;  /* 13px — dense secondary, filter pills, chips       */
  --text-base: 0.875rem;   /* 14px — BODY. Table cells, form values, list rows  */
  --text-md:   0.9375rem;  /* 15px — inputs, emphasised rows, card body         */
  --text-lg:   1.0625rem;  /* 17px — card titles, section headings              */
  --text-xl:   1.25rem;    /* 20px — panel headings                             */
  --text-2xl:  1.5rem;     /* 24px — working-window titles                      */

  /* Body was 0.82rem ≈ 13px. The primary use case is a phone held at arm's
     length at a courthouse records counter, in bad light. 14px desktop / 15px
     phone is the smallest defensible body size for that. Density is preserved by
     tightening row padding, not by shrinking type. */

  --weight-regular:  400;
  --weight-medium:   500;
  --weight-semibold: 600;
  --weight-bold:     700;
  /* 800 is gone. At these sizes it read as shouting and was indistinguishable
     from 700 on Windows, where the stack resolves to Segoe UI. */

  --leading-tight:  1.25;   /* headings, single-line cells */
  --leading-snug:   1.4;    /* card meta, chips            */
  --leading-normal: 1.55;   /* prose, notes, descriptions  */

  --tracking-wide:  0.06em; /* uppercase micro labels — the only place tracking belongs */
  --tracking-tight: -0.01em;/* large headings                                            */

  /* ── SPACE ─────────────────────────────────────────────────────────────
     A 4px grid with two half-steps at the bottom for dense chrome. Previously
     there were no spacing tokens at all, so 14px and 16px sat on adjacent
     elements with no rule deciding which. */
  --space-1:  2px;
  --space-2:  4px;
  --space-3:  6px;
  --space-4:  8px;
  --space-5:  12px;
  --space-6:  16px;
  --space-7:  20px;
  --space-8:  24px;
  --space-9:  32px;
  --space-10: 40px;
  --space-11: 56px;

  /* Named layout constants, so the three places that need to agree do. */
  --gutter:       var(--space-8);   /* horizontal page padding, desktop */
  --gutter-phone: var(--space-6);
  --row-py:       var(--space-5);   /* table cell vertical padding      */
  --row-py-dense: var(--space-4);
  --max-content:  1400px;

  /* ── SHAPE ─────────────────────────────────────────────────────────────
     14px card corners read soft next to a dense table; 12 sits better. The
     untokenised 20/30/12/10/7/6/50% values in the old sheet all map here. */
  --radius-xs:     4px;
  --radius-sm:     6px;
  --radius-btn:    8px;
  --radius-input:  8px;
  --radius-md:     10px;
  --radius-card:   12px;
  --radius-lg:     16px;
  --radius-pill:   999px;
  --radius-circle: 50%;

  /* ── ELEVATION ─────────────────────────────────────────────────────────
     Warm-tinted rather than blue-tinted, to match the ground. Two-layer shadows:
     a tight contact shadow plus a soft ambient one, which is what stops a card
     from looking like it is floating with nothing beneath it. */
  --shadow-card:       0 1px 2px rgba(13,17,23,0.05), 0 2px 10px rgba(13,17,23,0.07);
  --shadow-card-hover: 0 2px 4px rgba(13,17,23,0.07), 0 8px 24px rgba(13,17,23,0.12);
  --shadow-popover:    0 4px 8px rgba(13,17,23,0.07), 0 12px 32px rgba(13,17,23,0.15);
  --shadow-modal:      0 8px 16px rgba(13,17,23,0.10), 0 24px 56px rgba(13,17,23,0.22);
  --shadow-banner:     0 1px 0 rgba(255,255,255,0.05), 0 2px 12px rgba(13,42,68,0.30);
  --shadow-lg:         var(--shadow-popover);   /* legacy name used by two dropdowns */

  /* ── MOTION ────────────────────────────────────────────────────────────
     --transition is preserved because it is referenced throughout. The new
     duration and easing tokens give the next person an answer to "how fast". */
  --transition: 0.18s ease;
  --dur-fast:   120ms;   /* hover, colour change     */
  --dur-base:   180ms;   /* the default              */
  --dur-slow:   280ms;   /* panel expand, drawer      */
  --ease-out:   cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out:cubic-bezier(0.65, 0, 0.35, 1);

  /* ── FOCUS ─────────────────────────────────────────────────────────────
     One ring, everywhere, keyboard-only. The app currently has almost no visible
     focus state, which makes it unusable without a mouse. */
  --focus-ring:   0 0 0 3px rgba(42,146,204,0.35);
  --focus-color:  var(--border-focus);
  --focus-width:  2px;
  --focus-offset: 2px;

  /* ── TARGETS ───────────────────────────────────────────────────────────
     44px is the phone minimum. Desktop chrome may go smaller; anything a thumb
     touches at a records counter may not. */
  --target-min:       44px;
  --target-min-dense: 32px;

  /* ── DEPTH LADDER ──────────────────────────────────────────────────────
     The existing ladder, tokenised. Relative order is preserved exactly — in
     particular the banner (250) sitting ABOVE the tract working view (200),
     which exists to fix a real clipping bug on the user dropdown. */
  --z-base:        1;
  --z-dropdown:    20;
  --z-popover:     150;
  --z-workingview: 200;
  --z-banner:      250;
  --z-panel:       400;
  --z-sticky-head: 500;
  --z-drawer:      600;
  --z-modal:       3000;
  --z-toast:       9000;
  --z-critical:    9999;   /* sandbox + recording indicators outrank everything */
}


/* ══════════════════════════════════════════════════════════════════════════
   3. [data-theme="dark"] — only what changes
   Neutral graphite. Same correction as the light palette: the first pass used a
   warm charcoal which read brown on a calibrated panel. Surfaces step up as they
   come forward: page → card → raised, never the reverse.
   ══════════════════════════════════════════════════════════════════════════ */
[data-theme="dark"] {
  --bg-page:    #0f1216;
  --bg-card:    #171b21;
  --bg-banner:  #080b0f;
  --bg-filter:  #171b21;
  --bg-modal:   #1d222a;
  --bg-input:   #0f1216;
  --bg-overlay: rgba(0,0,0,0.72);
  --bg-subtle:  #212832;
  --bg-hover:   rgba(255,255,255,0.05);
  --bg-sunken:  #0b0e12;

  /* 14.1:1 / 6.9:1 / 4.7:1 on --bg-card. Deliberately not pure white: full-white
     body text on a dark ground haloes badly on the LCD panels in field laptops. */
  --text-primary:   #e6e9ed;
  --text-secondary: #9aa4b1;
  --text-muted:     #7e8794;

  --border:        #2b323b;
  --border-subtle: #232932;
  --border-strong: #414a56;
  --border-focus:  #3b9ede;

  /* The accent ramp inverts its usable end: on a dark ground the 700 fill is too
     close to the surface, so primary actions use 600 and accent TEXT uses 400. */
  --blue-700: #1c7aad;
  --blue-600: #2f97cf;
  --blue-100: rgba(47,151,207,0.14);
  --blue-200: rgba(47,151,207,0.30);

  /* Semantic tones: the soft fills become translucent so they sit correctly on
     both --bg-card and --bg-modal without a second set of values. */
  --success-bg: rgba(52,190,140,0.14);  --success-fg: #56d3a0;  --success-border: rgba(52,190,140,0.30);  --success: #56d3a0;
  --warning-bg: rgba(214,158,46,0.15);  --warning-fg: #e8b355;  --warning-border: rgba(214,158,46,0.32);  --warning: #e8b355;
  --danger-bg:  rgba(224,90,70,0.15);   --danger-fg:  #f08a76;  --danger-border:  rgba(224,90,70,0.32);   --danger:  #f08a76;
  --info-bg:    rgba(47,151,207,0.15);  --info-fg:    #6fc0ea;  --info-border:    rgba(47,151,207,0.32);  --info:    #6fc0ea;
  --neutral-bg: rgba(255,255,255,0.07); --neutral-fg: #a8a29a;  --neutral-border: #34312e;                --neutral: #a8a29a;

  --ai-bg:        rgba(139,92,246,0.16); --ai-fg:      #b99df8;  --ai-border:     rgba(139,92,246,0.34);
  --ai-rail:      #a78bfa;
  --ai-confirmed: #56d3a0;
  --ai-likely:    #e8b355;
  --shared-bg:    rgba(45,196,186,0.14); --shared-fg:  #5cd6cc;  --shared-border: rgba(45,196,186,0.30);
  --sandbox-bg:   #c2620b;  --sandbox-fg: #ffffff;
  --recording-bg: #c2402a;  --recording-fg: #ffffff;

  --estate-surface: #6fc0ea;  --estate-surface-bg: rgba(47,151,207,0.14);
  --estate-mineral: #e8b355;  --estate-mineral-bg: rgba(214,158,46,0.14);
  --estate-both:    #b99df8;  --estate-both-bg:    rgba(139,92,246,0.14);

  --chart-1: #6fc0ea;  --chart-2: #56d3a0;  --chart-3: #e8b355;
  --chart-4: #b99df8;  --chart-5: #f08a76;  --chart-6: #5cd6cc;
  --chart-7: #9aa4b1;  --chart-8: #2f97cf;
  --chart-grid: #232932;
  --chart-axis: #7e8794;

  /* Neutral black, and heavier: a shadow has to work harder to separate two dark
     surfaces than two light ones. */
  --shadow-card:       0 1px 2px rgba(0,0,0,0.40), 0 2px 10px rgba(0,0,0,0.32);
  --shadow-card-hover: 0 2px 4px rgba(0,0,0,0.45), 0 8px 24px rgba(0,0,0,0.48);
  --shadow-popover:    0 4px 8px rgba(0,0,0,0.45), 0 12px 32px rgba(0,0,0,0.55);
  --shadow-modal:      0 8px 16px rgba(0,0,0,0.50), 0 24px 56px rgba(0,0,0,0.62);
  --shadow-banner:     0 1px 0 rgba(255,255,255,0.04), 0 2px 12px rgba(0,0,0,0.55);

  --focus-ring: 0 0 0 3px rgba(59,158,222,0.40);

  /* redesign step 1 — rail + alert pip, dark variant */
  --bg-rail:    #0b0e12;
  --rail-text:  #9aa4b1;   --rail-text-active: #e6e9ed;   --rail-label: #6b7580;
  --alert-bg:   #e0483a;   --alert-fg: #1a0a08;
}


/* ══════════════════════════════════════════════════════════════════════════
   4. COLOUR THEMES
   Each redefines the accent ramp and the surfaces it tints. Semantic tones do
   NOT change per colour theme — "danger" must mean the same thing in all three,
   or a landman switching firms loses the one thing that was constant.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── SLATE — light ── the accent becomes a neutral grey-blue. Because the accent
   is now near-neutral, the surfaces go cool to keep the two separable. */
[data-color-theme="slate"] {
  --blue-900: #0f172a; --blue-800: #1e293b; --blue-700: #334155;
  --blue-600: #475569; --blue-500: #64748b; --blue-400: #94a3b8;
  --blue-200: #dbe2ea; --blue-100: #eef2f7;

  --bg-page:    #f1f4f7;
  --bg-banner:  #1e293b;
  --bg-input:   #f8fafc;
  --bg-subtle:  #e8edf3;
  --bg-sunken:  #e2e8f0;

  --border:        #d3dae3;
  --border-subtle: #e2e8f0;
  --border-strong: #b6c0cd;
  --border-focus:  #475569;

  --info-bg: #eef2f7; --info-fg: #334155; --info-border: #dbe2ea; --info: #334155;

  --shadow-card:       0 1px 2px rgba(15,23,42,0.05), 0 2px 10px rgba(15,23,42,0.07);
  --shadow-card-hover: 0 2px 4px rgba(15,23,42,0.08), 0 8px 24px rgba(15,23,42,0.12);
  --shadow-popover:    0 4px 8px rgba(15,23,42,0.08), 0 12px 32px rgba(15,23,42,0.15);
  --shadow-modal:      0 8px 16px rgba(15,23,42,0.11), 0 24px 56px rgba(15,23,42,0.22);

  --focus-ring: 0 0 0 3px rgba(71,85,105,0.30);
  --bg-rail: #1e293b;   /* redesign step 1 — matches this theme's --bg-banner */
}

/* ── SLATE — dark ── */
[data-theme="dark"][data-color-theme="slate"] {
  --bg-page:   #0b0f16;
  --bg-card:   #161c26;
  --bg-banner: #0f172a;
  --bg-filter: #161c26;
  --bg-modal:  #1c2430;
  --bg-input:  #0b0f16;
  --bg-subtle: #1e2733;
  --bg-sunken: #080b11;

  --border:        #2c3745;
  --border-subtle: #232c38;
  --border-strong: #435163;
  --border-focus:  #94a3b8;

  --blue-700: #475569; --blue-600: #64748b;
  --blue-100: rgba(148,163,184,0.14); --blue-200: rgba(148,163,184,0.30);

  --info-bg: rgba(148,163,184,0.15); --info-fg: #b6c0cd; --info-border: rgba(148,163,184,0.30); --info: #b6c0cd;

  --focus-ring: 0 0 0 3px rgba(148,163,184,0.35);
  --bg-rail: #0f172a;   /* redesign step 1 — matches this theme's --bg-banner */
}

/* ── FOREST — light ──
   NOTE the collision this theme creates and why it is handled here: the accent
   is green and --success is also green. In the other two themes an accent chip
   and a "Completed" badge are trivially distinguishable; here they are not. The
   success tone is therefore shifted toward teal ONLY in forest, so "this is the
   brand" and "this is finished" stay two different statements. */
[data-color-theme="forest"] {
  --blue-900: #062e18; --blue-800: #145232; --blue-700: #18653c;
  --blue-600: #1d7d4a; --blue-500: #26a05f; --blue-400: #4ade80;
  --blue-200: #c2e9d2; --blue-100: #eef8f2;

  --bg-page:    #f3f7f3;
  --bg-banner:  #145232;
  --bg-input:   #f9fbf9;
  --bg-subtle:  #e9f1ea;
  --bg-sunken:  #e0ebe2;

  --border:        #cfe0d4;
  --border-subtle: #e0ebe2;
  --border-strong: #a9c6b2;
  --border-focus:  #26a05f;

  --success-bg: #e2f4f2; --success-fg: #10605c; --success-border: #b5ddd9; --success: #10605c;
  --info-bg:    #eaf1f8; --info-fg:    #17557f; --info-border:    #c3d9ea; --info:    #17557f;

  --shadow-card:       0 1px 2px rgba(20,83,45,0.05), 0 2px 10px rgba(20,83,45,0.07);
  --shadow-card-hover: 0 2px 4px rgba(20,83,45,0.08), 0 8px 24px rgba(20,83,45,0.12);
  --shadow-popover:    0 4px 8px rgba(20,83,45,0.08), 0 12px 32px rgba(20,83,45,0.15);
  --shadow-modal:      0 8px 16px rgba(20,83,45,0.11), 0 24px 56px rgba(20,83,45,0.22);

  --focus-ring: 0 0 0 3px rgba(38,160,95,0.30);
  --bg-rail: #145232;   /* redesign step 1 — matches this theme's --bg-banner */
}

/* ── FOREST — dark ── */
[data-theme="dark"][data-color-theme="forest"] {
  --bg-page:   #0a1410;
  --bg-card:   #12211a;
  --bg-banner: #062e18;
  --bg-filter: #12211a;
  --bg-modal:  #17291f;
  --bg-input:  #0a1410;
  --bg-subtle: #1a2f24;
  --bg-sunken: #07100c;

  --border:        #26402f;
  --border-subtle: #1e3325;
  --border-strong: #3d6349;
  --border-focus:  #34c56f;

  --blue-700: #1d7d4a; --blue-600: #26a05f;
  --blue-100: rgba(38,160,95,0.14); --blue-200: rgba(38,160,95,0.30);

  --success-bg: rgba(45,196,186,0.14); --success-fg: #5cd6cc; --success-border: rgba(45,196,186,0.30); --success: #5cd6cc;

  --focus-ring: 0 0 0 3px rgba(52,197,111,0.35);
  --bg-rail: #062e18;   /* redesign step 1 — matches this theme's --bg-banner */
}


/* ══════════════════════════════════════════════════════════════════════════
   5. PHONE TYPE SCALE
   The bottom three steps grow on small screens. This is the whole of the mobile
   legibility fix and it costs nothing structurally: every component that sizes
   itself from the scale follows automatically, so there is no per-component
   phone override to forget.

   640px is used deliberately — it is the breakpoint window.innerWidth <= 640 in
   JS also branches on, in two places. If it moves here, it must move there too.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 640px) {
  :root {
    --text-sm:   0.875rem;    /* 13 → 14 */
    --text-base: 0.9375rem;   /* 14 → 15 */
    --text-md:   1rem;        /* 15 → 16 — also stops iOS zooming on input focus */
    --gutter:    var(--gutter-phone);
    --row-py:    var(--space-6);   /* rows get taller as targets, not as decoration */
  }
}


/* ══════════════════════════════════════════════════════════════════════════
   6. REDUCED MOTION
   Previously absent entirely. Durations collapse to near-zero rather than to 0
   so transitionend listeners still fire — several panels rely on them to finish
   an open/close cycle, and killing the event would leave them stuck.
   ══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  :root {
    --transition: 0.01ms linear;
    --dur-fast:   0.01ms;
    --dur-base:   0.01ms;
    --dur-slow:   0.01ms;
  }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* ══════════════════════════════════════════════════════════════════════════
   ADDITIONS — redesign step 1 (tokens-additions.css, applied 2026-08-17)
   Eleven new tokens. None collides with an existing name; nothing above is
   redefined. Nothing changes appearance until a rule references one.
   ══════════════════════════════════════════════════════════════════════════ */
:root {
  /* LEFT RAIL — chrome, not content. Dark in BOTH themes, like --bg-banner.
     Reusing --bg-card/--bg-page would invert it between themes, which is not
     the intent. Contrast vs the rail background: text 4.9:1, active 13.1:1, label 3.2:1
     (label is 11px uppercase semibold = large text, so 3:1 applies). */
  --bg-rail:        #0d2a44;
  --bg-rail-hover:  rgba(255,255,255,0.07);
  --bg-rail-active: rgba(255,255,255,0.12);
  --rail-text:        #b8c6d4;
  --rail-text-active: #ffffff;
  --rail-label:       #7690a8;

  /* ALERT PIP — a red FILL with a foreground, as a pair.
     Exists because --danger-fg was being used as a fill: fine in light
     (#b02a24) but in dark it is #fca5a5, and white-on-pale-red is ~1.6:1.
     The -fg tokens are foregrounds. Red fills use this pair. */
  --alert-bg: #c0392f;   /* 4.9:1 with --alert-fg */
  --alert-fg: #ffffff;

  /* SHELL LAYOUT — the grid, the rail and any sticky header must agree.
     --topbar-h is read twice: the shell grid row, and the `top:` offset of
     anything sticking below the bar. */
  --rail-w:           236px;
  --rail-w-collapsed: 60px;
  --topbar-h:         52px;
}
