/*
  tokens.css — averagejoematt.com v4 — "The Measured Life" (Direction 05)
  ----------------------------------------------------------------------------
  Source of truth: docs/DESIGN_SYSTEM_V4_THE_MEASURED_LIFE.md (§2 palette, §3 type),
  visual reference: docs/v4_art_direction_05_the_measured_life.html.
  Locked board-unanimous 2026-06-01.

  The concept: a measured life rendered as a beautifully made instrument log kept
  by a real person. Warm, crafted, honest — the opposite of clinical biohacking
  gloss AND of generic AI-template aesthetics.

  RULES (enforced everywhere downstream):
    - Never hardcode a colour, font, radius, or spacing outside this file.
    - Palette: locked hexes are the primitives; tints are derived with
      color-mix(in oklch, …) so the ramp stays perceptually even.
    - One ember accent only, used sparingly for "this is alive / this is up".
      Down / flat states use muted ink, NEVER red.
    - Type triad has fixed jobs: Fraunces = human voice, Instrument Sans =
      interface, IBM Plex Mono = machine voice & data (always tabular-nums).
    - No Inter / Roboto / system fonts. No purple gradients. No shadcn cards.

  The two ownable signatures (protect — do not let them metastasise):
    1. the measuring-rule spine     → --rule, --tick, --spine-* tokens (§ Spine)
    2. the machine ↔ human dialogue → --voice-* tokens (§ Two-voice)

  Fonts are <link>ed in each door's <head> (preconnect + the css2 request from the
  visual reference). This file only declares the family tokens.

  SECTIONS
    1. Palette — dark (primary)
    2. Typography
    3. Spacing & layout
    4. The measuring-rule spine (signature 1)
    5. The two-voice dialogue (signature 2)
    6. Honesty vocabulary
    7. Radii, borders, elevation
    8. Motion & z-index
    9. Light mode (Daybook-informed)
   10. Minimal base (applies the tokens)
*/

/* ── Cascade layers (#1007) ───────────────────────────────────────────────────
   Declared FIRST (in tokens.css, which every page loads before its per-door CSS) so
   the order is fixed regardless of stylesheet load order — later layer wins.
     chrome-base : the per-door desktop nav chrome (.doors, .story-top, …). Lives in
                   cockpit/story/evidence.css, which load AFTER tokens.css — so before
                   #1007 the mobile app-bar needed ~16 !important to beat them.
     chrome      : the mobile bottom app-bar (this file). Being a LATER layer, it wins
                   over chrome-base by layer order — no !important, no specificity war.
   This replaces the load-order-dependent !important stack that let the A2 bug (a
   .doors specificity collision resurrecting .nav-follow, #1003) happen. Unlayered
   rules still beat both layers, so every competing .doors/.*-top rule is placed in
   chrome-base; nothing that fights the app-bar is left unlayered. */
@layer chrome-base, chrome;

/* ════════════════════════════════════════════════════════════════════════════
   RESPONSIVE BREAKPOINTS — the canonical scale (#1006)
   ════════════════════════════════════════════════════════════════════════════
   CSS custom properties cannot be used inside @media queries (and this site runs
   with no CSS build step), so these are documented NAMED CONSTANTS, not var()s.
   Every @media (max-width|min-width) in the site's CSS uses one of these six
   boundaries — nothing else. (Before #1006: 17 ad-hoc values across 6 files, incl.
   near-dupes 759/760 + 819/820 and four competing "phone" boundaries, so a 580px
   viewport got mobile chrome with desktop grids.)

     --bp-xs   360   tiny-phone last resort — hide the brand tag, shrink door labels
     --bp-sm   480   small phone — stack tight 2-ups, drop non-essential columns
     --bp-md   600   PHONE / CHROME boundary — the bottom app-bar, single-column
                     grids, and the "is this a phone" decision ALL flip here
     --bp-lg   760   tablet — 2-col content grids, sticky side rails begin
     --bp-tw   820   tablet-wide — the evidence 2-col reading layout (rail + readout)
     --bp-xl   900   desktop — 3-col grids and the hero / subscribe split-screens

   MIN/MAX CONVENTION (avoids the boundary double-fire a naïve collapse reintroduces):
     • max-width queries use the token exactly       → 360 480 600 760 820
     • min-width queries use the token + 1px         → 601 761 821 901
   so a min/max pair straddling a boundary never both fire at the same pixel.

   USAGE RULE — pick the boundary by JOB, not pixel-taste:
     chrome / one-column vs. not  → 600 (max)
     content grid 1→2 col         → 761 (min)
     evidence reading split        → 820 (max) / 821 (min)
     grid 2→3 col / hero split      → 901 (min)
     phone fit-and-finish          → 480 / 360 (max)
   A new page MUST reuse these — do not invent an 18th boundary. The mobile section
   of docs/DESIGN_SYSTEM_V5.md restates this rule (#1011).
   ════════════════════════════════════════════════════════════════════════════ */

/* ── 1. Palette — dark (primary) ─────────────────────────────────────────── */
:root {
  color-scheme: dark;

  /* Locked primitives (DESIGN_SYSTEM §2). Hex is the source of truth;
     OKLCH equivalents noted for reference. */
  --page:        #0E0C08;   /* oklch(.15 .008 75)  — behind the paper */
  --surface:     #16130E;   /* oklch(.19 .010 75)  — paper / panels   */
  --ink:         #ECE3D2;   /* oklch(.91 .022 88)  — primary text      */
  --ink-muted:   #A99F8C;   /* oklch(.69 .020 84)  — secondary         */
  --ink-faint:   #8B806C;   /* oklch(.56 .018 82)  — labels, ticks · 5.0:1 on --page / 4.8:1 on
                               --surface (WCAG AA — #579 bump from #857B68, which cleared --page
                               at 4.68:1 but missed AA on --surface at 4.44:1) */
  --ember:       #DD7A37;   /* oklch(.69 .145 52)  — the one live accent */
  --alert:       #CB634C;   /* restrained oxblood — 5.1:1 on --page / 4.8:1 on --surface (WCAG AA
                               — #579 bump from #B14A33, which measured 3.6:1 on --page, failing
                               AA for the .vr-alert .vr-v ring-center label). RESERVED state alert
                               (vitals run-down / out-of-range only). NEVER encodes a falling direction. */
  --alert-wash:  color-mix(in oklch, var(--alert) 11%, transparent);
  --alert-line:  color-mix(in oklch, var(--alert) 45%, transparent);

  /* Derived tints (OKLCH mixing keeps the ramp even). */
  --surface-raised: color-mix(in oklch, var(--surface) 88%, var(--ink) 12%);
  --surface-sunken: color-mix(in oklch, var(--surface) 80%, var(--page) 20%);
  --shelf-spine-deep: #1d1810;  /* warm shadow foot of the mind.css book-spine gradient
                                   — one fixed value across both themes (a lit spine edge) */
  --ink-dim:        color-mix(in oklch, var(--ink) 70%, transparent);
  --ember-soft:     color-mix(in oklch, var(--ember) 18%, transparent);
  --ember-line:     color-mix(in oklch, var(--ember) 42%, transparent);
  --ember-wash:     color-mix(in oklch, var(--ember) 9%, transparent);

  /* Hairlines & ticks (the rule system) */
  --rule:        rgba(236, 227, 210, 0.12);   /* structural hairline */
  --rule-strong: rgba(236, 227, 210, 0.20);
  --tick:        rgba(236, 227, 210, 0.28);   /* measuring-rule ticks */

  /* Muted category tints for the two glance domains (low-sat, calm). */
  --body-tint: color-mix(in oklch, var(--ember) 6%, var(--surface));   /* Body domain */
  --mind-tint: color-mix(in oklch, var(--ink) 5%, var(--surface));     /* Mind domain */

  /* Semantic aliases (components reference these, not raw primitives). */
  --bg:          var(--page);
  --fg:          var(--ink);
  --accent:      var(--ember);
  --signal:      var(--ember);   /* "this is alive / this is up" */
}

/* ── 2. Typography ───────────────────────────────────────────────────────── */
:root {
  /* The triad — each font has one job (DESIGN_SYSTEM §3). */
  --font-serif: "Fraunces", ui-serif, Georgia, "Times New Roman", serif;        /* human voice */
  --font-sans:  "Instrument Sans", ui-sans-serif, "Helvetica Neue", Arial, sans-serif; /* interface */
  --font-mono:  "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;      /* machine & data */

  /* Fraunces variable axes */
  --fraunces-opsz-display: 144;
  --fraunces-opsz-text:    24;

  /* Fluid type scale. editorial display → data display → body → label. */
  --fs-display:  clamp(2.6rem, 1.4rem + 6vw, 5.5rem);   /* Fraunces, Story heroes */
  --fs-h1:       clamp(2rem, 1.3rem + 3.2vw, 3.25rem);  /* Fraunces */
  --fs-h2:       clamp(1.5rem, 1.1rem + 1.8vw, 2.25rem);
  --fs-h3:       clamp(1.2rem, 1rem + 0.9vw, 1.5rem);
  --fs-data-xl:  clamp(3.5rem, 2rem + 7vw, 5.25rem);    /* Plex Mono, the score */
  --fs-data-lg:  clamp(1.6rem, 1.2rem + 1.6vw, 2.25rem);
  --fs-body:     1.0625rem;                              /* Instrument Sans */
  --fs-body-lg:  1.1875rem;
  --fs-small:    0.875rem;
  --fs-label:    0.6875rem;                              /* Plex Mono uppercase, tracked */
  --fs-quote:    clamp(1.15rem, 1rem + 0.7vw, 1.45rem);  /* Fraunces italic, human reply */

  /* SVG-text floor tokens (#1210). These are viewBox-UNIT defaults for inline-SVG
     <text>; svgtype.js overrides each per-svg at runtime to max(base, 11px/scale)
     so the label renders >=11px effective at every width (the #1017 technique,
     generalized). The default = the designed desktop-true size; do NOT retune it
     without matching the `base` in svgtype.js SVG_TYPE_FLOORS. tests/visual_qa.py
     asserts the floor actually holds. */
  --fs-aq:           8px;    /* .aq-lab / .aq-ax — autonomic 2x2 labels + axes */
  --fs-radar-lbl:    10px;   /* .radar-lbl — pillar radar spoke labels */
  --fs-bm-cap:       8px;    /* .bm-cap — body-map front/back caption */
  --fs-emblem-level: 8px;    /* .emblem-level — tier emblem "LEVEL" caption */
  --fs-arch-at:      13px;   /* .arch-svg .at — architecture-diagram box title */
  --fs-att-sm:       11px;   /* .att-svg ticks/labels/marks — attempts overlay (#1375) */
  --fs-att-death:    13px;   /* .att-svg .att-death — attempts overlay collapse mark (#1375) */
  --fs-arch-as:      9.5px;  /* .arch-svg .as — architecture-diagram box subtitle */

  --lh-tight:    1.05;
  --lh-snug:     1.25;
  --lh-body:     1.6;
  --lh-relaxed:  1.7;

  --tracking-label: 0.16em;   /* uppercase mono labels */
  --tracking-tight: -0.02em;  /* big mono numerals */

  --weight-reg:    400;
  --weight-med:    500;
}

/* ── 3. Spacing & layout ─────────────────────────────────────────────────── */
:root {
  /* 4px base rhythm */
  --sp-1:  0.25rem;  --sp-2:  0.5rem;   --sp-3:  0.75rem;  --sp-4:  1rem;
  --sp-5:  1.5rem;   --sp-6:  2rem;     --sp-7:  3rem;     --sp-8:  4rem;
  --sp-9:  6rem;     --sp-10: 8rem;

  --measure:        68ch;     /* readable body line length */
  --measure-narrow: 46ch;
  --container:      1200px;   /* desktop max */
  --container-read: 760px;    /* Story / longform (matches reference .wrap) */
  --gutter:         clamp(1rem, 0.5rem + 2.5vw, 2.5rem);
  --bento-gap:      clamp(0.75rem, 0.4rem + 1.2vw, 1.25rem);
}

/* ── 4. The measuring-rule spine (SIGNATURE 1) ───────────────────────────── */
/*  A tick-marked rule as a structural element: anchors the Cockpit edge,
    segments the Story timeline, indexes the Evidence. Connective tissue across
    all three doors. Use with intent — not on every element. */
:root {
  --spine-width:   46px;          /* rail width (matches reference) */
  --spine-tick-gap: 15px;         /* distance between ticks */
  --spine-tick-w:   8px;          /* tick length into the rail */
  --spine-ticks: repeating-linear-gradient(
                   to bottom, var(--tick) 0 1px, transparent 1px var(--spine-tick-gap));
  --spine-border: 1px solid var(--rule);
}

/* ── 5. The two-voice dialogue (SIGNATURE 2) ─────────────────────────────── */
/*  Machine voice (mono) and human voice (serif) set in literal conversation —
    the Third Wall turned into the type system. The most on-brand device. */
:root {
  --voice-machine-font: var(--font-mono);
  --voice-machine-size: 0.84375rem;        /* 13.5px @16 */
  --voice-machine-lh:   1.55;
  --voice-machine-ink:  var(--ink);
  --voice-machine-mark: var(--ember);      /* the › lead glyph + emphasis */

  --voice-human-font:   var(--font-serif);
  --voice-human-style:  italic;
  --voice-human-size:   1.1875rem;         /* 19px */
  --voice-human-lh:     1.4;
  --voice-human-ink:    var(--ink);

  --voice-who-font:     var(--font-mono);  /* the speaker label */
  --voice-who-size:     0.5625rem;         /* 9px */
  --voice-who-ink:      var(--ink-faint);
  --voice-who-tracking: var(--tracking-label);
}

/* ── 6. Honesty vocabulary ───────────────────────────────────────────────── */
/*  Down weeks & pauses: muted ink + a DASHED hairline marker + plain language.
    First-class, never alarm-red, never hidden. The anti-Blueprint anchor. */
:root {
  --honest-ink:    var(--ink-muted);
  --honest-border: 1px dashed var(--rule);
  --honest-mark:   "▼";        /* down */
  --flat-mark:     "›";        /* holding */
  --up-mark:       "▲";        /* up — the only place ember is earned on a trend */
  --down-fill:     var(--ink-faint);   /* progress fill for a down pillar (muted, not red) */
  --up-fill:       var(--ember);       /* progress fill for an up pillar */
}

/* ── 7. Radii, borders, elevation ────────────────────────────────────────── */
:root {
  --radius-xs: 3px;    /* honesty chip, tags */
  --radius-sm: 6px;
  --radius:    10px;   /* panels (matches reference) */
  --radius-lg: 16px;

  --border-hair:   1px solid var(--rule);
  --border-strong: 1px solid var(--rule-strong);

  /* Restrained elevation — flat surfaces, soft depth. No neon glow. */
  --shadow-panel: 0 22px 50px rgba(0, 0, 0, 0.32);
  --shadow-soft:  0 6px 18px rgba(0, 0, 0, 0.22);
}

