/* GENERATED by scripts/build-guides.mjs — do not edit.
 *
 * src/theme/tokens.css followed by sherpa/app/guides.css. The guide pages are
 * static HTML outside the Vite graph, so there is no bundler to resolve an
 * @import and no build step to inline one. Concatenating is what lets the tokens
 * stay defined in exactly one place while the pages still load one stylesheet.
 */
:root {
  /* ── Backgrounds (Palantir deep blacks) ────────────────── */
  --bg-root: #0a0e14;
  --bg-card: #0d1117;
  --bg-elevated: #131920;
  --bg-hover: #1a2130;
  --bg-subtle: #0d1118;
  --bg-row-alt: rgba(255, 255, 255, 0.012);
  --bg-active-row: rgba(69, 215, 198, 0.03);

  /* ── Worksheet surface (Funding & Grants, 2026-09-25) ──────────────────
     A warm ground for a surface a founder COPIES FROM rather than only reads.
     Named for the role, not the color: `record` is one copyable block, and
     `copy-action` is the fill of the control that takes it. NO border or
     shadow token here on purpose: this repo already has a brutalist card
     idiom (AtlasGuideModule.module.css:77-85, Leaders.module.css:100-110)
     whose written doctrine is that the offset is tinted through
     --brand-navy, never black -- "a black shadow reads as dirt rather than
     as lift". Geometry stays in the module, tint comes from the brand.
     Defined in BOTH
     theme branches even though frontend-public pins data-theme="light"
     (index.html:6) — a token that exists in one branch only is a broken page
     the moment someone unpins it. */
  --surface-record: #1C1811;
  --surface-copy-action: #D9A85C;
  --copy-action-ink: #14110B;

  /* ── Text (warm grey Palantir palette) ─────────────────── */
  --text-primary: #d4d0c8;
  --text-secondary: #9a9aa3; /* WCAG AA: ~4.8:1 on --bg-root */
  --text-disabled: #8a9cac; /* WCAG AA: ~4.5:1 on --bg-root (was #6a8090 ~3.5:1) */
  --text-muted: #8a8e96; /* WCAG AA: ~4.5:1 on --bg-root */

  /* ── Accents (natural/muted tones — 2026-06-24 user: "less neon, more
     natural"). Desaturated + slightly warmed toward earthy contemporary tones,
     holding ~the same LUMINANCE as the prior neon set so WCAG contrast on
     --bg-root is preserved (contrast is luminance-driven). */
  --accent-teal: #6FB9AB;     /* was #45d7c6 neon cyan → muted sea-teal */
  /* --accent-teal-dim removed 2026-04-28: zero consumers across frontend/src
     and the new a11y-tokens CI job was warning on its 1.32:1 contrast vs
     --bg-root. If a tinted-teal surface token is needed later, name it
     --surface-teal-tint (not --accent-*) so the contrast gate doesn't
     match it by prefix. */
  --accent-gold: #D9A85C;     /* was #f5a623 → muted ochre/wheat */
  --accent-blue: #8AA6CE;     /* was #5B8DEF → dusty slate blue */
  --accent-purple: #ABA0CE;   /* was #9b8af7 → muted lavender */
  --accent-amber: #D9A85C;
  --accent-red: #DB9387;      /* was #ec7474 → dusty clay-rose */
  --accent-green: #84BE93;    /* was #4ade80 neon → muted sage */

  /* ── THE GOED BRAND PALETTE (brandbook V1.0, July 2022 — ingested 2026-09-23)
     Three of these were approximations of the brand blues and are now the
     brand blues exactly. The palette was already close — the largest move is
     `--brand-navy`, an RGB distance of 25 — so this reads as the same site,
     which is what "a minor adjustment to get to the color palette" asked for.

     PRIMARY, from the book:
       BLUE ECONOMY  #5BC2E7   PANTONE 2985 C        -> --brand-sky-soft
       RESILIENCE    #0085CA   PANTONE Process Blue  -> --brand-blue
       UNITY BLUE    #01426A   PANTONE 7694 C        -> --brand-navy
       STEEL GRAY    #53565A   PANTONE Cool Gray 11  -> unused here; the neutral
                                                        ramp is a11y-tuned and
                                                        swapping it is not minor
     SECONDARY: VENTURE #E35205 · ECO TECH #00C7B1 · EQUITY #2E008B ·
     INNOVATION #FFCD00 — available, not yet assigned to a token.

     ⚠️ `--brand-blue-ink` IS DELIBERATELY NOT A BRAND COLOR, and this is the
     one place the book cannot be followed literally. It is the link color for
     small text on white, which needs 4.5:1. RESILIENCE measures 4.03:1 there
     and FAILS; UNITY BLUE passes at 10.56:1 but is the navy, so links would
     stop looking like links. The book's own rule is "ensure there is
     sufficient contrast with the background", and this token is how that rule
     is kept. Measured, all against white unless stated:
       --brand-navy     #01426A  white on it 10.56:1  (was 12.09, still AA)
       --brand-blue     #0085CA   4.03:1  large/non-text only (was 3.49)
       --brand-blue-ink #1A62A8   6.26:1  AA for body links   (unchanged)
       --brand-sky-soft #5BC2E7   5.19:1 on the new navy      (was 5.29)
       --brand-sky-label #AFD6F4  6.91:1 on the new navy      (unchanged) */
  /* THE PERSISTENT BOTTOM BAR'S HEIGHT, declared once because THREE things
     depend on it and they must not disagree: the bar itself, the page padding
     that keeps content from hiding under it, and the Guide pill that would
     otherwise sit on top of it. A number typed in three places is the shape of
     a defect where one of them is edited. */
  --utility-bar-h: 1.75rem;

  /* VENTURE, the brandbook's own secondary (PANTONE 166 C, BRAND.md). Measured
     3.84:1 on white — so it is usable for NON-TEXT only: focus rings, rules,
     marks. It must never carry a small label. */
  --accent-venture: #E35205;

  --brand-navy:      #01426A;  /* UNITY BLUE — nav bar, hero overlay, headings, primary buttons */
  --brand-navy-deep: #012E4A;  /* a DERIVATIVE, not a brand color: UNITY BLUE darkened for gradients / hover */
  --brand-blue:      #0085CA;  /* RESILIENCE — icons, accents, large highlights */
  --brand-blue-ink:  #1A62A8;  /* AA-safe blue for links / CTA text on white — see the note above */

  /* ── ON-CARD CONTROLS (2026-08-19) ────────────────────────────────────────
     The profile selectors were authored for the NAVY masthead and styled in
     `--brand-on-navy` (white). They now live in the white ProfileDock card,
     where white-on-white made the region and entity controls effectively
     invisible — the user reported exactly this. Three of the four selectors
     still carried the masthead palette.
     These tokens are the card-context surface, defined ONCE so a control
     cannot drift back: quiet light-blue fills, navy-deep text, a real border
     so every control reads as a box rather than as floating text. */
  /* The TOUCH FLOOR, as a role name rather than a number repeated at each call
     site. design-taste §3.5: "the floors are floors, not preferences… density
     is bought with space and weight, never by shrinking a target." Stated once
     so a future density edit has to argue with the token instead of quietly
     re-crossing it at one call site. */
  --control-min-height: 44px;
  /* THE POINTER FLOOR, which is a different floor from the touch one above and
     applies at EVERY width. WCAG 2.5.8 (AA) sets 24x24 CSS px for a pointer
     target; the 44px touch floor is only ever applied inside a mobile media
     query, so above that breakpoint a control's height collapsed onto its font
     and there was no floor at all. Measured 2026-09-18 across all 26 public
     views at 1440: 212 controls under 24px, most commonly 17px.
     32px, not 24px: 24 is the minimum that PASSES, and a floor set exactly at
     the threshold fails the next time anyone removes two pixels of padding. */
  --control-min-pointer: 32px;
  --control-bg: color-mix(in srgb, var(--brand-sky-soft) 14%, var(--bg-card));
  --control-border: color-mix(in srgb, var(--brand-navy) 22%, transparent);
  --control-text: var(--brand-navy-deep);
  --control-text-muted: color-mix(in srgb, var(--brand-navy-deep) 68%, transparent);
  --control-hover-bg: color-mix(in srgb, var(--brand-sky-soft) 32%, transparent);
  --control-on-bg: color-mix(in srgb, var(--brand-sky-soft) 62%, transparent);
  --control-on-border: color-mix(in srgb, var(--brand-navy) 40%, transparent);

  /* ── Chat design language (UX spec §2a — Zoom-reference, blue/white) ──────
     ONE accent, and only one: the gradient is sanctioned on exactly two
     sites — the sparkle/brand mark and the circular send action. Everything
     else in the chat is surface, border, and text tokens. The indigo exists
     only as the gradient's far stop; it is not a standalone accent. */
  --chat-accent-from: var(--brand-blue);
  --chat-accent-to:   #4F46B8;  /* indigo — gradient far stop ONLY */
  --chat-accent-gradient: linear-gradient(135deg, var(--chat-accent-from), var(--chat-accent-to));
  --chat-surface: var(--bg-card);
  --chat-border:  var(--border-subtle);
  --chat-ghost-hover: color-mix(in srgb, var(--brand-blue) 7%, transparent);
  --brand-sky-soft:  #5BC2E7;  /* BLUE ECONOMY — accents ON navy (white-adjacent) */
  /* SMALL TEXT on the navy masthead. --brand-sky-soft is correct for what its
     own comment claims — accents — but it is 3.28:1 against the composited
     masthead (--brand-navy at 84% over white = rgb(59,84,131)), which passes
     the 3:1 large-text bar and fails the 4.5:1 normal-text bar it is actually
     being used at (10px tag, 22px/400 mark). Measured by QA across 76/76 cells
     and independently reproduced. Named for the ROLE it fills rather than the
     color it is: a label sitting on navy. 4.92:1, with headroom so a future
     navy adjustment does not silently re-cross the floor. */
  --brand-sky-label: #AFD6F4;
  --brand-on-navy:   #FFFFFF;
  --hero-overlay:    color-mix(in srgb, var(--brand-navy-deep) 66%, transparent);

  /* ── Policy-gap text palette (ResourceMatrix .gapBtn label) ───────────
     Per-gap identity for the small-label gap button. Bright values here
     match the existing `data/policyGaps.js` `color` field 1:1 — the
     light-mode override below darkens each for AA contrast on white.
     The bare `color` field on each gap stays as the bright hex (used
     by SVG fills with hex+alpha math: `${color}55`, `${color}44` —
     incompatible with var() syntax). Recon:
     `.claude/scratch/a11y-recon-exec-contrast-2026-05-11.md`. */
  --gap-vod-text:      #4d9de0;
  --gap-hgd-text:      #f59e0b;
  --gap-smegap-text:   #e84393;
  --gap-commons-text:  #9b8af7;
  --gap-seriesb-text:  #e05c5c;
  --gap-rural-text:    #2ecfa0;

  /* ── Semantic aliases (FE SPEC v1, 2026-04-30) ───────────────────────
     Three semantic + 1 capital. Lock per spec §3 design principles —
     "color is signal, never decoration". Aliases (not renames) so
     existing call-sites keep working unchanged; new code (BandA,
     KpiStripBand, future Band C work) consumes the semantic name. */
  --accent-positive: var(--accent-teal);
  --accent-watch: var(--accent-amber);
  --accent-adverse: var(--accent-red);
  --accent-capital: var(--accent-purple);

  /* ── Status ──────────────────────────────────────────── */
  --status-success: #34C9A8;
  --status-warning: #E5B654;
  --status-risk: #e05c5c;
  --status-info: #5B8DEF;

  /* ── Delta indicators ────────────────────────────────── */
  --delta-up: #4ade80;
  --delta-down: #e05c5c;
  --delta-neutral: #9a9aa3;

  /* ── Borders (Palantir muted blues) ────────────────────── */
  --border-subtle: #1c2733;
  --border-medium: #1c2733;
  --border-hover: #243040;
  --border-active: #243040;
  /* A CONTROL'S OWN BOUNDARY, where that boundary is what identifies it — an
     EMPTY text input has nothing inside it to name it, so WCAG 1.4.11 (AA) asks
     3:1 against the ground. MEASURED 2026-09-20: every other border token misses
     it on white — subtle 1.27, medium/default 1.48, and even hover/strong only
     2.56 — so there was nothing to swap to and this exists rather than darkening
     --border-default, which 46 files use for decorative edges that do not need it.
     3.48:1 on --bg-card, 3.56:1 on --bg-root. */
  --border-control: #5A6B82;
  --border-grid: rgba(255, 255, 255, 0.025);

  /* ── Typography ──────────────────────────────────────── */
  /* Platform-wide editorial serif (Newsreader) — the "Anthropic report" look,
     user-locked 2026-06-24 after the per-view trial (Newsreader won over Source
     Serif 4). Newsreader drives content typography (headings + body + UI chrome);
     IBM Plex Mono is RESERVED for data — numerics / IDs / tabular cells / code —
     so figures keep their tabular alignment + precision feel. A free Copernicus
     stand-in (Anthropic's real Copernicus/Styrene are paid, can't self-host). */
  --font-serif: 'Newsreader', Georgia, 'Times New Roman', serif;
  --font-heading: var(--font-serif);
  --font-body: var(--font-serif);
  --font-mono: 'IBM Plex Mono', monospace;
  --font-chrome: var(--font-serif);

  /* ATLAS DATA CHROME (user-directed 2026-09-14) — the two roles that separate
     a measured value from the prose around it. Both resolve to faces this app
     already loads; what they add is a NAME for the decision, so a surface
     spells the role it means rather than a face it happens to want.

     --font-numeric: every measured value. The original Atlas sets figures in
     tracked mono with tabular figures, which is what lets a column of numbers
     line up and what stops a figure reading as running prose. Serif stays on
     TITLES, exactly as the original does — this is not a swap of the display
     face, it is a narrower claim about numbers.

     --font-eyebrow: labels, column heads, as-of lines, section kickers. Quiet
     mono at micro size, tracked, in a muted ink — subordinate by color and
     tracking rather than by weight. */
  --font-numeric: var(--font-mono);
  --font-eyebrow: var(--font-mono);
  --tracking-eyebrow: 0.05em;

  /* ── Type scale (tightened) ────────────────────────────── */
  --text-hero: 2rem;        /* 32px — view-level hero headlines (KF) */
  --text-hero-md: 1.6rem;   /* 25.6px — hero on mobile / smaller view */
  --text-hero-sm: 1.4rem;   /* 22.4px — section subhero (e.g. KF multiplier) */
  /* ── px -> rem, 2026-08-05 (Section 508 / WCAG 1.4.4) ──────────────────
     This app pins no root `font-size`, so `rem` tracks the reader's browser
     preference and `px` does not. Every token below rendered identically at
     the 16px default before this change and still does — the conversion is
     invisible to a reader who has not asked for anything, and the whole point
     for one who has.

     WHY IT WAS MISSED. The main app's scale was converted earlier the same
     day and reported as "the Section 508 conversion". It was not: it covered
     `frontend/src` only. THIS is the app the public actually reads, and its
     362 token-driven font-size declarations were all still welded to px.
     Same shape as the note in scripts/ci/check_theme_tokens.py about this
     gate scanning half the tree — the rule was fine, the population was.
     Verified with a SAFETY sweep against a real preview of this app, not
     assumed: converting the main app's smallest tokens DID break three
     routes the first time, via a nav that lacked min-width containment. */
  --text-h1: 1.4375rem;
  --text-h2: 1.125rem;
  --text-h3: 1rem;
  --text-card-numeric: 1.375rem;
  --text-card-numeric-sm: 1.125rem;
  --text-body: 0.9375rem;
  --text-body-sm: 0.85rem;
  --text-caption: 0.78rem;
  --text-caption-sm: 0.6875rem;
  --text-micro: 0.625rem;
  /* 2026-09-16 — NOT A TASTE CHOICE, A BROWSER THRESHOLD. iOS Safari zooms the
     whole page when a form field with a font-size under 16px receives focus.
     That is a layout event, not a typography one: the page jumps, the field
     leaves the viewport, and the reader has to pinch back out. Measured on the
     public surfaces at three phone widths: the Directory and Events search
     boxes render 13.6px and Funding's 15px, so all three zoom today. Named for
     its purpose rather than reused from the ramp, because the next person to
     tune the type scale must not quietly drop this below 16. */
  --text-field-mobile: 1rem;
  --text-label: 0.6875rem;

  /* 2026-05-20 — aliases for components that reference shorter
     "sm/md" names (CapacityMiniTile and any future surface that
     wants the abbreviated semantics). Resolve to existing scale
     entries so the type ramp stays unified. */
  --text-sm: 0.85rem;  /* alias of --text-body-sm */
  --text-md: 0.9375rem;  /* alias of --text-body / --text-h3 */

  /* 2026-05-21 — doctrine #10 enforcement gate (check_undefined_css_tokens.py)
     found ~167 var() references to tokens that didn't exist in this
     file. Most were systematic alias names from components that
     followed a different naming convention than the canonical one
     above. Adding aliases (not renames) preserves all existing call
     sites while making the type/font/color ramp single-sourced.
     Spec: `.claude/specs/north-star-design-principles-2026-05-20.md` #10 */
  --text-xs: 0.6875rem;  /* alias of --text-caption-sm */
  --text-lg: 1.125rem;  /* alias of --text-h2 (mid-step between h3 13px and h1 20px) */
  --text-xl: 1.4375rem;  /* alias of --text-h1 */
  --text-2xl: 1.375rem;  /* alias of --text-card-numeric */
  --text-h4: 0.9375rem;  /* small heading; aliases body */
  --text-base: 0.9375rem;  /* alias of --text-body (Tailwind-style "base" naming) */
  --text-body-xs: 0.78rem;  /* alias of --text-caption (smaller body var) */
  --text-body-md: 0.9375rem;  /* alias of --text-body (explicit -md suffix variant) */
  --text-heading-md: 1.125rem; /* alias of --text-h2 */
  --text-heading-lg: 1.4375rem; /* alias of --text-h1 */
  --text-tertiary:  var(--text-disabled);  /* third-tier muted text */

  /* ── Legibility (fe-legibility-remediation WS4, 2026-06-29) ─────────────
     Minimum-readable floors + an AA-safe footnote color. Do NOT route small
     labels/footnotes through --text-micro (10px) or --text-caption-sm (11px) —
     use these. The economist review flagged sub-8px KPI labels (regions) and
     9px grey-on-white footnotes (cohorts); these tokens are the fix. */
  --text-label-min: 0.6875rem;            /* min size for any KPI / column / chip LABEL (never below) */
  /* ── RAISED 0.6875 → 0.75rem (11 → 12px), 2026-09-18 ─────────────────────
     THIS TOKEN WAS SET TO THE VALUE IT WAS CREATED TO REPLACE. The line three
     rows up says do not route footnotes through --text-caption-sm (11px) and
     use this instead — and this was 0.6875rem, the same 11px, so following the
     instruction changed the NAME and not the size. A rename is not a
     remediation, and the gap survived because the token read as a floor.
     12px is the readable floor the mobile audit measures against, and it is
     the size the 2026-08-25 pass already raised the PROSE cases to. That pass
     keyed on 60+ characters inside a <p>, so every disclosure written as a
     <span> — basis, source, fee, statute — was never in its population.
     DELIBERATELY NOT --text-caption-sm, which stays 11px: a chip, a badge and
     a date stamp are labels, and --text-label-min says 11px is right for those.
     The split this token encodes is prose against label, not small against
     large. */
  --text-footnote: 0.75rem;              /* min size for footnotes / provenance / source notes */
  --text-footnote-color: var(--text-muted); /* AA-contrast-safe (~4.5:1 both light+dark) footnote text */
  /* Table-density utility tokens — consumed by the global .atlas-table-dense
     class (end of file) so a packed directory can compress row height while
     keeping cell text at a readable size. */
  --table-cell-pad-y:       6px;     /* default vertical cell padding */
  --table-cell-pad-y-dense: 3px;     /* compact vertical cell padding */
  --table-cell-pad-x:       10px;    /* horizontal cell padding (unchanged by density) */

  --font-sans:      var(--font-body);
  --font-display:   var(--font-heading);

  --weight-normal:  var(--weight-regular);

  --bg-secondary:   var(--bg-elevated);
  --surface-card:   var(--bg-card);
  --surface-hover:  var(--bg-hover);
  --color-border:        var(--border-subtle);
  --color-border-subtle: var(--border-subtle);
  --border-hairline:     var(--border-subtle);

  /* Generic panel surface — some older components used --panel-bg
     before the bg-* taxonomy stabilized. Maps to --bg-elevated which
     is the closest panel-ish surface. */
  --panel-bg:       var(--bg-elevated);

  /* Motion timings — components reference these by generic names */
  --transition-medium: 200ms ease-out;

  /* ── Weights ─────────────────────────────────────────── */
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;

  /* ── Line heights ────────────────────────────────────── */
  --leading-tight: 1.1;
  --leading-snug: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.55;

  /* ── Spacing (compressed) ──────────────────────────────── */
  --space-2xs: 2px;
  --space-xs: 3px;
  --space-sm: 6px;
  --space-md: 8px;
  --space-lg: 12px;
  --space-xl: 16px;
  --space-2xl: 20px;
  --space-3xl: 28px;
  --space-4xl: 40px;

  /* ── Grid ─────────────────────────────────────────────── */
  --grid-columns: 12;
  --grid-gutter: 12px;
  --grid-margin: 20px;

  /* ── Radii ───────────────────────────────────────────── */
  --radius-xs: 2px;
  --radius-sm: 3px;
  --radius-md: 4px;
  --radius-lg: 6px;

  /* ── Transitions ─────────────────────────────────────── */
  --transition-instant: 80ms ease;
  --transition-fast: 150ms ease;
  --transition-normal: 250ms ease;
  --transition-slow: 400ms ease-out;

  /* ── Chart-style tokens (cross-domain canonical, 2026-04-25) ──────────
     Single source of truth for chart strokes / fills / ticks / axes /
     labels across both FE-Core (graph, sparkline, sectors table, KPI
     cards) and FE-Analytics (CapitalFlowChart, EcosystemGaps, PillarRadar,
     SelfOrganizationPanel, PatentsDashboard, IcapEcapHero) so a "thin
     line" looks the same in every chart. Numeric tokens are unitless
     because they go into SVG `stroke-width` / `fill-opacity` (no `px`).
     Owner: FE-Core for definition; consumers may add new variants but
     should not redefine these values inside their own modules. */
  --chart-stroke-thin: 1;
  --chart-stroke-primary: 1.5;
  --chart-stroke-emphasis: 2;
  --chart-fill-subtle: 0.12;
  --chart-fill-prominent: 0.20;
  --chart-tick-color: var(--text-secondary);
  --chart-axis-color: var(--text-disabled);
  --chart-label-size: 10px;

  /* ── Choropleth color scales (geo render, world-class P0.1) ───────────
     A scientific perceptually-uniform scale (user-locked 2026-06-21),
     SAMPLED to 5 quantile bins and shipped AS tokens so Hard Rule #6 stays
     green — the fixed scientific hex lives here (the canonical color source),
     mirrored into constants.ts for maplibre paint. Sequential = viridis-5
     (colorblind-friendly, distinct bins on the dark theme); diverging =
     ColorBrewer PuOr-5 (colorblind-safe) for centered metrics (LQ, deltas);
     --choro-null = the explicit no-data swatch (absent != the low bin). */
  --choro-seq-1: #440154;
  --choro-seq-2: #3b528b;
  --choro-seq-3: #21918c;
  --choro-seq-4: #5ec962;
  --choro-seq-5: #fde725;
  --choro-div-1: #e66101;
  --choro-div-2: #fdb863;
  --choro-div-3: #f7f7f7;
  --choro-div-4: #b2abd2;
  --choro-div-5: #5e3c99;
  --choro-null: #5a606b;

  /* ── Shadows ─────────────────────────────────────────── */
  --shadow-inset: inset 0 1px 0 rgba(255, 255, 255, 0.02);
  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.02);
  --shadow-elevated: 0 4px 16px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.03);
  --shadow-focus: 0 0 0 2px rgba(69, 215, 198, 0.5); /* WCAG 2.4.11 — bumped from 0.2 */
  /* Shadow/scrim TINTS (gate-#6 Phase B, 2026-06-11). Sites keep their
     own geometry + alpha via color-mix(in srgb, var(--tint) N%, transparent):
     color-mix(black N%) === rgba(0,0,0,N/100), so dark mode is
     pixel-identical. Light mode then softens every drop shadow through
     ONE knob (0.2 ratio — same as --shadow-card 0.3→0.06 above).
     --scrim-tint is intentionally theme-invariant: modal backdrops and
     text-legibility halos must stay black over both themes. */
  --shadow-tint: rgb(0 0 0);
  --scrim-tint: rgb(0 0 0);

  /* ── Layout ──────────────────────────────────────────── */
  --header-height: 44px;
  --kpi-strip-height: auto;
  --sidebar-width: 280px;
  --max-content-width: 1440px;

  /* ── Z-index ─────────────────────────────────────────── */
  --z-base: 0;
  --z-card: 1;
  --z-sticky: 10;
  --z-dropdown: 50;
  --z-tooltip: 100;
  --z-modal: 200;
  --z-overlay: 500;
  --z-top: 9999;

  /* ── Surface roles (theme-aware) ─────────────────────────────────────
     Active-tab pill background + matching foreground. Light mode uses a
     black pill (per screenshot reference); dark mode keeps the existing
     teal pill. Components consume these instead of hard-coding the
     active-state colors. */
  --surface-active:    var(--accent-teal);
  --surface-active-fg: #0a0e14;

  /* Elevation shadows expressed as a single token so light mode can
     swap to soft black-tint without redefining every box-shadow site. */
  --shadow-elevation-1: 0 1px 2px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.02);

  /* Legacy alias: --bg-panel was referenced in 3 component files but
     never defined here, so var(--bg-panel, #0a0e14) always fell back
     to the hardcoded hex regardless of theme. Aliased to bg-elevated
     so MacroIndicatorCard + RegionCommand follow the theme. New code
     should use --bg-elevated directly; this stays only as a back-compat
     shim. */
  --bg-panel:               var(--bg-elevated);

  /* Other legacy aliases — same root cause: var(--name, #hex) where
     --name was never defined, so the dark hex fallback won in both
     themes. CI lint catches new instances (scripts/ci/check_theme_tokens.py).
     Each line below points an old name at the closest defined token. */
  --bg:                     var(--bg-root);
  --bg-base:                var(--bg-root);
  --bg-deep:                var(--bg-root);
  --surface:                var(--bg-elevated);
  --surface-base:           var(--bg-root);
  --color-surface:          var(--bg-elevated);
  --border:                 var(--border-medium);
  --color-text-muted:       var(--text-muted);
  --color-text-secondary:   var(--text-secondary);
  --color-accent:           var(--accent-blue);
  --color-accent-strong:    var(--accent-blue);
  --accent-negative:        var(--accent-red);
  --teal:                   var(--accent-teal);
  --accent:                 var(--accent-teal);
  /* The four --activity-* tokens were removed here 2026-08-25. This app has NO
     activity surface at all — no ActivityBadge, no activity_tier, nothing reads
     a tier — and grep confirmed zero consumers of the tokens themselves, so
     they were a dead set inherited from a copy of the main app's palette.
     Worse than dead: --activity-stale encoded a FOUR-tier model that the main
     app retired the same day, because the dots only ever paint three buckets.
     A token that describes a model nothing here renders is how a stale idea
     survives a refactor and gets picked up as precedent — which is exactly how
     MapLegend came to paint four swatches over three-color dots. If this app
     ever grows an activity surface, take the model from
     frontend/src/utils/activityTier.js rather than reviving these. */
  --bg-input:               var(--bg-elevated);
  --bg-primary:             var(--bg-root);
  --bg-surface:             var(--bg-elevated);
  --border-default:         var(--border-medium);
  --border-strong:          var(--border-hover);

  /* Surface tint base — used as the base color for translucent hover
     highlights and subtle row striping. Dark mode tints toward white
     (light hover on dark surface); light mode flips to black so the
     same color-mix(... var(--surface-tint), transparent) call paints a
     readable highlight in both themes. Replaces the hardcoded
     rgba(255, 255, 255, X) sites that vanished on white. */
  --surface-tint: #FFFFFF;

  /* Text painted on top of saturated accent fills (toasts, badges).
     Tracks --bg-root so it inverts with the theme automatically: dark
     mode has bright accents → near-black text; light mode darkens every
     accent for AA on white → white text. No per-theme override needed. */
  --text-on-accent: var(--bg-root);
}

