/* ── Senshin Design Tokens ──────────────────────────────────────────────────
   Canonical source of truth for all colours, spacing, and typography.
   All pages must use these tokens. No hardcoded colours allowed.
   Design philosophy: The Dojo — Japanese martial arts discipline applied
   to project delivery. Warm materials palette (kinari, sumi, washi, tatami).
   Last updated: 2026-04-24
───────────────────────────────────────────────────────────────────────────── */

:root {
  /* ── Sensei sidebar ─────────────────────────────────────────────────── */
  --sidebar-w: 280px;
  --sidebar-w-wide: 340px;

  /* ── Brand — deep indigo (品格, dignity) ────────────────────────────── */
  --brand-primary: #1B4F72;
  --brand-primary-hover: #154360;
  /* Text/marks sitting ON brand-primary. Not theme-dependent: the brand blue
     is dark in both light and dark mode, so this stays white either way. */
  --on-brand: #ffffff;
  --brand-secondary: #7C3AED; /* AI voice purple — DS-2 exact (was #7D3C98) */
  /* ── The Playbook (pedagogy layer) — TASK_93 ──────────────────────────
     The baseline lever + coach affordance. Light values here; dark overrides
     in both dark blocks below. Lever track = brand; knob = on-brand ink;
     commit-line = a subtle threshold marker on the track. Coach glyph = a
     low-alpha Sensei-purple wash; the readable accent is --playbook-accent. */
  --lever-track: #1B4F72;
  --lever-knob: #ffffff;
  --lever-commit: rgba(27, 79, 114, 0.35);
  --coach-glyph: rgba(124, 58, 237, 0.12);
  /* Sensei-purple accent for Playbook TEXT (documentary state, coach labels).
     Theme-aware: #7C3AED is 3.18:1 on the dark card (fails AA), so dark lifts
     to #b39ddb (7.56:1) — the value the Design DC uses. */
  --playbook-accent: #7C3AED;
  /* Brand lockup strapline ("SEE · SHAPE · LEAD").
     Was `currentColor` at opacity .62, which computed to #6f8fa5 on the light
     nav — 3.21:1, under the 4.5:1 AA floor, on every page. Opacity is not a
     colour (CLAUDE.md Q3); the token carries the contrast guarantee instead.
     #597385 on --bg-nav #f8f8f7 = 4.69:1. Dark override below = 4.72:1.
     NOTE: the brand guide's specified #7E8FA1 is 3.12:1 and cannot be used. */
  /* --brand-strap lives in tokens.css — do not redefine :root */

  /* ── Not-attested marker (TASK_97, Alex 2026-07-29) ───────────────────
     A fragment field nobody has confirmed shows the ABSENCE, stated — an
     empty slot reads as "fine". Warm grey, deliberately: not RAG (amber
     would imply a project problem) and not AI purple (that is Senshin's
     Read only). It is a fact about the data, not a status.
     Measured by tools/audit_contrast.py, not by arithmetic in a comment:
     #3a3a37 on --bg1 = 11.41:1, on --bg2 = 10.74:1 — both clear the ≥7:1
     the accessibility discipline requires of 9px uppercase text. */
  --not-attested-fg: #3a3a37;
  --not-attested-bg: rgba(0, 0, 0, .04);
  --not-attested-br: #e8e8e5;

  /* ── Brand navy (primary button / kanji mark colour) ──────────────── */
  --color-navy: #1B4F72;
  --color-navy-hover: #154360;

  /* ── Dojo material palette ─────────────────────────────────────────── */
  --kinari:  #F5F0E8;   /* 生成り raw silk — page backgrounds, card surfaces  */
  --sumi:    #2C2C2A;   /* 墨 ink black — primary text, warm not clinical     */
  --sumi-light: #4A4A47; /* diluted ink — secondary text                       */
  --washi:   #E8E4DC;   /* 和紙 paper — borders, dividers, subtle backgrounds */
  --tatami:  #D4CFC4;   /* 畳 straw mat — inactive/disabled states            */
  /* --accent is owned by tokens.css (:root navy #1B4F72). Do not redefine
     it here. Platinum for orb / lockup is --platinum. */

  /* ── Button tokens (canonical — 2026-04-16 refined aesthetic) ─────────
     Buttons blend into the UI. Primary is refined filled, not chunky.
     Secondary is ghost/outline. No large black/navy casings — ever.
     ──────────────────────────────────────────────────────────────────── */
  --btn-primary-bg:         var(--brand-primary);  /* Deep indigo */
  --btn-primary-text:       #ffffff;
  --btn-primary-hover-bg:   var(--brand-primary-hover);
  --btn-secondary-bg:       transparent;
  --btn-secondary-text:     var(--brand-primary);
  --btn-secondary-border:   var(--br);
  --btn-secondary-hover-bg: rgba(27,79,114,.06);
  --btn-tertiary-text:      var(--t2);
  --btn-destructive-bg:     #7a2f2f;   /* Muted brick — NEVER --color-red */
  --btn-destructive-text:   #ffffff;

  /* ── AI state colours (NEVER red for AI output) ──────────────────── */
  --ai-observe:   var(--t3);           /* Grey — surface only */
  --ai-advise:    var(--color-blue);   /* Blue */
  --ai-challenge: var(--color-purple); /* Purple — the sensei speaks */

  /* ── Secondary — chart series 2+, schemes, tags. NEVER status.
     Canon values. Kachi never beside accent in charts; Jade never where
     it could read "on track". Series order: accent → jade → ember → ume. */
  --jade:  #0C6B55;
  --ume:   #8B1852;
  --ember: #7A3820;
  --kachi: #3F4B7D;
  --suna:  #9A7B4F;

  /* ── RAG — signal whispers, not alarm bells ────────────────────────
     DESIGN.md owns the hue: rag-amber is #D4813A (amber), never the brown
     text-safe ink. Ink is --rag-*-ink / --color-* for body text on paper
     (canon 29b). Fills, dots, bars, and RAG-as-type use --rag-*. */
  --rag-red:    #C0392B;
  --rag-red-bg: rgba(192,57,43,.08);
  --rag-green-bg: var(--color-green-bg);
  --rag-red-ink: #b02f22;
  --rag-amber:  #D4813A;
  --rag-amber-ink: #8a5425;
  --rag-green:  #1F8A5B;
  --rag-green-ink: #176b46;
  /* RGB triplets for rgba() composition in page code (DS-2 canon values) */
  --rag-red-rgb:   192,57,43;
  --rag-amber-rgb: 212,129,58;
  --rag-green-rgb: 31,138,91;

  /* ── Pill / chip geometry ─────────────────────────────────────────── */
  --pill-status-h:  24px;  /* Status chips: 24px height */
  --pill-status-px: 12px;  /* 12px horizontal padding */
  --pill-action-h:  32px;  /* Action chips: 32px height */
  --pill-action-px: 12px;

  /* ── Pill density (canonical — use in all pill/badge/chip CSS) ─────
     §24: RAG/status pills are screen furniture — 9–10px text, full
     border-radius, tight. One change here propagates to every
     token-driven pill (.pd-pill, components.css .pill/.ss-pill, …). */
  --pill-pad-y:     2px;   /* Vertical padding inside pill */
  --pill-pad-x:     8px;   /* Horizontal padding inside pill */
  --pill-font:      10px;  /* Pill body font size (§24: 9–10px) */
  --pill-radius:    999px; /* §24: full border-radius on status pills */
  --pill-height-xs: 18px;  /* Tightest pill (Discuss, micro-badge) */
  --pill-height-sm: 20px;  /* Standard status/RAG pill — compact */

  /* ── Inline form field density ────────────────────────────────────── */
  --field-height-sm: 32px; /* Compact form field height (ceremonies row etc.) */
  --field-pad-y:     6px;
  --field-pad-x:     10px;
  --field-font-sm:   13px;

  /* ── Text — warm grey scale (DS-2 exact, locked 2026-07-04) ────────── */
  --t1: #1a1a18;        /* Primary text — headings, active labels */
  --t2: #3a3a37;        /* Secondary text — body, button text */
  --t3: #6b6b67;        /* Tertiary text — labels, meta (WCAG AA 4.5:1) */
  --t4: #6b6b67;   /* 4.5:1+ on white, bg2 and bg3 (POCKET-005; was 70706d, 4.3:1 on bg3) */        /* Faint — timestamps, breadcrumb parents */
  --t5: #b0b0aa;        /* Disabled / hint */

  /* ── Backgrounds — Kinari (raw silk warmth) ────────────────────────── */
  --bg1: #ffffff;       /* Card / elevated surface — white on paper */
  --bg2: #f8f8f7;       /* Card / section background — barely warm */
  --bg3: #f0efed;       /* Hover / active state — light warm grey */
  --bg-nav: #f8f8f7;    /* Nav sidebar — light surface */

  /* ── Borders — (DS-2 exact, locked 2026-07-04) ─────────────────────── */
  --br: #e8e8e5;        /* Default border — inputs, cards, topbar, nav */
  --border-inner: #f0f0ee; /* Subtle internal dividers within components */
  --br-hover: #D4CFC4;  /* Hover border — tatami */
  --br-focus: #1B4F72;  /* Focus ring — brand indigo */

  /* ── The Brief — surface + cover-art tokens (TASK_62) ───────────────
     Warm presentation surfaces (canvas/stage/rail) get dark overrides
     below so the Brief follows the theme. Cover-art shades are a FIXED
     dark treatment (spec 2f) — like the brand mark, they do NOT invert. */
  --brief-canvas: #F7F5F0;        /* editor canvas — warm paper */
  --brief-stage: #EEECE7;         /* slide stage backdrop */
  --brief-chip-bg: #F4F4F2;       /* chips + segmented control */
  --brief-rail: #ECEAE5;          /* thumb bars + skeleton shimmer */
  --brief-slide-border: #E0DED9;  /* slide card edge */
  --brief-fragment-bg: #F4F2ED;   /* fragment tile fill */
  --brief-cover-bg: #0A0908;      /* cover / dark slide — fixed */
  --brief-cover-orb-hi: #12244A;  /* cover orb radial highlight — fixed */
  --brief-cover-orb-lo: #070B16;  /* cover orb radial base — fixed */
  --brief-cover-text: #CDD6E6;    /* cover subtitle on dark — fixed */

  /* ── Signal colours — the belt system ────────────────────────────────
     Critical=red belt, Attention=amber, Advise=blue, On track=green,
     Challenge=purple (the sensei speaks).
     2026-04-21: darkened text-on-bg tokens for WCAG 2.1 AA (4.5:1).
     2026-04-24: signal colours are whispers (left-border accents, pill
     fills at 12% opacity). Never flood a card background with colour.
     ──────────────────────────────────────────────────────────────────── */
  --color-red: #b02f22;   /* light status text/fill, exceeds AA — 5.9:1 on surface (was #C0392B, 4.99:1) */
  --color-red-bg: rgba(192,57,43,.08);
  --color-red-light: rgba(192,57,43,.12);
  --color-amber: #8a5425;   /* light amber, exceeds AA — 5.7:1 on surface (was #9a5e2a, 4.81:1) */
  --color-amber-bg: rgba(212,129,58,.08);
  --color-amber-light: rgba(212,129,58,.12);
  --color-green: #176b46;   /* light green, exceeds AA — 6.0:1 on surface (was #1c7b51, 4.82:1) */
  --color-green-bg: rgba(31,138,91,.08);
  --color-green-light: rgba(31,138,91,.12);
  --color-blue: #256a99;   /* light blue text/accent, exceeds AA — 5.35:1 on surface (was #2b7cb3, 4.16:1). A single blue can't clear light AND dark, so the dark blocks override it. */
  --color-blue-bg: rgba(46,134,193,.08);
  --color-blue-light: rgba(46,134,193,.12);
  --color-purple: #7C3AED;
  --color-purple-bg: rgba(124,58,237,.08);
  --color-purple-light: rgba(124,58,237,.12);

  /* ── Plan-on-a-Page / Gantt chart hues ─────────────────────────────────
     De-hardcoded from plan-on-page-renderer.js (2026-06-17) so the timeline
     theme-flips its canvas. VALUES are Alex's signed chart palette verbatim
     (#16a34a milestone/complete 2026-06-05, #5b6470 dep 2026-06-06, etc.) —
     tokenised for the no-hardcoded rule WITHOUT changing the signed look.
     The status hues stay vivid on a dark canvas (standard data-viz). Any
     change to these VALUES is a brand decision needing Alex's sign-off. ── */
  --gantt-complete: #16a34a;   /* complete bar + milestone diamond (signed) */
  --gantt-progress: #2563eb;   /* in-progress bar */
  --gantt-risk:     #d97706;   /* at-risk / pending bar */
  --gantt-late:     #dc2626;   /* late / blocked bar · today line · LATE tag */
  --gantt-todo:     #9ca3af;   /* not-started bar */
  --gantt-critical: #f87171;   /* soft coral — critical-path bar fill */
  --gantt-baseline: #d1cdc5;   /* baseline ghost — "where we planned to be" */
  --gantt-summary:  #1a1a1a;   /* MS-Project summary bracket */
  --gantt-dep:      #5b6470;   /* dependency arrow — medium slate */
  /* Full Gantt instrument (approved Gantt.dc.html, Pass 1 — 2026-08-15).
     Aliases keep the renderer semantic and inherit theme-aware parent tokens. */
  --gantt-planned:       var(--brand-primary);
  --gantt-progress-fill: rgb(var(--rag-amber-rgb));
  --gantt-milestone:     var(--gantt-complete); /* Alex 2026-08-25: bright green diamonds */
  --gantt-today:         var(--brand-primary);

  /* ── Compact chart fills (CANON 2026-07-21 — Charts & Multi-Colour Visuals).
     Status-token-DERIVED via color-mix, so they flip with the theme and can
     never drift from the status palette. Bars and legends must consume the
     SAME tokens. (--gantt-* above is the full renderer's separately-signed
     palette — a distinct decision; do not merge the two.) ── */
  --chart-complete:    color-mix(in srgb, var(--color-green) 65%, transparent);
  --chart-in-progress: color-mix(in srgb, var(--brand-primary) 60%, transparent);
  --chart-on-hold:     color-mix(in srgb, var(--color-amber) 60%, transparent);
  --chart-not-started: color-mix(in srgb, var(--t4) 50%, transparent);
  --chart-series-1:    var(--brand-primary);
  --chart-series-2:    var(--jade);
  --chart-series-3:    var(--ember);
  --chart-series-4:    var(--ume);

  /* ── Spacing ───────────────────────────────────────────────────────── */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 12px;
  --space-lg: 16px;
  --space-xl: 24px;
  --space-2xl: 32px;
  --space-3xl: 48px;

  /* ── Border radius ─────────────────────────────────────────────────── */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-pill: 999px;

  /* ══════════════════════════════════════════════════════════════════
     TYPOGRAPHY SCALE — CANONICAL, DO NOT DRIFT
     ──────────────────────────────────────────────────────────────────
     Single source of truth for every font-size, font-weight, and
     line-height across Senshin. Components MUST reference these
     tokens — never hard-code font-size: 12px or font-weight: 800.
     A lint guard in scripts/lint-type-scale.sh enforces this on CI.

     RULES
       1. Seven sizes. No off-ramp values (no 10px, 15px, 17px, 22px).
       2. Three weights only — 400 / 600 / 700. No 300/500/800.
       3. Three line-heights — tight / normal / relaxed.
       4. 11px is the readability floor (uppercase caption labels only).
          Body floor is 13px desktop, 14px phone.
       5. Form inputs are 16px on phone to prevent iOS zoom-on-focus.
       6. Density variants scale SPACING and LINE-HEIGHT only — never
          size. Resizing type via calc(base * 0.857) produces half-
          pixel blur and is forbidden.

     RATIO
       14px body anchor × 1.125 modular scale.
       11 • 12 • 13 • 14 • 16 • 20 • 28 • 36
     ══════════════════════════════════════════════════════════════════ */
  --font-family: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'Geist Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* Canonical sizes — role tokens */
  --fs-caption: 11px;   /* Uppercase tracked labels only. Readability floor. */
  --fs-small:   12px;   /* Meta, tertiary metadata, chip body, table cells in compact tables */
  --fs-body:    13px;   /* Dense body, dense table cells, supporting copy */
  --fs-base:    14px;   /* DEFAULT body, buttons, inputs, controls, nav items */
  --fs-lg:      16px;   /* Lead paragraph, subheading body, mobile body floor */
  --fs-xl:      20px;   /* h3, card titles */
  --fs-2xl:     28px;   /* h2, page-section titles, metric values */
  --fs-3xl:     36px;   /* h1, page titles */

  /* Canonical weights — only these three */
  --fw-regular:  400;
  --fw-semibold: 600;   /* Labels, buttons, table headers, UI chrome, emphasis */
  --fw-bold:     700;   /* Headings h1–h3 only */

  /* Canonical line-heights — only these three */
  --lh-tight:   1.15;   /* Headings */
  --lh-normal:  1.5;    /* Default body */
  --lh-relaxed: 1.65;   /* Long-form prose, onboarding copy */

  /* Back-compat aliases (being deprecated — do not reference in new code) */
  --font-size-xs:   var(--fs-caption);
  --font-size-sm:   var(--fs-small);
  --font-size-base: var(--fs-body);
  --font-size-md:   var(--fs-base);
  --font-size-lg:   var(--fs-lg);
  --font-size-xl:   var(--fs-xl);
  --font-size-2xl:  var(--fs-2xl);
  --font-size-3xl:  var(--fs-3xl);
  --line-height:    var(--lh-normal);

  /* ── Shadows — ink on paper (minimal, warm) ──────────────────────── */
  --shadow-sm: 0 1px 3px rgba(44,44,42,.06);
  --shadow-md: 0 2px 8px rgba(44,44,42,.08);
  --shadow-lg: 0 4px 16px rgba(44,44,42,.10);
  /* Finance Landing v4 DC names (e1/e2/e3, 3/5 radii). Hex stays in this
     token block. Pages consume the names — never a second hex. */
  --e1: 0 1px 6px rgba(0, 0, 0, 0.06);
  --e2: 0 4px 16px rgba(0, 0, 0, 0.08);
  --e3: 0 10px 44px rgba(0, 0, 0, 0.12);
  --radius-3: 3px;
  --radius-5: 5px;
  --strap: var(--brand-strap);

  /* ── Touch targets ─────────────────────────────────────────────────── */
  --touch-min: 44px;
  --touch-icon: 36px;

  /* ── Component dimensions — refined button scale (2026-04-16) ─────
     Buttons are screen furniture, not visual blocks.
     Primary: refined filled, small padding, medium weight.
     Secondary: ghost/outline or text-link.
     No large black casings — ever.
     ──────────────────────────────────────────────────────────────── */
  --comp-btn-h: 30px;
  --comp-btn-r: 6px;
  --comp-btn-px: 10px;
  --comp-btn-fs: 12px;              /* tighter — screen furniture, not blocks */
  --comp-btn-fw: 500;               /* medium weight, not bold */
  --comp-toggle-w: 36px;   /* compact/refined 2026-07-13 (was 44) */
  --comp-toggle-h: 20px;   /* was 24 */
  --comp-toggle-r: 10px;   /* was 12 */
  --comp-toggle-knob: 14px;/* was 18 */
  --comp-card-r: 10px;
  --comp-card-p: 16px;
  --comp-card-shadow: 0 1px 3px rgba(0,0,0,0.04), 0 1px 2px rgba(0,0,0,0.06);
  --comp-input-h: 40px;
  --comp-input-r: 8px;
  --comp-badge-r: 999px;
  --nav-item-h: 40px;
  --nav-item-r: 8px;

  /* ── Transitions ───────────────────────────────────────────────────── */
  --transition-fast: 0.12s ease;
  --transition-normal: 0.2s ease;
  --transition-slow: 0.3s ease;

  /* ── Cinematic shell — dark nav/header/footer (homepage energy) ──── */
  --shell-bg:         #0f0f11;   /* Near-black matching homepage */
  --shell-bg-subtle:  #161618;   /* Slightly elevated surface within dark shell */
  --shell-border:     rgba(255,255,255,0.06);  /* Hairline borders */
  --shell-text:       #E5E4E2;   /* Titanium — readable on dark */
  --shell-text-muted: #96928c;   /* Dimmed labels — was 8A8680, 4.21:1 */
  --shell-text-bright:#E8E4DC;   /* Washi white — headings, active items */
  --shell-hover:      rgba(255,255,255,0.05);  /* Hover wash */
  --shell-active-bg:  rgba(229,228,226,0.12);   /* Gold wash for active state */
  --shell-active-text:#E5E4E2;   /* Gold — active nav item */
  --shell-gold:       #E5E4E2;   /* Brand gold accent */
  --shell-gold-dim:   rgba(229,228,226,0.55);   /* Muted gold for borders */

  /* ── Zen shell tokens — nav + header chrome ──────────────────────── */
  --zen-ink:          var(--sumi);
  --zen-ink-faint:    rgba(44, 44, 42, 0.06);
  --zen-ink-medium:   rgba(44, 44, 42, 0.14);
  --zen-wash-top:     rgba(44, 44, 42, 0.035);
  --zen-divider-col:  rgba(44, 44, 42, 0.13);
  --zen-accent-warm:  rgba(229, 228, 226, 0.55);
  --zen-texture-op:   0.028;

  /* ── Zen palette — pervasive, not landing-only ──────────────────── */
  --zen-rice:      var(--kinari);
  --zen-parchment: #EDE6D6;
  --zen-mist:      var(--washi);
  --zen-stone:     var(--sumi-light);
  --zen-earth:     #b09070;
  --zen-gold:      var(--platinum);
  --zen-sage:      #7a9b76;

  /* ── Decision layer tokens ───────────────────────────────────────── */
  --dl-bg:            #fafbff;
  --dl-border:        rgba(37, 99, 235, 0.12);
  --dl-action-red:    #dc2626;
  --dl-action-amber:  #d97706;
  --dl-action-purple: #7c3aed;
  --dl-action-green:  #16a34a;

  /* ── Font stacks (display + mono for landing/AI) ─────────────────── */
  --font-display: 'Geist', -apple-system, sans-serif;
  --font-mono-ai: 'Geist Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* ── Dojo motion — ink settling, not rubber bouncing ────────────── */
  --motion-fade: 200ms ease-out;
  --motion-settle: 300ms ease-out;
  --motion-lift: 200ms ease;

  /* ══════════════════════════════════════════════════════════════════
     UNDEFINED-VAR SHIM (added 2026-05-12 after design-sweep audit)
     ──────────────────────────────────────────────────────────────────
     The 1ee698a tokenisation sweep left ~124 CSS variables referenced
     but never declared, with ~440 combined call sites across the app.
     Each one below aliases a legacy short-name to its canonical token
     so `var(--name)` resolves instead of falling back to the default.
     Do NOT add new uses of these — they exist only to stop the bleed.
     ══════════════════════════════════════════════════════════════════ */
  --line:     var(--br);                   /* 106 uses — border short-name */
  --ss-line:  var(--br);                   /*  18 uses — alt border name   */
  --surface:  #F7F5F0;                     /* Page paper — cards stay --bg1/#fff */
  --purple:   var(--color-purple);         /*  40 uses — accent shorthand  */
  --grey-mid: var(--t3);                   /*  32 uses — divider grey      */
  --r:        var(--radius-sm);            /*  23 uses — radius shorthand  */
  --s1: 4px;                                /* matches --space-xs / --s-1   */
  --s2: 8px;                                /*  86 uses — spacing shorthand */
  --s3: 12px;                               /*  19 uses                    */
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;
  --dur:  0.12s;                            /*  29 uses — transition dur    */
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);   /*  26 uses — canonical easing  */

  /* STYLIST-007 (2026-10-01): names used across pages with only a hard-coded
     fallback and no definition. Each aliases an existing themed token so the
     var() resolves (and follows dark mode) instead of silently using a literal.
     Do NOT add new uses — use the canonical token on the right. */
  --white:      var(--color-white);
  --font-ui:    var(--font-family);
  --font-sans:  var(--font-family);
  --font-body:  var(--font-family);
  --navy:       var(--brand-primary);
  --ai-purple:  var(--color-purple);
  --hairline:   var(--border-inner);
  --rule:       var(--br);
  --br-border:  var(--br);
  --br-strong:  var(--t3);
  --surface1:   var(--bg1);
  --surface2:   var(--bg2);
  --surface-0:  var(--bg2);
  --surface-subtle: var(--bg2);
  --bg-card:    var(--bg1);
  --bg-subtle:  var(--bg2);
  --border-subtle: var(--border-inner);
  --text-primary:   var(--t1);
  --text-secondary: var(--t2);
  --text-tertiary:  var(--t3);
  --nav-txt:        var(--t2);
  --nav-txt-strong: var(--t1);
  --nav-txt-dim:    var(--t3);
  --nav-divider:    var(--border-inner);
  --sp-xs: var(--space-xs);
  --sp-sm: var(--space-sm);
  --sp-md: var(--space-md);
  --sp-lg: var(--space-lg);
  --rag-purple: var(--color-purple);
  --sig-red:    var(--rag-red);
  --sig-amber:  var(--rag-amber);
  --sig-green:  var(--rag-green);
  --c-red:      var(--rag-red);
  --c-green:    var(--rag-green);
  --rag-amber-bg: rgba(var(--rag-amber-rgb),.08);
  --rag-red-light: rgba(var(--rag-red-rgb),.15);
  --brand-primary-rgb: 27,79,114;

  /* ══════════════════════════════════════════════════════════════════
     EXTENDED PALETTE (added 2026-05-25 — §24 hex→token migration)
     ──────────────────────────────────────────────────────────────────
     Exact-value tokens for colours that recurred as hardcoded hex across
     pages_vnext / components_vnext. Tailwind-aligned shade names for the
     action families; dojo-semantic names for warm neutrals. Zero render
     change — each maps a literal hex 1:1. Prefer the canonical RAG /
     --color-* / --dl-action-* tokens above for NEW work; these exist to
     retire hardcoded hex without altering pixels.
     ══════════════════════════════════════════════════════════════════ */
  /* Neutrals — pure */
  --color-white: #ffffff;
  --color-black: #000000;
  /* Red scale (action/danger) */
  --red-50: #fdecec;
  --red-100: #fee2e2;
  --red-200: #fecaca;
  --red-700: #b91c1c;
  --red-800: #991b1b;
  --red-900: #7f1d1d;
  /* Amber / orange scale */
  --amber-50: #fff8e5;
  --amber-100: #fef3c7;
  --amber-500: #f59e0b;
  --amber-700: #9a5e2a;   /* was D4813A — 2.60:1 as text on amber tint */
  --amber-800: #92400e;
  --amber-900: #78350f;
  --orange-600: #ea580c;
  --orange-800: #9a3412;
  /* Green / emerald / teal scale */
  --green-100: #dcfce7;
  --green-800: #166534;
  --emerald-600: #059669;
  --emerald-700: #047857;
  --emerald-800: #065f46;
  --teal-100: #ccfbf1;
  --teal-200: #99f6e4;
  --teal-600: #0d9488;
  /* Blue scale */
  --blue-100: #dbeafe;
  --blue-500: #3b82f6;
  --blue-600: #2563eb;
  --blue-700: #1d4ed8;
  --blue-800: #1e40af;
  /* Violet / indigo scale */
  --violet-100: #ede9fe;
  --violet-200: #ddd6fe;
  --violet-300: #c4b5fd;
  --violet-700: #6d28d9;
  --violet-800: #5b21b6;
  --violet-900: #4c1d95;
  --violet-950: #2e1065;
  --indigo-800: #3730a3;
  --indigo-950: #1e1b4b;
  /* Slate / zinc / grey (cool) */
  --slate-200: #e2e8f0;
  --slate-300: #cbd5e1;
  --slate-500: #64748b;
  --slate-800: #1e293b;
  --zinc-400: #a1a1aa;
  --zinc-500: #71717a;
  --gray-500: #6b7280;
  --grey-200: #d4d4d2;
  --grey-300: #cccccc;
  --neutral-50: #fafafa;
  /* Warm dojo neutrals */
  --stone-100: #e6e6e3;
  --stone-200: #e0ddd6;
  --stone-300: #d8d6d0;
  --ink-900: #1a1a17;
  --ink-950: #1c1c1a;
  --orb-field: #060e1f;  /* crystal-ball cover — DESIGN.md */
  --platinum: #E5E4E2;
  --ink-orb: #9BAAB8;                    /* secondary text on the navy orb field */
  --bdr-orb: rgba(229,228,226,.16);      /* hairline on the navy orb field */
  --card-sub: #fafaf8;                   /* card footer strip */
  --card-sub-hover: #f0f0ee;            /* footer strip on hover */
  --wire: #e2e0db;                       /* drawn placeholder line (report pictures) */
  --wire2: #eeece7;                      /* drawn placeholder block */
  /* Earth / brand accents */
  --brown-400: #8d6e63;
  --gold-900: #6b4f0f;
  --brand-muted: #597385;  /* was #7e8fa1 — 3.12:1 on white, retired (CANON 2026-07-21) */

  /* ── Report spine (build-by-page/Report Spine.dc.html, SPINE_BUILD.md Part D · Tokens) ──
     Board-facing status fills: white words on colour, never themed, the same in
     dark mode (the CLAUDE.md board-facing exception). Outlines: a built-in
     block, a card slot and a free space are three different lines on purpose. */
  --spine-status-green: #1B7A50;
  --spine-status-amber: #A8561A;
  --spine-status-red:   #C0392B;
  --spine-status-ink:   #ffffff;
  --spine-status-grey:  #6b6b67;   /* the pills in the structure view, before any data colours them */
  --spine-active-tint:  rgba(27, 79, 114, .10);
  --spine-thumb-builtin: #b9c9d6;
  --spine-thumb-slot:    #e2e0db;
  --spine-thumb-cover:   #060e1f;
  --spine-cover-ink:     #e5e4e2;
  --spine-cover-muted:   #9baab8;
  --spine-cover-alert:   #e07c6d;
  --spine-rail:         #fafaf8;
  --spine-block-outline: var(--t5);
  --spine-slot-outline:  #8f8c88;
  /* the Data Coach label (Report Spine v2 line 68): an outline in the severity tint, the same tints as the Data Coach drawer */
  --spine-coach-red-tint:     rgba(var(--rag-red-rgb), .07);
  --spine-coach-red-border:   rgba(var(--rag-red-rgb), .25);
  --spine-coach-red-ink:      var(--rag-red-ink);
  --spine-coach-amber-tint:   rgba(var(--rag-amber-rgb), .08);
  --spine-coach-amber-border: rgba(var(--rag-amber-rgb), .28);
  --spine-coach-amber-ink:    var(--amber-700);
  --spine-free-outline:  #c9c6bf;
  --spine-sensei-tint:  rgba(124, 58, 237, .06);
  --spine-accent-tint:  rgba(27, 79, 114, .08);
  --spine-popover-shadow: 0 8px 24px rgba(0, 0, 0, .12);
  --spine-tip-shadow:     0 4px 12px rgba(0, 0, 0, .10);
  --spine-thumb-shadow:   0 1px 3px rgba(0, 0, 0, .08);   /* the export preview's page card */
  --spine-page-shadow:    0 1px 3px rgba(0, 0, 0, .08), 0 8px 24px rgba(0, 0, 0, .10);
}