/* ── 8. Motion & z-index ─────────────────────────────────────────────────── */
:root {
  --ease-out:   cubic-bezier(0.2, 0.7, 0.2, 1);   /* reveal / rise */
  --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast:   0.18s;
  --dur:        0.36s;
  --dur-slow:   0.7s;        /* page-load rise (reference) */
  --stagger:    0.06s;       /* between bento items on the orchestrated reveal */

  /* View Transitions: door-to-door + in-place pillar disclosure. */
  --vt-dur:     0.4s;

  --z-base: 0; --z-spine: 5; --z-sticky: 50; --z-overlay: 100; --z-toast: 200;
}

/* ── 9. Light mode (Daybook-informed) ────────────────────────────────────── */
/*  Real, first-class light mode. Activated by [data-theme="light"] OR by the OS
    when the user hasn't explicitly chosen dark. */
:root[data-theme="light"],
:root:not([data-theme="dark"]) {
  /* placeholder so the cascade below can override cleanly */
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;
    --page:      #F4EFE4;   /* oklch(.95 .012 88) */
    --surface:   #FBF8F1;   /* oklch(.98 .008 88) */
    --ink:       #221E17;   /* oklch(.26 .012 78) */
    --ink-muted: #6E665A;   /* oklch(.49 .015 82) */
    --ink-faint: #6F6757;   /* oklch(.48 .015 84) · 4.9:1 on --page (WCAG AA) */
    --ember:     #A34E13;   /* AA-compliant on paper — 5.0:1 on #F4EFE4 (small ember text: nav, footer headers, labels) */
    --alert:     #9E4732;   /* light-mode oxblood — 5.4:1 on #F4EFE4 / 5.8:1 on #FBF8F1 (WCAG AA — dark-root
                              #CB634C measured only 3.37:1 on --page, failing AA for the .vr-alert .vr-v
                              ring-center value; #579 fixed dark only, #1222 adds the light override) */
    --rule:        rgba(34, 30, 23, 0.13);
    --rule-strong: rgba(34, 30, 23, 0.22);
    --tick:        rgba(34, 30, 23, 0.30);
    --shadow-panel: 0 18px 44px rgba(34, 30, 23, 0.12);
    --shadow-soft:  0 6px 16px rgba(34, 30, 23, 0.08);
  }
}

/* Explicit user choice always wins over the OS preference. */
:root[data-theme="light"] {
  color-scheme: light;
  --page:      #F4EFE4;
  --surface:   #FBF8F1;
  --ink:       #221E17;
  --ink-muted: #6E665A;
  --ink-faint: #6F6757;   /* 4.9:1 on --page (WCAG AA) */
  --ember:     #A34E13;
  --alert:     #9E4732;   /* light-mode oxblood — 5.4:1 on #F4EFE4 / 5.8:1 on #FBF8F1 (WCAG AA — see #1222) */
  --rule:        rgba(34, 30, 23, 0.13);
  --rule-strong: rgba(34, 30, 23, 0.22);
  --tick:        rgba(34, 30, 23, 0.30);
  --shadow-panel: 0 18px 44px rgba(34, 30, 23, 0.12);
  --shadow-soft:  0 6px 16px rgba(34, 30, 23, 0.08);
}

/* ── 10. Minimal base — applies the tokens so every door starts honest ───── */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-size-adjust: 100%;
}

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-synthesis: none;
}

/* Headings carry the human voice unless a component opts out. */
h1, h2, h3 {
  font-family: var(--font-serif);
  font-weight: var(--weight-med);
  line-height: var(--lh-tight);
  text-wrap: balance;
}
p { text-wrap: pretty; max-width: var(--measure); }

/* Every number / label is mono + tabular by default. */
.mono, [data-voice="machine"], .data, .num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* The label primitive (Plex Mono, uppercase, tracked). */
.label {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-faint);
}

a { color: inherit; text-underline-offset: 0.18em; }

/* Screen-reader-only — visually hidden, available to AT (e.g. a page's <h1> landmark). */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

/* The orchestrated page-load reveal — motion earns its weight, or it's cut. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Dual-device dumbbell (sleep §2, P0.3) — A=ember dot, B=muted dot, connector = the spread.
   "Agreement, not truth." Reuses the .suf-row grid. */
.db-track { position: relative; height: 10px; }
.db-track::before { content: ""; position: absolute; left: 0; right: 0; top: 4px; height: 2px; background: var(--surface-2, var(--surface)); border-radius: 2px; }
.db-bar { position: absolute; top: 4px; height: 2px; background: var(--ink-faint); border-radius: 2px; }
.db-dot { position: absolute; top: 0; width: 9px; height: 9px; border-radius: 50%; transform: translateX(-50%); top: 1px; }
.db-dot.db-a, .sbar-dot.db-a { background: var(--ember); }
.db-dot.db-b, .sbar-dot.db-b { background: var(--ink-muted); }

/* Per-group trend small-multiples (habits §6, P0.7) — low/floor groups muted, never red. */
.gt-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--sp-3); margin-top: var(--sp-3); }
.gt-card { border: 1px solid var(--rule); border-radius: var(--radius-sm); padding: var(--sp-3); }
.gt-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); }
.gt-name { font-weight: var(--weight-med); font-size: var(--fs-small); }
.gt-pct { color: var(--ink-faint); }
.gt-thin { color: var(--ink-faint); }
.gt-low { opacity: 0.85; }
.gt-low .chart-line { stroke: var(--ink-muted); }

/* Effort map (habits §5, P0.6) — ranked dot-strip: size = habit count, ember = adherence. */
.em-strip { display: flex; flex-direction: column; gap: var(--sp-3); margin-top: var(--sp-3); }
.em-row { display: flex; align-items: center; gap: var(--sp-3); }
.em-dot { border-radius: 50%; flex: 0 0 auto; border: 1px solid var(--rule);
  background: color-mix(in oklch, var(--ember) calc(var(--heat, 0.12) * 100%), var(--surface-2, var(--surface))); }
.em-l { font-weight: var(--weight-med); }
.em-meta { color: var(--ink-faint); margin-left: auto; white-space: nowrap; }

/* Habit state taxonomy (habits §4, P0.5) — ONE ember+ink ramp + markers, NO rainbow, no red.
   Automatic = full ember · Holding = ember tint · Needs-attention = muted + marker ·
   Backlog = dashed outline (shown, honestly empty). */
.st-lane { margin-top: var(--sp-3); }
.st-lanehead { display: flex; align-items: baseline; gap: var(--sp-2); }
.st-marker { width: 10px; height: 10px; border-radius: 2px; display: inline-block; flex: 0 0 auto; }
.st-name { font-weight: var(--weight-med); }
.st-desc { color: var(--ink-faint); }
.st-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-2); }
.st-chip { display: inline-flex; align-items: center; gap: var(--sp-1); border-radius: var(--radius-xs); padding: 2px 8px; font-size: var(--fs-small); border: 1px solid var(--rule); }
.st-pct { font-family: var(--font-mono); font-size: 0.65rem; color: var(--ink-faint); }
.st-chip.st-auto { background: var(--ember); border-color: var(--ember); color: var(--page); }
.st-chip.st-auto .st-pct { color: var(--page); }
.st-marker.st-auto { background: var(--ember); }
.st-chip.st-hold { background: var(--ember-wash); border-color: var(--ember); }
.st-marker.st-hold { background: color-mix(in oklch, var(--ember) 55%, var(--surface)); }
.st-chip.st-need { background: var(--surface-2, var(--surface)); border-color: var(--ink-muted); }
.st-marker.st-need { background: var(--ink-muted); }
.st-chip.st-backlog { background: transparent; border: 1px dashed var(--rule); color: var(--ink-faint); }
.st-marker.st-backlog { background: transparent; border: 1px dashed var(--ink-faint); }

/* P1.1 — auto-derived per-habit context tags in the registry list. One ink+ember
   ramp, muted: these are heuristic reads, not loud signals. */
.hb-tax-row { display: inline-flex; gap: var(--sp-1); margin-left: var(--sp-2); vertical-align: middle; }
.hb-tax { font-family: var(--font-mono); font-size: 0.58rem; text-transform: uppercase; letter-spacing: var(--tracking-label);
  border-radius: var(--radius-xs); padding: 1px 6px; border: 1px solid var(--rule); color: var(--ink-faint); white-space: nowrap; }
.hb-tax-time { color: var(--ink-muted); }
.hb-tax-type { border-color: color-mix(in oklch, var(--ember) 40%, var(--rule)); color: var(--ember); }

/* P1.2 — friction tag from real adherence. Ember=automatic (earned), fading to a muted
   outline as friction rises. Never red — high friction is information, not failure. */
.hb-fr { font-family: var(--font-mono); font-size: 0.58rem; text-transform: uppercase; letter-spacing: var(--tracking-label);
  border-radius: var(--radius-xs); padding: 1px 6px; margin-left: var(--sp-2); white-space: nowrap; vertical-align: middle; border: 1px solid var(--rule); }
.hb-fr.fr-auto { background: var(--ember-wash); border-color: var(--ember); color: var(--ember); }
.hb-fr.fr-mid { color: var(--ink-muted); }
.hb-fr.fr-hard { border-style: dashed; border-color: var(--ink-muted); color: var(--ink-faint); }

/* #1107 — per-habit 30-day dot strip (genesis-clamped window, ADR-077). One ink+ember
   ramp, never red: kept = ember · missed = muted ink · not-scheduled = faint outline ·
   no-data = the honest-absence dash (a flattened dash-dot, visually distinct from a miss). */
.hb-dotcell { vertical-align: middle; }
.hb-dots { display: inline-flex; align-items: center; gap: 2px; flex-wrap: wrap; max-width: 100%; }
.hb-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; flex: none; }
.hb-dot.hd-done { background: var(--ember); }
.hb-dot.hd-miss { background: var(--ink-muted); }
.hb-dot.hd-off { background: transparent; border: 1px solid var(--rule); }
.hb-dot.hd-abs { background: transparent; border-top: 1px dashed var(--ink-faint); border-radius: 0; height: 0; }

/* P1.3 drivers table + P1.4 misses — honest-empty cells read faint/italic, never alarming. */
.rd-drv th { font-family: var(--font-mono); font-size: 0.6rem; text-transform: uppercase; letter-spacing: var(--tracking-label); color: var(--ink-faint); text-align: left; font-weight: var(--weight-reg); padding-bottom: var(--sp-1); }
.drv-empty { color: var(--ink-faint); font-style: italic; font-size: var(--fs-small); }
/* #422 — provenance chip on a captured driver / why-missed value (in-app note vs Claude reflection). */
.drv-src { margin-left: var(--sp-1); color: var(--ink-faint); font-size: 0.6rem; text-transform: uppercase; letter-spacing: var(--tracking-label); font-family: var(--font-mono); }
.wm-list { display: flex; flex-direction: column; gap: var(--sp-2); margin-top: var(--sp-3); }
.wm-row { display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap; padding-bottom: var(--sp-2); border-bottom: 1px solid var(--rule); }
.wm-name { font-weight: var(--weight-med); }
.wm-n { color: var(--ink-muted); font-size: var(--fs-small); }
.wm-why { margin-left: auto; }

/* Cross-source signal board (sleep §8, Phase 2) — self-policing cards: n + overlap +
   confidence; direction-only under 2 weeks. Muted/honest, never alarm. */
.cb-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--sp-3); margin-top: var(--sp-3); }
.cb-card { border: var(--honest-border); border-radius: var(--radius-sm); padding: var(--sp-3); display: flex; flex-direction: column; gap: var(--sp-2); }
.cb-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.cb-pair { font-size: var(--fs-small); font-weight: var(--weight-med); margin: 0; }
.cb-arrow { color: var(--ember); }
.cb-tag { font-family: var(--font-mono); font-size: 0.6rem; text-transform: uppercase; letter-spacing: var(--tracking-label); color: var(--ink-faint); white-space: nowrap; }
.cb-note { color: var(--ink-muted); font-size: var(--fs-small); margin: 0; }
.cb-read { display: flex; flex-direction: column; gap: var(--sp-1); }
.cb-dir { margin: 0; color: var(--ink); font-size: var(--fs-small); }
.cb-withheld { color: var(--ink-muted); font-style: italic; }
.cb-noise { color: var(--ink-muted); font-size: 0.7rem; font-family: var(--font-mono); }
.cb-meta { color: var(--ink-faint); }

/* Circadian forecast hero (sleep §0, P0.1) — anchors as suf-rows; the lever (weakest
   anchor) labelled ember; at-risk anchors muted ink, NEVER red. */
.fc-anchors { margin-top: var(--sp-3); }
.fc-lever .suf-l { color: var(--ember); }

/* Anatomical body-map (training, P1.4) — muscle zones shaded by ember intensity (--o set
   per zone) = weekly volume vs optimal. ONE hue, never red. */
.bodymap svg { width: 100%; height: clamp(180px, 26vw, 240px); }
.bm-head { fill: var(--ink-faint); opacity: 0.28; }
.bm-m { fill: var(--ember); fill-opacity: var(--o, 0.1); stroke: var(--rule); stroke-width: 0.5; }
.bm-cap { fill: var(--ink-faint); font-family: var(--font-mono); font-size: var(--fs-bm-cap); text-anchor: middle; letter-spacing: 0.12em; text-transform: uppercase; } /* floored >=11px effective by svgtype.js (#1210) */
/* Tier-emblem "LEVEL" caption (sigils.js tierEmblem) — viewBox-unit text, floored >=11px effective by svgtype.js (#1210). */
.emblem-level { font-size: var(--fs-emblem-level); }

/* Per-muscle volume landmark bars (training §5, P1.3) — MEV–MAV band shaded, MRV tick;
   ember in the optimal band, muted ink under/over (never red). Reuses the .suf-row grid. */
.lmk-track { position: relative; height: 10px; border-radius: 5px; background: var(--surface-2, var(--surface)); }
.lmk-band { position: absolute; top: 0; height: 100%; background: var(--ember-wash);
  border-left: 1px dashed var(--ink-faint); border-right: 1px dashed var(--ink-faint); }
.lmk-fill { position: absolute; top: 0; height: 100%; border-radius: 5px; }
.lmk-fill.suf-ember { background: var(--ember); }
.lmk-fill.suf-ink { background: var(--ink-muted); }
.lmk-mrv { position: absolute; top: -2px; width: 1px; height: 14px; background: var(--ink-faint); }

/* The Lift Index (training §2, P0.1) — per-lift load TREND tiles (sparkline + ▲/▼/flat).
   Ember = load up; down = muted ink, NEVER red; under 3 sessions = honest fills-in tile. */
.li-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--sp-3); margin-top: var(--sp-3); }
.li-tile { border: 1px solid var(--rule); border-radius: var(--radius-sm); padding: var(--sp-3); display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0; }
.li-top { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); }
.li-name { font-weight: var(--weight-med); }
.li-tag { font-family: var(--font-mono); font-size: 0.6rem; white-space: nowrap; }
.li-up { color: var(--ember); }
.li-down { color: var(--ink-muted); }
.li-flat { color: var(--ink-faint); }
.li-meta { color: var(--ink-faint); }
.li-thin { opacity: 0.9; justify-content: center; min-height: 4.5rem; }
.li-fill { color: var(--ink-faint); }