/* ── Light theme overrides ──────────────────────────────────────────────
   Phase 1 of light-mode rollout (spec: .claude/specs/light-mode-2026-05-01.md
   §3.4). Toggle activates via `[data-theme="light"]` on `<html>`. Anti-FOUC
   inline script in index.html sets the attribute before any stylesheet
   loads. ~50 token overrides — every component already reads `var(--…)` so
   no per-component refactor is required for P1. P2 audits hard-coded hex
   values; P3 handles component edges (graph node colors, Leaflet tiles,
   chart stroke contrasts). */
[data-theme="light"] {
  /* Backgrounds — white surface, light-grey panel chrome */
  --bg-root:        #FFFFFF;
  --bg-card:        #FAFAFA;
  --bg-elevated:    #FFFFFF;
  --bg-hover:       #F0F0F1;
  --bg-subtle:      #FAFAFA;
  --bg-row-alt:     rgba(0, 0, 0, 0.012);
  --bg-active-row:  rgba(13, 148, 136, 0.05);

  /* Light branch — the one frontend-public actually ships.
     USER-DIRECTED 2026-09-25, correcting my first pass: the PAGE stays white
     and the warm cream moves onto the record. That is the better division.
     The cream now marks the thing you can lift rather than the thing you are
     standing on, so the color carries a meaning instead of a mood, and a
     white page keeps every other surface's neutral unchanged.
     Contrast re-measured on #FBF3DE: all 12 text/accent tokens clear AA.
     Tightest are --text-tertiary at 4.84 (floor 4.5) and --border-control at
     3.35 (floor 3.0) — the first two things a darker cream would break. */
  --surface-record: #FBF3DE;
  /* INNOVATION, PANTONE 116 C, straight off the GOED brandbook V1.0 block
     above (line ~48), where it is documented but was never assigned a token.
     The copy control is the one place on this page that wants the brand's
     loudest warm, and a sanctioned hue beats an invented one. */
  --surface-copy-action: #FFCD00;
  --copy-action-ink: var(--brand-navy-deep);

  /* Text — near-black headlines, neutral greys for caption + muted */
  --text-primary:   #1A1A1A;
  --text-secondary: #52525B; /* ~7.0:1 on white */
  /* 2026-05-08 a11y fix: was #A1A1AA (~2.46:1 on --bg-card) failing WCAG AA
     across 1200+ axe-core nodes. Round 1 went to #6E6E76 (~4.6:1 on
     --bg-card, ~5.1:1 on --bg-root) — but still 4.44:1 on --bg-hover
     (alpha-tinted hover state). Round 2 nudges to #6A6A72 for ~4.55:1
     across all 5 surface backgrounds. Same gray family. */
  --text-disabled:  #6A6A72;
  /* --text-muted #71717A was 5.0:1 on white but 4.24:1 on --bg-hover
     (#F0F0F1 — slightly darker than white). Round 2 bump #6E6E76
     still fell short at 4.44:1 on --bg-hover. Round 3 to #686870
     for ~4.65:1 across all 5 surfaces. */
  --text-muted:     #686870;

  /* Accents — darker tones than dark-mode counterparts so each clears
     ≥4.5:1 against the white surface.
     2026-05-08 a11y fix: --accent-teal was #0D9488 (~3.74:1 on --bg-root,
     fails normal-text 4.5:1 floor). Darkened to #0A6B5E for ~4.7:1.
     Carries the brand teal feel; clears ~280 axe-core nodes. */
  /* NBH retheme: teal (the old Palantir signature) yields to the brand blue as
     the app's primary accent — links, active states, kickers now read blue.
     Kept as a token so the 4-sector spiral palette stays distinct. */
  --accent-teal:    #1A6354;   /* nudged warmer/muted (natural tones, 2026-06-24) */
  /* 2026-05-08 round 2: gold/amber were #CA8A04 (~2.81-2.94:1 on
     all 5 light surface backgrounds — fails AA-large 3:1). Darkened
     to #A66700 for ~3.65:1 on white.
     2026-05-09 round 4: small badges (.label, .statusPill, status_amber)
     at 9px need AA-normal 4.5:1, not AA-large 3:1. #A66700 fell short
     at small sizes (160+ axe nodes on Methodology + status table).
     Darkened further to #7A5000 for ~5.5:1 on all light surfaces.
     Same warm-yellow family. */
  --accent-gold:    #7A5000;
  --accent-blue:    #3C5E93;   /* was #2563EB vivid → muted slate blue */
  --accent-purple:  #6B4E89;   /* was #7C3AED vivid → muted plum */
  --accent-amber:   #7A5000;
  /* 2026-06-10 QA round 5: #DC2626 read 4.46:1 on the #f6f6f6 light panel
     bg — a hair under AA 4.5; 47 of methodology's 50 contrast nodes were
     this one value (Stale status pills). Darkened, same hue. */
  --accent-red:     #A4463C;   /* was #C81E1E vivid → muted brick */
  /* 2026-05-09 round 4: was #059669 (~3.68:1 on white) — passed
     AA-large but failed AA-normal at small badge sizes. Same root
     cause as round-2 amber. Cell tier "Fresh" badges (Ops view) hit
     163 axe nodes. Darkened to #016237 for ~6.4:1; same green hue. */
  --accent-green:   #016237;

  /* Policy-gap text variants — darkened so .gapBtn passes AA (≥4.5:1) on
     #FAFAFA. Bright fill/swatch/border tokens (--gap-vod etc.) inherit
     :root values and remain intentionally bright for overlay rectangles.
     Recon: a11y-recon-exec-contrast-2026-05-11 — 6 axe nodes (1.9–3.55:1).
     Computed contrasts vs #FAFAFA: vod 6.2 / hgd 5.8 / smegap 7.0 /
     commons 6.5 / seriesb 7.2 / rural 6.0. */
  --gap-vod-text:      #1E5C8C;
  --gap-hgd-text:      #8A5300;
  --gap-smegap-text:   #A01265;
  --gap-commons-text:  #5546B8;
  --gap-seriesb-text:  #A01A1A;
  --gap-rural-text:    #006E54;

  /* Status — matches the accent set, semantic intent unchanged */
  --status-success: #016237;
  --status-warning: #CA8A04;
  --status-risk:    #DC2626;
  --status-info:    #2563EB;

  /* Delta indicators */
  --delta-up:       #059669;
  --delta-down:     #DC2626;
  --delta-neutral:  #71717A;

  /* Borders — subtle on white reads at #E4E4E7; medium at #D4D4D8 */
  --border-subtle:  #E4E4E7;
  --border-medium:  #D4D4D8;
  --border-hover:   #A1A1AA;
  --border-active:  #2563EB;
  /* 3.71:1 on white — headroom over the 3:1 floor on purpose, so a later nudge
     toward a lighter grey cannot drop it under without someone noticing. */
  --border-control: #84848D;
  --border-grid:    rgba(0, 0, 0, 0.04);

  /* Chart axis/tick — re-inherit from updated text tokens; no override
     needed (the original definitions reference --text-secondary etc.). */

  /* Shadows — invert from black-with-light-glow to soft black-tint */
  --shadow-inset:    inset 0 1px 0 rgba(255, 255, 255, 0.6);
  --shadow-card:     0 1px 2px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.04);
  --shadow-elevated: 0 4px 16px rgba(0, 0, 0, 0.08), 0 0 0 1px rgba(0, 0, 0, 0.04);
  --shadow-focus:    0 0 0 2px rgba(37, 99, 235, 0.4);
  --shadow-elevation-1: 0 1px 2px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.04);
  /* Phase-B tint: 0.2× every color-mix'd drop shadow (matches the
     0.3→0.06 card ratio). --scrim-tint deliberately NOT overridden. */
  --shadow-tint: rgb(0 0 0 / 0.2);

  /* Active-tab pill — Nevada cobalt (NBH brand) on white */
  --surface-active:    #16346B;
  --surface-active-fg: #FFFFFF;

  /* Light-mode surface tint flips to black so translucent highlights
     stay visible against the white surface. */
  --surface-tint: #0A0A0A;
}