/* ── Dark mode — tatami-to-ink inversion (warm, not blue-black) ─────── */
/* Two activation paths:
   1. OS prefers-color-scheme: dark AND user hasn't explicitly chosen
      light or auto — covers the data-theme="auto" path
   2. User explicitly picked Dark in the avatar dropdown
      (data-theme="dark") — overrides OS preference */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not([data-theme="dark"]) {
    --t1: #E8E4DC;
    --t2: #E5E4E2;
    --t3: #a3a39f;
    --t4: #a3a39f;
    --bg1: #1A1A18;
    --bg2: #2C2C2A;
    --bg3: #3A3A37;
    --bg-nav: #1A1A18;
    /* Not-attested, dark. Solid titanium, never an alpha tint: the
       accessibility discipline's ≥0.65-alpha floor exists for exactly this
       case, and solid sidesteps the question. Measured: #C8C4B8 on --bg1 =
       10.00:1, on --bg2 = 8.03:1. */
    --not-attested-fg: #C8C4B8;
    --not-attested-bg: rgba(255, 255, 255, .06);
    --not-attested-br: rgba(255, 255, 255, .14);
    --brand-strap: #6a899e;   /* on --bg-nav #1A1A18 = 4.72:1 */
    /* Brand blue as TEXT on a dark ground measured 2.05:1. Lifted to clear AA
    (5.00:1 on the dark surface). It is also the primary button fill, so
    button text flips to the dark ink already used on accent fills
    (6.86:1) rather than white (2.61:1). */
    --brand-primary: #318ecd;
    --btn-primary-text: #081828;
    /* Playbook layer (TASK_93) — dark */
    --lever-track: #5ba8d8;
    --lever-knob: #081828;
    --lever-commit: rgba(91, 168, 216, 0.40);
    --coach-glyph: rgba(124, 58, 237, 0.22);
    --playbook-accent: #b39ddb;
    --br: #4A4A47;
    --br-hover: #6B6B67;
    --brief-canvas: #161618;
    --brief-stage: #1F1F1E;
    --brief-chip-bg: #2C2C2A;
    --brief-rail: #3A3A37;
    --brief-slide-border: #4A4A47;
    --brief-fragment-bg: #2C2C2A;
    --spine-rail: #232321;
    --spine-block-outline: var(--t5);
    --spine-slot-outline: #6B6B67;
    --spine-free-outline: #4A4A47;
    /* Status text + on-brand ink for the dark ground. --color-amber/-green have
    no dark override; darkening them for the LIGHT tint made them worse here
    (4.02:1 -> 3.33:1), caught by tools/audit_contrast.py. --on-brand must be
    dark ink now that --brand-primary is lifted, or button text sits at 3.57:1. */
    --color-amber: #cf9d70; /* unified dark amber, exceeds AA — 6.9:1 on amber tint, 7.4:1 on base (was #b87132, 4.55:1) */
    --amber-700: #cf9d70;   /* had NO dark override; light #9a5e2a leaked in at 3.16:1. Now matches --color-amber, 6.9:1 */
    --color-green: #6bd0a6; /* was #229461 — 3.05:1 as status text on the dark green tint, now 6.2:1 (9.5:1 on base) */
    --color-red: #f47c70; /* dark red status, exceeds AA — 6.8:1 on base, 6.3:1 on tint (was #eb4635, 4.5:1) */
    --on-brand: #081828;
    --color-red-bg: rgba(211,47,47,.18);
    --color-red-light: rgba(211,47,47,.25);
    --color-amber-bg: rgba(230,126,34,.18);
    --color-amber-light: rgba(230,126,34,.25);
    --color-green-bg: rgba(34,197,94,.18);
    --color-green-light: rgba(34,197,94,.25);
    --color-blue: #5ba8d8;   /* dark blue text/accent, exceeds AA — 6.8:1 on base (light #256a99 would be 3.9:1 here) */
    --color-blue-bg: rgba(46,134,193,.18);
    --color-blue-light: rgba(46,134,193,.25);
    --color-purple-bg: rgba(125,60,152,.18);
    --color-purple-light: rgba(125,60,152,.25);
    --shadow-sm: 0 1px 3px rgba(0,0,0,.30);
    --shadow-md: 0 2px 8px rgba(0,0,0,.35);
    --shadow-lg: 0 4px 16px rgba(0,0,0,.40);
    --kinari: #2C2C2A;
    --sumi: #E8E4DC;
    --washi: #4A4A47;
    --tatami: #6B6B67;
    --card-sub: #232321;
    --card-sub-hover: #2C2C2A;
  }
}