/* Ember-intensity steps heat strip (training §1c, P0.6) — saturation = volume; low days
   muted (faint ember), never hidden. ONE hue — intensity, not a second colour. */
.heat-strip { display: flex; flex-wrap: wrap; gap: 4px; margin-top: var(--sp-3); }
.heat-cell { flex: 1 1 2.2rem; min-width: 2.2rem; aspect-ratio: 1 / 1; border-radius: var(--radius-xs);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  background: color-mix(in oklch, var(--ember) calc(var(--heat, 0.1) * 100%), var(--surface-2, var(--surface)));
  border: 1px solid var(--rule); }
.heat-v { font-size: 0.6rem; line-height: 1; color: var(--ink); }
.heat-d { font-size: 0.5rem; color: var(--ink-faint); }
/* Dense GitHub-style calendar (habits §2, P0.3) — saturation only; cut-start ringed ember. */
.heat-strip-compact { gap: 3px; }
.heat-cell.heat-compact { flex: 0 0 auto; width: 13px; min-width: 13px; aspect-ratio: 1 / 1; border-radius: 2px; }
.heat-cell.heat-cut { outline: 1.5px solid var(--ember); outline-offset: 1px; }

/* ── Charts (shared, token-driven — see assets/js/charts.js) ──────────────── */
.chart { margin-top: var(--sp-4); }
.chart svg { width: 100%; height: clamp(110px, 8vw + 80px, 150px); display: block; overflow: visible; }
.chart-line { fill: none; stroke: var(--ember); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart-fill { fill: var(--ember-wash); stroke: none; }
.chart-goal { stroke: var(--ink-faint); stroke-width: 1; stroke-dasharray: 3 4; }
.chart-down { stroke: var(--ink-muted); stroke-width: 1.5; stroke-dasharray: 4 3; }
.chart-dot { fill: var(--ember); }
/* Interactive line-chart hover (wired by motion.js, data from data-cpts) — a focus dot
   + cursor-following readout that make every trend chart explorable. */
.chart-focus { position: absolute; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; border-radius: 50%;
  background: var(--ember); box-shadow: 0 0 0 3px var(--ember-wash); pointer-events: none; z-index: 3; }
.chart-tip { position: absolute; transform: translate(-50%, calc(-100% - 10px)); white-space: nowrap; pointer-events: none;
  background: var(--surface-raised); border: var(--border-hair); border-radius: var(--radius-xs); padding: 2px 8px;
  color: var(--ink); box-shadow: var(--shadow-soft); z-index: 4; }
.chart-focus[hidden], .chart-tip[hidden] { display: none; }
/* P0.1 — trend-weight hero: faint raw daily dots (scale noise) + confident ember trend +
   a muted genesis rule. Ember = the smoothed signal; dots are deliberately faint. */
.wt-trend { stroke: var(--ember); stroke-width: 2.25; stroke-linejoin: round; stroke-linecap: round; }
.wt-raw { fill: var(--ink-faint); opacity: 0.45; }
.wt-genesis { stroke: var(--ink-muted); stroke-width: 1; stroke-dasharray: 2 4; }
.wt-key { white-space: nowrap; }
.wt-swatch { display: inline-block; width: 0.7em; height: 0.7em; vertical-align: baseline; margin-right: 0.25em; border-radius: 2px; }
.wt-swatch-raw { background: var(--ink-faint); opacity: 0.5; border-radius: 50%; }
.wt-swatch-trend { background: var(--ember); height: 0.22em; border-radius: 1px; }
/* P0.2 — the scrub marker the silhouette drives in lockstep; ember dashed, pins to the
   floor (muted) when the scrubbed weight is below the chart's data range (toward goal). */
.wt-marker { stroke: var(--ember); stroke-width: 1.25; stroke-dasharray: 5 4; transition: opacity 0.15s; }
.wt-marker-below { stroke: var(--ink-muted); }
/* P0.4 — milestone ladder: a vertical measuring-rule. A rail (border) with a tick per
   10-lb rung; crossed rungs ember, the live edge marked, future rungs muted. Never red. */
.ml-ladder { display: flex; flex-direction: column; margin: var(--sp-3) 0; border-left: 2px solid var(--rule); padding-left: 0; }
.ml-rung { position: relative; display: flex; align-items: baseline; gap: var(--sp-3); padding: var(--sp-2) 0 var(--sp-2) var(--sp-4); min-height: 1.4em; }
.ml-tick { position: absolute; left: -2px; top: 0.7em; width: 14px; height: 2px; background: var(--rule); transform: translateX(-50%); }
.ml-w { font-size: var(--fs-h4, 1.05rem); min-width: 3.4em; }
.ml-cap { text-transform: uppercase; letter-spacing: var(--tracking-label); color: var(--ink-faint); }
.ml-meta { color: var(--ink-faint); }
.ml-crossed .ml-tick { background: var(--ember); width: 20px; height: 3px; top: 0.66em; }
.ml-crossed .ml-w { color: var(--ember); }
.ml-future .ml-w, .ml-next .ml-w { color: var(--ink-muted); }
.ml-next .ml-tick { background: var(--ink-muted); width: 16px; }
.ml-now { margin-left: auto; font-family: var(--font-mono); font-size: var(--fs-small); color: var(--ember); border: 1px solid var(--ember); border-radius: var(--radius-xs); padding: 1px 8px; white-space: nowrap; }
/* P0.5 — rate tempo: slope-gauges, ember intensity (width + opacity) = pace. Gain = ink. */
.rt-strip { display: flex; flex-direction: column; gap: var(--sp-3); margin: var(--sp-3) 0; }
.rt-row { display: grid; grid-template-columns: 7.5em 1fr auto; align-items: center; gap: var(--sp-3); }
.rt-label { color: var(--ink); font-weight: var(--weight-med); }
.rt-sub { color: var(--ink-faint); font-family: var(--font-mono); font-size: 0.7rem; }
.rt-gauge { height: 10px; background: var(--surface-2, var(--surface)); border: 1px solid var(--rule); border-radius: 5px; overflow: hidden; }
.rt-fill { display: block; height: 100%; }
.rt-fill.rt-ember { background: var(--ember); }
.rt-fill.rt-ink { background: var(--ink-muted); }
.rt-v { white-space: nowrap; color: var(--ember); }
.rt-v.rt-na { color: var(--ink-faint); }
.rt-flag { grid-column: 3; color: var(--ink-faint); font-size: 0.66rem; text-transform: uppercase; letter-spacing: var(--tracking-label); }
/* P0.6 — projection cone: ember-wash widening band, ember mid-line, muted goal + rung marks. */
.pc-cone { fill: var(--ember-wash); stroke: var(--ember-line); stroke-width: 0.5; }
.pc-mid { stroke: var(--ember); stroke-width: 2; stroke-linejoin: round; }
.pc-goal { stroke: var(--ink-muted); stroke-width: 1; stroke-dasharray: 4 4; }
.pc-rung { stroke: var(--rule); stroke-width: 1; stroke-dasharray: 2 4; }
.pc-bet { display: block; margin-top: var(--sp-2); color: var(--ink-muted); font-style: italic; line-height: 1.5; }
.pc-bet strong { color: var(--ember); font-style: normal; }

/* ── Uncertainty-first visual language (#551) — the confidence grammar ────────────
   The site's real intervals (block-bootstrap CIs) and real sample sizes rendered as a
   first-class visual primitive. ONE hue (ember), never red; band opacity + edge treatment
   carry the message. HIGH = defined + tight, MEDIUM = wider + dashed edge, LOW = no band. */
:root {
  --band-fill-hi:  color-mix(in oklch, var(--ember) 14%, transparent);
  --band-fill-med: color-mix(in oklch, var(--ember) 8%, transparent);
  --band-edge-hi:  color-mix(in oklch, var(--ember) 52%, transparent);
  --band-edge-med: color-mix(in oklch, var(--ember) 30%, transparent);
}
/* Projection FAN — the widening band is the real slope CI; edge treatment by confidence. */
.pc-cone.cf-high { fill: var(--band-fill-hi); stroke: var(--band-edge-hi); stroke-width: 0.6; }
.pc-cone.cf-med  { fill: var(--band-fill-med); stroke: var(--band-edge-med); stroke-width: 0.6; stroke-dasharray: 3 3; }
.pc-cone.cf-low  { fill: none; stroke: none; }   /* low ⇒ no band drawn at all (honest) */
/* Sample-size DOTS — n as a dot row; the grammar tints it (many = solid ember, few = faint). */
.ndots { display: inline-flex; align-items: center; gap: 2px; vertical-align: middle; }
.ndot { width: 4px; height: 4px; border-radius: 50%; background: currentColor; opacity: 0.9; flex: 0 0 auto; }
.ndots.cf-high { color: var(--ember); }
.ndots.cf-med  { color: var(--ink-muted); }
.ndots.cf-low, .ndots--none { color: var(--ink-faint); }
.ndots-more { font-size: 0.62rem; margin-left: 2px; opacity: 0.8; }
.ndots-n { font-size: 0.62rem; margin-left: 5px; color: var(--ink-faint); }
.corr-n { white-space: nowrap; }
/* CI whisker — a point estimate + its real interval on an auto-scaled rail; faint zero ref. */
.ciw-fig { margin: var(--sp-2) 0 0; }
.ciw-wrap { position: relative; height: 40px; margin-top: var(--sp-2); }
.ciw-rule { position: absolute; top: 50%; left: 0; right: 0; height: 1px; background: var(--rule); }
.ciw-zero { position: absolute; top: 28%; height: 44%; width: 1px; background: var(--ink-faint); }
.ciw-band { position: absolute; top: 50%; transform: translateY(-50%); height: 13px; border-radius: 7px; }
.ciw-band.cf-high { background: var(--band-fill-hi); border: 1px solid var(--band-edge-hi); }
.ciw-band.cf-med  { background: var(--band-fill-med); border: 1px dashed var(--band-edge-med); }
.ciw-band.cf-low  { background: none; }
.ciw-dot { position: absolute; top: 50%; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; border-radius: 50%; background: var(--ember); }
.ciw-dot.cf-low { background: var(--ink-muted); }
/* P0.7 — BMI, de-emphasized: muted, small, never ember. The caption does the talking. */
.rd-bmi { margin: var(--sp-2) 0 0; display: flex; align-items: baseline; gap: var(--sp-2); }
.rd-bmi-v { font-size: var(--fs-h3, 1.5rem); color: var(--ink-muted); }
/* P1.1 — next-DEXA countdown: the arc anchor. Ember number = anticipation, not alarm. */
.dx-count { display: flex; align-items: baseline; gap: var(--sp-3); margin: var(--sp-2) 0; }
.dx-days { font-size: var(--fs-display, 2.4rem); color: var(--ember); line-height: 1; }
.dx-unit { color: var(--ink-muted); }
/* P1.3 — visceral fat: a directional risk gauge. Ember intensity (never red) rises with the
   zone; the marker is an ink tick. Bands are subtle background, the caption does the judging. */
.vf-wrap { margin: var(--sp-3) 0; }
.vf-fig { display: flex; align-items: baseline; gap: var(--sp-3); }
.vf-v { font-size: var(--fs-h3, 1.5rem); color: var(--ink); }
.vf-band { text-transform: uppercase; letter-spacing: var(--tracking-label); padding: 1px 8px; border-radius: var(--radius-xs); }
.vf-band.vf-low { color: var(--ink-muted); border: 1px solid var(--rule); }
.vf-band.vf-moderate { color: var(--ember); border: 1px solid var(--ember-line); }
.vf-band.vf-elevated { color: var(--ember); border: 1px solid var(--ember); background: var(--ember-wash); }
.vf-gauge { position: relative; display: flex; height: 12px; margin-top: var(--sp-2); border-radius: 6px; overflow: hidden; border: 1px solid var(--rule); }
.vf-zone { height: 100%; }
.vf-z1 { width: 33.33%; background: var(--ember-wash); }
.vf-z2 { width: 33.33%; background: var(--ember-soft); }
.vf-z3 { width: 33.34%; background: var(--ember-line); }
.vf-mark { position: absolute; top: -2px; width: 2px; height: 16px; background: var(--ink); transform: translateX(-50%); }
.vf-scale { display: flex; justify-content: space-between; margin-top: var(--sp-1); color: var(--ink-faint); }
/* P1.5 — PhenoAge: a single transparent biological-age figure (Option A — no chronological).
   Drivers expand; ember = pushing younger, muted = older. Younger-bio-age reads ember-positive. */
.pa-dial { display: flex; align-items: baseline; gap: var(--sp-3); margin: var(--sp-2) 0; }
.pa-v { font-size: var(--fs-display, 2.6rem); color: var(--ember); line-height: 1; }
.pa-unit { color: var(--ink-muted); }
.pa-details { margin-top: var(--sp-3); border-top: 1px solid var(--rule); padding-top: var(--sp-2); }
.pa-sum { cursor: pointer; color: var(--ink-muted); }
.pa-drivers { display: flex; flex-direction: column; gap: 1px; margin-top: var(--sp-3); }
.pa-driver { display: grid; grid-template-columns: 1fr auto 5.5em; align-items: baseline; gap: var(--sp-3); padding: var(--sp-2) 0; border-bottom: 1px solid var(--rule); }
.pa-d-name { font-weight: var(--weight-med); }
.pa-d-val { color: var(--ink-muted); white-space: nowrap; }
.pa-d-dir { text-transform: uppercase; letter-spacing: var(--tracking-label); text-align: right; }
.pa-younger .pa-d-dir { color: var(--ember); }
.pa-older .pa-d-dir { color: var(--ink-muted); }
.pa-neutral .pa-d-dir { color: var(--ink-faint); }
.pa-derived { font-family: var(--font-mono); font-size: 0.58rem; text-transform: uppercase; letter-spacing: var(--tracking-label); color: var(--ink-faint); border: 1px solid var(--rule); border-radius: var(--radius-xs); padding: 0 5px; }
/* P1.6 — full-scan expander + the suppressed-artifact flag (muted ember, never red). */
.fs-exp { margin: var(--sp-2) 0; }
.fs-body { margin-top: var(--sp-3); }
.fs-body .hb-group { margin-top: var(--sp-4); }
.rd-flag-note { color: var(--ember); border: 1px dashed var(--ember-line); border-radius: var(--radius-xs); padding: var(--sp-2) var(--sp-3); background: var(--ember-wash); }
/* P2.1–P2.5 — capture backlog cards: honest pending states, never alarmist. */
.cap-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--sp-3); margin: var(--sp-3) 0; }
.cap-card { border: var(--honest-border); border-radius: var(--radius-sm); padding: var(--sp-3); }
.cap-h { margin: 0 0 var(--sp-2); display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; font-size: var(--fs-small); }
.cap-tag { font-family: var(--font-mono); font-size: 0.58rem; text-transform: uppercase; letter-spacing: var(--tracking-label); color: var(--ink-faint); border: 1px solid var(--rule); border-radius: var(--radius-xs); padding: 0 6px; white-space: nowrap; }
.cap-tag.cap-private { color: var(--ember); border-color: var(--ember-line); }
/* RQA-05 — deficit-sustainability five-channel read. Strain = ember (look here), holding =
   muted ink, too-few = faint. Verdict header ember-toned on attention, calm otherwise. No red. */