/* Light-mode global background — match --bg-root since :root rule below
   may not exist in this file's consumer. Components inherit the body
   color through their own tokenized backgrounds. */
[data-theme="light"] body {
  background: var(--bg-root);
  color: var(--text-primary);
}

/* ── Table-density utility (fe-legibility-remediation WS4, 2026-06-29) ──────
   Opt-in: add `className="atlas-table-dense"` to a <table> to compress row
   HEIGHT (vertical cell padding) for a packed directory — text size stays at
   its readable value (only padding tightens). Global because tokens.css is a
   non-module import (main.jsx); S4/S5 apply the class, never re-create it. */
.atlas-table-dense th,
.atlas-table-dense td {
  padding-top: var(--table-cell-pad-y-dense);
  padding-bottom: var(--table-cell-pad-y-dense);
  line-height: var(--leading-snug);
}

/* Guide pages — the eighteen outcome guides, set as static reference pages.
 *
 * EVERY COLOR IS A TOKEN from src/theme/tokens.css, same rule and same reason
 * as sherpa.css: the CI gate (scripts/ci/check_theme_tokens.py) scans this
 * directory and fails on a single literal. Translucency goes through
 * color-mix(in srgb, var(--token) N%, transparent), never rgba().
 *
 * ── WHAT THESE PAGES ARE, WHICH DECIDES HOW THEY LOOK ─────────────────────
 * They are not a marketing surface and they are not the tool. They are the
 * answer somebody arrived at from a search for "Nevada contractor license Las
 * Vegas", and the whole job is that they read like a well-set reference page:
 * one column at a comfortable measure, headings that are scannable without
 * shouting, and a line height you can read three paragraphs of on a phone.
 *
 * The measure is 34rem rather than sherpa.css's 40rem, and that is not an
 * inconsistency. Sherpa's pane holds short cards inside a two-pane shell; these
 * pages hold continuous prose in a single column, where a longer line is
 * measurably harder to track back from the end of one line to the start of the
 * next.
 *
 * ── THEY SHIP WITHOUT A BUNDLER ───────────────────────────────────────────
 * Nothing here is part of the Vite graph. scripts/build-guides.mjs concatenates
 * src/theme/tokens.css in front of this file and writes the result as
 * guides/guides.css, so the tokens these rules reference are defined exactly
 * once and the pages still work as plain HTML with one stylesheet link.
 */

