/* ─── Self-hosted variable webfonts (Train 4, 2026-05-12) ─────────────
   Real fonts: Inter / EB Garamond / JetBrains Mono. Latin subset only.
   Metric-matched fallback @font-face declarations follow so the FOUT
   window has zero layout shift. */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("./fonts/inter-variable-latin.woff2") format("woff2");
}
@font-face {
  font-family: "EB Garamond";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("./fonts/eb-garamond-variable-latin.woff2") format("woff2");
}
@font-face {
  font-family: "EB Garamond";
  font-style: italic;
  font-weight: 400 800;
  font-display: swap;
  src: url("./fonts/eb-garamond-italic-variable-latin.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("./fonts/jetbrains-mono-variable-latin.woff2") format("woff2");
}

/* ─── Metric-matched fallbacks (Train 4 — eliminates swap-CLS) ────────
   Each declaration locally aliases a system font then overrides its
   reported metrics so glyphs occupy the same vertical box as the
   matching webfont. When the webfont arrives, the swap is layout-
   neutral. The browser uses these fallbacks until the real font loads
   (FOUT), at which point the swap is visually unnoticeable.

   Numbers verified against:
     - Inter v20         ascent  968 / descent  241 / line-gap   0 / units 1000
     - Arial Liberation  ascent 1854 / descent  434 / line-gap  67 / units 2048
       → Inter Fallback (Arial-based) needs size-adjust ~107%, ascent
         override ~90%, descent override ~22.4%.
     - EB Garamond v32   ascent  860 / descent  282 / line-gap   0 / units 1000
     - Georgia           ascent 1878 / descent  450 / line-gap   0 / units 2048
       → EB Garamond Fallback (Georgia) needs size-adjust ~98%,
         ascent override ~88.6%, descent override ~29%.
     - JetBrains Mono v20 ascent 1020 / descent  300 / line-gap   0 / units 1000
     - Menlo / ui-mono    ascent 1556 / descent  424 / line-gap   0 / units 2048
       → JBM Fallback (ui-monospace) needs size-adjust ~104%, ascent
         override ~98%, descent override ~29%. */

@font-face {
  font-family: "Inter Fallback";
  font-style: normal;
  font-weight: 100 900;
  src: local("Arial");
  size-adjust: 107.4%;
  ascent-override: 90.2%;
  descent-override: 22.4%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "EB Garamond Fallback";
  font-style: normal;
  font-weight: 400 800;
  src: local("Georgia");
  size-adjust: 97.8%;
  ascent-override: 87.9%;
  descent-override: 28.8%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "EB Garamond Fallback";
  font-style: italic;
  font-weight: 400 800;
  src: local("Georgia Italic"), local("Georgia");
  size-adjust: 97.8%;
  ascent-override: 87.9%;
  descent-override: 28.8%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "JetBrains Mono Fallback";
  font-style: normal;
  font-weight: 100 800;
  src: local("Menlo"), local("Consolas"), local("Courier New");
  size-adjust: 104.0%;
  ascent-override: 98.0%;
  descent-override: 28.8%;
  line-gap-override: 0%;
}
/* Endenza dashboard — shared design system (aligned with modular pages) */
:root {
  --bg: #faf7f2; --bg-raised: #ffffff; --bg-elevated: #fdfaf4; --bg-sunken: #f2ede2;
  --ink: #18150f; --ink-2: #3c362c; --ink-3: #6e6657; --ink-4: #a8a193;
  --rule: #ebe4d4; --rule-2: #f0ead9;
  --accent: #c2711f; --accent-2: #e08a2c; --accent-3: #f3c07a; --accent-ink: #3a1f04;
  --accent-soft: rgba(194,113,31,0.08);
  --danger: #c73a2a;
  /* Narrative voice accents — 4 hand-picked brand-adjacent shades for the
     .endenza-story__voice border-lefts on the homepage. Voice 3 reuses
     --accent so the brand chord ties the set together. 2026-05-18 added
     as tokens to keep raw hex out of page CSS. */
  --voice-accent-1: #d56b3b;
  --voice-accent-2: #6fa6d8;
  --voice-accent-4: #9c7ec0;
  --voice-accent-5: #4a9a7a;
  /* Train 4 (2026-05-12) — metric-matched fallback families ship in
     styles/fonts.py so the FOUT window has zero CLS. The fallback name
     is listed BETWEEN the webfont and the system stack so the browser
     paints with the fallback first (matched metrics → no shift on
     swap), then switches to the real webfont once loaded. */
  --serif: "EB Garamond", "EB Garamond Fallback", ui-serif, Georgia, serif;
  --sans: "Inter", "Inter Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --mono: "JetBrains Mono", "JetBrains Mono Fallback", ui-monospace, "SF Mono", Menlo, monospace;
  --terminal-mono: ui-monospace, "SF Mono", Menlo, "Cascadia Code", monospace;
  --maxw: 1180px; --gutter: clamp(20px, 4vw, 40px);
  --radius: 16px; --radius-md: 12px; --radius-sm: 10px; --radius-pill: 9999px;
  --ease: cubic-bezier(.2, .7, .2, 1);
  --shadow-sm: 0 1px 2px rgba(24,21,15,0.04), 0 2px 4px rgba(24,21,15,0.02);
  --shadow-md: 0 4px 24px -8px rgba(194,113,31,0.18), 0 2px 8px rgba(24,21,15,0.04);
  /* Crisp 1px ink-toned drop for small brand marks (topnav logo tile).
     Tighter + more opaque than --shadow-sm's soft two-layer card shadow,
     and ink-toned (--ink #18150f) rather than pure black so it reads warm
     on the bone background. Replaces a raw rgba(24,21,15,0.18) literal. */
  --shadow-brand: 0 1px 3px rgba(24,21,15,0.18);
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f0d0a; --bg-raised: #1a1711; --bg-elevated: #1f1b14; --bg-sunken: #141109;
    --ink: #f3ecdd; --ink-2: #d6cdb9; --ink-3: #9a9078; --ink-4: #5e564a;
    --rule: #2a2620; --rule-2: #221e18;
    --accent: #e08a2c; --accent-2: #f3c07a; --accent-3: #7a4a16; --accent-ink: #1a0d02;
    --accent-soft: rgba(224,138,44,0.10);
    --danger: #ef6a5c;
    --shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
    --shadow-md: 0 4px 24px -8px rgba(224,138,44,0.25);
    /* On dark surfaces the brand-mark drop reads against near-black, so
       switch to a pure-black shadow (matches --shadow-sm's dark shift). */
    --shadow-brand: 0 1px 3px rgba(0,0,0,0.4);
  }
}

/* ─── Slot α (warp-20260429T230955Z, 2026-04-29) — substrate tokens ── */
:root {
  /* — Spacing scale (4px base; 8pt grid friendly) — */
  --space-0: 0;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --space-9: 96px;

  /* Shared desktop top-nav cluster metrics. */
  --topnav-cluster-gap: var(--space-1);
  --topnav-link-padding-block: var(--space-2);
  --topnav-link-padding-inline: 14px;
  --topnav-brand-mark-size: 28px;

  /* City map firefly accents. */
  --city-firefly-core: #f8dc8a;
  --city-firefly-glow: rgba(248, 220, 138, 0.95);
  --city-firefly-glow-soft: rgba(248, 220, 138, 0.55);
  --city-firefly-glow-green: rgba(119, 184, 148, 0.35);
  --city-firefly-spark: rgba(255, 255, 255, 0.88);

  /* — Modular type scale (≈1.25 ratio, hand-tuned) — */
  --fs-11: 11px;
  --fs-12: 12px;
  --fs-13: 13px;
  --fs-14: 14px;
  --fs-15: 15px;
  --fs-16: 16px;
  --fs-18: 18px;
  --fs-22: 22px;
  --fs-24: 24px;
  --fs-28: 28px;
  --fs-32: 32px;
  --fs-36: 36px;
  --fs-56: 56px;

  /* — Line-height tokens — */
  --lh-tight: 1.15;
  --lh-snug: 1.35;
  --lh-relaxed: 1.55;

  /* — Hero / page-header canon (2026-06-02) —
     ONE typographic system for every page hero. The home page
     (endenza_body.py `.endenza-showcase__head`) is the reference
     treatment; these tokens lift its headline scale, weight, line-
     height, and tracking so every other page hero — Tools, Atlas,
     Settings, Suite Library, Docs, the bare `.hero` fallback — reads
     as the same system instead of drifting to its own size/weight.
     Before this, five competing h1 rules existed (layout `h1`,
     cards `body.hub .hero h1`, critical `body.hub .hero h1`,
     typography `.doc-title`, and the showcase head). All now route
     through these vars. Add a new value here, never a per-page px. */
  --fs-hero: clamp(40px, 6.6vw, 74px);     /* hero h1 headline */
  --fs-hero-lead: clamp(17px, 1.9vw, 21px);/* hero lead / subhead */
  --lh-hero: 1.02;                          /* hero h1 line-height */
  --lh-hero-lead: 1.45;                     /* hero lead line-height */
  --weight-hero: 500;                       /* hero h1 weight (serif) */
  --tracking-hero: -0.025em;                /* hero h1 letter-spacing */
  --tracking-eyebrow: 0.14em;               /* eyebrow letter-spacing */

  /* — Container widths (3-step) — */
  --w-prose: 760px;
  --w-app: 1180px;
  --w-wide: 1200px;

  /* — Canvas stage background (intentionally dark; theme-stable) — */
  --canvas-stage-bg: #15161a;

  /* — Transition duration aliases — */
  --dur-fast: 120ms;
  --dur-base: 180ms;
  --dur-slow: 200ms;

  /* — Missing alias from audit (restores 11+ broken refs in buttons.py) — */
  --ink-1: var(--ink);

  /* — Status colors (semantic; theme-overridable) — */
  --success: #16a34a;
  --warning: #d97706;
  --danger:  #dc2626;

  /* — Status status-light aliases (currently-undefined refs the audit
     flagged) — components referenced var(--ok, #hex) / var(--warn, #hex)
     and silently fell through to the hex fallback because --ok / --warn
     were never defined. Define them as semantic aliases so the fallback
     hex is never reached. */
  --ok:   var(--success);
  --warn: var(--warning);

  /* — macOS-style traffic-light status dots (status page, tour mockups) —
     The familiar red/amber/green window-control dots. Intentionally
     theme-invariant: this exact triad reads as "system status" on every
     palette, the same way it does in the OS chrome it evokes. Used as the
     dot fill AND as the low-opacity tint behind status pills. The readable
     text-on-tint shades live in the -ink trio below (the bright dot hue
     is too light to use directly as body text — see contrast note). */
  --status-ok:       #28c840;  /* up / healthy */
  --status-degraded: #febc2e;  /* degraded / caution */
  --status-down:     #ff5f57;  /* down / error */
  /* Darker, AA-tuned text shades for labels sitting on the matching
     status tint (status-row state pills, tour "armed" pill). Kept distinct
     from the bright dot hues above so on-tint text stays legible. */
  --status-ok-ink:       #1f8c2c;
  --status-degraded-ink: #b07700;
  --status-down-ink:     #c0332d;
  /* Deep, opaque status fills sized for WHITE text (solid status badges,
     e.g. the a11y dev widget chips). The bright dot hues above are too
     light to carry white text at AA; these deeper shades clear 5:1 with
     --on-status. Theme-invariant — an opaque badge keeps its own contrast
     on any page background. */
  --status-ok-solid:       #15803d;
  --status-degraded-solid: #b45309;
  --status-down-solid:     #b91c1c;
  /* Theme-invariant white for text sitting on a solid status fill. (The
     accent's on-color is --accent-ink; status fills need their own
     on-color because they are not the accent surface.) */
  --on-status: #ffffff;

  /* — Spend-cap meter fills (settings spend widget) — alias to the
     warning/danger status hues; the meter turns amber as it nears the
     cap and red once blown, matching the standard status semantics. */
  --spend-near: var(--warning);
  --spend-full: var(--danger);

  /* — Project lifecycle pill accents (states not covered by accent /
     success / danger) — launch-prep reads as a forward-looking violet;
     warmer reads as a warm orange. Hand-picked; theme-invariant so the
     lifecycle legend stays stable across palettes. */
  --pill-launch-prep: #a855f7;
  --pill-warmer:      #f97316;

  /* — Overlay scrim tokens (pure-black transparency; theme-invariant) —
     Replaces raw rgba(0,0,0,X) literals in page bodies. Step names match
     opacity as a two-digit integer (04 = 4 %, 55 = 55 %). No dark-mode
     override needed — black is black on every palette. Inventory from
     runs/warp-20260518T090101Z/c/report.md (endenza_body, studio, your-studio). */
  --overlay-03: rgba(0, 0, 0, 0.03);  /* hairline light-mode topnav underline */
  --overlay-04: rgba(0, 0, 0, 0.04);  /* whisper hover layer */
  --overlay-08: rgba(0, 0, 0, 0.08);  /* pressed / low-key scrim */
  --overlay-10: rgba(0, 0, 0, 0.10);  /* subtle card shadow */
  --overlay-12: rgba(0, 0, 0, 0.12);  /* card hover shadow */
  --overlay-14: rgba(0, 0, 0, 0.14);  /* secondary modal shadow layer (app.html publish modal) */
  --overlay-15: rgba(0, 0, 0, 0.15);  /* dropdown / popover shadow */
  --overlay-16: rgba(0, 0, 0, 0.16);  /* soft card shadow (homepage Arcade SVG) */
  --overlay-18: rgba(0, 0, 0, 0.18);  /* medium panel shadow */
  --overlay-22: rgba(0, 0, 0, 0.22);  /* drawer backdrop */
  --overlay-24: rgba(0, 0, 0, 0.24);  /* stronger panel backdrop */
  --overlay-28: rgba(0, 0, 0, 0.28);  /* hero vignette + structural shadow */
  --overlay-32: rgba(0, 0, 0, 0.32);  /* modal scrim */
  --overlay-45: rgba(0, 0, 0, 0.45);  /* settings modal scrim */
  --overlay-48: rgba(0, 0, 0, 0.48);  /* app.html publish-celebrate modal scrim */
  --overlay-55: rgba(0, 0, 0, 0.55);  /* dialog backdrop */

  /* — White-overlay scrim tokens (for borders/highlights on colored surfaces) —
     Mirror of the black overlay ladder. Used for borders + highlights when
     a control sits over a saturated background (where black overlays read
     as grime; white overlays read as glass). Theme-invariant. */
  --overlay-white-10: rgba(255, 255, 255, 0.10);
  --overlay-white-18: rgba(255, 255, 255, 0.18);
  --overlay-white-40: rgba(255, 255, 255, 0.40);
  --overlay-white-50: rgba(255, 255, 255, 0.50);
  --overlay-white-75: rgba(255, 255, 255, 0.75);
  --overlay-white-88: rgba(255, 255, 255, 0.88);  /* bright inner sparkle */

  /* Map firefly accents. Theme-invariant because these are tiny emissive
     particles over the walkable city, not readable UI surfaces. */
  --map-firefly-core: #f8dc8a;
  --map-firefly-glow: rgba(248, 220, 138, 0.95);
  --map-firefly-glow-soft: rgba(248, 220, 138, 0.55);
  --map-firefly-halo: rgba(119, 184, 148, 0.35);

  /* — Council/Maestro surface accent (cyan-blue for "info" appearance) —
     Hoisted from council_body.py inline declaration so the canonical hex
     lives in tokens. Page bodies reference via var(--council-aqua). */
  --council-aqua: #7bcfff;

  /* — Alert box semantic tokens (inline alert chrome bg+fg pairs) —
     Bootstrap-standard defaults for the light palette. Dark-mode overrides
     below use deep-tinted bg + bright luminance-shifted fg (mirrors the
     --success / --warning / --danger shift pattern in the dark block).
     Replaces the six raw hex literals in settings_body.py. */
  --alert-success-bg: #d4edda;
  --alert-success-fg: #155724;
  --alert-warning-bg: #fff3cd;
  --alert-warning-fg: #856404;
  --alert-danger-bg:  #f8d7da;
  --alert-danger-fg:  #721c24;

  /* — Agentic-OS surfaces grid tokens (warp-20260524T0432Z slot d) —
     Homepage 5-tile grid between the dashboard hero and the walkable
     showcase. These name the grid rhythm so page CSS stays magic-number-free. */
  --surfaces-tile-gap: var(--space-5);       /* 24px — gap between tiles */
  --surfaces-tile-pad: var(--space-5);       /* 24px — inner tile padding */
  --surfaces-tile-radius: var(--radius-sm);  /* 10px — tile corner radius */
  --surfaces-section-py: var(--space-8);     /* 64px — section top/bottom padding */
  --walkable-section-py: var(--space-7);     /* 48px — walkable section padding */

  /* — Atlas (/atlas.html) card-grid column sizing —
     Names the min/max track width for the auto-fit card grids on the
     Atlas index so the grid centers within its .wrap (justify-content:
     center) and partial final rows stay balanced instead of left-
     anchoring. min keeps cards readable on tablet; max keeps three
     columns from stretching too wide on a maximized window. */
  --atlas-card-min: 280px;
  --atlas-card-max: 360px;
}
@media (prefers-color-scheme: dark) {
  :root {
    /* Status colors — luminance-shifted for dark backgrounds (same hues). */
    --success: #22c55e;
    --warning: #f59e0b;
    --danger:  #ef4444;
    /* Alert tokens — deep-tinted bg + bright fg for dark surfaces. */
    --alert-success-bg: #0d2e1a;
    --alert-success-fg: #4ade80;
    --alert-warning-bg: #2a1f04;
    --alert-warning-fg: #fbbf24;
    --alert-danger-bg:  #2a0f0f;
    --alert-danger-fg:  #f87171;
  }
}

/* ─── Sprite palette ramps — 16-bit retint spine (2026-05-28) ─────────
   Accent ramp RETINTS via color-mix(var(--accent)); skin / hair / wood /
   ink / bronze are FIXED. Shading relationship is theme-invariant. See
   bin/dashgen/styles/tokens.py header for the full rationale. */
:root {
  /* — ACCENT ramp — RETINTS with the theme (mid = live --accent) —
     Constant mix percentages → the shadow-to-mid delta is identical on
     every one of the 21 themes; only the hue shifts. */
  --sprite-accent-900: color-mix(in srgb, var(--accent), black 46%);  /* deep core shadow */
  --sprite-accent-700: color-mix(in srgb, var(--accent), black 26%);  /* shadow / selout edge */
  --sprite-accent-500: var(--accent);                                 /* base / mid (== --accent) */
  --sprite-accent-300: color-mix(in srgb, var(--accent), white 30%);  /* top-left highlight */
  --sprite-accent-100: color-mix(in srgb, var(--accent), white 58%);  /* rim / specular highlight */

  /* — BRONZE secondary ramp — FIXED (theme-independent) —
     The demoted warm-brass secondary. Baton / brass instrument / warm
     wood-metal accents ONLY. Mirrors the historical bronze-on-ink accent
     trio (#7e4a18 / #c2711f / #e08a2c) so the conductor identity survives
     a cerulean-forward world. */
  --sprite-bronze-700: #7e4a18;  /* bronze shadow */
  --sprite-bronze-500: #c2711f;  /* bronze mid */
  --sprite-bronze-300: #e08a2c;  /* bronze highlight */

  /* — SKIN ramps — FIXED (theme-independent; ART_DIRECTION §5.3) —
     Ten human skin tones (warm-fair → deep), each as a {shadow, mid,
     light} triad with a constant ~top-left-light relationship. Skin must
     NOT shift blue under theme=cerulean — these never appear in a
     body.theme-* override. Tone index ascends light→deep. */
  --sprite-skin-1-shadow: #e8a07c; --sprite-skin-1-mid: #f6c2a0; --sprite-skin-1-light: #ffe0c8;  /* porcelain */
  --sprite-skin-2-shadow: #d99069; --sprite-skin-2-mid: #f0b890; --sprite-skin-2-light: #fcd8ba;  /* fair */
  --sprite-skin-3-shadow: #cf8156; --sprite-skin-3-mid: #e8aa7c; --sprite-skin-3-light: #f8cda4;  /* light-warm */
  --sprite-skin-4-shadow: #c0703f; --sprite-skin-4-mid: #db9663; --sprite-skin-4-light: #f0bd90;  /* golden */
  --sprite-skin-5-shadow: #ad6033; --sprite-skin-5-mid: #c98551; --sprite-skin-5-light: #e3ac7d;  /* tan */
  --sprite-skin-6-shadow: #985330; --sprite-skin-6-mid: #b87545; --sprite-skin-6-light: #d49a6c;  /* olive-tan */
  --sprite-skin-7-shadow: #7e4226; --sprite-skin-7-mid: #9c6038; --sprite-skin-7-light: #be855a;  /* brown */
  --sprite-skin-8-shadow: #683420; --sprite-skin-8-mid: #844c2e; --sprite-skin-8-light: #a66f48;  /* deep-brown */
  --sprite-skin-9-shadow: #4f271a; --sprite-skin-9-mid: #693a26; --sprite-skin-9-light: #8a583a;  /* dark */
  --sprite-skin-10-shadow: #3a1d15; --sprite-skin-10-mid: #4f2c20; --sprite-skin-10-light: #6e4632; /* deepest */

  /* — HAIR ramp(s) — FIXED (theme-independent) —
     Six natural hair colors, each a {shadow, mid, light} triad. Fixed so a
     character keeps the same hair on every theme. */
  --sprite-hair-black-700: #15120f; --sprite-hair-black-500: #2b2521; --sprite-hair-black-300: #4a423a;
  --sprite-hair-brown-700: #3a2415; --sprite-hair-brown-500: #5e3a22; --sprite-hair-brown-300: #8a5c38;
  --sprite-hair-blond-700: #9c6f2a; --sprite-hair-blond-500: #d4a23f; --sprite-hair-blond-300: #f0cd7a;
  --sprite-hair-auburn-700: #5a2412; --sprite-hair-auburn-500: #8a3a1c; --sprite-hair-auburn-300: #b85e34;
  --sprite-hair-gray-700: #6e6a64; --sprite-hair-gray-500: #9a958d; --sprite-hair-gray-300: #c4bfb6;
  --sprite-hair-white-700: #b8b4ac; --sprite-hair-white-500: #ddd9d0; --sprite-hair-white-300: #f4f1e9;

  /* — WOOD ramp — FIXED (theme-independent; "always warm" §5.3) —
     Instruments, benches, stools, ledges, furniture. */
  --sprite-wood-700: #6b4423;  /* wood shadow / grain */
  --sprite-wood-500: #976b3e;  /* wood mid */
  --sprite-wood-300: #c39a64;  /* wood highlight */

  /* — INK / outline ramp — FIXED (theme-independent) —
     Sprite outlines + selective-outline (selout). A near-black warm brown
     (NOT pure #000) so the outline reads on every palette without going
     dead-flat. 900 = primary 1px outline; 700 = interior selout / softer
     contour. */
  --sprite-ink-900: #1a1310;  /* primary outline */
  --sprite-ink-700: #3a2c22;  /* interior selout / soft contour */

  /* — NATURE / ENVIRONMENT ramps — FIXED (theme-independent; ART_DIRECTION
     §5.3 "Sky / outdoors … always sky-blue regardless of theme") —
     The cozy-town world (grass, foliage, water, paths, walls, dirt, sky)
     must read the same on every one of the 21 themes: a forest stays green
     under theme=crimson, a pond stays blue under theme=warm. So — like
     skin / wood / ink — these NEVER appear in a body.theme-<name> override;
     they live ONLY here in :root. The accent ramp above is the retintable
     identity spine; these are the fixed natural backdrop it sits against.
     Values are slightly muted + warm-leaning so they harmonize with the
     cerulean-forward + bronze-secondary identity instead of reading as a
     garish Stardew-clone green. Each ramp keeps the same {700 shadow /
     500 mid / 300 light} top-left-light relationship as every other ramp. */

  /* FOLIAGE green — tree canopy, bushes, hedges, leaf masses (deeper +
     cooler than ground grass so layered planting reads with depth). */
  --sprite-leaf-700: #2f5a32;  /* foliage core shadow */
  --sprite-leaf-500: #4f8a4a;  /* foliage mid */
  --sprite-leaf-300: #7fb45f;  /* foliage top-left highlight */

  /* GROUND grass — lawns, field ground cover. Warmer + a touch lighter
     than --sprite-leaf so the ground plane reads distinct beneath foliage. */
  --sprite-grass-700: #4a7a36;  /* grass shadow / clump */
  --sprite-grass-500: #6fa544;  /* grass mid */
  --sprite-grass-300: #9ec766;  /* grass sunlit highlight */

  /* WATER — ponds, streams, fountains. A cerulean COUSIN but FIXED and a
     touch desaturated so it harmonizes with the live --accent without
     competing with it (and stays blue on every theme). */
  --sprite-water-700: #2a6c93;  /* water depth / shadow */
  --sprite-water-500: #3f9bc4;  /* water mid */
  --sprite-water-300: #7cc6e3;  /* water surface highlight / ripple */

  /* STONE — paths, walls, paving, ledges. Warm-leaning neutral grey (not
     cold concrete) so it sits with the bronze secondary. */
  --sprite-stone-700: #5e5950;  /* stone shadow / mortar */
  --sprite-stone-500: #8a8479;  /* stone mid */
  --sprite-stone-300: #b8b2a4;  /* stone top-left highlight */

  /* SOIL — dirt paths, planters, tilled ground. Warm earth, kept cooler +
     more neutral than --sprite-wood so dirt and timber never read alike. */
  --sprite-soil-700: #6b4a2c;  /* soil shadow / furrow */
  --sprite-soil-500: #946b44;  /* soil mid */
  --sprite-soil-300: #bf9468;  /* soil dry / sunlit highlight */

  /* SKY — outdoor backdrop. ART_DIRECTION §5.3 makes sky a deliberate
     theme-INVARIANT exception ("always sky-blue regardless of theme — a
     deliberate exception that grounds the scene"). Soft + slightly
     desaturated, lighter than water. Two steps (mid + light) — sky reads
     as a gentle top-light gradient, it has no deep core shadow. */
  --sprite-sky-500: #7bb4dd;  /* sky mid (horizon-ward) */
  --sprite-sky-300: #acd4ee;  /* sky light (zenith / highlight) */

  /* ─── INTERIOR MATERIALS (R14 16-bit drawer refinement, 2026-05-30) ───
     Indoor-prop ramps so Studio furniture (arcades, TVs, speakers, couches)
     shades with the same shadow→mid→highlight discipline as the town set.
     Each is a deliberate hue away from its neighbours so materials never
     read alike: metal cool-grey, cloth warm-plum, screen emissive-cerulean. */

  /* METAL — appliances, arcade cabinets, speakers, mic stands, desk legs.
     Cool neutral grey-blue, kept distinct from --sprite-stone (warmer) so
     manufactured metal never reads as masonry. */
  --sprite-metal-900: #20242b;  /* deep shadow / cavity */
  --sprite-metal-700: #33383f;  /* shadow / contour */
  --sprite-metal-500: #576069;  /* mid / body */
  --sprite-metal-300: #8a939e;  /* top-left highlight */
  --sprite-metal-100: #bcc4cd;  /* rim / chrome specular */

  /* CLOTH — upholstery, couches, cushions, curtains. Muted plum so soft
     furnishings sit warm against the bronze/ink without competing with it. */
  --sprite-cloth-700: #3c2b46;  /* shadow / seam */
  --sprite-cloth-500: #5b4568;  /* mid / face */
  --sprite-cloth-300: #7e6390;  /* top-left highlight */

  /* SCREEN — CRT / monitor / arcade glass glow. Cerulean, emissive + brighter
     than --sprite-water; drives the "alive" read of TVs, arcades and neon. */
  --sprite-screen-900: #08182a;  /* dark glass / off pixels */
  --sprite-screen-500: #16527e;  /* mid glow */
  --sprite-screen-300: #2a8ec8;  /* lit phosphor */
  --sprite-screen-100: #9adcff;  /* specular / scanline crest */
}

/* Top nav (shared with home/settings/commands/docs) */
.topnav {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--rule);
}
.topnav .wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  gap: var(--space-5);
  height: 56px;
}
.topnav .brand {
  font-family: var(--serif); font-weight: 600; font-size: var(--fs-18); letter-spacing: -0.01em;
  color: var(--ink); text-decoration: none;
  display: inline-flex; align-items: center; gap: 10px;
}
.topnav .brand em { color: var(--accent-text, var(--accent)); font-style: normal; }
.topnav .brand-mark {
  width: var(--topnav-brand-mark-size); height: var(--topnav-brand-mark-size); border-radius: 7px; display: block;
  flex-shrink: 0; box-shadow: var(--shadow-brand);
}
.topnav .brand-word { line-height: 1; }
@media (max-width: 480px) {
  .topnav .brand-word { display: none; }
  .topnav .brand-mark { width: 30px; height: 30px; }
}
.topnav ul { list-style: none; padding: 0; margin: 0; display: flex; gap: var(--topnav-cluster-gap); flex: 0 1 auto; justify-content: center; }
.topnav li.nav__identity-item,
.topnav li.nav__auth-action-item {
  display: flex;
  align-items: center;
  gap: 6px;
}
/* Desktop layout: the identity chip (left) + Sign-in/Log-out action (right)
   leave normal flow and sit absolutely in the gutters, letting the 7-link
   <ul> stay optically centered. Gated at >=880px so it pairs with the drawer
   breakpoint in nav.py (<=879px). Seam raised 721->880 on 2026-06-01: at the
   old 721 seam the 721-879px band (iPad portrait 768px) used this absolute
   layout, and the centered cluster grew wide enough to overlap the side
   items. Keeping that band in the in-flow drawer layout removes the overlap.
   The two breakpoints MUST stay adjacent (879 / 880). */
@media (min-width: 880px) {
  .topnav li.nav__identity-item,
  .topnav li.nav__auth-action-item {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    margin-left: 0;
  }
  .topnav li.nav__identity-item {
    left: clamp(16px, 3vw, 32px);
  }
  .topnav li.nav__auth-action-item {
    right: clamp(16px, 3vw, 32px);
  }
}
.topnav li a {
  display: block; padding: var(--topnav-link-padding-block) var(--topnav-link-padding-inline); font-size: var(--fs-14); font-weight: 500;
  color: var(--ink-3); text-decoration: none; border-radius: 8px;
  transition: background 200ms var(--ease), color 200ms var(--ease);
}
.topnav .nav__auth-chip {
  display: inline-flex;
  align-items: center;
  min-height: 34px;
  max-width: 170px;
  padding: 0 10px;
  border: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  border-radius: 999px;
  color: var(--ink-2);
  background: color-mix(in srgb, var(--bg-raised, var(--bg)) 70%, transparent);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.topnav li a.nav__auth-link {
  color: color-mix(in srgb, var(--accent-text, var(--accent)) 78%, black 22%);
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 22%, transparent);
}
.topnav li a.nav__auth-link[data-auth-state="signed-in"] {
  color: var(--ink-2);
  background: color-mix(in srgb, var(--ink) 5%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 12%, transparent);
}
.topnav li a.nav__auth-link[aria-disabled="true"] { opacity: 0.7; pointer-events: none; }
.topnav li a:hover { color: var(--ink); background: var(--accent-soft); }
.topnav li a.active { color: var(--accent-text, var(--accent)); background: var(--accent-soft); }
body[data-mode="light"] .topnav {
  background: color-mix(in srgb, var(--bg) 88%, white 12%);
  box-shadow: 0 1px 0 var(--overlay-03);
}
body[data-mode="light"] .topnav li a.active {
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 5%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 8%, transparent);
}
body[data-mode="light"] .topnav li a:hover {
  color: var(--accent-text, var(--accent));
  background: var(--accent-soft);
}
@media (max-width: 640px) {
  .topnav ul { overflow-x: auto; -webkit-overflow-scrolling: touch; }
}
/* WCAG 2.5.5 / Apple HIG: 44x44 minimum tap target on touch viewports. */
@media (max-width: 768px) {
  .topnav li a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}