.dsx-verdict { display: flex; align-items: baseline; gap: var(--sp-3); margin: var(--sp-3) 0; padding: var(--sp-3); border: var(--honest-border); border-radius: var(--radius-sm); }
.dsx-verdict.dsx-sev-attn { border-color: var(--ember-line); background: var(--ember-wash); }
.dsx-count { font-size: var(--fs-h3, 1.4rem); color: var(--ember); white-space: nowrap; }
.dsx-vtext { color: var(--ink); }
.dsx-rows { display: flex; flex-direction: column; gap: 1px; margin: var(--sp-3) 0; }
.dsx-row { display: grid; grid-template-columns: 1fr auto 7em; align-items: baseline; gap: var(--sp-3); padding: var(--sp-2) 0; border-bottom: 1px solid var(--rule); }
.dsx-name { font-weight: var(--weight-med); }
.dsx-dir { color: var(--ink-muted); white-space: nowrap; }
.dsx-status { text-align: right; text-transform: uppercase; letter-spacing: var(--tracking-label); }
.dsx-strain .dsx-status, .dsx-strain .dsx-dir { color: var(--ember); }
.dsx-hold .dsx-status { color: var(--ink-muted); }
.dsx-none .dsx-status, .dsx-none .dsx-dir { color: var(--ink-faint); }
/* WQA-06 — coach disagreements: head-to-head positions + the integrator's call. The argument
   is the moat; surfaced not averaged. Ember marks the verdict, never alarm. */
.dis-grid { display: flex; flex-direction: column; gap: var(--sp-4); margin: var(--sp-3) 0; }
.dis-card { border: var(--honest-border); border-radius: var(--radius-sm); padding: var(--sp-4); }
.dis-topic { margin: 0 0 var(--sp-3); font-size: var(--fs-h4, 1.05rem); }
.dis-cols { display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--sp-3); align-items: start; }
.dis-pos { display: flex; flex-direction: column; gap: var(--sp-2); }
.dis-who { text-transform: uppercase; letter-spacing: var(--tracking-label); color: var(--ember); }
.dis-text { margin: 0; color: var(--ink-muted); line-height: 1.5; font-size: var(--fs-small); }
.dis-vs { align-self: center; font-family: var(--font-mono); color: var(--ink-faint); font-size: 0.7rem; text-transform: uppercase; }
.dis-call { margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--rule); }
.dis-call-k { text-transform: uppercase; letter-spacing: var(--tracking-label); color: var(--ember); display: block; margin-bottom: var(--sp-1); }
.dis-call .dis-text { color: var(--ink); }
@media (max-width: 760px) { .dis-cols { grid-template-columns: 1fr; } .dis-vs { justify-self: start; } }
/* ── /evidence/vitals/ — glance-first instrument panel (P0.1+) ───────────────────
   Component rings + status band. tone: ember=good, muted=neutral/forming, alert=RED STATE
   (reserved). Red never encodes a direction. */
.vs-band { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; margin: var(--sp-2) 0 var(--sp-3); padding: var(--sp-3); border-radius: var(--radius-sm); border: var(--honest-border); }
.vs-band.vs-ember { border-color: var(--ember-line); background: var(--ember-wash); }
.vs-band.vs-alert { border-color: var(--alert-line); background: var(--alert-wash); }
.vs-word { display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap; }
.vs-w { font-family: var(--font-serif, Fraunces), serif; font-size: var(--fs-h2, 1.8rem); line-height: 1; }
.vs-ember .vs-w { color: var(--ember); }
.vs-alert .vs-w { color: var(--alert); }
.vs-muted .vs-w { color: var(--ink); }
.vs-line { color: var(--ink-muted); }
.vs-stamp { color: var(--ink-faint); font-family: var(--font-mono); font-size: var(--fs-small); white-space: nowrap; }
.vr-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3); margin: var(--sp-3) 0; }
.vr { display: flex; flex-direction: column; align-items: center; gap: var(--sp-2); position: relative; }
.vr-svg { width: 100%; max-width: 92px; aspect-ratio: 1; }
.vr-track { stroke: var(--rule); stroke-width: 7; }
.vr-arc { stroke: var(--ember); stroke-width: 7; transition: stroke-dasharray 0.4s; }
.vr-muted .vr-arc { stroke: var(--ink-muted); }
.vr-alert .vr-arc { stroke: var(--alert); }
.vr-thin .vr-arc { opacity: 0.72; }
/* P0.4 — thin-data ring state: a dashed track signals the baseline is still forming, so a
   10-day fill never reads as a settled normal. Pairs with the "N days in" band stamp. */
.vr-thin .vr-track { stroke-dasharray: 3 5; opacity: 0.8; }
/* P1.2 — autonomic hero: two ember lines (HRV solid, RHR inverted dashed). Both up = recovery. */
.ah-hrv { stroke: var(--ember); stroke-width: 2.25; stroke-linejoin: round; }
.ah-rhr { stroke: var(--ember); stroke-width: 2; stroke-dasharray: 5 4; opacity: 0.8; stroke-linejoin: round; }
.ah-key { white-space: nowrap; }
.ah-sw { display: inline-block; width: 0.8em; height: 0.18em; vertical-align: middle; margin-right: 0.3em; background: var(--ember); }
.ah-sw-rhr { opacity: 0.7; height: 0; border-top: 2px dashed var(--ember); }
/* P1.3 — readiness driver bars reuse the sufficiency kit; alert tone for a run-down driver. */
.suf-fill.vd-alert { background: var(--alert); }
/* P2.1 — autonomic 2x2 */
.aq-wrap { max-width: 340px; margin: var(--sp-3) auto 0; }
.aq-chart svg { width: 100%; height: auto; }
.aq-div { stroke: var(--rule); stroke-width: 1; stroke-dasharray: 2 4; }
.aq-lab { fill: var(--ink-faint); font-family: var(--font-mono); font-size: var(--fs-aq); letter-spacing: 0.08em; } /* floored >=11px effective by svgtype.js (#1210) */
.aq-ax { fill: var(--ink-faint); font-family: var(--font-mono); font-size: var(--fs-aq); } /* floored >=11px effective by svgtype.js (#1210) */
.aq-dot { fill: var(--ink-muted); opacity: 0.6; }
.aq-dot.aq-today { fill: var(--ember); opacity: 1; }
/* P2.2 — small-multiples grid */
.sm-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-3); margin: var(--sp-3) 0; }
.sm-cell { border: 1px solid var(--rule); border-radius: var(--radius-xs); padding: var(--sp-2) var(--sp-3); }
.sm-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); }
.sm-lbl { font-weight: var(--weight-med); text-transform: lowercase; font-size: var(--fs-small); }
.sm-frame { color: var(--ink-faint); font-size: 0.56rem; text-transform: uppercase; letter-spacing: var(--tracking-label); }
.sm-cell .spark { width: 100%; height: 30px; margin: var(--sp-1) 0; }
.sm-foot { display: flex; justify-content: space-between; align-items: baseline; }
.sm-v { font-size: var(--fs-small); }
.sm-trend { color: var(--ink-muted); }
.sm-empty { color: var(--ink-faint); padding: var(--sp-3) 0; text-align: center; }
@media (max-width: 760px) { .sm-grid { grid-template-columns: repeat(2, 1fr); } }
/* P2.4 — hub links */
.vh-row { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin: var(--sp-3) 0; }
.vh-link { display: inline-block; padding: var(--sp-2) var(--sp-3); border: 1px solid var(--rule); border-radius: var(--radius-xs); text-decoration: none; color: var(--ink); font-size: var(--fs-small); }
.vh-link:hover { border-color: var(--ember-line); color: var(--ember); }
/* pointer-events:none so the center value label (pure display) never shadows the ring's
   own interactive readout (#583 data-cpts) — a hover over the middle still reaches the svg. */
.vr-c { position: absolute; top: calc(50% - 0.7em); left: 0; right: 0; display: flex; flex-direction: column; align-items: center; line-height: 1; pointer-events: none; }
.vr-v { font-size: var(--fs-h4, 1.05rem); }
.vr-ember .vr-v { color: var(--ember); }
.vr-alert .vr-v { color: var(--alert); }
.vr-sub { color: var(--ink-faint); font-size: 0.6rem; }
.vr-l { text-transform: lowercase; color: var(--ink-muted); }
@media (max-width: 600px) { .vr-row { grid-template-columns: repeat(2, 1fr); } }
/* P0.2 — now/7d/30d ladder, aligned under the ring grid. */
.vl-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3); margin-top: calc(-1 * var(--sp-2)); }
/* #1099 — reserved slot geometry: min-width:0 lets a cell shrink inside its grid
   track instead of overprinting the neighbour, and flex-wrap absorbs any value
   still too wide (absent values render the honest "—", never a collapsed cell). */
.vl-cell { display: flex; justify-content: center; gap: var(--sp-3); min-width: 0; flex-wrap: wrap; }
.vl-pair { display: flex; flex-direction: column; align-items: center; gap: 1px; min-width: 0; }
.vl-k { color: var(--ink-faint); font-size: 0.58rem; text-transform: uppercase; letter-spacing: var(--tracking-label); }
.vl-v { font-size: var(--fs-small); }
.vl-v.vl-base { color: var(--ink-muted); }
@media (max-width: 600px) { .vl-row { grid-template-columns: repeat(2, 1fr); } }
/* ── /evidence/mind/ — restraint (P0.1). NO RED anywhere on this page (the reserved-red rule
   is explicitly excluded here). Ember = held/earned; muted ink = reset/future. No alarm. */
.mr-cum { display: flex; align-items: baseline; gap: var(--sp-3); margin: var(--sp-2) 0 var(--sp-3); }
.mr-cum-v { font-size: var(--fs-display, 2.6rem); color: var(--ember); line-height: 1; }
.mr-cum-k { color: var(--ink-muted); max-width: 34ch; }
.mr-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-3) 0; }
.mr-chip { font-family: var(--font-mono); font-size: var(--fs-small); color: var(--ember); border: 1px solid var(--ember-line); border-radius: var(--radius-xs); padding: 2px 10px; }
.mr-ladder { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: baseline; margin: var(--sp-3) 0; }
.mr-rung { font-family: var(--font-mono); font-size: 0.7rem; padding: 1px 7px; border-radius: var(--radius-xs); }
.mr-rung.mr-crossed { color: var(--ember); border: 1px solid var(--ember); }
.mr-rung.mr-future { color: var(--ink-faint); border: 1px dashed var(--rule); }
.mr-reset { color: var(--ink-muted); font-style: italic; margin-top: var(--sp-2); }
/* P0.2 — the inviting absence: a held, dignified empty state, never a hollow axis. */
.mi-absence { border: 1px dashed var(--ember-line); border-radius: var(--radius-sm); padding: var(--sp-4); background: var(--ember-wash); }
.mi-lead { margin: 0; font-family: var(--font-serif, Fraunces), serif; font-size: var(--fs-h4, 1.1rem); line-height: 1.4; color: var(--ink); }
.mi-sub { margin: var(--sp-3) 0 0; color: var(--ink-muted); line-height: 1.55; }
.mi-entry { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); margin-top: var(--sp-4); }
.mi-cta { font-family: var(--font-mono); font-size: var(--fs-small); color: var(--ember); background: transparent; border: 1px solid var(--ember); border-radius: var(--radius-xs); padding: var(--sp-2) var(--sp-3); cursor: pointer; }
.mi-cta:hover { background: var(--ember-wash); }
.mi-entry-note { color: var(--ink-faint); }
/* P0.3 — Mind pillar decomposed into its inputs (anti-black-box). */
.mp-rows { display: flex; flex-direction: column; gap: 1px; margin: var(--sp-3) 0; }
.mp-row { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-3); padding: var(--sp-2) 0; border-bottom: 1px solid var(--rule); }
.mp-l { color: var(--ink); }
.mp-v { color: var(--ember); }
.mp-v.mp-await { color: var(--ink-faint); font-style: italic; }
/* P0.4 — Third Wall: the held "last word" slot (waiting, not absent). No red. */
.mw-pending { border: 1px dashed var(--ember-line); border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-4); background: var(--ember-wash); margin-top: var(--sp-3); }
.mw-who { color: var(--ember); text-transform: uppercase; letter-spacing: var(--tracking-label); display: block; margin-bottom: var(--sp-1); }
.mw-lead { margin: 0; font-family: var(--font-serif, Fraunces), serif; font-style: italic; font-size: var(--fs-quote, 1.15rem); color: var(--ink); }
.mw-sub { margin: var(--sp-2) 0 0; color: var(--ink-muted); line-height: 1.5; }
/* P0.3 — earned glyphs: lit ember on real signal, unlit (faint outline) otherwise. */
.vg-row { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-3) 0; }
.vg { display: inline-flex; align-items: center; gap: var(--sp-2); padding: 3px 10px; border-radius: var(--radius-xs); border: 1px solid var(--rule); text-decoration: none; }
.vg-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: var(--rule); }
.vg-word { font-size: var(--fs-small); text-transform: lowercase; }
.vg-val { font-size: var(--fs-small); color: var(--ink-muted); }
.vg-lit { border-color: var(--ember-line); }
.vg-lit .vg-dot { background: var(--ember); }
.vg-lit .vg-word { color: var(--ember); }
.vg-off { opacity: 0.55; border-style: dashed; }
.vg-off .vg-word { color: var(--ink-faint); }
a.vg-lit:hover { background: var(--ember-wash); }
/* #1371 — "warming up": hollow marks filling toward an instrument's real arming
   threshold (the earned-glyph vocabulary applied to a not-yet-armed instrument).
   Hollow = days still needed, lit ember = days accrued. Never red, never a spinner —
   an instrument arming, not a failure state. */
.warmup { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-3) 0; }
.wu-marks { display: inline-flex; gap: 5px; align-items: center; }
.wu-dot { width: 8px; height: 8px; border-radius: 50%; border: 1px solid var(--ink-faint); background: transparent; flex: 0 0 auto; }
.wu-dot.lit { background: var(--ember); border-color: var(--ember); }
.wu-label { color: var(--ink-muted); }
/* Compound selector: .rd-badge's border shorthand (evidence.css, loads later)
   would re-solidify a bare .wu-carried — 0,2,0 wins regardless of order. */
.wu-carried, .rd-badge.wu-carried { border-style: dashed; opacity: 0.85; }
.chart-cap { display: block; margin-top: var(--sp-2); color: var(--ink-faint); }
.chart--empty { border: var(--honest-border); border-radius: var(--radius-xs); padding: var(--sp-4); text-align: center; }
.chart--empty .chart-cap { margin: 0; }
/* Measuring-rule tick spine on a trend chart (SIGNATURE 1, P0.8) — a ticked y-axis rail
   with the max (top) / min (bottom) value. Token-driven ticks; muted ink, never red. */