:root {
  /* Named rather than repeated so the header, the sections and the footer
     cannot drift out of alignment with each other. */
  --guide-measure: 34rem;
  --guide-gutter: clamp(1.25rem, 5vw, 2.5rem);

  /* ── The --at-* vocabulary, DEFINED (ORCH filing 2026-08-11) ─────────────
     The workbench styles arrived speaking the WordPress kit's --at-* dialect
     in a stylesheet that defined none of it: every --at-* var() resolved to
     nothing, colors fell back to inherited values, and the amber watch-callout
     rendered only via its hex fallback — which the theme gate then rightly
     flagged. Stripping the fallbacks first would have shipped an INVISIBLE
     callout; define-then-strip is the order.
     Each alias points at the app token, so dark mode and future a11y bumps to
     tokens.css propagate here with no second edit. */
  --at-text: var(--text-primary);
  --at-text-secondary: var(--text-secondary);
  --at-text-muted: var(--text-muted);
  --at-bg: var(--bg-root);
  --at-hover: var(--bg-hover);
  --at-border: var(--border-subtle);
  --at-border-control: var(--border-default);
  --at-brand-navy: var(--brand-navy);
  --at-radius: var(--radius-sm);
  --at-accent-amber: var(--accent-watch);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg-root);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 100%;
  line-height: 1.65;
  -webkit-text-size-adjust: 100%;
}