:root[data-theme="dark"] {
  --t1: #E8E4DC;
  --t2: #E5E4E2;
  --t3: #a3a39f;
  --t4: #a3a39f;
  --bg1: #1A1A18;
  --bg2: #2C2C2A;
  --bg3: #3A3A37;
  --bg-nav: #1A1A18;
  /* Not-attested, dark — see the prefers-color-scheme block above. */
  --not-attested-fg: #C8C4B8;
  --not-attested-bg: rgba(255, 255, 255, .06);
  --not-attested-br: rgba(255, 255, 255, .14);
  --br: #4A4A47;
  --br-hover: #6B6B67;
  --brief-canvas: #161618;
  --brief-stage: #1F1F1E;
  --brief-chip-bg: #2C2C2A;
  --brief-rail: #3A3A37;
  --brief-slide-border: #4A4A47;
  --brief-fragment-bg: #2C2C2A;
  --spine-rail: #232321;
  --spine-block-outline: var(--t5);
  --spine-slot-outline: #6B6B67;
  --spine-free-outline: #4A4A47;
  /* Status text + on-brand ink for the dark ground — see the matching block
     in the prefers-color-scheme rule above. Darkening amber/green for the
     LIGHT tint made them worse here (4.02:1 -> 3.33:1); --on-brand must be
     dark ink now that --brand-primary is lifted (white was 3.57:1). */
  --color-amber: #cf9d70; /* unified dark amber, exceeds AA — 6.9:1 on amber tint, 7.4:1 on base (was #b87132, 4.55:1) */
  --amber-700: #cf9d70;   /* had NO dark override; light #9a5e2a leaked in at 3.16:1. Now matches --color-amber, 6.9:1 */
  --color-green: #6bd0a6; /* was #229461 — 3.05:1 as status text on the dark green tint, now 6.2:1 (9.5:1 on base) */
  --color-red: #f47c70;   /* dark red status, exceeds AA — 6.8:1 on base, 6.3:1 on tint (was #eb4635, 4.5:1) */
  --on-brand: #081828;
  --color-red-bg: rgba(211,47,47,.18);
  --color-red-light: rgba(211,47,47,.25);
  --color-amber-bg: rgba(230,126,34,.18);
  --color-amber-light: rgba(230,126,34,.25);
  --color-green-bg: rgba(34,197,94,.18);
  --color-green-light: rgba(34,197,94,.25);
  --color-blue: #5ba8d8;   /* dark blue text/accent, exceeds AA — 6.8:1 on base (light #256a99 would be 3.9:1 here) */
  --color-blue-bg: rgba(46,134,193,.18);
  --color-blue-light: rgba(46,134,193,.25);
  --color-purple-bg: rgba(125,60,152,.18);
  --color-purple-light: rgba(125,60,152,.25);
  --gantt-critical: #fb7185;
  --gantt-baseline: #4A4A47;
  --gantt-summary:  #E8E4DC;
  --gantt-dep:      #9aa3b0;
  --shadow-sm: 0 1px 3px rgba(0,0,0,.30);
  --shadow-md: 0 2px 8px rgba(0,0,0,.35);
  --shadow-lg: 0 4px 16px rgba(0,0,0,.40);
  --kinari: #2C2C2A;
  --sumi: #E8E4DC;
  --washi: #4A4A47;
  --tatami: #6B6B67;
  --brand-strap: #6a899e;   /* on --bg-nav #1A1A18 = 4.72:1 */
  /* Brand blue as TEXT on a dark ground measured 2.05:1. Lifted to clear AA
  (5.00:1 on the dark surface). It is also the primary button fill, so
  button text flips to the dark ink already used on accent fills
  (6.86:1) rather than white (2.61:1). */
  --brand-primary: #318ecd;
  --btn-primary-text: #081828;
  /* Playbook layer (TASK_93) — dark */
  --lever-track: #5ba8d8;
  --lever-knob: #081828;
  --lever-commit: rgba(91, 168, 216, 0.40);
  --coach-glyph: rgba(124, 58, 237, 0.22);
  --playbook-accent: #b39ddb;
  --card-sub: #232321;
  --card-sub-hover: #2C2C2A;
}