.chart--spined { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-2); align-items: stretch; }
.chart--spined > svg { grid-column: 2; }
.chart--spined > .chart-cap { grid-column: 1 / -1; }
.chart-spine { grid-column: 1; align-self: stretch; min-height: clamp(110px, 8vw + 80px, 150px);
  width: var(--spine-width); border-right: var(--spine-border); background: var(--spine-ticks);
  display: flex; flex-direction: column; justify-content: space-between; padding: 2px 5px 2px 0;
  text-align: right; }
.chart-spine-v { color: var(--ink-faint); font-size: 0.6rem; line-height: 1; }
/* Serif "what this means" annotation under a chart (SIGNATURE 2 human voice). */
.nut-anno { margin-top: var(--sp-3); }
/* Designed "coming online" empty state (P1.3 hunger/energy, §8 CGM) — honest, flagged. */
.nut-coming { border: var(--honest-border); border-radius: var(--radius-sm); padding: var(--sp-4); margin-top: var(--sp-3); }
.nut-coming .rd-archive { margin: 0; }
/* Ghosted CGM curve placeholder (§8) — muted, dashed, no live data. */
.cgm-ghost .cgm-svg { width: 100%; height: 110px; display: block; opacity: 0.45; margin-bottom: var(--sp-3); }
.cgm-curve { fill: none; stroke: var(--ink-muted); stroke-width: 2; stroke-dasharray: 5 4; }
.cgm-meal { stroke: var(--ember); stroke-width: 1; stroke-dasharray: 2 3; opacity: 0.55; }
.cgm-meal-dot { fill: var(--ember); opacity: 0.55; }
/* Loss-rate readout (nutrition §1, P0.9) — rate chain + deficit-intensity flag. Ember on
   "aggressive" = attention, not alarm (never red); moderate/mild are muted ink. */
.nut-lossrate { margin-top: var(--sp-3); }
.nut-flag { display: inline-block; font-family: var(--font-mono); font-size: 0.6rem;
  text-transform: uppercase; letter-spacing: var(--tracking-label); padding: 1px 6px;
  border-radius: var(--radius-xs); border: 1px solid var(--rule); color: var(--ink-muted); white-space: nowrap; }
.nut-flag-aggressive { border-color: var(--ember); color: var(--ember); }
/* Standing-bet verdict chips (P2.1) — pending/confirmed/refuted. Refuted = muted, never red. */
.nut-flag.pj-pending { border-color: var(--rule); color: var(--ink-muted); }
.nut-flag.pj-ok { border-color: var(--ember); color: var(--ember); }
.nut-flag.pj-warn { border-color: var(--ink-muted); color: var(--ink-muted); }
.spark { width: 96px; height: 28px; vertical-align: middle; }
.spark .chart-line { stroke-width: 1.5; }
.spark--empty { display: inline-block; width: 96px; height: 1px; background: var(--rule); }
.cbars { display: flex; align-items: flex-end; gap: 3px; height: var(--cbar-h, 130px); margin-top: var(--sp-3); }
.cbar { flex: 1; display: flex; flex-direction: column; align-items: center; gap: var(--sp-1); height: 100%; justify-content: flex-end; min-width: 0; }
.cbar-fill { width: 76%; background: var(--ember); border-radius: 2px 2px 0 0; min-height: 3px; }
.cbar-l { color: var(--ink-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }

/* Stacked composition bar (macro split, etc.) — ember = primary/tracked, muted inks for rest. */
.sbar { display: flex; width: 100%; height: 14px; border-radius: var(--radius-xs); overflow: hidden; margin-top: var(--sp-3); background: var(--surface-2, var(--surface)); }
.sbar-seg { height: 100%; display: block; }
.sbar-ember { background: var(--ember); }
.sbar-ink { background: var(--ink-muted); }
.sbar-faint { background: var(--ink-faint); }
.sbar-legend { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3); }
.sbar-key { display: inline-flex; align-items: center; gap: var(--sp-1); }
.sbar-dot { width: 8px; height: 8px; border-radius: 2px; display: inline-block; }

/* Hero energy spine (nutrition §0) — a horizontal measuring-rule 0→maintenance with
   the intake + maintenance ticks and the deficit gap shaded. Reuses SIGNATURE 1. */
.nut-hero { margin-top: var(--sp-4); }
.spine-fig { margin-top: var(--sp-2); }
.hspine { position: relative; height: 58px; margin: var(--sp-6) 0 var(--sp-3); }
.hspine-rule { position: absolute; left: 0; right: 0; top: 34px; height: 7px;
  background: repeating-linear-gradient(to right, var(--tick) 0 1px, transparent 1px var(--spine-tick-gap));
  border-bottom: var(--spine-border); }
.hspine-gap { position: absolute; top: 30px; height: 15px; background: var(--ember-wash);
  border-left: 1px solid var(--ember); border-right: 1px dashed var(--ink-faint); }
.hspine-gap-surplus { background: var(--surface-2, var(--surface)); border-left: 1px dashed var(--ink-faint); }
/* Zero-width anchor at the value's true position; the tick (::after) stays centered on
   it, the label anchors inward near the edges so a far-right value never clips at 390px. */
.hspine-mark { position: absolute; top: 0; bottom: 0; width: 0; }
.hspine-mark::after { content: ""; position: absolute; top: 30px; left: 0; transform: translateX(-50%);
  width: 1px; height: 20px; background: var(--ink-muted); }
.hspine-lab { position: absolute; bottom: 26px; display: flex; flex-direction: column; gap: 2px;
  white-space: nowrap; max-width: 9rem; }
.hspine-c .hspine-lab { left: 0; transform: translateX(-50%); align-items: center; }
.hspine-l .hspine-lab { left: 0; align-items: flex-start; }
.hspine-r .hspine-lab { right: 0; align-items: flex-end; }
.hspine-intake { color: var(--ember); }
.hspine-intake::after { background: var(--ember); }
.hspine-v { font-size: var(--fs-data-lg); line-height: 1; }
.hspine-k { color: var(--ink-faint); }
/* targetSpine only (Zone-2 etc.): taller rail + smaller labels, value ABOVE the rule and
   target BELOW — vertical stagger that prevents the two endpoint labels colliding when
   value≈target horizontally (the "150 min210 min" overlap). Scoped so the nutrition spine
   is untouched. */
.spine-fig--targets .hspine { height: 88px; }
.spine-fig--targets .hspine-rule { top: 42px; }
.spine-fig--targets .hspine-mark::after { top: 38px; height: 22px; }
.spine-fig--targets .hspine-v { font-size: var(--fs-h4, 1.05rem); }
.spine-fig--targets .hspine-lab { bottom: auto; top: 6px; }
.spine-fig--targets .hspine-lab.hspine-lab-below { top: 62px; }

/* Sufficiency bars (nutrient intake vs target, P0.4) — horizontal 0→100%, worst-first.
   Value in its own grid column so it never clips the right edge; the track's right edge
   is the 100% target rule. Ember = worst offenders short of the floor, NOT a win. */
.suf-head { margin-top: var(--sp-3); color: var(--ink-faint); }
.suf-rows { display: flex; flex-direction: column; gap: var(--sp-2); margin-top: var(--sp-2); }
.suf-row { display: grid; grid-template-columns: minmax(4.5rem, 7.5rem) 1fr auto; align-items: center; gap: var(--sp-2) var(--sp-3); }
.suf-l { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-muted); }
.suf-track { position: relative; height: 8px; border-radius: 4px;
  background: var(--surface-2, var(--surface)); border-right: 1px solid var(--ink-faint); }
.suf-fill { display: block; height: 100%; border-radius: 4px; }
.suf-ember { background: var(--ember); }
.suf-ink { background: var(--ink-muted); }
.suf-v { color: var(--ink-faint); white-space: nowrap; font-size: var(--fs-label, 0.78rem); }
.suf-amt { color: var(--ink-faint); margin-left: var(--sp-2); }
@media (max-width: 600px) { .suf-amt { display: none; } .suf-row { grid-template-columns: minmax(4rem, 6rem) 1fr auto; } }

/* Autonomic 2×2 grid (#414, RQA-06) — the COMPUTED nervous-system view (Flow/Stress/
   Recovery/Burnout), a plain-HTML 2×2 distinct from the vitals page's strain-vs-recovery
   SVG. The current cell earns an ember rule; the rest carry their 7-day count. */
.aqg { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); margin-top: var(--sp-3); }
.aqg-cell { display: grid; gap: var(--sp-1); padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--ink-faint); border-radius: var(--radius, 8px); background: var(--surface-2, var(--surface)); }
.aqg-cell.is-current { border-color: var(--ember); box-shadow: inset 0 0 0 1px var(--ember); }
.aqg-name { font-family: var(--font-mono); font-weight: var(--weight-med); letter-spacing: 0.02em; }
.aqg-cell.is-current .aqg-name { color: var(--ember); }
.aqg-n { color: var(--ink-faint); }
.aqg-here { color: var(--ember); }
.aq-spark { margin-top: var(--sp-3); }
.aq-spark .spark { width: 100%; height: 44px; }

/* Zone-2 distribution bars (#414, RQA-07) — where cardio time landed across 5 HR zones.
   Zone 2 (the longevity zone) is the ember-highlighted row; the rest are muted ink. */
.zdist { display: flex; flex-direction: column; gap: var(--sp-2); margin-top: var(--sp-3); }
.zdist-row { display: grid; grid-template-columns: minmax(7rem, 11rem) 1fr auto; align-items: center; gap: var(--sp-2) var(--sp-3); }
.zdist-l { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-muted); }
.zdist-track { position: relative; height: 8px; border-radius: 4px; background: var(--surface-2, var(--surface));
  border: 1px solid var(--ink-faint); overflow: hidden; }
.zdist-fill { display: block; height: 100%; background: var(--ink-muted); border-radius: 4px; }
.zdist-fill.is-z2 { background: var(--ember); }
.zdist-v { color: var(--ink-faint); white-space: nowrap; font-size: var(--fs-label, 0.78rem); }
.zsport { list-style: none; padding: 0; margin: var(--sp-3) 0 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.zsport li { display: flex; justify-content: space-between; gap: var(--sp-3); border-bottom: 1px solid var(--ink-faint); padding-bottom: var(--sp-2); }
.zsport .mono { color: var(--ink-faint); }
@media (max-width: 600px) { .zdist-row { grid-template-columns: minmax(5.5rem, 8rem) 1fr auto; } }

/* Protein lead (nutrition §2, P0.2) — the failing metric weighted as the headline.
   Ember = attention/warning when under floor (giant figure + ▼ + "under floor"),
   NEVER an ember "win" block for a miss (HARD RULE 3). */
.nut-lead { margin-top: var(--sp-5); display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--sp-1) var(--sp-4); }
.nut-lead .lead-fig { display: flex; flex-direction: column; gap: var(--sp-1); }
.nut-lead .lead-v { font-family: var(--font-mono); font-weight: var(--weight-med);
  font-size: clamp(2.4rem, 1.4rem + 5vw, 3.6rem); line-height: 1; }
.nut-lead .lead-k { color: var(--ink-faint); }
.nut-lead .lead-sub { color: var(--ink-muted); }
.nut-lead.lead-warn .lead-v { color: var(--ember); }
.nut-lead.lead-warn .lead-k { color: var(--ember); }
.nut-lead.lead-warn .lead-k::before { content: "▼ "; }
.nut-lead.lead-ok .lead-v { color: var(--ink); }

/* Staleness-honest as-of line (truth audit 2026-07-10) — a stalled manual log reads
   as a calm ember-edged statement, never red panic. */
.nut-asof.nut-stalled { color: var(--ember); border-left: 2px solid var(--ember); padding-left: var(--sp-3); }

/* Two-voice verdict (SIGNATURE 2) — mono states, serif judges. */
.two-voice { margin-top: var(--sp-4); display: grid; gap: var(--sp-2);
  border-left: 2px solid var(--ember); padding-left: var(--sp-4); }
.tv-machine { font-family: var(--voice-machine-font); font-size: var(--voice-machine-size);
  line-height: var(--voice-machine-lh); color: var(--voice-machine-ink); }
.tv-mark { color: var(--voice-machine-mark); }
.tv-human { font-family: var(--voice-human-font); font-style: var(--voice-human-style);
  font-size: var(--voice-human-size); line-height: var(--voice-human-lh); color: var(--voice-human-ink);
  max-width: var(--measure); }

/* Eating-window ribbon (nutrition §4, P1.1) — ember = actual window, faint = 16:8 ref. */
.ewin-rows { display: flex; flex-direction: column; gap: var(--sp-2); margin-top: var(--sp-2); }
.ewin-row { display: grid; grid-template-columns: minmax(3rem, 4rem) 1fr auto; align-items: center; gap: var(--sp-2) var(--sp-3); }
.ewin-day { color: var(--ink-muted); white-space: nowrap; }
.ewin-track { position: relative; height: 12px; border-radius: 3px; background: var(--surface-2, var(--surface)); }
.ewin-ref { position: absolute; top: 0; height: 100%; background: var(--ink-faint); opacity: 0.35; border-radius: 3px; }
.ewin-bar { position: absolute; top: 0; height: 100%; background: var(--ember); border-radius: 3px; }
.ewin-v { color: var(--ink-faint); white-space: nowrap; font-size: var(--fs-label, 0.78rem); }
@media (max-width: 600px) { .ewin-v { font-size: 0.6rem; } .ewin-row { grid-template-columns: minmax(2.5rem, 3.5rem) 1fr auto; } }

/* Per-day macro composition columns by energy (nutrition §3, P0.7) — ember = protein
   (the floor), muted inks for carbs/fat. Refuses < 4 points in the chart kit. */
.scols { display: flex; align-items: flex-end; gap: 4px; height: 150px; margin-top: var(--sp-3); }
.scol { flex: 1; min-width: 0; height: 100%; display: flex; flex-direction: column;
  align-items: center; gap: var(--sp-1); justify-content: flex-end; }
.scol-stack { width: 72%; height: 100%; display: flex; flex-direction: column;
  justify-content: flex-end; border-radius: 2px 2px 0 0; overflow: hidden; }
.scol-seg { width: 100%; display: block; }
/* Modality ramp (training §3, P0.7) — ONE hue: lift = ember, walk/cardio = ember tint,
   mobility = muted ink. seg-<tone> for stackedDayColumns + the legend dots. */
.seg-lift { background: var(--ember); }
.seg-cardio { background: color-mix(in oklch, var(--ember) 52%, var(--surface)); }
.seg-mob { background: var(--ink-muted); }
.sbar-dot.seg-lift, .sbar-dot.seg-cardio, .sbar-dot.seg-mob { border-radius: 2px; }
.scol-l { color: var(--ink-faint); font-size: 0.6rem; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; max-width: 100%; }