/* The default UA focus ring vanishes against white on several browsers, and a
   keyboard reader losing their place on a page of links is the exact failure
   this costs nothing to prevent. */
:focus-visible {
  outline: 2px solid var(--brand-navy);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

a { color: var(--brand-blue-ink); text-underline-offset: 0.2em; }
a:hover { color: var(--brand-navy); }

/* ── The column ──────────────────────────────────────────────────────────── */

.guide-page {
  max-width: var(--guide-measure);
  margin-inline: auto;
  padding: var(--space-4xl) var(--guide-gutter) var(--space-4xl);
}

.guide-back {
  margin: 0 0 var(--space-3xl);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.guide-title {
  font-family: var(--font-heading);
  font-size: clamp(1.75rem, 1.2rem + 2.2vw, 2.5rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin: 0 0 var(--space-4xl);
  color: var(--brand-navy);
  text-wrap: balance;
}

/* ── Sections ────────────────────────────────────────────────────────────── */

/* The section heading is deliberately small and quiet. On a page with six of
   them, headings sized like headings turn a short answer into a form; the
   founder is scanning for one of six known labels, not being introduced to
   them. */
.guide-h {
  font-family: var(--font-heading);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 var(--space-md);
}

.guide-section { margin: 0 0 var(--space-4xl); }
.guide-section p { margin: 0 0 var(--space-lg); }
.guide-section p:last-child { margin-bottom: 0; }

.guide-list { margin: 0; padding-left: 1.25rem; }
.guide-list li { margin-bottom: var(--space-lg); }
.guide-list li:last-child { margin-bottom: 0; }

/* "Start here" carries the one instruction the whole page exists to give, so it
   gets the only block emphasis on the page. Everything else earning a box would
   leave nothing emphasized. */
.guide-first-move {
  padding: var(--space-2xl);
  border: 1px solid var(--border-subtle);
  border-left: 3px solid var(--brand-navy);
  border-radius: var(--radius-lg);
  background: var(--bg-card);
  font-size: 1.0625rem;
}

/* The mistake is a warning, not an alarm: the founder has not made it yet. A
   tint at this strength reads as "pay attention" where a red panel would read
   as "something has gone wrong". */
.guide-mistake {
  padding: var(--space-xl) var(--space-2xl);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--accent-gold) 12%, transparent);
}

/* ── Open in Sherpa ──────────────────────────────────────────────────────── */

.guide-cta { margin: 0 0 var(--space-4xl); }

.guide-cta-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px; /* WCAG 2.5.5 */
  padding: var(--space-lg) var(--space-2xl);
  font-weight: 600;
  color: var(--brand-on-navy);
  background: var(--brand-navy);
  border: 1px solid var(--brand-navy);
  border-radius: var(--radius-lg);
  text-decoration: none;
}
.guide-cta-link:hover { background: var(--brand-navy-deep); color: var(--brand-on-navy); }