/* Theme overrides (apply via body.theme-<name>) */
body.theme-slate { --bg: #08111f; --bg-raised: #101a2b; --bg-elevated: #16223a; --bg-sunken: #050a14; --ink: #e7edf7; --ink-2: #c7d2e4; --ink-3: #95a4ba; --ink-4: #65758d; --rule: #25324a; --rule-2: #1b263b; --accent: #2f7bf7; --accent-text: #60a5fa; --accent-2: #60a5fa; --accent-3: #93c5fd; --accent-ink: #ffffff; --accent-soft: rgba(47,123,247,0.18); }
@media (prefers-color-scheme: dark) { body.theme-slate { --bg: #08111f; --bg-raised: #101a2b; --bg-elevated: #16223a; --bg-sunken: #050a14; --ink: #e7edf7; --ink-2: #c7d2e4; --ink-3: #95a4ba; --ink-4: #65758d; --rule: #25324a; --rule-2: #1b263b; --accent: #2f7bf7; --accent-text: #60a5fa; --accent-2: #60a5fa; --accent-3: #93c5fd; --accent-ink: #ffffff; --accent-soft: rgba(47,123,247,0.18); } }
body.theme-mono { --bg: #ffffff; --bg-raised: #f8f8f8; --bg-sunken: #f0f0f0; --ink: #000000; --ink-2: #1a1a1a; --ink-3: #555555; --ink-4: #999999; --rule: #d4d4d4; --rule-2: #e8e8e8; --accent: #000000; --accent-2: #333333; --accent-3: #777777; --accent-soft: rgba(0,0,0,0.06); }
@media (prefers-color-scheme: dark) { body.theme-mono { --bg: #050505; --bg-raised: #111111; --bg-sunken: #000000; --ink: #ffffff; --ink-2: #e5e5e5; --ink-3: #999999; --ink-4: #666666; --rule: #2a2a2a; --rule-2: #1f1f1f; --accent: #949494; --accent-2: #cccccc; --accent-3: #888888; --accent-soft: rgba(255,255,255,0.08); } }
body.theme-forest { --bg: #f4f7f3; --bg-raised: #ffffff; --bg-sunken: #ebf0e9; --ink: #0f1f15; --ink-2: #2c3d33; --ink-3: #5d6f64; --ink-4: #97a59c; --rule: #d8e2d6; --rule-2: #e8efe5; --accent: #117f3a; --accent-2: #22c55e; --accent-3: #86efac; --accent-soft: rgba(22,163,74,0.10); }
@media (prefers-color-scheme: dark) { body.theme-forest { --bg: #0a120c; --bg-raised: #131c16; --bg-sunken: #060a07; --ink: #e8f0e8; --ink-2: #c8d4c9; --ink-3: #8a9a8c; --ink-4: #5a6a5c; --rule: #243228; --rule-2: #1c2820; --accent: #1dab52; --accent-2: #4ade80; --accent-3: #86efac; --accent-soft: rgba(34,197,94,0.14); } }
body.theme-aurora { --bg: #f8f5ff; --bg-raised: #ffffff; --bg-sunken: #ede8f5; --ink: #1a0f2e; --ink-2: #3a2c52; --ink-3: #7060a0; --ink-4: #a896c8; --rule: #e2d8f0; --rule-2: #ece4f5; --accent: #7d49f5; --accent-2: #a78bfa; --accent-3: #c4b5fd; --accent-soft: rgba(139,92,246,0.10); }
@media (prefers-color-scheme: dark) { body.theme-aurora { --bg: #0d0820; --bg-raised: #160d2e; --bg-sunken: #08051a; --ink: #ede4ff; --ink-2: #d4c4f5; --ink-3: #a08cc8; --ink-4: #6e5e8e; --rule: #2c2046; --rule-2: #221638; --accent: #9c7cf9; --accent-2: #c4b5fd; --accent-3: #ddd6fe; --accent-soft: rgba(167,139,250,0.14); } }

body.theme-rose { --bg: #fff5f7; --bg-raised: #ffffff; --bg-sunken: #ffe8ed; --ink: #2a0a14; --ink-2: #4a1f2c; --ink-3: #8a4f66; --ink-4: #c896aa; --rule: #f0d8e0; --rule-2: #f5e4ea; --accent: #dc1c47; --accent-2: #f43f5e; --accent-3: #fda4af; --accent-soft: rgba(225,29,72,0.10); }
@media (prefers-color-scheme: dark) { body.theme-rose { --bg: #1a0810; --bg-raised: #2a1218; --bg-sunken: #0f0408; --ink: #ffe4eb; --ink-2: #f5c0ce; --ink-3: #c88898; --ink-4: #8a5868; --rule: #3a1a26; --rule-2: #2c121a; --accent: #f43f5e; --accent-2: #fb7185; --accent-3: #fda4af; --accent-soft: rgba(244,63,94,0.14); } }

body.theme-ocean { --bg: #f0f9fb; --bg-raised: #ffffff; --bg-sunken: #e0f2f7; --ink: #0a1f26; --ink-2: #1f3d4d; --ink-3: #4d7588; --ink-4: #8ab0bf; --rule: #cbe3eb; --rule-2: #ddebf0; --accent: #077995; --accent-2: #06b6d4; --accent-3: #67e8f9; --accent-soft: rgba(8,145,178,0.10); }
@media (prefers-color-scheme: dark) { body.theme-ocean { --bg: #051418; --bg-raised: #0c1f26; --bg-sunken: #020a0d; --ink: #d8f0f7; --ink-2: #b0d8e3; --ink-3: #7daabe; --ink-4: #4a7388; --rule: #1a3540; --rule-2: #122832; --accent: #05a1bb; --accent-2: #22d3ee; --accent-3: #67e8f9; --accent-soft: rgba(6,182,212,0.14); } }

body.theme-sunset { --bg: #fff7ed; --bg-raised: #ffffff; --bg-sunken: #fde8d0; --ink: #2a1505; --ink-2: #4d2810; --ink-3: #8b5524; --ink-4: #c89970; --rule: #f0d8b8; --rule-2: #f5e4cc; --accent: #c3490a; --accent-2: #f97316; --accent-3: #fdba74; --accent-soft: rgba(234,88,12,0.10); }
@media (prefers-color-scheme: dark) { body.theme-sunset { --bg: #1a0a02; --bg-raised: #2a1408; --bg-sunken: #0e0501; --ink: #ffe8d0; --ink-2: #f5c8a0; --ink-3: #c89570; --ink-4: #8a5f40; --rule: #3a1f0c; --rule-2: #2c1808; --accent: #f46806; --accent-2: #fb923c; --accent-3: #fdba74; --accent-soft: rgba(249,115,22,0.14); } }

body.theme-midnight { --bg: #f4f6fb; --bg-raised: #ffffff; --bg-sunken: #e8ecf5; --ink: #0c1024; --ink-2: #1e2440; --ink-3: #4d557a; --ink-4: #8a92b3; --rule: #d6dbeb; --rule-2: #e3e8f2; --accent: #4f46e5; --accent-2: #6366f1; --accent-3: #a5b4fc; --accent-soft: rgba(79,70,229,0.10); }
@media (prefers-color-scheme: dark) { body.theme-midnight { --bg: #060814; --bg-raised: #0d1024; --bg-sunken: #02030a; --ink: #e0e4f5; --ink-2: #c0c8e0; --ink-3: #8a92b3; --ink-4: #565d80; --rule: #1a1d35; --rule-2: #131628; --accent: #6c6ff2; --accent-2: #818cf8; --accent-3: #a5b4fc; --accent-soft: rgba(99,102,241,0.16); } }

body.theme-sage { --bg: #f6faf2; --bg-raised: #ffffff; --bg-sunken: #ecf2e3; --ink: #182410; --ink-2: #2d3d22; --ink-3: #5a704a; --ink-4: #9ab088; --rule: #d8e3c8; --rule-2: #e8efdc; --accent: #4e7d0a; --accent-2: #84cc16; --accent-3: #bef264; --accent-soft: rgba(101,163,13,0.10); }
@media (prefers-color-scheme: dark) { body.theme-sage { --bg: #0a1505; --bg-raised: #131f0a; --bg-sunken: #050a02; --ink: #e0f0d0; --ink-2: #c0d8a8; --ink-3: #8aa870; --ink-4: #5a7048; --rule: #243018; --rule-2: #1a2410; --accent: #69a312; --accent-2: #a3e635; --accent-3: #bef264; --accent-soft: rgba(132,204,22,0.14); } }

/* ─── Phase 2.5 expansion — 8 new themes for the 18-color spectrum ──── */

body.theme-crimson { --bg: #fff5f5; --bg-raised: #ffffff; --bg-sunken: #ffe5e5; --ink: #2a0a0a; --ink-2: #4a1f1f; --ink-3: #8a4a4a; --ink-4: #c89090; --rule: #f0d0d0; --rule-2: #f5e0e0; --accent: #dc2626; --accent-2: #ef4444; --accent-3: #fca5a5; --accent-soft: rgba(220,38,38,0.10); }
@media (prefers-color-scheme: dark) { body.theme-crimson { --bg: #1a0808; --bg-raised: #2a1010; --bg-sunken: #0e0404; --ink: #ffe0e0; --ink-2: #f5b8b8; --ink-3: #c88080; --ink-4: #8a5050; --rule: #3a1818; --rule-2: #2c1010; --accent: #ef4444; --accent-2: #f87171; --accent-3: #fca5a5; --accent-soft: rgba(239,68,68,0.14); } }

body.theme-amber { --bg: #fffbeb; --bg-raised: #ffffff; --bg-sunken: #fef3c7; --ink: #2a1a02; --ink-2: #4a2f0d; --ink-3: #8a6422; --ink-4: #c8a368; --rule: #f0e0b0; --rule-2: #f5ead0; --accent: #ac5f05; --accent-2: #f59e0b; --accent-3: #fcd34d; --accent-soft: rgba(217,119,6,0.10); }
@media (prefers-color-scheme: dark) { body.theme-amber { --bg: #1a1402; --bg-raised: #2a200a; --bg-sunken: #0e0a01; --ink: #ffeac0; --ink-2: #f5d088; --ink-3: #c8a060; --ink-4: #8a6c40; --rule: #3a2810; --rule-2: #2c1c08; --accent: #ca8208; --accent-2: #fbbf24; --accent-3: #fcd34d; --accent-soft: rgba(245,158,11,0.14); } }

body.theme-sand { --bg: #faf6f0; --bg-raised: #ffffff; --bg-sunken: #f0e6d4; --ink: #2a1f10; --ink-2: #4a3a25; --ink-3: #876d4e; --ink-4: #c8b090; --rule: #e8d8c0; --rule-2: #efe2cc; --accent: #a16207; --accent-2: #ca8a04; --accent-3: #eab308; --accent-soft: rgba(161,98,7,0.10); }
@media (prefers-color-scheme: dark) { body.theme-sand { --bg: #14100a; --bg-raised: #221b10; --bg-sunken: #0a0805; --ink: #f0e0c0; --ink-2: #d4c0a0; --ink-3: #a08868; --ink-4: #685844; --rule: #2a2010; --rule-2: #1c1408; --accent: #c58704; --accent-2: #eab308; --accent-3: #facc15; --accent-soft: rgba(202,138,4,0.14); } }

body.theme-lime { --bg: #f4fff0; --bg-raised: #ffffff; --bg-sunken: #e6ffd0; --ink: #0a2a05; --ink-2: #1a4a10; --ink-3: #3a8025; --ink-4: #80b878; --rule: #c0e8b0; --rule-2: #d8f0c8; --accent: #517e0e; --accent-2: #a3e635; --accent-3: #d9f99d; --accent-soft: rgba(132,204,22,0.12); }
@media (prefers-color-scheme: dark) { body.theme-lime { --bg: #060f02; --bg-raised: #0d1f04; --bg-sunken: #030702; --ink: #e0f5c0; --ink-2: #c0d8a0; --ink-3: #88a868; --ink-4: #586845; --rule: #1c2810; --rule-2: #121d08; --accent: #6ca114; --accent-2: #bef264; --accent-3: #d9f99d; --accent-soft: rgba(163,230,53,0.16); } }

body.theme-teal { --bg: #f0fdfa; --bg-raised: #ffffff; --bg-sunken: #ccfbf1; --ink: #042521; --ink-2: #0d3d36; --ink-3: #357066; --ink-4: #80b8b0; --rule: #a8e0d4; --rule-2: #c4eae0; --accent: #0b8177; --accent-2: #14b8a6; --accent-3: #5eead4; --accent-soft: rgba(13,148,136,0.10); }
@media (prefers-color-scheme: dark) { body.theme-teal { --bg: #021a16; --bg-raised: #0a2520; --bg-sunken: #010d0a; --ink: #d0f8f0; --ink-2: #a0d8c8; --ink-3: #68b0a8; --ink-4: #487868; --rule: #1a3530; --rule-2: #122820; --accent: #12a695; --accent-2: #2dd4bf; --accent-3: #5eead4; --accent-soft: rgba(20,184,166,0.16); } }

body.theme-cobalt { --bg: #eff6ff; --bg-raised: #ffffff; --bg-sunken: #dbeafe; --ink: #0a1a3a; --ink-2: #1e2f5a; --ink-3: #4a5a90; --ink-4: #8a9ac0; --rule: #c8d8f0; --rule-2: #dce5f5; --accent: #1e40af; --accent-2: #2563eb; --accent-3: #93c5fd; --accent-soft: rgba(30,64,175,0.10); }
@media (prefers-color-scheme: dark) { body.theme-cobalt { --bg: #050b1a; --bg-raised: #0a1428; --bg-sunken: #02060f; --ink: #d8e5ff; --ink-2: #b0c5e8; --ink-3: #809ac0; --ink-4: #506888; --rule: #1c2a4a; --rule-2: #131e38; --accent: #467aee; --accent-2: #3b82f6; --accent-3: #60a5fa; --accent-soft: rgba(37,99,235,0.16); } }

body.theme-plum { --bg: #fdf4ff; --bg-raised: #ffffff; --bg-sunken: #f5d0fe; --ink: #2a062a; --ink-2: #4a1a4a; --ink-3: #8a4a8a; --ink-4: #c88ec8; --rule: #f0d0f0; --rule-2: #f5e0f5; --accent: #a21caf; --accent-2: #c026d3; --accent-3: #f0abfc; --accent-soft: rgba(162,28,175,0.10); }
@media (prefers-color-scheme: dark) { body.theme-plum { --bg: #1a061a; --bg-raised: #2a102a; --bg-sunken: #0e030e; --ink: #ffd8ff; --ink-2: #e8a8e8; --ink-3: #b870b8; --ink-4: #784878; --rule: #3a1a3a; --rule-2: #2c102c; --accent: #cd44dd; --accent-2: #d946ef; --accent-3: #f0abfc; --accent-soft: rgba(192,38,211,0.16); } }

/* Polish Round 5 Slot α (2026-05-21) — renamed from `ash` to `cerulean`.
   Round 5b (2026-05-22) — main accent locked at #1e7fc4 (steel-blue,
   the live "Ask Council" button colour the user signed off on). Earlier
   bright sky #39B7FF needed WCAG workarounds (white-on-it = 2.24:1,
   failed button-fill) that drove a light/dark accent flip; #1e7fc4
   carries white labels in both modes (4.66:1 → passes AA normal-text)
   and removes the flip entirely.  --accent-2 is the channel-mean
   midpoint between the new accent and the old sky:
       (30+57)/2, (127+183)/2, (196+255)/2  →  (43, 155, 225)  →  #2c9be2
   Token derivations:
     · `--accent` = `--accent-text` = `--accent-focus` = `--action`
       all #1e7fc4 — one main blue everywhere it can be.
     · `--accent-2` = `--action-2` = #2c9be2 — gradient partner,
       kiss moments, secondary highlight (visibly lighter than --accent
       while staying in the cerulean family).
     · `--action-ink` = #ffffff (white button labels, 4.66:1 on
       --action, passes AA normal-text + 3:1 UI components).
     · Dark mode keeps --accent at #1e7fc4 (same colour, no flip).
       --accent-text in dark mode uses the slightly lighter #2c9be2
       for prose voice (5.4:1 on #2b2d31, passes AA). */
body.theme-cerulean { --bg: #e1e7ef; --bg-raised: #fbfcfe; --bg-sunken: #dce3ec; --ink: #101827; --ink-2: #2d3948; --ink-3: #4f5b6b; --ink-4: #7e8b9b; --rule: #c6d0dc; --rule-2: #dbe2ea; --accent: #1e7fc4; --accent-text: #1666a8; --accent-2: #2c9be2; --accent-3: #8bb8de; --accent-ink: #ffffff; --accent-focus: #1e7fc4; --accent-soft: rgba(30,127,196,0.18); --action: #1e7fc4; --action-2: #2c9be2; --action-ink: #ffffff; --action-soft: rgba(30,127,196,0.16); }
@media (prefers-color-scheme: dark) { body.theme-cerulean { --bg: #2b2d31; --bg-raised: #2b2d31; --bg-sunken: #232529; --ink: #f3f4f6; --ink-2: #d1d5db; --ink-3: #aeb6c2; --ink-4: #7b8492; --rule: #4b5563; --rule-2: #3f4651; --accent: #1e7fc4; --accent-text: #2c9be2; --accent-2: #2c9be2; --accent-3: #9ca3af; --accent-soft: rgba(30,127,196,0.18); } }

/* ─── Three new themes added 2026-04-28 (palette gaps: bold red, true yellow, light neutral) ─── */
body.theme-ruby { --bg: #fff5f5; --bg-raised: #ffffff; --bg-sunken: #ffe4e4; --ink: #2a0606; --ink-2: #4a1414; --ink-3: #8a3a3a; --ink-4: #c87878; --rule: #f0c0c0; --rule-2: #f5d4d4; --accent: #c81825; --accent-2: #ee2e3a; --accent-3: #fca5a5; --accent-soft: rgba(238,46,58,0.10); }
@media (prefers-color-scheme: dark) { body.theme-ruby { --bg: #1a0606; --bg-raised: #2c0a0a; --bg-sunken: #0e0303; --ink: #ffd0d0; --ink-2: #f5a0a0; --ink-3: #c87070; --ink-4: #8a4848; --rule: #3a1010; --rule-2: #2c0c0c; --accent: #ef3743; --accent-2: #f87171; --accent-3: #fecaca; --accent-soft: rgba(244,113,113,0.16); } }

body.theme-lemon { --bg: #fefce8; --bg-raised: #ffffff; --bg-sunken: #fef9c3; --ink: #2a2002; --ink-2: #4a3a05; --ink-3: #8a6e15; --ink-4: #c8a830; --rule: #f0e8a8; --rule-2: #f5edc4; --accent: #a16207; --accent-2: #ca8a04; --accent-3: #facc15; --accent-soft: rgba(202,138,4,0.10); }
@media (prefers-color-scheme: dark) { body.theme-lemon { --bg: #1a1602; --bg-raised: #2c2408; --bg-sunken: #0e0a01; --ink: #fef08a; --ink-2: #fde047; --ink-3: #c8a830; --ink-4: #8a7020; --rule: #3a2c08; --rule-2: #2c2006; --accent: #b49104; --accent-2: #fde047; --accent-3: #fef9c3; --accent-soft: rgba(250,204,21,0.16); } }

/* paper: light variant — INTENTIONALLY light regardless of system mode.
   For users who explicitly want a light/neutral interface.  No dark-mode
   override — paper stays paper. */
body.theme-paper { --bg: #f5f5f3; --bg-raised: #ffffff; --bg-sunken: #ebebe8; --ink: #0a0a0a; --ink-2: #262626; --ink-3: #525252; --ink-4: #8a8a8a; --rule: #d4d4d2; --rule-2: #e3e3e0; --accent: #262626; --accent-2: #404040; --accent-3: #737373; --accent-soft: rgba(38,38,38,0.10); }
@media (prefers-color-scheme: dark) { body.theme-paper { --bg: #f5f5f3; --bg-raised: #ffffff; --bg-sunken: #ebebe8; --ink: #0a0a0a; --ink-2: #262626; --ink-3: #525252; --ink-4: #8a8a8a; --rule: #d4d4d2; --rule-2: #e3e3e0; --accent: #262626; --accent-2: #404040; --accent-3: #737373; --accent-soft: rgba(38,38,38,0.10); } }

/* ─── Limited Edition (2026-05-04 → 2027-05-04) ──────────────────────
   Synthwave / vaporwave / cyberpunk. Vibe-coder aesthetic. These themes
   are inherently dark — the "light mode" palettes here keep the neon
   punch by lifting only the bg toward a pale cream while preserving the
   accent saturation. Per LIMITED_EDITION_THEMES in canvas_atmospheres. */
/* sunset_drive (LE). Hot-pink #ff006e stays the decorative accent (borders,
   glows, gradients). Prose routes through --accent-text (#cc0059, 5.27:1 on
   the pale-magenta bg — AA normal text) so the neon never carries body copy.
   White button labels clear UI 3:1 on the #ff006e fill (3.83:1), so
   --accent-ink stays #ffffff. See tests/test_a11y_contrast.py. */
body.theme-sunset_drive { --bg: #fdf4ff; --bg-raised: #ffffff; --bg-sunken: #f5e8fa; --ink: #1a0118; --ink-2: #3a0e5c; --ink-3: #6e3a8a; --ink-4: #9d75b8; --rule: #ead6f0; --rule-2: #f0e2f5; --accent: #ff006e; --accent-text: #cc0059; --accent-2: #ff3d8c; --accent-3: #ffaad0; --accent-ink: #ffffff; --accent-soft: rgba(255,0,110,0.12); }
@media (prefers-color-scheme: dark) { body.theme-sunset_drive { --bg: #0a0118; --bg-raised: #170730; --bg-sunken: #050010; --ink: #ffd6e6; --ink-2: #f0a8c8; --ink-3: #b878a0; --ink-4: #6e4868; --rule: #2a0e44; --rule-2: #1a0830; --accent: #ff006e; --accent-text: #ff006e; --accent-2: #ff3d8c; --accent-3: #ff80b8; --accent-ink: #ffffff; --accent-soft: rgba(131,56,236,0.20); } }

/* mall_plaza (LE). Electric-blue #0080ff (light) / cyan #00d4ff (dark) stay
   the decorative accent. Prose routes through --accent-text (#0060cc light,
   5.59:1; cyan already 11.44:1 in dark). Button labels: white clears UI 3:1
   on the light #0080ff fill (3.80:1) so --accent-ink stays #ffffff in light;
   in dark, white on cyan is only 1.77:1 (fail) so --accent-ink drops to the
   near-black #020611 (11.44:1). --ink-3 darkened #4080b0 → #36739f (same
   ~205° steel-blue hue) to clear the 4.5:1 muted-text floor on bg (was
   4.02:1 — a pre-existing fail the underscore-blind audit regex had hidden).
   See tests/test_a11y_contrast.py. */
body.theme-mall_plaza { --bg: #f0faff; --bg-raised: #ffffff; --bg-sunken: #d8eeff; --ink: #00102a; --ink-2: #003366; --ink-3: #36739f; --ink-4: #80b0d0; --rule: #cce0f0; --rule-2: #e0eef8; --accent: #0080ff; --accent-text: #0060cc; --accent-2: #d100d1; --accent-3: #ff5cf0; --accent-ink: #ffffff; --accent-soft: rgba(209,0,209,0.16); }
@media (prefers-color-scheme: dark) { body.theme-mall_plaza { --bg: #020611; --bg-raised: #06122a; --bg-sunken: #010308; --ink: #d8f0ff; --ink-2: #80c8ff; --ink-3: #5090c0; --ink-4: #305878; --rule: #2a0a3a; --rule-2: #1a0628; --accent: #00d4ff; --accent-text: #00d4ff; --accent-2: #ff00ff; --accent-3: #ff80f8; --accent-ink: #020611; --accent-soft: rgba(255,0,255,0.22); } }

/* neo_city (LE). Neon-green #00b80c (light) / #39ff14 (dark) stay the
   decorative accent. Neon green is the hardest LE hue for text: prose routes
   through --accent-text (#007a08 light, 5.36:1; the brighter #39ff14 already
   hits 15:1 on the near-black dark bg). White labels fail UI 3:1 on the green
   fill in BOTH modes (2.67:1 light, 1.36:1 dark), so --accent-ink is a
   near-black green-ink: #0a2a05 in light (5.84:1) and #020310 in dark
   (15.14:1). See tests/test_a11y_contrast.py. */
body.theme-neo_city { --bg: #f4ffe8; --bg-raised: #ffffff; --bg-sunken: #e8f5d0; --ink: #021a02; --ink-2: #0a3a08; --ink-3: #2c6a28; --ink-4: #689060; --rule: #c8e4b8; --rule-2: #d8eccc; --accent: #00b80c; --accent-text: #007a08; --accent-2: #1ce020; --accent-3: #80f068; --accent-ink: #0a2a05; --accent-soft: rgba(57,255,20,0.12); }
@media (prefers-color-scheme: dark) { body.theme-neo_city { --bg: #020310; --bg-raised: #080a20; --bg-sunken: #000008; --ink: #d8ffe0; --ink-2: #a0f0a8; --ink-3: #58b068; --ink-4: #386040; --rule: #1a2030; --rule-2: #0a1020; --accent: #39ff14; --accent-text: #39ff14; --accent-2: #6eff48; --accent-3: #a0ff80; --accent-ink: #020310; --accent-soft: rgba(255,16,240,0.20); } }


/* ─── Phase B Slot 1B (2026-04-29) — explicit dark-mode selectors ─────
   Mirrors of the @media (prefers-color-scheme: dark) palettes above,
   on body.theme-<name>[data-mode="dark"] so users who pick "dark" in
   the preferences UI get the dark palette regardless of OS setting.
   Paper is intentionally absent here — its dark variant is hand-tuned
   in themes_light.py rather than being a copy of the light palette. */
body.theme-slate[data-mode="dark"] { --bg: #08111f; --bg-raised: #101a2b; --bg-elevated: #16223a; --bg-sunken: #050a14; --ink: #e7edf7; --ink-2: #c7d2e4; --ink-3: #95a4ba; --ink-4: #65758d; --rule: #25324a; --rule-2: #1b263b; --accent: #2f7bf7; --accent-text: #60a5fa; --accent-2: #60a5fa; --accent-3: #93c5fd; --accent-ink: #ffffff; --accent-soft: rgba(47,123,247,0.18); }
body.theme-mono[data-mode="dark"] { --bg: #050505; --bg-raised: #111111; --bg-sunken: #000000; --ink: #ffffff; --ink-2: #e5e5e5; --ink-3: #999999; --ink-4: #666666; --rule: #2a2a2a; --rule-2: #1f1f1f; --accent: #949494; --accent-2: #cccccc; --accent-3: #888888; --accent-soft: rgba(255,255,255,0.08); }
body.theme-forest[data-mode="dark"] { --bg: #0a120c; --bg-raised: #131c16; --bg-sunken: #060a07; --ink: #e8f0e8; --ink-2: #c8d4c9; --ink-3: #8a9a8c; --ink-4: #5a6a5c; --rule: #243228; --rule-2: #1c2820; --accent: #1dab52; --accent-2: #4ade80; --accent-3: #86efac; --accent-soft: rgba(34,197,94,0.14); }
body.theme-aurora[data-mode="dark"] { --bg: #0d0820; --bg-raised: #160d2e; --bg-sunken: #08051a; --ink: #ede4ff; --ink-2: #d4c4f5; --ink-3: #a08cc8; --ink-4: #6e5e8e; --rule: #2c2046; --rule-2: #221638; --accent: #9c7cf9; --accent-2: #c4b5fd; --accent-3: #ddd6fe; --accent-soft: rgba(167,139,250,0.14); }
body.theme-rose[data-mode="dark"] { --bg: #1a0810; --bg-raised: #2a1218; --bg-sunken: #0f0408; --ink: #ffe4eb; --ink-2: #f5c0ce; --ink-3: #c88898; --ink-4: #8a5868; --rule: #3a1a26; --rule-2: #2c121a; --accent: #f43f5e; --accent-2: #fb7185; --accent-3: #fda4af; --accent-soft: rgba(244,63,94,0.14); }
body.theme-ocean[data-mode="dark"] { --bg: #051418; --bg-raised: #0c1f26; --bg-sunken: #020a0d; --ink: #d8f0f7; --ink-2: #b0d8e3; --ink-3: #7daabe; --ink-4: #4a7388; --rule: #1a3540; --rule-2: #122832; --accent: #05a1bb; --accent-2: #22d3ee; --accent-3: #67e8f9; --accent-soft: rgba(6,182,212,0.14); }
body.theme-sunset[data-mode="dark"] { --bg: #1a0a02; --bg-raised: #2a1408; --bg-sunken: #0e0501; --ink: #ffe8d0; --ink-2: #f5c8a0; --ink-3: #c89570; --ink-4: #8a5f40; --rule: #3a1f0c; --rule-2: #2c1808; --accent: #f46806; --accent-2: #fb923c; --accent-3: #fdba74; --accent-soft: rgba(249,115,22,0.14); }
body.theme-midnight[data-mode="dark"] { --bg: #060814; --bg-raised: #0d1024; --bg-sunken: #02030a; --ink: #e0e4f5; --ink-2: #c0c8e0; --ink-3: #8a92b3; --ink-4: #565d80; --rule: #1a1d35; --rule-2: #131628; --accent: #6c6ff2; --accent-2: #818cf8; --accent-3: #a5b4fc; --accent-soft: rgba(99,102,241,0.16); }
body.theme-sage[data-mode="dark"] { --bg: #0a1505; --bg-raised: #131f0a; --bg-sunken: #050a02; --ink: #e0f0d0; --ink-2: #c0d8a8; --ink-3: #8aa870; --ink-4: #5a7048; --rule: #243018; --rule-2: #1a2410; --accent: #69a312; --accent-2: #a3e635; --accent-3: #bef264; --accent-soft: rgba(132,204,22,0.14); }
body.theme-crimson[data-mode="dark"] { --bg: #1a0808; --bg-raised: #2a1010; --bg-sunken: #0e0404; --ink: #ffe0e0; --ink-2: #f5b8b8; --ink-3: #c88080; --ink-4: #8a5050; --rule: #3a1818; --rule-2: #2c1010; --accent: #ef4444; --accent-2: #f87171; --accent-3: #fca5a5; --accent-soft: rgba(239,68,68,0.14); }
body.theme-amber[data-mode="dark"] { --bg: #1a1402; --bg-raised: #2a200a; --bg-sunken: #0e0a01; --ink: #ffeac0; --ink-2: #f5d088; --ink-3: #c8a060; --ink-4: #8a6c40; --rule: #3a2810; --rule-2: #2c1c08; --accent: #ca8208; --accent-2: #fbbf24; --accent-3: #fcd34d; --accent-soft: rgba(245,158,11,0.14); }
body.theme-sand[data-mode="dark"] { --bg: #14100a; --bg-raised: #221b10; --bg-sunken: #0a0805; --ink: #f0e0c0; --ink-2: #d4c0a0; --ink-3: #a08868; --ink-4: #685844; --rule: #2a2010; --rule-2: #1c1408; --accent: #c58704; --accent-2: #eab308; --accent-3: #facc15; --accent-soft: rgba(202,138,4,0.14); }
body.theme-lime[data-mode="dark"] { --bg: #060f02; --bg-raised: #0d1f04; --bg-sunken: #030702; --ink: #e0f5c0; --ink-2: #c0d8a0; --ink-3: #88a868; --ink-4: #586845; --rule: #1c2810; --rule-2: #121d08; --accent: #6ca114; --accent-2: #bef264; --accent-3: #d9f99d; --accent-soft: rgba(163,230,53,0.16); }
body.theme-teal[data-mode="dark"] { --bg: #021a16; --bg-raised: #0a2520; --bg-sunken: #010d0a; --ink: #d0f8f0; --ink-2: #a0d8c8; --ink-3: #68b0a8; --ink-4: #487868; --rule: #1a3530; --rule-2: #122820; --accent: #12a695; --accent-2: #2dd4bf; --accent-3: #5eead4; --accent-soft: rgba(20,184,166,0.16); }
body.theme-cobalt[data-mode="dark"] { --bg: #050b1a; --bg-raised: #0a1428; --bg-sunken: #02060f; --ink: #d8e5ff; --ink-2: #b0c5e8; --ink-3: #809ac0; --ink-4: #506888; --rule: #1c2a4a; --rule-2: #131e38; --accent: #467aee; --accent-2: #3b82f6; --accent-3: #60a5fa; --accent-soft: rgba(37,99,235,0.16); }
body.theme-plum[data-mode="dark"] { --bg: #1a061a; --bg-raised: #2a102a; --bg-sunken: #0e030e; --ink: #ffd8ff; --ink-2: #e8a8e8; --ink-3: #b870b8; --ink-4: #784878; --rule: #3a1a3a; --rule-2: #2c102c; --accent: #cd44dd; --accent-2: #d946ef; --accent-3: #f0abfc; --accent-soft: rgba(192,38,211,0.16); }
body.theme-cerulean[data-mode="dark"] { --bg: #2b2d31; --bg-raised: #2b2d31; --bg-sunken: #232529; --ink: #f3f4f6; --ink-2: #d1d5db; --ink-3: #aeb6c2; --ink-4: #7b8492; --rule: #4b5563; --rule-2: #3f4651; --accent: #1e7fc4; --accent-text: #2c9be2; --accent-2: #2c9be2; --accent-3: #9ca3af; --accent-soft: rgba(30,127,196,0.18); }
body.theme-ruby[data-mode="dark"] { --bg: #1a0606; --bg-raised: #2c0a0a; --bg-sunken: #0e0303; --ink: #ffd0d0; --ink-2: #f5a0a0; --ink-3: #c87070; --ink-4: #8a4848; --rule: #3a1010; --rule-2: #2c0c0c; --accent: #ef3743; --accent-2: #f87171; --accent-3: #fecaca; --accent-soft: rgba(244,113,113,0.16); }
body.theme-lemon[data-mode="dark"] { --bg: #1a1602; --bg-raised: #2c2408; --bg-sunken: #0e0a01; --ink: #fef08a; --ink-2: #fde047; --ink-3: #c8a830; --ink-4: #8a7020; --rule: #3a2c08; --rule-2: #2c2006; --accent: #b49104; --accent-2: #fde047; --accent-3: #fef9c3; --accent-soft: rgba(250,204,21,0.16); }

/* ─── Slot α (warp-20260429T230955Z, 2026-04-29) — status colors ──────
   Explicit-dark mirror of the @media (prefers-color-scheme: dark) status
   palette in tokens.py.  Lets users who pick "dark" in Preferences get
   the same luminance-shifted status hues regardless of OS setting.  Hue
   stays semantic (success → green, warning → orange, danger → red);
   only luminance shifts so the colors clear contrast on dark surfaces.
   --ink-1 mirrors --ink so the audit's missing-alias fix carries through
   the explicit dark cascade too. */
body[data-mode="dark"] {
  --success: #22c55e;
  --warning: #f59e0b;
  --danger:  #ef4444;
  --ink-1: var(--ink);
  /* Alert tokens — explicit-dark mirror of the @media dark overrides in tokens.py. */
  --alert-success-bg: #0d2e1a;
  --alert-success-fg: #4ade80;
  --alert-warning-bg: #2a1f04;
  --alert-warning-fg: #fbbf24;
  --alert-danger-bg:  #2a0f0f;
  --alert-danger-fg:  #f87171;
}

/* ─── Documentation pages — onepager-grade editorial ─────────────────── */
.doc-page { position: relative; padding: clamp(40px, 6vw, 64px) 0 clamp(72px, 9vw, 120px); }
.doc-wrap { max-width: var(--w-prose); margin: 0 auto; padding: 0 clamp(20px, 4vw, 40px); }

.doc-progress {
  position: fixed; top: 56px; left: 0; right: 0; height: 2px; z-index: 50;
  background: transparent; pointer-events: none;
}
.doc-progress__bar {
  height: 100%; width: 0%;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  transition: width 80ms linear;
}

.doc-back {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-12); font-weight: 500; color: var(--ink-3);
  text-decoration: none; margin-bottom: 32px;
  transition: color 200ms var(--ease);
}
.doc-back:hover { color: var(--accent-text, var(--accent)); }

/* Doc / Creator-Library hero. Hero canon (2026-06-02): the doc-page
   headline (Documentation pages + the Creator Recolor / Library guide)
   now shares the canonical hero tokens so it reads as the same system
   as Tools, Atlas, Settings, and the home page. Was its own
   clamp(40px,6vw,72px) / 600 / 1.04 / -0.02em scale. */
.doc-hero { margin: 0 0 clamp(48px, 6vw, 72px); }
.doc-eyebrow {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--sans);
  font-size: var(--fs-12); font-weight: 600; letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase; color: var(--accent-text, var(--accent));
  margin: 0 0 18px;
}
.doc-eyebrow::before {
  content: ""; width: 24px; height: 1px; background: var(--accent); display: inline-block;
}
.doc-title {
  font-family: var(--serif); font-weight: var(--weight-hero);
  font-size: var(--fs-hero); line-height: var(--lh-hero); letter-spacing: var(--tracking-hero);
  color: var(--ink); margin: 0 0 clamp(16px, 2vw, 24px);
}
.doc-title em { font-style: italic; font-weight: 500; color: var(--accent-text, var(--accent)); }
.doc-lead {
  font-family: var(--serif);
  font-size: var(--fs-hero-lead); line-height: var(--lh-hero-lead);
  color: var(--ink-2); margin: 0 0 clamp(20px, 2.5vw, 32px); max-width: 60ch;
}
.doc-meta {
  font-family: var(--mono); font-size: var(--fs-12); color: var(--ink-3);
  margin: 0; display: inline-flex; align-items: center; gap: var(--space-2);
}
.doc-meta code {
  font-family: var(--mono); font-size: var(--fs-12); padding: 2px 8px;
  background: var(--bg-sunken, var(--bg-elevated, var(--bg-raised)));
  border: 1px solid var(--rule); border-radius: 5px; color: var(--ink-2);
}
.doc-meta .dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
  display: inline-block;
}

.doc-foot {
  margin-top: clamp(64px, 8vw, 96px); padding-top: 32px;
  border-top: 1px solid var(--rule);
}

.doc-content {
  font-family: var(--sans);
  color: var(--ink-2);
  line-height: 1.7;
  font-size: var(--fs-16);
}
.doc-content > *:first-child { margin-top: 0; }
.doc-content > *:last-child { margin-bottom: 0; }

/* Drop cap on the very first paragraph, onepager-style */
.doc-content > p:first-of-type::first-letter {
  font-family: var(--serif);
  font-size: 4.4em; line-height: 0.85;
  float: left; margin: 0.06em 0.1em -0.08em 0;
  color: var(--accent-text, var(--accent)); font-weight: 600;
}

.doc-content h1 {
  font-family: var(--serif);
  font-size: clamp(36px, 5vw, 52px); line-height: 1.06; font-weight: 600;
  letter-spacing: -0.02em; color: var(--ink);
  margin: 56px 0 20px;
}
.doc-content h1 em { font-style: italic; font-weight: 500; color: var(--accent-text, var(--accent)); }
.doc-content h2 {
  font-family: var(--serif);
  font-size: clamp(28px, 3.6vw, 36px); line-height: 1.15; font-weight: 500;
  letter-spacing: -0.01em; color: var(--ink);
  margin: clamp(48px, 6vw, 72px) 0 16px; padding-top: 8px;
  position: relative;
}
.doc-content h2::before {
  content: ""; display: block; width: 32px; height: 2px; background: var(--accent);
  margin-bottom: 16px; opacity: 0.85;
}
.doc-content h2 em { font-style: italic; font-weight: 500; color: var(--accent-text, var(--accent)); }
.doc-content h3 {
  font-family: var(--sans);
  font-size: 20px; font-weight: 600; letter-spacing: -0.005em;
  color: var(--ink); margin: 36px 0 12px;
}
.doc-content h4 {
  font-family: var(--sans);
  font-size: var(--fs-16); font-weight: 600; color: var(--ink);
  margin: 24px 0 8px;
}
.doc-content p {
  margin: 0 0 16px;
}
.doc-content blockquote {
  margin: 24px 0;
  padding: 12px 0 12px 20px;
  border-left: 3px solid var(--accent);
  color: var(--ink-3);
  font-family: var(--serif);
  font-size: var(--fs-18); font-style: italic;
}
.doc-content blockquote p:last-child { margin-bottom: 0; }
.doc-content ul, .doc-content ol {
  margin: 16px 0; padding-left: 28px;
}
.doc-content li { margin: 6px 0; }
.doc-content li > p { margin-bottom: 8px; }
.doc-content code {
  font-family: var(--mono); font-size: 0.88em;
  background: var(--bg-sunken); border: 1px solid var(--rule);
  padding: 2px 6px; border-radius: 5px;
  color: var(--ink);
}
.doc-content pre {
  margin: 24px 0;
  background: var(--bg-sunken); border: 1px solid var(--rule);
  border-radius: 10px; padding: 18px 20px;
  overflow-x: auto;
  font-family: var(--mono); font-size: var(--fs-12); line-height: 1.55;
}
.doc-content pre code {
  background: transparent; border: 0; padding: 0;
  font-size: inherit; color: var(--ink);
}
.doc-content a {
  color: var(--accent-text, var(--accent)); text-decoration: underline; text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  transition: color 200ms var(--ease);
}
.doc-content a:hover { color: var(--accent-2); }
.doc-content hr {
  border: 0; border-top: 1px solid var(--rule);
  margin: 48px 0;
}
.doc-content table {
  width: 100%; border-collapse: collapse; margin: 24px 0;
  font-size: var(--fs-14);
}
.doc-content th, .doc-content td {
  border-bottom: 1px solid var(--rule);
  padding: 10px 14px; text-align: left;
}
.doc-content th {
  font-weight: 600; color: var(--ink);
  background: var(--bg-sunken);
}
.doc-content img {
  max-width: 100%; height: auto; border-radius: 8px;
  margin: 16px 0;
}
.doc-content strong { color: var(--ink); font-weight: 600; }
.doc-content em { font-style: italic; }
.doc-content del { color: var(--ink-4); }

/* TOC inline (markdown's [TOC] extension output) */
.doc-content .toc {
  background: var(--bg-raised); border: 1px solid var(--rule);
  border-radius: var(--radius-sm); padding: 16px 20px;
  margin: 24px 0 32px;
  font-size: var(--fs-12);
}
.doc-content .toc > ul { margin: 0; padding-left: 20px; }
.doc-content .toc ul ul { padding-left: 18px; margin: 4px 0; }
.doc-content .toc li { margin: 4px 0; }
.doc-content .toc a {
  color: var(--ink-2); text-decoration: none;
}
.doc-content .toc a:hover { color: var(--accent-text, var(--accent)); text-decoration: underline; }

/* Doc-meta header (above h1 — slug, generated time) */
.doc-meta {
  font-family: var(--mono); font-size: var(--fs-12);
  color: var(--ink-4);
  margin-bottom: 8px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
*,*::before,*::after { box-sizing: border-box; }
html,body { margin:0; padding:0; }
body { font-family:var(--sans); font-size:var(--fs-16); line-height:1.55; color:var(--ink); background:var(--bg);
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale; }
@media (prefers-reduced-motion: reduce) { *,*::before,*::after { animation-duration:.01ms !important; transition-duration:.01ms !important; } }
.wrap { max-width:var(--maxw); margin:0 auto; padding:0 var(--gutter); }
a { color:var(--accent); text-decoration:none; }
code { font-family:var(--mono); font-size:.9em; }

/* Hero shared */
.hero { padding:clamp(32px,5vw,56px) 0 clamp(20px,3vw,32px); position:relative; overflow:hidden; }
.stage-light {
  position:absolute; inset:-20% -20% auto auto;
  width:min(720px,110vw); height:min(720px,110vw);
  background:radial-gradient(closest-side,color-mix(in srgb,var(--accent) 18%,transparent),transparent 70%);
  pointer-events:none; z-index:0;
  animation:drift 22s var(--ease) infinite alternate;
}
@keyframes drift { from { transform:translate(0,0) scale(1); } to { transform:translate(-5%,3%) scale(1.04); } }
.hero-inner { position:relative; z-index:1; }
.eyebrow {
  display:inline-flex; align-items:center; gap:10px;
  font-size:11.5px; font-weight:600; letter-spacing:.14em; text-transform:uppercase;
  color:var(--accent); margin:0 0 18px;
}
.eyebrow::before { content:""; width:22px; height:1px; background:var(--accent); display:inline-block; }
.eyebrow a { color:inherit; border-bottom:1px dotted color-mix(in srgb,var(--accent) 40%,transparent); }
.tm-mark { font-size:0.55em; font-weight:500; letter-spacing:0; vertical-align:super; line-height:1; opacity:0.85; padding-left:1px; }
/* Global h1 = hero canon (2026-06-02). Every <h1> in the generator is a
   page hero headline (body content uses h2/h3), so the element default now
   sources the shared hero tokens — the same scale/weight/tracking/line-height
   as the home reference. This also makes the bare `.hero` fallback correct on
   any page that omits body.hub. Was clamp(36px,6.5vw,60px)/600/1.06/-.014em. */
h1 { font-family:var(--serif); font-weight:var(--weight-hero); font-size:var(--fs-hero); line-height:var(--lh-hero); letter-spacing:var(--tracking-hero); margin:0; color:var(--ink); }
h1 em { font-style:italic; color:var(--accent); font-weight:500; padding:0 .03em; }
h2 { font-family:var(--serif); font-weight:500; font-size:clamp(26px,4vw,38px); line-height:1.16; letter-spacing:-.01em; margin:0 0 14px; color:var(--ink); }
h2 em { font-style:italic; color:var(--accent); font-weight:500; padding:0 .03em; }
p { margin:0 0 14px; color:var(--ink-2); }
.timestamp { margin-top:24px; font-family:var(--mono); font-size:var(--fs-12); color:var(--ink-3); }
.timestamp .dot { display:inline-block; width:7px; height:7px; border-radius:50%; background:var(--accent); margin-right:8px;
  box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 45%,transparent); animation:pulse 3.2s var(--ease) infinite; }
@keyframes pulse {
  0%   { box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 45%,transparent); }
  70%  { box-shadow:0 0 0 9px color-mix(in srgb,var(--accent) 0%,transparent); }
  100% { box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 0%,transparent); }
}

/* ─── Hero variant modifiers (Polish T1a, 2026-05-26) ─────────────────
   Composable modifiers on `.hero`. Use any combination. Bare `.hero` is
   the existing left-aligned default — these variants layer on top.
   See docs/archive/2026-05/POLISH_T1_SUBPLAN_2026-05-26.md for the per-page mapping. */

/* Centered text + eyebrow alignment. Marketing + auth anchor pages. */
/* Hero alignment rule of thumb: marketing / landing surfaces are CENTERED
   (use .hero--centered — home, tour, vibe-coders, for-builders, status,
   tools, marketplace, showcase); content / app surfaces stay LEFT-aligned
   (the bare .hero default — project pages, docs, settings, tool consoles). */
.hero--centered .hero-inner { text-align:center; }
.hero--centered .eyebrow { justify-content:center; }

/* Slot for a brand-mark / logo / hero icon above the eyebrow. Pages
   that lead with the Endenza icon or a per-page symbol. The slot is
   the existing `.brand-mark` class — this just sets layout. */
.hero--with-mark .hero-inner .brand-mark { display:block; margin:0 auto var(--space-3); width:clamp(48px,9vw,128px); height:clamp(48px,9vw,128px); }
.hero--with-mark:not(.hero--centered) .hero-inner .brand-mark { margin-left:0; }

/* Tighter padding for deep pages that already sit under a sub-nav strip
   (e.g. /tools/linked-repos.html). Also drops the `.stage-light` glow
   so the page feels closer to the controls above it. */
.hero--mini { padding:clamp(20px,3vw,32px) 0 clamp(12px,2vw,20px); }
.hero--mini .stage-light { display:none; }

/* Composable slot for an extra row below the lead — chip cluster, CTA
   pair, sub-fine line. Rendered as a `<div class="hero-extras">` child
   inside `.hero-inner` after the lead paragraph. */
.hero-extras { margin-top:var(--space-4); display:flex; flex-wrap:wrap; gap:var(--space-2); }
.hero--centered .hero-extras { justify-content:center; }
.hero--with-extras .hero-extras { /* explicit anchor — same defaults */ }

/* ROSTER (index.html) */
.hero-inner.roster-hero { display:flex; align-items:end; justify-content:space-between; gap:var(--space-6); flex-wrap:wrap; }
.hero-left { flex:1 1 380px; }
.hero-stats { display:flex; gap:24px 36px; flex-wrap:wrap; align-items:end; }
.stat { display:flex; flex-direction:column; gap:2px; }
.stat-num { font-family:var(--serif); font-style:italic; font-weight:500; font-size:var(--fs-36); line-height:1; color:var(--accent); }
.stat-label { font-family:var(--sans); font-size:11.5px; font-weight:500; letter-spacing:.08em; text-transform:uppercase; color:var(--ink-3); }

main { padding:clamp(12px,2vw,24px) 0 clamp(32px,5vw,56px); border-top:1px solid var(--rule); }
body.studio main { padding:0; border-top:none; overflow:hidden; }
.roster {
  display:grid; gap:clamp(14px,2vw,22px);
  grid-template-columns:repeat(auto-fill,minmax(min(330px, 100%),1fr));
}
.roster .card {
  background:var(--bg-raised); border:1px solid var(--rule); border-radius:var(--radius);
  padding:24px 22px 20px;
  display:flex; flex-direction:column; gap:12px;
  text-align:center;
  transition:border-color .2s var(--ease), transform .2s var(--ease);
  min-height:300px;
  color:inherit; text-decoration:none;
}
.roster .card:hover { border-color:color-mix(in srgb,var(--accent) 50%,var(--rule)); transform:translateY(-1px); }

/* Empty state — shown when no projects onboarded (T4#10 from previous polish pass) */
.roster-empty {
  grid-column:1/-1;
  display:flex; flex-direction:column; align-items:center; text-align:center;
  gap:18px; padding:clamp(48px,8vw,96px) 24px;
  background:var(--bg-raised); border:1px solid var(--rule); border-radius:var(--radius);
}
.roster-empty__icon { color:var(--accent); opacity:0.85; }
.roster-empty h2 {
  font-family:var(--serif); font-weight:500; font-size:clamp(22px,2vw,28px);
  letter-spacing:-.012em; color:var(--ink); margin:4px 0 0;
}
.roster-empty p {
  font-family:var(--serif); font-size:16px; line-height:1.55; color:var(--ink-2);
  max-width:48ch; margin:0;
}
.roster-empty__cta-row { display:flex; flex-wrap:wrap; align-items:center; justify-content:center; gap:18px; margin-top:8px; }
.roster-empty__secondary {
  font-family:var(--sans); font-weight:500; color:var(--ink-3); text-decoration:none;
  border-bottom:1px solid color-mix(in srgb,var(--ink-4) 60%,transparent); padding-bottom:2px;
}
.roster-empty__secondary:hover { color:var(--ink-2); border-color:var(--ink-3); }
.card-head { display:flex; flex-direction:column; align-items:center; gap:10px; }
.card-pie { width:78px; height:78px; color:var(--ink); }
.card-pie svg.pie { width:100%; height:100%; }
.card-id { width:100%; text-align:center; }
.card-id h2 {
  font-family:var(--serif); font-weight:500;
  font-size:clamp(18px,1.45vw,21px); line-height:1.16; letter-spacing:-.004em;
  margin:0 auto 6px; color:var(--ink); text-align:center; max-width:27ch;
  display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical;
  overflow:hidden;
}
.card-phase {
  font-family:var(--sans); font-size:var(--fs-12); font-weight:500; color:var(--ink-3);
  margin:0; text-align:center;
}
.card-attn {
  display:flex; justify-content:center; margin:6px 0 0; padding:0;
}
.card-note {
  font-family:var(--sans); font-size:13.5px; line-height:1.55; color:var(--ink-3);
  margin:10px auto 0; text-align:center; max-width:36ch;
}
.card-meta {
  display:flex; flex-wrap:wrap; gap:5px 7px; align-items:center; justify-content:center;
  margin-top:auto; padding-top:14px; border-top:1px solid var(--rule);
  font-family:var(--mono); font-size:11.5px; color:var(--ink-4);
}
.meta-hb { width:100%; margin-top:4px; opacity:.85; }

.pill {
  display:inline-block; font-family:var(--sans); font-size:10.5px; font-weight:600;
  letter-spacing:.04em; text-transform:uppercase; padding:3px 8px; border-radius:999px;
}
.pill-tier-agent    { color:var(--accent); background:color-mix(in srgb,var(--accent) 14%,transparent); }
.pill-tier-virtuoso { color:var(--ink-2); background:var(--rule-2); }
.pill-auto    { color:var(--accent); background:color-mix(in srgb,var(--accent) 12%,transparent); }
.pill-passive { color:var(--ink-4); background:var(--rule-2); }
.pill-attn    { color:var(--danger); background:color-mix(in srgb,var(--danger) 12%,transparent); }
.pill-state-building     { color:var(--accent-text, var(--accent)); background:color-mix(in srgb,var(--accent) 14%,transparent); }
.pill-state-launch-prep  { color:var(--pill-launch-prep); background:color-mix(in srgb,var(--pill-launch-prep) 14%,transparent); }
.pill-state-warmer       { color:var(--pill-warmer); background:color-mix(in srgb,var(--pill-warmer) 14%,transparent); }
.pill-state-rd           { color:var(--success); background:color-mix(in srgb,var(--success) 14%,transparent); }
.pill-state-updates      { color:var(--accent-text, var(--accent)); background:color-mix(in srgb,var(--accent-2, var(--accent)) 14%,transparent); }
.pill-state-hibernating  { color:var(--ink-4); background:var(--rule-2); }

/* PROJECT PAGE */
.proj-hero-inner { display:flex; align-items:center; gap:40px; flex-wrap:wrap; }
.pie-big { width:160px; height:160px; flex:0 0 160px; color:var(--ink); }
.proj-hero-text { flex:1 1 320px; min-width:0; }
.proj-hero-text h1 { font-size:clamp(30px,3.8vw,46px); line-height:1.06; }
.proj-phase {
  font-family:var(--mono); font-size:var(--fs-12); font-weight:500; letter-spacing:.06em;
  text-transform:uppercase; color:var(--accent); margin:14px 0 10px;
}
.proj-desc { font-size:clamp(16px,1.8vw,18px); color:var(--ink-2); line-height:1.55; max-width:60ch; margin:0; }

.proj-pills { display:flex; flex-wrap:wrap; gap:var(--space-2); margin-top:18px; }

main.proj-main { padding:clamp(36px,5vw,72px) 0; border-top:1px solid var(--rule); }
.proj-section { padding:clamp(36px,5vw,64px) 0; border-top:1px solid var(--rule); }
.proj-section:first-of-type { border-top:none; padding-top:0; }
.proj-section h2 { margin-bottom:24px; }
.proj-lead { font-size:clamp(18px,2vw,21px); color:var(--ink-2); line-height:1.5; max-width:56ch; margin:0 0 28px; }

/* /ensemble.html — the project page is wrapped in the Studio 3-tab
   shell (Canvas/Cadenza/Ensemble) for brand-Ensemble visual parity
   with /map.html and /canvas/<id>.html. The base studio-shell rules
   in styles/studio.py lock height to 100dvh and clip overflow (right
   for /studio.html, where the canvas stage owns the viewport); the
   project page needs to scroll the hero + timeline + sections like
   any other content page, so we relax those two rules and constrain
   the inner .wrap to 960px to match the canvas-view viewport width. */
body.proj-page .studio-shell {
  height: auto;
  overflow: visible;
  min-height: calc(100dvh - 56px);
}
body.proj-page .studio-content {
  height: auto;
  overflow: visible;
  display: block;
}
body.proj-page .studio-content .wrap { max-width: 960px; }

/* Timeline */
.timeline { position:relative; padding-left:38px; }
.timeline::before {
  content:""; position:absolute; left:11px; top:8px; bottom:8px;
  width:2px; background:linear-gradient(180deg,var(--accent) 0%,var(--rule) 60%,var(--rule) 100%);
}
.tl-item { position:relative; padding:0 0 32px; }
.tl-item:last-child { padding-bottom:0; }
.tl-marker {
  position:absolute; left:-38px; top:6px;
  width:24px; height:24px; border-radius:50%;
  background:var(--bg); border:2px solid var(--rule);
  display:flex; align-items:center; justify-content:center;
}
.tl-item.done .tl-marker { background:var(--accent); border-color:var(--accent); }
.tl-item.now .tl-marker {
  background:var(--bg); border-color:var(--accent);
  box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 50%,transparent);
  animation:pulse 3s var(--ease) infinite;
}
.tl-item.next .tl-marker { background:var(--bg); border-color:var(--rule); border-style:dashed; }
.tl-marker::after { content:""; width:6px; height:6px; border-radius:50%; background:var(--accent-ink); }
.tl-item.done .tl-marker::after { background:var(--accent-ink); }
.tl-item.now .tl-marker::after { background:var(--accent); }
.tl-item.next .tl-marker::after { background:var(--ink-4); }
.tl-date {
  font-family:var(--mono); font-size:var(--fs-12); font-weight:500;
  letter-spacing:.08em; text-transform:uppercase; color:var(--ink-3);
  margin-bottom:4px;
}
.tl-item.now .tl-date { color:var(--accent); }
.tl-item.next .tl-date { color:var(--ink-4); }
.tl-title {
  font-family:var(--serif); font-weight:500;
  font-size:clamp(20px,2vw,24px); letter-spacing:-.005em;
  margin:0 0 6px; color:var(--ink);
}
.tl-item.next .tl-title { color:var(--ink-2); font-style:italic; }
.tl-summary { font-size:14.5px; color:var(--ink-3); line-height:1.55; margin:0; max-width:62ch; }

/* Now block */
.now-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(280px, 100%),1fr)); gap:var(--space-5); margin-top:8px; }
.now-card { background:var(--bg-raised); border:1px solid var(--rule); border-radius:var(--radius); padding:22px 24px; }
.now-card h3 {
  font-family:var(--sans); font-size:11.5px; font-weight:600; letter-spacing:.08em; text-transform:uppercase;
  color:var(--accent); margin:0 0 10px;
}
.now-card p { font-size:14.5px; color:var(--ink-2); margin:0; line-height:1.55; }
.now-card ul { margin:0; padding:0 0 0 16px; font-size:13.5px; color:var(--ink-3); line-height:1.6; }
.now-card ul li { margin-bottom:5px; }
.now-card code { color:var(--accent); }

/* Next block */
.next-rec { background:color-mix(in srgb,var(--accent) 6%,var(--bg-raised));
  border:1px solid color-mix(in srgb,var(--accent) 30%,var(--rule));
  border-radius:var(--radius); padding:24px 26px; margin-bottom:18px; }
.next-rec h3 {
  font-family:var(--sans); font-size:11.5px; font-weight:600; letter-spacing:.08em; text-transform:uppercase;
  color:var(--accent); margin:0 0 8px;
}
.next-rec h4 {
  font-family:var(--serif); font-weight:500; font-size:clamp(20px,2vw,24px); letter-spacing:-.005em;
  margin:0 0 8px; color:var(--ink);
}
.next-rec p { font-size:14.5px; color:var(--ink-2); margin:0; line-height:1.55; }

.next-alts { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(280px, 100%),1fr)); gap:14px; }
.next-alt { background:var(--bg-raised); border:1px solid var(--rule); border-radius:var(--radius-sm); padding:16px 18px; }
.next-alt h4 { font-family:var(--sans); font-size:var(--fs-14); font-weight:600; margin:0 0 4px; color:var(--ink); }
.next-alt p { font-size:var(--fs-12); color:var(--ink-3); margin:0; line-height:1.5; }

/* Footer */
footer { border-top:1px solid var(--rule); padding:24px 0 36px; }
.foot-inner { display:flex; flex-wrap:wrap; gap:12px 28px; justify-content:space-between; align-items:center; }
.foot-inner p { margin:0; font-size:12.5px; color:var(--ink-4); font-family:var(--mono); }
.foot-back { display:inline-flex; align-items:center; gap:6px; color:var(--accent); font-family:var(--sans); font-size:var(--fs-12); font-weight:500; }
.foot-back:hover { text-decoration:underline; }

/* ─── Hub page styles (formerly inline in SHARED_HEAD) ────────────────────
   Phase 2 unification: every CSS rule lives here exactly once. Hub-only
   rules that would collide with project-page selectors are scoped under
   `body.hub`. Class-scoped rules with no project equivalent (.tile,
   .widget, .command-card, etc.) stay un-scoped — they only fire on
   pages that emit the matching markup. */

/* Smoke-test banner (lives in SHARED_NAV, hidden until JS shows it) */
#smoke-banner {
  display: none; padding: var(--space-4) 0;
  background: color-mix(in srgb, var(--accent) 8%, var(--bg));
  border-bottom: 2px solid var(--accent);
}
#smoke-banner .wrap {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4); flex-wrap: wrap;
}
#smoke-banner strong {
  display: block; color: var(--accent-text, var(--accent)); font-size: var(--fs-12);
  letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 4px;
}
#smoke-banner-text { margin: 0; font-size: var(--fs-14); color: var(--ink-2); }
#smoke-banner .cmd-row { display: flex; gap: var(--space-2); flex: 0 0 auto; }

/* Hub hero — scoped so it doesn't override .proj-hero-text h1 on project pages.
   Hero canon (2026-06-02): converged onto the shared hero tokens so every hub
   page (Tools, Atlas, Settings, Suite Library, Commands, Pricing, …) renders
   the same headline scale/weight/tracking/line-height as the home reference.
   Was clamp(40px,6vw,68px) / 600 / 1.05 / -0.02em — its own drifting scale. */
body.hub .hero { padding: clamp(20px, 3.4vw, 40px) 0 clamp(14px, 2.4vw, 24px); }
body.hub .hero .eyebrow {
  font-family: var(--sans); font-size: var(--fs-12); font-weight: 600;
  letter-spacing: var(--tracking-eyebrow); text-transform: uppercase; color: var(--accent-text, var(--accent));
  margin: 0 0 18px;
  display: inline-flex; align-items: center; gap: 10px;
}
/* Restore the tracked-dash prefix so the hub eyebrow matches the home +
   docs + layout eyebrows (the dash was previously suppressed here). */
body.hub .hero .eyebrow::before {
  content: ""; width: 22px; height: 1px; background: var(--accent); display: inline-block;
}
body.hub .hero h1 {
  font-family: var(--serif); font-size: var(--fs-hero);
  line-height: var(--lh-hero); letter-spacing: var(--tracking-hero); font-weight: var(--weight-hero);
  color: var(--ink); margin: 0 0 16px;
}
body.hub .hero h1 em { font-style: italic; font-weight: 500; color: var(--accent-text, var(--accent)); }
body.hub .hero p.lead {
  font-family: var(--serif); font-size: var(--fs-hero-lead);
  line-height: var(--lh-hero-lead); color: var(--ink-2); margin: 0; max-width: 60ch;
}
/* Centered hero variant (settings / dashboard / tools): center the eyebrow,
   headline, and lead, and re-center the 60ch lead block (the base
   body.hub .hero p.lead pins margin:0, so this higher-specificity rule wins). */
body.hub .hero.hero--centered .eyebrow,
body.hub .hero.hero--centered h1,
body.hub .hero.hero--centered p.lead { text-align: center; }
body.hub .hero.hero--centered p.lead { margin-inline: auto; }
body.hub .hero.hero--centered .meta { justify-content: center; }
/* When the dash-subnav follows a centered hero (the Dashboard), center the tab
   strip too so it lines up under the centered hero rather than the gutter. */
.hero--centered + nav.dash-subnav .wrap { justify-content: center; }
body.hub .hero .meta {
  display: flex; gap: var(--space-4); flex-wrap: wrap; margin-top: var(--space-6);
  font-size: var(--fs-12); color: var(--ink-3); font-family: var(--mono);
}
body.hub .hero .meta span { display: inline-flex; align-items: center; gap: var(--space-2); }
body.hub .hero .meta .dot {
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent); animation: none; box-shadow: none;
}

/* Hub sections — scoped so they don't override .proj-section padding/h2 */
body.hub section { padding: clamp(32px, 5vw, 56px) 0; }
body.hub main > .hero + section,
body.hub main > .hero + .dash-subnav + section {
  padding-top: clamp(22px, 3vw, 36px);
}
body.hub section h2 {
  font-family: var(--serif); font-size: clamp(26px, 3.4vw, 36px);
  line-height: 1.15; letter-spacing: -0.01em; font-weight: 500;
  color: var(--ink); margin: 0 0 8px;
}
body.hub section .section-sub { color: var(--ink-3); font-size: var(--fs-14); margin: 0 0 28px; }

/* Tile cards (home) */
.tiles { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-4); }
@media (max-width: 640px) { .tiles { grid-template-columns: 1fr; } }
.tile {
  display: block; background: var(--bg-raised);
  border: 1px solid var(--rule); border-radius: var(--radius);
  padding: var(--space-6) var(--space-5) var(--space-5); text-decoration: none; color: var(--ink-2);
  transition: border-color 200ms var(--ease), transform 200ms var(--ease), box-shadow 200ms var(--ease);
  position: relative; overflow: hidden;
}
.tile:hover {
  border-color: color-mix(in srgb, var(--accent) 35%, var(--rule));
  transform: translateY(-2px); box-shadow: var(--shadow-md);
}
.tile .glyph {
  width: 44px; height: 44px; border-radius: 12px;
  background: var(--accent-soft); color: var(--accent-text, var(--accent));
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-22); margin-bottom: var(--space-4);
}
.tile h3 {
  font-family: var(--serif); font-size: 26px; font-weight: 500;
  letter-spacing: -0.01em; color: var(--ink); margin: 0 0 6px;
}
.tile p.tile-sub { font-size: var(--fs-14); color: var(--ink-3); margin: 0 0 12px; }
.tile p.tile-stat {
  font-family: var(--mono); font-size: var(--fs-12); color: var(--accent-text, var(--accent));
  margin: 0; font-weight: 500;
}
.tile .arrow {
  position: absolute; right: 22px; top: 28px; color: var(--ink-4);
  font-size: 20px; transition: transform 200ms var(--ease), color 200ms var(--ease);
}
.tile:hover .arrow { color: var(--accent-text, var(--accent)); transform: translateX(4px); }

/* Cards (docs hub) — un-scoped because project cards now live under .roster .card */
.cards { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-4); }
@media (min-width: 768px) { .cards { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1024px) { .cards.two { grid-template-columns: repeat(2, 1fr); gap: 20px; } }
.card {
  display: block; background: var(--bg-raised); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: 22px 22px 20px; text-decoration: none;
  color: var(--ink-2); transition: border-color 200ms var(--ease), transform 200ms var(--ease), box-shadow 200ms var(--ease);
}
.card:hover {
  border-color: color-mix(in srgb, var(--accent) 35%, var(--rule));
  transform: translateY(-1px); box-shadow: var(--shadow-md);
}
.card .ico {
  width: 32px; height: 32px; border-radius: 8px;
  background: var(--accent-soft); display: inline-flex; align-items: center;
  justify-content: center; color: var(--accent-text, var(--accent)); margin-bottom: 14px; font-size: var(--fs-18);
}
.card h3 {
  font-family: var(--sans); font-size: var(--fs-16); font-weight: 600;
  letter-spacing: -0.005em; color: var(--ink); margin: 0 0 4px;
}
.card p { font-size: var(--fs-14); color: var(--ink-3); margin: 0; line-height: 1.5; }
.card .tag {
  display: inline-block; font-family: var(--mono); font-size: var(--fs-12);
  font-weight: 500; color: var(--accent-text, var(--accent)); margin-top: 10px;
}
.cards.repos .card { padding: 18px 20px; }
.cards.repos .card h3 { font-family: var(--mono); font-size: var(--fs-12); font-weight: 500; color: var(--ink); }

/* Settings */
.settings-grid { display: grid; grid-template-columns: 1fr; gap: 12px; min-width: 0; }
@media (min-width: 768px) { .settings-grid { grid-template-columns: repeat(3, 1fr); } }
.setting {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: 14px 16px; background: var(--bg-raised); border: 1px solid var(--rule);
  border-radius: var(--radius-sm); cursor: pointer;
  transition: border-color 200ms var(--ease);
  min-width: 0;
}
.setting:hover { border-color: color-mix(in srgb, var(--accent) 30%, var(--rule)); }
.setting-name {
  font-size: var(--fs-12); font-weight: 500; color: var(--ink-2);
  min-width: 0; overflow-wrap: anywhere;
}
.setting input[type="number"], .setting select {
  font-family: var(--mono); font-size: var(--fs-12); padding: 6px 10px;
  border: 1px solid var(--rule); border-radius: 6px;
  background: var(--bg); color: var(--ink); width: min(110px, 42vw);
  box-sizing: border-box;
}
.setting input[type="checkbox"] {
  width: 38px; height: 22px; appearance: none; background: var(--rule);
  border-radius: 999px; cursor: pointer; position: relative;
  transition: background 200ms var(--ease);
}
.setting input[type="checkbox"]:checked { background: var(--accent); }
.setting input[type="checkbox"]::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 18px; height: 18px; border-radius: 50%; background: white;
  transition: transform 200ms var(--ease);
}
.setting input[type="checkbox"]:checked::after { transform: translateX(16px); }
.settings-help {
  font-size: var(--fs-12); color: var(--ink-3); margin-top: 14px; font-family: var(--mono);
}

/* Settings sub-headings (Slot polA · 2026-04-30) — replaces inline-styled
   <h3 style="font-family:..."> blocks in pages/settings_body.py with a
   semantic class so the modular type scale + ink token apply uniformly. */
.settings-h3 {
  font-family: var(--sans); font-size: var(--fs-14); font-weight: 600;
  color: var(--ink); margin: var(--space-6) 0 var(--space-2);
}
.settings-h3--tight { margin: 0 0 var(--space-3); }
.settings-section-sub--tight { margin-bottom: var(--space-3); }
.settings-help--tight { margin-top: var(--space-3); }
.settings-pair-cta { margin: 0 0 var(--space-5); }
.settings-pair-cta__link { display: inline-block; text-decoration: none; padding: 10px 22px; }
.settings-privacy-toggle { margin-bottom: var(--space-4); max-width: 60ch; }
.settings-privacy-help { margin: -10px 0 var(--space-5); }
.settings-privacy-empty { color: var(--ink-3); font-size: 13px; }
.settings-inline-link { color: var(--accent-text, var(--accent)); font-weight: 500; }

/* Privacy list — per-project visibility row (rendered by settings.html JS) */
.settings-privacy-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); padding: 10px 14px;
  background: var(--bg-raised); border: 1px solid var(--rule);
  border-radius: var(--radius-sm); margin-bottom: var(--space-2);
  min-width: 0;
}
.settings-privacy-row__label { flex: 1; min-width: 0; }
.settings-privacy-row__slug {
  font-size: 13px; font-weight: 500; color: var(--ink);
}
.settings-privacy-row__meta {
  margin-left: 10px; font-family: var(--mono); font-size: 11px; color: var(--ink-4);
}
.settings-privacy-row__select {
  font-family: var(--mono); font-size: var(--fs-12); padding: 5px 8px;
  border: 1px solid var(--rule); border-radius: 6px;
  background: var(--bg); color: var(--ink);
  max-width: 100%;
}
.settings-privacy-row .privacy-status {
  font-family: var(--mono); font-size: 11px; color: var(--ink-4);
  min-width: 60px; text-align: right;
}
@media (max-width: 430px) {
  .setting { align-items: center; padding: 13px 14px; }
  .settings-privacy-row {
    align-items: stretch;
    flex-direction: column;
    gap: var(--space-2);
  }
  .settings-privacy-row__meta {
    display: block;
    margin-left: 0;
    margin-top: 2px;
  }
  .settings-privacy-row .privacy-status {
    min-width: 0;
    text-align: left;
  }
}

/* Commands */
.commands-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-4); }
@media (min-width: 768px) { .commands-grid { grid-template-columns: repeat(2, 1fr); } }
.command-card {
  background: var(--bg-raised); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: 24px 24px 20px;
}
.command-card.danger { border-color: color-mix(in srgb, #c73a2a 30%, var(--rule)); }
.command-card h3 {
  font-family: var(--sans); font-size: var(--fs-14); font-weight: 600;
  letter-spacing: -0.005em; color: var(--ink); margin: 0 0 16px;
}
.cmd-row { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
.cmd-row select, .cmd-row input {
  font-family: var(--mono); font-size: var(--fs-12); padding: 8px 10px;
  border: 1px solid var(--rule); border-radius: 6px;
  background: var(--bg); color: var(--ink); flex: 1; min-width: 0;
}
.cmd-row input[type="number"], .cmd-row input[type="datetime-local"] { max-width: 160px; }
.btn-primary {
  background: var(--action, var(--accent)); color: var(--action-ink, var(--accent-ink, white)); border: 0; padding: 8px 16px;
  border-radius: 6px; font-family: var(--sans); font-size: var(--fs-12); font-weight: 600;
  cursor: pointer; transition: background 200ms var(--ease);
}
.btn-primary:hover { background: var(--action-2, var(--accent-2)); }
.btn-secondary {
  background: transparent; color: var(--ink-2); border: 1px solid var(--rule);
  padding: 8px 12px; border-radius: 6px; font-family: var(--sans);
  font-size: var(--fs-12); font-weight: 500; cursor: pointer;
}
.btn-secondary:hover { border-color: var(--accent); color: var(--accent-text, var(--accent)); }
.btn-danger {
  background: #c73a2a; color: white; border: 0; padding: 8px 16px;
  border-radius: 6px; font-family: var(--sans); font-size: var(--fs-12);
  font-weight: 600; cursor: pointer; transition: filter 200ms var(--ease);
}
.btn-danger:hover { filter: brightness(1.1); }
.cmd-help { font-size: var(--fs-12); color: var(--ink-3); margin-top: 12px; line-height: 1.5; }
.cmd-help a { color: var(--accent-text, var(--accent)); text-decoration: none; }
.cmd-help a:hover { text-decoration: underline; }

/* Hub site footer (only emitted by SHARED_FOOTER).
   2026-04-30: top-level layout-only rules; column layout, link colors,
   and visual hierarchy live in shared_footer.py's scoped <style>
   block so the parent here doesn't fight specificity with the cols. */
footer.site-footer {
  margin-top: var(--space-7, 48px);
  border-top: 1px solid var(--rule);
  color: var(--ink-3);
  font-size: var(--fs-12);
}
footer.site-footer .brand { font-family: var(--serif); font-weight: 500; }

/* Owner-only banner (read-only mode) — safe on every page; hidden until JS shows */
.owner-banner {
  padding: 12px 16px; background: color-mix(in srgb, var(--ink-3) 8%, var(--bg));
  border: 1px dashed var(--rule); border-radius: var(--radius-sm);
  font-size: var(--fs-12); color: var(--ink-3); margin-bottom: var(--space-5);
  display: flex; align-items: center; gap: 10px;
}
.owner-banner.show { display: flex; }
.owner-banner.hide { display: none; }
.owner-banner strong { color: var(--ink); }
body:not(.signed-in) [data-owner-only],
body:not(.signed-in) [data-operator-only] { display: none !important; }
.read-only [data-owner-only] { opacity: 0.5; pointer-events: none; }
.read-only [data-owner-only] input,
.read-only [data-owner-only] select,
.read-only [data-owner-only] button { cursor: not-allowed; }

/* Theme picker UI (settings) */
.theme-picker { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.theme-swatch {
  width: 44px; height: 44px; border-radius: 12px; cursor: pointer;
  border: 2px solid var(--rule);
  transition: border-color 200ms var(--ease), transform 200ms var(--ease), box-shadow 200ms var(--ease);
  background: linear-gradient(135deg, var(--swatch-a), var(--swatch-b));
}
.theme-swatch:hover { transform: scale(1.05); }
.theme-swatch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.theme-swatch.active { border-color: var(--ink); transform: scale(1.05); box-shadow: var(--shadow-md); }

/* Default-marker dot — small white-with-shadow pip in the bottom-right
   corner of the swatch flagged as the system default. Visible regardless
   of swatch color (white + drop-shadow gives it both bright + dark
   themes a contrast halo). Same pattern works for bg-swatches below. */
.theme-swatch[data-default="true"],
.bg-swatch[data-default="true"] {
  position: relative;
}
.theme-swatch[data-default="true"]::after,
.bg-swatch[data-default="true"]::after {
  content: "";
  position: absolute;
  bottom: 4px;
  right: 4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #ffffff;
  box-shadow: 0 0 0 1.5px rgba(0, 0, 0, 0.55), 0 1px 2px rgba(0, 0, 0, 0.4);
  pointer-events: none;
}
/* Warm-to-cool spectrum (18 colors). Order matches the picker UI. */
.theme-swatch.crimson  { --swatch-a: #dc2626; --swatch-b: #ef4444; }
.theme-swatch.rose     { --swatch-a: #e11d48; --swatch-b: #f43f5e; }
.theme-swatch.sunset   { --swatch-a: #ea580c; --swatch-b: #f97316; }
.theme-swatch.amber    { --swatch-a: #d97706; --swatch-b: #f59e0b; }
.theme-swatch.warm     { --swatch-a: #c2711f; --swatch-b: #e08a2c; }
.theme-swatch.sand     { --swatch-a: #a16207; --swatch-b: #ca8a04; }
.theme-swatch.sage     { --swatch-a: #65a30d; --swatch-b: #84cc16; }
.theme-swatch.lime     { --swatch-a: #84cc16; --swatch-b: #a3e635; }
.theme-swatch.forest   { --swatch-a: #16a34a; --swatch-b: #22c55e; }
.theme-swatch.teal     { --swatch-a: #0d9488; --swatch-b: #14b8a6; }
.theme-swatch.ocean    { --swatch-a: #0891b2; --swatch-b: #06b6d4; }
/* Slate is a dark site skin, but the picker chip should read like the
   other standard color chips: one color, not a mini background preview. */
.theme-swatch.slate    { --swatch-a: #2f7bf7; --swatch-b: #2f7bf7; }
.theme-swatch.cobalt   { --swatch-a: #1e40af; --swatch-b: #2563eb; }
.theme-swatch.midnight { --swatch-a: #4f46e5; --swatch-b: #6366f1; }
.theme-swatch.aurora   { --swatch-a: #8b5cf6; --swatch-b: #a78bfa; }
.theme-swatch.plum     { --swatch-a: #a21caf; --swatch-b: #c026d3; }
.theme-swatch.cerulean { --swatch-a: #1e7fc4; --swatch-b: #2c9be2; }
.theme-swatch.mono     { --swatch-a: #0a0a0a; --swatch-b: #555555; }
.theme-swatch.ruby     { --swatch-a: #c81825; --swatch-b: #ee2e3a; }
.theme-swatch.lemon    { --swatch-a: #ca8a04; --swatch-b: #facc15; }
.theme-swatch.paper    { --swatch-a: #f5f5f3; --swatch-b: #e3e3e0; border: 1px solid #d4d4d2; }

/* ── Limited Edition (2026-05-04 → 2027-05-04) ──────────────────────
   Synthwave / vaporwave / cyberpunk swatches. Three-color gradient
   for visual punch (each swatch reads as a tiny sunset/skyline). */
.theme-swatch.sunset_drive {
  background: linear-gradient(135deg, #ff006e 0%, #8338ec 50%, #3a86ff 100%);
}
.theme-swatch.mall_plaza {
  /* Chrome Dive — vibrant magenta primary with electric-blue support.
     Diagonal: cyan-blue → purple-bridge → vibrant magenta so the swatch
     reads as 'classic cyberpunk neon' at a glance. Per Kevin's brief
     (not enough pink, vibrant magenta). */
  background: linear-gradient(135deg, #00d4ff 0%, #6040ff 45%, #ff00ff 100%);
}
.theme-swatch.neo_city {
  background: linear-gradient(135deg, #39ff14 0%, #ff10f0 50%, #00fff5 100%);
}

/* Limited-edition badge — small ★ in top-right corner of the swatch.
   Hidden visually by the picker default so non-LE swatches stay clean.
   Free-tier mechanic (LIMITED_EDITION_THEMES in canvas_atmospheres):
   visible badge + tooltip explains "free until May 2027, then
   collectible." After cutoff, picker filter hides them from new
   pickers; CSS stays bundled so users who saved them keep working. */
.theme-swatch--limited {
  position: relative;
}
.theme-swatch--limited::before {
  content: "★";
  position: absolute;
  top: -4px;
  right: -4px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #ffeb3b;
  color: #000;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45), 0 0 0 1.5px rgba(0, 0, 0, 0.25);
  pointer-events: none;
  z-index: 2;
}


/* ── /docs.html — "Everything Endenza" About page (2026-05-04) ──────
   Replaces the 6-section / 20-link docs index with a focused About
   surface: short intro, personal bio, FAQ accordion, single deep-doc
   link footer. Editorial-leaning typography; narrower wrap for reading. */

.about-section {
  padding: clamp(28px, 4vw, 48px) 0;
}
.about-section .wrap,
.about-hero .wrap {
  max-width: 720px;
  margin-left: auto;
  margin-right: auto;
}
.about-section h2 {
  font-family: var(--serif);
  font-size: clamp(22px, 2.6vw, 30px);
  font-weight: 500;
  letter-spacing: -0.01em;
  margin: 0 0 16px;
  color: var(--ink);
}
.about-section p {
  font-family: var(--serif);
  font-size: clamp(15px, 1.3vw, 17px);
  line-height: 1.7;
  color: var(--ink-2);
  margin: 0 0 14px;
}
.about-section p strong { color: var(--ink); font-weight: 600; }
.about-section p em { color: var(--accent-text, var(--accent)); font-style: italic; }
.about-section--quiet {
  padding: clamp(20px, 3vw, 32px) 0 clamp(36px, 5vw, 56px);
  border-top: 1px solid var(--rule);
  margin-top: clamp(24px, 4vw, 40px);
}
.about-section--quiet h2 { font-size: clamp(18px, 2vw, 22px); }
.about-section--quiet .section-sub {
  font-family: var(--sans);
  font-size: 14px;
  color: var(--ink-3);
  margin: 0 0 14px;
}

.about-bio { display: flex; flex-direction: column; gap: 12px; }
.about-bio p { margin: 0; }
.about-bio__contact {
  font-family: var(--sans);
  font-size: 14px;
  color: var(--ink-3);
  padding-top: 8px;
  border-top: 1px dashed var(--rule);
}
.about-bio__contact a { color: var(--accent-text, var(--accent)); text-decoration: none; font-weight: 500; }
.about-bio__contact a:hover { text-decoration: underline; }

.about-faq { display: flex; flex-direction: column; }
.about-faq__item {
  border-top: 1px solid var(--rule);
  padding: 14px 0;
}
.about-faq__item:last-of-type { border-bottom: 1px solid var(--rule); }
.about-faq__item summary {
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  letter-spacing: -0.005em;
}
.about-faq__item summary::-webkit-details-marker { display: none; }
.about-faq__item summary::after {
  content: "+";
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 18px;
  font-weight: 400;
  color: var(--ink-3);
  width: 20px;
  text-align: center;
  transition: color 180ms var(--ease, ease);
}
.about-faq__item[open] summary::after { content: "−"; color: var(--accent-text, var(--accent)); }
.about-faq__item summary:hover { color: var(--accent-text, var(--accent)); }
.about-faq__item p {
  margin: 10px 0 0;
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.7;
  color: var(--ink-2);
}
.about-faq__item p a { color: var(--accent-text, var(--accent)); text-decoration: none; }
.about-faq__item p a:hover { text-decoration: underline; }
.about-faq__item code {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 13px;
  padding: 1px 6px;
  background: var(--bg-sunken, rgba(0, 0, 0, 0.05));
  border-radius: 4px;
}

.about-deep-link-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 18px;
  margin-top: 8px;
}
.about-deep-link {
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 600;
  color: var(--accent-text, var(--accent));
  text-decoration: none;
  letter-spacing: -0.005em;
}
.about-deep-link:hover { color: var(--accent-2, var(--accent)); text-decoration: underline; }
.about-deep-link--quiet {
  color: var(--ink-3);
  font-weight: 500;
  font-size: 14px;
}
.about-deep-link--quiet:hover { color: var(--ink); }

/* ─── Funky backgrounds — applied via body.bg-<name> class ──────────── */

.bg-picker { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.bg-swatch {
  width: 64px; height: 44px; border-radius: 10px; cursor: pointer;
  border: 2px solid var(--rule);
  position: relative; overflow: hidden;
  background: var(--bg-raised);
  transition: border-color 200ms var(--ease), transform 200ms var(--ease), box-shadow 200ms var(--ease);
}
.bg-swatch:hover { transform: scale(1.04); }
.bg-swatch.active { border-color: var(--ink); transform: scale(1.04); box-shadow: var(--shadow-md); }
.bg-swatch__label {
  position: absolute; bottom: 2px; left: 0; right: 0;
  font-family: var(--mono); font-size: 9px; font-weight: 500;
  text-align: center; color: var(--ink-3);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.bg-swatch.solid { background: var(--bg); }
.bg-swatch.dots {
  background-color: var(--bg);
  background-image: radial-gradient(circle, var(--ink-3) 1px, transparent 1.4px);
  background-size: 8px 8px;
}
.bg-swatch.grid {
  background-color: var(--bg);
  background-image:
    linear-gradient(var(--rule) 1px, transparent 1px),
    linear-gradient(90deg, var(--rule) 1px, transparent 1px);
  background-size: 10px 10px;
}
.bg-swatch.mesh {
  background:
    radial-gradient(at 25% 30%, var(--accent-soft) 0%, transparent 60%),
    radial-gradient(at 75% 70%, color-mix(in srgb, var(--accent-2) 40%, transparent) 0%, transparent 60%),
    var(--bg);
}
.bg-swatch.noise {
  background-color: var(--bg);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='1.2' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)' opacity='0.35'/></svg>");
}
.bg-swatch.aurora {
  background:
    linear-gradient(135deg, var(--accent-soft) 0%, transparent 40%, color-mix(in srgb, var(--accent-2) 30%, transparent) 70%, transparent 100%),
    var(--bg);
}

/* Body backgrounds — fixed-attachment so they stay put while scrolling */
body.bg-dots {
  background-image: radial-gradient(circle, color-mix(in srgb, var(--ink-3) 38%, transparent) 1px, transparent 1.5px);
  background-size: 22px 22px;
  background-attachment: fixed;
}
body.bg-grid {
  background-image:
    linear-gradient(color-mix(in srgb, var(--rule) 70%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--rule) 70%, transparent) 1px, transparent 1px);
  background-size: 32px 32px;
  background-attachment: fixed;
}
body.bg-mesh {
  background-image:
    radial-gradient(at 20% 25%, var(--accent-soft) 0%, transparent 50%),
    radial-gradient(at 80% 75%, color-mix(in srgb, var(--accent-2) 12%, transparent) 0%, transparent 55%),
    radial-gradient(at 60% 15%, color-mix(in srgb, var(--accent-3) 8%, transparent) 0%, transparent 50%),
    radial-gradient(at 30% 80%, color-mix(in srgb, var(--accent) 10%, transparent) 0%, transparent 55%);
  background-attachment: fixed;
}
body.bg-noise {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)' opacity='0.18'/></svg>");
  background-attachment: fixed;
}
body.bg-aurora {
  background-image: linear-gradient(135deg,
    var(--accent-soft) 0%,
    transparent 30%,
    color-mix(in srgb, var(--accent-2) 8%, transparent) 50%,
    transparent 70%,
    var(--accent-soft) 100%);
  background-size: 200% 200%;
  background-attachment: fixed;
  animation: aurora-drift 38s ease-in-out infinite alternate;
}
@keyframes aurora-drift {
  from { background-position: 0% 0%; }
  to   { background-position: 100% 100%; }
}

/* ─── New backgrounds (Phase A.4 polish — wallpapers expansion 2026-04-27) ─── */

body.bg-scanlines {
  background-image: repeating-linear-gradient(
    180deg,
    transparent 0px,
    transparent 3px,
    color-mix(in srgb, var(--ink-3) 14%, transparent) 3px,
    color-mix(in srgb, var(--ink-3) 14%, transparent) 4px
  );
  background-attachment: fixed;
}

body.bg-topo {
  /* Refined topographic contour map — hand-tuned bezier paths at four
     elevation rings around two peaks plus valley flow lines, painted via
     mask-image so the line color tracks --accent across all themes. */
  background-color: var(--bg);
}
body.bg-topo::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background-color: var(--accent);
  opacity: 0.10;
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 600'><g fill='none' stroke='black' stroke-width='1.2' stroke-linecap='round'><path d='M 170 175 Q 200 175 215 195 Q 215 215 170 218 Q 130 215 130 195 Q 135 175 170 175 Z'/><path d='M 165 150 Q 220 152 240 195 Q 240 235 170 240 Q 110 235 105 195 Q 110 152 165 150 Z'/><path d='M 155 122 Q 245 125 275 195 Q 270 270 170 275 Q 80 270 75 195 Q 82 128 155 122 Z'/><path d='M 140 90 Q 275 95 310 195 Q 300 305 170 312 Q 45 305 38 195 Q 50 100 140 90 Z'/><path d='M 435 385 Q 462 385 478 405 Q 478 425 435 428 Q 395 425 395 405 Q 400 385 435 385 Z'/><path d='M 430 360 Q 482 362 502 405 Q 500 445 435 450 Q 375 445 372 405 Q 380 362 430 360 Z'/><path d='M 422 332 Q 510 335 535 405 Q 528 480 435 485 Q 345 480 340 405 Q 348 338 422 332 Z'/><path d='M 410 302 Q 540 308 568 405 Q 558 510 435 518 Q 305 510 298 405 Q 312 312 410 302 Z'/><path d='M 0 530 Q 150 515 300 535 Q 450 555 600 525'/><path d='M 0 555 Q 150 545 300 562 Q 450 580 600 552'/><path d='M 0 38 Q 150 50 300 30 Q 450 10 600 40'/></g></svg>");
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 600'><g fill='none' stroke='black' stroke-width='1.2' stroke-linecap='round'><path d='M 170 175 Q 200 175 215 195 Q 215 215 170 218 Q 130 215 130 195 Q 135 175 170 175 Z'/><path d='M 165 150 Q 220 152 240 195 Q 240 235 170 240 Q 110 235 105 195 Q 110 152 165 150 Z'/><path d='M 155 122 Q 245 125 275 195 Q 270 270 170 275 Q 80 270 75 195 Q 82 128 155 122 Z'/><path d='M 140 90 Q 275 95 310 195 Q 300 305 170 312 Q 45 305 38 195 Q 50 100 140 90 Z'/><path d='M 435 385 Q 462 385 478 405 Q 478 425 435 428 Q 395 425 395 405 Q 400 385 435 385 Z'/><path d='M 430 360 Q 482 362 502 405 Q 500 445 435 450 Q 375 445 372 405 Q 380 362 430 360 Z'/><path d='M 422 332 Q 510 335 535 405 Q 528 480 435 485 Q 345 480 340 405 Q 348 338 422 332 Z'/><path d='M 410 302 Q 540 308 568 405 Q 558 510 435 518 Q 305 510 298 405 Q 312 312 410 302 Z'/><path d='M 0 530 Q 150 515 300 535 Q 450 555 600 525'/><path d='M 0 555 Q 150 545 300 562 Q 450 580 600 552'/><path d='M 0 38 Q 150 50 300 30 Q 450 10 600 40'/></g></svg>");
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
  -webkit-mask-size: 600px 600px;
  mask-size: 600px 600px;
}

body.bg-vignette {
  background-image: radial-gradient(ellipse at center,
    transparent 0%,
    transparent 40%,
    color-mix(in srgb, var(--ink) 12%, transparent) 100%);
  background-attachment: fixed;
}

body.bg-paper {
  /* Heavier turbulence + warm tint = vintage paper texture. */
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='320' height='320'><filter id='p'><feTurbulence type='fractalNoise' baseFrequency='0.55' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.7  0 0 0 0 0.55  0 0 0 0 0.4  0 0 0 0.30 0'/></filter><rect width='100%' height='100%' filter='url(%23p)' opacity='0.32'/></svg>"),
    radial-gradient(ellipse at center, transparent 60%, color-mix(in srgb, var(--ink) 8%, transparent) 100%);
  background-attachment: fixed;
}

/* ─── Wallpaper-α (2026-05-02) — linen, blueprint, constellation, marble ─── */

body.bg-linen {
  /* Subtle woven cross-hatch texture, warm undertone — book fabric. */
  background-color: color-mix(in srgb, var(--accent) 3%, var(--bg));
  background-image:
    repeating-linear-gradient(45deg,
      color-mix(in srgb, var(--ink-3) 12%, transparent) 0 1px,
      transparent 1px 5px),
    repeating-linear-gradient(-45deg,
      color-mix(in srgb, var(--ink-3) 12%, transparent) 0 1px,
      transparent 1px 5px);
  background-attachment: fixed;
}

body.bg-blueprint {
  /* Faint technical-drawing grid: 16px minor + 96px major lines, accent-tinted. */
  background-color: color-mix(in srgb, var(--accent) 5%, var(--bg));
  background-image:
    linear-gradient(color-mix(in srgb, var(--accent) 22%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--accent) 22%, transparent) 1px, transparent 1px),
    linear-gradient(color-mix(in srgb, var(--accent) 9%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--accent) 9%, transparent) 1px, transparent 1px);
  background-size: 96px 96px, 96px 96px, 16px 16px, 16px 16px;
  background-attachment: fixed;
}

body.bg-constellation {
  /* Sparse star map of dots + a few faint asterism lines. Painted via
     mask so the dot color tracks --accent. Hand-placed coordinates so
     the layout reads as composed, not random. */
  background-color: var(--bg);
}
body.bg-constellation::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background-color: var(--accent);
  opacity: 0.32;
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 400'><g fill='black' stroke='black'><circle cx='37' cy='62' r='1.0' stroke='none'/><circle cx='124' cy='28' r='0.7' stroke='none'/><circle cx='198' cy='95' r='1.4' stroke='none'/><circle cx='284' cy='52' r='0.9' stroke='none'/><circle cx='342' cy='118' r='1.1' stroke='none'/><circle cx='65' cy='148' r='1.2' stroke='none'/><circle cx='176' cy='185' r='0.8' stroke='none'/><circle cx='258' cy='162' r='1.0' stroke='none'/><circle cx='358' cy='205' r='0.7' stroke='none'/><circle cx='29' cy='235' r='1.3' stroke='none'/><circle cx='112' cy='268' r='0.9' stroke='none'/><circle cx='195' cy='245' r='1.1' stroke='none'/><circle cx='278' cy='288' r='0.8' stroke='none'/><circle cx='340' cy='258' r='1.4' stroke='none'/><circle cx='52' cy='312' r='0.7' stroke='none'/><circle cx='148' cy='342' r='1.0' stroke='none'/><circle cx='225' cy='325' r='1.2' stroke='none'/><circle cx='305' cy='362' r='0.8' stroke='none'/><circle cx='378' cy='338' r='0.9' stroke='none'/><circle cx='88' cy='378' r='1.1' stroke='none'/><line x1='124' y1='28' x2='198' y2='95' stroke-width='0.4' opacity='0.35'/><line x1='198' y1='95' x2='258' y2='162' stroke-width='0.4' opacity='0.35'/><line x1='176' y1='185' x2='258' y2='162' stroke-width='0.4' opacity='0.35'/><line x1='112' y1='268' x2='195' y2='245' stroke-width='0.4' opacity='0.35'/><line x1='195' y1='245' x2='278' y2='288' stroke-width='0.4' opacity='0.35'/></g></svg>");
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 400'><g fill='black' stroke='black'><circle cx='37' cy='62' r='1.0' stroke='none'/><circle cx='124' cy='28' r='0.7' stroke='none'/><circle cx='198' cy='95' r='1.4' stroke='none'/><circle cx='284' cy='52' r='0.9' stroke='none'/><circle cx='342' cy='118' r='1.1' stroke='none'/><circle cx='65' cy='148' r='1.2' stroke='none'/><circle cx='176' cy='185' r='0.8' stroke='none'/><circle cx='258' cy='162' r='1.0' stroke='none'/><circle cx='358' cy='205' r='0.7' stroke='none'/><circle cx='29' cy='235' r='1.3' stroke='none'/><circle cx='112' cy='268' r='0.9' stroke='none'/><circle cx='195' cy='245' r='1.1' stroke='none'/><circle cx='278' cy='288' r='0.8' stroke='none'/><circle cx='340' cy='258' r='1.4' stroke='none'/><circle cx='52' cy='312' r='0.7' stroke='none'/><circle cx='148' cy='342' r='1.0' stroke='none'/><circle cx='225' cy='325' r='1.2' stroke='none'/><circle cx='305' cy='362' r='0.8' stroke='none'/><circle cx='378' cy='338' r='0.9' stroke='none'/><circle cx='88' cy='378' r='1.1' stroke='none'/><line x1='124' y1='28' x2='198' y2='95' stroke-width='0.4' opacity='0.35'/><line x1='198' y1='95' x2='258' y2='162' stroke-width='0.4' opacity='0.35'/><line x1='176' y1='185' x2='258' y2='162' stroke-width='0.4' opacity='0.35'/><line x1='112' y1='268' x2='195' y2='245' stroke-width='0.4' opacity='0.35'/><line x1='195' y1='245' x2='278' y2='288' stroke-width='0.4' opacity='0.35'/></g></svg>");
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
  -webkit-mask-size: 400px 400px;
  mask-size: 400px 400px;
}

body.bg-marble {
  /* Subtle veined texture: anisotropic turbulence + soft accent washes
     for marble-like streaks.  Cooler / less granular than `paper`. */
  background-color: var(--bg);
  background-image:
    radial-gradient(at 18% 22%, color-mix(in srgb, var(--accent) 8%, transparent) 0%, transparent 35%),
    radial-gradient(at 78% 35%, color-mix(in srgb, var(--accent-2) 6%, transparent) 0%, transparent 45%),
    radial-gradient(at 35% 75%, color-mix(in srgb, var(--accent-3) 6%, transparent) 0%, transparent 40%),
    radial-gradient(at 88% 88%, color-mix(in srgb, var(--accent) 5%, transparent) 0%, transparent 35%),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='480' height='480'><filter id='m'><feTurbulence type='fractalNoise' baseFrequency='0.012 0.04' numOctaves='2' stitchTiles='stitch' seed='3'/><feColorMatrix values='0 0 0 0 0.55  0 0 0 0 0.55  0 0 0 0 0.6  0 0 0 0.18 0'/></filter><rect width='100%' height='100%' filter='url(%23m)' opacity='0.55'/></svg>");
  background-size: auto, auto, auto, auto, 480px 480px;
  background-attachment: fixed;
}

/* Custom-uploaded wallpaper: applied via inline style.backgroundImage from
   shared_scripts.js when bg=custom (data URL persisted in localStorage).
   This rule supplies the cover sizing + fixed attachment so the inline
   image renders correctly. */
body.bg-custom {
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  background-attachment: fixed;
}

/* Swatch previews for the new backgrounds (visible in Settings → Appearance) */
.bg-swatch.scanlines {
  background-image: repeating-linear-gradient(180deg, var(--bg) 0 2px, var(--ink-3) 2px 3px);
}
.bg-swatch.topo {
  /* Concentric contour rings — preview of the refined body.bg-topo. */
  background-color: var(--bg);
  background-image:
    radial-gradient(circle at 32% 38%,
      transparent 12%,
      color-mix(in srgb, var(--accent) 38%, transparent) 13%, transparent 14%,
      transparent 22%,
      color-mix(in srgb, var(--accent) 30%, transparent) 23%, transparent 24%,
      transparent 32%,
      color-mix(in srgb, var(--accent) 22%, transparent) 33%, transparent 34%,
      transparent 42%,
      color-mix(in srgb, var(--accent) 16%, transparent) 43%, transparent 44%);
}
.bg-swatch.vignette {
  background-image: radial-gradient(ellipse at center, var(--bg) 30%, var(--ink) 100%);
}
.bg-swatch.paper {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80'><filter id='p'><feTurbulence type='fractalNoise' baseFrequency='0.55' numOctaves='3'/></filter><rect width='100%' height='100%' filter='url(%23p)' opacity='0.4'/></svg>");
  background-color: color-mix(in srgb, var(--accent) 12%, var(--bg));
}
.bg-swatch.linen {
  background-color: color-mix(in srgb, var(--accent) 6%, var(--bg));
  background-image:
    repeating-linear-gradient(45deg, color-mix(in srgb, var(--ink-3) 28%, transparent) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(-45deg, color-mix(in srgb, var(--ink-3) 28%, transparent) 0 1px, transparent 1px 4px);
}
.bg-swatch.blueprint {
  background-color: color-mix(in srgb, var(--accent) 8%, var(--bg));
  background-image:
    linear-gradient(color-mix(in srgb, var(--accent) 35%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--accent) 35%, transparent) 1px, transparent 1px);
  background-size: 14px 14px;
}
.bg-swatch.constellation {
  background-color: var(--bg);
  background-image:
    radial-gradient(circle at 18% 28%, color-mix(in srgb, var(--accent) 75%, transparent) 0.8px, transparent 1.4px),
    radial-gradient(circle at 62% 22%, color-mix(in srgb, var(--accent) 60%, transparent) 0.6px, transparent 1.1px),
    radial-gradient(circle at 36% 58%, color-mix(in srgb, var(--accent) 80%, transparent) 0.9px, transparent 1.5px),
    radial-gradient(circle at 78% 68%, color-mix(in srgb, var(--accent) 60%, transparent) 0.6px, transparent 1.1px),
    radial-gradient(circle at 88% 38%, color-mix(in srgb, var(--accent) 70%, transparent) 0.7px, transparent 1.2px);
  background-size: 100% 100%;
}
.bg-swatch.marble {
  background-color: var(--bg);
  background-image:
    radial-gradient(at 25% 30%, color-mix(in srgb, var(--accent) 22%, transparent) 0%, transparent 60%),
    radial-gradient(at 75% 70%, color-mix(in srgb, var(--accent-2) 16%, transparent) 0%, transparent 60%),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80'><filter id='m'><feTurbulence type='fractalNoise' baseFrequency='0.05 0.16' numOctaves='2'/></filter><rect width='100%' height='100%' filter='url(%23m)' opacity='0.4'/></svg>");
}
.bg-swatch.custom {
  background-image: linear-gradient(135deg, color-mix(in srgb, var(--accent) 24%, transparent), transparent);
  background-color: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sans);
  font-size: var(--fs-16);
  color: var(--accent-text, var(--accent));
}
.bg-swatch.custom::before { content: "↑"; font-weight: 600; }
@media (prefers-reduced-motion: reduce) {
  body.bg-aurora { animation: none; }
}


/* ─── Phase B Slot 1B (2026-04-29) — auto-mode light source ──────────
   When data-mode="auto" AND the OS reports prefers-color-scheme: light,
   these rules are the explicit light palette for the four hero themes.
   `data-mode` is stamped only on <body> (shared_head.py / shared_scripts.py),
   and "auto" / "light" / "dark" are mutually exclusive on one element, so
   this is purely the auto-mode-light definition — it never has to out-rank
   a sibling [data-mode="dark"].

   DRIFT NOTE (audited 2026-05-28): these four palettes are NOT a uniform
   copy of one canonical source, so treat each pair below as hand-synced:
     · slate / cerulean — value-identical to their bare body.theme-X rules
       in themes.py (which already apply under OS-light); kept explicit
       because tests/test_themes_light_contrast.py pins the cerulean auto
       selector, and to keep the four-theme block symmetric.
     · paper / mono — DISTINCT palettes (paper differs from bare in ~12
       tokens incl. a bare-absent --accent-ink; mono differs in ~6). For
       these two this block is the SOLE auto-mode-light definition — do not
       delete or "reconcile" to the bare rule without changing rendered
       output. Raw CSS has no rule-reference, so a true text-dedupe would
       need a preprocessor; until then, sync values here by hand if the
       canonical light palettes change. */
@media (prefers-color-scheme: light) {
  body.theme-paper[data-mode="auto"] { --bg: #f7f6f2; --bg-raised: #ffffff; --bg-sunken: #eceae4; --ink: #111111; --ink-2: #2e2a24; --ink-3: #5f5a50; --ink-4: #918a7c; --rule: #d9d5cc; --rule-2: #e7e2d8; --accent: #c2711f; --accent-2: #b45f17; --accent-3: #9b4a0d; --accent-ink: #1a0d02; --accent-soft: rgba(224,138,44,0.11); }
  body.theme-cerulean[data-mode="auto"]   { --bg: #e1e7ef; --bg-raised: #fbfcfe; --bg-sunken: #dce3ec; --ink: #101827; --ink-2: #2d3948; --ink-3: #4f5b6b; --ink-4: #7e8b9b; --rule: #c6d0dc; --rule-2: #dbe2ea; --accent: #1e7fc4; --accent-text: #1666a8; --accent-2: #2c9be2; --accent-3: #8bb8de; --accent-ink: #ffffff; --accent-focus: #1e7fc4; --accent-soft: rgba(30,127,196,0.18); --action: #1e7fc4; --action-2: #2c9be2; --action-ink: #ffffff; --action-soft: rgba(30,127,196,0.16); }
  body.theme-slate[data-mode="auto"] { --bg: #08111f; --bg-raised: #101a2b; --bg-elevated: #16223a; --bg-sunken: #050a14; --ink: #e7edf7; --ink-2: #c7d2e4; --ink-3: #95a4ba; --ink-4: #65758d; --rule: #25324a; --rule-2: #1b263b; --accent: #2f7bf7; --accent-text: #60a5fa; --accent-2: #60a5fa; --accent-3: #93c5fd; --accent-ink: #ffffff; --accent-soft: rgba(47,123,247,0.18); }
  body.theme-mono[data-mode="auto"]  { --bg: #ffffff; --bg-raised: #fafafa; --bg-sunken: #f0f0f0; --ink: #000000; --ink-2: #1a1a1a; --ink-3: #4a4a4a; --ink-4: #888888; --rule: #cccccc; --rule-2: #e0e0e0; --accent: #000000; --accent-2: #333333; --accent-3: #666666; --accent-soft: rgba(0,0,0,0.06); }
}

/* ─── Widget dashboard ──────────────────────────────────────────────── */
.widget-grid {
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-auto-rows: minmax(140px, auto); gap: var(--space-4); margin: var(--space-4) 0;
  min-width: 0;
}
@media (max-width: 768px) {
  .widget-grid { grid-template-columns: 1fr; gap: var(--space-3); }
  .widget--1x1, .widget--2x1, .widget--1x2, .widget--2x2, .widget--4x3, .widget--6x4 { grid-column: span 1; grid-row: span 1; }
}
@media (min-width: 769px) and (max-width: 1100px) {
  .widget-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .widget--1x1 { grid-column: span 3; grid-row: span 1; }
  .widget--2x1 { grid-column: span 6; grid-row: span 1; }
  .widget--1x2 { grid-column: span 3; grid-row: span 2; }
  .widget--2x2 { grid-column: span 6; grid-row: span 2; }
  .widget--4x3 { grid-column: span 6; grid-row: span 3; }
  .widget--6x4 { grid-column: span 6; grid-row: span 4; }
}
@media (min-width: 1101px) {
  .widget--1x1 { grid-column: span 3; grid-row: span 1; }
  .widget--2x1 { grid-column: span 6; grid-row: span 1; }
  .widget--1x2 { grid-column: span 3; grid-row: span 2; }
  .widget--2x2 { grid-column: span 6; grid-row: span 2; }
  .widget--4x3 { grid-column: span 6; grid-row: span 3; }
  .widget--6x4 { grid-column: span 6; grid-row: span 4; }
}
.widget {
  position: relative; background: var(--bg-raised); border: 1px solid var(--rule);
  border-radius: var(--radius-md, var(--radius)); padding: 18px;
  display: flex; flex-direction: column;
  transition: transform 220ms var(--ease), box-shadow 220ms var(--ease), border-color 200ms var(--ease);
  min-height: 0; min-width: 0;
}
.widget:hover { box-shadow: var(--shadow-md); }
.widget__header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 10px; gap: var(--space-2);
}
.widget__title {
  font-size: var(--fs-12); font-weight: 600; color: var(--ink-3);
  text-transform: uppercase; letter-spacing: 0.06em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.widget__hint { font-size: 10px; color: var(--ink-3); white-space: nowrap; }
.widget__body {
  flex: 1; display: flex; flex-direction: column; gap: var(--space-2);
  min-height: 0; min-width: 0; overflow: hidden;
}
.widget__body--center { justify-content: center; align-items: center; text-align: center; }
.widget__numeral {
  font-family: var(--font-display, var(--font-heading, var(--serif)));
  font-size: 44px; font-weight: 600; line-height: 1; color: var(--ink);
  letter-spacing: -0.02em;
}
.widget__numeral .muted { opacity: 0.62; font-size: 0.6em; font-weight: 500; }
.widget__chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: 100px; font-size: var(--fs-12); font-weight: 600;
  background: var(--accent-soft); color: var(--accent-text, var(--accent));
  border: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
}
/* Train 4 a11y (2026-05-12): chip color blends to ink so text contrast
   meets WCAG AA on light backgrounds. The hue still reads as "green/
   amber/red" because the saturated source is preserved at 65%, but
   the 35% ink blend pulls luminance into the readable range. */
.widget__chip--success { background: color-mix(in srgb, var(--success) 14%, transparent); color: color-mix(in srgb, var(--success) 65%, var(--ink)); border-color: color-mix(in srgb, var(--success) 28%, transparent); }
.widget__chip--warn    { background: color-mix(in srgb, var(--warning) 14%, transparent); color: color-mix(in srgb, var(--warning) 65%, var(--ink)); border-color: color-mix(in srgb, var(--warning) 28%, transparent); }
.widget__chip--danger  { background: color-mix(in srgb, var(--danger) 14%, transparent); color: color-mix(in srgb, var(--danger) 65%, var(--ink)); border-color: color-mix(in srgb, var(--danger) 28%, transparent); }
.widget__chip--neutral { background: var(--bg-sunken, var(--bg-elevated)); color: var(--ink-2); border-color: var(--rule); }

.widget__btn {
  appearance: none; padding: 8px 14px; border-radius: 8px; cursor: pointer;
  background: var(--bg-elevated, var(--bg-raised)); border: 1px solid var(--rule);
  color: var(--ink); font-size: var(--fs-12); font-weight: 500; font-family: inherit;
  transition: all 180ms var(--ease);
}
.widget__btn:hover { border-color: var(--accent); color: var(--accent-text, var(--accent)); }
.widget__btn--primary { background: var(--action, var(--accent)); color: var(--action-ink, var(--accent-ink, #fff)); border-color: var(--action, var(--accent)); }
.widget__btn--primary:hover { background: var(--action-2, var(--accent-2)); border-color: var(--action-2, var(--accent-2)); color: var(--action-ink, var(--accent-ink, #fff)); }
.widget__btn--danger { background: var(--danger); color: #fff; border-color: var(--danger); }
.widget__btn--danger:hover { background: color-mix(in srgb, var(--danger) 80%, white); border-color: color-mix(in srgb, var(--danger) 80%, white); color: #fff; }
.widget__btn--big { padding: 14px 20px; font-size: var(--fs-14); font-weight: 600; }
.widget__btn--ghost { background: transparent; border-color: var(--rule-2); color: var(--ink-2); }
.widget__btn--ghost:hover { color: var(--accent-text, var(--accent)); border-color: var(--accent); }

.widget__row { display: flex; gap: 6px; flex-wrap: wrap; }
.widget__row--tight { gap: var(--space-1); }
.widget__divider { height: 1px; background: var(--rule-2); margin: 8px 0; }

.widget__select, .widget__input, .widget__textarea {
  appearance: none; padding: 8px 10px; border: 1px solid var(--rule);
  border-radius: 8px; background: var(--bg); color: var(--ink);
  font-family: inherit; font-size: var(--fs-12); width: 100%;
  transition: border-color 180ms var(--ease);
}
.widget__select:focus, .widget__input:focus, .widget__textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
.widget__textarea { resize: vertical; min-height: 60px; font-family: var(--font-mono, var(--mono)); }

.widget__list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; overflow-y: auto; }
.widget__list li {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 8px; border-radius: 6px; font-size: var(--fs-12);
  background: var(--bg-elevated, var(--bg-raised));
  border: 1px solid transparent;
}
.widget__list li:hover { border-color: var(--rule); }

/* Edit mode + chrome */
.widget__remove, .widget__resize, .widget__handle { display: none; }
.editing-dashboard .widget {
  cursor: default; user-select: none;
  animation: widget-wiggle 0.6s ease-in-out infinite;
  border-style: dashed;
  border-color: color-mix(in srgb, var(--accent) 40%, var(--rule));
}
.editing-dashboard .widget:nth-child(odd) { animation-delay: -0.3s; }
.editing-dashboard .widget:hover { border-color: color-mix(in srgb, var(--accent) 65%, var(--rule)); }
.editing-dashboard .widget__remove,
.editing-dashboard .widget__resize { display: flex; }
.editing-dashboard .widget__handle { display: flex; opacity: 0; transition: opacity 120ms var(--ease); }
.editing-dashboard .widget:hover .widget__handle,
.editing-dashboard .widget__handle:focus-visible { opacity: 1; }
.editing-dashboard .widget input,
.editing-dashboard .widget textarea,
.editing-dashboard .widget select,
.editing-dashboard .widget button:not(.widget__remove):not(.widget__resize):not(.widget__handle) {
  pointer-events: none; opacity: 0.55;
}
.widget__remove {
  position: absolute; top: -10px; right: -10px;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--danger); color: #fff; border: 2px solid var(--bg);
  cursor: pointer; font-size: var(--fs-14); line-height: 1;
  align-items: center; justify-content: center;
  z-index: 3; box-shadow: var(--shadow-md);
  transition: transform 150ms var(--ease);
}
.widget__remove:hover { transform: scale(1.12); background: color-mix(in srgb, var(--danger) 80%, white); }
.widget__resize {
  position: absolute; bottom: 8px; right: 8px;
  width: 26px; height: 26px; border-radius: 6px;
  background: var(--accent-soft); color: var(--accent-text, var(--accent));
  cursor: pointer; font-size: var(--fs-14); line-height: 1;
  align-items: center; justify-content: center; border: none;
  z-index: 3; transition: transform 150ms var(--ease);
}
.widget__resize:hover { transform: scale(1.12); }
.widget__handle {
  position: absolute; top: 10px; left: 8px;
  width: 24px; height: 24px; border-radius: 4px;
  background: transparent; border: none;
  cursor: grab; font-size: 14px; font-family: var(--mono, ui-monospace, monospace);
  color: var(--ink-4); line-height: 1;
  align-items: center; justify-content: center;
  z-index: 3; transition: color 120ms var(--ease), background 120ms var(--ease);
  user-select: none;
}
.widget__handle:hover { color: var(--accent-text, var(--accent)); background: var(--accent-soft, rgba(192,113,31,0.10)); }
.widget__handle:active { cursor: grabbing; }
.widget.is-dragging { opacity: 0.4; transform: scale(0.96); }
.widget.is-drop-target { outline: 2px dashed var(--accent); outline-offset: -2px; }

@keyframes widget-wiggle {
  0%   { transform: rotate(0deg); }
  25%  { transform: rotate(-0.5deg); }
  75%  { transform: rotate(0.5deg); }
  100% { transform: rotate(0deg); }
}

.dash-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  margin: 8px 0 0 0; gap: var(--space-3); flex-wrap: wrap;
}
.dash-toolbar__hint { font-size: var(--fs-12); color: var(--ink-3); }
.btn-edit-dashboard, .btn-add-widget, .btn-reset-layout {
  appearance: none; padding: 9px 18px; border-radius: 100px;
  background: var(--bg-raised); border: 1px solid var(--rule);
  cursor: pointer; font-size: var(--fs-12); font-weight: 600; color: var(--ink);
  font-family: inherit;
  transition: all 200ms var(--ease);
}
.btn-edit-dashboard:hover, .btn-add-widget:hover, .btn-reset-layout:hover {
  border-color: var(--accent); color: var(--accent-text, var(--accent));
}
.editing-dashboard .btn-edit-dashboard {
  background: var(--action, var(--accent)); color: var(--action-ink, var(--accent-ink, #fff)); border-color: var(--action, var(--accent));
}
.editing-dashboard .btn-edit-dashboard:hover { background: var(--action-2, var(--accent-2)); border-color: var(--action-2, var(--accent-2)); color: var(--action-ink, var(--accent-ink, #fff)); }
.btn-add-widget { background: var(--action, var(--accent)); color: var(--action-ink, var(--accent-ink, #fff)); border-color: var(--action, var(--accent)); }
.btn-add-widget:hover { background: var(--action-2, var(--accent-2)); border-color: var(--action-2, var(--accent-2)); color: var(--action-ink, var(--accent-ink, #fff)); }

/* Add Widget bottom sheet
   2026-04-30 evening: max-height trimmed from 60vh to 33vh per Kevin —
   the shelf was eating more than half the viewport in edit mode,
   pushing dashboard widgets off-screen. 1/3 viewport keeps the shelf
   visible without crowding the editing surface. */
.widget-shelf {
  position: fixed; bottom: 0; left: 0; right: 0;
  background: var(--bg-raised); border-top: 1px solid var(--rule);
  box-shadow: 0 -8px 32px rgba(0,0,0,0.10); padding: var(--space-5);
  /* Default 33vh, but the JS-applied --shelf-height (read from
     localStorage during edit-mode entry) overrides this for users who
     have dragged the resize handle. Range is enforced in JS to
     [20vh, 80vh]. */
  height: var(--shelf-height, 33vh);
  box-sizing: border-box;
  max-width: 100vw;
  max-height: 80vh; overflow-y: auto; overflow-x: hidden; z-index: 100;
  transform: translateY(100%); transition: transform 320ms var(--ease);
}
.editing-dashboard .widget-shelf { transform: translateY(0); }
/* Resize handle — sits along the top edge of the shelf, draggable up/down
   to make the shelf taller/shorter while jiggle mode is active. The grip
   line in the middle is the visual affordance. Hidden when not in edit
   mode (the shelf itself is offscreen anyway). */
.widget-shelf__resize-handle {
  position: absolute; top: 0; left: 0; right: 0;
  height: 14px;
  cursor: ns-resize;
  display: flex; align-items: center; justify-content: center;
  background: transparent;
  /* Slight visual feedback on hover so the affordance is discoverable. */
  transition: background 120ms var(--ease);
}
.widget-shelf__resize-handle:hover,
.widget-shelf__resize-handle:focus-visible,
.widget-shelf__resize-handle.is-dragging {
  background: var(--accent-soft, rgba(194, 113, 31, 0.08));
  outline: none;
}
.widget-shelf__resize-grip {
  width: 48px; height: 4px; border-radius: 2px;
  background: var(--ink-3, #6e6657);
  opacity: 0.4;
  transition: opacity 120ms var(--ease);
}
.widget-shelf__resize-handle:hover .widget-shelf__resize-grip,
.widget-shelf__resize-handle:focus-visible .widget-shelf__resize-grip,
.widget-shelf__resize-handle.is-dragging .widget-shelf__resize-grip {
  opacity: 0.9;
}
/* While dragging, kill the global text selection + reduce shelf
   transition jank. */
body.is-resizing-shelf {
  cursor: ns-resize !important;
  user-select: none;
}
body.is-resizing-shelf .widget-shelf {
  transition: none;
}
/* Reduced-motion: keep the shelf snap-resize, no smooth transition on
   the slide-in/out either. Resize itself stays usable (height changes
   are instant), just no animation. */
@media (prefers-reduced-motion: reduce) {
  .widget-shelf,
  .widget-shelf__resize-handle,
  .widget-shelf__resize-grip {
    transition: none;
  }
}
.widget-shelf__header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 16px; max-width: var(--w-wide); margin-left: auto; margin-right: auto;
}
.widget-shelf__title { font-size: var(--fs-18); font-weight: 600; margin: 0; }
.widget-shelf__close { font-size: var(--fs-22); line-height: 1; cursor: pointer; padding: 4px 12px;
  background: var(--bg); border: 1px solid var(--rule); border-radius: 100px; }
.widget-shelf__grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr));
  gap: 10px; max-width: var(--w-wide); margin: 0 auto;
}
.widget-shelf__item {
  padding: 14px; background: var(--bg); border: 1px solid var(--rule);
  border-radius: var(--radius-sm); cursor: pointer; text-align: left;
  font-family: inherit;
  transition: transform 160ms var(--ease), border-color 160ms var(--ease);
}
.widget-shelf__item:hover { transform: translateY(-2px); border-color: var(--accent); }
.widget-shelf__item h3 { font-size: var(--fs-12); margin: 0 0 4px 0; font-weight: 600; color: var(--ink); }
.widget-shelf__item p { font-size: var(--fs-12); color: var(--ink-3); margin: 0; line-height: 1.4; }
.widget-shelf__item.is-on-grid { opacity: 0.4; pointer-events: none; }
.widget-shelf__item.is-on-grid h3::after { content: " · already on grid"; font-weight: 400; color: var(--ink-4); }
.widget-shelf__hidden-section { margin-top: var(--space-4, 16px); border-top: 1px solid var(--rule); padding-top: var(--space-4, 16px); }
.widget-shelf__hidden-title {
  font-size: var(--fs-10, 10px); font-weight: 600; color: var(--ink-4);
  text-transform: uppercase; letter-spacing: 0.08em; margin: 0 0 var(--space-2, 8px) 0;
}
.widget-shelf__item--hidden { opacity: 0.65; }
.widget-shelf__item--hidden:hover { opacity: 1; border-color: var(--accent); }
.widget-shelf__restore-pill {
  display: inline-block; margin-top: 6px;
  font-size: 10px; font-weight: 600; color: var(--accent-text, var(--accent));
  font-family: var(--sans, system-ui, sans-serif);
}

/* ─── Widget sections (public + private partitioning) ──────────────── */
.widget-section { margin: var(--space-4) 0 32px; }
.widget-section + .widget-section { border-top: 1px dashed var(--rule); padding-top: 24px; }
.widget-section__header {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3);
  margin-bottom: 14px; padding-bottom: 10px;
}
.widget-section__heading { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.widget-section__heading > div { min-width: 0; }
.widget-section__title {
  font-family: var(--font-heading, var(--font-display, var(--serif)));
  font-size: var(--fs-18); font-weight: 600; margin: 0; color: var(--ink);
  letter-spacing: -0.01em;
}
.widget-section__sub { font-size: var(--fs-12); color: var(--ink-3); margin: 2px 0 0; }
.widget-section__chip {
  display: inline-flex; align-items: center; padding: 4px 10px; border-radius: 100px;
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  flex-shrink: 0;
}
.widget-section__chip--public {
  background: color-mix(in srgb, var(--success) 14%, transparent); color: var(--success);
  border: 1px solid color-mix(in srgb, var(--success) 28%, transparent);
}
.widget-section__chip--private {
  background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent-text, var(--accent));
  border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
}
.widget-section__move {
  appearance: none; padding: 6px 14px; border-radius: 100px;
  background: var(--bg-raised); border: 1px solid var(--rule); cursor: pointer;
  font-size: var(--fs-12); font-weight: 600; color: var(--ink-2); font-family: inherit;
  transition: all 180ms var(--ease); white-space: nowrap; flex-shrink: 0;
  display: none;
}
.editing-dashboard .widget-section__move { display: inline-block; }
.widget-section__move:hover { border-color: var(--accent); color: var(--accent-text, var(--accent)); }

/* Hide section headers when NOT in edit mode for a quieter default view */
/* Train 4 a11y (2026-05-12): replaced opacity 0.6 with explicit
   color: var(--ink-2). The opacity approach killed contrast on the
   heading text (measured 1.39:1 in Lighthouse — way under WCAG AA).
   Using --ink-2 directly preserves the "quieter than full ink" affordance
   while keeping contrast above 7:1 against the page background. */
:not(.editing-dashboard) .widget-section__header { color: var(--ink-2); }
:not(.editing-dashboard) .widget-section__header .widget-section__title { color: var(--ink-2); }
:not(.editing-dashboard) .widget-section__header .widget-section__sub-title { color: var(--ink-3); }
:not(.editing-dashboard) .widget-section__sub { display: none; }

/* Shelf row chip */
.widget-shelf__row { display: flex; align-items: center; gap: var(--space-2); margin-bottom: 4px; }

/* ─── Control deck — tree widget + tap-to-popup ──────────────────────── */
.ctrl-tree { display: flex; flex-direction: column; gap: var(--space-3); font-size: var(--fs-12); padding: 4px 0; }
.ctrl-tree__root { padding-bottom: 4px; border-bottom: 1px dashed var(--rule); }
.ctrl-tree__group {
  display: flex; flex-direction: column; gap: var(--space-1);
  padding: 4px 0 4px 14px;
  margin-left: 18px;
  position: relative;
}
.ctrl-tree__group::before {
  content: ""; position: absolute; left: 0; top: 4px; bottom: 4px;
  width: 2px;
  background: linear-gradient(180deg, var(--accent) 0%, var(--rule) 70%, var(--rule) 100%);
  opacity: 0.6;
}
.ctrl-tree__label {
  font-family: var(--mono); font-size: 10px; color: var(--ink-4);
  text-transform: uppercase; letter-spacing: 0.06em;
  padding: 4px 0;
}
.ctrl-tree__empty {
  font-family: var(--mono); font-size: var(--fs-12); color: var(--ink-4);
  margin: 0; padding: var(--space-3);
  text-align: center;
}
.ctrl-node {
  appearance: none; cursor: pointer;
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px;
  background: var(--bg-elevated, var(--bg-raised));
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  font-family: inherit; font-size: var(--fs-12); color: var(--ink);
  text-align: left; width: 100%;
  transition: border-color 180ms var(--ease), transform 180ms var(--ease), background 180ms var(--ease);
}
.ctrl-node:hover {
  border-color: var(--accent);
  transform: translateX(2px);
  background: color-mix(in srgb, var(--accent-soft) 50%, var(--bg-elevated, var(--bg-raised)));
}
.ctrl-node:active { transform: translateX(2px) scale(0.98); }
.ctrl-node__icon { font-size: var(--fs-16); flex-shrink: 0; }
.ctrl-node__name { flex: 1; font-weight: 500; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ctrl-node__tag {
  font-family: var(--mono); font-size: 10px; color: var(--ink-3);
  text-transform: uppercase; letter-spacing: 0.06em; flex-shrink: 0;
  padding: 2px 8px; border-radius: 999px;
  background: var(--bg-sunken, var(--rule-2));
}
.ctrl-node--maestro {
  background: linear-gradient(135deg, var(--accent-soft) 0%, var(--bg-raised) 100%);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--rule));
}
.ctrl-node--maestro .ctrl-node__tag {
  color: var(--accent-text, var(--accent)); background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.ctrl-node--virtuoso .ctrl-node__icon { color: var(--accent-text, var(--accent)); }
.ctrl-node--alert {
  border-color: color-mix(in srgb, var(--danger) 50%, var(--rule));
}
.ctrl-node--alert .ctrl-node__tag {
  color: var(--danger); background: color-mix(in srgb, var(--danger) 14%, transparent);
}

/* Popup overlay (singleton at body level, shown via .show class) */
.ctrl-popup {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(0,0,0,0.55);
  display: none;
  align-items: center; justify-content: center;
  padding: var(--space-5);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.ctrl-popup.show { display: flex; animation: ctrl-popup-fade 180ms var(--ease); }
@keyframes ctrl-popup-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.ctrl-popup__panel {
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  max-width: 60ch; width: 100%;
  max-height: 84vh;
  display: flex; flex-direction: column;
  box-shadow: 0 24px 64px rgba(0,0,0,0.32), 0 4px 12px rgba(0,0,0,0.18);
  animation: ctrl-popup-rise 220ms var(--ease);
}
@keyframes ctrl-popup-rise {
  from { transform: translateY(12px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}
.ctrl-popup__header {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-4) 20px;
  border-bottom: 1px solid var(--rule);
  flex-shrink: 0;
}
.ctrl-popup__title {
  font-family: var(--serif); font-size: var(--fs-18); font-weight: 500;
  letter-spacing: -0.005em; margin: 0; color: var(--ink);
}
.ctrl-popup__close {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  font-size: var(--fs-22); line-height: 1; padding: 4px 12px;
  border-radius: 999px; color: var(--ink-3);
  transition: background 180ms var(--ease), color 180ms var(--ease);
}
.ctrl-popup__close:hover { background: var(--bg-sunken, var(--rule-2)); color: var(--ink); }
.ctrl-popup__body {
  padding: var(--space-4) 20px 20px;
  overflow-y: auto;
  display: flex; flex-direction: column; gap: var(--space-3);
}

/* Action card inside popup */
.ctrl-action {
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  background: var(--bg);
}
.ctrl-action h4 {
  font-family: var(--sans); font-size: var(--fs-12); font-weight: 600;
  color: var(--ink-3);
  text-transform: uppercase; letter-spacing: 0.08em;
  margin: 0 0 8px;
}
.ctrl-action__row {
  display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center;
}
.ctrl-action__current {
  font-family: var(--mono); font-size: var(--fs-12); color: var(--ink-2);
  padding: 4px 10px; border-radius: 999px;
  background: var(--bg-sunken, var(--rule-2));
  flex-shrink: 0;
}
.ctrl-action__feedback {
  font-family: var(--mono); font-size: var(--fs-12); color: var(--ink-3);
  margin: 6px 0 0; min-height: 14px;
}
.ctrl-action--danger {
  border-color: color-mix(in srgb, var(--danger) 30%, var(--rule));
  background: color-mix(in srgb, var(--danger) 4%, var(--bg));
}

/* ─── Pair page (browser <-> Mac handshake UX) ───────────────────────── */
.pair-card {
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 24px 28px 22px;
  display: flex; flex-direction: column; gap: 18px;
  box-shadow: var(--shadow-sm);
}
.pair-page__mode-note {
  margin: 0 0 var(--space-3);
  font-family: var(--mono); font-size: var(--fs-12); color: var(--ink-3);
}
.pair-local-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: var(--space-3);
  align-items: start;
}
.pair-local-grid[hidden] { display: none; }
.pair-local-card,
.pair-device-card,
.pair-code-card {
  background: linear-gradient(135deg, var(--bg-raised), color-mix(in srgb, var(--accent) 7%, var(--bg-raised)));
  border: 1px solid color-mix(in srgb, var(--accent) 22%, var(--rule));
  border-radius: var(--radius);
  padding: 24px 28px 22px;
  display: flex; flex-direction: column; gap: 18px;
  box-shadow: var(--shadow-sm);
}
.pair-local-card[hidden],
.pair-device-card[hidden],
.pair-code-card[hidden],
.pair-code-output[hidden] { display: none; }
.pair-local-card__header {
  display: grid; grid-template-columns: 54px minmax(0, 1fr);
  gap: 14px; align-items: center;
}
.pair-local-card__avatar {
  width: 54px; height: 54px; border-radius: 14px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg); border: 1px solid var(--rule);
  color: var(--accent-text, var(--accent));
  font-size: 26px; box-shadow: inset 0 1px 0 rgba(255,255,255,0.35);
}
.pair-local-card__eyebrow {
  margin: 0 0 3px;
  font-family: var(--mono); font-size: var(--fs-12); color: var(--accent-text, var(--accent));
  text-transform: uppercase; letter-spacing: 0.08em;
}
.pair-local-card h2,
.pair-device-card h2,
.pair-code-card h2 {
  margin: 0;
  font-size: clamp(24px, 4vw, 32px); line-height: 1.08;
}
.pair-local-card__server {
  margin: 7px 0 0;
  font-family: var(--mono); font-size: var(--fs-12); color: var(--ink-3);
  overflow-wrap: anywhere;
}
.pair-local-card__copy,
.pair-code-card .pair-local-card__copy,
.pair-manual__copy {
  margin: 0;
  color: var(--ink-2); line-height: var(--lh-relaxed);
}
.pair-local-card__copy code {
  font-size: var(--fs-12); padding: 1px 5px; border-radius: 4px;
  background: var(--bg-sunken, var(--bg)); border: 1px solid var(--rule);
}
.pair-local-actions {
  display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center;
}
.pair-local-go {
  display: inline-flex; align-items: center; gap: 8px;
  font-weight: 700; padding: 11px 22px;
}
.pair-local-check {
  width: 18px; height: 18px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--success) 18%, var(--bg));
  color: var(--success); font-size: 12px; line-height: 1;
  transform: scale(0.85); opacity: 0.8;
  transition: transform 180ms var(--ease), opacity 180ms var(--ease);
}
.pair-local-card.is-paired .pair-local-check {
  animation: pair-check-pop 420ms var(--ease) both;
  opacity: 1;
}
.pair-code-output {
  display: grid;
  justify-items: center;
  gap: var(--space-2);
  padding: 14px;
  border: 1px dashed color-mix(in srgb, var(--accent) 35%, var(--rule));
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--bg) 86%, var(--accent) 14%);
}
.pair-qr-frame {
  width: min(220px, 100%);
  aspect-ratio: 1;
  padding: 10px;
  border-radius: 8px;
  background: #fff;
  border: 1px solid var(--rule);
}
.pair-qr-frame img {
  display: block;
  width: 100%;
  height: 100%;
}
.pair-offer-code {
  font-family: var(--mono);
  font-size: clamp(34px, 7vw, 48px);
  line-height: 1;
  letter-spacing: 0;
  color: var(--ink);
}
.pair-countdown {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--fs-12);
  color: var(--ink-3);
}
.pair-code-label {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.pair-code-input {
  font-size: 28px;
  text-align: center;
  letter-spacing: 0;
}
.pair-manual {
  margin-top: var(--space-4);
}
.pair-manual > summary {
  cursor: pointer;
  font-family: var(--sans); font-size: var(--fs-12); font-weight: 700;
  color: var(--accent-text, var(--accent));
  list-style: none;
}
.pair-manual > summary::-webkit-details-marker { display: none; }
.pair-manual__copy {
  margin-top: var(--space-2);
  font-size: var(--fs-14);
}
.pair-manual .pair-card { margin-top: var(--space-3); }
.pair-field { display: flex; flex-direction: column; gap: 6px; }
.pair-field label { display: flex; flex-direction: column; gap: 2px; cursor: default; }
.pair-field__name {
  font-family: var(--sans); font-size: var(--fs-12); font-weight: 600;
  color: var(--ink);
}
.pair-field__hint {
  font-family: var(--mono); font-size: var(--fs-12); color: var(--ink-3);
}
.pair-field__hint code {
  font-size: var(--fs-12); padding: 1px 5px; border-radius: 4px;
  background: var(--bg-sunken, var(--bg)); border: 1px solid var(--rule);
}
.pair-input {
  font-family: var(--mono); font-size: var(--fs-12);
  padding: 10px 12px;
  border: 1px solid var(--rule); border-radius: 8px;
  background: var(--bg); color: var(--ink);
  width: 100%;
  transition: border-color 180ms var(--ease), box-shadow 180ms var(--ease);
}
.pair-input:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.pair-input-row { display: flex; gap: 6px; align-items: stretch; }
.pair-input-row .pair-input { flex: 1; min-width: 0; }
.pair-toggle {
  font-family: var(--mono); font-size: var(--fs-12);
  min-width: 56px; padding: 8px 12px;
}
.pair-actions {
  display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center;
  margin-top: 4px;
}
.pair-go {
  font-weight: 600; padding: 10px 22px;
}
.pair-status {
  font-family: var(--mono); font-size: var(--fs-12); line-height: 1.5;
  margin: 6px 0 0; padding: 10px 12px;
  border-radius: var(--radius-sm);
  display: none;
  border: 1px solid transparent;
}
.pair-status:not(:empty) { display: block; }
.pair-status--info {
  background: var(--accent-soft); color: var(--accent-text, var(--accent));
  border-color: color-mix(in srgb, var(--accent) 24%, transparent);
}
.pair-status--success {
  background: color-mix(in srgb, var(--success) 14%, transparent);
  color: var(--success);
  border-color: color-mix(in srgb, var(--success) 28%, transparent);
}
.pair-status--warn {
  background: color-mix(in srgb, var(--warning) 14%, transparent);
  color: var(--warning);
  border-color: color-mix(in srgb, var(--warning) 28%, transparent);
}
.pair-status--error {
  background: color-mix(in srgb, var(--danger) 14%, transparent);
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 28%, transparent);
}
/* Prefill status pill — shown above the actions row when localStorage
   already has a paired URL/token, so the user can see what is stored
   before editing or resetting. (warp:b 2026-05-02) */
.pair-prefill-status {
  font-family: var(--mono); font-size: var(--fs-12); line-height: 1.5;
  margin: 4px 0 0; padding: 8px 12px;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 8%, var(--bg-raised));
  border: 1px dashed color-mix(in srgb, var(--accent) 30%, var(--rule));
  color: var(--ink-2);
  word-break: break-word;
}
@keyframes pair-check-pop {
  0% { transform: scale(0.65); }
  60% { transform: scale(1.18); }
  100% { transform: scale(1); }
}

.activity-feed .activity-row {
  display: grid;
  grid-template-columns: 48px 16px 1fr auto;
  align-items: center;
  gap: 6px;
  padding: 5px 4px;
  border-radius: 4px;
  font-size: var(--fs-12, 12px);
  transition: background 120ms var(--ease);
}
.activity-feed .activity-row:hover { background: var(--bg-elevated, var(--bg-raised)); }
.activity-row__ts {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: 11px;
  color: var(--ink-4);
  text-align: right;
  white-space: nowrap;
}
.activity-row__icon {
  font-size: 11px;
  color: var(--ink-3);
  text-align: center;
}
.activity-row__slug {
  font-weight: 500;
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  grid-column: 3;
}
.activity-row__summary {
  font-size: 11px;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  grid-column: 4;
  max-width: 120px;
}
.tb-bar {
  height: 6px;
  background: var(--bg-sunken, rgba(0,0,0,0.06));
  border-radius: 100px;
  overflow: hidden;
}
.tb-bar__fill {
  height: 100%;
  border-radius: 100px;
  transition: width 0.4s var(--ease);
}
.tb--green .tb-bar__fill { background: var(--success); }
.tb--amber .tb-bar__fill { background: var(--warning); }
.tb--red .tb-bar__fill { background: var(--danger); }
.tb--pulse .tb-bar__fill { animation: tb-pulse 1.8s ease-in-out infinite; }
@keyframes tb-pulse {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0.6; }
}
@media (prefers-reduced-motion: reduce) {
  .tb-bar__fill { transition: none; }
  .tb--pulse .tb-bar__fill { animation: none; }
}
/* ─── Unified .btn system (Slot β · 2026-04-29) ──────────────────────────
   One base class + composable modifiers. Replaces the 7 disjoint primary-
   button classes that proliferated across the codebase. New code uses
   `class="btn btn--primary"` etc.; legacy aliases at the bottom of this
   bundle keep existing HTML rendering until the v1.1 cleanup pass.       */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2, 8px);
  padding: var(--space-3, 12px) var(--space-5, 24px);
  border: 1px solid transparent;
  border-radius: var(--r-md, 10px);
  background: transparent;
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--fs-14, 14px);
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  box-shadow: none;
  transition: background-color var(--dur-base, 180ms) var(--ease),
              border-color var(--dur-base, 180ms) var(--ease),
              color var(--dur-base, 180ms) var(--ease),
              transform var(--dur-base, 180ms) var(--ease),
              box-shadow var(--dur-base, 180ms) var(--ease);
  text-decoration: none;
  user-select: none;
}

.btn:disabled,
.btn[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Size modifiers */
.btn--lg { padding: var(--space-4, 16px) var(--space-6, 32px); font-size: var(--fs-16, 16px); }
.btn--xl { padding: var(--space-5, 24px) var(--space-7, 40px); font-size: var(--fs-18, 18px); }

/* Color modifiers */
.btn--primary {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--action-2, var(--accent-2, var(--accent))) 86%, white),
      var(--action, var(--accent)));
  border-color: color-mix(in srgb, var(--action, var(--accent)) 86%, black);
  color: var(--action-ink, var(--accent-ink, var(--bg)));
  box-shadow: 0 10px 22px -18px color-mix(in srgb, var(--action, var(--accent)) 62%, transparent);
}
.btn--primary:hover:not(:disabled):not([disabled]) {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--action-2, var(--accent-2, var(--accent))) 92%, white),
      color-mix(in srgb, var(--action, var(--accent)) 92%, black));
  border-color: color-mix(in srgb, var(--action, var(--accent)) 92%, black);
  transform: translateY(-1px);
  box-shadow: 0 14px 28px -20px color-mix(in srgb, var(--action, var(--accent)) 68%, transparent);
}

.btn--secondary {
  border-color: var(--rule);
  color: var(--ink);
  background: color-mix(in srgb, var(--bg-raised) 86%, var(--bg));
}
.btn--secondary:hover:not(:disabled):not([disabled]) {
  border-color: var(--accent-mid, var(--accent));
  color: var(--accent-mid, var(--accent));
  background: color-mix(in srgb, var(--accent) 7%, var(--bg-raised));
  transform: translateY(-1px);
}

.btn--ghost {
  color: var(--ink-2);
}
.btn--ghost:hover:not(:disabled):not([disabled]) {
  background: var(--bg-raised);
  color: var(--ink);
}

.btn--destructive {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}
.btn--destructive:hover:not(:disabled):not([disabled]) {
  filter: brightness(1.08);
}

/* Shape modifier */
.btn--pill { border-radius: var(--radius-pill); }

/* Min-tap-target on mobile (Slot γ may also bump; either lands first). */
@media (max-width: 768px) {
  .btn { min-height: 44px; }
}

/* ─── GitHub link card (Settings, Phase A.4 part 1, locked 2026-04-27 evening) ─────── */


.github-link {
  margin: 4px 0 28px;
  padding: 18px 20px;
  background: color-mix(in srgb, var(--bg-raised) 92%, var(--accent-soft, transparent));
  border: 1px solid color-mix(in srgb, var(--accent) 18%, var(--rule));
  border-radius: 12px;
  max-width: var(--w-prose);
}
.github-link[data-state="linked"] {
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 7%, transparent), var(--bg-raised));
  border-color: color-mix(in srgb, var(--accent) 32%, var(--rule));
}
.github-link[data-state="unconfigured"] {
  background: var(--bg-raised);
  border-color: color-mix(in srgb, var(--ink-2) 22%, var(--rule));
}
.github-link[data-state="offline"] {
  opacity: 0.7;
}
.github-link__row {
  display: flex;
  align-items: center;
  gap: 16px;
}
.github-link__avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent-text, var(--accent));
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  flex-shrink: 0;
}
.github-link__body {
  flex: 1;
  min-width: 0;
}
.github-link__status {
  margin: 0 0 4px;
  font-size: 14px;
  color: var(--ink);
  font-weight: 500;
}
.github-link__status strong {
  color: var(--accent-text, var(--accent));
}
.github-link__detail {
  margin: 0;
  font-size: 12px;
  color: var(--ink-3);
  line-height: 1.5;
}
.github-link__detail code {
  font-family: var(--mono);
  font-size: 11px;
  background: color-mix(in srgb, var(--ink-1) 6%, transparent);
  padding: 1px 5px;
  border-radius: 4px;
}
.github-link__actions {
  flex-shrink: 0;
}
.github-link__btn {
  display: inline-block;
  padding: 8px 16px;
  font-size: 13px;
  text-decoration: none;
  border-radius: 8px;
}
.github-link__toggle {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px dashed color-mix(in srgb, var(--ink-2) 18%, transparent);
}
.github-link__toggle-label {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  font-size: 13px;
  color: var(--ink);
}
.github-link__toggle-label input[type="checkbox"] {
  margin: 0;
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
}
.github-link__toggle-help {
  margin: 8px 0 0 26px;
  font-size: 12px;
  color: var(--ink-3);
  line-height: 1.5;
}
.github-link__notice {
  margin: 14px 0 0;
  padding: 10px 14px;
  font-size: 13px;
  border-radius: 8px;
  line-height: 1.5;
}
.github-link__notice--ok {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--ink);
  border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
}
.github-link__notice--err {
  background: color-mix(in srgb, var(--danger, #c44) 14%, transparent);
  color: var(--ink);
  border: 1px solid color-mix(in srgb, var(--danger, #c44) 28%, transparent);
}

@media (max-width: 540px) {
  .github-link__row { flex-wrap: wrap; }
  .github-link__actions { width: 100%; margin-top: 8px; }
  .github-link__btn { width: 100%; text-align: center; }
}

/* ─── BYOK Anthropic key card variant (Phase A.4 part 2.5) ─────────── */
.byok-card { margin-top: 12px; }
.byok-card[data-state="byok"] {
  border-color: color-mix(in srgb, var(--accent) 28%, transparent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
.byok-card[data-state="missing"] {
  border-color: color-mix(in srgb, var(--ink-2) 28%, transparent);
}
.byok-form {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px dashed color-mix(in srgb, var(--ink-2) 18%, transparent);
}
.byok-form__label {
  display: block;
  font-size: 12px;
  color: var(--ink-2);
  margin-bottom: 6px;
  font-weight: 500;
}
.byok-form__row {
  display: flex;
  gap: 8px;
  margin-bottom: 10px;
}
.byok-form__input {
  flex: 1;
  padding: 8px 12px;
  font-size: 13px;
  font-family: var(--mono);
  background: color-mix(in srgb, var(--ink) 4%, transparent);
  border: 1px solid color-mix(in srgb, var(--ink-2) 22%, transparent);
  border-radius: 8px;
  color: var(--ink-1);
  transition: border-color 0.16s ease;
}
.byok-form__input:focus {
  outline: none;
  border-color: var(--accent);
}
.byok-form__actions {
  display: flex;
  gap: 8px;
}
.byok-form__help {
  margin: 10px 0 0;
  font-size: 12px;
  color: var(--ink-3);
  line-height: 1.5;
}

/* ─── Dashboard sub-nav (Overview / Projects / Controls — locked 2026-04-27 evening) ──── */

.dash-subnav {
  border-bottom: 1px solid color-mix(in srgb, var(--ink-2) 18%, transparent);
  background: color-mix(in srgb, var(--ink-1) 2%, transparent);
}
.dash-subnav .wrap {
  display: flex;
  gap: 4px;
  /* Horizontal padding tracks the standard `.wrap` gutter (was a
     hardcoded 16px) so the Dashboard tab strip lines up with the hero
     headline above it and the section content below — one vertical
     gutter line down the whole page. The small vertical pad keeps the
     tabs tucked under the hero. (2026-06-02 spacing pass.) */
  padding: 4px var(--gutter) 0;
  flex-wrap: wrap;
}
.dash-subnav__link {
  padding: 10px 14px;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.02em;
  /* 2026-05-18 contrast fix: was --ink-3 (#6e6657) which sat below 4.5:1
     against the tinted nav background and felt washed out on Kevin's
     monitor. --ink-2 (#3c362c) is the right balance for an inactive
     but legible tab. Hover then bumps to --ink for a clear darkening. */
  color: var(--ink-2);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  border-radius: 6px 6px 0 0;
  transition: color 0.16s ease, background 0.16s ease, border-color 0.16s ease;
}
.dash-subnav__link:hover,
.dash-subnav__link:focus-visible {
  color: var(--ink);
  /* Soft hover surface so the hover state reads as a target, not just a
     color shift. Matches the in-pill hover treatment we use on the
     bundle gallery on the homepage. */
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  outline: none;
}
.dash-subnav__link:focus-visible {
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 40%, transparent);
}
.dash-subnav__link.is-active {
  color: var(--accent-text, var(--accent));
  border-bottom-color: var(--accent);
  font-weight: 600;
}
.dash-subnav__link.is-active:hover,
.dash-subnav__link.is-active:focus-visible {
  /* Preserve the accent color on the active tab even on hover, so the
     "you are here" cue does not flicker to ink-color and back. */
  color: var(--accent-text, var(--accent));
}

/* ─── Endenza manifesto page (the new /index.html, locked 2026-04-27 evening) ─────────── */

.ensemble-hero { padding-bottom: 24px; }
.ensemble-hero h1 { letter-spacing: -0.01em; }

.ensemble-pitch { padding: 48px 0 16px; }
.ensemble-pitch__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  align-items: stretch;
}
.ensemble-pitch__card {
  display: flex;
  flex-direction: column;
  background: color-mix(in srgb, var(--ink-1) 4%, transparent);
  border: 1px solid color-mix(in srgb, var(--ink-2) 18%, transparent);
  border-radius: 14px;
  padding: 28px 28px 24px;
  min-height: 280px;
}
.ensemble-pitch__card .eyebrow {
  margin: 0 0 12px;
  color: var(--accent-text, var(--accent));
  letter-spacing: 0.12em;
  font-size: 11px;
  font-weight: 600;
}
.ensemble-pitch__card h2 {
  font-size: 26px;
  line-height: 1.18;
  margin: 0 0 14px;
  letter-spacing: -0.005em;
}
.ensemble-pitch__card h2 em {
  font-style: italic;
  color: var(--accent-text, var(--accent));
}
.ensemble-pitch__card p {
  color: var(--ink-2);
  line-height: 1.6;
  margin: 0 0 16px;
  flex: 1;
}
.ensemble-pitch__link {
  display: inline-block;
  color: var(--accent-text, var(--accent));
  font-weight: 500;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  padding-bottom: 1px;
  margin-top: auto;
  align-self: flex-start;
  transition: border-color 0.16s ease;
}
.ensemble-pitch__link:hover {
  border-bottom-color: var(--accent);
}
.ensemble-pitch__link--soon {
  color: var(--ink-3);
  border-bottom-color: color-mix(in srgb, var(--ink-3) 30%, transparent);
  font-style: italic;
  pointer-events: none;
}

.ensemble-cadence { padding: 56px 0 32px; }
.ensemble-cadence h2 {
  font-size: 30px;
  line-height: 1.2;
  margin: 4px 0 12px;
  letter-spacing: -0.005em;
}
.ensemble-cadence h2 em {
  font-style: italic;
  color: var(--accent-text, var(--accent));
}
.ensemble-cadence .lead {
  margin-bottom: 24px;
  max-width: 60ch;
}
.ensemble-cadence__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  border-top: 1px solid color-mix(in srgb, var(--ink-2) 18%, transparent);
}
.ensemble-cadence__list li {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 24px;
  padding: 16px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--ink-2) 14%, transparent);
  align-items: baseline;
}
.ensemble-cadence__list li strong {
  font-weight: 600;
  color: var(--ink, #18150f);
  font-variant-numeric: tabular-nums;
}
.ensemble-cadence__list li span {
  color: var(--ink-2);
  line-height: 1.5;
}
.ensemble-cadence__list li span em {
  color: var(--accent-text, var(--accent));
  font-style: italic;
}

/* "What's ahead" timeline polish — mark shipped (is-done) + current
   (is-now) items so the reader can find the present moment at a glance.
   Moved here from endenza_body.py:ENDENZA_CTA_POLISH_CSS in Polish R6
   (2026-05-22) so both /index.html and /roadmap.html pick up the same
   visual chord — the cadence list now lives on both surfaces. */
.ensemble-cadence__list .is-done {
  position: relative;
  padding-left: 38px;
}
.ensemble-cadence__list .is-done::before {
  content: "¹3";
  position: absolute;
  left: 6px;
  top: 4px;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 18%, var(--bg-raised));
  color: var(--accent);
  display: grid;
  place-items: center;
  font: 700 12px/1 var(--sans);
  border: 1px solid color-mix(in srgb, var(--accent) 32%, var(--rule));
}
.ensemble-cadence__list .is-done strong { color: var(--accent); }
.ensemble-cadence__list .is-now {
  position: relative;
  padding-left: 38px;
}
.ensemble-cadence__list .is-now::before {
  content: "";
  position: absolute;
  left: 6px;
  top: 4px;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent-2, var(--accent)) 16%, var(--bg-raised));
  border: 1px solid color-mix(in srgb, var(--accent-2, var(--accent)) 38%, var(--rule));
  box-shadow:
    inset 0 0 0 4px var(--bg-raised),
    inset 0 0 0 5px color-mix(in srgb, var(--accent-2, var(--accent)) 80%, transparent);
  animation: endenza-cadence-pulse 2200ms var(--ease, ease-in-out) infinite alternate;
}
.ensemble-cadence__list .is-now strong { color: var(--accent-2, var(--accent)); }
@keyframes endenza-cadence-pulse {
  from { box-shadow: inset 0 0 0 4px var(--bg-raised), inset 0 0 0 5px color-mix(in srgb, var(--accent-2, var(--accent)) 60%, transparent); }
  to { box-shadow: inset 0 0 0 4px var(--bg-raised), inset 0 0 0 5px color-mix(in srgb, var(--accent-2, var(--accent)) 95%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .ensemble-cadence__list .is-now::before { animation: none; }
}

.ensemble-featured { padding: 56px 0 32px; }
.ensemble-featured h2 {
  font-size: 30px;
  line-height: 1.2;
  margin: 4px 0 12px;
  letter-spacing: -0.005em;
}
.ensemble-featured h2 em {
  font-style: italic;
  color: var(--accent-text, var(--accent));
}
.ensemble-featured .lead {
  margin-bottom: 28px;
  max-width: 60ch;
}
.ensemble-featured__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 18px;
}
.ensemble-featured__card {
  background: color-mix(in srgb, var(--ink-1) 3%, transparent);
  border: 1px solid color-mix(in srgb, var(--ink-2) 16%, transparent);
  border-radius: 12px;
  padding: 18px;
  display: flex;
  flex-direction: column;
}
.ensemble-featured__thumb {
  aspect-ratio: 4 / 3;
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--accent) 18%, transparent),
    color-mix(in srgb, var(--ink-1) 8%, transparent));
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 14px;
}
.ensemble-featured__thumb span {
  color: var(--ink-3);
  font-size: 12px;
  letter-spacing: 0.1em;
  font-weight: 500;
  text-transform: uppercase;
}
.ensemble-featured__card h3 {
  font-size: 16px;
  margin: 0 0 8px;
  font-weight: 600;
  color: var(--ink-1);
}
.ensemble-featured__card p {
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.5;
  margin: 0;
}
.ensemble-featured__card--your {
  border-color: color-mix(in srgb, var(--accent) 38%, transparent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.ensemble-featured__card--your h3 {
  color: var(--accent-text, var(--accent));
}
.ensemble-featured__card--your .ensemble-featured__thumb {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
}
.ensemble-featured__card--your .ensemble-featured__thumb span {
  color: var(--accent-text, var(--accent));
}

.endenza-cta { padding: 56px 0 64px; }
.endenza-cta h2 {
  font-size: 30px;
  line-height: 1.2;
  margin: 4px 0 12px;
  letter-spacing: -0.005em;
}
.endenza-cta h2 em {
  font-style: italic;
  color: var(--accent-text, var(--accent));
}
.endenza-cta .lead {
  margin-bottom: 24px;
  max-width: 60ch;
}
/* .endenza-cta__form + __input + __btn + __note styles removed
   2026-05-19 (Polish Round 1 Slot β) along with the form markup +
   wireWaitlistPolish JS. The build-log Formspree opt-in posted to a
   placeholder URL; killing it simplified the CTA hierarchy to a
   single Sign-in-with-GitHub primary. */

@media (max-width: 900px) {
  .ensemble-pitch__grid { grid-template-columns: 1fr; }
  .ensemble-pitch__card { min-height: 0; }
  .ensemble-cadence__list li { grid-template-columns: 1fr; gap: 4px; }
  .ensemble-featured__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 540px) {
  .ensemble-featured__grid { grid-template-columns: 1fr; }
}

/* ─── Legacy button aliases (Slot β · 2026-04-29) ────────────────────────
   Map old class names to the new .btn system. These selectors are
   no-ops on their own — they exist so authors can migrate HTML to
   `class="btn btn--primary cta-generate"` (or similar) and keep the
   old class for any external CSS / tests that reference it.

   Removal target: v1.1 cleanup pass once every emit site uses the
   new .btn classes directly.                                            */

.cta-generate,
.first-run-submit,
.studio-publish__primary,
.workshop-genesis__btn,
.endenza-cta__btn,
.widget__btn--primary {
  /* No-op — actual presentation comes from .btn / .btn--primary in the
     same class attribute. */
}

/* === iPhone-width responsive tightening (warp:b 2026-05-02) ============== */

/* Bottom-line guard: never let a stray fixed-width element push the page
   wider than the viewport. Every per-element overflow rule below is the
   real fix; this is the safety net. */
html, body { max-width: 100%; }

/* Safe-area insets — Face ID iPhones have rounded corners + a home
   indicator that need clearance. The wrap utility already gates
   horizontal padding via --gutter, so we only nudge the
   bottom/top here, plus the footer which sits at the home-indicator. */
@supports (padding: max(0px)) {
  body { padding-bottom: env(safe-area-inset-bottom, 0); }
  footer { padding-bottom: max(36px, calc(24px + env(safe-area-inset-bottom, 0))); }
  .topnav .wrap {
    padding-left: max(var(--gutter), env(safe-area-inset-left, 0));
    padding-right: max(var(--gutter), env(safe-area-inset-right, 0));
  }
}

/* Tap targets >= 44x44 — extend beyond the topnav rule that already exists.
   Buttons, swatches, and link rows need to clear the same bar on touch
   viewports. */
@media (max-width: 768px) {
  button,
  .btn-primary,
  .btn-secondary,
  .btn-danger,
  .pair-go,
  .pair-toggle,
  [role="button"] {
    min-height: 44px;
  }
  .theme-swatch,
  .bg-swatch {
    min-width: 44px; min-height: 44px;
  }
}

/* iPhone Pro Max + Plus widths (430 / 428). At this point the page should
   already be a single column; we just clamp the hero typography so it
   never overflows the viewport. */
@media (max-width: 430px) {
  body { overflow-x: hidden; }
  .wrap { padding-left: max(16px, env(safe-area-inset-left, 0));
          padding-right: max(16px, env(safe-area-inset-right, 0)); }
  /* Hero typography — clamp sizes that previously hit 60px+ */
  h1 { font-size: clamp(28px, 8.5vw, 40px); }
  h2 { font-size: clamp(22px, 6.2vw, 28px); }
  .proj-hero-text h1 { font-size: clamp(32px, 9vw, 44px); }
  /* Stat row should stack instead of overflow */
  .hero-stats { gap: 14px 22px; }
  /* Roster cards: single column at narrow widths */
  .roster { grid-template-columns: 1fr; }
  /* Pair card: tighten padding so the input row fits at 375px */
  .pair-card { padding: 18px 16px 16px; }
  .pair-local-card,
  .pair-device-card,
  .pair-code-card { padding: 18px 16px 16px; }
  .pair-local-card__header { grid-template-columns: 46px minmax(0, 1fr); gap: 10px; }
  .pair-local-card__avatar { width: 46px; height: 46px; border-radius: 12px; }
  .pair-qr-frame { width: min(190px, 100%); }
  .pair-input-row { flex-wrap: wrap; }
  .pair-input-row .pair-input { min-width: 0; flex: 1 1 100%; }
  .pair-input-row .pair-toggle,
  .pair-input-row [data-act="paste"] { flex: 1 1 auto; }
  /* Settings + studio chrome: drop section padding so content breathes */
  main, .proj-section, .proj-main { padding-left: 0; padding-right: 0; }
  /* CTA form responsive rules removed 2026-05-19 — form itself dropped
     in Polish Round 1 Slot β. */
  /* Pricing CTA: stack the 3 buttons */
  .pricing-cta { display: block; width: 100%; min-width: 0; }
}

/* iPhone 13/14 standard width (390). Identical clamp, with extra
   tightening for the proj-hero left+right column flex. */
@media (max-width: 390px) {
  .proj-hero-inner { gap: 24px; }
  .pie-big { width: 120px; height: 120px; flex: 0 0 120px; }
  /* Eyebrow + nav status: trim letter-spacing so it fits */
  .eyebrow { letter-spacing: .10em; }
  /* Pair page wrap: collapse the side gutter */
  section .wrap { max-width: 100%; }
}

/* iPhone SE / mini (375) — the narrow target. Last-mile fixes. */
@media (max-width: 375px) {
  /* Footer: stack rows so the timestamp + back-link don't fight */
  .foot-inner { flex-direction: column; align-items: flex-start; gap: 8px; }
  /* Roster card padding: trim so the title doesn't overflow */
  .roster .card { padding: 22px 18px 16px; }
  /* Pair actions: column-stack so both buttons hit 44px each */
  .pair-actions { flex-direction: column; align-items: stretch; }
  .pair-local-actions { flex-direction: column; align-items: stretch; }
  .pair-actions .btn-primary,
  .pair-actions .btn-secondary,
  .pair-local-actions .btn-primary,
  .pair-local-actions .btn-secondary { width: 100%; }
}

/* === Server-URL banner ("Pair this device" on GH Pages) ================== */
/* Rendered by wireServerUrlBanner() in shared_scripts.py when:
     - location.hostname is NOT localhost/127.0.0.1, AND
     - localStorage.ENDENZA_SERVER_URL is empty.
   It sits just below the topnav, warm accent, dismissible. Sticky-ish via
   the document flow — not position:fixed (which clips against the topnav). */
.server-url-banner {
  display: flex; align-items: center; justify-content: center;
  gap: 12px; flex-wrap: wrap;
  padding: 10px 16px;
  font-family: var(--sans); font-size: var(--fs-12);
  color: var(--ink);
  background: color-mix(in srgb, var(--accent) 12%, var(--bg));
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 30%, var(--rule));
  text-align: center;
}
.server-url-banner a {
  color: var(--accent-text, var(--accent)); font-weight: 600; text-decoration: underline;
  text-underline-offset: 2px;
}
.server-url-banner__dismiss {
  background: transparent; border: 0;
  color: var(--ink-3); cursor: pointer;
  font-size: 18px; line-height: 1; padding: 4px 8px;
  min-width: 44px; min-height: 44px;
}
.server-url-banner__dismiss:hover { color: var(--ink); }
@media (max-width: 430px) {
  .server-url-banner { font-size: 13px; padding: 12px 14px; }
}

/* ─── Auth-page shell (.auth-page) ───────────────────────────────────────── */
.auth-page {
  display: grid;
  place-items: center;
  min-height: calc(100vh - var(--topnav-h, 64px));
  padding: clamp(28px, 7vw, 72px) 16px;
  background: var(--bg);
}
.auth-page__card {
  width: min(980px, calc(100vw - 32px));
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(260px, 0.85fr);
  gap: clamp(22px, 4vw, 48px);
  align-items: center;
  padding: clamp(28px, 5vw, 54px);
  border: 1px solid var(--rule);
  border-radius: 30px;
  background: var(--bg-raised);
  box-shadow: var(--shadow-md);
}
.auth-page__eyebrow {
  margin: 0 0 8px;
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-text, var(--accent));
}
.auth-page h1 {
  margin: 10px 0 14px;
  font-family: var(--serif);
  font-size: clamp(44px, 8vw, 72px);
  line-height: 0.95;
  color: var(--ink);
}
.auth-page p {
  color: var(--ink-2);
  max-width: 52ch;
}
.auth-page__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 22px;
}
.auth-page .session-note {
  min-height: 22px;
  margin: 18px 0 0;
  font-size: 14px;
  font-weight: 700;
  color: var(--ink-3);
}
.auth-page .warn {
  color: var(--danger);
}
.auth-page__flow {
  border: 1px solid var(--rule);
  border-radius: 24px;
  background: var(--bg-elevated);
  padding: 18px;
}
.auth-page__flow-label {
  margin: 0 0 12px;
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 900;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.auth-page__steps {
  display: grid;
  gap: 10px;
}
.auth-page__step {
  border: 1px solid var(--rule);
  border-radius: 18px;
  background: var(--bg-raised);
  padding: 14px;
  color: var(--ink-2);
  font-size: 14px;
  line-height: 1.45;
}
.auth-page__step strong {
  display: block;
  color: var(--ink);
  margin-bottom: 3px;
}
@media (max-width: 860px) {
  .auth-page__card {
    grid-template-columns: 1fr;
  }
}
/* Mobile nav drawer (<=879px) - Round1-gamma; seam raised 720->879 on 2026-06-01 */
.nav__toggle {
  display: none;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 0;
  width: 44px;
  height: 44px;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 4px;
  margin-left: auto;
}
.nav__toggle:hover { background: var(--accent-soft); }
.nav__toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.nav__toggle-bar {
  display: block;
  width: 18px;
  height: 2px;
  background: var(--ink);
  border-radius: 2px;
  transition: transform 200ms var(--ease), opacity 200ms var(--ease);
}
.topnav[data-drawer-state="open"] .nav__toggle-bar:nth-child(1) {
  transform: translateY(6px) rotate(45deg);
}
.topnav[data-drawer-state="open"] .nav__toggle-bar:nth-child(2) {
  opacity: 0;
}
.topnav[data-drawer-state="open"] .nav__toggle-bar:nth-child(3) {
  transform: translateY(-6px) rotate(-45deg);
}

/* The mobile bar brand (left side) is desktop-hidden; the desktop nav uses the
   brand inside the centered link cluster (.nav__brand-drawer). The .topnav
   prefix raises specificity above .brand--icon-only's own display rule. */
.topnav .nav__brand-mobile { display: none; }

@media (max-width: 879px) {
  .topnav .wrap {
    flex-wrap: wrap;
    height: auto;
    min-height: 48px;
    align-items: center;
    position: relative;
    padding-top: max(6px, calc(env(safe-area-inset-top, 0px) + 6px));
    padding-bottom: 6px;
  }
  .nav__toggle { display: inline-flex; }
  /* Brand on the left of the mobile bar; hide the in-drawer copy to avoid a
     duplicate logo row when the drawer opens. */
  .topnav .nav__brand-mobile { display: inline-flex; align-items: center; }
  .topnav .nav__brand-drawer { display: none; }
  .topnav li.nav__identity-item,
  .topnav li.nav__auth-action-item {
    position: static;
    right: auto;
    left: auto;
    top: auto;
    transform: none;
    margin-left: 0;
    flex-wrap: wrap;
    padding: 10px 16px;
    border-top: 1px solid var(--rule);
    background: var(--bg-raised, #1c1d22);
  }
  .topnav ul {
    flex-basis: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    max-height: 0;
    overflow: hidden;
    transition: max-height 200ms var(--ease);
    margin: 0;
    padding: 0;
    /* Drop the drawer out of the bar's flex flow so the collapsed bar is just
       the toggle (cleanly centered, short) and the open drawer overlays the
       page below the bar instead of stretching the bar itself. */
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 50;
  }
  .topnav[data-drawer-state="open"] ul {
    max-height: 70vh;
    overflow-y: auto;
    padding-bottom: var(--space-3, 12px);
    /* Opaque drawer surface so labels are readable against any page
       background (was transparent — text bled into hero / canvas). */
    background: var(--bg-raised, #1c1d22);
    border-top: 1px solid var(--rule);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  }
  .topnav li a {
    display: block;
    padding: 14px 16px;
    border-radius: 0;
    border-top: 1px solid var(--rule);
    background: var(--bg-raised, #1c1d22);
    color: var(--ink, #e8eaee);
    font-weight: 500;
  }
  .topnav .nav__auth-chip {
    flex: 1 1 100%;
    max-width: none;
    justify-content: center;
  }
  .topnav li.nav__auth-action-item a {
    flex: 1 1 auto;
    justify-content: center;
    text-align: center;
    border-top: 0;
  }
  .topnav li a:hover,
  .topnav li a:focus-visible {
    background: var(--bg-sunken, #14151a);
    color: var(--accent, #c2711f);
  }
  .topnav li a.active {
    background: var(--accent-soft, rgba(194, 113, 31, 0.12));
    color: var(--accent, #c2711f);
  }

}

/* Pricing page table - Round1-gamma */
.pricing-table {
  width: 100%;
  border-collapse: collapse;
  margin: var(--space-6, 32px) 0;
  font-family: var(--sans);
}
.pricing-table__caption {
  caption-side: bottom;
  font-size: var(--fs-12);
  color: var(--ink-3);
  text-align: left;
  padding: var(--space-3, 12px) 0;
}
.pricing-table thead th {
  padding: var(--space-4, 16px);
  vertical-align: top;
  text-align: left;
  border-bottom: 1px solid var(--rule);
}
.pricing-tier {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-weight: 500;
}
.pricing-tier__name {
  font-family: var(--serif);
  font-size: var(--fs-20, 20px);
  color: var(--ink);
  letter-spacing: -0.01em;
}
.pricing-tier__price {
  font-family: var(--mono);
  font-size: var(--fs-18, 18px);
  color: var(--ink);
}
.pricing-tier__per {
  font-size: var(--fs-12, 12px);
  color: var(--ink-3);
  margin-left: 2px;
}
.pricing-tier__sub {
  font-size: var(--fs-12, 12px);
  color: var(--ink-3);
  font-weight: 400;
}
.pricing-tier__badge {
  align-self: flex-start;
  font-size: 11px;
  font-weight: 600;
  color: var(--accent-text, var(--accent));
  background: var(--accent-soft);
  padding: 2px 8px;
  border-radius: 999px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  transform: rotate(-2deg);
  display: inline-block;
}
.pricing-tier--featured {
  background: color-mix(in srgb, var(--accent-soft) 50%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--rule));
  border-radius: 8px;
  position: relative;
}
.pricing-table tbody th,
.pricing-table tbody td {
  padding: var(--space-3, 12px) var(--space-4, 16px);
  border-bottom: 1px solid var(--rule);
  font-size: var(--fs-14);
  color: var(--ink-2);
}
.pricing-table tbody th {
  text-align: left;
  color: var(--ink);
  font-weight: 500;
}
.pricing-table tfoot td {
  padding: var(--space-4, 16px);
}
.pricing-cta {
  display: inline-block;
  min-width: 140px;
  text-align: center;
}
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.section-sub { color: var(--ink-2); font-size: var(--fs-14); margin: 0 0 var(--space-4, 16px); }


/* Pricing BYOK callout — full-width strip below the tier table */
.pricing-byok {
  display: flex;
  align-items: center;
  gap: var(--space-3, 12px);
  margin-top: var(--space-5, 24px);
  padding: var(--space-3, 12px) var(--space-4, 16px);
  background: color-mix(in srgb, var(--accent-warm, var(--accent-soft)) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 15%, var(--rule));
  border-radius: 8px;
  font-size: var(--fs-14);
  color: var(--ink-2);
}
.pricing-byok__icon { font-size: 1.2em; flex-shrink: 0; }
.pricing-byok__text { flex: 1; }
.pricing-byok__link {
  color: var(--accent-text, var(--accent));
  text-decoration: none;
  white-space: nowrap;
  font-weight: 500;
}
.pricing-byok__link:hover { text-decoration: underline; }

/* Section break inside the matrix — Workspace / Workforce / Polish / Selling / Support */
.pricing-section th {
  font-family: var(--sans);
  font-size: var(--fs-11, 11px);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--accent-text, var(--accent));
  padding: var(--space-5, 24px) var(--space-4, 16px) var(--space-2, 8px);
  border-bottom: 1px solid var(--rule);
  background: color-mix(in srgb, var(--accent-soft) 25%, transparent);
}

/* Alt-price line under Pro headline — "or $120/yr" */
.pricing-tier__price-alt {
  font-family: var(--mono);
  font-size: var(--fs-12, 12px);
  color: var(--ink-3);
}

/* Secondary CTA under the primary "Go Pro" button — annual discount link */
.pricing-cta-alt {
  display: block;
  margin-top: var(--space-2, 8px);
  font-size: var(--fs-12, 12px);
  color: var(--accent-text, var(--accent));
  text-decoration: none;
  text-align: center;
}
.pricing-cta-alt:hover,
.pricing-cta-alt:focus-visible {
  text-decoration: underline;
}

/* FAQ definition list below the matrix */
.pricing-faq__list {
  display: grid;
  gap: var(--space-3, 12px);
  margin: var(--space-4, 16px) 0 var(--space-5, 24px);
  font-family: var(--sans);
}
.pricing-faq__list dt {
  font-weight: 600;
  color: var(--ink);
  font-size: var(--fs-14);
  margin-bottom: 2px;
}
.pricing-faq__list dd {
  margin: 0;
  font-size: var(--fs-14);
  color: var(--ink-2);
  line-height: var(--lh-snug, 1.45);
}

@media (max-width: 720px) {
  .pricing-table thead { display: none; }
  .pricing-table,
  .pricing-table tbody,
  .pricing-table tfoot,
  .pricing-table tr { display: block; }
  .pricing-table tbody td,
  .pricing-table tfoot td {
    display: flex;
    justify-content: space-between;
    border-bottom: 1px solid var(--rule);
    padding: var(--space-2, 8px) var(--space-3, 12px);
  }
  .pricing-table tbody tr { margin-bottom: var(--space-4, 16px); border-bottom: 2px solid var(--rule); }
  /* Section headers on mobile become full-width strip labels */
  .pricing-section th {
    display: block;
    text-align: left;
    padding: var(--space-3, 12px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .topnav ul,
  .nav__toggle-bar { transition: none; }
}

/* Pricing success page — post-checkout celebration card */
.pricing-success { display: flex; align-items: flex-start; justify-content: center; padding: var(--space-10, 80px) var(--space-4, 16px); min-height: 60vh; }
.pricing-success__wrap { width: 100%; max-width: 540px; }
.pricing-success__card {
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: 12px;
  padding: var(--space-8, 48px) var(--space-6, 32px);
  text-align: center;
}
.pricing-success__check {
  font-size: 3rem;
  color: var(--accent-text, var(--accent));
  margin-bottom: var(--space-4, 16px);
  line-height: 1;
}
.pricing-success__headline {
  font-family: var(--serif, Georgia, serif);
  font-size: var(--fs-40, 40px);
  font-weight: 700;
  color: var(--ink);
  letter-spacing: -0.02em;
  margin: 0 0 var(--space-3, 12px);
}
.pricing-success__sub {
  font-size: var(--fs-16);
  color: var(--ink-2);
  margin: 0 0 var(--space-6, 32px);
}
.pricing-success__unlocked {
  list-style: none;
  margin: 0 0 var(--space-6, 32px);
  padding: 0;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: var(--space-2, 8px);
  font-family: var(--mono);
  font-size: var(--fs-13, 13px);
  color: var(--ink-2);
}
.pricing-success__unlocked li::before { content: "✓ "; color: var(--accent-text, var(--accent)); }
.pricing-success__cta { display: inline-block; margin-bottom: var(--space-5, 24px); }
.pricing-success__warning {
  font-size: var(--fs-13, 13px);
  color: var(--ink-3);
  background: color-mix(in srgb, var(--accent-warm, var(--accent-soft)) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 20%, var(--rule));
  border-radius: 6px;
  padding: var(--space-3, 12px);
  margin-top: var(--space-4, 16px);
  text-align: left;
}
@media (prefers-reduced-motion: reduce) {
  .pricing-success__check { animation: none; }
}

/* ── Account action item ────────────────────────────────────────────── */
/* The avatar-trigger + dropdown menu (Profile / Settings / Log out) was
   removed 2026-05-25 evening — Settings is already in the top nav and
   the inline Sign in ↔ Log out link is enough. Settings + Profile were
   accessible via the topnav anyway, so the dropdown was redundant +
   the empty-avatar circle looked broken when the GitHub avatar URL
   was missing. */
.nav__auth-action-item {
  position: relative;
}

/* === a11y baseline (WCAG 2.4.1 Level A) === */

/* Skip-link — first body child on every page. Hidden off-screen until
   focused via Tab. Keyboard-only users press Tab on page load to reveal it,
   Enter jumps to <main id="main">. */
.skip-link {
  position: absolute;
  top: 0;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
  z-index: 999;

  background: var(--accent, #2f6fab);
  color: var(--accent-ink, #fff);
  font-family: var(--sans, system-ui, sans-serif);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.01em;
  padding: 12px 18px;
  border-radius: 0 0 8px 0;
  text-decoration: none;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
}
.skip-link:focus,
.skip-link:focus-visible {
  position: static;
  width: auto;
  height: auto;
  overflow: visible;
  outline: 3px solid var(--ink, #1a1813);
  outline-offset: 2px;
}

/* When skip-link target receives focus, no visual shift — keep it natural. */
main:focus,
main:focus-visible {
  outline: none;
}

/* === Workshop drawer focus-trap visual marker ===
   When the trap is active (studio_body.py adds .has-focus-trap during Tab
   cycling), give the drawer a subtle accent border so sighted keyboard
   users see the trap boundary. */
.workshop-drawer.has-focus-trap {
  box-shadow: inset 0 0 0 1px var(--accent-soft, rgba(184, 117, 60, 0.18));
}

/* === :focus-visible baseline (WCAG 2.4.7 + 2.4.11) ===
   Lift focus-ring coverage across interactive surfaces. Per audit Slot C,
   only ~8% of interactive elements had explicit focus styling before this
   block. Keyboard users now get a consistent 2px accent ring on every
   tab-stop. Mouse users keep clean visuals - :focus-visible is the spec'd
   selector that suppresses focus rings for pointer activation. */
.topnav li a:focus-visible,
.tile:focus-visible,
.card:focus-visible,
.btn:focus-visible,
.btn--primary:focus-visible,
.btn--secondary:focus-visible,
.btn--ghost:focus-visible,
.btn--destructive:focus-visible,
.pill:focus-visible,
button:focus-visible,
[role="tab"]:focus-visible,
[role="radio"]:focus-visible {
  outline: 2px solid var(--accent-focus, var(--accent));
  outline-offset: 2px;
}

/* === Inline content-link focus ring (WCAG 2.4.7) ===
   The baseline above rings buttons/cards/tiles/pills, but plain inline
   <a> links inside body copy (lead paragraphs, story text, list items)
   had no explicit focus indicator. Give every inline link inside a page
   content region the same accent ring keyboard users get everywhere
   else. Scoped to `main` + `.wrap` so it reaches content links on every
   page (the bodies wrap copy in `.wrap`, and skip-link/main land under
   `main`). The :not() chain excludes the component classes the baseline
   already rings, so an <a class="btn"> / .card / .tile / .pill keeps its
   own (identical) ring with no selector overlap. Low specificity — a
   per-component rule still wins. */
main a:focus-visible,
.wrap a:focus-visible {
  outline: 2px solid var(--accent-focus, var(--accent));
  outline-offset: 2px;
}
main a.btn:focus-visible,
main a.card:focus-visible,
main a.tile:focus-visible,
main a.pill:focus-visible,
.wrap a.btn:focus-visible,
.wrap a.card:focus-visible,
.wrap a.tile:focus-visible,
.wrap a.pill:focus-visible {
  /* Defer to the component baseline ring above; identical values, no
     double-ring, but keeps the cascade unambiguous. */
  outline-offset: 2px;
}

/* === Reduced-motion safety net ===
   Per WCAG 2.3.3 + Apple HIG: respect users who opt out of motion. We
   keep the structural transitions (display/transform jumps) but kill
   long animations and infinite loops. Per-component CSS may further
   opt-in to their own reduced-motion variants. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* Pause infinite shimmer / pulse cleanly. (Spinners removed per
     canon §4.5/§6.5/§12 — designed loading states now use .loading-dots
     and skeleton shimmer, both listed here.) */
  .skeleton-card,
  .endenza-skeleton,
  .canvas-view__skeleton,
  .loading-dots {
    animation: none !important;
  }
}
/* === shared state primitives === */

/* Skeleton placeholder block — bronze shimmer over ink. Used by endenza grid,
   canvas detail, and first-run flow loading states. */
.skeleton-card,
.endenza-skeleton,
.canvas-view__skeleton {
  background: linear-gradient(
    90deg,
    var(--ink-2, #2a2622) 0%,
    var(--ink-1, #3a342c) 50%,
    var(--ink-2, #2a2622) 100%
  );
  background-size: 200% 100%;
  animation: endenza-shimmer 1.5s ease-in-out infinite;
  border-radius: var(--radius, 12px);
}
@keyframes endenza-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .skeleton-card,
  .endenza-skeleton,
  .canvas-view__skeleton {
    animation: none;
    background: var(--ink-2, #2a2622);
  }
}

/* Loading dots — three dots staggered. Reduced-motion: render as "..." text. */
.loading-dots {
  display: inline-flex;
  gap: 4px;
  align-items: center;
  vertical-align: middle;
}
.loading-dots span {
  width: 6px;
  height: 6px;
  background: currentColor;
  border-radius: 50%;
  opacity: 0.4;
  animation: endenza-dot-pulse 1.2s ease-in-out infinite;
}
.loading-dots span:nth-child(2) { animation-delay: 0.2s; }
.loading-dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes endenza-dot-pulse {
  0%, 80%, 100% { opacity: 0.4; transform: scale(0.85); }
  40%           { opacity: 1.0; transform: scale(1.0); }
}
@media (prefers-reduced-motion: reduce) {
  .loading-dots {
    display: inline-block;
  }
  .loading-dots span { display: none; }
  .loading-dots::after {
    content: "...";
    font-family: var(--mono, "JetBrains Mono", monospace);
    letter-spacing: 0.04em;
  }
}

/* Generic state-card pattern (error / empty fallback). */
.state-card {
  max-width: 520px;
  margin: 24px auto;
  padding: 36px 28px;
  text-align: center;
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.state-card__icon {
  display: block;
  margin: 0 auto 14px;
  width: 32px;
  height: 32px;
  color: var(--accent-text, var(--accent));
  opacity: 0.7;
}
.state-card__title {
  font-family: var(--serif);
  font-size: clamp(20px, 2.5vw, 24px);
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0;
}
.state-card__sub {
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink-2);
  margin: 10px auto 18px;
  max-width: 440px;
}
.state-card__actions {
  display: inline-flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
}
.state-card__actions .btn-primary,
.state-card__actions .btn-secondary {
  text-decoration: none;
  padding: 10px 20px;
  border-radius: 8px;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
}

/* === endenza showcase: skeleton grid (loading) + error state === */

.endenza-showcase__skeleton-grid {
  display: grid;
  gap: 24px;
  grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 900px) {
  .endenza-showcase__skeleton-grid { grid-template-columns: repeat(2, 1fr); gap: 18px; }
}
@media (max-width: 580px) {
  .endenza-showcase__skeleton-grid { grid-template-columns: 1fr; gap: 16px; }
}
.endenza-showcase__skeleton-grid[hidden] { display: none; }
.endenza-skeleton-card {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.endenza-skeleton-card .endenza-skeleton {
  width: 100%;
  height: 60%;
}
.endenza-skeleton-card .endenza-skeleton--line {
  height: 12px;
  width: 80%;
}
.endenza-skeleton-card .endenza-skeleton--short {
  height: 10px;
  width: 50%;
}

.endenza-showcase__error {
  max-width: 540px;
  margin: 16px auto 0;
  padding: 36px 24px;
  text-align: center;
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.endenza-showcase__error[hidden] { display: none; }
.endenza-showcase__error h3 {
  font-family: var(--serif);
  font-size: clamp(18px, 2.5vw, 22px);
  font-weight: 500;
  margin: 0;
  color: var(--ink);
}
.endenza-showcase__error p {
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink-2);
  margin: 8px auto 18px;
  max-width: 420px;
}
.endenza-showcase__error .btn-secondary {
  display: inline-block;
  padding: 9px 18px;
  border-radius: 999px;
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--ink);
  font-family: var(--sans);
  font-size: 13px;
  cursor: pointer;
  text-decoration: none;
}
.endenza-showcase__error .btn-secondary:hover {
  border-color: var(--accent);
  color: var(--accent-text, var(--accent));
}
.endenza-showcase__error-hint {
  font-family: var(--sans);
  font-size: 12px;
  color: var(--ink-3, var(--ink-2));
  margin-top: 14px !important;
  opacity: 0.8;
}
.endenza-showcase__error-hint code {
  font-family: var(--mono, "JetBrains Mono", monospace);
  background: var(--bg);
  padding: 1px 6px;
  border-radius: 4px;
  border: 1px solid var(--rule);
}

/* === canvas detail loading + error === */

.canvas-view__loading {
  padding: 24px 28px;
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  margin: 24px 0 28px;
}
.canvas-view__loading[hidden] { display: none; }
.canvas-view__skeleton-title {
  height: 36px;
  width: 60%;
  margin-bottom: 12px;
}
.canvas-view__skeleton-byline {
  height: 14px;
  width: 35%;
  margin-bottom: 18px;
}
.canvas-view__skeleton-stage {
  width: 100%;
  aspect-ratio: 16 / 10;
}

.canvas-view__error {
  max-width: 560px;
  margin: 64px auto;
  padding: 48px 32px;
  text-align: center;
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.canvas-view__error[hidden] { display: none; }
.canvas-view__error-icon {
  display: inline-block;
  font-family: var(--serif);
  font-size: 48px;
  color: var(--accent-text, var(--accent));
  opacity: 0.55;
  margin-bottom: 12px;
  line-height: 1;
}
.canvas-view__error-title {
  font-family: var(--serif);
  font-size: clamp(22px, 3vw, 28px);
  font-weight: 500;
  color: var(--ink);
  margin: 0;
}
.canvas-view__error-sub {
  font-family: var(--serif);
  font-size: 15px;
  line-height: 1.55;
  color: var(--ink-2);
  margin: 10px auto 20px;
  max-width: 440px;
}
.canvas-view__error-cta {
  display: inline-block;
  padding: 10px 22px;
  text-decoration: none;
  background: var(--accent);
  color: #fff;
  border-radius: 8px;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
}
.canvas-view__error-cta:hover { background: var(--accent-2, var(--accent)); }
.canvas-view__error-id {
  display: block;
  margin-top: 14px;
  font-family: var(--mono, "JetBrains Mono", monospace);
  font-size: 12px;
  color: var(--ink-3, var(--ink-2));
  opacity: 0.7;
}

/* === first-run modal: loading + error states === */

.first-run-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 32px 22px;
  gap: 14px;
}
.first-run-loading[hidden] { display: none; }
.first-run-loading__title {
  font-family: var(--serif);
  font-size: 20px;
  font-weight: 500;
  margin: 0;
  color: var(--ink);
}
.first-run-loading__sub {
  font-family: var(--serif);
  font-size: 14px;
  color: var(--ink-2);
  margin: 0;
  max-width: 360px;
}
.first-run-loading__steps {
  width: min(100%, 360px);
  margin: 2px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  font-family: var(--sans);
  font-size: 11px;
  line-height: 1.25;
  color: var(--ink-3, var(--ink-2));
}
.first-run-loading__steps li {
  position: relative;
  padding-top: 16px;
  text-align: center;
}
.first-run-loading__steps li::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: 9px;
  height: 9px;
  transform: translateX(-50%);
  border-radius: 999px;
  background: var(--rule);
  box-shadow: 0 0 0 3px var(--bg-raised);
}
.first-run-loading[data-state="queued"] [data-progress-state="queued"],
.first-run-loading[data-state="reading"] [data-progress-state="queued"],
.first-run-loading[data-state="reading"] [data-progress-state="reading"],
.first-run-loading[data-state="building"] [data-progress-state="queued"],
.first-run-loading[data-state="building"] [data-progress-state="reading"],
.first-run-loading[data-state="building"] [data-progress-state="building"],
.first-run-loading[data-state="revealing"] [data-progress-state="queued"],
.first-run-loading[data-state="revealing"] [data-progress-state="reading"],
.first-run-loading[data-state="revealing"] [data-progress-state="building"],
.first-run-loading[data-state="revealing"] [data-progress-state="revealing"] {
  color: var(--ink);
  font-weight: 600;
}
.first-run-loading[data-state="queued"] [data-progress-state="queued"]::before,
.first-run-loading[data-state="reading"] [data-progress-state="reading"]::before,
.first-run-loading[data-state="building"] [data-progress-state="building"]::before,
.first-run-loading[data-state="revealing"] [data-progress-state="revealing"]::before {
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft, rgba(30, 127, 196, 0.18));
}
.first-run-loading[data-state="reading"] [data-progress-state="queued"]::before,
.first-run-loading[data-state="building"] [data-progress-state="queued"]::before,
.first-run-loading[data-state="building"] [data-progress-state="reading"]::before,
.first-run-loading[data-state="revealing"] [data-progress-state="queued"]::before,
.first-run-loading[data-state="revealing"] [data-progress-state="reading"]::before,
.first-run-loading[data-state="revealing"] [data-progress-state="building"]::before {
  background: var(--accent);
}
.first-run-loading__eta {
  font-family: var(--mono, "JetBrains Mono", monospace);
  font-size: 12px;
  color: var(--ink-3, var(--ink-2));
  letter-spacing: 0.02em;
}

.first-run-fail {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 28px 22px;
  gap: 12px;
}
.first-run-fail[hidden] { display: none; }
.first-run-fail__title {
  font-family: var(--serif);
  font-size: 20px;
  font-weight: 500;
  margin: 0;
  color: var(--ink);
}
.first-run-fail__sub {
  font-family: var(--serif);
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-2);
  margin: 0 auto;
  max-width: 380px;
}
.first-run-fail__actions {
  display: flex;
  gap: 10px;
  margin-top: 8px;
  flex-wrap: wrap;
  justify-content: center;
}
.first-run-fail__actions button {
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  padding: 10px 16px;
  border-radius: 8px;
  cursor: pointer;
}
.first-run-fail__retry {
  background: var(--accent);
  color: #fff;
  border: 0;
}
.first-run-fail__retry:hover { background: var(--accent-2, var(--accent)); }
.first-run-fail__skip {
  background: transparent;
  color: var(--ink-2);
  border: 1px solid var(--rule);
}
.first-run-fail__skip:hover { border-color: var(--accent); color: var(--accent-text, var(--accent)); }

/* === studio publish panel: empty + per-row loading === */

.studio-publish__empty-banner {
  margin: 16px 0 18px;
  padding: 18px 20px;
  background: var(--bg);
  border: 1px dashed var(--rule);
  border-radius: var(--radius);
  text-align: center;
  font-family: var(--serif);
  color: var(--ink-2);
}
.studio-publish__empty-banner[hidden] { display: none; }
.studio-publish__empty-banner strong {
  display: block;
  font-weight: 500;
  color: var(--ink);
  margin-bottom: 4px;
}

/* Designed inline busy state (canon §4.5/§6.5/§12: no spinners). Pulsing
   loading-dots sit next to the row label while a publish slot mutates.
   .loading-dots carries its own reduced-motion "..." text fallback. */
.studio-publish__row-spinner {
  display: inline-flex;
  margin-left: 8px;
  vertical-align: middle;
  color: var(--accent-text, var(--accent));
}
.studio-publish__slot.is-busy .studio-publish__card,
.studio-publish__draft.is-busy .studio-publish__card {
  opacity: 0.55;
  pointer-events: none;
}

/* === pair body inline error === */

.pair-field-error {
  font-family: var(--sans);
  font-size: 13px;
  color: var(--danger);
  margin: 6px 0 0;
  min-height: 1.1em;
}
.pair-field-error:empty { display: none; }
.pair-input[aria-invalid="true"] {
  border-color: var(--danger);
}
/* Designed pairing busy state (canon §4.5/§6.5/§12: no spinners). The
   button label is joined by a "Pairing…" text-state with pulsing
   loading-dots — says WHAT is happening, not that something spins.
   .loading-dots brings its own reduced-motion "..." fallback. */
.pair-spinner {
  display: none;
  align-items: baseline;
  gap: 2px;
  margin-left: 8px;
  vertical-align: middle;
  color: var(--accent-ink);
}
.is-loading > .pair-spinner { display: inline-flex; }
.is-loading { cursor: progress; opacity: 0.85; }

/* === settings -> appearance: theme picker, mode toggle, save row === */

.settings-appearance { padding-top: 8px; padding-bottom: 8px; }

.settings-appearance__notice {
  margin: 12px 0 16px;
  padding: 10px 14px;
  border-radius: var(--radius-sm, 8px);
  font-family: var(--sans);
  font-size: 13px;
  line-height: 1.45;
}
.settings-appearance__notice[hidden] { display: none; }
.settings-appearance__notice--ok {
  background: rgba(34, 139, 95, 0.10);
  border: 1px solid rgba(34, 139, 95, 0.35);
  color: var(--ink);
}
.settings-appearance__notice--warn {
  background: rgba(202, 138, 4, 0.10);
  border: 1px solid rgba(202, 138, 4, 0.35);
  color: var(--ink);
}
.settings-appearance__notice--err {
  background: rgba(179, 38, 30, 0.10);
  border: 1px solid rgba(179, 38, 30, 0.35);
  color: var(--ink);
}

.settings-theme-picker {
  border: 0;
  padding: 0;
  margin: 0 0 28px;
}
.settings-theme-picker__legend {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding: 0;
  margin: 0 0 10px;
}
.settings-theme-picker__current {
  font-family: var(--serif);
  font-size: 13px;
  color: var(--ink-2);
  margin: 10px 0 4px;
}
.settings-theme-picker__current strong {
  font-weight: 500;
  color: var(--ink);
}
.settings-theme-picker__help {
  font-family: var(--serif);
  font-size: 12px;
  color: var(--ink-3, var(--ink-2));
  margin: 4px 0 0;
}

.settings-theme-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(72px, 100%), 1fr));
  gap: 10px;
  max-width: var(--w-prose);
}
@media (max-width: 540px) {
  .settings-theme-grid {
    grid-template-columns: repeat(auto-fill, minmax(min(64px, 100%), 1fr));
    gap: 8px;
  }
}

.settings-theme-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 4px 4px 6px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm, 8px);
  cursor: pointer;
  font-family: var(--sans);
  font-size: 11px;
  color: var(--ink-2);
  transition: transform 140ms var(--ease, ease-out),
              background-color 140ms var(--ease, ease-out);
  -webkit-tap-highlight-color: transparent;
}
.settings-theme-tile:hover { background: var(--bg-raised); transform: translateY(-1px); }
.settings-theme-tile:focus-visible {
  outline: 2px solid var(--accent, var(--accent-mid, currentColor));
  outline-offset: 2px;
}
.settings-theme-tile__swatch {
  width: 56px;
  height: 56px;
  border-radius: 8px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06),
              0 1px 2px rgba(0, 0, 0, 0.25);
  transition: box-shadow 140ms var(--ease, ease-out);
}
.settings-theme-tile__name {
  line-height: 1.1;
  letter-spacing: 0.01em;
  color: var(--ink-2);
  max-width: 64px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.settings-theme-tile.is-active {
  background: var(--bg-raised);
}
.settings-theme-tile.is-active .settings-theme-tile__swatch {
  box-shadow: 0 0 0 2px var(--accent, var(--accent-mid, currentColor)),
              0 1px 4px rgba(0, 0, 0, 0.30);
}
.settings-theme-tile.is-active .settings-theme-tile__name {
  color: var(--ink);
  font-weight: 500;
}

.settings-theme-tile--none .settings-theme-tile__swatch { display: none; }
.settings-theme-tile--none { justify-content: center; }
.settings-theme-tile__none-glyph {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 8px;
  background: var(--bg-raised);
  border: 1px dashed var(--rule);
  color: var(--ink-3, var(--ink-2));
  font-family: var(--serif);
  font-size: 22px;
  line-height: 1;
}
.settings-theme-tile--none.is-active .settings-theme-tile__none-glyph {
  border-style: solid;
  box-shadow: 0 0 0 2px var(--accent, var(--accent-mid, currentColor));
  color: var(--ink);
}

@media (prefers-reduced-motion: reduce) {
  .settings-theme-tile,
  .settings-theme-tile__swatch { transition: none; }
  .settings-theme-tile:hover { transform: none; }
}

/* mode toggle (light / dark / auto) */

.settings-mode-toggle {
  border: 0;
  padding: 0;
  margin: 0 0 24px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px 14px;
  align-items: center;
}
.settings-mode-toggle__legend {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding: 0;
  margin: 0 12px 8px 0;
  width: 100%;
}
.settings-mode-toggle__option {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid var(--rule);
  background: var(--bg);
  cursor: pointer;
  user-select: none;
  font-family: var(--sans);
  font-size: 14px;
  color: var(--ink);
  transition: background-color 140ms var(--ease, ease-out),
              border-color 140ms var(--ease, ease-out);
}
.settings-mode-toggle__option:hover {
  background: var(--bg-raised);
  border-color: var(--accent, var(--rule));
}
.settings-mode-toggle__option:has(input:checked) {
  background: var(--bg-raised);
  border-color: var(--accent, var(--rule));
  box-shadow: inset 0 0 0 1px var(--accent, transparent);
}
.settings-mode-toggle__option input[type="radio"] {
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--accent, currentColor);
}
.settings-mode-toggle__hint {
  color: var(--ink-3, var(--ink-2));
  font-size: 12px;
}

@media (prefers-reduced-motion: reduce) {
  .settings-mode-toggle__option { transition: none; }
}

/* save / revert action row */

.settings-appearance__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--rule);
}
.settings-appearance__actions .btn-primary[disabled],
.settings-appearance__actions .btn-secondary[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* --- Chat drawer * magic-moment v2 (Slot 2A) -------------------------------
   Additive layer - extends the base drawer in styles/studio.py.
   Token meter + quota UI + new-conversation + BYOK onboarding.
*/

/* New-conversation button in the drawer header */
.studio-claude-drawer__new-conversation {
  appearance: none; cursor: pointer; border: 0; background: transparent;
  font-family: var(--mono); font-size: 10px;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-3);
  padding: 4px 10px;
  border-radius: 999px;
  transition: background 180ms var(--ease), color 180ms var(--ease);
}
.studio-claude-drawer__new-conversation:hover {
  background: var(--bg-sunken, var(--rule-2));
  color: var(--ink);
}
.studio-claude-drawer__new-conversation:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Compact toolbar above the input that holds the new-conversation control
   alongside the existing minimize button - keeps the header line clean
   even when many controls are needed. */
.studio-claude-drawer__toolbar {
  display: flex; align-items: center; gap: 6px;
}

/* --- Live token-cost meter (footer) --------------------------------------- */
.studio-claude-drawer__token-meter {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px;
  padding: 8px 14px;
  border-top: 1px solid var(--rule);
  background: var(--bg-sunken, var(--rule-2));
  font-family: var(--mono); font-size: 10.5px; line-height: 1.4;
  color: var(--ink-3);
  flex-shrink: 0;
  letter-spacing: 0.02em;
  transition: background 180ms var(--ease), color 180ms var(--ease);
}
.studio-claude-drawer.is-minimized .studio-claude-drawer__token-meter { display: none; }

.studio-claude-drawer__token-meter-tokens {
  white-space: nowrap;
}
.studio-claude-drawer__token-meter-cost {
  white-space: nowrap;
  text-align: right;
  color: var(--ink-2);
}
/* "your key" vs "platform credit" attribution badge - green/blue tints
   so the user knows which billing surface is responsible. */
.studio-claude-drawer__token-meter-source {
  display: inline-block;
  font-family: var(--mono); font-size: 9.5px;
  letter-spacing: 0.06em; text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 999px;
  margin-left: 6px;
  vertical-align: 1px;
}
.studio-claude-drawer__token-meter-source[data-source="byok"] {
  background: color-mix(in srgb, var(--success) 14%, transparent);
  color: var(--success);
}
.studio-claude-drawer__token-meter-source[data-source="platform"] {
  background: var(--accent-soft);
  color: var(--accent-text, var(--accent));
}
.studio-claude-drawer__token-meter-source[data-source="unknown"] {
  background: transparent;
  color: var(--ink-4);
}

/* Approaching-limit warning state - lights the meter amber. Pre-quota
   nudge to encourage BYOK linking before the user hits the wall. */
.studio-claude-drawer__token-meter[data-state="warning"] {
  background: color-mix(in srgb, var(--warning) 8%, var(--bg-sunken, var(--rule-2)));
  color: var(--warning);
}
.studio-claude-drawer__token-meter[data-state="warning"]
  .studio-claude-drawer__token-meter-cost {
  color: var(--warning);
}

/* --- Quota-exceeded inline banner ----------------------------------------- */
.studio-claude-drawer__quota-exceeded {
  margin: 0 14px 10px;
  padding: 12px 14px;
  background: color-mix(in srgb, var(--danger) 8%, var(--bg-raised));
  border: 1px solid color-mix(in srgb, var(--danger) 35%, var(--rule));
  border-radius: 10px;
  font-family: var(--sans); font-size: 12.5px; line-height: 1.45;
  color: var(--ink-2);
  display: none;
}
.studio-claude-drawer__quota-exceeded[data-state="visible"] { display: block; }

.studio-claude-drawer__quota-title {
  font-weight: 600;
  color: var(--ink);
  margin: 0 0 4px;
  font-size: 13px;
}
.studio-claude-drawer__quota-body {
  margin: 0 0 10px;
  color: var(--ink-2);
}
.studio-claude-drawer__quota-cta {
  display: inline-block;
  appearance: none; cursor: pointer;
  font-family: var(--sans); font-size: 12px; font-weight: 600;
  text-decoration: none;
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--accent); color: #fff;
  border: 1px solid var(--accent);
  transition: background 180ms var(--ease), border-color 180ms var(--ease);
}
.studio-claude-drawer__quota-cta:hover {
  background: var(--accent-2); border-color: var(--accent-2);
  color: #fff;
}
.studio-claude-drawer__quota-cta--ghost {
  background: transparent;
  color: var(--accent-text, var(--accent));
  border-color: color-mix(in srgb, var(--accent) 40%, var(--rule));
  margin-left: 6px;
}
.studio-claude-drawer__quota-cta--ghost:hover {
  background: var(--accent-soft);
  color: var(--accent-text, var(--accent));
}

/* Multi-suggestion summary - when Claude generates several components in
   one turn, show a compact "Generated N components" header above the
   per-suggestion blocks. Visual continuity with the existing card. */
.studio-claude-suggestion-summary {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px;
  padding: 6px 10px;
  margin: 4px 0 6px;
  background: color-mix(in srgb, var(--accent-soft) 30%, var(--bg));
  border: 1px dashed color-mix(in srgb, var(--accent) 25%, var(--rule));
  border-radius: 8px;
  font-family: var(--mono); font-size: 11px;
  color: var(--ink-2);
}
.studio-claude-suggestion-summary__count {
  color: var(--accent-text, var(--accent));
  font-weight: 600;
}

/* --- BYOK onboarding overlay (one-time) -----------------------------------
   Stub the drawer-side overlay so Slot 2C can wire the full overlay later.
   This style is for the placeholder card the drawer shows when triggered;
   2C may replace it with a full-screen overlay. */
.studio-claude-byok-onboarding {
  margin: 0 14px 10px;
  padding: 14px;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 10%, var(--bg-raised)) 0%,
    var(--bg-raised) 100%);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--rule));
  border-radius: 10px;
  font-family: var(--sans); font-size: 12.5px; line-height: 1.5;
  color: var(--ink-2);
  display: none;
}
.studio-claude-byok-onboarding[data-state="visible"] { display: block; }

.studio-claude-byok-onboarding__title {
  font-family: var(--serif);
  font-size: 14px; font-weight: 600;
  color: var(--ink);
  margin: 0 0 6px;
  letter-spacing: -0.005em;
}
.studio-claude-byok-onboarding__body {
  margin: 0 0 10px;
  color: var(--ink-2);
}
.studio-claude-byok-onboarding__row {
  display: flex; gap: 8px; align-items: center;
}
.studio-claude-byok-onboarding__cta {
  appearance: none; cursor: pointer;
  font-family: var(--sans); font-size: 12px; font-weight: 600;
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--accent); color: #fff;
  border: 1px solid var(--accent);
  transition: background 180ms var(--ease);
}
.studio-claude-byok-onboarding__cta:hover { background: var(--accent-2); }
.studio-claude-byok-onboarding__dismiss {
  appearance: none; cursor: pointer;
  background: transparent; border: 0;
  font-family: var(--mono); font-size: 11px;
  color: var(--ink-3);
  padding: 6px 10px;
  border-radius: 999px;
}
.studio-claude-byok-onboarding__dismiss:hover { color: var(--ink); }

/* --- Reduced-motion fallback ----------------------------------------------
   The base drawer in studio.py already honors prefers-reduced-motion for
   its slide animation. These additive surfaces also need to skip transitions
   so screen reader / vestibular-disorder users get instant state changes. */
@media (prefers-reduced-motion: reduce) {
  .studio-claude-drawer__token-meter,
  .studio-claude-drawer__new-conversation,
  .studio-claude-drawer__quota-cta,
  .studio-claude-byok-onboarding__cta {
    transition: none;
  }
}

/* ─── Slot β unified modal chrome (2026-04-29) ─────────────────────
   Single source of truth for every modal panel + scrim in the PWA.
   Per-modal modules layer their own classes on top for sizing /
   layout / behavior; chrome stays here. */

.modal__scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 200;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-base, 180ms) var(--ease, ease-out);
}
.modal__scrim[data-open="true"],
.modal__scrim.is-open {
  opacity: 1;
  pointer-events: auto;
}

.modal__panel {
  position: fixed;
  z-index: 220;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: var(--r-lg, 12px);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.4);
  max-width: var(--w-prose, 760px);
  width: calc(100% - var(--space-6, 32px));
  max-height: 90vh;
  overflow: auto;
}

/* Sized variants — per-modal opt-in. */
.modal__panel--narrow { max-width: 480px; }
.modal__panel--wide   { max-width: var(--w-app, 1180px); }

.modal__header {
  padding: var(--space-5, 24px) var(--space-6, 32px);
  border-bottom: 1px solid var(--rule);
}
.modal__header h2,
.modal__header h3 {
  margin: 0;
}

.modal__body {
  padding: var(--space-5, 24px) var(--space-6, 32px);
}

.modal__footer {
  padding: var(--space-4, 16px) var(--space-6, 32px);
  border-top: 1px solid var(--rule);
  display: flex;
  gap: var(--space-3, 12px);
  justify-content: flex-end;
  align-items: center;
}

.modal__close {
  position: absolute;
  top: var(--space-3, 12px);
  right: var(--space-3, 12px);
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--ink-3);
  padding: var(--space-2, 8px);
  border-radius: var(--r-md, 8px);
  font-size: var(--fs-18, 18px);
  line-height: 1;
}
.modal__close:hover {
  color: var(--ink);
  background: var(--bg-sunken, var(--bg-raised));
}
.modal__close:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Mobile: the panel takes the full viewport minus a small inset so it
   reads as a sheet rather than a tiny floating card. */
@media (max-width: 540px) {
  .modal__panel {
    top: var(--space-4, 16px);
    left: var(--space-4, 16px);
    right: var(--space-4, 16px);
    bottom: var(--space-4, 16px);
    transform: none;
    width: auto;
    max-width: none;
    max-height: none;
  }
  .modal__header,
  .modal__body,
  .modal__footer {
    padding-left: var(--space-5, 24px);
    padding-right: var(--space-5, 24px);
  }
}

/* Reduced-motion: skip the scrim fade. */
@media (prefers-reduced-motion: reduce) {
  .modal__scrim {
    transition: none;
  }
}

/* ─── Warp slot c (2026-04-30) — visitor sound chip ────────────────
   Tiny pill in the lower-right corner of every Studio surface.
   Visible at all times so the visitor knows audio is opt-in.
   Behavior wires up in widgets/sound_runtime.py. */

.sound-chip {
  position: fixed;
  right: var(--space-4, 16px);
  bottom: var(--space-4, 16px);
  z-index: 150;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2, 8px);
  padding: var(--space-1, 4px) var(--space-3, 12px);
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: 999px;
  box-shadow: var(--shadow-sm);
  font-family: var(--sans);
  font-size: var(--fs-12, 12px);
  color: var(--ink-3);
  transition: box-shadow var(--dur-base, 180ms) var(--ease, ease-out),
              border-color var(--dur-base, 180ms) var(--ease, ease-out);
}
.sound-chip:hover {
  box-shadow: 0 4px 16px -4px rgba(0,0,0,0.10), var(--shadow-sm);
  border-color: var(--ink-4);
}
.sound-chip[data-enabled="true"] {
  border-color: var(--accent);
  color: var(--ink);
}

.sound-chip__toggle {
  appearance: none;
  background: transparent;
  border: 0;
  margin: 0;
  padding: var(--space-1, 4px) var(--space-2, 8px);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2, 8px);
  cursor: pointer;
  font: inherit;
  color: inherit;
  border-radius: 999px;
  transition: background var(--dur-fast, 120ms) var(--ease, ease-out);
}
.sound-chip__toggle:hover {
  background: var(--accent-soft, rgba(0,0,0,0.04));
}
.sound-chip__toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.sound-chip__icon {
  display: inline-block;
  font-size: var(--fs-14, 14px);
  line-height: 1;
}

.sound-chip__label {
  display: inline-block;
  font-size: var(--fs-12, 12px);
  letter-spacing: 0.02em;
}

.sound-chip__slider {
  -webkit-appearance: none;
  appearance: none;
  width: 88px;
  height: 4px;
  background: var(--rule);
  border-radius: 999px;
  outline: none;
  margin: 0;
  cursor: pointer;
}
.sound-chip__slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg-raised);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
}
.sound-chip__slider::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--bg-raised);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
}
.sound-chip__slider[hidden] { display: none; }

.sound-chip__slider:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
}

@media (prefers-reduced-motion: reduce) {
  .sound-chip,
  .sound-chip__toggle {
    transition: none;
  }
}

@media (max-width: 480px) {
  .sound-chip {
    right: var(--space-2, 8px);
    bottom: var(--space-2, 8px);
    padding: var(--space-1, 4px) var(--space-2, 8px);
  }
  .sound-chip__slider { width: 64px; }
}
/* Slot warp:flowA - shared first-run progress strip. */
.first-run-progress {
  display: flex; align-items: center; gap: 12px;
  font-family: var(--sans); font-size: 12px;
  color: var(--ink-3, var(--ink-2));
  letter-spacing: 0.04em; text-transform: uppercase;
  padding: 6px 10px;
  border-radius: 999px;
  background: var(--bg-sunken, transparent);
  border: 1px solid var(--rule);
  width: fit-content;
}
.first-run-progress__label {
  white-space: nowrap;
  font-weight: 600;
}
.first-run-progress__pips {
  display: flex; gap: 6px; align-items: center;
}
.first-run-progress__pip {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--rule, rgba(0, 0, 0, 0.18));
  transition: background 200ms var(--ease, ease);
}
.first-run-progress__pip.is-done {
  background: var(--accent, #2f6fab);
}
.first-run-progress__pip.is-active {
  background: var(--accent, #2f6fab);
  box-shadow: 0 0 0 3px var(--accent-soft, rgba(30, 127, 196, 0.18));
}
/* ─── Phase B Slot 1B (2026-04-29) — hand-tuned light variants ──────
   Hero light variants for paper / cerulean / slate / mono.  Overrides
   trigger only on explicit data-mode="light".  Polished against
   WCAG 2.1 §1.4.3 — see tests/test_themes_light_contrast.py for the
   per-pair assertions. */

/* Warm appearance lock. The site default remains cerulean/light, but selecting
   Warm should restore the original low-light Endenza look: near-black canvas,
   cream text, and orange as the main accent. Keep this scoped to theme-warm:
   a broad body[data-mode="light"] selector loads after themes.py with
   equal specificity to body.theme-X and masks almost every palette. */
body.theme-warm[data-mode="light"] {
  --bg: #0f0d0a;
  --bg-raised: #1a1711;
  --bg-elevated: #1f1b14;
  --bg-sunken: #141109;
  --ink: #f3ecdd;
  --ink-2: #d6cdb9;
  --ink-3: #9a9078;
  --ink-4: #5e564a;
  --rule: #2a2620;
  --rule-2: #221e18;
  --accent: #e08a2c;
  --accent-2: #f3c07a;
  --accent-3: #7a4a16;
  --accent-ink: #1a0d02;
  --accent-soft: rgba(224,138,44,0.10);
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
  --shadow-md: 0 4px 24px -8px rgba(224,138,44,0.25);
  --success: #16a34a;
  --warning: #d97706;
  --danger:  #dc2626;
  --ink-1: var(--ink);
}

/* Warm — explicit dark variant.  Warm is the original low-light Endenza
   look (bronze-on-ink) in BOTH modes, so picking "dark" must keep that
   palette rather than falling through to the generic body[data-mode="dark"]
   defaults (which it would otherwise, since warm has no bare body.theme-warm
   rule in themes.py).  Values mirror the warm light block — both come from
   the ART_DIRECTION.md §2.1 Dark column (bronze-on-ink core). */
body.theme-warm[data-mode="dark"] {
  --bg: #0f0d0a;
  --bg-raised: #1a1711;
  --bg-elevated: #1f1b14;
  --bg-sunken: #141109;
  --ink: #f3ecdd;
  --ink-2: #d6cdb9;
  --ink-3: #9a9078;
  --ink-4: #5e564a;
  --rule: #2a2620;
  --rule-2: #221e18;
  --accent: #e08a2c;
  --accent-2: #f3c07a;
  --accent-3: #7a4a16;
  --accent-ink: #1a0d02;
  --accent-soft: rgba(224,138,44,0.10);
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
  --shadow-md: 0 4px 24px -8px rgba(224,138,44,0.25);
  --success: #16a34a;
  --warning: #d97706;
  --danger:  #dc2626;
  --ink-1: var(--ink);
}

/* paper — already light-by-default; the [data-mode="light"] selector
   re-asserts the same palette so it wins over an inherited dark mode
   on parent containers. */
body.theme-paper[data-mode="light"] { --bg: #f7f6f2; --bg-raised: #ffffff; --bg-elevated: #fbfaf6; --bg-sunken: #eceae4; --ink: #111111; --ink-2: #2e2a24; --ink-3: #5f5a50; --ink-4: #918a7c; --rule: #d9d5cc; --rule-2: #e7e2d8; --accent: #c2711f; --accent-2: #b45f17; --accent-3: #9b4a0d; --accent-ink: #1a0d02; --accent-soft: rgba(224,138,44,0.11); }

/* paper — explicit dark variant.  Paper resists OS dark mode by
   default; this rule only fires when the user picks "dark" in the
   Preferences UI.  Warm near-black bg + cream ink keeps the paper
   feel rather than slipping toward a generic dark theme. */
body.theme-paper[data-mode="dark"] { --bg: #1a1612; --bg-raised: #232019; --bg-sunken: #100d0a; --ink: #faf6ec; --ink-2: #d8d2c0; --ink-3: #9a9485; --ink-4: #6a6458; --rule: #2c2820; --rule-2: #211e18; --accent: #d4ccaa; --accent-2: #ebe2bd; --accent-3: #807a64; --accent-soft: rgba(212,204,170,0.14); }

/* cerulean — default steel-blue light variant.  Round 5b (2026-05-22)
   locked --accent on #1e7fc4 (the live "Ask Council" button colour the
   user signed off on).  --accent-2 is the channel-mean midpoint between
   the new accent and the prior sky #39B7FF: (43, 155, 225) → #2c9be2.
   All tokens collapse onto the same hue family:
     · --accent / --accent-text / --accent-focus / --action all #1e7fc4
     · --accent-2 / --action-2 all #2c9be2 (gradient + secondary)
     · --accent-ink / --action-ink = #ffffff (white labels, 4.66:1
       on #1e7fc4, passes AA normal-text + 3:1 UI components).
   See themes.py for the full Round-5b rationale + dark-mode equivalent. */
body.theme-cerulean[data-mode="light"] { --bg: #e1e7ef; --bg-raised: #fbfcfe; --bg-elevated: #f4f7fb; --bg-sunken: #dce3ec; --ink: #101827; --ink-2: #2d3948; --ink-3: #4f5b6b; --ink-4: #7e8b9b; --rule: #c6d0dc; --rule-2: #dbe2ea; --accent: #1e7fc4; --accent-text: #1666a8; --accent-2: #2c9be2; --accent-3: #8bb8de; --accent-ink: #ffffff; --accent-focus: #1e7fc4; --accent-soft: rgba(30,127,196,0.18); --action: #1e7fc4; --action-2: #2c9be2; --action-ink: #ffffff; --action-soft: rgba(30,127,196,0.16); }

/* slate — intentionally dark blue. Selecting Slate should never fall
   back to the pale white/blue surface; it is the focused low-light
   counterpart to Ash. */
body.theme-slate[data-mode="light"] { --bg: #08111f; --bg-raised: #101a2b; --bg-elevated: #16223a; --bg-sunken: #050a14; --ink: #e7edf7; --ink-2: #c7d2e4; --ink-3: #95a4ba; --ink-4: #65758d; --rule: #25324a; --rule-2: #1b263b; --accent: #2f7bf7; --accent-text: #60a5fa; --accent-2: #60a5fa; --accent-3: #93c5fd; --accent-ink: #ffffff; --accent-soft: rgba(47,123,247,0.18); }

/* mono — Apple-grade graphite light variant (LightMono-β,
   warp-20260502T060532Z slot b, 2026-05-02).  Hand-tuned away from the
   pure-white/pure-black starkness of the bare body.theme-mono rule:
   warm off-white bg (R≥G≥B, ~3°K shift), pure-white cards float above,
   subtle warm sunken well; ink steps from charcoal #1c1c1c through
   three perceptually-spaced grays (12-18% per step); rule hairlines
   sit ~3-10% off bg for the "barely visible" Apple divider feel;
   accent shifts from pure-black to medium graphite #494949 — a single
   tasteful gray with personality, distinct from ink so muted controls
   read as deliberate rather than as ink-equivalent.  Contrast verified
   in tests/test_themes_light_contrast.py (ink ≥ 15:1 on bg, ink-3
   ≥ 5:1, accent ≥ 8:1). */
body.theme-mono[data-mode="light"] { --bg: #f6f6f4; --bg-raised: #ffffff; --bg-elevated: #fafafa; --bg-sunken: #ececea; --ink: #1c1c1c; --ink-2: #3a3a3a; --ink-3: #5e5e5e; --ink-4: #8a8a8a; --rule: #d8d6d3; --rule-2: #ebe9e6; --accent: #494949; --accent-2: #6e6e6e; --accent-3: #a0a0a0; --accent-ink: #ffffff; --accent-soft: rgba(73,73,73,0.10); }