/* Correlation chips — strength by bar width, sign by direction word (correlative, N=1). */
.corr-rows { display: flex; flex-direction: column; gap: var(--sp-2); margin-top: var(--sp-3); }
.corr-row { display: grid; grid-template-columns: minmax(0,1fr) 90px auto; align-items: center; gap: var(--sp-2); }
.corr-l { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.corr-bar { height: 6px; border-radius: 3px; background: var(--surface-2, var(--surface)); overflow: hidden; }
.corr-fill { display: block; height: 100%; }
.corr-r { color: var(--ink-faint); font-size: var(--fs-label, 0.78rem); white-space: nowrap; }

/* Accountability ledger — cause cards (shown even at $0 so the rules + personality read). */
.cause-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--sp-3); margin-top: var(--sp-3); }
.cause-card { border: var(--honest-border); border-radius: var(--radius-sm); padding: var(--sp-3); display: flex; flex-direction: column; gap: var(--sp-1); }
.cause-name { font-weight: 600; color: var(--ink); text-decoration: none; }
.cause-name:hover { color: var(--ember); }
.cause-desc { color: var(--ink-faint); }
.cause-why { color: var(--ink-muted); font-style: italic; margin: var(--sp-1) 0 0; font-size: 0.95em; }
.cause-amt { color: var(--ink-faint); margin-top: var(--sp-1); }

/* ── Shared footer mega-menu (CC-05) — on all three doors ─────────────────── */
.site-foot { border-top: var(--border-hair); margin-top: var(--sp-7); padding: var(--sp-6) var(--gutter) var(--sp-5); }
.site-foot-cols { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-5); max-width: var(--container); margin-inline: auto; }
@media (min-width: 761px) { .site-foot-cols { grid-template-columns: repeat(4, 1fr); } }
.sf-col { display: grid; gap: var(--sp-2); align-content: start; }
.sf-col a { font-size: var(--fs-small); color: var(--ink-muted); text-decoration: none; }
.sf-col a:hover { color: var(--ember); }
.sf-h { color: var(--ember); margin-bottom: var(--sp-1); }
.sf-base { max-width: var(--container); margin: var(--sp-5) auto 0; display: flex; justify-content: space-between; gap: var(--sp-3); color: var(--ink-faint); }
.sf-base a { color: var(--ink-faint); text-decoration: none; }
.sf-base a:hover { color: var(--ember); }

/* ── Coach-name popover (CC-04) — progressive disclosure in reader prose ───── */
.coach-chip { font: inherit; color: var(--ember); background: none; border: none; padding: 0; cursor: pointer;
  border-bottom: 1px dotted var(--ember-line); }
.coach-chip:hover { border-bottom-style: solid; }
.coach-chip:focus-visible { border-bottom-style: solid; outline: 2px solid var(--ember); outline-offset: 2px; border-radius: var(--radius-xs); }
.coach-pop { position: absolute; z-index: var(--z-overlay, 60); max-width: 280px; padding: var(--sp-3) var(--sp-4);
  background: var(--surface-raised); border: var(--border-hair); border-radius: var(--radius-sm);
  box-shadow: 0 8px 28px rgba(0,0,0,0.18); }
.cp-name { font-family: var(--font-mono); font-size: var(--fs-small); color: var(--ink); }
.cp-role { color: var(--ember); margin-top: 2px; }
.cp-bio { margin-top: var(--sp-2); font-size: var(--fs-small); color: var(--ink-muted); line-height: var(--lh-relaxed); }
.cp-link { display: inline-block; margin-top: var(--sp-2); font-family: var(--font-mono); font-size: var(--fs-label);
  color: var(--ember); text-decoration: none; border-bottom: 1px solid var(--ember-line); }
.cp-link:hover { border-color: var(--ember); }

/* ── Shared top-bar chrome (#1105) — ONE rule for every door's desktop top bar.
   Stickiness, padding, translucent blur backdrop and the hairline rule were
   previously three diverging per-sheet rules (.story-top sticky+blur, .ev-top and
   .cockpit-top static, container-boxed, different padding). Normalized on the
   Story treatment: sticky, full-bleed, --sp-3/--gutter padding; doors right-align
   via space-between on every page (the cockpit's scope switcher moved to its own
   row — cockpit.css .top-controls). Lives in @layer chrome-base so the mobile
   app-bar (@layer chrome, below) keeps winning at ≤600 by layer order — no
   !important (#1007). Page sheets keep only page-specific extras. */
@layer chrome-base {
  .story-top, .ev-top, .cockpit-top {
    position: sticky; top: 0; z-index: var(--z-sticky);
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--sp-4); padding: var(--sp-3) var(--gutter);
    background: color-mix(in oklch, var(--page) 82%, transparent);
    backdrop-filter: blur(10px);
    border-bottom: var(--border-hair);
  }
  @media (max-width: 480px) {
    .story-top, .ev-top, .cockpit-top { padding: var(--sp-3) var(--sp-4); gap: var(--sp-3); }
  }
}
/* Fragment jumps + scrollIntoView clear the now-sticky bar on every page. */
html { scroll-padding-top: calc(var(--sp-7) + var(--sp-2)); }

/* ── Mobile: the five doors become a fixed, thumb-reachable bottom app-bar
   (2026-06-14; five doors since the v5 IA). Replaces the old top-nav shrink-to-9px.
   Lives in @layer chrome so it wins over the per-door desktop nav (@layer chrome-base)
   by LAYER ORDER — no !important, no specificity war. Load order no longer matters:
   the layer order is fixed by the @layer statement at the top of this file. (#1007) */
@layer chrome {
  @media (max-width: 600px) {
    /* A backdrop-filter / transform / filter ancestor creates a containing block that
       traps position:fixed children — that pinned the door-bar to the .story-top bar
       (top) on Story/Home while it sat at the viewport bottom on Evidence/Cockpit.
       Neutralize those on the top bars at mobile width so the bar is ALWAYS bottom.
       (Load-bearing — see the mobile PWA memory. Only .story-top actually sets a
       backdrop-filter; the rest are defensive. No !important needed: this layer beats
       chrome-base, where every top-bar rule now lives.) */
    .story-top, .ev-top, .cockpit-top {
      backdrop-filter: none; -webkit-backdrop-filter: none;
      transform: none; filter: none; perspective: none;
    }
    .doors {
      position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
      display: flex; justify-content: space-around; align-items: stretch;
      gap: 0;
      padding: 0 .25rem env(safe-area-inset-bottom);
      background: var(--surface-raised, #14110b);
      border-top: 1px solid var(--ember-line, rgba(140,120,90,.28));
      box-shadow: 0 -2px 14px rgba(0,0,0,.28);
    }
    /* :not(.nav-follow) keeps the follow pill out of the flex rule so it stays hidden
       (the A2/#1003 collision). Icon-over-label column so the longest word ("protocols")
       gets the full door width; min-width:0 lets the 5 doors + toggle fit 360px. */
    .doors a:not(.nav-follow) {
      flex: 1 1 0; display: flex; flex-direction: column; gap: 2px;
      align-items: center; justify-content: center;
      min-width: 0; min-height: 52px; padding: .5rem 0;
      font-size: .66rem; letter-spacing: .02em; text-align: center;
    }
    .doors a:not(.nav-follow) .ico-door { margin: 0 0 1px 0; }
    .doors a[aria-current="page"] { border-bottom: 0; color: var(--ember); }
    /* The theme toggle is a <button>, so the door flex rule never sized it — reserve its
       own space (don't grow) and center it. (44px tap-target work is #1010.) */
    .doors .theme-toggle { flex: 0 0 auto; align-self: center; margin: 0 .15rem; }
    .nav-follow { display: none; }
    /* content clears the fixed bar (incl. iOS home-indicator inset); the icon-over-label
       column doors make the bar ~66px tall, so clearance matches. */
    body { padding-bottom: calc(68px + env(safe-area-inset-bottom)); }
  }
}

/* ── Follow link — desktop nav affordance; hidden on mobile (bottom bar has no room) ── */
.nav-follow {
  font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-faint); text-decoration: none;
  border: 1px solid var(--ember-line, rgba(140,120,90,.28));
  border-radius: 4px; padding: .1em .5em .15em; white-space: nowrap;
}
.nav-follow:hover { color: var(--ember); border-color: var(--ember); }
/* nav-follow is hidden on mobile inside @layer chrome above (no !important needed). */

/* ════════════════════════════════════════════════════════════════════════════
   TAP-TARGET FLOOR (#1010) — 44px effective hit areas on touch
   ════════════════════════════════════════════════════════════════════════════
   Interactive controls the mobile review measured below the 44px floor. Expanded
   WITHOUT visual redesign: an isolated icon/button gets a transparent, centered
   ::after overlay that enlarges its clickable region while the visible mark is
   unchanged; a form control gets min-height; a checkbox is tapped via its wrapping
   <label> (pseudo-elements don't render on replaced inputs). Documented in the
   DESIGN_SYSTEM_V5 mobile section (#1011); the visual_qa tap-target audit (#1013)
   reports any future regressions. Only on touch-width so desktop hover is untouched. */
@media (max-width: 600px) {
  /* Isolated icon/button controls — invisible hit overlay, visual unchanged. Expands
     VERTICALLY to 44px (width stays 100% of the control) so the overlay never pushes
     past the viewport — the app-bar toggle sits at the right edge, and a horizontal
     overlay there would overflow the fixed bar. The intro "×" gets the same. */
  .theme-toggle, .cockpit-intro__x, .ev-intro__x { position: relative; }
  .theme-toggle::after, .cockpit-intro__x::after, .ev-intro__x::after {
    content: ""; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 100%; height: max(100%, 44px);
  }
  /* Form controls — the time-travel scrubber (the cockpit's headline interaction) and
     the day/scrub selects: a taller box = a thumb-draggable/tappable target. */
  .scrub, .scrub-days, .df-scrub { min-height: 44px; }
  /* Text buttons ("got it" / "place me" / dismiss-go) — pad to the floor. */
  .cockpit-intro__go, .ev-intro__go, .okay-cta, .place-btn { min-height: 44px; padding-block: 11px; }
  /* Checkbox: tap the whole label row (the input itself is a 13px replaced element). */
  .cv-all { min-height: 44px; display: inline-flex; align-items: center; gap: var(--sp-2); }
  .cv-all input[type="checkbox"] { width: 20px; height: 20px; }
  /* Inline evidence-link pills + breadcrumbs — vertical padding extends the tap area
     without shifting the line box (inline padding doesn't reflow prose), so the visual
     stays put but the thumb target grows. Not an inline-flex 44px block (that reads as
     a broken gap in prose) and not an overlay (that would swallow taps on adjacent text). */
  .supp-ev-link, [data-crumb] a, .dx-crumb a { padding-block: 12px; }
  /* Waveform day-bars (#1249) — each scored bar is an anchor into that day's cockpit,
     but flex:1 / min-width:2px shrinks them toward ~2px wide (and low-score bars to
     ~13px tall) as the cycle fills. Same vertical-only ::after grammar as the app-bar
     toggle: a transparent overlay lifts the TOUCH height to the 44px floor while the
     thin visible bar is unchanged. Anchored to the bar's bottom so it grows UP into the
     empty waveform above (never spills below into the legend) and its width stays the
     bar's — a dense ~90-day strip can't hold 90 × 44px-wide targets, and motion.js's
     nearest-hit readout already mediates horizontal selection. Out-of-flow + transparent
     ⇒ no visual change, no layout shift. Only the navigable a.bar anchors, not the
     non-scored .bar spans. */
  .wave a.bar { position: relative; }
  .wave a.bar::after {
    content: ""; position: absolute; left: 0; bottom: 0;
    width: 100%; height: max(100%, 44px);
  }
}

/* Build stamp — muted deploy fingerprint (git SHA), shown on machine-facing footers. */
/* muted, but NOT below AA: --ink-faint is AA-tuned (4.9:1 on paper); the old opacity:0.6 dropped it to 2.34. */
.build-stamp { display: block; margin-top: var(--sp-2); font-family: var(--font-mono); font-size: 11px; color: var(--ink-faint); }

/* ════════════════════════════════════════════════════════════════════════════
   11. SHARED CONTENT & PAGE KIT — v5 "Coherence" (docs/DESIGN_SYSTEM_V5.md)
   The pieces that make every door read as one package. Additive: opt a page in
   by adding the class — nothing changes until adopted.
   ════════════════════════════════════════════════════════════════════════════ */

/* ── .prose — the ONE wrapper for all injected / long-form / AI-generated HTML
   (chronicle posts, coach reads, evidence readouts, journal, about pages).
   Apply to the CONTAINER; it pins the triad's jobs on every descendant so
   model-emitted <h2>/<ul>/<blockquote> render identically everywhere. This is
   the fix for "fonts all over the place" — replaces ad-hoc per-section type. */
.prose { color: var(--ink); font-size: var(--fs-body-lg); line-height: var(--lh-relaxed); max-width: var(--measure); }
.prose > :first-child { margin-top: 0; }
.prose > * + * { margin-top: var(--sp-4); }
.prose h2 { font-family: var(--font-serif); font-weight: var(--weight-med); font-size: var(--fs-h2); line-height: var(--lh-snug); margin-top: var(--sp-6); }
.prose h3 { font-family: var(--font-serif); font-weight: var(--weight-med); font-size: var(--fs-h3); line-height: var(--lh-snug); margin-top: var(--sp-5); }
.prose h4 { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--ink-faint); margin-top: var(--sp-5); }
.prose p { max-width: var(--measure); }
.prose a { color: var(--ember); text-decoration: none; border-bottom: 1px solid var(--ember-line); }
.prose a:hover { border-color: var(--ember); }
.prose strong, .prose b { font-weight: var(--weight-med); color: var(--ink); }
.prose em, .prose i { font-style: italic; }
.prose ul, .prose ol { padding-left: 1.3em; max-width: var(--measure); }
.prose li { line-height: var(--lh-relaxed); }
.prose li + li { margin-top: var(--sp-2); }
.prose blockquote { font-family: var(--font-serif); font-style: italic; font-size: var(--fs-quote); line-height: 1.5; color: var(--ink); border-left: 2px solid var(--ember); padding-left: var(--sp-4); }
.prose code, .prose kbd, .prose samp { font-family: var(--font-mono); font-size: 0.9em; }
.prose hr { border: 0; border-top: var(--honest-border); margin: var(--sp-6) 0; }
.prose figure, .prose img { max-width: 100%; }
.prose small, .prose .prose-meta { color: var(--ink-faint); font-size: var(--fs-small); }
/* compact variant for sidebars / cards / disclosed sub-sections */
.prose.prose-sm { font-size: var(--fs-body); }
.prose.prose-sm > * + * { margin-top: var(--sp-3); }

/* ── Responsive table primitive (#1008) ───────────────────────────────────────
   ONE shared pattern for wide data tables: on a narrow viewport a table keeps its
   natural column widths and scrolls INSIDE its own box (a horizontal swipe strip)
   instead of squeezing columns until headers truncate ("FLAG" → "FL") or blowing
   the page out. The block-scroll trick needs no wrapper element — set on the
   <table> itself; rows keep their table layout, columns stay aligned.

   Two ways to use it:
     • add class="table-scroll" to any <table> (or a wrapping <div>), OR
     • use the shared .rd-tbl readout-table class — it inherits this behaviour at
       ≤ --bp-tw (820) automatically (see the readout-table rule in evidence.css).
   Desktop is untouched: full column layout, no scrollbar, until the tablet width.
   Documented in the mobile section of docs/DESIGN_SYSTEM_V5.md (#1011). */