/* ── Related, sources, footer ────────────────────────────────────────────── */

.guide-related { margin: 0; padding: 0; list-style: none; }
.guide-related li { margin-bottom: var(--space-lg); }

.guide-sources { margin: 0; padding: 0; list-style: none; font-size: 0.875rem; }
.guide-sources li { margin-bottom: var(--space-md); }
/* Long source URLs and titles are the one thing on these pages that can push a
   375px viewport into horizontal scroll. */
.guide-sources a { overflow-wrap: anywhere; }

.guide-foot {
  margin: var(--space-4xl) 0 0;
  padding-top: var(--space-xl);
  border-top: 1px solid var(--border-subtle);
  font-size: 0.8125rem;
  color: var(--text-muted);
}

/* ── The index ───────────────────────────────────────────────────────────── */

.guide-lede {
  margin: 0 0 var(--space-4xl);
  font-size: 1.0625rem;
  color: var(--text-secondary);
}

/* ── The index, and its section rail ─────────────────────────────────────── */

/* 34rem is the reading measure for a guide's prose. The index is a list of
   links, not prose, so it can afford the width the rail needs beside it — but
   only the index gets it, which is why this is a modifier rather than a change
   to .guide-page. */
.guide-page--index { max-width: 52rem; }

.guide-index-layout { display: block; }

.guide-rail { margin: 0 0 var(--space-3xl); }