/* ── Accessibility ──────────────────────────────────────────────────── */

/* Focus visible ring */
:focus-visible {
  outline: 2px solid var(--br-focus);
  outline-offset: 2px;
}

/* Skip to content link */
.skip-link {
  position: absolute;
  top: -100%;
  left: 8px;
  z-index: 10000;
  padding: 8px 16px;
  background: var(--brand-primary);
  color: #fff;
  border-radius: var(--radius-md);
  font-size: var(--font-size-sm);
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus {
  top: 8px;
}

/* Minimum touch target */
button, a, input[type="checkbox"], input[type="radio"], select {
  min-height: var(--touch-icon);
}

/* Screen reader only */
.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;
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* High contrast mode */
@media (forced-colors: active) {
  .pd-pill, .les-tag, .md-card, .audit-action {
    border: 1px solid currentColor;
  }
}

/* ── Density variants ─────────────────────────────────────────────────
   Density affects SPACING + LINE-HEIGHT only — never font-size.
   Resizing type via density produces half-pixel blur.
   ───────────────────────────────────────────────────────────────────── */
body.density-compact {
  --density-padding: 6px 10px;
  --density-gap: 6px;
  --density-row-height: 36px;
  --density-line-height: 1.4;
}
body.density-comfortable {
  --density-padding: 10px 14px;
  --density-gap: 10px;
  --density-row-height: 44px;
  --density-line-height: 1.5;
}
body.density-spacious {
  --density-padding: 14px 18px;
  --density-gap: 14px;
  --density-row-height: 52px;
  --density-line-height: 1.65;
}

/* ── Decision tags — Act Now / Escalate / Watch / On Track (whisper) ── */
.dtag {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  white-space: nowrap;
  line-height: 1.6;
  border: 1px solid transparent;
}
.dtag--act      { background: var(--color-red-bg);    color: var(--rag-red-ink, var(--color-red)); }
.dtag--watch    { background: var(--color-amber-bg);  color: var(--rag-amber-ink, var(--color-amber)); }
.dtag--escalate { background: var(--color-purple-bg); color: var(--deduced-ink, #5B21B6); }
.dtag--track    { background: var(--color-green-bg);  color: var(--rag-green-ink, var(--color-green)); }

/* ── Deduced marker voice — structure + colour, never italic mono theatre ── */
.ai-voice {
  font-family: var(--font-family);
  font-size: 13px;
  font-weight: 400;
  font-style: normal;
  line-height: 1.5;
  color: var(--t2);
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 16px;
}
.ai-voice::before {
  content: '\25C6';
  font-size: 8px;
  margin-top: 3px;
  flex-shrink: 0;
  opacity: 0.7;
}
.ai-voice--dark  { color: rgba(167,139,250,0.7); }
/* Hero ranking justification sits on a LIGHT card — the old pale-lavender at
   60% opacity / 11px was ~1.5:1 (barely visible). Use the solid brand purple
   (~7:1 on white) at the readable 12px AI-voice size. */
.ai-voice--hero  { color: var(--brand-secondary); margin-top: 12px; font-size: 12px; }
.ai-voice--muted { color: var(--t3); }

/* ── Impact chain — causal flow visualisation ──────────────────────── */
.impact-chain {
  display: flex;
  align-items: center;
  gap: 0;
  flex-wrap: wrap;
}
.impact-chain__node {
  padding: 4px 8px;
  border-radius: var(--radius-sm);
  font-size: 11px;
  font-weight: var(--fw-semibold);
  white-space: nowrap;
}
.impact-chain__node--trigger { background: var(--color-red-bg); color: var(--color-red); }
.impact-chain__node--effect  { background: var(--color-amber-bg); color: var(--color-amber); }
.impact-chain__node--cascade { background: var(--color-purple-bg); color: var(--color-purple); }
.impact-chain__arrow {
  font-size: 12px;
  color: var(--t3);
  margin: 0 4px;
  flex-shrink: 0;
}

/* ── Base ────────────────────────────────────────────────────────────── */
body {
  font-family: var(--font-family);
  color: var(--t1);
  background: var(--bg1);
  line-height: var(--line-height);
  -webkit-font-smoothing: antialiased;
}