.table-scroll {
  display: block; max-width: 100%; width: 100%;
  overflow-x: auto; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain;
}

/* ── .trust-doc — the static skeptic's contract on /method/ (#731). Container
   geometry only; all typography comes from .prose. Deliberately server-rendered
   static HTML — it must read with JS off, so it never depends on evidence.js. */
.trust-doc { max-width: var(--container); margin-inline: auto; padding: 0 var(--gutter) var(--sp-7); }
.trust-doc .prose { border-top: var(--border-hair); padding-top: var(--sp-6); }

/* ── .page-hero — the "which part of the loop am I" banner. Identical on all
   five doors so a visitor always orients the same way. */
.page-hero { max-width: var(--container); margin-inline: auto; padding: var(--sp-7) var(--gutter) var(--sp-5); }
.page-hero .ph-kicker { color: var(--ember); }
.page-hero .ph-title { font-family: var(--font-serif); font-weight: var(--weight-med); font-size: var(--fs-h1); line-height: var(--lh-tight); margin-top: var(--sp-3); }
.page-hero .ph-promise { margin-top: var(--sp-3); color: var(--ink-muted); font-size: var(--fs-body-lg); line-height: var(--lh-relaxed); max-width: var(--measure); }

/* ── The loop ribbon — the platform's spine made literal: where you are in
   Data → Coaching → Protocols → Story. Lives in the page-hero and the footer
   so the through-line is on every page. The current door is marked ember. */
.loop-ribbon { margin-top: var(--sp-4); display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center;
  font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--ink-faint); }
.loop-ribbon a { color: var(--ink-faint); text-decoration: none; }
.loop-ribbon a:hover { color: var(--ink-muted); }
.loop-ribbon .lr-here { color: var(--ember); border-bottom: 1px solid var(--ember); }
.loop-ribbon .lr-arrow { color: var(--ember); opacity: 0.55; }
.loop-ribbon .lr-sep { color: var(--ink-faint); opacity: 0.7; margin-inline-start: calc(-1 * var(--sp-1)); }

/* ── The loop diagram — the full causal-loop card row (Home + About). */
.loop { display: flex; flex-wrap: wrap; align-items: stretch; gap: var(--sp-2); margin: var(--sp-5) 0; }
.loop-node { flex: 1 1 9rem; min-width: 8rem; border: var(--border-hair); border-radius: var(--radius-sm);
  padding: var(--sp-4); text-decoration: none; color: var(--ink); display: flex; flex-direction: column; gap: var(--sp-1); }
.loop-node:hover { border-color: var(--ember-line); }
.loop-node .ln-name { font-family: var(--font-serif); font-size: var(--fs-h3); line-height: 1.1; }
.loop-node .ln-role { color: var(--ink-muted); font-size: var(--fs-small); line-height: 1.45; }
.loop-arrow { align-self: center; color: var(--ember); font-family: var(--font-mono); flex: 0 0 auto; }
@media (max-width: 760px) { .loop { flex-direction: column; } .loop-arrow { transform: rotate(90deg); } }

/* ── .provenance — every number says where it came from and how fresh. The
   "elite / trustworthy" signal, standardized so it's free on every readout. */
.provenance { margin-top: var(--sp-2); font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-faint); display: inline-flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3); }
.provenance .pv-src { color: var(--ink-muted); }
.provenance .pv-stale { color: var(--ember); }

/* ── .tabset — accessible tab strip (Coaching profiles, Protocols sub-sections).
   Pair buttons (role=tab, aria-selected) with .tabpanel regions. */
.tabset { display: flex; flex-wrap: wrap; gap: var(--sp-1); border-bottom: var(--border-hair); margin-bottom: var(--sp-5); }
.tabset .tab { appearance: none; background: transparent; border: 0; border-bottom: 2px solid transparent; cursor: pointer;
  padding: var(--sp-2) var(--sp-3); margin-bottom: -1px; font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--ink-faint); white-space: nowrap; }
.tabset .tab:hover { color: var(--ink-muted); }
.tabset .tab[aria-selected="true"] { color: var(--ember); border-bottom-color: var(--ember); }
.tabset .tab:focus-visible { outline: 2px solid var(--ember); outline-offset: 2px; border-radius: var(--radius-xs); }
.tabpanel[hidden] { display: none; }

/* ════════════════════════════════════════════════════════════════════════════
   12. MOTION LAYER — v5 "alive". Purposeful, restrained, reduced-motion-aware.
   The hidden state is gated on html.mo (set by a tiny head script ONLY when
   motion is safe) so if motion.js never runs, a failsafe removes .mo and all
   content shows — motion fails OPEN, never hides content.
   ════════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: no-preference) {
  /* NB: .rd-card was removed from this reveal set (2026-07-02). Those are the
     per-coach read cards on Coaching's landing tab (+ protocol cards), injected on
     every tab render — opacity-gating them left the flagship AI digest reading as
     DEAD AIR in any non-scrolling context (the heading isn't gated; the cards were).
     Content over entrance: they paint immediately.
     .page-hero was removed too (2026-07-05, #588): it's the always-above-fold
     orientation banner + the cross-document View-Transition morph target — capturing
     it mid-fade flashed a blank hero on arrival. It paints instantly. */
  html.mo .hero, html.mo .ev-head, html.mo .dx-head, html.mo .beat,
  html.mo .loop, html.mo .rd-sec, html.mo .two-voice, html.mo .coach-daily, html.mo .coach-progress,
  html.mo .coach-report, html.mo .coach-stance, html.mo .team-lead, html.mo .team-focus,
  html.mo .team-tension, html.mo .team-huddle, html.mo .supp, html.mo .cap-card,
  html.mo .vr-row, html.mo .figs, html.mo .ml-ladder {
    opacity: 0; transform: translateY(16px);
    transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out);
  }
  html.mo .is-in { opacity: 1 !important; transform: none !important; }

  /* Hover lifts — pure CSS, zero risk, no flash. */
  .loop-node, .vh-link, .dx-item, .ev-tile, .supp { transition: transform 0.18s var(--ease-out), border-color 0.18s var(--ease-out); }
  .loop-node:hover, .ev-tile:hover { transform: translateY(-2px); }

  /* The pillar constellation drifts — DETERMINISTIC per-node seeded motion (#590):
     each node's --dx/--dy/--dur/--ddelay come from a stable hash of the pillar name
     (story.js driftVars → same seed → same motion, the sigils.js grammar). This whole
     block is inside prefers-reduced-motion: no-preference, so reduced-motion is static. */
  .constellation .node { animation: cn-drift var(--dur, 7s) var(--ease-inout) infinite; animation-delay: var(--ddelay, 0s); }
  @keyframes cn-drift { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(var(--dx, 0), var(--dy, 0)); } }

  /* The loop flows — arrows pulse Data → Coaching → Protocols → Story in sequence,
     so the site's core thesis reads as a living cycle, not a static diagram. */
  .loop > .loop-arrow { animation: mo-flow 2.6s var(--ease-inout) infinite; }
  .loop > .loop-arrow:nth-child(4) { animation-delay: 0.35s; }
  .loop > .loop-arrow:nth-child(6) { animation-delay: 0.7s; }
  @keyframes mo-flow { 0%, 100% { opacity: 0.4; } 45% { opacity: 1; transform: translateX(2px); } }
}

/* ════════════════════════════════════════════════════════════════════════════
   12b. MOTION v2 (#588) — cross-document View Transitions. The site navigates as
   ONE continuous instrument: the loop-ribbon persists and its ember "you are here"
   marker slides to the new door while page content cross-fades — the causal loop
   made continuous, not re-drawn each page. Pure progressive enhancement: a browser
   without @view-transition just navigates, zero fallback cost. All of it is off
   under reduced-motion (the swap stays instant; the cross-fade + morph drop).
   The service worker enables NO navigation preload and returns a clean fetch() for
   navigations, so it composes with cross-document transitions unchanged.
   ════════════════════════════════════════════════════════════════════════════ */
@view-transition { navigation: auto; }

/* Shared elements that persist across a door navigation. Each name is unique per
   document (one brand, one ribbon per page), which is what a VT name requires. */
.brand { view-transition-name: site-brand; }
.loop-ribbon { view-transition-name: loop-ribbon; }

@media (prefers-reduced-motion: no-preference) {
  /* The ember marker settles as the new door arrives — a half-second confirm of
     "you landed here," on top of the ribbon morph. Opacity only, so it never
     shifts layout or fights the transition. */
  .loop-ribbon .lr-here { animation: lr-settle 0.5s var(--ease-out) both; }
  @keyframes lr-settle { from { opacity: 0.35; } to { opacity: 1; } }
}

/* Reduced-motion: keep the instant page swap, drop the cross-fade + morph. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

/* ════════════════════════════════════════════════════════════════════════════
   12c. FRESHNESS PULSE (#589) — the epic's no-fake-liveness rule made a reusable
   primitive: the ONLY motion on a "live" signal is motion tied to a REAL
   timestamp. Any element carrying data-fresh-ts (an ISO instant) + data-fresh-
   window (seconds — the SOURCE'S OWN freshness window, sourced from
   source_registry.py via /api/source_freshness or /api/last_sync, never a
   guessed constant) gets .fr-live from motion.js's wireFreshness() ONLY while
   now − ts is inside that window. Past the window nothing here applies — the
   element just carries whatever page-level "stale" treatment it already has
   (e.g. the existing .provenance .pv-stale below), motionless. Reduced-motion:
   .fr-live still sets color, the keyframe itself never runs — state reads
   through color/text alone, exactly like every other stale rendering.
   ════════════════════════════════════════════════════════════════════════════ */
.fr-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--ink-faint); margin-right: 4px; vertical-align: 1px; }
[data-fresh-ts].fr-live { color: var(--ember); }
.fr-dot.fr-live, .fr-live .fr-dot { background: var(--ember); }
@media (prefers-reduced-motion: no-preference) {
  [data-fresh-ts].fr-live { animation: fr-pulse 2.6s var(--ease-inout) infinite; }
  @keyframes fr-pulse { 0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--ember) 45%, transparent); } 50% { box-shadow: 0 0 0 4px transparent; } }
}

/* ════════════════════════════════════════════════════════════════════════════
   13. ICONOGRAPHY — the line-icon set (icons.js + /assets/icons/icons.svg) and the
   generative coach sigils (sigils.js). Stroke-based, currentColor, no fills/gloss —
   an instrument set, never decoration. Icons inherit their host element's `color`.
   ════════════════════════════════════════════════════════════════════════════ */

/* The icon primitive. Sizes to surrounding text (1em); explicitly NOT width:100%,
   so an icon dropped inside a .chart figure won't stretch to the chart's width. */
.ico {
  inline-size: 1em;
  block-size: 1em;
  flex: none;
  vertical-align: -0.125em;
  color: inherit;            /* stroke=currentColor in the sprite resolves to this */
}
/* Domain/kicker icons read clearly (muted, not faint); ember when their row is live. */
.kicker-ico { color: var(--ink-muted); margin-inline-end: 0.45em; }
.ico-door   { color: var(--ink-muted); margin-inline-end: 0.45em; inline-size: 1.15em; block-size: 1.15em; }
.doors a:hover .ico-door { color: var(--ink); }
.doors a[aria-current="page"] .ico-door { color: var(--ember); }
/* A pillar/domain mark beside a serif or label heading. */
.dom-ico { color: var(--ink-muted); margin-inline-end: 0.45em; vertical-align: -0.14em; }
/* The page-title / lead domain mark — ember, a deliberate identity accent. */
.dom-ico-lead { color: var(--ember); inline-size: 1.05em; block-size: 1.05em; }
/* A winning (up) pillar's domain icon goes ember — earned, information-bearing. */
.row.up .dom-ico { color: var(--ember); }

/* ── Coach sigils — a deterministic geometric mark per persona (sigils.js).
   Colour comes from the existing per-coach --coach channel (the sanctioned
   persona-identity exception to one-ember); falls back to ember. */
.sigil { display: block; inline-size: 100%; block-size: 100%; color: var(--coach, var(--ember)); overflow: visible; }
.sigil [stroke] { stroke: currentColor; }
.sigil-node { fill: currentColor; stroke: none; }
.sigil-ring { opacity: 0.95; }
.sigil-tick { opacity: 0.8; }
/* Inside the existing 34px coach badge, the sigil fills more of it for presence. */
.coach-badge .sigil { inline-size: 36px; block-size: 36px; }
/* A larger sigil for coach detail headers / the team roster. */
.sigil-lg { inline-size: 58px; block-size: 58px; flex: none; color: var(--coach, var(--ember)); }
/* Mid-size sigil for list/card surfaces (visual uplevel P1) — at 16-27px the
   distinguishing geometry vanished and every coach read as an interchangeable
   colored dot; 40px is where the rings/ticks become a face. */
.sigil-md { inline-size: 40px; block-size: 40px; flex: none; color: var(--coach, var(--ember)); }
/* The coach's NAME carries their sanctioned identity color (§8.2 — persona
   identity only, never data encoding), pairing name ↔ sigil into a character. */
.rd-name, .th-name, .cv-name, .coach-name { color: var(--coach, var(--ink)); }
/* Sigil draw-in — self-contained (pathLength="1" in sigils.js): every sigil
   draws itself the moment it enters the DOM; rings sweep, ticks follow. Inside
   the reduced-motion wrapper below, so motion-averse readers see it complete.
   Fail-open: if the animation never runs, strokes are simply... drawn. */
@media (prefers-reduced-motion: no-preference) {
  .sigil-ring { stroke-dasharray: 1; stroke-dashoffset: 1; animation: sigilDraw 0.9s var(--ease-out) forwards; }
  .sigil-tick { stroke-dasharray: 1; stroke-dashoffset: 1; animation: sigilDraw 0.5s var(--ease-out) 0.35s forwards; }
  @keyframes sigilDraw { to { stroke-dashoffset: 0; } }
  /* The SELECTED coach is present: their sigil's orbital nodes breathe — the
     constellation's own 6s grammar, nothing else moves. */
  .coach-pick.is-active .sigil-node, .coach-head .sigil-lg .sigil-node {
    animation: mo-breathe 6s var(--ease-inout) infinite;
  }
}
/* An inline persona mark for kickers / headlines (sits at ~text height). */
.coach-mark { display: inline-block; inline-size: 1.15em; block-size: 1.15em; vertical-align: -0.22em;
  margin-inline-end: 0.4em; color: var(--coach, var(--ember)); }