.guide-rail-h {
  margin: 0 0 var(--space-md);
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.guide-rail-list {
  margin: 0;
  padding: 0;
  list-style: none;
  /* Narrow screens get a wrapping row of pills rather than a second vertical
     list stacked on top of the one it points at — which would make a founder
     scroll past a table of contents to reach contents of the same length. */
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

.guide-rail-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 44px;
  padding: 0 var(--space-lg);
  font-size: 0.875rem;
  text-decoration: none;
  color: var(--text-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
}
.guide-rail-link:hover {
  color: var(--brand-navy);
  border-color: var(--brand-navy);
  background: color-mix(in srgb, var(--brand-navy) 4%, transparent);
}

.guide-rail-n {
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}

@media (min-width: 60rem) {
  .guide-index-layout {
    display: grid;
    grid-template-columns: 13rem minmax(0, 1fr);
    gap: var(--space-4xl);
    align-items: start;
  }

  .guide-rail {
    position: sticky;
    /* Clears the sticky offset used by the anchor targets below, so the rail
       and the heading it just scrolled to are never overlapping. */
    top: var(--space-3xl);
    margin: 0;
  }

  /* Vertical on desktop: the pills were a small-screen accommodation, and a
     column reads as a table of contents where a wrapped row reads as filters. */
  .guide-rail-list { display: block; }
  .guide-rail-link {
    justify-content: space-between;
    width: 100%;
    min-height: 0;
    padding: var(--space-sm) var(--space-md);
    border: 0;
    border-left: 2px solid var(--border-subtle);
    border-radius: 0;
  }
  .guide-rail-link:hover {
    border-left-color: var(--brand-navy);
    background: color-mix(in srgb, var(--brand-navy) 4%, transparent);
  }
}

.guide-group { margin: 0 0 var(--space-4xl); }

/* An anchored heading must not land under the sticky rail, and on small screens
   it must not land flush against the top edge either. */
.guide-group { scroll-margin-top: var(--space-3xl); }

.guide-group-h {
  font-family: var(--font-heading);
  font-size: 1.25rem;
  font-weight: 650;
  line-height: 1.25;
  margin: 0 0 var(--space-lg);
  color: var(--brand-navy);
}

.guide-index { margin: 0; padding: 0; list-style: none; }
.guide-index li {
  margin: 0;
  border-top: 1px solid var(--border-subtle);
}
.guide-index li:last-child { border-bottom: 1px solid var(--border-subtle); }
.guide-index a {
  display: block;
  padding: var(--space-lg) var(--space-md);
  min-height: 44px;
  text-decoration: none;
}
.guide-index a:hover { background: var(--bg-hover); text-decoration: underline; }

/* ── THE WORKBENCH ───────────────────────────────────────────────────────────
   The guide's checklist. Every color is a token, same rule as the rest of this
   file — the CI gate scans it.

   Sizing note: the checkbox is 24px and the label row has 44px of tappable
   height. Those are the WCAG 2.2 SC 2.5.8 floor and the comfortable-target
   figure respectively, stated here so a later density pass has to argue with
   them rather than quietly cross them, which is exactly how a 23px control
   shipped on this surface once already. */
.wb-intro { color: var(--at-text-secondary); max-width: 60ch; }

.wb-progress {
  display: flex; align-items: center; gap: var(--space-lg);
  margin: var(--space-lg) 0; font-size: 0.9375rem;
}
.wb-count { color: var(--at-text-secondary); }
.wb-reset {
  font: inherit; font-size: 0.875rem; min-height: 24px;
  padding: 0.15rem 0.6rem; cursor: pointer;
  color: var(--at-brand-navy); background: var(--at-bg);
  border: 1px solid var(--at-border-control); border-radius: var(--at-radius);
}
.wb-reset:hover { background: var(--at-hover); }
.wb-reset:focus-visible { outline: 2px solid var(--at-brand-navy); outline-offset: 2px; }

.wb-steps { margin: var(--space-lg) 0 0; padding-left: 0; }
.wb-steps--unordered { list-style: none; }
.wb-unordered-note {
  margin-top: var(--space-xl); color: var(--at-text-secondary);
  font-size: 0.9375rem; max-width: 60ch;
}

.wb-step {
  display: grid; grid-template-columns: auto 1fr; gap: 0 var(--space-lg);
  align-items: start; padding: var(--space-md) 0;
  border-top: 1px solid var(--at-border);
}
.wb-steps--unordered .wb-step { margin-left: 0; }

.wb-check { width: 24px; height: 24px; margin: 0; flex: none; accent-color: var(--at-brand-navy); }
.wb-body { display: block; min-height: 44px; cursor: pointer; }
.wb-label { display: block; color: var(--at-text); }

/* The hedge. Visually distinct from the action because it IS a different kind
   of statement — "if your board requires supervised hours" is a condition, and
   a checklist that renders it in the same weight as the instruction has turned
   a conditional into a requirement. */
.wb-when {
  display: block; margin-top: 0.15rem;
  color: var(--at-text-muted); font-size: 0.875rem; font-style: italic;
}
.wb-detail {
  display: block; margin-top: 0.25rem; max-width: 60ch;
  color: var(--at-text-secondary); font-size: 0.9375rem;
}
.wb-go { display: block; margin-top: 0.3rem; font-size: 0.9375rem; }

/* A ticked step recedes but stays legible — never struck through. Someone
   re-reading a finished step needs to read it, and line-through on a 14px
   hedge is the least readable text on the page. */
.wb-check:checked + .wb-body .wb-label { color: var(--at-text-muted); }

@media print {
  .wb-progress, .wb-reset { display: none; }
  .wb-check { -webkit-appearance: none; appearance: none; border: 1px solid var(--at-border-control); }
}

/* WATCH steps — an amber callout, no checkbox. The warning holds its editorial
   position in the flow (zoning-before-lease ahead of step 1). Color is not the
   only signal: no checkbox and no number already distinguish it (1.4.1). */
.wb-step--watch {
  display: block;
  padding: var(--space-md) var(--space-lg);
  border-left: 3px solid var(--at-accent-amber);
  background: color-mix(in srgb, var(--at-accent-amber) 6%, transparent);
  border-radius: var(--at-radius);
  list-style: none;
}
.wb-watch-label { margin: 0; font-weight: 600; color: var(--at-text); }
.wb-items { margin: 0.35rem 0 0; padding-left: 1.1rem; }
.wb-items li { color: var(--at-text-secondary); font-size: 0.9375rem; margin-bottom: 0.2rem; }

/* B2 cases — dated, sourced facts on a step. Bordered, not colored-only. */
.wb-case {
  display: block;
  margin-top: 0.35rem;
  padding: 0.4rem 0.6rem;
  max-width: 60ch;
  font-size: 0.875rem;
  color: var(--text-secondary);
  border-left: 3px solid var(--border-default);
  background: color-mix(in srgb, var(--text-primary) 4%, transparent);
  border-radius: var(--radius-sm);
}
.wb-case a { white-space: nowrap; }

/* ── Detail-page rail (2026-08-27) ───────────────────────────────────────────
   The index rail's language, carried onto the eighteen guide pages so a
   founder can move between guides without going back through the index.
   Desktop only: below the grid breakpoint the list would stack eighteen links
   above the answer the founder just clicked for — the back link is the
   small-screen path. */
.guide-rail--detail { display: none; }

@media (min-width: 60rem) {
  .guide-page--detail { max-width: 52rem; }
  .guide-rail--detail { display: block; }

  .guide-rail-goal {
    margin: var(--space-lg) 0 var(--space-2xs);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
  }
  .guide-rail-goal:first-of-type { margin-top: 0; }

  .guide-rail-list--detail { display: block; }
  .guide-rail-link--detail {
    display: block;
    width: 100%;
    padding: var(--space-2xs) var(--space-md);
    min-height: 0;
    font-size: 0.8125rem;
    line-height: 1.35;
    border: 0;
    border-left: 2px solid var(--border-subtle);
    border-radius: 0;
  }
  .guide-rail-link--detail[aria-current="page"] {
    color: var(--brand-navy);
    font-weight: 600;
    border-left-color: var(--brand-navy);
    background: color-mix(in srgb, var(--brand-navy) 5%, transparent);
  }

  /* Eighteen links can outrun a short viewport; the rail scrolls itself
     rather than pinning the page. */
  .guide-rail--detail {
    max-height: calc(100vh - 2 * var(--space-3xl));
    overflow-y: auto;
  }
}

/* ── The copy control (2026-08-27) ───────────────────────────────────────────
   Injected by copy.js (a copy button with no clipboard would lie about its
   own effect, so the static page never renders one). Top right of the
   content column, with a small hover tip. */
.guide-index-main { position: relative; }

.guide-copy {
  position: absolute;
  top: 0;
  right: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md, 8px);
  color: var(--text-muted);
  cursor: pointer;
}
.guide-copy:hover {
  color: var(--brand-navy);
  border-color: var(--brand-navy);
  background: color-mix(in srgb, var(--brand-navy) 4%, transparent);
}
.guide-copy:focus-visible {
  outline: 2px solid var(--brand-navy);
  outline-offset: 2px;
}

/* The tip. Shown on hover and on keyboard focus alike. */
.guide-copy::after {
  content: attr(data-tip);
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  padding: 3px 8px;
  font-size: 0.75rem;
  line-height: 1.4;
  white-space: nowrap;
  color: var(--brand-on-navy);
  background: var(--brand-navy);
  border-radius: var(--radius-sm, 4px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease;
}
.guide-copy:hover::after,
.guide-copy:focus-visible::after { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .guide-copy::after { transition: none; } }

/* The visually-hidden live region inside the button. */
.guide-copy-live {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* Keep the back link clear of the button. */
.guide-page--detail .guide-back { padding-right: 3rem; }

/* Touch floor, same gate as the app's. 2.25rem is a mouse size. */
@media (max-width: 768px), (pointer: coarse) {
  .guide-copy { width: 44px; height: 44px; }
}

/* ── /plan — the workbench as its own page (P2/14) ──────────────────────────
   Reuses every .wb-* rule above; this owns only the page's grouping chrome. */
.guide-page--plan { max-width: 52rem; }
.plan-noscript { color: var(--at-text-secondary); }
.plan-summary {
  margin: 0 0 var(--space-xl); padding: var(--space-md) var(--space-lg);
  border: 1px solid var(--at-border-control); border-radius: var(--at-radius);
  background: var(--at-bg); color: var(--at-text); font-weight: 600;
}
.plan-h { font-size: 1.125rem; margin: var(--space-2xl) 0 var(--space-lg); color: var(--at-text-secondary); text-transform: uppercase; letter-spacing: 0.06em; }
.plan-guide { margin: 0 0 var(--space-3xl); padding: 0 0 var(--space-xl); border-bottom: 1px solid var(--at-border-control); }
.plan-guide:last-child { border-bottom: 0; }
.plan-guide-goal { margin: 0; font-size: 0.875rem; color: var(--at-text-secondary); }
.plan-guide .guide-section { margin-bottom: var(--space-lg); }
.plan-guide-open { margin: 0; font-size: 0.9375rem; }
.wb-plan-link { margin: var(--space-lg) 0 0; font-size: 0.9375rem; }

/* ── SITE MASTHEAD (generated; scripts/lib/site-masthead.mjs) ────────────── */
.atlas-mh {
  position: sticky;
  top: 0;
  z-index: 40;
  --mh-ink: var(--brand-on-navy);
  --mh-rule: color-mix(in srgb, var(--brand-on-navy) 24%, transparent);
  border-bottom: 1px solid var(--mh-rule);
}
/* A PERSISTENT BOTTOM BAR, matching the app (user-directed 2026-09-24). The
   two mastheads must not disagree about where the site's own furniture lives:
   a reader crossing from a guide page to the app would watch the strip jump
   from the top of the screen to the bottom.

   NO SCRIPT HERE, BY RULE — these pages are deliberately script-free, so the
   height cannot be measured and written back the way the app does it. The
   reserved space is a media-query value instead, taken from the app's measured
   heights at the same breakpoints (28 / 49 / 76px), which transfer because the
   strings and the type scale are shared. If the attribution string changes
   length, re-measure. */
.atlas-mh__utility {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 30;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  flex-wrap: wrap;
  padding: var(--space-2xs) var(--space-md);
  background: var(--brand-navy-deep);
  color: var(--brand-sky-label);
  border-top: 1px solid color-mix(in srgb, var(--brand-sky-label) 22%, transparent);
  font-size: var(--text-caption-sm);
}
/* The document stops where the bar starts. Applied to body rather than to a
   wrapper because these pages have several different shells. */
body { padding-bottom: 28px; }
@media (max-width: 768px) { body { padding-bottom: 49px; } }
@media (max-width: 430px) { body { padding-bottom: 76px; } }
.atlas-mh__utilnav { display: flex; gap: var(--space-md); }
.atlas-mh__utilnav a,
.atlas-mh__nav a {
  color: inherit;
  text-decoration: none;
  /* A masthead link is a target, not a word in a sentence: the 44px floor is
     the rule the density rule yields to (design-taste 3.5). */
  min-height: var(--control-min-height);
  display: inline-flex;
  align-items: center;
}
.atlas-mh__utilnav a:hover,
.atlas-mh__nav a:hover { color: var(--brand-on-navy); text-decoration: underline; }
.atlas-mh__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  padding: var(--space-2xs) var(--space-md);
  background: var(--brand-navy);
}
.atlas-mh__brand {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-xs);
  min-height: var(--control-min-height);
  color: var(--mh-ink);
  text-decoration: none;
}
/* The mark, centered rather than baseline-aligned: the lockup aligns on the
   BASELINE, which is right for two runs of text and wrong for a picture, which
   would hang its bottom edge on the descriptor's baseline and sit far too
   high. Sized in em so it tracks the wordmark rather than a fixed pixel height
   that drifts from the type at any breakpoint. */
.atlas-mh__mark {
  /* 0.78em, not 1.9 — measured at 29px against an 11px text line, so the mark
     overhung the words. Matches the wordmark's optical band (user 2026-09-24). */
  height: 0.78em;
  width: auto;
  align-self: center;
  flex: 0 0 auto;
  display: block;
}
.atlas-mh__name {
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.06em;
}
.atlas-mh__desc {
  /* --text-micro, the public scale's smallest allowed size. The mark beside it
     is larger, so the step survives without going under the floor. */
  font-size: var(--text-micro);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--brand-sky-label);
}
.atlas-mh__nav {
  display: flex;
  gap: var(--space-md);
  flex-wrap: wrap;
  color: var(--brand-sky-soft);
  font-size: var(--text-body-sm);
}
.atlas-mh__utilnav a[aria-current='page'],
.atlas-mh__nav a[aria-current='page'] {
  color: var(--brand-on-navy);
  box-shadow: inset 0 -2px 0 var(--brand-sky-soft);
}
.atlas-mh__utilnav a:focus-visible,
.atlas-mh__nav a:focus-visible,
.atlas-mh__brand:focus-visible {
  outline: 2px solid var(--brand-on-navy);
  outline-offset: 2px;
}
/* AT PHONE WIDTHS THE STRIP TRADES ITS LINKS FOR THE ATTRIBUTION (user-directed
   2026-09-19: a powered-by banner on top, on mobile). It used to do the
   opposite — the three links stayed and the powered-by stood down — which is
   why the line was invisible on every phone.
   IT ALSO MAKES THE MASTHEAD SHORTER, not taller, and that is the point. Each
   of the three rows is 44px because every link in it carries the touch floor,
   so a row of LINKS costs 44px while a row of TEXT costs about 20. Dropping the
   utility nav here and showing the attribution instead returns ~24px of a
   phone viewport AND puts the banner on top.
   NOTHING BECOMES UNREACHABLE, which is the only reason this is allowed:
   "About" is where the brand mark already goes, "Contact" and GOED are both in
   the footer below, and on the Guide pages "Get Started" is the CURRENT page.
   Above 480px the strip is unchanged — there the links and the line both fit. */
@media (max-width: 29.99rem) {
  .atlas-mh__utilnav { display: none; }
  .atlas-mh__utility {
    justify-content: center;
    padding: 3px var(--space-md);
    text-align: center;
  }
  /* THE DESCRIPTOR STANDS DOWN so the brand and the section nav fit on ONE row.
     Measured at 430px: brand 285px + nav 248px exceeds the viewport, so the bar
     wrapped to two rows of 44px and the masthead cost 124px of a 932px screen.
     The 44px is a touch floor and does not move; the descriptor is what makes
     the row too wide. Nothing is lost — every page under this masthead names
     itself immediately below it, and the brand mark still reads ATLAS. */
  .atlas-mh__desc { display: none; }
}