/* A coach detail header: the large sigil beside the kicker + serif name. */
.coach-head { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.coach-head .dx-kicker { margin: 0; }
.coach-head .dx-title { margin: 0.1em 0 0; }

/* ── Coach portraits (§8.7 / ADR-106) — engraved layered marks (portraits.js).
   Contours are INK (currentColor from --ink), the one accent layer (.pt-hatch,
   the engraved shading) rides var(--coach) — the same sanctioned persona colour
   channel as the sigils. viewBox 0 0 100 120 → 5:6 aspect. Sizes: base 40px,
   -md 56px, -lg 96px (the contact-sheet triplet). Fallback chain is the call
   sites' concern: portrait(c) || sigil(c) — no recipe, no portrait, no change. */
.portrait { display: block; inline-size: 40px; block-size: 48px; flex: none;
  color: var(--ink); overflow: visible; }
.portrait.portrait-md { inline-size: 56px; block-size: 67px; }
.portrait.portrait-lg { inline-size: 96px; block-size: 115px; }
.coach-mark .portrait { inline-size: 100%; block-size: 100%; }
.portrait .pt-frame { opacity: 0.35; }
.portrait .pt-hatch { color: var(--coach, var(--ember)); opacity: 0.75; }
/* Hover (#594) — 1.2° tilt + 0.8px eye shift, 200ms, CSS transitions only (never
   a continuous cursor-follow). The tilt lives on the portrait ROOT rather than
   the literal `head` recipe layer, or `.pt-breath` (below): `.pt-breath` already
   drives its own always-on translateY `animation`, and a `:hover` transform on
   that SAME element/property would just lose to the running animation, not
   compose with it — so the "head-group" here is the whole mark, and only the
   independently-animated eyes layers (opacity-only blink, no transform of their
   own) get their own, non-conflicting shift. The transition itself sits outside
   the reduced-motion guard (harmless with no motion to trigger it, same as the
   .ask-chip color transitions above); the actual :hover transform values are
   inside it, alongside the semantic states below. */
.portrait { transition: transform 0.2s var(--ease-out); transform-origin: 50% 55%; }
/* Portrait motion — all inside the reduced-motion wrapper: draw-in reuses the
   sigilDraw grammar; the blink period is per-coach (portraits.js seeds
   --pt-blink from FNV-1a, 4–8s); breath is a 4.5s micro-translate. Fail-open:
   eyes-closed/mouth variants are hidden by INLINE opacity, so with no CSS (or
   reduced motion) the portrait is simply... complete and still. */
@media (prefers-reduced-motion: no-preference) {
  .portrait .pt-stroke { stroke-dasharray: 1; stroke-dashoffset: 1; animation: sigilDraw 0.9s var(--ease-out) forwards; }
  .portrait .pt-hatch .pt-stroke { animation-delay: 0.35s; animation-duration: 0.6s; }
  .portrait .pt-eyes-open { animation: ptBlink var(--pt-blink, 6s) infinite; }
  .portrait .pt-eyes-closed { animation: ptBlinkInv var(--pt-blink, 6s) infinite; }
  @keyframes ptBlink { 0%, 95.9% { opacity: 1; } 96%, 99.4% { opacity: 0; } 99.5%, 100% { opacity: 1; } }
  @keyframes ptBlinkInv { 0%, 95.9% { opacity: 0; } 96%, 99.4% { opacity: 1; } 99.5%, 100% { opacity: 0; } }
  .portrait .pt-breath { transform-box: fill-box; transform-origin: 50% 65%;
    animation: ptBreath 4.5s var(--ease-inout, ease-in-out) infinite; }
  @keyframes ptBreath { 50% { transform: translateY(0.7px); } }

  .portrait:hover { transform: rotate(1.2deg); }
  .portrait:hover .pt-eyes-open, .portrait:hover .pt-eyes-closed {
    transform-box: fill-box; transition: transform 0.2s var(--ease-out); transform: translateX(0.8px);
  }

  /* Semantic states (#594) — data-portrait-state set by portraits.js
     (wireSpeakingAudio/markStanceChange) or passed at render time (writing).
     Same restrained ring-pulse idiom as the freshness dot above ([data-fresh-ts]
     .fr-live / fr-pulse) — box-shadow only, never a bespoke glow, "earned" per
     §8.5: speaking rides real audio playback, writing rides real authorship,
     stance-change rides a real new stance_history entry. */
  .portrait[data-portrait-state="speaking"] { animation: ptSpeakPulse var(--dur) var(--ease-inout) infinite; border-radius: 50%; }
  @keyframes ptSpeakPulse {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--coach, var(--ember)) 45%, transparent); }
    50% { box-shadow: 0 0 0 4px transparent; }
  }
  /* period = var(--dur) = 0.36s = exactly two of the JS mouth-toggle's 180ms
     ticks, so the pulse crests on the same cadence as the mouth motion without
     inventing a new number — not frame-locked to it (independent timers). */

  /* Elena's chronicle byline while she carries authorship of the piece: slow
     (6s), textural, NO transform/scale (earned glow / no gloss, §8.5) — ambient,
     never attention-grabbing. */
  .portrait[data-portrait-state="writing"] { animation: ptWriteShimmer 6s var(--ease-inout) infinite; border-radius: 50%; }
  @keyframes ptWriteShimmer {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--coach, var(--ember)) 18%, transparent); }
    50% { box-shadow: 0 0 0 3px color-mix(in oklch, var(--coach, var(--ember)) 5%, transparent); }
  }

  /* A coach's read on Matthew just moved — the instrument recalibrating. ONE
     sweep (iteration-count 1, not infinite); portraits.js clears the attribute
     after it plays so a later re-render can't replay it without a genuinely
     new stance_history entry. */
  .portrait[data-portrait-state="stance-change"] { animation: ptStanceSweep 1.1s var(--ease-out) 1; border-radius: 50%; }
  @keyframes ptStanceSweep {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--coach, var(--ember)) 55%, transparent); }
    45% { box-shadow: 0 0 0 6px transparent; }
  }
}

/* ── Pillar identity colors (§8.6) — the character sheet's seven pillars.
   Like --coach, these encode IDENTITY only (ring segments, radar vertices,
   stat-bar fills, heatmap row accents, sparkline strokes) — never buttons,
   text, alerts, or non-pillar data. Ember stays the "earned/up" accent.
   Desaturated toward the v5 register; mid-lightness so one set reads on
   both themes. */
:root {
  --pillar-sleep:         #7B87C4;   /* indigo, calmed   */
  --pillar-movement:      #4E9E7C;   /* green, muted     */
  --pillar-nutrition:     #B8862F;   /* wheat gold       */
  --pillar-metabolic:     #4E93A5;   /* teal, quiet      */
  --pillar-mind:          #9781BC;   /* violet, muted    */
  --pillar-relationships: #B06E6A;   /* clay rose        */
  --pillar-consistency:   #8A9455;   /* olive            */
}
/* Tier accent — set via data-tier on the character sheet's section root; used
   only on the tier emblem + hero frame (the tier IS an identity, not data). */
[data-tier="foundation"] { --tier-accent: color-mix(in oklch, var(--ember) 55%, var(--ink)); }
[data-tier="momentum"]   { --tier-accent: var(--ember); }
[data-tier="discipline"] { --tier-accent: #8BA6B5; }
[data-tier="mastery"]    { --tier-accent: #C4A24A; }
[data-tier="elite"]      { --tier-accent: #D4B45C; }

/* ── Editorial imagery (Part II) — atmospheric, free-license cover art on the
   narrative Story surfaces ONLY (chronicle · podcast · blog). A warm duotone
   treatment ties any photo to the ember/ink palette so it reads as editorial
   texture, never a literal data claim. Always carries attribution. */
.editorial-img { position: relative; margin: 0 0 var(--sp-5); border-radius: var(--radius); overflow: hidden;
  aspect-ratio: 21 / 9; background: var(--surface-sunken); isolation: isolate; }
.editorial-cover { aspect-ratio: 16 / 10; max-width: 420px; }
.editorial-img .img-duotone { width: 100%; height: 100%; object-fit: cover; display: block; filter: saturate(0.62) contrast(1.04); }
/* The warm wash — soft-light so the photo stays legible (no gloss, no flatten). */
.editorial-img::after { content: ""; position: absolute; inset: 0; pointer-events: none; mix-blend-mode: soft-light;
  background: linear-gradient(180deg, color-mix(in oklch, var(--ember) 24%, transparent), transparent 55%,
    color-mix(in oklch, var(--page) 28%, transparent)); }
.img-credit { position: absolute; right: var(--sp-2); bottom: var(--sp-2); margin: 0; padding: 2px 7px;
  border-radius: var(--radius-xs); background: color-mix(in oklch, #000 48%, transparent); color: #ECE3D2; }

/* ════════════════════════════════════════════════════════════════════════════
   §13 — the shared "ask the data" widget (assets/js/ask.js, uplevel P2).
   Moved up from evidence.css so Home (tokens+story only) mounts the same
   experience as the Data door. The honest-state classes are ASK-SCOPED here;
   evidence.css keeps its page-wide .rd-archive/.correlative for its renderers.
   ════════════════════════════════════════════════════════════════════════════ */
.ask-form { margin-top: var(--sp-5); }
.ask-row { display: flex; gap: var(--sp-2); margin-top: var(--sp-2); max-width: var(--measure); }
.ask-in { flex: 1; background: var(--surface); border: var(--border-hair); border-radius: var(--radius-sm); color: var(--ink);
  font-family: var(--font-sans); font-size: var(--fs-body); padding: var(--sp-3) var(--sp-4); }
.ask-in:focus-visible, .ask-btn:focus-visible { outline: 2px solid var(--ember); outline-offset: 1px; }
.ask-btn { cursor: pointer; background: var(--ember); color: var(--page); border: 0; border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: var(--fs-small); letter-spacing: 0.04em; padding: 0 var(--sp-5); }
.ask-btn:disabled { opacity: 0.55; cursor: progress; }
.ask-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-4); max-width: var(--measure); }
.ask-chip { cursor: pointer; background: transparent; border: var(--border-hair); border-radius: var(--radius-sm);
  color: var(--ink-muted); font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: 0.02em;
  padding: var(--sp-2) var(--sp-3); transition: color var(--dur-fast), border-color var(--dur-fast); }
.ask-chip:hover { color: var(--ember); border-color: var(--ember-line); }
.ask-chip:focus-visible { outline: 2px solid var(--ember); outline-offset: 2px; border-radius: var(--radius-xs); }
.ask-out { margin-top: var(--sp-4); display: grid; gap: var(--sp-4); }
.ask-turn { display: grid; gap: var(--sp-2); }
.ask-q { font-family: var(--font-mono); font-size: var(--fs-small); color: var(--ink-muted); max-width: var(--measure); }
.ask-q .label, .ask-answer .label { display: block; color: var(--ink-faint); margin-bottom: 2px; }
.ask-answer { font-family: var(--font-serif); font-size: var(--fs-quote); line-height: 1.5; max-width: var(--measure); color: var(--ink); }
/* honest states inside the widget (rate-limit / paused / offline) + the note */
.ask-out .rd-archive { margin-top: 0; color: var(--ink-muted); font-family: var(--font-serif); font-style: italic;
  font-size: var(--fs-quote); line-height: 1.5; max-width: var(--measure); border-left: 2px solid var(--rule); padding-left: var(--sp-4); }
[data-ask-mount] .correlative, .beat-ask .correlative { margin-top: var(--sp-5); padding: var(--sp-3) var(--sp-4); border: var(--honest-border);
  border-radius: var(--radius-xs); color: var(--ink-muted); font-family: var(--font-mono); font-size: var(--fs-small); max-width: var(--measure); }


/* ── #403 'explain this page' — one-tap server-grounded explainer ─────────── */
.explain-mount { margin-top: var(--sp-4); max-width: var(--measure); }
.explain-btn { cursor: pointer; background: transparent; border: var(--border-hair); border-radius: var(--radius-sm);
  color: var(--ink-muted); font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: 0.04em;
  padding: var(--sp-2) var(--sp-3); transition: color var(--dur-fast), border-color var(--dur-fast); }
.explain-btn:hover { color: var(--ember); border-color: var(--ember-line); }
.explain-btn:disabled { opacity: 0.55; cursor: progress; }
.explain-btn:focus-visible { outline: 2px solid var(--ember); outline-offset: 2px; }
.explain-out { margin-top: var(--sp-3); font-family: var(--font-serif); font-size: var(--fs-quote); line-height: 1.5;
  color: var(--ink); border-left: 2px solid var(--rule); padding-left: var(--sp-4); }
li.wk-explain { list-style: none; }

/* ── #404 moment share affordance ─────────────────────────────────────────── */
.share-btn { cursor: pointer; background: transparent; border: var(--border-hair); border-radius: var(--radius-sm);
  color: var(--ink-muted); font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: 0.04em;
  padding: var(--sp-2) var(--sp-3); margin-left: var(--sp-3); transition: color var(--dur-fast), border-color var(--dur-fast); }
.share-btn:hover { color: var(--ember); border-color: var(--ember-line); }
.share-btn:focus-visible { outline: 2px solid var(--ember); outline-offset: 2px; }
.sc-call .share-btn, .qa-share .share-btn { margin-left: 0; margin-top: var(--sp-3); }

/* ── #584 Provenance popover — "how is this number computed?" on any [data-method]
   stat tile. Registry-fed by provenance_popover.js from /api/methods; mirrors the
   coach-name popover chrome. APPENDED at file end to avoid collision with concurrent
   #551 tokens.css edits (keep both sides on merge). ──────────────────────────── */
.prov-btn { display: inline-flex; align-items: center; justify-content: center; vertical-align: baseline;
  margin-left: 0.35em; padding: 0; width: 1.05em; height: 1.05em; line-height: 1; font: inherit; font-size: 0.92em;
  color: var(--ink-faint); background: none; border: none; cursor: pointer;
  transition: color var(--dur-fast, 0.15s); }
.prov-btn:hover { color: var(--ember); }
.prov-btn:focus-visible { color: var(--ember); outline: 2px solid var(--ember); outline-offset: 2px; border-radius: var(--radius-xs); }
.prov-pop { position: absolute; z-index: var(--z-overlay, 60); max-width: 320px; padding: var(--sp-3) var(--sp-4);
  background: var(--surface-raised); border: var(--border-hair); border-radius: var(--radius-sm);
  box-shadow: 0 8px 28px rgba(0,0,0,0.18); }
.prov-pop .pv-name { font-family: var(--font-serif); font-weight: var(--weight-med); font-size: var(--fs-small); color: var(--ink); }
.prov-pop .pv-formula { margin-top: var(--sp-2); padding: var(--sp-2) var(--sp-3); border-radius: var(--radius-xs);
  background: var(--surface-sunken); font-family: var(--font-mono); font-size: var(--fs-label); color: var(--ink);
  line-height: var(--lh-relaxed); overflow-x: auto; white-space: pre-wrap; overflow-wrap: break-word; }
.prov-pop .pv-fields { margin-top: var(--sp-3); display: grid; gap: var(--sp-2); }
.prov-pop .pv-row dt { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--tracking-label, 0.06em);
  text-transform: uppercase; color: var(--ink-faint); }
.prov-pop .pv-row dd { margin: 2px 0 0; font-size: var(--fs-small); color: var(--ink-muted); line-height: var(--lh-relaxed); }
.prov-pop .pv-row dd { word-break: break-word; }
.prov-pop .pv-link { display: inline-block; margin-top: var(--sp-3); font-family: var(--font-mono); font-size: var(--fs-label);
  color: var(--ember); text-decoration: none; border-bottom: 1px solid var(--ember-line); }
.prov-pop .pv-link:hover { border-color: var(--ember); }
