/* ═══════════ SYSTEM STATUS ═══════════ */
.system-status {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  min-width: 70px;
  border: none;
  background: none;
  font-family: var(--font-mono);
}

.system-status.clickable {
  cursor: pointer;
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  transition: background 0.15s;
}

.system-status.clickable:hover {
  background: var(--bg-panel);
}

.system-status.clickable:hover .system-status-name {
  color: var(--text-primary);
}

.system-status.clickable:hover .system-status-level {
  opacity: 1;
}

.system-status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
}

.system-status-dot.green { background: var(--green); }
.system-status-dot.amber { background: var(--amber); }
.system-status-dot.yellow { background: var(--yellow); }
.system-status-dot.red { background: var(--red); }

.system-status-name {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.5px;
  color: var(--text-secondary);
}

.system-status-level {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 1px;
  text-transform: uppercase;
  opacity: 0.65;
}

.system-status-level.green { color: var(--green); }
.system-status-level.amber { color: var(--amber); }
.system-status-level.yellow { color: var(--yellow); }
.system-status-level.red { color: var(--red); }

/* ═══════════ STATUS TAGS ═══════════ */
.tag {
  font-size: 12px;
  padding: 2px 6px;
  border-radius: 4px;
  font-weight: 600;
  letter-spacing: 0.5px;
  display: inline-block;
  font-variant-numeric: tabular-nums;
}

.tag-high { color: var(--amber); background: var(--amber-bg); }
.tag-critical { color: var(--red); background: var(--red-bg); }
.tag-borderline { color: var(--yellow); background: var(--yellow-bg); }
.tag-nominal { color: var(--green); background: var(--green-bg); }
.tag-low { color: var(--text-dim); background: none; font-weight: 500; }
.tag-mod { color: var(--text-dim); background: none; font-weight: 500; }
.tag-elevated { color: var(--amber); background: var(--amber-bg); }

/* ═══════════ BIOMARKER ROWS ═══════════ */
.biomarker-row {
  display: grid;
  grid-template-columns: 140px 90px 1fr 60px auto;
  align-items: center;
  gap: var(--space-sm);
  padding: 6px 0;
  border-bottom: 1px solid var(--border-subtle);
}

.biomarker-row:last-child {
  border-bottom: none;
}

.biomarker-label {
  font-size: 12px;
  color: var(--text-secondary);
}

.biomarker-value {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
}

.biomarker-unit {
  font-size: 13px;
  color: var(--text-dim);
  font-weight: 400;
}

.biomarker-detail {
  font-size: 13px;
  color: var(--text-dim);
}

/* ═══════════ RANGE BAR ═══════════ */
.range-bar {
  position: relative;
  height: 8px;
  background: var(--border);
  border-radius: 4px;
  min-width: 120px;
}

.range-bar-normal {
  position: absolute;
  height: 100%;
  background: rgba(5, 150, 105, 0.25);
  border-radius: 4px;
}

.range-bar-marker {
  position: absolute;
  top: -4px;
  width: 4px;
  height: 16px;
  border-radius: 2px;
  transform: translateX(-2px);
  box-shadow: 0 0 3px rgba(0,0,0,0.15);
}

.range-bar-marker.nominal { background: var(--green); }
.range-bar-marker.borderline { background: var(--yellow); }
.range-bar-marker.high { background: var(--amber); }
.range-bar-marker.critical { background: var(--red); }

.range-bar-ref {
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
}

/* ═══════════ SCORECARD ═══════════ */
.scorecard-summary {
  padding: var(--space-sm) 0;
  margin-bottom: var(--space-md);
  font-size: 13px;
}

.scorecard-count {
  font-weight: 600;
  letter-spacing: 0.5px;
  color: var(--text-secondary);
}

.scorecard-all-clear .scorecard-count {
  color: var(--green);
}

.scorecard-domain {
  margin-bottom: var(--space-xl);
}

.scorecard-row {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 14px 0;
  border-bottom: 1px solid var(--border-subtle);
  font-size: 12px;
  flex-wrap: wrap;
}

.scorecard-row:last-child {
  border-bottom: none;
}

/* First row in a domain sits flush against the header — keeps title +
   first marker visually tied as one block. */
.scorecard-domain .scorecard-item:first-of-type .scorecard-row {
  padding-top: 6px;
}

.scorecard-row.nominal {
  opacity: 0.7;
}

.scorecard-label {
  font-size: 12px;
  color: var(--text-secondary);
  min-width: 130px;
}

.scorecard-value {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  min-width: 80px;
}

.scorecard-spark {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-width: 64px;
}

.scorecard-spark svg {
  display: block;
}

.scorecard-spark-empty {
  color: var(--text-dim);
  font-size: 12px;
  opacity: 0.4;
  justify-content: center;
}

.spark-dir {
  font-size: 11px;
  font-weight: 600;
}

.scorecard-nominal-tag {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--green);
  opacity: 0.6;
}

/* Nominal items hidden by default, shown with .show-nominal on container */
.scorecard-item-nominal {
  display: none;
}

.show-nominal .scorecard-item-nominal {
  display: block;
}

.scorecard-domain-nominal-only {
  display: none;
}

.show-nominal .scorecard-domain-nominal-only {
  display: grid;
}

/* "Show all" / "Flagged only" button in the overall bar */
.scorecard-show-all {
  margin-left: auto;
  border: none;
  background: var(--bg-surface);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-dim);
  cursor: pointer;
  padding: 4px 12px;
  transition: all 0.15s;
}

.scorecard-show-all:hover {
  background: var(--bg-hover);
  color: var(--text-secondary);
}

.show-nominal .scorecard-show-all {
  color: var(--accent);
  background: var(--accent-soft);
}

/* Segmented Flagged ↔ All toggle in the overall bar. Mirrors the look of the
   Scorecard/Analysis mode toggle so the page has one consistent toggle idiom. */
.scorecard-mode-toggle {
  margin-left: auto;
  display: inline-flex;
  background: var(--bg-surface);
  border-radius: var(--radius-sm);
  padding: 2px;
  gap: 2px;
}

.scorecard-mode-toggle .mode-seg {
  border: none;
  background: transparent;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  cursor: pointer;
  padding: 4px 10px;
  border-radius: 3px;
  transition: all 0.15s;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.scorecard-mode-toggle .mode-seg:hover {
  color: var(--text-secondary);
}

.scorecard-mode-toggle .mode-seg.active {
  background: var(--bg-panel);
  color: var(--text-primary);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.scorecard-mode-toggle .mode-seg-count {
  font-weight: 400;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.scorecard-mode-toggle .mode-seg.active .mode-seg-count {
  color: var(--text-secondary);
}

/* ═══════════ GOOD NEWS GRID ═══════════ */
.good-news-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-xs) var(--space-xl);
}

.good-news-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 0;
  font-size: 12px;
}

.good-news-label {
  color: var(--text-secondary);
}

.good-news-value {
  color: var(--text-primary);
  font-weight: 500;
}

/* ═══════════ LIVE VITALS (bloodwork tab) ═══════════ */
.vitals-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md);
}

.vital-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.vital-card-header {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
}

.vital-card-label {
  font-size: 13px;
  color: var(--text-dim);
  letter-spacing: 1px;
  font-weight: 600;
}

.vital-card-value {
  font-size: 18px;
  font-weight: 400;
  color: var(--text-primary);
}

.vital-card-unit {
  font-size: 12px;
  color: var(--text-dim);
}

.vital-spark {
  width: 100%;
  height: 32px;
}

/* ═══════════ PROFILE ═══════════ */
.profile-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 var(--space-2xl);
}

.profile-row {
  display: flex;
  justify-content: space-between;
  padding: 6px 0;
  font-size: 12px;
  line-height: 1.6;
}

.profile-label {
  color: var(--text-secondary);
}

.profile-value {
  color: var(--text-primary);
  font-weight: 500;
  text-align: right;
}

.profile-value.positive { color: var(--green); }
.profile-value.negative { color: var(--amber); }
.profile-value.accent { color: var(--accent); font-weight: 600; }

/* ═══════════ GENETIC MARKERS ═══════════ */
.genetic-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-sm) 0;
  border-bottom: 1px solid var(--border-subtle);
  font-size: 12px;
}

.genetic-row:last-child { border-bottom: none; }

.genetic-marker { color: var(--text-secondary); }
.genetic-value { font-weight: 500; }
.genetic-value.confirmed { color: var(--red); }
.genetic-value.unknown { color: var(--text-dim); }
.genetic-value.probable { color: var(--amber); }
.genetic-value.investigating { color: var(--yellow); }

.genetic-tag {
  font-size: 12px;
  opacity: 0.6;
  margin-left: 4px;
}

/* ═══════════ BASELINE SURVIVAL SECTION ═══════════ */
.baseline-survival-section {
  margin-top: var(--space-xl);
}

.baseline-empty-soft {
  font-size: 11px;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  padding: var(--space-sm) 0;
}

/* ═══════════ SURVIVAL CHART ═══════════ */
.survival-container {
  height: 220px;
  padding: var(--space-sm) 0;
}

/* ═══════════ PLAN ACTION CARDS ═══════════
   One card chassis used for both hero (ranks 1..3) and backlog (4..N).
   Identical chrome end-to-end so the live plan reads as one ranked
   Pareto. Lift comes from a panel-white background, a real border, and
   a hairline shadow — they need to stand off the page-grey backdrop. */
.view-narrow {
  max-width: 880px;
}

.plan-cards {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  margin-bottom: var(--space-xl);
}

/* Outer chassis: border, background, shadow. The flex layout that used
   to live here moves down to .plan-card-summary / .plan-card-flat so the
   card can be a <details> wrapping a summary row (the expandable variant)
   without nested flex on the details element itself.

   Progressive disclosure: hero cards default collapsed and reveal the
   "briefing" surface (why · target pills · refs) on click. The "picker"
   surface (rank · priority · threats · title · +Xy · Done/Dismiss) stays
   visible at rest, so a user can commit to an action without opening
   the card. */
.plan-card {
  background: var(--bg-panel);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-card);
  transition: border-color 0.15s, box-shadow 0.15s;
}

.plan-card:hover {
  border-color: var(--border);
  box-shadow: var(--shadow-card-hover);
}

/* Layout chrome shared by the expandable summary and the flat (non-
   expanding) variant. Same flex skeleton end to end so the eye reads
   them as the same kind of card regardless of whether there's a
   chevron. align-items:flex-start so a wrapped title doesn't stretch
   the rank gutter; the rank sits at the cap-height of the first line. */
.plan-card-flat,
.plan-card-summary {
  display: flex;
  align-items: flex-start;
  gap: var(--space-lg);
  padding: var(--space-lg) var(--space-xl);
}

.plan-card-summary {
  list-style: none;
  cursor: pointer;
}
.plan-card-summary::-webkit-details-marker { display: none; }
.plan-card-summary::marker { content: ''; }

/* Controls row inside the summary — Done / Dismiss sits below the title
   alongside the Show-details toggle. Sibling layout: Done/Dismiss left,
   toggle right (margin-left:auto). Done/Dismiss preventDefault their
   click so the card doesn't flip open when committing; the toggle does
   NOT preventDefault — its click is meant to bubble to <summary> and
   fire the native disclosure. */
.plan-card-controls-row {
  margin-top: 6px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 14px;
}

/* The default .action-controls margin-top is meant for a stacked layout
   (when controls sit directly under target pills on the Risks side).
   Inside .plan-card-controls-row they're a flex child centered with the
   toggle, so the extra top margin would offset their vertical centering. */
.plan-card-controls-row > .action-controls {
  margin-top: 0;
}

/* Show / Hide details toggle. Text-button label for the native <summary>
   click — pointer-events:none so clicks bubble to <summary>, tabindex=-1
   so keyboard nav stays on <summary>. The button is purely a visual cue
   that says "click to reveal more." Right-aligned via margin-left:auto
   so it pins to the actual card edge inside the controls row. */
.plan-card-details-toggle {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: inherit;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.3px;
  color: var(--text-dim);
  background: none;
  border: 0;
  padding: 4px 0;
  cursor: pointer;
  pointer-events: none;
  transition: color 0.15s;
}

/* Hover affordance lives on the whole card, not the toggle alone —
   the entire summary is the click target. */
.plan-card:hover .plan-card-details-toggle {
  color: var(--text-secondary);
}

.plan-card-details-toggle-label {
  display: inline-flex;
}

/* Both label states render in DOM; CSS swaps visibility off the
   parent <details>'s [open] state. Removes the stale-label bug where
   the toggle says "Show details" while the card is already open. */
.plan-card-details-toggle-label > [data-state] { display: none; }
.plan-card:not([open]) .plan-card-details-toggle-label > [data-state="collapsed"] { display: inline; }
.plan-card[open] .plan-card-details-toggle-label > [data-state="expanded"] { display: inline; }

.plan-card-details-toggle-chevron {
  font-size: 13px;
  line-height: 1;
  color: inherit;
  transition: transform 0.15s;
  display: inline-block;
}

.plan-card[open] .plan-card-details-toggle-chevron {
  transform: rotate(180deg);
}

/* Expanded panel — appears below the summary when the card is open.
   Indented past the rank gutter (24px + space-lg gap) so the why and
   pills align with the action title's left edge. No divider above —
   the disclosure animation + the "Hide details ▴" toggle in the
   summary are enough visual cue that the panel belongs to the card
   above; a border just turned one card into two.

   Grid layout uses the card's horizontal real estate: the why prose
   spans the full width (capped at 70ch for readability), and pills /
   refs share the row below — pills anchored left, refs flush right.
   On narrow viewports refs wrap below pills (grid row 3). */
.plan-card-details {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--space-xl);
  row-gap: var(--space-md);
  padding: var(--space-xs) var(--space-xl) var(--space-md)
           calc(var(--space-xl) + 24px + var(--space-lg));
  animation: expand-in 0.15s ease-out;
}

.plan-card-details > .action-why {
  grid-column: 1 / -1;
  font-size: 11.5px;
  color: var(--text-dim);
  line-height: 1.6;
  max-width: 70ch;
  margin: 0;
}

.plan-card-details > .target-pills {
  grid-column: 1;
  margin-top: 0;
  align-self: center;
}

.plan-card-details > .action-refs {
  grid-column: 2;
  align-self: center;
  margin-top: 0;
  text-align: right;
  white-space: nowrap;
}

.plan-card-rank {
  font-size: 18px;
  font-weight: 300;
  color: var(--text-dim);
  min-width: 24px;
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
  padding-top: 2px;
}

/* Body is a column inside the rank-gutter row. Three stacked sections:
   title-line (the JTBD scan: title + impact), meta-line (priority ·
   threats — secondary categorization in one phrase), controls-row
   (Done/Dismiss + Show details). Same vertical structure as a backlog
   row, just with a bigger title and visible commit controls. */
.plan-card-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Primary line: title on the left, +Xy on the right (baseline-aligned).
   The +Xy was a right-rail block in the previous design; pulling it
   onto the title line tightens the JTBD scan (what to do · how much it
   matters) into one horizontal sweep, and frees the controls row to
   anchor "Show details" at the actual card edge instead of floating
   inside an artificial body-width column. */
.plan-card-title-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}

.plan-card-title {
  flex: 1;
  min-width: 0;
  font-size: 15px;
  font-weight: 500;
  color: var(--text-primary);
  line-height: 1.45;
}

.plan-card-impact {
  font-size: 22px;
  font-weight: 400;
  color: var(--leverage);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.4px;
  line-height: 1.1;
  flex-shrink: 0;
  cursor: help;
}

/* Meta line: priority · threats — lever-subtitle. Same idiom as the
   backlog row's meta-line so hero and backlog read as the same kind
   of thing, just at different scales. All small-caps dim, priority
   keeps its semantic warm color but at the same size/weight as the
   threats so the row reads as one phrase rather than a colored chip
   stacked next to neutral chips. */
.plan-card-meta-line {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--text-dim);
  flex-wrap: wrap;
}

.plan-card-priority { line-height: 1.2; }
.plan-card-priority-urgent { color: var(--red); }
.plan-card-priority-soon { color: var(--amber); }
.plan-card-priority-pharmaceutical { color: var(--amber); }
.plan-card-priority-ongoing { color: var(--green); }

.plan-card-meta-sep {
  color: var(--border);
  font-weight: 400;
}

/* Lever-name subtitle ("APOB · LP(A)") sits at the right end of the
   meta line, pushed there by margin-left:auto. Anchors the +Xy above
   to the variable it moves so two cards citing the same lever read
   as alternatives, not additive wins. */
.plan-card-lever {
  margin-left: auto;
  color: var(--text-dim);
  white-space: nowrap;
}

/* Investigation/diagnostic actions don't move a measured target, so the
   subtitle reads "expected" — italicized to mark it as probabilistic
   rather than a lever name. */
.plan-card-lever-expected {
  font-style: italic;
  letter-spacing: 0.8px;
  text-transform: lowercase;
  font-weight: 500;
}

/* Card footer: controls (Done/Dismiss) on the left, evidence refs on
   the right. Single row — refs are tertiary metadata, sitting flush
   right keeps them out of the primary scan path. Wrap on narrow widths
   so the refs drop below the controls instead of getting clipped.
   Used by backlog row + secured/dismissed action-item details; hero
   cards put controls in the summary and refs in the expanded panel. */
.plan-card-footer {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin-top: 12px;
}


/* ═══════════ PLAN HORIZON ═══════════
   Three nodes anchor the line: BASELINE (demographic prior, far left),
   TODAY (your actual current projection, sitting at the banked/open
   handoff), and CEILING (reachable end-age, far right). Solid track
   behind TODAY = ground walked from baseline by being you; dashed
   track ahead = open opportunity. The headline +Xy is the unambiguous
   answer; ages on TODAY and CEILING anchor the journey. */
.plan-horizon {
  margin-bottom: var(--space-xl);
}

/* Stacked-left header with three layers of hierarchy: the eyebrow
   row pairs the metric label with an optional threat tag (capsule
   chip styled like the per-card .plan-threat-tag); the hero number
   stands alone; the anchor sits beneath when filtered. Different
   typographic weights at each level so it reads as structure, not
   a run-on phrase. */
.plan-horizon-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: var(--space-lg);
}

.plan-horizon-eyebrow {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Threat tag in the header — same vocabulary as `.plan-threat-tag` on
   action cards so the user reads it as the same noun. Filled rather
   than ghosted because here it's a confirmation of active scope, not
   a clickable affordance. The placeholder variant reserves the eyebrow
   row's vertical slot when unfiltered so the hero below doesn't jump
   when a threat is selected. */
.plan-horizon-scope-tag {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  padding: 3px 8px;
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  color: var(--accent);
}

.plan-horizon-scope-tag-placeholder {
  visibility: hidden;
}

.plan-horizon-scope {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* Subtitle anchoring a filtered headline to the total. Quiet
   typographic treatment — a context cue, not a peer of the headline
   number. The placeholder variant reserves the row's vertical slot
   when no filter is active so toggling the filter doesn't shift the
   timeline below. */
.plan-horizon-anchor {
  font-size: 10px;
  letter-spacing: 0.6px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.plan-horizon-anchor-placeholder {
  visibility: hidden;
}

/* The hero. Single number that answers "what's my opportunity?" — so
   sized to dominate. Anything else competing in the headline area
   makes the user wonder which figure to anchor on. */
.plan-horizon-total {
  font-size: 28px;
  font-weight: 500;
  color: var(--leverage);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.4px;
  line-height: 1;
}

/* Track inset so the ceiling node (translate -50% past left:100%)
   has room for its age label without overflowing the content box.
   Height accommodates three vertical bands stacked around top:50%:
   ages above (22px), rail+markers at center (16px), then names and
   the banked-segment caption sharing the bottom band (22px) — pulled
   tight so the caption reads as part of the horizon rather than a
   detached footnote. */
.plan-horizon-track {
  position: relative;
  height: 82px;
  margin: 0 var(--space-xl);
}

/* One axis, two textures, two colors. Banked and open sit on the same
   horizontal line with no gap between them. Texture (solid vs dashed)
   conveys proportion at a glance; color reinforces the semantic — green
   for ground already held, cyan for leverage still open. Both halves
   muted so the line reads as armature, not data. */
.plan-horizon-rail {
  position: absolute;
  top: 50%;
  height: 2px;
  transform: translateY(-50%);
  z-index: 1;
  border-radius: 1px;
}

.plan-horizon-rail-banked {
  background: var(--green);
  opacity: 0.55;
}

.plan-horizon-rail-open {
  background: repeating-linear-gradient(
    to right,
    var(--leverage) 0,
    var(--leverage) 7px,
    transparent 7px,
    transparent 13px
  );
}

/* Scope highlight — solid leverage-cyan band shown when a threat is
   filtered. Anchored at the banked/open handoff, extends right into
   the dashed open region by scope-open / total-reachable, so its
   width visually equals the headline +Xy. Sits above the banked +
   open rails (z-index 2); the bg-page outline gives it a crisp lift
   off the line. Read as: "starting from where you already are,
   solving this threat gives you this much more." */
.plan-horizon-rail-scope {
  position: absolute;
  top: 50%;
  height: 5px;
  transform: translateY(-50%);
  z-index: 2;
  background: var(--leverage);
  border-radius: 2.5px;
  box-shadow: 0 0 0 1px var(--bg-page);
}

/* Node = vertical stack centered on the line. Translated -50% so the
   marker sits exactly at (left%, 50%) on the track. Three-row grid
   keeps BASELINE, TODAY, and CEILING vertically aligned. BASELINE's
   top cell is an empty spacer (no age); TODAY and CEILING carry an
   age above the marker. */
.plan-horizon-node {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  display: grid;
  grid-template-rows: 22px 16px 22px;
  align-items: center;
  justify-items: center;
  row-gap: 4px;
  z-index: 3;
  pointer-events: none;
}

.plan-horizon-node-spacer {
  align-self: end;
}

/* End-age readout above a node's marker. Tabular and visible but
   not competing with the +Xy hero — text-secondary, not leverage
   cyan, so it reads as data rather than a second headline. Used by
   both TODAY and CEILING. */
.plan-horizon-age {
  font-size: 14px;
  color: var(--text-secondary);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.1px;
  align-self: end;
}

.plan-horizon-name {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 1.2px;
  text-transform: uppercase;
  align-self: start;
}

/* TODAY and CEILING names share the same prominent treatment — both
   anchor the user's mental model. BASELINE keeps the dim default so
   it reads as a quiet origin, not a peer. */
.plan-horizon-name-today,
.plan-horizon-name-ceiling {
  color: var(--text-secondary);
  font-weight: 600;
  letter-spacing: 1.4px;
}

/* Three marker treatments, three roles:
     BASELINE = small dim pip — demographic prior, quiet origin. It's a
                hypothetical reference ("where you'd be with no
                behaviors"), not a banked position, so it stays
                structural rather than green.
     TODAY    = filled green dot, lifted off the rail with a bg-page
                outline so the banked/open texture handoff doesn't
                visually swallow it. Sits at the apex of the banked
                segment — semantically "where you currently are," which
                IS the result of everything you've already banked.
                Green color reinforces that.
     CEILING  = leverage-cyan ring with halo — the reachable end-age,
                the visual terminus the dashed open segment resolves
                toward. Echoes the +Xy hero number upstairs. */
.plan-horizon-marker {
  border-radius: 50%;
  z-index: 4;
}

.plan-horizon-marker-baseline {
  width: 7px;
  height: 7px;
  background: var(--text-dim);
}

.plan-horizon-marker-today {
  width: 11px;
  height: 11px;
  background: var(--green);
  box-shadow: 0 0 0 2.5px var(--bg-page);
}

.plan-horizon-marker-ceiling {
  width: 14px;
  height: 14px;
  background: var(--bg-page);
  border: 2.5px solid var(--leverage);
  box-sizing: border-box;
  box-shadow: 0 0 0 4px var(--leverage-bg);
}

/* Segment value labels — anchored under the line, centered on each
   span. BANKED sits under the solid segment (always reflects total
   banked, invariant under the filter). OPEN rides with scope: under
   the full dashed segment when unfiltered, sliding under the cyan
   band when filtered, where its value matches the headline +Xy. The
   number gets text-secondary (data weight); the trailing word gets
   text-dim (label weight) — same hierarchy as a tagged readout. */
.plan-horizon-seg {
  position: absolute;
  top: 60px;
  transform: translateX(-50%);
  display: flex;
  gap: 4px;
  align-items: baseline;
  white-space: nowrap;
  pointer-events: none;
  z-index: 3;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.4px;
}

.plan-horizon-seg-num {
  color: var(--text-secondary);
  font-weight: 500;
}

.plan-horizon-seg-name {
  color: var(--text-dim);
}

.plan-horizon-empty {
  height: 82px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* ═══════════ PLAN FILTER CHIPS ═══════════
   Threat-axis pivot. Same vocabulary as the per-action `.plan-threat-tag`
   chips so the user reads the row as "the threats the cards already
   talk about". Sorted by user's elevation (most-elevated first). The
   "All" chip is always first; counts ride alongside each label. */
.plan-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: var(--space-lg);
}

.plan-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.9px;
  text-transform: uppercase;
  padding: 6px 10px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
  white-space: nowrap;
}

.plan-filter-chip:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
  border-color: var(--border);
}

.plan-filter-chip.active {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent);
}

.plan-filter-chip-count {
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.3px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.plan-filter-chip.active .plan-filter-chip-count {
  color: var(--accent);
  opacity: 0.8;
}

/* ═══════════ PLAN BACKLOG ═══════════
   The active queue, no longer hidden behind a button. Section head
   echoes the `.action-group-head` chassis (label · meta) and the body
   uses the same `.plan-card` chrome as the hero — the divider is the
   only visual break, so the live plan reads as one continuous Pareto. */
.plan-backlog {
  margin-top: var(--space-xl);
}

.plan-backlog-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
}

.plan-backlog-label {
  color: var(--text-secondary);
}

.plan-backlog-meta {
  margin-left: auto;
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-sm);
  font-size: 11px;
  letter-spacing: 0.5px;
  font-variant-numeric: tabular-nums;
}

.plan-backlog-count {
  color: var(--text-dim);
}

/* ═══════════ PLAN BACKLOG ROWS ═══════════
   Ranks 4..N as a unified list inside one container — internal dividers
   between rows, no per-row borders — so the backlog reads as items in a
   list, not a stack of mini-cards. Two-line summary: title + impact
   on the primary line, priority + threat tags as subordinate inline
   text on the secondary line. Everything else (Done / Dismiss / why /
   pills / refs) lives behind the expand. */
.plan-rows {
  display: flex;
  flex-direction: column;
  margin-bottom: var(--space-xl);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.03);
}

.plan-row {
  background: transparent;
  border-bottom: 1px solid var(--border-subtle);
  transition: background 0.15s;
}

.plan-row:last-child {
  border-bottom: 0;
}

.plan-row:hover {
  background: var(--bg-surface);
}

.plan-row-summary {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 12px 18px;
  list-style: none;
  cursor: pointer;
}
.plan-row-summary::-webkit-details-marker { display: none; }
.plan-row-summary::marker { content: ''; }

.plan-row-rank {
  font-size: 12px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  min-width: 18px;
  text-align: right;
  flex-shrink: 0;
  padding-top: 2px;
  line-height: 1.4;
}

/* Two-row body: title-line above, meta-line below. min-width:0 so the
   title can ellipsis-truncate inside the flex parent instead of pushing
   the impact off the right edge on narrow viewports. */
.plan-row-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* Primary line — title on the left, +Xy on the right. The title is the
   identifier the eye lands on; the +Xy is the value that drives ranking.
   Both baseline-aligned so a "Discuss X" sits on the same line as its
   "+1y" without the number floating off the cap height. */
.plan-row-title-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.plan-row-title {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  line-height: 1.45;
}

.plan-row-impact {
  font-size: 15px;
  font-weight: 400;
  color: var(--leverage);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.3px;
  flex-shrink: 0;
  line-height: 1.2;
}

/* Secondary line — priority label + threat tags as inline text, with a
   trailing chevron that says "click to expand". All small, all dim,
   middot-separated so it reads as one phrase rather than three chips. */
.plan-row-meta-line {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 1.1px;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--text-dim);
  flex-wrap: wrap;
}

.plan-row-priority {
  line-height: 1.2;
}
.plan-row-priority-urgent { color: var(--red); }
.plan-row-priority-soon { color: var(--amber); }
.plan-row-priority-pharmaceutical { color: var(--amber); }
.plan-row-priority-ongoing { color: var(--green); }

.plan-row-meta-sep {
  color: var(--border);
  font-weight: 400;
}

/* Threat tag as inline text inside a meta line — strip the chip chrome
   so the tag flows as text with the priority label and middot
   separators around it. Both hero cards (.plan-card-threat) and backlog
   rows (.plan-row-threat) use this idiom; the .plan-threat-tag class
   is kept so the existing click handler that navigates to Risks
   matches both contexts. */
.plan-row-threat.plan-threat-tag,
.plan-card-threat.plan-threat-tag {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font-size: inherit;
  font-family: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
  cursor: pointer;
  line-height: inherit;
  border-radius: 0;
}

.plan-row-threat.plan-threat-tag:hover,
.plan-card-threat.plan-threat-tag:hover {
  color: var(--text-secondary);
  background: none;
  border: 0;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* Chevron at the end of the meta line — always trails the row, rotates
   180° when the row is open. CSS rotation rather than glyph swap so
   the chevron stays in sync with [open] without needing a re-render
   on every toggle. Sized larger than the meta text so the disclosure
   cue stays legible — it's the only affordance for "click me" on a
   row this tight. */
.plan-row-chevron {
  margin-left: auto;
  font-size: 14px;
  color: var(--text-dim);
  transition: color 0.15s, transform 0.15s;
  flex-shrink: 0;
  padding-left: 8px;
  display: inline-block;
  line-height: 1;
}

.plan-row:hover .plan-row-chevron,
.plan-row[open] .plan-row-chevron {
  color: var(--text-secondary);
}

.plan-row[open] .plan-row-chevron {
  transform: rotate(180deg);
}

/* Expanded panel — same vertical reading order as the hero card's
   expanded state: controls → why → pills + refs. Hero keeps its
   Done/Dismiss in the summary (always visible); backlog can't afford
   that horizontal real estate at rest, so the controls land at the
   top of the panel here — same position the eye lands on for hero
   the moment a card is open. Refs detach from controls and pair with
   pills, mirroring hero. No divider — the [open] state itself is
   the boundary. */
.plan-row-details {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--space-xl);
  row-gap: var(--space-sm);
  padding: 4px 18px 14px calc(18px + 18px + 14px);
  animation: expand-in 0.15s ease-out;
}

.plan-row-details > .action-controls {
  grid-column: 1 / -1;
  margin-top: 0;
}

.plan-row-details > .action-why {
  grid-column: 1 / -1;
  font-size: 11.5px;
  color: var(--text-dim);
  line-height: 1.6;
  max-width: 70ch;
  margin: 0;
}

.plan-row-details > .target-pills {
  grid-column: 1;
  margin-top: 0;
  align-self: center;
}

.plan-row-details > .action-refs {
  grid-column: 2;
  align-self: center;
  margin-top: 0;
  text-align: right;
  white-space: nowrap;
}

/* Empty-state copy for filter pivots that come up empty. */
.plan-empty {
  padding: var(--space-md);
  margin-bottom: var(--space-lg);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  font-size: 12px;
  color: var(--text-secondary);
  text-align: center;
}

/* ═══════════ ACTION ITEMS ═══════════ */
.action-group + .action-group {
  margin-top: var(--space-lg);
}

.action-group-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
}

.action-group-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
}

.action-group-label.urgent { color: var(--red); }
.action-group-label.soon { color: var(--amber); }
.action-group-label.ongoing { color: var(--green); }
.action-group-label.pharmaceutical { color: var(--accent); }
.action-group-label.secured { color: var(--green); }
.action-group-label.dismissed { color: var(--text-dim); }

/* Group total — sum of per-row leverage across actions in this priority
   group. Pushed right; tabular-nums so the column aligns down the page. */
.action-group-leverage {
  margin-left: auto;
  font-size: 11px;
  font-weight: 600;
  color: var(--leverage);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.3px;
}

.action-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-sm) 0;
  border-bottom: 1px solid var(--border-subtle);
  font-size: 12px;
}

.action-item-body {
  flex: 1;
  min-width: 0;
}

.action-item:last-child {
  border-bottom: none;
}

.action-bullet {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
  margin-top: 7px;
}

.action-bullet.urgent { background: var(--red); }
.action-bullet.soon { background: var(--amber); }
.action-bullet.ongoing { background: var(--green); }
.action-bullet.pharmaceutical { background: var(--accent); }

.action-text {
  flex: 1;
  color: var(--text-primary);
  line-height: 1.5;
}

.action-why {
  font-size: 13px;
  color: var(--text-secondary);
  margin-top: 2px;
  line-height: 1.5;
}

/* ═══════════ 3D SCHEMATIC ═══════════ */
.schematic-canvas {
  position: fixed !important;
  top: 56px;
  left: 0;
  width: 100vw !important;
  height: calc(100vh - 56px - 72px) !important;
  z-index: 0;
  display: block;
}

/* ═══════════ TOOLTIP ═══════════
   Reusable hover tooltip. Add `data-tooltip="..."` to any element.
   Light panel, JBM mono, fades on hover/focus. CSS-only, no JS. */
[data-tooltip] {
  position: relative;
}

[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  background: var(--bg-panel);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-weight: 400;
  letter-spacing: 0.2px;
  line-height: 1.45;
  width: max-content;
  max-width: 280px;
  white-space: normal;
  text-align: left;
  text-transform: none;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s;
  z-index: 100;
}

[data-tooltip]:hover::after,
[data-tooltip]:focus-visible::after {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

[data-tooltip-anchor="left"]::after  { left: 0;     transform: translateY(4px); }
[data-tooltip-anchor="left"]:hover::after,
[data-tooltip-anchor="left"]:focus-visible::after  { transform: translateY(0); }

[data-tooltip-anchor="right"]::after { left: auto;  right: 0; transform: translateY(4px); }
[data-tooltip-anchor="right"]:hover::after,
[data-tooltip-anchor="right"]:focus-visible::after { transform: translateY(0); }

/* ═══════════ LE TREE ═══════════ */
.le-tree-row {
  display: flex;
  justify-content: space-between;
  padding: 4px 0;
  font-size: 12px;
  line-height: 1.6;
}

.le-tree-label {
  color: var(--text-dim);
}

.le-tree-value {
  font-weight: 500;
}

.le-tree-value.positive { color: var(--green); }
.le-tree-value.negative { color: var(--amber); }

/* ═══════════ EXPANDABLE DETAIL PANELS ═══════════ */
.expandable {
  transition: background 0.15s;
  border-radius: var(--radius-sm);
  padding-left: var(--space-xs);
  margin-left: calc(-1 * var(--space-xs));
}

.expandable:hover {
  background: var(--bg-hover);
}

.expandable.expanded {
  background: var(--bg-surface);
}

.expand-panel {
  padding: var(--space-sm) var(--space-md);
  margin: 0 0 var(--space-sm) var(--space-md);
  border-left: 2px solid var(--border);
  font-size: 13px;
  line-height: 1.6;
  animation: expand-in 0.15s ease-out;
}

@keyframes expand-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}

.expand-source {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-dim);
  letter-spacing: 0.3px;
  margin-bottom: 4px;
}

.expand-link {
  color: var(--accent);
  text-decoration: none;
  font-weight: 500;
}

.expand-link:hover {
  text-decoration: underline;
}

.expand-detail {
  color: var(--text-secondary);
}

/* Contextual explore button inside expand panels */
.expand-explore-btn {
  display: inline-flex;
  align-items: center;
  margin-top: 8px;
  border: none;
  background: none;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  color: var(--accent);
  cursor: pointer;
  padding: 3px 0;
  letter-spacing: 0.3px;
  opacity: 0.75;
  transition: opacity 0.15s;
}

.expand-explore-btn:hover {
  opacity: 1;
}

.expand-explore-btn.open {
  opacity: 1;
}

.expand-modifiers {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 6px;
}

.modifier-pill {
  font-size: 13px;
  padding: 1px 6px;
  border-radius: 3px;
  background: var(--amber-bg);
  color: var(--amber);
  font-weight: 500;
  letter-spacing: 0.3px;
}

/* ═══════════ LIFE PHASES ═══════════ */
.life-phases {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-sm);
  margin-top: var(--space-md);
}

.life-phase {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-sm) var(--space-md);
  background: var(--bg-surface);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-subtle);
  font-size: 13px;
}

.phase-label {
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.phase-ages {
  font-weight: 500;
  color: var(--text-primary);
}

.phase-risk {
  color: var(--amber);
  font-size: 12px;
}

.phase-threats {
  color: var(--text-secondary);
  font-size: 12px;
}

/* ═══════════ ACTION REFERENCES ═══════════
   Tertiary evidence — small inline footnote-style links. Stripped of
   pill chrome so they don't compete with the action title or the +Xy
   punchline. Dim color, hover lifts to leverage cyan. Middot separators
   between links keep the row reading as a single citation phrase. */
.action-refs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 6px;
  font-size: 10px;
  letter-spacing: 0.3px;
}

.action-ref-link {
  color: var(--text-dim);
  text-decoration: none;
  font-weight: 500;
  border-bottom: 1px dotted var(--border);
  padding-bottom: 1px;
  transition: color 0.15s, border-color 0.15s;
}

.action-ref-link:hover {
  color: var(--leverage);
  border-bottom-color: var(--leverage);
}

.action-refs-sep {
  color: var(--text-dim);
  opacity: 0.5;
}

/* ═══════════ SECTION SOURCES ═══════════ */
.section-sources {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: var(--space-md);
}

.source-note {
  font-size: 13px;
  color: var(--text-dim);
  letter-spacing: 0.3px;
}

/* ═══════════ ACTIONS SUMMARY (overview badge) ═══════════ */
.alert-badge {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: var(--radius-sm);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.5px;
}

.alert-badge.amber {
  color: var(--amber);
  background: var(--amber-bg);
}

/* ═══════════ SECTION DIVIDER ═══════════ */
.section-divider {
  border: none;
  border-top: 1px solid var(--border-subtle);
  margin: var(--space-lg) 0;
}

/* ═══════════ VIEW HEADER ROW ═══════════ */
.view-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* ═══════════ TRENDS SIDEBAR LAYOUT ═══════════ */
.trends-layout {
  display: grid;
  grid-template-columns: 320px 1fr;
  grid-template-rows: auto 1fr;
  height: 100%;
  max-width: 1240px;
  margin: 0 auto;
  padding: var(--space-lg) var(--space-lg) 0;
  overflow: hidden;
  min-height: 0;
}

.trends-title {
  grid-column: 1 / -1;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: var(--space-lg);
}

.trends-sidebar {
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border-subtle);
  overflow: hidden;
}

.trends-sidebar-header {
  display: flex;
  align-items: center;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-surface);
}

.trends-search {
  flex: 1;
  padding: var(--space-sm) var(--space-md);
  border: none;
  background: transparent;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-primary);
  outline: none;
  min-width: 0;
}

.trends-search::placeholder {
  color: var(--text-dim);
}

.trends-sidebar-header:focus-within {
  background: var(--bg-panel);
  border-bottom-color: var(--accent);
}

.trends-clear {
  border: none;
  background: none;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
  cursor: pointer;
  padding: var(--space-xs) var(--space-sm);
  margin-right: var(--space-xs);
  border-radius: var(--radius-sm);
  transition: all 0.15s;
}

.trends-clear:hover {
  color: var(--text-secondary);
  background: var(--bg-hover);
}

.trends-optimal-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: none;
  background: none;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
  cursor: pointer;
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-sm);
  transition: color 0.15s, background 0.15s;
}

.trends-optimal-toggle:hover {
  color: var(--text-secondary);
  background: var(--bg-hover);
}

.trends-optimal-dot {
  width: 10px;
  height: 6px;
  border-radius: 2px;
  background: var(--text-dim);
  opacity: 0.35;
  transition: background 0.15s, opacity 0.15s;
}

.trends-optimal-toggle.on {
  color: var(--text-secondary);
}

.trends-optimal-toggle.on .trends-optimal-dot {
  background: var(--accent, #4f7cff);
  opacity: 0.55;
}

.trends-sidebar-scroll {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-sm) 0 var(--space-2xl);
  min-height: 0;
  mask-image: linear-gradient(to bottom, black 0, black calc(100% - 32px), transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, black 0, black calc(100% - 32px), transparent 100%);
}

.trends-sidebar-scroll::-webkit-scrollbar { width: 4px; }
.trends-sidebar-scroll::-webkit-scrollbar-track { background: transparent; }
.trends-sidebar-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }

.trends-marker-list {
  padding: 0;
}

/* ═══════════ TRENDS SIDEBAR — INSIGHTS SECTION ═══════════
   Curated patterns rendered in the sidebar above the marker checkbox list.
   Both are "what to chart" pickers — a click on an insight row writes its
   pair into the marker section's checkboxes and into the chart.

   Top-N matched priors are shown by default; "show more" expands to all 10.
   Active row expands inline with the prior detail prose.
*/

.trends-insights {
  padding: 4px 0 6px;
  font-family: var(--font-mono);
}

/* Insights is a peer of the marker groups in layout, but visually it's the
 * curated/featured section. Tint its header chevron and title in the
 * agency accent (cool tone) to mark it as "the recommended picks" without
 * adding chrome that would break alignment with the marker groups. */
.trends-insights > .ti-header .ti-chevron,
.trends-insights > .ti-header .ti-title {
  color: var(--accent);
}

.ti-header {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 4px;
  border: none;
  background: none;
  font-family: var(--font-mono);
  cursor: pointer;
  text-align: left;
  border-radius: var(--radius-sm);
  transition: background 0.12s;
}

.ti-header:hover {
  background: var(--bg-hover);
}

.ti-chevron {
  font-size: 10px;
  color: var(--text-dim);
  transition: transform 0.15s;
  width: 10px;
  display: inline-flex;
  justify-content: center;
}

.trends-insights.collapsed .ti-chevron,
.trends-group.collapsed .ti-chevron {
  transform: rotate(-90deg);
}

.ti-title {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-secondary);
  flex: 1;
}

.ti-count {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.5px;
  font-variant-numeric: tabular-nums;
}

.ti-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 4px;
}

.trends-insights.collapsed .ti-list,
.trends-insights.collapsed .ti-more,
.trends-group.collapsed .trends-group-list {
  display: none;
}

.ti-row {
  width: 100%;
  display: grid;
  grid-template-columns: 3px minmax(0, 1fr);
  gap: 9px;
  align-items: center;
  padding: 8px 10px 8px 8px;
  border: 1px solid transparent;
  background: transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: var(--font-mono);
  text-align: left;
  transition: background 0.12s, border-color 0.12s;
}

.ti-row:hover {
  background: var(--bg-hover);
}

/* Leading selection bar — drawn as a filled block in the reserved 3px
 * column. Transparent on inactive rows so the layout never shifts. */
.ti-mark {
  width: 3px;
  height: 28px;
  border-radius: 2px;
  background: transparent;
  transition: background 0.12s;
  align-self: center;
}

.ti-row.tier-exploratory .ti-marker-name {
  color: var(--text-secondary);
}

.ti-row.active {
  background: var(--bg-surface);
  border-color: var(--border-subtle);
}

.ti-row.active .ti-mark {
  background: var(--accent);
}

/* Anomaly: recolor the direction arrow only. The arrow IS the direction,
 * so colouring it amber says "this direction is unexpected" without adding
 * new chrome or breaking the row's layout rhythm. */
.ti-row.tier-conflict .ti-arrow {
  color: var(--amber);
}

/* Two-column row content: marker pair on the left, meta stack on the right. */
.ti-row-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
}

.ti-pair {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.ti-marker {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.ti-swatch {
  width: 8px;
  height: 8px;
  border-radius: 2px;
  flex-shrink: 0;
}

.ti-marker-name {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ti-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: right;
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
  /* Reserved width — fits "UNEXPECTED" so the meta column is the same width
   * on every row, conflict or not. Non-conflict rows show their `Nmo`
   * right-aligned with empty space to the left. */
  min-width: 78px;
}

.ti-meta-top {
  display: inline-flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 6px;
  font-size: 11px;
  color: var(--text-secondary);
}

.ti-arrow {
  font-size: 10px;
  color: var(--text-dim);
}

.ti-r {
  font-weight: 500;
  color: var(--text-secondary);
}

.ti-r.strong {
  color: var(--text-primary);
  font-weight: 600;
}

.ti-r.moderate {
  color: var(--text-secondary);
}

.ti-r.weak {
  color: var(--text-dim);
  font-weight: 400;
}

.ti-meta-bottom {
  display: inline-flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 4px;
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.3px;
}

.ti-n {
  white-space: nowrap;
}

/* Anomaly label — replaces the duration on conflict rows. Same slot, so the
 * row's grid stays identical to non-conflict rows. The duration moves to
 * the row tooltip and the context band when selected. */
.ti-unexpected {
  color: var(--amber);
  white-space: nowrap;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-weight: 600;
}

.ti-more {
  display: block;
  width: 100%;
  margin-top: 4px;
  padding: 6px 10px 6px 20px;
  border: none;
  background: none;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
  cursor: pointer;
  text-align: left;
  border-radius: var(--radius-sm);
  letter-spacing: 0.3px;
  transition: color 0.12s, background 0.12s;
}

.ti-more:hover {
  color: var(--text-secondary);
  background: var(--bg-hover);
}

/* Visual divider between the insights section and the marker checkbox list. */
.trends-section-divider {
  height: 1px;
  background: var(--border-subtle);
  margin: 8px 12px 4px;
}

/* ═══════════ TRENDS CONTEXT BAND ═══════════
   Lives above the chart when an insight is active. The sidebar row is the
   selector; this band is the *content*. Full chart-area width gives the prose
   room to breathe in 2–3 lines instead of 6 cramped sidebar lines.
*/

.trends-context {
  flex-shrink: 0;
}

.trends-context:empty {
  display: none;
}

.trends-context-band {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 18px;
  margin: var(--space-md) var(--space-md) 0;
  background: var(--bg-panel);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
}

/* Exploratory pairs don't carry a clinical explanation — the band shrinks
   to a thin breadcrumb so it doesn't claim more attention than it earns. */
.trends-context-band.exploratory {
  gap: 6px;
  padding: 10px 18px;
  background: var(--bg-surface);
}

/* Header row: pair on the left, meta in the middle, dismiss on the right */
.tcb-head {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

.tcb-pair {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.tcb-marker {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.tcb-swatch {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  flex-shrink: 0;
}

.tcb-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
}

.tcb-arrow {
  font-size: 12px;
  color: var(--text-dim);
}

.tcb-meta {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.3px;
  flex: 1;
  min-width: 0;
}

.tcb-r {
  font-weight: 500;
  color: var(--text-secondary);
}

.tcb-r.strong { color: var(--text-primary); font-weight: 600; }
.tcb-r.moderate { color: var(--text-secondary); }
.tcb-r.weak { color: var(--text-dim); font-weight: 400; }

.tcb-sep { color: var(--border); }

.tcb-cluster {
  font-weight: 500;
}

.tcb-cluster.exploratory {
  font-style: italic;
}

.tcb-cluster.conflict {
  color: var(--amber);
  font-style: italic;
}

.tcb-dismiss {
  margin-left: auto;
  width: 22px;
  height: 22px;
  border: none;
  background: transparent;
  font-family: var(--font-mono);
  font-size: 16px;
  line-height: 1;
  color: var(--text-dim);
  cursor: pointer;
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.12s, color 0.12s;
  flex-shrink: 0;
}

.tcb-dismiss:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

/* Body: prose, conflict callout, footnote */
.tcb-prose {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text-secondary);
  max-width: 72ch; /* keep line length readable on wide chart areas */
}

.tcb-conflict {
  font-size: 12px;
  color: var(--amber);
  background: rgba(245, 158, 11, 0.06);
  border: 1px solid rgba(245, 158, 11, 0.2);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  line-height: 1.5;
}

.tcb-conflict-label {
  font-weight: 600;
  margin-right: 4px;
}

.tcb-foot {
  display: flex;
  align-items: baseline;
  gap: 0;
  font-size: 10px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.tcb-confidence {
  font-weight: 600;
}

.tcb-confidence.high { color: var(--text-secondary); }
.tcb-confidence.moderate { color: var(--text-dim); }

.trends-group {
  margin-bottom: var(--space-sm);
}

.trends-group-list {
  display: flex;
  flex-direction: column;
  margin-top: 4px;
}

.trends-marker {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px var(--space-md);
  font-size: 12px;
  cursor: pointer;
  transition: background 0.1s;
}

.trends-marker:hover {
  background: var(--bg-hover);
}

.trends-marker input[type="checkbox"] {
  width: 13px;
  height: 13px;
  margin: 0;
  accent-color: var(--accent);
  flex-shrink: 0;
}

.trends-swatch {
  width: 8px;
  height: 8px;
  border-radius: 2px;
  flex-shrink: 0;
  opacity: 0.3;
  transition: opacity 0.15s;
}

.trends-marker input:checked ~ .trends-swatch {
  opacity: 1;
}

.trends-marker-name {
  color: var(--text-secondary);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: color 0.15s;
}

.trends-marker input:checked ~ .trends-marker-name {
  color: var(--text-primary);
}

.trends-marker-latest {
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
  flex-shrink: 0;
}

.trends-chart-area {
  position: relative;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.trends-charts {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-md) var(--space-md) var(--space-2xl);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  mask-image: linear-gradient(to bottom, black 0, black calc(100% - 32px), transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, black 0, black calc(100% - 32px), transparent 100%);
}

.trends-charts::-webkit-scrollbar { width: 4px; }
.trends-charts::-webkit-scrollbar-track { background: transparent; }
.trends-charts::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }

.trends-multiple {
  flex: 0 0 220px;
  display: grid;
  grid-template-rows: auto 1fr;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--bg-panel);
  overflow: hidden;
}

.trends-charts.single-group .trends-multiple {
  flex: 1 1 auto;
}

.trends-multiple-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-surface);
}

.trends-multiple-markers {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  align-items: center;
  min-width: 0;
}

.trends-multiple-marker {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--text-primary);
}

.trends-multiple-swatch {
  width: 8px;
  height: 8px;
  border-radius: 2px;
  flex-shrink: 0;
}

.trends-multiple-name {
  font-weight: 500;
}

.trends-multiple-unit {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.5px;
  flex-shrink: 0;
  white-space: nowrap;
}

.trends-multiple-meta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-md);
  flex-shrink: 0;
}

.trends-multiple-header .trends-optimal-toggle {
  padding: 2px 6px;
  font-size: 10px;
  letter-spacing: 0.3px;
  color: var(--text-dim);
}

.trends-multiple-header .trends-optimal-toggle .trends-optimal-dot {
  width: 8px;
  height: 4px;
}

.trends-multiple-chart {
  position: relative;
  padding: var(--space-sm) var(--space-md);
  min-height: 0;
}

/* ─── Active-insight temporal rail (in context band) ────────────
   The window for the active insight is shown once, in the context band
   above the charts. The rail's left/right margins are set in JS so the
   bar inside aligns with the first chart's plot area below. */
.tcb-rail-row {
  display: grid;
  grid-template-columns: max-content 1fr max-content;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
  font-size: 10px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 600;
}

.tcb-rail-label {
  display: flex;
  align-items: center;
  gap: 6px;
}
.tcb-rail-label::before {
  content: "▸";
  color: rgb(99, 102, 241);
  font-size: 9px;
  letter-spacing: 0;
}

.tcb-rail {
  position: relative;
  height: 8px;
  /* margin-left/right set by JS to align with first chart's plot area */
}

.tcb-rail::before,
.tcb-rail::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 1px;
  height: 8px;
  background: var(--text-dim);
  opacity: 0.45;
  transform: translateY(-50%);
}
.tcb-rail::before { left: 0; }
.tcb-rail::after  { right: 0; }

.tcb-rail-line {
  position: absolute;
  top: 50%;
  left: 1px;
  right: 1px;
  height: 1px;
  background: var(--border);
  transform: translateY(-50%);
}

.tcb-rail-bar {
  position: absolute;
  top: 50%;
  height: 7px;
  transform: translateY(-50%);
  background: rgba(99, 102, 241, 0.85);
  /* left/right percentages set by JS */
}
.tcb-rail-bar::before,
.tcb-rail-bar::after {
  content: "";
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 1px;
  background: rgb(99, 102, 241);
}
.tcb-rail-bar::before { left: 0; }
.tcb-rail-bar::after  { right: 0; }

.tcb-rail-dates {
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.trends-empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  color: var(--text-dim);
  letter-spacing: 0.3px;
  pointer-events: none;
}

/* ═══════════ TREND CHART ═══════════ */
.trend-chart-wrap {
  height: 320px;
  margin-bottom: var(--space-md);
}

/* ═══════════ TREND LEGEND ═══════════ */
.trend-legend {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.trend-legend-item {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 6px 0;
  border-bottom: 1px solid var(--border-subtle);
  font-size: 12px;
}

.trend-legend-item:last-child {
  border-bottom: none;
}

.trend-legend-swatch {
  width: 12px;
  height: 3px;
  border-radius: 2px;
  flex-shrink: 0;
}

.trend-legend-name {
  color: var(--text-secondary);
  min-width: 120px;
}

.trend-legend-latest {
  font-weight: 500;
  color: var(--text-primary);
}

.trend-legend-latest.out-of-range {
  color: var(--amber);
}

.trend-legend-unit {
  font-size: 12px;
  color: var(--text-dim);
  font-weight: 400;
}

.trend-legend-range {
  font-size: 12px;
  color: var(--text-dim);
  margin-left: auto;
}

/* ═══════════ RED FLAGS STRIP ═══════════ */
.red-flags-strip {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-md);
  padding: var(--space-sm) var(--space-md);
  margin-bottom: var(--space-xl);
  border-radius: var(--radius-sm);
  background: var(--amber-bg);
  border-left: 3px solid var(--amber);
}

.red-flags-count {
  font-size: 13px;
  font-weight: 600;
  color: var(--amber);
  white-space: nowrap;
}

.red-flags-list {
  font-size: 13px;
  color: var(--text-secondary);
}

/* ═══════════ BIOMARKER CATEGORY ═══════════ */
.biomarker-category {
  transition: background 0.3s;
}

.biomarker-category.category-highlight {
  background: var(--accent-soft);
  border-radius: var(--radius-sm);
  padding: var(--space-md);
  margin-left: calc(-1 * var(--space-md));
  margin-right: calc(-1 * var(--space-md));
}

.category-flag-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--amber);
  margin-left: 6px;
  vertical-align: middle;
}

/* Nominal markers in category — more compact */
.biomarker-row.nominal-row {
  opacity: 0.7;
}

.biomarker-row.nominal-row .biomarker-value {
  font-weight: 400;
}

/* ═══════════ MARKER → ACTION LINKS ═══════════ */
.marker-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  grid-column: 1 / -1;
  padding-top: 2px;
}

.marker-action-link {
  font-size: 12px;
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-decoration: none;
  transition: background 0.15s;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  color: var(--text-secondary);
}

.marker-action-link:hover {
  background: var(--bg-hover);
  border-color: var(--accent);
  color: var(--accent);
}

.marker-action-link.red { color: var(--red); border-color: var(--red-bg); }
.marker-action-link.red:hover { background: var(--red-bg); }
.marker-action-link.amber { color: var(--amber); border-color: var(--amber-bg); }
.marker-action-link.amber:hover { background: var(--amber-bg); }
.marker-action-link.green { color: var(--green); border-color: var(--green-bg); }
.marker-action-link.green:hover { background: var(--green-bg); }
.marker-action-link.accent { color: var(--accent); border-color: var(--accent-soft); }
.marker-action-link.accent:hover { background: var(--accent-soft); }

.watch-name {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.watch-value {
  font-size: 14px;
  font-weight: 500;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.watch-unit {
  font-size: 10px;
  color: var(--text-dim);
  margin-left: 4px;
  font-weight: 400;
}

/* ═══════════ OVERVIEW READOUT GROUP LABELS ═══════════ */
.readouts-group-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 0 4px;
  margin-bottom: 4px;
  text-align: left;
}

/* ═══════════ FLAGGED BIOMARKER ROW (wrap for action links) ═══════════ */
.biomarker-row.flagged {
  flex-wrap: wrap;
}

/* ═══════════ ACTION HIGHLIGHT (cross-nav from bloodwork) ═══════════ */
.action-item.action-highlight {
  background: var(--accent-soft);
  border-radius: var(--radius-sm);
  padding-left: var(--space-sm);
  margin-left: calc(-1 * var(--space-sm));
  transition: background 0.3s;
}

/* ═══════════ WELCOME / UPLOAD SCREEN ═══════════ */
/* Two-column layout: the figure occupies the left column (rendered
   via the `.welcome-model-stage` canvas with a laterally-panned
   camera; see `csv` / `apple` / `genetic` / `complete` presets in
   wireframe-model-scene.js) and the card occupies the right column.
   The grid only positions the card — `.welcome-model-stage` stays
   absolute and ignores it. `max-width` caps the layout so on wide
   external monitors the card doesn't drift to the far right and the
   gap between figure and card doesn't sprawl: both are anchored to
   a centered 1400px box, with empty viewport space on either side
   on screens wider than that. */
.welcome {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  height: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: var(--space-xl);
  transition: opacity 320ms ease;
  overflow: hidden;
}
.welcome.is-leaving { opacity: 0; }

/* Quiet escape hatch back to the landing page. Anchored to the top-left
   of the viewport (not the card) so it reads as global navigation —
   "out of this flow" — rather than a step within it. `fixed` rather
   than `absolute` so it stays at the viewport corner even when the
   welcome layout is max-width-constrained on wide monitors. Fades in
   along with the rest of the chrome. */
.welcome-back {
  position: fixed;
  top: clamp(16px, 3vh, 28px);
  left: clamp(16px, 3vw, 32px);
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  background: transparent;
  border: 0;
  padding: 6px 4px;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.6px;
  text-transform: lowercase;
  color: var(--text-dim);
  z-index: 2;
  opacity: 0;
  animation: welcome-fade-rise 900ms cubic-bezier(.2, .7, .2, 1) 1900ms forwards;
  transition: color 200ms ease;
}
.welcome-back:hover { color: var(--text-secondary); }
.welcome-back:hover .welcome-back-arrow { transform: translateX(-2px); }
.welcome-back-arrow {
  font-size: 13px;
  letter-spacing: 0;
  transition: transform 200ms ease;
}

.welcome-card {
  position: relative;
  z-index: 2;
  grid-column: 2;
  justify-self: center;
  text-align: center;
  max-width: 440px;
  width: 100%;
  min-height: 560px;
  max-height: min(80vh, 660px);
  display: flex;
  flex-direction: column;
  background: rgba(250, 252, 255, 0.52);
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 20px;
  box-shadow: 0 18px 54px rgba(8, 24, 40, 0.07);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  padding: 34px 24px 22px;
}

/* Cinematic open: logo settles its letter-spacing as it fades in,
   subtitle + progress thread + slot stagger in below. The card stays
   anchored across acts — only the inner slot swaps later. */
.welcome-logo {
  font-size: 28px;
  font-weight: 700;
  letter-spacing: 8px;
  color: var(--accent);
  margin-bottom: var(--space-sm);
  opacity: 0;
  animation: welcome-logo-in 1500ms cubic-bezier(.2, .7, .2, 1) 350ms forwards;
}

.welcome-subtitle {
  font-size: 12px;
  color: var(--text-dim);
  letter-spacing: 1px;
  margin-bottom: 16px;
  opacity: 0;
  animation: welcome-fade-rise 900ms cubic-bezier(.2, .7, .2, 1) 1300ms forwards;
}

.welcome-progress {
  display: flex;
  justify-content: center;
  gap: 12px;
  margin-bottom: 20px;
  opacity: 0;
  animation: welcome-fade-rise 900ms cubic-bezier(.2, .7, .2, 1) 1900ms forwards;
}

.welcome-progress-tick {
  position: relative;
  width: 34px;
  height: 6px;
  background: rgba(153, 164, 180, 0.35);
  border: none;
  border-radius: 999px;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: default;
  transition: background 280ms ease, box-shadow 280ms ease, transform 280ms ease, opacity 280ms ease;
  opacity: 0.8;
}
.welcome-progress-tick.is-active {
  background: rgba(255, 255, 255, 0.18);
  box-shadow: 0 0 0 1.5px rgba(140, 154, 172, 0.72);
  transform: translateY(-1px);
  opacity: 1;
}
.welcome-progress-tick.is-complete {
  background: var(--green);
  box-shadow: 0 0 0 1px rgba(16, 185, 129, 0.22);
  opacity: 1;
}

/* Revisit affordance: any pill the user has been at-or-past becomes
   clickable. Lifts subtly on hover so the user knows it's hot, and a
   small floating chip surfaces the destination — `↶ Bloodwork`,
   `↶ Apple Health`, `↶ Genome` — drawn from the data-name attribute
   set in the welcome HTML. The current step's pill is never
   `is-clickable` even though it has `is-active` — you can't navigate
   to where you already are. */
.welcome-progress {
  /* Allow tooltip overflow above the pills. */
  overflow: visible;
}
.welcome-progress-tick.is-clickable {
  cursor: pointer;
}
.welcome-progress-tick.is-clickable:hover,
.welcome-progress-tick.is-clickable:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 0 0 1px rgba(8, 145, 178, 0.45), 0 4px 10px rgba(8, 145, 178, 0.18);
  outline: none;
}
.welcome-progress-tick.is-clickable.is-complete:hover,
.welcome-progress-tick.is-clickable.is-complete:focus-visible {
  box-shadow: 0 0 0 1px rgba(5, 150, 105, 0.55), 0 4px 10px rgba(5, 150, 105, 0.18);
}
.welcome-progress-tick.is-clickable::after {
  content: '↶ ' attr(data-name);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(2px);
  font-size: 9px;
  letter-spacing: 0.6px;
  color: var(--text-secondary);
  background: rgba(255, 255, 255, 0.94);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 3px 7px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms ease, transform 180ms ease;
  text-transform: uppercase;
  box-shadow: 0 4px 10px rgba(10, 28, 44, 0.06);
}
.welcome-progress-tick.is-clickable:hover::after,
.welcome-progress-tick.is-clickable:focus-visible::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .welcome-progress-tick.is-clickable:hover,
  .welcome-progress-tick.is-clickable:focus-visible {
    transform: none;
  }
  .welcome-progress-tick.is-clickable::after { transition: none; }
}

.welcome-slot {
  /* Resting state is fully visible. The slot is the "content" — it
     fades in straight (no rise) so it feels like it's appearing rather
     than arriving from somewhere. Chrome above (logo, subtitle,
     progress) keeps its rise; the content does not. `backwards` fill
     holds opacity:0 during the delay so the slot doesn't flash before
     its turn. We intentionally do NOT use `forwards` — the post-fill
     would override the .is-leaving rule below and block the crossfade. */
  animation: welcome-slot-fade-in 1000ms ease 2400ms backwards;
  transition: opacity 500ms ease;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Crossfade between acts: pure opacity, no transform. The chrome stays
   anchored; only the content swaps in place. */
.welcome-slot.is-leaving {
  opacity: 0;
}

/* ── Welcome act layout ── */

.welcome-act-prompt {
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-secondary);
  margin: 0 0 14px;
  letter-spacing: 0.2px;
}
.welcome-act {
  width: 100%;
  max-width: 100%;
}
.welcome-act-prompt-aside {
  color: var(--text-dim);
  font-size: 12px;
  letter-spacing: 0.4px;
}

.welcome-act-footer {
  margin-top: var(--space-sm);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-md);
}

.welcome-act-footer-apple {
  gap: var(--space-xl);
}

/* Quiet variant for the secondary footer actions on each act —
   smaller, dimmer, no visual weight competing with the dropzone. */
.upload-help-quiet {
  margin-top: 0;
  text-align: center;
}
.upload-help-quiet > summary {
  font-size: 9px;
  letter-spacing: 0.6px;
  color: var(--text-dim);
  text-transform: uppercase;
  list-style: none;
  opacity: 0.75;
}
.upload-help-quiet > summary:hover { opacity: 1; }
.upload-help-quiet > summary::-webkit-details-marker { display: none; }
.upload-help-quiet > summary::before {
  content: '+ ';
  color: var(--text-dim);
  margin-right: 2px;
}
.upload-help-quiet[open] > summary::before { content: '− '; }

.welcome-btn-quiet {
  font-size: 9px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  padding: 2px 6px;
  opacity: 0.75;
}
.welcome-btn-quiet:hover { opacity: 1; }

@keyframes welcome-logo-in {
  from { opacity: 0; letter-spacing: 12px; }
  to   { opacity: 1; letter-spacing: 8px; }
}
@keyframes welcome-fade-rise {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes welcome-slot-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .welcome-logo,
  .welcome-subtitle,
  .welcome-progress,
  .welcome-slot,
  .welcome-back {
    animation: none;
    opacity: 1;
    transform: none;
  }
  .welcome-slot,
  .welcome {
    transition: none;
  }
}

/* ─── CALIBRATION transition ─────────────────────────────────────
   Final beat of onboarding: welcome card → status readout → hairline
   that settles at the topbar's separator Y, where the topbar's own
   border-bottom takes over. Driven by welcome.js#runCalibration. */

.welcome-slot.is-leaving-final {
  opacity: 0;
  transform: scale(0.985);
  transition: opacity 240ms ease, transform 240ms ease;
}
.welcome-back.is-leaving-final {
  opacity: 0;
  transition: opacity 200ms ease;
}

/* Subtitle becomes the instrument's status readout. Same spacing/weight
   as a section header so it reads as a label, not a tagline. The caret
   is the tell — this is being typed live. */
.welcome-subtitle.welcome-status {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 1.5px;
  color: var(--text-secondary);
  text-transform: uppercase;
  /* Cancel the welcome-fade-rise animation in case it hasn't finished. */
  animation: none;
  opacity: 1;
  transform: none;
}
.welcome-subtitle.welcome-status::after {
  content: '_';
  display: inline-block;
  margin-left: 3px;
  color: var(--accent);
  animation: cal-cursor-blink 700ms steps(2) infinite;
}
.welcome-subtitle.welcome-status.is-typed::after {
  animation: cal-cursor-fade 280ms ease 120ms forwards;
}
@keyframes cal-cursor-blink {
  50% { opacity: 0; }
}
@keyframes cal-cursor-fade {
  to { opacity: 0; }
}

/* Thread pulse — single beat that confirms the three layers landed.
   Each tick briefly thickens vertically and shifts to green (the
   instrument's "all systems nominal" tone) before settling back to
   accent. Staggered so it reads as a sweep, not a flash. */
.welcome-progress.is-pulsing .welcome-progress-tick {
  animation: cal-tick-pulse 480ms cubic-bezier(.2, .7, .2, 1) both;
}
.welcome-progress.is-pulsing .welcome-progress-tick[data-step="1"] { animation-delay: 0ms;  }
.welcome-progress.is-pulsing .welcome-progress-tick[data-step="2"] { animation-delay: 70ms; }
.welcome-progress.is-pulsing .welcome-progress-tick[data-step="3"] { animation-delay: 140ms; }
@keyframes cal-tick-pulse {
  0%   { transform: scaleY(1); box-shadow: 0 0 0 1.5px rgba(140, 154, 172, 0.72); }
  35%  { transform: scaleY(2.2); box-shadow: 0 0 0 1px rgba(16, 185, 129, 0.24); }
  100% { transform: scaleY(1); box-shadow: 0 0 0 1.5px rgba(140, 154, 172, 0.72); }
}

.welcome-card.is-collapsing {
  transition: opacity 320ms ease 80ms,
              transform 360ms cubic-bezier(.4, 0, 0.2, 1) 80ms;
  opacity: 0;
  transform: scaleY(0.04);
  /* Origin at horizontal center so the card converges into the same
     Y where we placed the hairline. */
  transform-origin: center center;
  pointer-events: none;
}

/* The hairline that becomes the topbar separator. JS sets initial
   top + width to match the card's resting geometry; toggling
   .is-extended runs the transition to viewport-wide at y=56px. */
.calibration-rule {
  position: fixed;
  left: 50%;
  height: 1px;
  background: var(--border);
  transform: translateX(-50%);
  transition: width 460ms cubic-bezier(.4, 0, 0.2, 1),
              top 460ms cubic-bezier(.4, 0, 0.2, 1);
  z-index: 200;
  pointer-events: none;
}
.calibration-rule.is-extended {
  width: 100vw;
  top: 56px;
}

@media (prefers-reduced-motion: reduce) {
  .welcome-subtitle.welcome-status::after,
  .welcome-progress.is-pulsing .welcome-progress-tick {
    animation: none;
  }
  .welcome-card.is-collapsing,
  .calibration-rule {
    transition: none;
  }
}

.upload-zone {
  border: 1px solid rgba(153, 164, 180, 0.28);
  border-radius: var(--radius-lg);
  padding: var(--space-2xl) var(--space-xl);
  min-height: 210px;
  width: 100%;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s;
  background: rgba(255, 255, 255, 0.38);
  border-color: rgba(153, 164, 180, 0.28);
  backdrop-filter: blur(10px) saturate(115%);
  -webkit-backdrop-filter: blur(10px) saturate(115%);
  box-shadow: 0 10px 28px rgba(10, 28, 44, 0.06);
}
.welcome-act .upload-zone,
.welcome-act .upload-zone-success {
  width: 100%;
  min-width: 100%;
  max-width: 100%;
}

.upload-zone:hover,
.upload-zone-active {
  border-color: var(--accent);
  background: rgba(235, 247, 253, 0.7);
}

.upload-icon {
  color: var(--text-dim);
  margin-bottom: var(--space-md);
}

.upload-zone:hover .upload-icon,
.upload-zone-active .upload-icon {
  color: var(--accent);
}

.upload-label {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  margin-bottom: var(--space-xs);
}

.upload-hint {
  font-size: 13px;
  color: var(--text-dim);
}

.upload-help {
  margin-top: var(--space-lg);
  text-align: left;
}

.upload-help summary {
  font-size: 13px;
  color: var(--text-dim);
  cursor: pointer;
  letter-spacing: 0.5px;
}

.upload-format {
  font-size: 12px;
  color: var(--text-secondary);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  padding: var(--space-md);
  margin-top: var(--space-sm);
  overflow-x: auto;
  line-height: 1.6;
}

.welcome-actions {
  margin-top: var(--space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  align-items: center;
}

.welcome-btn {
  font-family: var(--font-mono);
  font-size: 12px;
  border: none;
  border-radius: var(--radius-sm);
  padding: var(--space-sm) var(--space-xl);
  cursor: pointer;
  letter-spacing: 0.5px;
}
.welcome-btn-skip {
  background: none;
  color: var(--text-dim);
  font-size: 13px;
  padding: var(--space-xs) var(--space-md);
}
.welcome-btn-skip:hover { color: var(--text-secondary); }

.upload-zone-secondary {
  padding: var(--space-md) var(--space-lg);
  min-height: 210px;
}
.upload-zone-secondary .upload-icon { margin-bottom: var(--space-xs); }
.upload-zone-content {
  width: 100%;
  min-height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  opacity: 1;
  transform: scale(1);
  transition: opacity 220ms ease, transform 220ms ease;
}
.upload-zone-content.is-exiting {
  opacity: 0;
  transform: scale(0.985);
}

/* Apple Health success: dropzone contents are replaced with a
   centered checkmark — single, calm confirmation. The zone keeps
   its frame so the swap reads as "this slot resolved" rather than
   a new element appearing. */
.upload-zone-success {
  border-style: solid;
  border-color: var(--green);
  background: rgba(52, 168, 83, 0.06);
  cursor: default;
  pointer-events: none;
  display: flex;
  width: 100%;
  box-sizing: border-box;
  align-items: center;
  justify-content: center;
  min-height: 210px;
}
.upload-success-check {
  color: var(--green);
  display: flex;
  align-items: center;
  justify-content: center;
  animation: upload-success-enter 360ms cubic-bezier(.2, .7, .2, 1) both;
}
@keyframes upload-success-enter {
  0%   { opacity: 0; transform: scale(0.9); }
  70%  { opacity: 1; transform: scale(1.02); }
  100% { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .upload-success-check { animation: none; }
}

/* Revisit / loaded state — same green frame as `.upload-zone-success`,
   but the zone is interactive again so drop-to-replace works without
   a trip through the picker. The checkmark sits above filename + a
   one-line summary; the explicit replace + continue controls live in
   `.welcome-act-controls` below. */
.upload-zone-loaded {
  flex-direction: column;
  cursor: default;
  pointer-events: auto;
  padding: var(--space-lg) var(--space-xl);
}
.upload-zone-loaded.upload-zone-active {
  background: rgba(235, 247, 253, 0.7);
  border-color: var(--accent);
}
.upload-zone-loaded .upload-success-check {
  margin-bottom: var(--space-sm);
}
.upload-loaded-filename {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  margin: 0;
  word-break: break-all;
  text-align: center;
}
.upload-loaded-summary {
  font-size: 11px;
  color: var(--text-dim);
  margin-top: 2px;
  letter-spacing: 0.3px;
  text-align: center;
}

/* Footer for revisited (loaded-state) acts. Two affordances laid out
   inline: a quiet `↻ replace` link and a primary `continue →` button.
   Replace stays small/dim — the dropzone itself is the primary swap
   surface; the link is just a keyboard-accessible alias for the same
   intent. Continue is the act of intent the user came back for. */
.welcome-act-controls {
  margin-top: var(--space-md);
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: var(--space-lg);
}
.welcome-link-quiet {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--text-dim);
  background: none;
  border: none;
  padding: var(--space-xs) var(--space-sm);
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: color 160ms ease, background 160ms ease;
}
.welcome-link-quiet:hover,
.welcome-link-quiet:focus-visible {
  color: var(--text-secondary);
  background: var(--bg-surface);
  outline: none;
}
.welcome-btn-continue {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid rgba(8, 145, 178, 0.28);
  border-radius: var(--radius-sm);
  padding: var(--space-sm) var(--space-lg);
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease, transform 160ms ease, box-shadow 160ms ease;
}
.welcome-btn-continue:hover,
.welcome-btn-continue:focus-visible {
  background: rgba(8, 145, 178, 0.14);
  border-color: var(--accent);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(8, 145, 178, 0.18);
  outline: none;
}
@media (prefers-reduced-motion: reduce) {
  .welcome-btn-continue:hover,
  .welcome-btn-continue:focus-visible { transform: none; }
}

.apple-health-steps {
  padding: var(--space-sm) 0;
  font-size: 12px;
  line-height: 1.7;
  color: var(--text-secondary);
}
.apple-health-steps ol {
  margin: 0;
  padding-left: var(--space-lg);
}
.apple-health-steps code {
  font-size: 13px;
  padding: 1px 4px;
  border-radius: 3px;
  background: rgba(0,0,0,0.04);
}
.apple-health-steps a {
  color: var(--blue);
  text-decoration: none;
}
.apple-health-steps a:hover { text-decoration: underline; }

.upload-error {
  margin-top: var(--space-md);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-sm);
  background: var(--red-bg);
  color: var(--red);
  font-size: 13px;
}

/* ═══════════════════════════════════════════
   SOURCE DATA EXPLORERS (inline panels)
   ═══════════════════════════════════════════ */

/* ── Explore toggle button ── */
.src-explore-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  padding: 8px 0;
  margin-top: var(--space-md);
  border: none;
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  cursor: pointer;
  transition: all 0.15s;
}

.src-explore-toggle:hover {
  color: var(--accent);
  background: var(--accent-soft);
}

.src-explore-toggle.open {
  color: var(--accent);
  background: var(--accent-soft);
}

.src-explore-arrow {
  transition: transform 0.2s;
  font-size: 14px;
}

.src-explore-toggle.open .src-explore-arrow {
  transform: rotate(180deg);
}

/* ── Slide-over header & components ── */
.slide-over-header {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.slide-over-title {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-secondary);
  white-space: nowrap;
}

.slide-over-tabs {
  display: flex;
  gap: 2px;
  margin-left: var(--space-md);
}

.slide-over-tab {
  padding: 4px 12px;
  border: none;
  background: none;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: all 0.12s;
}

.slide-over-tab:hover {
  color: var(--text-secondary);
  background: var(--bg-hover);
}

.slide-over-tab.active {
  color: var(--accent);
  background: var(--accent-soft);
}

.slide-over-spacer { flex: 1; }

.slide-over-close {
  width: 28px;
  height: 28px;
  border: none;
  background: none;
  font-size: 15px;
  color: var(--text-dim);
  cursor: pointer;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: all 0.12s;
}

.slide-over-close:hover {
  color: var(--text-primary);
  background: var(--bg-hover);
}

.slide-over-body {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-lg);
}

/* Hide scrollbar on slide-over body */
.slide-over-body::-webkit-scrollbar { width: 4px; }
.slide-over-body::-webkit-scrollbar-track { background: transparent; }
.slide-over-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }

/* ═══════════ TOPBAR DATA FRESHNESS BUTTON ═══════════ */
.topbar-data {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}

.topbar-data:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.topbar-data-pulse {
  position: absolute;
  top: -2px;
  right: -2px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--green);
  border: 1.5px solid var(--bg-panel);
  opacity: 0;
  transition: opacity 0.15s;
}

.topbar-data.tier-fresh .topbar-data-pulse {
  background: var(--green);
  opacity: 0.7;
}

.topbar-data.tier-aging .topbar-data-pulse {
  background: var(--amber);
  opacity: 1;
}

.topbar-data.tier-stale .topbar-data-pulse {
  background: var(--red);
  opacity: 1;
}

/* ═══════════ DATA PANEL ═══════════ */
.data-panel-backdrop {
  position: fixed;
  top: 56px;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.08);
  z-index: 300;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}

.data-panel-backdrop.visible {
  opacity: 1;
  pointer-events: auto;
  cursor: pointer;
}

.data-panel {
  position: fixed;
  top: 56px;
  right: 0;
  bottom: 0;
  width: 400px;
  max-width: 90vw;
  background: var(--bg-panel);
  border-left: 1px solid var(--border);
  box-shadow: -6px 0 24px rgba(0, 0, 0, 0.06);
  z-index: 310;
  transform: translateX(100%);
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
  display: flex;
  flex-direction: column;
}

.data-panel.open {
  transform: translateX(0);
}

/* Privacy reassurance — sits under the panel title in the header */
.dp-privacy {
  font-size: 11px;
  color: var(--text-dim);
  margin-top: 6px;
  letter-spacing: 0.2px;
}

/* Panel body */
.dp-body {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

.dp-body::-webkit-scrollbar { width: 4px; }
.dp-body::-webkit-scrollbar-track { background: transparent; }
.dp-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }

/* Sections */
.dp-section {
  padding: var(--space-lg);
}

.dp-section-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: var(--space-md);
}

/* Inventory rows */
.dp-inv-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  padding: var(--space-sm) 0;
}

.dp-inv-row + .dp-inv-row {
  border-top: 1px solid var(--border-subtle);
}

.dp-inv-left { flex: 1; min-width: 0; }

.dp-inv-label {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
}

.dp-inv-sub {
  font-size: 11px;
  color: var(--text-dim);
  margin-top: 2px;
}

.dp-inv-missing {
  opacity: 0.5;
}

.dp-inv-right {
  text-align: right;
  flex-shrink: 0;
  margin-left: var(--space-md);
}

.dp-inv-date {
  font-size: 11px;
  color: var(--text-secondary);
  white-space: nowrap;
}

.dp-inv-age {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.3px;
  margin-top: 2px;
}

.dp-inv-age.tier-fresh { color: var(--green); }
.dp-inv-age.tier-aging { color: var(--amber); }
.dp-inv-age.tier-stale { color: var(--red); }
.dp-inv-age.tier-none  { color: var(--text-dim); }

/* Divider */
.dp-divider {
  height: 1px;
  background: var(--border);
}

/* Upload zones (compact) */
.dp-upload-zone {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 10px var(--space-md);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: all 0.15s;
}

.dp-upload-zone:hover,
.dp-upload-zone-hover {
  border-color: var(--accent);
  background: var(--accent-soft);
}

.dp-upload-zone:hover .dp-upload-icon,
.dp-upload-zone-hover .dp-upload-icon {
  color: var(--accent);
}

.dp-upload-icon {
  color: var(--text-dim);
  flex-shrink: 0;
}

.dp-upload-label {
  font-size: 12px;
  color: var(--text-secondary);
}

.dp-upload-zone-done {
  border-style: solid;
  border-color: var(--green);
  background: var(--green-bg);
  pointer-events: none;
}

.dp-upload-zone-done .dp-upload-label { color: var(--green); }
.dp-upload-zone-done .dp-upload-icon  { color: var(--green); }

/* Feedback messages */
.dp-feedback {
  font-size: 11px;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
  margin-top: 4px;
}

.dp-feedback-success {
  color: var(--green);
  background: var(--green-bg);
}

.dp-feedback-error {
  color: var(--red);
  background: var(--red-bg);
}

/* Clear section — pinned to bottom */
.dp-clear-wrap {
  margin-top: auto;
  padding: var(--space-md) var(--space-lg);
  border-top: 1px solid var(--border-subtle);
  display: flex;
  justify-content: center;
}

.dp-clear-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
  cursor: pointer;
  padding: 6px 14px;
  letter-spacing: 0.3px;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.dp-clear-btn:hover {
  color: var(--red);
  border-color: var(--red);
}
.dp-clear-btn:hover .dp-clear-icon { color: var(--red); }

.dp-clear-icon {
  color: var(--text-dim);
  flex-shrink: 0;
  transition: color 0.15s;
}

.dp-clear-btn-armed {
  color: var(--red) !important;
  border-color: var(--red) !important;
  background: var(--red-bg) !important;
  font-weight: 500;
}

.dp-clear-btn-armed .dp-clear-icon { color: var(--red); }

.dp-clear-btn-secondary {
  font-size: 11px;
  letter-spacing: 0.3px;
}

/* ── Data section header (inside explore panel) ── */
.src-data-section {
  margin-bottom: var(--space-sm);
}

.src-data-hd {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: var(--space-sm);
  gap: var(--space-md);
}

.src-data-title {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 2px;
}

.src-data-meta {
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.6;
  margin-bottom: 2px;
}

.src-data-meta em {
  font-style: normal;
  color: var(--text-primary);
  font-weight: 500;
}

.src-ext-link {
  font-size: 12px;
  font-weight: 500;
  color: var(--accent);
  text-decoration: none;
  opacity: 0.7;
  white-space: nowrap;
  flex-shrink: 0;
  transition: opacity 0.15s;
}

.src-ext-link:hover {
  opacity: 1;
}

.src-ext-links {
  display: flex;
  gap: var(--space-sm);
  flex-shrink: 0;
}

/* ── Filter bar ── */
.src-filters {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}

.filter-group {
  display: flex;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.filter-btn {
  padding: 5px 12px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.5px;
  border: none;
  background: none;
  color: var(--text-dim);
  cursor: pointer;
  transition: all 0.12s;
  white-space: nowrap;
}

.filter-btn + .filter-btn {
  border-left: 1px solid var(--border);
}

.filter-btn:hover {
  color: var(--text-secondary);
  background: var(--bg-hover);
}

.filter-btn.active {
  background: var(--bg-panel);
  color: var(--accent);
}

/* ── Table wrapper ── */
.src-table-wrap {
  overflow-x: auto;
}

.src-table-label {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-dim);
  letter-spacing: 0.5px;
  text-transform: uppercase;
  margin-bottom: 6px;
}

.src-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.src-table th {
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 6px 12px 6px 0;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.src-table th.src-th-num {
  text-align: right;
  padding-right: 20px;
}

.src-table th.src-th-center {
  text-align: center;
  padding-right: 8px;
}

.src-table td {
  padding: 7px 12px 7px 0;
  border-bottom: 1px solid var(--border-subtle);
  vertical-align: middle;
  color: var(--text-primary);
}

.src-table tr:last-child td {
  border-bottom: none;
}

.src-table tr:hover td {
  background: var(--bg-surface);
}

/* Highlighted row for user's age / selected population */
.src-row-you td {
  background: var(--accent-soft) !important;
  color: var(--accent);
}

.src-row-you td.src-td-num,
.src-row-you td.src-td-age {
  font-weight: 600;
}

/* Base row (middle class, native-born) */
.src-row-base td {
  color: var(--text-secondary);
  font-style: italic;
}

/* Numeric cells */
.src-td-age,
.src-td-num,
.src-td-pct {
  font-variant-numeric: tabular-nums;
  text-align: right;
  padding-right: 20px;
  white-space: nowrap;
}

.src-td-age {
  color: var(--text-dim);
  font-size: 12px;
  width: 60px;
}

.src-td-rank {
  color: var(--text-dim);
  font-size: 12px;
  width: 32px;
  text-align: center;
  padding-right: 8px;
}

.src-td-cause {
  font-weight: 500;
}

.src-td-tier {
  font-weight: 500;
  white-space: nowrap;
}

.src-td-desc {
  color: var(--text-secondary);
  font-size: 12px;
}

.src-td-adj {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
  padding-right: 16px;
  width: 80px;
}

.src-td-adj.positive { color: var(--green); }
.src-td-adj.negative { color: var(--red); }

.src-td-range {
  font-size: 12px;
  color: var(--text-dim);
  white-space: nowrap;
  padding-right: 16px;
}

.src-td-detail-text {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.6;
  max-width: 400px;
}

/* ── Bar visualizations ── */
.src-td-bar {
  width: 200px;
  padding-right: 0;
}

.src-bar-track {
  height: 6px;
  background: var(--bg-surface);
  border-radius: 3px;
  overflow: hidden;
  position: relative;
  border: 1px solid var(--border-subtle);
}

.src-bar-fill {
  height: 100%;
  border-radius: 3px;
  background: var(--accent);
  transition: width 0.3s ease;
}

.src-bar-fill.src-bar-high     { background: var(--red); }
.src-bar-fill.src-bar-elevated { background: var(--amber); }
.src-bar-fill.src-bar-mod      { background: var(--yellow); }
.src-bar-fill.src-bar-low      { background: var(--green); }

/* Centered deviation bar (SES section) */
.src-bar-track-centered {
  overflow: visible;
  background: var(--bg-surface);
}

.src-bar-center-line {
  position: absolute;
  left: 50%;
  top: 0;
  width: 1px;
  height: 100%;
  background: var(--border);
}

.src-bar-deviation {
  position: absolute;
  top: 0;
  height: 100%;
  border-radius: 2px;
}

/* ── COD expandable rows ── */
.src-cod-row {
  cursor: default;
}

.src-cod-row[data-expandable] {
  cursor: pointer;
}

.src-cod-row[data-expandable]:hover td {
  background: var(--bg-surface);
}

.src-cod-row.src-row-open td {
  background: var(--bg-surface);
  color: var(--accent);
}

.src-cod-detail-cell {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.7;
  padding: 8px 16px 12px 16px !important;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-subtle);
}

/* ── Misc ── */
.src-table-note {
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.3px;
  margin-top: 6px;
}

.src-empty {
  font-size: 13px;
  color: var(--text-dim);
  padding: var(--space-md) 0;
}

.src-table-imm td.src-td-tier {
  min-width: 160px;
}

/* ── Table inner wrap (for lazy-rendered content) ── */
.src-table-inner {
  overflow-x: auto;
}

/* ═══════════ SLIDE-OVER HEADER STRUCTURE ═══════════
   Header is two rows now: title row on top, optional controls strip
   below (filters that scope the entire panel). Controls row hides
   itself when empty so panels that don't use it don't get extra chrome. */
.slide-over-header-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.slide-over-header-controls:empty {
  display: none;
}

.slide-over-header-controls {
  margin-top: var(--space-md);
  padding-top: var(--space-md);
  border-top: 1px solid var(--border-subtle);
}

/* Header filter row — label + group, label + group. Wraps at narrow
   widths so the slide-over still works at min-width. */
.slide-over-filter-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-sm);
  row-gap: 8px;
}

.slide-over-filter-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-left: var(--space-xs);
}

.slide-over-filter-row .slide-over-filter-label:first-child {
  margin-left: 0;
}

/* Allow the eth filter group to wrap onto a second row if drawer is narrow */
.filter-group.filter-group-wrap {
  flex-wrap: wrap;
}

/* ═══════════ PANEL A — POPULATION BASE RATES ═══════════
   Content max-width constrains the table; bars don't need to be longer
   to be more readable, they need to be the right ratio of label to bar. */
.src-cod-section {
  max-width: 720px;
}

/* Active-filter readout strip — confirms what was just clicked in the
   header, in instrument-readout style. Visually replaces the old
   src-table-label. */
.src-cod-readout {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-sm);
  padding: 10px 0;
  margin-bottom: var(--space-sm);
  border-bottom: 1px solid var(--border);
  font-size: 11px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}

.src-cod-readout-label {
  color: var(--text-dim);
  font-weight: 500;
}

.src-cod-readout-pop {
  color: var(--text-primary);
  font-weight: 600;
  letter-spacing: 1px;
}

.src-cod-readout-vs {
  color: var(--text-secondary);
  font-weight: 500;
}

.src-cod-readout-spacer { flex: 1; }

.src-cod-readout-source {
  color: var(--text-dim);
  font-weight: 500;
  font-size: 10px;
}

/* Δ column — directional glyph + magnitude. Color is muted; the glyph
   carries direction without screaming. */
.src-td-delta {
  font-variant-numeric: tabular-nums;
  text-align: right;
  padding-right: 16px;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 500;
}

.src-cod-delta-glyph {
  display: inline-block;
  width: 11px;
  margin-right: 4px;
  font-size: 9px;
  position: relative;
  top: -1px;
}

.src-cod-delta-num {
  font-variant-numeric: tabular-nums;
}

.src-td-delta-flat .src-cod-delta-glyph,
.src-td-delta-flat .src-cod-delta-num {
  color: var(--text-dim);
}

.src-td-delta-up {
  color: var(--text-primary);
}
.src-td-delta-up .src-cod-delta-glyph {
  color: var(--amber);
}

.src-td-delta-down {
  color: var(--text-primary);
}
.src-td-delta-down .src-cod-delta-glyph {
  color: var(--text-secondary);
}

/* Ghost bar — the all-US baseline rendered behind the filtered fill.
   When the fill is shorter than the ghost, the ghost extends past it
   and the eye reads "your population gets this less than national." */
.src-bar-ghost {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  background: repeating-linear-gradient(
    90deg,
    var(--border) 0 2px,
    transparent 2px 4px
  );
  border-radius: 3px;
  z-index: 0;
}

/* Ensure the fill paints over the ghost */
.src-cod-row .src-bar-fill {
  position: relative;
  z-index: 1;
}

/* Compare-mode legend below the table */
.src-cod-legend {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-md);
  margin-top: var(--space-sm);
  font-size: 10px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.src-cod-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.src-cod-legend-tail {
  margin-left: auto;
  font-style: normal;
}

.src-cod-legend-swatch {
  display: inline-block;
  width: 18px;
  height: 6px;
  border-radius: 2px;
  border: 1px solid var(--border-subtle);
}

.src-cod-legend-swatch.src-cod-legend-fill {
  background: var(--accent);
  border-color: var(--accent);
}

.src-cod-legend-swatch.src-cod-legend-ghost {
  background: repeating-linear-gradient(
    90deg,
    var(--border) 0 2px,
    transparent 2px 4px
  );
}


/* ═══════════ HEALTH SCORE CARD (overview) ═══════════ */
.health-score-card {
  text-align: center;
  padding: var(--space-lg) var(--space-xl);
  background: rgba(245, 246, 248, 0.72);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(232, 234, 237, 0.6);
  border-radius: var(--radius-md);
  box-shadow:
    0 1px 3px rgba(0, 0, 0, 0.03),
    inset 0 0.5px 0 rgba(255, 255, 255, 0.7);
}

.health-score-value {
  font-size: 56px;
  font-weight: 300;
  line-height: 1;
}

.health-score-fraction {
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.5px;
  margin-top: var(--space-sm);
}

.health-score-divider {
  height: 1px;
  background: var(--border-subtle);
  margin: var(--space-md) calc(-1 * var(--space-xl)) var(--space-sm);
}

.health-score-takeaway {
  font-size: 11px;
  letter-spacing: 0.4px;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex-wrap: wrap;
  line-height: 1.4;
}

.health-score-takeaway.green {
  color: var(--green);
  letter-spacing: 1.2px;
  text-transform: uppercase;
  font-size: 10px;
  font-weight: 600;
}

.takeaway-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}
.takeaway-dot.green { background: var(--green); }
.takeaway-dot.amber { background: var(--amber); }
.takeaway-dot.red { background: var(--red); }

.takeaway-names {
  color: var(--text-primary);
  font-weight: 500;
}

.overview-allclear {
  text-align: center;
  padding: var(--space-xl) var(--space-lg);
  font-size: 12px;
  color: var(--green);
  letter-spacing: 0.5px;
  background: rgba(245, 246, 248, 0.72);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(232, 234, 237, 0.6);
  border-radius: var(--radius-md);
}

/* ═══════════ INSIGHT CARDS (overview, single-line) ═══════════ */
.insights-stack {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.insight-card {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s;
  font-family: var(--font-mono);
  text-align: left;
  box-shadow: none;
  min-width: 0;
}

.insight-card:hover {
  background: rgba(245, 246, 248, 0.72);
}

.insight-meter {
  display: inline-flex;
  align-items: flex-end;
  gap: 2px;
  height: 12px;
}

.insight-meter i {
  display: block;
  width: 3px;
  border-radius: 1px;
  background: var(--border);
}

.insight-meter i:nth-child(1) { height: 5px; }
.insight-meter i:nth-child(2) { height: 8px; }
.insight-meter i:nth-child(3) { height: 12px; }

.insight-meter i.on { background: var(--text-secondary); }

.insight-pair {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  color: var(--text-primary);
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.insight-marker-name {
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.insight-arrow {
  color: var(--text-dim);
  font-size: 10px;
  margin: 0 1px;
  flex-shrink: 0;
}

.insight-tail {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

.insight-r {
  color: var(--text-secondary);
  font-weight: 500;
}

.insight-conflict {
  color: var(--amber);
  font-weight: 700;
  font-size: 11px;
}

.insights-empty {
  padding: var(--space-sm) var(--space-md);
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.3px;
  line-height: 1.5;
  text-align: left;
}

/* ═══════════ STATUS STRIP (overview bottom) ═══════════ */
.strip-segment {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 16px;
  background: transparent;
  border: none;
  border-right: 1px solid var(--border-subtle);
  cursor: pointer;
  transition: background 0.15s;
  font-family: var(--font-mono);
  text-align: left;
  min-width: 0;
}

.strip-segment:last-child { border-right: none; }

.strip-segment:hover {
  background: rgba(245, 246, 248, 0.72);
}

.strip-segment.empty {
  cursor: default;
}

.strip-segment.empty .strip-name { color: var(--text-dim); }

.strip-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--text-dim);
}

.strip-dot.green { background: var(--green); }
.strip-dot.amber { background: var(--amber); }
.strip-dot.red { background: var(--red); }

.strip-name {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.strip-detail {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.3px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ═══════════ SCORECARD OVERALL BAR ═══════════ */
.scorecard-overall {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-md) 0;
  margin-bottom: var(--space-lg);
  border-bottom: 1px solid var(--border-subtle);
}

.scorecard-overall-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.scorecard-overall-score {
  font-size: 24px;
  font-weight: 400;
}

.scorecard-overall-score.green { color: var(--green); }
.scorecard-overall-score.amber { color: var(--amber); }
.scorecard-overall-score.red { color: var(--red); }

.scorecard-overall-detail {
  font-size: 12px;
  color: var(--text-dim);
}

/* ═══════════ SCORECARD DOMAIN HEADER ═══════════ */
.scorecard-domain-header {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-xs);
}

.scorecard-domain-header .section-title {
  margin-bottom: 0;
}

.domain-score-badge {
  font-size: 13px;
  font-weight: 500;
  padding: 2px 8px;
  border-radius: 4px;
}

.domain-flagged-ratio {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  margin-left: var(--space-xs);
}

/* In show-all mode, the domain header reflects "0 of N" for nominal-only
   domains. Same dim label, no special treatment needed. */

.domain-score-badge.green { color: var(--green); background: var(--green-bg); }
.domain-score-badge.amber { color: var(--amber); background: var(--amber-bg); }
.domain-score-badge.red { color: var(--red); background: var(--red-bg); }

/* ═══════════ SCORECARD RANGE BAR (inline) ═══════════ */
.scorecard-range {
  flex: 1;
  min-width: 80px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
}

.range-labels {
  position: relative;
  height: 12px;
}

.range-label-threshold {
  position: absolute;
  transform: translateX(-50%);
  font-size: 10px;
  color: var(--text-dim);
  white-space: nowrap;
  line-height: 1;
}

.scorecard-range .range-bar {
  display: block;
  min-width: 0;
  width: 100%;
  height: 6px;
}

.scorecard-range .range-bar-marker {
  top: -3px;
  width: 3px;
  height: 12px;
}

/* ═══════════ SCORECARD EXPANDABLE CONTEXT ═══════════ */
.scorecard-row.expandable {
  cursor: pointer;
  border-radius: var(--radius-sm);
  padding-left: var(--space-xs);
  margin-left: calc(-1 * var(--space-xs));
  transition: background 0.15s;
}

.scorecard-row.expandable:hover {
  background: var(--bg-hover);
}

.scorecard-row.expanded {
  background: var(--bg-surface);
}

.scorecard-expand-arrow {
  font-size: 11px;
  color: var(--text-dim);
  margin-left: auto;
  opacity: 0.6;
}

.scorecard-context {
  padding: var(--space-sm) var(--space-md);
  margin: 0 0 var(--space-sm);
  border-left: 2px solid var(--border);
  font-size: 12px;
  line-height: 1.6;
  animation: expand-in 0.15s ease-out;
}

.scorecard-context-what {
  color: var(--text-secondary);
  margin-bottom: var(--space-xs);
}

.scorecard-context-detail {
  color: var(--text-dim);
  font-style: italic;
}

.scorecard-context .marker-actions {
  margin-top: var(--space-sm);
}

/* (scorecard nominal groups removed — nominal markers now inline in domain sections) */

/* ═══════════ EXPLAINER POPOVERS ═══════════ */
.explainer-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.explainer-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: none;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  color: var(--text-dim);
  cursor: pointer;
  transition: all 0.15s;
  vertical-align: middle;
  margin-left: 4px;
  flex-shrink: 0;
}

.explainer-btn:hover,
.explainer-wrap:hover .explainer-btn {
  color: var(--accent);
  border-color: var(--accent);
}

.explainer-text {
  position: absolute;
  top: calc(100% + 6px);
  left: -4px;
  z-index: 300;
  width: 280px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-sm) var(--space-md);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-secondary);
  line-height: 1.5;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s;
}

.explainer-wrap:hover .explainer-text {
  opacity: 1;
}

/* ═══════════ TOPBAR HELP ═══════════ */
.topbar-help {
  width: 30px;
  height: 30px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: transparent;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-dim);
  cursor: pointer;
  transition: all 0.15s;
}

.topbar-help:hover {
  color: var(--accent);
  border-color: var(--accent);
}

/* Tour celebration uses this to draw the eye to the re-entry point.
   A two-ring sonar pulse on the help button for the duration of the
   completion toast — the inner ring is saturated, the outer ring is
   a softer halo trailing behind. Faster cadence than the first-time
   toast (which is ambient) because this fires for ~7s and needs to
   read as a directional cue, not background ambience. */
.topbar-help.is-tour-help-pulse {
  color: var(--accent);
  border-color: var(--accent);
  animation: topbarHelpPulse 1.7s cubic-bezier(0.2, 0.6, 0.3, 1) infinite;
}
@keyframes topbarHelpPulse {
  0%, 100% {
    box-shadow:
      0 0 0 0 transparent,
      0 0 0 0 transparent;
  }
  20% {
    box-shadow:
      0 0 0 0 color-mix(in srgb, var(--accent) 28%, transparent),
      0 0 0 0 color-mix(in srgb, var(--accent) 12%, transparent);
  }
  75% {
    box-shadow:
      0 0 0 6px transparent,
      0 0 0 14px transparent;
  }
}
@media (prefers-reduced-motion: reduce) {
  .topbar-help.is-tour-help-pulse { animation: none; }
}

/* While the framework overlay is open, the same button acts as the
   close affordance — visually active and showing × instead of ?. The
   glyph swap is via ::after so the underlying text content stays "?"
   (used by screen readers via the title attribute).

   The active state must read as clearly distinct from :hover on the
   default button, otherwise it looks like a stuck hover. So instead
   of a tinted/bordered look (which mirrors :hover), we go solid:
   filled accent background, white glyph, matching border. This is
   the "engaged / toggle-on" texture — unambiguously different from
   the "you're pointing at me" texture of :hover. */
.topbar-help[aria-expanded="true"] {
  color: #fff;
  border-color: var(--accent);
  background: var(--accent);
  font-size: 0;            /* hide the literal ? */
  position: relative;
}
.topbar-help[aria-expanded="true"]:hover {
  background: #0a7d99;     /* slightly darker accent on hover */
  border-color: #0a7d99;
}
.topbar-help[aria-expanded="true"]::after {
  content: '×';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  font-weight: 500;
  line-height: 1;
}

/* ═══════════ METHOD OVERLAY ═══════════
   The "?" overlay holds three sibling pages — Method (orientation),
   Map (schema), Sources (papers). Shared chrome (page header, sub-nav,
   replay-tour launcher) lives here. Page-specific blocks (.method-*,
   .model-*, .lit-*) follow further down. */

/* Method-page hero. Two columns on wide screens — the value-prop block
   on the left (kicker → headline → three-question teaser) and the
   tour CTA as a self-contained dark pill on the right. The CTA sizes
   to content and centers vertically; we don't stretch it to fill the
   hero, which kept it looking hollow. Stacks under 720px. */
.method-hero {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--space-xl);
  margin-bottom: var(--space-3xl);
  padding: var(--space-xl);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
@media (max-width: 720px) {
  .method-hero {
    grid-template-columns: 1fr;
    padding: var(--space-lg);
    gap: var(--space-lg);
  }
}

.method-hero-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  min-width: 0;
}

/* Labels (kicker, question numbers) stay quiet — accent is reserved
   for semantic leverage (+Xy, modifiable-budget segment) so it doesn't
   lose meaning by appearing on every chrome element. */
.method-hero-kicker {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* One typographic step above the framework-overview-title (22px) used
   by Sources and Model — Method is the orientation page and is allowed
   to read a touch more hero-y, but the gap is small enough that the
   three pages still share one register. */
.method-hero-title {
  font-size: 24px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.3px;
  color: var(--text-primary);
  margin: 0;
  max-width: 28ch;
}
@media (max-width: 720px) {
  .method-hero-title { font-size: 21px; }
}

.method-hero-sub {
  font-size: 13px;
  color: var(--text-secondary);
  letter-spacing: 0.2px;
  margin: var(--space-sm) 0 var(--space-xs) 0;
}

.method-hero-questions {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.method-hero-questions li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 14px;
  color: var(--text-primary);
  letter-spacing: 0.1px;
}
.method-hero-q-num {
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.2px;
  color: var(--text-dim);
  min-width: 22px;
}
.method-hero-q-text {
  font-weight: 400;
}

/* Tour CTA — designed as a stopwatch-dial instrument tag, not a
   generic primary button. The "60-second tour" claim is illustrated
   by the dial itself: 60 fine ticks around the perimeter + 4 quarter
   marks, framed by corner registration brackets on a sharp-cornered
   card. Carries action-weight through typography, framing, and motion
   rather than by borrowing the accent color. */
.method-hero-cta {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: 18px;
  padding: 18px 22px;
  background: var(--accent-soft);
  border: 1px solid rgba(8, 145, 178, 0.28);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  min-width: 300px;
  transition: background-color 200ms ease, border-color 200ms ease, box-shadow 200ms ease, transform 200ms ease;
}
.method-hero-cta:hover,
.method-hero-cta:focus-visible {
  background: rgba(8, 145, 178, 0.13);
  border-color: var(--accent);
  outline: none;
  transform: translateY(-1px);
  box-shadow: 0 6px 18px -10px rgba(8, 145, 178, 0.55);
}
.method-hero-cta:focus-visible {
  box-shadow: 0 0 0 2px var(--bg-panel), 0 0 0 3px var(--accent), 0 6px 18px -10px rgba(8, 145, 178, 0.55);
}

/* Corner registration brackets — typographic crop marks at top-left
   and bottom-right. Drawn with two pseudo-elements; the bracket is
   the union of a top/left border (TL) or bottom/right border (BR). */
.method-hero-cta::before,
.method-hero-cta::after {
  content: '';
  position: absolute;
  width: 9px;
  height: 9px;
  pointer-events: none;
  transition: border-color 200ms ease;
}
.method-hero-cta::before {
  top: 5px;
  left: 5px;
  border-top: 1.5px solid var(--accent);
  border-left: 1.5px solid var(--accent);
}
.method-hero-cta::after {
  bottom: 5px;
  right: 5px;
  border-bottom: 1.5px solid var(--accent);
  border-right: 1.5px solid var(--accent);
}

/* Stopwatch dial — 44px circle housing two ring layers and a glyph.
   Outer ring: 60 thin minute ticks (one per second). Inner ring just
   inside it: 4 longer quarter ticks at 12/3/6/9. The dial center
   holds a static ↻ glyph. On hover, the minute ring rotates one full
   turn (800ms) and the quarter ring rotates a quarter-turn the other
   way — gives the impression of a watch movement winding. */
.method-hero-cta-dial {
  position: relative;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border-radius: 50%;
}

/* Outer minute ring — 60 ticks (every 6°). Mask carves the inner
   area to a hollow ring so the ticks read as a perimeter, not a
   starburst. */
.method-hero-cta-dial-ticks {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: repeating-conic-gradient(
    from -0.3deg,
    var(--accent) 0deg 0.6deg,
    transparent 0.6deg 6deg
  );
  -webkit-mask: radial-gradient(circle, transparent 0 65%, #000 67% 100%);
          mask: radial-gradient(circle, transparent 0 65%, #000 67% 100%);
  transition: transform 800ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* Quarter ring — 4 longer ticks at the cardinal positions. Sits
   slightly inboard of the minute ring and uses a thicker arc so it
   reads as the "hour" register on a watch face. */
.method-hero-cta-dial-quarters {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: repeating-conic-gradient(
    from -0.9deg,
    var(--accent) 0deg 1.8deg,
    transparent 1.8deg 90deg
  );
  -webkit-mask: radial-gradient(circle, transparent 0 55%, #000 57% 65%, transparent 67% 100%);
          mask: radial-gradient(circle, transparent 0 55%, #000 57% 65%, transparent 67% 100%);
  transition: transform 800ms cubic-bezier(0.22, 1, 0.36, 1);
}

.method-hero-cta:hover .method-hero-cta-dial-ticks,
.method-hero-cta:focus-visible .method-hero-cta-dial-ticks {
  transform: rotate(360deg);
}
.method-hero-cta:hover .method-hero-cta-dial-quarters,
.method-hero-cta:focus-visible .method-hero-cta-dial-quarters {
  transform: rotate(-90deg);
}

/* ↻ glyph centered in the dial — static while the rings spin. The
   replay semantics (this tour can be re-watched) live here. */
.method-hero-cta-glyph {
  position: absolute;
  inset: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  font-weight: 500;
  line-height: 1;
  color: var(--accent);
  z-index: 1;
}

.method-hero-cta-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
/* Title — uppercase mono with tracking, matching the
   technical/instrument register established by the dial and brackets. */
.method-hero-cta-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-primary);
  white-space: nowrap;
}
.method-hero-cta-sub {
  font-size: 11px;
  letter-spacing: 0.2px;
  color: var(--text-secondary);
}

.method-hero-cta-arrow {
  flex: 0 0 auto;
  margin-left: auto;
  padding-left: 14px;
  font-size: 14px;
  color: var(--accent);
  transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1), color 200ms ease;
}
.method-hero-cta:hover .method-hero-cta-arrow,
.method-hero-cta:focus-visible .method-hero-cta-arrow {
  color: var(--accent);
  transform: translateX(4px);
}

/* Inline leverage token used in method-page prose to point at the
   leverage color the user sees throughout the dashboard. */
.framework-leverage-token {
  color: var(--leverage);
  font-weight: 600;
}

/* ═══════════ METHOD PAGE ═══════════
   Visual-first orientation surface. Three sections, each carried by a
   figure with a single-paragraph caption underneath. No bullet trios,
   no nested term/definition blocks — the prose is captioning the
   visual, not substituting for it. */

.method-section {
  margin-bottom: var(--space-2xl);
}
.method-section:last-child {
  margin-bottom: 0;
}

/* Numbered kicker + bibliographic-scale title. Method intentionally
   skips the top-hairline anchoring used on Sources/Model — the
   numbered kicker (01 — 02 — 03) already structures the page, and
   stacking three rules across the reading made the surface read
   harder than the prose justifies. */
.method-section-head {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: var(--space-lg);
}

.method-section-kicker {
  display: inline-block;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.method-section-title {
  font-size: 18px;
  font-weight: 500;
  color: var(--text-primary);
  line-height: 1.3;
  letter-spacing: -0.2px;
  margin: 0;
}

/* Brief figcaption sitting muted under each figure — names what the
   visual depicts in a phrase. Echoes the small "median lifespan"
   axis-tick subtext that used to live inside the projection curve;
   now promoted to a real <figcaption> so it labels the figure as a
   whole, not just one tick. */
.method-figcaption {
  display: block;
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.3px;
  text-align: center;
  margin-top: var(--space-md);
}
/* Section 3's figure ends with the cyan Mortality-Profile output card —
   a heavier visual tail than sections 1/2's airy axis ticks. The
   figcaption needs more room above it to breathe. */
.method-figure-inputs .method-figcaption {
  margin-top: var(--space-xl);
}

/* Description paragraph beneath each figure. Generous top margin so
   it sits clearly below the figcaption regardless of how visually
   heavy the figure's tail is — section 3's cyan Mortality-Profile
   output card crowds at a tighter margin. Same gap on all three. */
.method-desc {
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.7;
  margin: var(--space-2xl) 0 0 0;
}

.method-desc strong {
  color: var(--text-primary);
  font-weight: 600;
}

.method-desc em {
  font-style: italic;
  color: var(--text-secondary);
}

/* Shared figure container. Figures sit bare on the page — no border,
   no fill — so the hero card above stays the page's only true "card"
   and the figures read as figures inside an article (kicker + title
   above, caption below). Horizontal margin (not padding — the variant
   classes own padding for their internal layouts) insets the figure
   under its section header so it reads as a child of the section
   rather than a full-bleed sibling. The inset replaces the visual
   cue the old border used to provide. */
.method-figure {
  margin: 0 var(--space-3xl);
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
}
@media (max-width: 720px) {
  .method-figure { margin: 0 var(--space-md); }
}

/* ── Projection figure (survival curves) ───────────────────────────
   Gompertz-style schematic, two variants:
   • 'projection' — single curve with a median marker.
   • 'ceiling'    — same curve + a shifted-right ceiling curve. The lens
     between them is the modifiable-budget shade.
   Both variants share the same viewBox so the projection median sits in
   the same place across figures — section 02 reads as section 01 + gain.
   Schematic only; not personalized. */

.method-figure-curve {
  padding: var(--space-sm) 0 0;
  position: relative;
}

/* aspect-ratio matches the viewBox so circles render circular regardless
   of container width. preserveAspectRatio="none" on the SVG element
   becomes irrelevant once the display aspect matches the viewBox. */
.method-curve-svg {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1000 / 200;
  overflow: visible;
}

/* Curve stroke + fill — the dark stroke reads as the trajectory, the
   soft fill underneath gives it weight without competing with the data. */
.method-curve-stroke {
  fill: none;
  stroke: var(--text-primary);
  stroke-width: 2.5;
  vector-effect: non-scaling-stroke;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.method-curve-stroke--ceil {
  stroke: var(--leverage);
}
.method-curve-area {
  /* fill set inline via url(#…) gradient */
}
.method-curve-baseline {
  stroke: var(--border);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.method-curve-median {
  stroke: var(--text-primary);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  vector-effect: non-scaling-stroke;
  opacity: 0.45;
}
.method-curve-median--ceil {
  stroke: var(--leverage);
  opacity: 0.9;
}
.method-curve-dot {
  fill: var(--bg-panel);
  stroke: var(--text-primary);
  stroke-width: 2.5;
  vector-effect: non-scaling-stroke;
}
.method-curve-dot--ceil {
  stroke: var(--leverage);
}
.method-curve-budget {
  /* fill via url(#…) */
}

/* X-axis ticks — positioned absolutely so they line up with the median
   verticals in the SVG (which use percentage-based x coords in viewBox).
   Each .method-curve-tick is centered on its left: % anchor. */
.method-curve-xaxis {
  position: relative;
  margin-top: var(--space-sm);
  height: 36px;
}
.method-curve-tick {
  position: absolute;
  top: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  transform: translateX(-50%);
  white-space: nowrap;
}
.method-curve-tick[style*="left: 0%"] {
  transform: translateX(0);
  align-items: flex-start;
}
.method-curve-tick-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.method-curve-tick--proj .method-curve-tick-label,
.method-curve-tick--ceil .method-curve-tick-label {
  color: var(--text-primary);
}
.method-curve-tick--ceil .method-curve-tick-label {
  color: var(--leverage);
}

/* ── Inputs figure ─────────────────────────────────────────────────
   Five fixed-height tiles → an SVG of converging paths → an accented
   synthesis card. The card has a two-line reserved name region so
   single-word names ("Genome") and wrapping names ("Personal history")
   render at the same shape. The synthesis is the value prop, so it gets
   the visual weight: cyan accent + tokens listing what's in the profile. */

.method-figure-inputs {
  padding: var(--space-sm) 0 0;
}

/* No column dividers — the five title-underlines align at the same y
   to form their own rhythm. Grid alignment + whitespace structure the
   columns; the synthesis box below earns its own border as the
   climax. */
.method-inputs-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-lg);
}
@media (max-width: 760px) {
  .method-inputs-grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-md); }
}
@media (max-width: 420px) {
  .method-inputs-grid { grid-template-columns: 1fr; }
}

/* Centered so each column's content sits over the point where its flow
   line emerges. Left-aligned text drifted to the side of the column and
   the line looked like it came from empty space. Each input is its own
   bordered tile — once the outer figure frame was dropped, the loose
   columns needed individual boundaries to read as discrete inputs the
   engine consumes. */
.method-input {
  position: relative;
  display: flex;
  flex-direction: column;
  text-align: center;
  padding: var(--space-md) var(--space-sm);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

/* Channel glyph that sits above the kicker. Stroke-based at 24×24 so it
   reads in the same idiom as the engine-pillar icons below. Sized small
   (18px) and centered so all five icons share a baseline and the kicker
   row underneath stays aligned across cards regardless of glyph shape. */
.method-input-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 26px;
  margin: 4px 0 7px;
  color: var(--text-dim);
}
.method-input-icon svg {
  width: 24px;
  height: 24px;
}

/* Channel-number kicker that sits above the title. Stacks vertically
   instead of fighting for space in the top-right corner with long titles
   like 'Demographics'. */
.method-input-tag {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.2px;
  color: var(--text-dim);
  margin-bottom: 4px;
}
/* All five titles are single-word now (History, not Personal history)
   so no 2-line reservation is needed — they all sit at the same y by
   default, and the divider below them aligns across cards. */
.method-input-name {
  font-size: 13px;
  font-weight: 400;
  color: var(--text-primary);
  letter-spacing: 0.2px;
  line-height: 1.25;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}
.method-input-items {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.method-input-items li {
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.4px;
  line-height: 1.5;
}

/* ── Converging-flow synthesis ──────────────────────────────────────
   SVG between the inputs and the engine. Five curved paths converge
   near the SVG's bottom, then a leverage chevron sits right at the
   convergence point and points down into the engine — same grammar
   as the engine→profile connector below, so both transitions share
   one visual idiom. */
.method-inputs-flow {
  position: relative;
  margin-top: var(--space-md);
  display: flex;
  flex-direction: column;
  align-items: center;
}
/* Aspect-ratio locks the SVG to a wide-and-short shape so the convergence
   reads as a flow into a hub regardless of container width. */
.method-inputs-flow-svg {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1000 / 80;
  overflow: visible;
}
.method-flow-line {
  fill: none;
  stroke-width: 1.25;
  vector-effect: non-scaling-stroke;
}
.method-flow-stop-start {
  stop-color: var(--text-dim);
  stop-opacity: 0.12;
}
.method-flow-stop-end {
  stop-color: var(--leverage);
  stop-opacity: 0.9;
}
/* HTML chevron sits right after the SVG. The 5 convergence paths end
   at the SVG's bottom edge (y=80) and the chevron is placed flush
   below — small negative margin pulls it up so the visible stem
   aligns with the convergence point. Matches the engine→profile
   chevron exactly (same character, same size) so the two transitions
   share one visual idiom. */
.method-flow-arrow {
  font-size: 14px;
  line-height: 1;
  color: var(--leverage);
  margin-top: -4px;
}
@media (max-width: 760px) {
  /* On narrow widths the cards stack to a 2-col grid; a 5-stream
     synthesis SVG no longer maps onto card columns. Hide it and let
     the engine bar sit directly beneath. */
  .method-inputs-flow { display: none; }
}

/* ── Engine layer ──────────────────────────────────────────────────
   The middle stage. Five inputs converge into this bar; this is where
   raw signals become a profile. Treated as a "processing node": a
   subtle frame with leverage-accent corner brackets at the four
   corners, borrowed from the hero CTA's instrument language — turns
   the box from a generic UI panel into something that reads as a
   piece of machinery. */
.method-engine {
  position: relative;
  isolation: isolate;
  margin: var(--space-lg) auto 0;
  max-width: 460px;
  width: 100%;
  padding: var(--space-md) var(--space-lg);
  background: transparent;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

/* Four corner brackets — typographic crop marks in the leverage
   accent. Top-left + bottom-right are drawn via ::before/::after on
   .method-engine itself; top-right + bottom-left are drawn via
   pseudo-elements on .method-engine-head (which sits at the top of
   the box and shares its corner geometry). */
.method-engine::before,
.method-engine::after {
  content: '';
  position: absolute;
  width: 9px;
  height: 9px;
  pointer-events: none;
}
.method-engine::before {
  top: -1px;
  left: -1px;
  border-top: 1.5px solid var(--accent);
  border-left: 1.5px solid var(--accent);
}
.method-engine::after {
  bottom: -1px;
  right: -1px;
  border-bottom: 1.5px solid var(--accent);
  border-right: 1.5px solid var(--accent);
}

/* Head row — Aescle mark + ENGINE title, centered on the panel so
   the figure shares one vertical spine (input convergence hub →
   engine head → connector → profile center). The mark inherits the
   leverage accent — a deliberate splash of color that brands the
   engine as Aescle's, matching the topbar's branded mark. Title
   sized up so it reads as the dominant element in the block; the
   pillar labels below are intentionally smaller. */
.method-engine-head {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}
.method-engine-mark {
  display: inline-flex;
  width: 18px;
  height: 18px;
  color: var(--accent);
}
.method-engine-mark svg {
  width: 100%;
  height: 100%;
}
.method-engine-kicker {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--text-primary);
}

/* Three pillars inside the engine — what the framework actually does.
   Each pillar: icon glyph + short label. Icons inherit currentColor
   from the pillar so a single color tweak retones all three. */
.method-engine-pillars {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-lg);
  align-items: start;
}
@media (max-width: 600px) {
  .method-engine-pillars {
    grid-template-columns: 1fr;
    gap: var(--space-sm);
  }
}
.method-engine-pillar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: var(--text-secondary);
  text-align: center;
}
.method-engine-pillar-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  color: var(--text-secondary);
}
.method-engine-pillar-icon svg {
  width: 100%;
  height: 100%;
}
.method-engine-pillar-label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.5px;
  color: var(--text-secondary);
}

/* Connector — leverage line + chevron from engine bottom into the
   profile. Sized and colored to match the input→engine chevron above
   (same 14px, same full-opacity leverage) so both transitions read
   as the same kind of object — one is a confluence, the other a
   delivery, but the visual idiom is identical. */
.method-engine-connector {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: var(--space-md) 0 0;
  pointer-events: none;
}
.method-engine-connector-line {
  display: block;
  width: 1px;
  height: 30px;
  background: var(--leverage);
}
.method-engine-connector-arrow {
  font-size: 14px;
  line-height: 1;
  color: var(--leverage);
  margin-top: -4px;
}

/* The climax — narrower than the engine and centered, so the eye
   reads it as the focused destination. Cyan is softened: a faint
   leverage tint fill + a half-opacity leverage border + a
   secondary-color title. Quieter than a full-strength cyan card, but
   still clearly the destination thanks to its placement at the end
   of the spine and the bracket-framed engine that delivers into it. */
.method-inputs-output {
  position: relative;
  background: var(--leverage-bg);
  color: var(--text-primary);
  border: 1px solid rgba(8, 145, 178, 0.35);
  border-radius: var(--radius-sm);
  padding: 14px var(--space-xl);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  max-width: 380px;
  margin: 4px auto 0;
}
.method-inputs-output-title {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.method-inputs-output-sub {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--text-secondary);
}

/* ── Methodology appendix ──────────────────────────────────────────
   Demoted disclosure for the math/limitations. Most users don't care,
   but it needs to be discoverable for the ones who do. Quieter than a
   section: top rule, kicker + small heading, dim body text. */
.method-appendix {
  margin-top: var(--space-2xl);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--border);
}

.method-appendix-head {
  margin-bottom: var(--space-sm);
}

.method-appendix-kicker {
  display: inline-block;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 4px;
}

.method-appendix-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-secondary);
  letter-spacing: 0.2px;
  margin: 0;
}

.method-appendix-body {
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.7;
  margin: 0;
}
.method-appendix-body strong {
  color: var(--text-secondary);
  font-weight: 600;
}

/* ═══════════ FRAMEWORK SUB-NAV + PAGE HEADER ═══════════
   The overlay carries three sibling pages — Method (prose), Model
   (schema), Sources (papers). The chrome band is one horizontal row
   split into two zones by a vertical hairline: a left section-identity
   block ("How it works / The framework") and the right tab strip
   (Method / Model / Sources). They share the row as structural
   peers — section on the left, pages on the right — rather than as
   a label-above-tabs stack, which has no anchor when the "tabs"
   below are three equal-weight cells. A two-step accent indicator
   runs along the chrome's bottom edge: soft under the section
   block (always-on, "you're in this section") and full-opacity
   under the active tab ("on this page"). Together they form one
   continuous you-are-here strip across the chrome. */

/* Chrome band: full-bleed strip attached to the topbar. The flex
   row lets the section block size to its content (left-anchored
   at the 1080px content column's left edge) and the tab strip
   fill the remaining width. Pinned to the top of the scroll so
   the sub-nav stays reachable as the user reads down the page. */
.framework-pageheader {
  display: flex;
  align-items: stretch;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
  padding: 0 max(var(--space-lg), calc((100% - 1080px) / 2 + var(--space-lg)));
  margin-bottom: 0;
  position: sticky;
  top: 0;
  z-index: 5;
}

/* Section identity zone — left rail of the chrome row. Distinct
   typographic grammar from the tabs: single-line title (14px / 600
   / 2px / --text-secondary), no descriptor below. The big lever
   here is FORMAT not WEIGHT — section is one line, tabs are two —
   which is what makes the section read as a different KIND of
   element (heading vs control). Size is bumped one step above the
   tab labels (14px vs 13px) to reinforce; color stays at
   --text-secondary so the section title doesn't out-compete the
   active tab's --accent for focal weight. Vertical hairline on
   the right matches the chrome band's bottom border, meeting at
   the corner. Not interactive: this is identity, not a control. */
.framework-section {
  position: relative;
  display: flex;
  align-items: center;
  padding: 14px var(--space-lg) 14px var(--space-md);
  margin-right: var(--space-lg);
  border-right: 1px solid var(--border);
  flex-shrink: 0;
  user-select: none;
}
/* Section flag — a small accent rail at the section's left edge,
   vertically centered. Orthogonal to the active-tab indicator's
   horizontal axis, so the two coexist without competing: the
   vertical rail says "this is a marked section," the horizontal
   indicator says "this is the active page." Together they form a
   simple two-axis coordinate system across the chrome (vertical =
   section, horizontal = page) — cleaner than the prior bottom-edge
   accent strip, which left a visible gap between section and
   active-tab when Method was selected. */
.framework-section::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 2px;
  height: 22px;
  background: var(--accent);
}
.framework-section-label {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* Tab strip — fills the right zone of the chrome row. Equal-width
   cells preserved (just on the narrower remaining canvas), so the
   traveling active indicator pattern still works as before. */
.framework-subnav {
  display: flex;
  flex: 1;
  gap: 0;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
}

/* Mobile — shrink the section title and tighten its padding to
   claw back horizontal space for the tabs. Title stays single-line
   and visually dominant relative to tab labels (which keep their
   13px size), just at a less ceremonial scale. */
@media (max-width: 720px) {
  .framework-section {
    padding-right: var(--space-md);
    margin-right: var(--space-md);
  }
  .framework-section-label {
    font-size: 13px;
    letter-spacing: 1.5px;
  }
}
.framework-subnav-tab {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 14px var(--space-lg);
  font-family: inherit;
  cursor: pointer;
  position: relative;
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  text-align: center;
  transition: background-color 160ms ease, color 160ms ease;
}
.framework-subnav-tab:hover {
  background: var(--bg-panel);
}

/* Chrome-scale typography, one step above .section-title (12px) so the
   tabs read as scannable controls but stay subordinate to the topbar's
   primary chrome above. */
.framework-subnav-label {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--text-secondary);
  transition: color 160ms ease;
}
.framework-subnav-hint {
  font-size: 11px;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  transition: color 160ms ease;
}

.framework-subnav-tab:hover .framework-subnav-label {
  color: var(--text-primary);
}

/* Active tab: accent label + a 2px accent segment that sits on the
   hairline rail under just this cell, mirroring the bottom-nav
   indicator pattern. No fill block — the rail + indicator carry the
   weight, and the three cells stay typographically equal. */
.framework-subnav-tab.active .framework-subnav-label {
  color: var(--accent);
}
.framework-subnav-tab.active .framework-subnav-hint {
  color: var(--text-secondary);
}
.framework-subnav-tab.active::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: var(--accent);
}

/* Shared overview block — the orientation header for Model and Sources.
   Mirrors Method's hero structurally (kicker → title → one-line sub →
   optional 3-stat strip) and matches its panel chrome (bordered card
   on bg-panel) so all three tabs share the same top-of-page idiom.
   No tour CTA — Model and Sources are working surfaces, not pitches. */
.framework-overview {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  column-gap: var(--space-xl);
  row-gap: var(--space-xl);
  margin-bottom: var(--space-3xl);
  padding: var(--space-xl);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
@media (max-width: 720px) {
  .framework-overview { padding: var(--space-lg); grid-template-columns: 1fr; }
}

/* Section emblem — mirrors Method's CTA slot in grid col 2. Tile-framed
   icon, content-sized, vertically centered with the head. */
.framework-overview-hero {
  grid-column: 2;
  width: 120px;
  height: 120px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 28px;
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
}
.framework-overview-hero svg { width: 100%; height: 100%; display: block; }

.framework-overview-head {
  grid-column: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  min-width: 0;
}
.framework-overview-stats { grid-column: 1 / -1; }
.framework-overview-kicker {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--text-dim);
}
.framework-overview-kicker-icon {
  display: inline-flex;
  width: 13px;
  height: 13px;
  color: var(--accent);
}
.framework-overview-kicker-icon svg { width: 100%; height: 100%; display: block; }
.framework-overview-title {
  font-size: 22px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.3px;
  color: var(--text-primary);
  margin: 0;
}
.framework-overview-sub {
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.6;
  margin: 0;
}
/* Stats sit inside the overview card, so they shed their own card
   chrome — typographic stats separated by a top hairline read as
   summary metadata for the card above them, not as nested tiles. */
.framework-overview-stats {
  display: grid;
  /* Auto-fit lets the strip grow from 3 → 4 columns when a page passes
     a fourth stat (Sources), and drops cleanly to 2 then 1 on narrow
     viewports without each page declaring its own grid. */
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--space-lg);
  padding-top: var(--space-xl);
  border-top: 1px solid var(--border);
}
@media (max-width: 640px) {
  .framework-overview-stats { grid-template-columns: 1fr; gap: var(--space-md); }
}
/* Stat cell layout — when an icon is present, the cell becomes a
   two-column flex row with the icon anchoring the left edge and the
   number+label stacked to its right. Without an icon (other pages that
   may use renderOverview later), the body div just fills the cell on
   its own. */
.framework-overview-stat {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}
.framework-overview-stat-icon {
  flex: 0 0 auto;
  display: inline-flex;
  width: 28px;
  height: 28px;
  color: var(--text-secondary);
}
.framework-overview-stat-icon svg { width: 100%; height: 100%; display: block; }
.framework-overview-stat-body {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.framework-overview-stat-num {
  font-size: 24px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: -0.5px;
  line-height: 1.2;
}
.framework-overview-stat-label {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 1px;
  text-transform: uppercase;
  margin-top: 8px;
}

/* No-stats variant — Model has only kicker/title/sub, no stat strip
   on the right to occupy the slot a CTA or stats column would fill.
   Shrink the card to its content so the head's 720px cap defines the
   right edge and there's no empty quadrant beside the prose. */
.framework-overview:not(:has(.framework-overview-stats)) {
  width: fit-content;
  max-width: 100%;
}

/* ═══════════ MODEL PAGE — schematic + trace explorer ═══════════
   Two surfaces stacked: a static schematic establishing the
   Actions → Levers → Threats vocabulary, then a trace explorer that
   lets the user walk the graph by clicking. The selection model is
   one anchor at a time; the trace card surfaces its 1-hop neighbours
   in both directions with per-edge years promoted where the data
   carries them (lever ↔ threat). Action edges are connections only. */

/* ── Hero ─────────────────────────────────────────────────────────
   One composed surface: header text, schema flow, caption. Single
   outer card; the flow sits inside chromelessly as a figure inside
   an article. The three layers each get a visual that reflects what
   *kind* of thing the layer is — checklist for actions, sliders for
   levers, mortality bars for threats — so the columns can't be
   mistaken for each other at a glance. */
/* Matches Sources' framework-overview chrome: same padding + gap so
   the three top-of-page cards (Method hero, Model hero, Sources hero)
   read at one rhythm. Internal flow + caption divisions are carried by
   hairlines, not by extra outer padding. */
.model-hero {
  margin: 0 0 var(--space-3xl);
  padding: var(--space-xl);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-xl);
}
.model-hero-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  max-width: 720px;
}
.model-hero-kicker {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--text-dim);
}
.model-hero-title {
  font-size: 22px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.3px;
  color: var(--text-primary);
  margin: 0;
}
.model-hero-lede {
  margin: 0;
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.6;
}

.model-hero-flow {
  display: grid;
  grid-template-columns: 1fr auto 1fr auto 1fr;
  gap: var(--space-md);
  align-items: start;
  padding-top: var(--space-xl);
  border-top: 1px solid var(--border-subtle);
}
@media (max-width: 720px) {
  .model-hero-flow {
    grid-template-columns: 1fr;
    gap: var(--space-lg);
  }
}

.model-layer {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-sm);
  position: relative;
  padding: var(--space-md) var(--space-sm) var(--space-sm);
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  border-radius: 8px;
  transition: transform 0.25s ease, background-color 0.25s ease;
}
.model-layer:hover,
.model-layer:focus-visible {
  transform: translateY(-2px);
  background-color: var(--bg-surface);
}
.model-layer:focus-visible {
  outline: 2px solid var(--leverage);
  outline-offset: 2px;
}
/* Top-stripe encodes the axis: agency (green) → leverage (cyan) →
   severity (red). Matches the trace explorer's row-side bars below. */
.model-layer::before {
  content: "";
  position: absolute;
  inset: 0 20% auto;
  height: 2px;
  background: var(--text-dim);
  border-radius: 1px;
  transition: inset 0.25s ease;
}
.model-layer:hover::before,
.model-layer:focus-visible::before {
  inset: 0 10% auto;
}
.model-layer-action::before { background: var(--green); }
.model-layer-lever::before  { background: var(--leverage); }
.model-layer-threat::before { background: var(--red); }

.model-layer-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.model-layer-name {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
}
.model-layer-action .model-layer-name { color: var(--green); }
.model-layer-lever  .model-layer-name { color: var(--leverage); }
.model-layer-threat .model-layer-name { color: var(--red); }
.model-layer-count {
  font-size: 11px;
  color: var(--text-dim);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.model-visual {
  display: block;
  width: 90px;
  height: 56px;
}
.model-layer-action .model-visual { color: var(--green); }
.model-layer-lever  .model-visual { color: var(--leverage); }
.model-layer-threat .model-visual { color: var(--red); }

/* ─── Visual hover animations ─────────────────────────────────────────
   Each layer's mini-diagram comes alive on hover with motion that maps
   to its semantic: actions tick themselves off, levers slide their
   markers, threats shrink their bars. SVG geometry properties (cx,
   x2, y, height) are animated via CSS — modern browsers only. */

/* Actions — staggered "todo completes itself" */
.model-action-row .model-action-box {
  fill-opacity: 0;
  opacity: 0.6;
  transition: fill-opacity 0.3s ease, opacity 0.3s ease;
}
.model-action-row .model-action-check {
  stroke-dasharray: 10;
  stroke-dashoffset: 10;
  transition: stroke-dashoffset 0.35s ease;
}
.model-action-row .model-action-line {
  opacity: 0.45;
  transition: opacity 0.3s ease;
}
.model-action-row.is-checked .model-action-box  { fill-opacity: 1; opacity: 1; }
.model-action-row.is-checked .model-action-check { stroke-dashoffset: 0; }
.model-action-row.is-checked .model-action-line { opacity: 0.8; }

.model-layer-action:hover .model-action-row:not(.is-checked) .model-action-box,
.model-layer-action:focus-visible .model-action-row:not(.is-checked) .model-action-box {
  fill-opacity: 1;
  opacity: 1;
  transition-delay: calc(var(--i, 0) * 110ms);
}
.model-layer-action:hover .model-action-row:not(.is-checked) .model-action-check,
.model-layer-action:focus-visible .model-action-row:not(.is-checked) .model-action-check {
  stroke-dashoffset: 0;
  transition-delay: calc(var(--i, 0) * 110ms + 90ms);
}
.model-layer-action:hover .model-action-row:not(.is-checked) .model-action-line,
.model-layer-action:focus-visible .model-action-row:not(.is-checked) .model-action-line {
  opacity: 0.8;
  transition-delay: calc(var(--i, 0) * 110ms);
}

/* Levers — staggered "marker glides" */
.model-lever-bg {
  stroke: currentColor;
  stroke-width: 1;
  opacity: 0.35;
}
.model-lever-fill {
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  transition: x2 0.6s cubic-bezier(0.4, 0, 0.2, 1);
  transition-delay: calc(var(--i, 0) * 90ms);
}
.model-lever-dot {
  fill: #fff;
  stroke: currentColor;
  stroke-width: 1.6;
  transition: cx 0.6s cubic-bezier(0.4, 0, 0.2, 1);
  transition-delay: calc(var(--i, 0) * 90ms);
}
.model-layer-lever:hover .model-lever-fill,
.model-layer-lever:focus-visible .model-lever-fill { x2: var(--to); }
.model-layer-lever:hover .model-lever-dot,
.model-layer-lever:focus-visible .model-lever-dot   { cx: var(--to); }

/* Threats — staggered "slam down" with overshoot, ghost outlines
   fade in to mark the reclaimed gap, baseline pulses brighter. */
.model-threat-ghost {
  opacity: 0;
  transition: opacity 0.45s ease;
  transition-delay: calc(var(--i, 0) * 80ms);
}
.model-threat-bar {
  transition: y 0.55s cubic-bezier(0.34, 1.56, 0.64, 1),
              height 0.55s cubic-bezier(0.34, 1.56, 0.64, 1),
              opacity 0.3s ease;
  transition-delay: calc(var(--i, 0) * 80ms);
}
.model-threat-base {
  transition: opacity 0.3s ease, stroke-width 0.3s ease;
}
.model-layer-threat:hover .model-threat-ghost,
.model-layer-threat:focus-visible .model-threat-ghost {
  opacity: calc(var(--base-opacity, 0.6) * 0.55);
}
.model-layer-threat:hover .model-threat-bar,
.model-layer-threat:focus-visible .model-threat-bar {
  y: var(--to-y);
  height: var(--to-h);
}
.model-layer-threat:hover .model-threat-base,
.model-layer-threat:focus-visible .model-threat-base {
  opacity: 0.85;
  stroke-width: 1.4;
}

@media (prefers-reduced-motion: reduce) {
  .model-layer,
  .model-layer::before,
  .model-action-box,
  .model-action-check,
  .model-action-line,
  .model-lever-fill,
  .model-lever-dot,
  .model-threat-bar,
  .model-threat-ghost,
  .model-threat-base {
    transition: none !important;
  }
}

.model-layer-def {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-primary);
  letter-spacing: 0.2px;
}
.model-layer-examples {
  font-size: 11px;
  color: var(--text-dim);
  line-height: 1.5;
  max-width: 200px;
  letter-spacing: 0.2px;
}

.model-edge {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  color: var(--text-dim);
  min-width: 96px;
  padding-top: 56px;
}
.model-edge-svg {
  width: 80px;
  height: 12px;
}
.model-edge-verb {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-secondary);
}
@media (max-width: 720px) {
  .model-edge {
    padding-top: 0;
    min-width: 0;
    flex-direction: row;
    gap: var(--space-sm);
  }
  .model-edge-svg { transform: rotate(90deg); width: 40px; }
}

.model-hero-caption {
  margin: 0;
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.7;
  text-align: center;
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
  padding-top: var(--space-lg);
  border-top: 1px solid var(--border-subtle);
}

/* ── Trace explorer ───────────────────────────────────────────────
   Picker rail (left) + trace card (right). The rail lists anchors
   of the active kind; the card is the focused neighborhood of the
   selected anchor. Kind tabs sit above both. */
.trace {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
/* Same anchoring pattern as Sources' lit-sect-head — top hairline, a
   primary-colored uppercase kicker as the "section title", a dim
   one-line sub. Pulls the trace explorer into the same section-break
   register the rest of the overlay uses. */
.trace-head {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: var(--space-lg);
  border-top: 1px solid var(--border);
}
.trace-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: 1.8px;
  text-transform: uppercase;
  margin: 0;
}
.trace-sub {
  margin: 0;
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.6;
  letter-spacing: 0.2px;
}

.trace-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border);
}
.trace-tab {
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  padding: var(--space-sm) var(--space-md);
  font-family: inherit;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: -1px;
  transition: color 120ms ease, border-color 120ms ease;
}
/* Per-kind glyph that sits before the label — bars / sliders / check.
   Picks up the active tab's accent via currentColor; resting tabs
   inherit the dim label color so the icons stay paired with their
   text rather than out-shouting it. */
.trace-tab-icon {
  display: inline-flex;
  width: 16px;
  height: 16px;
  color: var(--text-dim);
  transition: color 120ms ease;
}
.trace-tab-icon svg { width: 100%; height: 100%; display: block; }
.trace-tab:hover .trace-tab-icon { color: var(--text-secondary); }
.trace-tab.active .trace-tab-icon { color: inherit; }
.trace-tab-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-dim);
}
.trace-tab-count {
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.trace-tab:hover .trace-tab-label { color: var(--text-secondary); }
.trace-tab.active {
  border-bottom-color: currentColor;
}
.trace-tab-threat.active { color: var(--red); }
.trace-tab-lever.active  { color: var(--leverage); }
.trace-tab-action.active { color: var(--green); }
.trace-tab.active .trace-tab-label { color: inherit; }

.trace-body {
  display: grid;
  grid-template-columns: minmax(220px, 280px) 1fr;
  gap: var(--space-md);
  /* Top-aligned columns: rail flows to natural height, card sticks so
     it stays in view as the page scrolls past long rails. No nested
     scroll container means no hidden-scrollbar problem to fight. */
  align-items: start;
}
@media (max-width: 920px) {
  .trace-body { grid-template-columns: 1fr; }
}

/* ── Anchor rail ── */
.trace-rail {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.trace-rail-group {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.trace-rail-group + .trace-rail-group { margin-top: var(--space-md); }
.trace-rail-group-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  padding: 14px 10px 6px 14px;
}
.trace-rail-group:first-child .trace-rail-group-head { padding-top: 6px; }
.trace-rail-group-label {
  font-size: 10px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 600;
}
.trace-rail-row {
  appearance: none;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 6px 10px 6px 20px;
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  display: flex;
  align-items: center;
  gap: 8px;
  position: relative;
  transition: background-color 120ms ease;
}
.trace-rail-icon {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--text-dim);
  transition: color 120ms ease;
}
.trace-rail-row:hover .trace-rail-icon,
.trace-rail-row.active .trace-rail-icon { color: var(--red); }
.trace-rail-row:hover {
  background: var(--bg-hover);
}
.trace-rail-row.active {
  background: var(--bg-hover);
}
.trace-rail-row.active::before {
  content: "";
  position: absolute;
  left: 10px;
  top: 6px;
  bottom: 6px;
  width: 2px;
  border-radius: 1px;
  background: var(--text-primary);
}
.trace-rail-row.active .trace-rail-label {
  color: var(--text-primary);
  font-weight: 500;
}
.trace-rail-main {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1 1 auto;
}
.trace-rail-label {
  font-size: 12px;
  font-weight: 400;
  color: var(--text-primary);
  line-height: 1.4;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.trace-rail-sub {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.3px;
  text-transform: uppercase;
}
.trace-rail-meta {
  font-size: 11px;
  font-weight: 400;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  flex: 0 0 auto;
}
.trace-rail-row.active .trace-rail-meta { color: var(--text-secondary); }

/* ── Trace track ───────────────────────────────────────────────────
   Three fixed bands stacked vertically: Threats / Levers / Actions.
   The selected node lives in its own band as a focal headline; the
   other two bands list its 1-hop neighbours. Bands never swap
   positions — the geometry is the lesson.

   No bordered card-in-card. The outer container is a single quiet
   surface; each band is separated by a hairline. Neighbour rows are
   hairline-divided too — list, not tile grid. */
.trace-track {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.trace-band {
  padding: var(--space-md) var(--space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  border-top: 1px solid var(--border);
}
.trace-band:first-child { border-top: 0; }

/* Focal band — slightly more padding and a subtle tint so the eye
   lands on the selected node first. The band still doesn't change
   position; only the focal band carries this treatment. */
.trace-band.is-focal {
  padding-top: var(--space-lg);
  padding-bottom: var(--space-lg);
  background: var(--bg-surface);
}

.trace-band-head {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
/* Per-kind glyph at the head of each band — same icon set as the
   trace tabs above. Color tracks the layer (red / cyan / green) so
   the band identity reads at a glance as the user scrolls the focal
   card, mirroring the active-tab color above. */
.trace-band-icon {
  display: inline-flex;
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
}
.trace-band-icon svg { width: 100%; height: 100%; display: block; }
.trace-band-icon { color: var(--text-dim); }
.trace-band.is-focal.trace-band-threat .trace-band-icon { color: var(--red); }
.trace-band.is-focal.trace-band-lever  .trace-band-icon { color: var(--leverage); }
.trace-band.is-focal.trace-band-action .trace-band-icon { color: var(--green); }
.trace-band-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.trace-band-count {
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.trace-band-empty {
  margin: 0;
  font-size: 12px;
  color: var(--text-dim);
  font-style: italic;
}
.trace-band-group-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  padding: var(--space-sm) 14px 2px;
}
.trace-band-group-head:first-of-type { padding-top: 0; }
.trace-band-group-label {
  font-size: 10px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-dim);
}
/* Focal node — name + optional sub + meta line. Medium weight
   (not bold) on the name; the band dot and the "here" tag carry
   the visual emphasis instead of typographic weight. */
.trace-focal {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-left: 14px; /* aligns under the dot + label rhythm */
}
.trace-focal.has-icon {
  flex-direction: row;
  align-items: flex-start;
  gap: var(--space-md);
}
.trace-focal-icon {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--red);
  margin-top: 2px;
}
.trace-focal-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  flex: 1 1 auto;
}
.trace-focal-name {
  font-size: 17px;
  font-weight: 500;
  color: var(--text-primary);
  letter-spacing: -0.2px;
  line-height: 1.3;
}
.trace-focal-sub {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.5;
}
.trace-focal-meta {
  font-size: 12px;
  color: var(--text-secondary);
  margin-top: 2px;
}
.trace-focal-desc {
  margin: var(--space-sm) 0 0;
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.6;
  max-width: 64ch;
}
/* Focal "View sources" affordance — quiet bottom-bordered text link,
   matches the .lit-entry-link / .lit-cs-link grammar on the Sources
   page so the transition reads as continuous. */
.trace-focal-sources {
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 1px solid transparent;
  margin: var(--space-md) 0 0;
  padding: 1px 0 2px;
  align-self: flex-start;
  cursor: pointer;
  font-family: inherit;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.6px;
  color: var(--accent);
  transition: border-color 120ms ease;
}
.trace-focal-sources:hover { border-bottom-color: var(--accent); }
.trace-focal-sources:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Neighbour list — hairline-divided rows, no per-row border,
   no per-row background. */
.trace-band-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.trace-band-list > li + li { border-top: 1px solid var(--border-subtle); }
.trace-row {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 8px 14px 8px 14px;
  margin: 0;
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  width: 100%;
  transition: background-color 120ms ease;
}
.trace-row:hover { background: var(--bg-hover); }
.trace-row-main {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-width: 0;
  flex: 1 1 auto;
}
.trace-row-icon {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--text-dim);
  transition: color 120ms ease;
}
.trace-row:hover .trace-row-icon { color: var(--red); }
.trace-row-name {
  font-size: 13px;
  font-weight: 400;
  color: var(--text-primary);
  line-height: 1.4;
}
.trace-row-sub {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.3px;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.trace-row-years {
  font-size: 12px;
  font-weight: 400;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
  white-space: nowrap;
  flex: 0 0 auto;
}
/* Neutralize the leverage token inside the trace surface — the rows
   already form a column of Ny values; the color stripe is noise here. */
.trace-band .framework-leverage-token {
  color: inherit;
  font-weight: 400;
}
.trace-priority {
  font-size: 10px;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--text-dim);
  white-space: nowrap;
  flex: 0 0 auto;
}

/* ═══════════ LITERATURE PAGE — bibliography reading list ═══════════
   Editorial table of contents, not a tile grid. Three layers stacked:

     1) Authorities masthead — italic wordmarks grouped into peer-
        reviewed journals and clinical guideline bodies. The social
        proof sits before any individual entry; the eye reads the
        institutions before reading a single takeaway.
     2) Filter rail — typographic taxonomy index, not chip pills. One
        line of text labels with a thin underline marking the active
        filter. Reads as a catalog filter, not a button bar.
     3) Bibliography list — each paper a two-column entry: a margin
        sidebar (year, journal, italic citation marginalia) and a body
        (title, italic abstract, threat + lever tag rows, link).
        Hairlines between entries; no card backgrounds, no card
        borders. The column rhythm and the typography do all the work.

   Citation counts are deliberately demoted — ~10px italic gray, the
   way a footnote sits beside a paragraph, not the way a stat sits in a
   dashboard. The year is the load-bearing identifier in each row.
   The page reads slowly and looks scholarly. */

.lit-board {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

/* ── Foundation reading (cornerstones triptych) ───────────────────
   No outer container — to avoid stacking two near-identical
   rounded rectangles, the section sheds its mat and lets three
   pillar cards float directly on the page background. The
   centered header sits in open space above them, marking a
   "section break" rather than a second hero panel. Topology is
   the differentiator: overview = one card; foundation = a row of
   discrete elements with no enclosing frame. */
.lit-cornerstones {
  position: relative;
  margin: var(--space-xl) 0 var(--space-2xl);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

/* Shared section-head pattern across the Sources page. A top hairline
   anchors each section as its own stratum; the kicker + lede sit
   left-aligned with a constrained measure so heads read as editorial
   intros even when the content below spans full width. Without this
   structure the page reads flat — same width, same weight, no eye
   anchors between Foundation Reading / Browse by Domain / Bibliography. */
.lit-sect-head {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: var(--space-lg);
  border-top: 1px solid var(--border);
}
.lit-sect-kicker {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--text-primary);
}
.lit-sect-sub {
  font-size: 12px;
  color: var(--text-dim);
  line-height: 1.6;
  margin: 0;
  max-width: 64ch;
}
.lit-sect-sub em {
  font-style: normal;
  font-weight: 500;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.lit-sect-sub strong {
  color: var(--text-primary);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Bibliography head sits below the medallion grid with a touch more
   air above so the list reads as its own section, not a continuation
   of the grid. */
.lit-list-head {
  margin-top: var(--space-lg);
}

/* Cornerstones head — left-aligned (override the prior centered
   layout); inherits .lit-sect-head structure above. */
.lit-cornerstones-head { /* placeholder kept for future overrides */ }

.lit-cornerstones-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md);
  align-items: stretch;
}
@media (max-width: 960px) {
  .lit-cornerstones-grid { grid-template-columns: 1fr; }
}

/* Pillars: bordered white cards arranged as an editorial entry —
   left-aligned, three zones (header / body / footer). Header pairs
   a framed icon with the topic kicker and source line; body holds
   the slug + takeaway; footer is the stat strip. Title-link
   stretches across the whole card for click. */
.lit-cornerstone {
  position: relative;
  display: grid;
  grid-template-rows: auto 1fr auto;
  align-items: start;
  row-gap: var(--space-md);
  padding: var(--space-md);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-align: left;
  opacity: 0;
  transform: translateY(12px);
  animation: lit-cs-rise 560ms cubic-bezier(0.22, 0.61, 0.36, 1) var(--cs-delay, 0ms) forwards;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}
.lit-cornerstone:hover {
  border-color: rgba(8, 145, 178, 0.45);
  box-shadow: 0 1px 0 rgba(8, 145, 178, 0.06);
}
@keyframes lit-cs-rise {
  to { opacity: 1; transform: translateY(0); }
}

/* Stretched click target — anchor occupies the whole card so the
   user doesn't have to aim at the title. Visually invisible. */
.lit-cs-title-link {
  position: absolute;
  inset: 0;
  z-index: 1;
  text-indent: -9999px;
  overflow: hidden;
}

/* Header zone — icon left, topic + venue text right. Tight little
   masthead at the top of every card. */
.lit-cs-head {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  width: 100%;
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--border-subtle);
}
.lit-cs-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
}
.lit-cs-icon svg {
  width: 24px;
  height: 24px;
  display: block;
}
.lit-cs-head-text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.lit-cs-topic-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--accent);
}
.lit-cs-source {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.2px;
}
.lit-cs-source-venue { font-style: italic; color: var(--text-secondary); }
.lit-cs-source-year { font-variant-numeric: tabular-nums; }
.lit-cs-source-sep { opacity: 0.55; }

/* Body zone — slug on top (small italic, since the labels are slug-
   like, not real titles), takeaway below as the primary read. Left-
   aligned monospace at 12.5px / 1.6 reads cleanly. */
.lit-cs-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-self: start;
}
.lit-cs-slug {
  font-size: 11px;
  font-style: italic;
  color: var(--text-secondary);
  line-height: 1.45;
  margin: 0;
}
.lit-cs-takeaway {
  font-size: 12.5px;
  font-weight: 400;
  color: var(--text-primary);
  line-height: 1.6;
  margin: 0;
}

/* Footer stat strip — left-aligned to match the rest of the card,
   separated by a hairline. */
.lit-cs-reach {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: flex-start;
  gap: 10px;
  padding-top: var(--space-sm);
  border-top: 1px solid var(--border-subtle);
  width: 100%;
}
.lit-cs-stat {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
}
.lit-cs-stat-num {
  font-size: 12.5px;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  letter-spacing: -0.1px;
}
.lit-cs-stat-label {
  font-size: 10px;
  letter-spacing: 0.4px;
  text-transform: lowercase;
  color: var(--text-dim);
}
.lit-cs-stat-hero .lit-cs-stat-num {
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -0.1px;
}
.lit-cs-stat-standard {
  text-transform: uppercase;
  font-weight: 600;
  letter-spacing: 1px;
  font-size: 9px;
  color: var(--text-secondary);
}
.lit-cs-stat-sep { color: var(--text-dim); opacity: 0.45; font-size: 11px; }

/* ── Authorities masthead ─────────────────────────────────────────
   Two grouped rows of italic wordmarks beneath the hero. Roman
   numerals on each row head reinforce the editorial register.
   Hairlines top + bottom, no panel chrome — the strip lives on the
   page background, not in a card, so it reads as a masthead, not a
   sidebar widget. */
.lit-mast {
  margin: var(--space-2xl) 0 var(--space-2xl);
  padding: var(--space-xl) 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}
.lit-mast-head {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  max-width: 640px;
}
.lit-mast-kicker {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--text-dim);
}
.lit-mast-lede {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.7;
  margin: 0;
}
.lit-mast-lede em {
  font-style: italic;
  font-weight: 500;
  color: var(--text-primary);
}

.lit-mast-row {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: var(--space-xl);
  align-items: baseline;
  padding-top: var(--space-md);
  border-top: 1px solid var(--border-subtle);
}
@media (max-width: 720px) {
  .lit-mast-row {
    grid-template-columns: 1fr;
    gap: var(--space-sm);
  }
}
.lit-mast-rowhead {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
}
.lit-mast-rowhead-roman {
  font-size: 12px;
  font-style: italic;
  font-weight: 400;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  width: 18px;
  letter-spacing: 0.5px;
}
.lit-mast-rowhead-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.lit-mast-rowhead-count {
  font-size: 10px;
  font-weight: 600;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  margin-left: auto;
}
.lit-mast-rowbody {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  min-width: 0;
}

/* Tier-1 stamps — bordered bibliographic plates for high-prestige
   venues. The border gives the names visual weight against the
   italicized long-tail list that follows, and the row reads as a
   masthead row of journal nameplates. */
.lit-mast-stamps {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 8px;
  list-style: none;
  padding: 0;
  margin: 0;
}
.lit-mast-stamp {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: var(--bg-panel);
  transition: border-color 120ms ease, color 120ms ease;
}
.lit-mast-stamp:hover {
  border-color: var(--text-dim);
}
.lit-mast-stamp-name {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-primary);
  letter-spacing: -0.1px;
}
.lit-mast-stamp-count {
  font-size: 10px;
  font-weight: 500;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.3px;
}

.lit-mast-list {
  display: flex;
  flex-wrap: wrap;
  gap: 4px var(--space-md);
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: 12px;
  line-height: 1.7;
}
.lit-mast-item {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
}
.lit-mast-name {
  font-style: italic;
  font-weight: 400;
  color: var(--text-secondary);
  letter-spacing: 0.1px;
}
.lit-mast-count {
  font-size: 9px;
  font-weight: 500;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.4px;
  position: relative;
  top: -1px;
}

/* Guideline bodies — clean acronyms (USPSTF · KDIGO · WHO ...). Read
   as institutional shorthand, not italicized prose. */
.lit-mast-bodies {
  display: flex;
  flex-wrap: wrap;
  gap: 4px var(--space-md);
  list-style: none;
  padding: 0;
  margin: 0;
}
.lit-mast-body {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font-size: 12px;
  line-height: 1.6;
}
.lit-mast-body-name {
  font-weight: 600;
  letter-spacing: 0.4px;
  color: var(--text-secondary);
}
.lit-mast-body-count {
  font-size: 9px;
  font-weight: 500;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.4px;
  position: relative;
  top: -1px;
}

/* ── Scope banner ─────────────────────────────────────────────────
   Shown only when Sources is opened from the Model page's focal
   "View sources" affordance. Replaces the category chip rail in
   that mode — a single banner row that names the threat / lever /
   action being scoped to, with a Clear button to return to the
   full list. Uses the accent rail color so it reads as a different
   register from the regular category filter. */
.lit-scope {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 12px 14px;
  margin-bottom: var(--space-lg);
  background: rgba(8, 145, 178, 0.06);
  border: 1px solid rgba(8, 145, 178, 0.22);
  border-radius: var(--radius-md);
}
.lit-scope-kicker {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--accent);
}
.lit-scope-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: -0.1px;
}
.lit-scope-count {
  font-size: 11px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.lit-scope-clear {
  appearance: none;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 4px 10px;
  margin-left: auto;
  cursor: pointer;
  font-family: inherit;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--text-secondary);
  transition: border-color 120ms ease, color 120ms ease;
}
.lit-scope-clear:hover {
  color: var(--text-primary);
  border-color: var(--text-secondary);
}

/* ── Domain medallions ────────────────────────────────────────────
   Ten clickable cards — one per threat — that replace the old chip
   filter. Each medallion carries a cause-of-death glyph (the same
   icons used on the Model page), the threat name, its paper count,
   and a thin weight bar normalized to the highest-paper domain.
   The grid reads two ways at once: a credibility scan (every domain
   is covered) and the page's primary filter (click to scope the
   bibliography below). */
.lit-domains {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  padding-bottom: var(--space-lg);
}
/* Filtered-state row layout — the kicker, lede, and Show-all button
   collapse onto one line. Overrides the column layout from
   .lit-sect-head. The accent recolor signals the filter is active. */
.lit-domains-head.is-filtered {
  flex-direction: row;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-md);
  max-width: none;
}
.lit-domains-head.is-filtered .lit-sect-kicker { color: var(--accent); }
.lit-domains-clear {
  appearance: none;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 4px 10px;
  margin-left: auto;
  cursor: pointer;
  font-family: inherit;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--text-secondary);
  transition: border-color 120ms ease, color 120ms ease;
}
.lit-domains-clear:hover {
  color: var(--text-primary);
  border-color: var(--text-secondary);
}

.lit-domains-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-sm);
  list-style: none;
  padding: 0;
  margin: 0;
}
@media (max-width: 960px) {
  .lit-domains-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .lit-domains-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.lit-domain {
  appearance: none;
  width: 100%;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-md) var(--space-sm) 0;
  font-family: inherit;
  text-align: center;
  cursor: pointer;
  display: grid;
  grid-template-rows: auto auto auto 4px;
  row-gap: 6px;
  align-items: start;
  justify-items: center;
  position: relative;
  overflow: hidden;
  transition:
    border-color 140ms ease,
    background-color 140ms ease,
    transform 140ms ease,
    opacity 180ms ease;
}
.lit-domain:hover {
  border-color: var(--text-dim);
  background: var(--bg-surface);
  transform: translateY(-1px);
}
.lit-domain:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.lit-domain.active {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.lit-domain.dim {
  opacity: 0.42;
}
.lit-domain.dim:hover {
  opacity: 0.85;
}
.lit-domain.empty {
  cursor: not-allowed;
  opacity: 0.32;
}
.lit-domain.empty:hover {
  transform: none;
  background: var(--bg-panel);
  border-color: var(--border);
}

/* Resting icon is muted graphite — the grid is a credibility scan,
   not a severity readout. Saturated red would conflate "we have
   evidence here" with "you are in danger here" (the meaning the same
   glyph carries on Risks/Model). Active state picks up the accent. */
.lit-domain-icon {
  display: inline-flex;
  color: var(--text-secondary);
  width: 32px;
  height: 32px;
  transition: color 140ms ease;
}
.lit-domain-icon svg { width: 100%; height: 100%; display: block; }
.lit-domain.active .lit-domain-icon { color: var(--accent); }
.lit-domain:hover:not(.active):not(.empty) .lit-domain-icon { color: var(--text-primary); }

.lit-domain-name {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-secondary);
  line-height: 1.3;
  max-width: 100%;
  word-break: break-word;
}
.lit-domain.active .lit-domain-name { color: var(--text-primary); }

.lit-domain-count {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  padding-bottom: var(--space-sm);
}
.lit-domain-count-num {
  font-size: 18px;
  font-weight: 600;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.3px;
  line-height: 1;
}
.lit-domain-count-label {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.4px;
}
.lit-domain.active .lit-domain-count-num { color: var(--accent); }
.lit-domain.empty .lit-domain-count-num { color: var(--text-dim); }

/* Weight bar — sits flush at the bottom edge, width proportional to
   that domain's share of the corpus. Quiet by default; saturates on
   the active medallion. */
.lit-domain-bar {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 3px;
  width: calc(var(--weight, 0) * 100%);
  background: linear-gradient(to right, color-mix(in srgb, var(--text-dim) 45%, transparent), color-mix(in srgb, var(--text-secondary) 70%, transparent));
  transition: background 140ms ease, width 220ms ease;
}
.lit-domain.active .lit-domain-bar {
  background: linear-gradient(to right, color-mix(in srgb, var(--accent) 55%, transparent), var(--accent));
}
.lit-domain.dim .lit-domain-bar { opacity: 0.5; }
.lit-domain.empty .lit-domain-bar { background: transparent; }

/* ── Bibliography list ────────────────────────────────────────────
   Single-column entries with a top meta line ("journal · year ·
   citations"), then the title (the loudest element in the row), then
   the italic abstract, then the tag rows, then the link. Hairline
   dividers; no card chrome. The title is what the eye lands on first;
   everything else reads as supporting metadata around it. */
.lit-list {
  display: flex;
  flex-direction: column;
}
.lit-entry {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-xl) 0;
  border-bottom: 1px solid var(--border);
}
@media (max-width: 720px) {
  .lit-entry { padding: var(--space-lg) 0; }
}

/* Bibliographic header — one inline phrase. Italic for the venue
   (the journal name, in the way bibliographies italicize publications),
   normal for the year and citation count, all small and dim. The
   separator is its own span so the gap is the same on both sides
   regardless of the surrounding text. */
.lit-entry-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.2px;
  line-height: 1.5;
}
.lit-entry-meta-venue {
  font-style: italic;
  font-weight: 500;
  color: var(--text-secondary);
}
.lit-entry-meta-year {
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
}
.lit-entry-meta-cite {
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}
.lit-entry-meta-standard {
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  font-size: 9px;
  color: var(--text-secondary);
}
.lit-entry-meta-sep {
  color: var(--text-dim);
  opacity: 0.55;
}

.lit-entry-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.4;
  letter-spacing: -0.1px;
  margin: 0;
}
.lit-entry-abstract {
  font-size: 13px;
  font-style: italic;
  color: var(--text-secondary);
  line-height: 1.75;
  margin: 4px 0 var(--space-sm);
  max-width: 72ch;
}

/* Tag rows: small uppercase label in a fixed-width column on the
   left, the tags themselves on the right. Two-column rhythm
   echoes the entry-level sidebar/body split. */
.lit-entry-row {
  display: grid;
  grid-template-columns: 120px 1fr;
  gap: var(--space-md);
  align-items: baseline;
}
@media (max-width: 720px) {
  .lit-entry-row {
    grid-template-columns: 1fr;
    gap: 4px;
  }
}
.lit-entry-row-label {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-dim);
  line-height: 1.6;
}
.lit-entry-row-list {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: baseline;
}
.lit-entry-threat {
  font-size: 10px;
  letter-spacing: 0.3px;
  color: var(--text-secondary);
  padding: 2px 7px;
  border: 1px solid var(--border);
  border-radius: 3px;
  white-space: nowrap;
}
.lit-entry-lever {
  font-size: 10px;
  color: var(--leverage);
  background: var(--leverage-bg);
  padding: 2px 7px;
  border-radius: 3px;
  letter-spacing: 0.2px;
  white-space: nowrap;
}
.lit-entry-more {
  font-size: 10px;
  font-style: italic;
  color: var(--text-dim);
  letter-spacing: 0.2px;
  padding: 2px 4px;
}

.lit-entry-link {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.6px;
  color: var(--accent);
  text-decoration: none;
  align-self: flex-start;
  margin-top: 4px;
  padding-bottom: 1px;
  border-bottom: 1px solid transparent;
  transition: border-color 120ms ease;
}
.lit-entry-link:hover { border-bottom-color: var(--accent); }

.lit-empty {
  padding: var(--space-2xl) 0;
  text-align: center;
  font-size: 13px;
  font-style: italic;
  color: var(--text-dim);
}

/* ═══════════ GENETICS STATUS ═══════════ */
.genetics-status {
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.5px;
  margin-top: var(--space-md);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--border-subtle);
}

/* ═══════════ THREAT WATCH ROWS ═══════════ */
.threat-watch-row {
  display: grid;
  grid-template-columns: 3px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 9px 12px 9px 0;
  text-align: left;
  min-width: 0;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.threat-watch-row .watch-name {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.threat-watch-row .watch-value {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.threat-watch-row.quiet {
  opacity: 0.5;
}
.threat-watch-row.quiet .watch-value {
  color: var(--text-secondary);
  font-weight: 400;
}

/* ═══════════ BASELINE LE BREAKDOWN (inside hero card) ═══════════ */
.baseline-breakdown {
  display: flex;
  flex-direction: column;
  text-align: left;
}

.baseline-breakdown .le-tree-row {
  font-size: 12px;
}

.baseline-breakdown .le-tree-label {
  color: var(--text-dim);
}

.baseline-breakdown .expand-panel {
  padding: 6px var(--space-md);
  margin: 2px 0 6px var(--space-md);
  border-left: 1px solid var(--border-subtle);
  font-size: 12px;
  line-height: 1.6;
}

.baseline-breakdown .expand-source {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  text-transform: uppercase;
  margin-bottom: 4px;
}

.baseline-breakdown .expand-detail {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.6;
}

.baseline-breakdown .expandable.expanded {
  background: transparent;
}

/* ═══════════ BASELINE INLINE CITATION LINKS ═══════════ */
.baseline-cite {
  display: inline-block;
  align-self: flex-start;
  margin-top: var(--space-xs);
  padding: 4px 0;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  text-decoration: none;
  cursor: pointer;
  transition: color 0.15s;
}

.baseline-cite:hover,
.baseline-cite:focus-visible {
  color: var(--accent);
  outline: none;
}

/* ═══════════ OVERVIEW: editorial composition ═══════════
   Three beats top-to-bottom:
     HEADLINE  — LE hero (left) + Biggest Lever card (right). Same row,
                 same eye-level. Fact on the left, lever on the right.
     SPARKLINE — Full-width Life Horizon below. NOW, threat stations,
                 terminus echo. Answers "what will kill me and when?"
   The LE hero is itself the entry point to the weeks calendar. */

.overview-shell {
  min-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-xl) var(--space-lg);
  box-sizing: border-box;
}

/* Layout container — transparent. The lever card carries chrome; the
   LE block and timeline sit bare on the page. .ovh just spaces them.
   The column width is tuned so the LE hero pins to the column's left
   edge, the lever card to the right, and the timeline below fills the
   exact same frame — both rows share left and right edges, no dead
   space above the path. */
.ovh {
  width: 100%;
  max-width: 880px;
  display: flex;
  flex-direction: column;
  gap: 28px;
  box-sizing: border-box;
}

@media (max-width: 720px) {
  .ovh { gap: 24px; }
}

/* Shared section-title spec for the overview headers (Life expectancy,
   Biggest lever, On the path) — three eyebrows, one rhythm. */
.ovh-section-title {
  margin-bottom: 14px;
}

/* ── Headline row ──
   LE block on the left edge of the column, lever card on the right.
   justify-content: space-between is what aligns the lever's right edge
   with the timeline's terminus below — the gap between LE and lever
   becomes whatever slack the column has after both content widths are
   subtracted, which gives the pair a generous, paired-but-breathing
   feel without leaving dead space at the column's right edge. The
   80px gap acts as a minimum for narrower columns. */
.ovh-headline {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  justify-content: space-between;
  gap: 80px;
}

@media (max-width: 820px) {
  .ovh-headline { gap: 24px; }
}

/* ── LE block — unframed ──
   Page-level editorial content: eyebrow, hero number, meta line. No
   card, no border, no background. The huge 100 doesn't need a frame;
   a frame would diminish it. Padding mirrors the lever card's internal
   padding (top/bottom) so the two eyebrows align horizontally.
   Content-sized so the lever sits right next to it, not pushed out
   to a distant right edge on wide screens. */
.ovh-le-block {
  flex: 0 0 auto;
  min-width: 0;
  padding: 32px 0;
  display: flex;
  flex-direction: column;
}

@media (max-width: 720px) {
  .ovh-le-block { padding: 24px 0 8px; }
}

.ovh-le-num-wrap {
  display: flex;
  align-items: baseline;
  gap: 16px;
  line-height: 1;
}

.ovh-le-num {
  font-family: var(--font-mono);
  font-size: 152px;
  font-weight: 300;
  line-height: 0.86;
  letter-spacing: -4px;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums slashed-zero;
  font-feature-settings: "zero" 1, "tnum" 1;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
}

/* Hero is fact-projection, not leverage — kept neutral so cyan can mean
   one thing on the page (the +Ny callouts under and beside the hero, all
   of which are years the user can still bank). Scale, not color, carries
   the headline; a cyan 152px digit was overpowering the three smaller
   cyan leverage readings it shares a viewport with. */
button.ovh-le-num {
  cursor: pointer;
  transition: color 0.18s, transform 0.22s cubic-bezier(0.4, 0, 0.2, 1);
  transform-origin: left center;
}
button.ovh-le-num:hover,
button.ovh-le-num:focus-visible {
  color: var(--accent);
  transform: scale(1.012);
  outline: none;
}
button.ovh-le-num:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 6px;
  border-radius: 4px;
}

.ovh-le-num-suffix {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--text-dim);
  position: relative;
  top: -6px;
}

/* Meta row: remaining-years/year on the left, modifiable-upside on the
   right, separated by a hairline divider. Labels are intentionally
   absent — context (the giant LE number above, the green color/arrow
   on the upside) does the work. margin-top: auto pins the meta to the
   bottom of the block so it sits at the same y as the lever's foot
   link to its right. */
.ovh-le-meta {
  margin-top: auto;
  padding-top: 28px;
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
  font-family: var(--font-mono);
}
.ovh-le-meta-val {
  font-size: 13px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
}
.ovh-le-meta-divider {
  width: 1px;
  height: 14px;
  background: var(--border);
}

.ovh-le-upside {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  background: transparent;
  border: 0;
  padding: 0;
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 600;
  color: var(--leverage);
  letter-spacing: 0.2px;
  text-align: left;
  white-space: nowrap;
}
button.ovh-le-upside { cursor: pointer; }
span.ovh-le-upside { cursor: help; }
.ovh-le-upside-val { font-variant-numeric: tabular-nums; }
.ovh-le-upside-arrow {
  font-size: 12px;
  color: var(--leverage);
  transition: transform 0.15s;
}
button.ovh-le-upside:hover .ovh-le-upside-arrow,
button.ovh-le-upside:focus-visible .ovh-le-upside-arrow {
  transform: translate(2px, -2px);
}
button.ovh-le-upside:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 4px;
  border-radius: 4px;
}

.ovh-le-ceiling {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text-secondary);
  font-family: var(--font-mono);
  font-size: 14px;
  letter-spacing: 0.2px;
  cursor: help;
}
.ovh-le-ceiling-mark {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--green);
}
.ovh-le-ceiling-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
}

/* ── Lever side — parallel architecture to the LE block ──
   eyebrow (top) → framed action card (middle) → foot link (bottom).
   Only the action card carries chrome — it's the sole framed object
   on the page and the page's primary CTA. The eyebrow and foot sit
   bare on the page like the LE eyebrow and meta to their left.
   Fixed-width so the headline row never balloons on wide screens. */
.ovh-lever-block {
  flex: 0 0 auto;
  width: 440px;
  max-width: 100%;
  min-width: 0;
  padding: 32px 0;
  display: flex;
  flex-direction: column;
}

@media (max-width: 820px) {
  .ovh-lever-block { width: 100%; }
}

@media (max-width: 720px) {
  .ovh-lever-block { padding: 0 0 8px; }
}

/* The action card itself — the only framed thing on the overview.
   Three baseline-aligned columns: priority pill, action text, gain.
   Putting the pill inline with the text keeps the card compact (no
   tag row stacked above body) and reads as a single horizontal unit
   — like a list-item row with a leading tag, not a stacked sub-card. */
.ovh-lever-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 14px;
  align-items: baseline;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: 20px 24px;
  font-family: var(--font-mono);
  text-align: left;
  cursor: pointer;
  transition: border-color 0.2s, box-shadow 0.2s, transform 0.2s;
}
.ovh-lever-card:hover {
  border-color: color-mix(in srgb, var(--accent) 28%, var(--border));
  box-shadow: var(--shadow-card-hover);
  transform: translateY(-1px);
}
.ovh-lever-card:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 2px;
}

.ovh-lever-tag {
  /* Nudge the pill up so its visual mass aligns with the first line
     of body text rather than dropping below it. Baseline-alignment
     in CSS doesn't account for the pill's bg padding. */
  position: relative;
  top: -1px;
}

.ovh-lever-text {
  font-size: 13.5px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--text-primary);
  letter-spacing: 0.1px;
  min-width: 0;
}

.ovh-lever-gain {
  font-size: 20px;
  font-weight: 600;
  color: var(--leverage);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.5px;
  white-space: nowrap;
  line-height: 1.05;
}

/* Foot — quiet tail link below the card. Shrinks to content width via
   align-self (no longer a full-width row), so it reads as a small
   auxiliary mark rather than a UI bar. Sits close to the card as its
   caption — not pinned to the LE block's baseline — so the lever
   half reads as a tight CTA unit rather than a stretched column. */
.ovh-lever-foot {
  align-self: flex-end;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 18px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-dim);
}
.ovh-lever-foot-count { font-variant-numeric: tabular-nums; }

.ovh-lever-foot-link {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--text-secondary);
  font-weight: 600;
  transition: color 0.15s;
}
.ovh-lever-foot-arrow {
  font-size: 12px;
  margin-left: 4px;
  transition: transform 0.15s, color 0.15s;
}
.ovh-lever-foot-link:hover,
.ovh-lever-foot-link:focus-visible {
  color: var(--accent);
  outline: none;
}
.ovh-lever-foot-link:hover .ovh-lever-foot-arrow,
.ovh-lever-foot-link:focus-visible .ovh-lever-foot-arrow {
  transform: translate(2px, -2px);
}
.ovh-lever-foot-link:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ── Timeline block — unframed ──
   Sits on the page background with no card and no divider. The "ON THE
   PATH" eyebrow plus the generous gap above carry the section change;
   a hairline rule felt like clutter, especially where the lever card
   crosses it. */
.ovh-timeline-block {
  padding-bottom: 8px;
}

.ovh-timeline-mount { width: 100%; }

/* LE responsive */
@media (max-width: 1180px) {
  .ovh-le-num { font-size: 132px; letter-spacing: -3.5px; }
}
@media (max-width: 980px) {
  .ovh { gap: var(--space-xl); }
  .ovh-le-num { font-size: 116px; letter-spacing: -3px; }
}
@media (max-width: 720px) {
  .ovh-le-num { font-size: 96px; letter-spacing: -2.5px; }
  .ovh-le-num-suffix { font-size: 11px; }
  .ovh-le-meta-divider { display: none; }
  .ovh-le-meta { gap: 16px; }
}
@media (max-width: 520px) {
  .ovh-le-num { font-size: 80px; letter-spacing: -2px; }
}

/* ═══════════ LIFE HORIZON — sparkline ═══════════
   Single horizontal axis beneath the Overview headline. NOW (left),
   threat stations between, the terminus echo on the right. The LE
   number itself lives in the headline above (.ovh-le-num); the
   terminus here echoes it quietly in accent — same digit, much
   smaller — to confirm the headline without competing with it.

   Marks share one geometry: name above the dot, dot on the line,
   meta below the dot. Variants (now / station / end) only re-tune
   typography and color. Severity is carried on the station dot via
   the inline --dot custom property. Decade ticks notch below the
   line and retire near labeled marks. */

.lhz {
  font-family: var(--font-mono);
  width: 100%;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  --lhz-axis-height: 96px;
  --lhz-line-color: #d4d8de;
  --lhz-mark-size: 11px;
  --lhz-anchor-size: 13px;
}

.lhz-eyebrow.section-title {
  /* Gap above handles the spacing to the axis; zero out the inherited
     section-title margin so it doesn't double up. */
  margin-bottom: 0;
}

.lhz-axis {
  position: relative;
  height: var(--lhz-axis-height);
  margin: 0 24px;
}

/* Main line: a neutral track from NOW to the projected terminus. Cyan is
   owned by the hero LE digit upstairs — a second cyan statement on the
   line was paying double rent for the same idea. The line is plumbing;
   the colored marks (red threats, green ceiling) are the signal. The
   axis still owns --lhz-projected (read by .lhz-line-extension), so the
   green dashed tail starts exactly where the solid track ends. */
.lhz-line {
  position: absolute;
  top: 50%;
  left: 0;
  width: var(--lhz-projected, 100%);
  height: 1.5px;
  background: var(--lhz-line-color);
  transform: translateY(-50%);
  border-radius: 1px;
}

/* Lived segment — birth → now. Solid like the projection track, but a
   sub-shade deeper so the eye reads concrete past distinctly from the
   modeled future. Same grammar as the rest of the line: solid carries
   fact, dashing is reserved for upside. Sits atop the neutral base
   line in DOM order so the heavier color wins in the lived window
   without changing the base line's geometry. */
.lhz-line-lived {
  position: absolute;
  top: 50%;
  left: 0;
  width: var(--lhz-now, 0%);
  height: 1.5px;
  background: #9aa1aa;
  transform: translateY(-50%);
  border-radius: 1px;
}

/* Dashed extension — the modifiable upside is hypothetical territory,
   not realized projection. Dashing signals "potential, not fact." Runs
   from the projected terminus out to the leverage ceiling pip; only
   present when there's modifiable upside to show. Cyan (leverage),
   matching the app-wide convention that --leverage marks years the
   user can still bank — same color as the +Ny callouts on the LE meta
   row, in Risks rail, and on Plan cards. */
.lhz-line-extension {
  position: absolute;
  top: 50%;
  left: var(--lhz-projected);
  right: 0;
  height: 1.5px;
  transform: translateY(-50%);
  background: repeating-linear-gradient(
    to right,
    color-mix(in srgb, var(--leverage) 75%, transparent) 0,
    color-mix(in srgb, var(--leverage) 75%, transparent) 4px,
    transparent 4px,
    transparent 8px
  );
}

/* Unified mark geometry. */
.lhz-mark {
  position: absolute;
  top: 50%;
  width: 0;
  height: 0;
  transform: translateY(-50%);
  background: none;
  border: 0;
  padding: 0;
  font-family: inherit;
  color: inherit;
  z-index: 2;
}

.lhz-mark-dot {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: var(--lhz-mark-size);
  height: var(--lhz-mark-size);
  border-radius: 50%;
  background: var(--dot, var(--text-dim));
  box-shadow: 0 0 0 3px var(--bg-panel);
  z-index: 1;
}

.lhz-mark-name {
  position: absolute;
  bottom: calc(var(--lhz-mark-size) / 2 + 14px);
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -0.1px;
  color: var(--text-primary);
  line-height: 1.05;
  transition: color 0.15s;
}

/* Station name composes an icon + the threat label. The icon takes
   the severity color (it carries the same signal the dot below does);
   the text sits dimmer than other marks so the row reads as a quiet
   row of glyph+caption labels, not three loud names. */
.lhz-mark-station .lhz-mark-name {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-secondary);
}
.lhz-mark-icon {
  display: inline-flex;
  align-items: center;
  line-height: 0;
}
.lhz-mark-icon .cod-icon {
  display: block;
}

/* Anchor meta — the age under each dot. Sits in the top row beneath
   the line; year ticks (.lhz-decade-label) live in a second row below
   this one so the two label types don't compete on a shared baseline. */
.lhz-mark-meta {
  position: absolute;
  top: calc(var(--lhz-mark-size) / 2 + 12px);
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  line-height: 1;
}

/* Station — clickable threat anchor. */
button.lhz-mark-station {
  cursor: pointer;
  transition: transform 0.15s;
}
button.lhz-mark-station:hover {
  transform: translateY(-50%) translateY(-1px);
}
button.lhz-mark-station:hover .lhz-mark-name {
  color: var(--accent);
}
button.lhz-mark-station:focus-visible {
  outline: none;
}
button.lhz-mark-station:focus-visible .lhz-mark-dot {
  box-shadow: 0 0 0 3px var(--bg-panel), 0 0 0 5px var(--accent);
}

/* BIRTH — origin anchor on the far left. The lightest of the three
   anchor weights: it's lived past, immutable, and the user can't act
   on it. Sits one notch dimmer than NOW/END so the row reads
   faint-origin → heavy-you → heavy-bookend instead of three identical
   grays. (Earlier the three anchors all wore the same gray, which let
   a single red threat dot outshout the terminus — see lhz-mark-end
   note for the matched fix on the right edge.) */
.lhz-mark-birth .lhz-mark-dot {
  width: var(--lhz-anchor-size);
  height: var(--lhz-anchor-size);
  background: var(--text-dim);
  box-shadow: 0 0 0 3px var(--bg-panel);
}
.lhz-mark-birth .lhz-mark-name {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--text-dim);
}
.lhz-mark-birth .lhz-mark-meta {
  color: var(--text-dim);
}

button.lhz-mark-birth {
  cursor: pointer;
  transition: transform 0.15s;
}
button.lhz-mark-birth:hover {
  transform: translateY(-50%) translateY(-1px);
}
button.lhz-mark-birth:focus-visible {
  outline: none;
}
button.lhz-mark-birth:focus-visible .lhz-mark-dot {
  box-shadow: 0 0 0 3px var(--bg-panel), 0 0 0 5px var(--accent);
}

/* NOW — the "you-are-here" anchor. Wears the heaviest neutral
   (--text-primary, the same ink as the hero LE digit) because this is
   the single most important fact on the row: the user's current
   position. Paired with END at the same ink weight so the two
   personal-fact bookends frame the threats and upside that live
   between them. Cyan stays reserved for leverage upside (line
   extension, ceiling pip, "+Ny reachable" link) so it doesn't get
   diluted by spending it on a present-tense fact. */
.lhz-mark-now .lhz-mark-dot {
  width: var(--lhz-anchor-size);
  height: var(--lhz-anchor-size);
  background: var(--text-primary);
  box-shadow: 0 0 0 3px var(--bg-panel);
}
.lhz-mark-now .lhz-mark-name {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.lhz-mark-now .lhz-mark-meta {
  color: var(--text-secondary);
}

/* When NOW is a button (clickable → life calendar), strip default chrome
   and give it a subtle hover/focus affordance matching the station marks. */
button.lhz-mark-now {
  cursor: pointer;
  transition: transform 0.15s;
}
button.lhz-mark-now:hover {
  transform: translateY(-50%) translateY(-1px);
}
button.lhz-mark-now:focus-visible {
  outline: none;
}
button.lhz-mark-now:focus-visible .lhz-mark-dot {
  box-shadow: 0 0 0 3px var(--bg-panel), 0 0 0 5px var(--accent);
}

/* END — terminus anchor on the right. Wears the same --text-primary
   ink as NOW: the two are peers — the bookends of the personal axis,
   the two facts (where you are, where you'll land) that frame
   everything the horizon is trying to say. Earlier END sat at
   --text-secondary, one shade above its neighbors — which left a
   single red threat dot as the loudest point on the row, ahead of the
   actual terminus of the projection. Matching NOW's ink puts the two
   anchors on equal footing and lets the threat color be loud against
   meaningful neutrals instead of timid ones. Cyan still doesn't land
   on the dot itself — the dashed cyan extension physically emanates
   from this dot, so the projection-upside relationship is carried by
   adjacency, not by tinting the death-marker. */
.lhz-mark-end .lhz-mark-dot {
  width: var(--lhz-anchor-size);
  height: var(--lhz-anchor-size);
  background: var(--text-primary);
  box-shadow: 0 0 0 3px var(--bg-panel);
}
.lhz-mark-end .lhz-mark-name {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.lhz-mark-end .lhz-mark-meta {
  color: var(--text-secondary);
}

/* When END is a button (clickable → life calendar), match NOW's affordance. */
button.lhz-mark-end {
  cursor: pointer;
  transition: transform 0.15s;
}
button.lhz-mark-end:hover {
  transform: translateY(-50%) translateY(-1px);
}
button.lhz-mark-end:focus-visible {
  outline: none;
}
button.lhz-mark-end:focus-visible .lhz-mark-dot {
  box-shadow: 0 0 0 3px var(--bg-panel), 0 0 0 5px var(--text-secondary);
}

/* CEILING — modifiable upside terminus. Wears --leverage (cyan) to match
   the app-wide convention: cyan = years the user can still bank. Same
   color as the "+Ny reachable" link on the LE meta row, the Risks-rail
   reach column, and Plan card leverage numerals — so a +3y reading
   carries one consistent color identity wherever it appears. The "+Ny"
   label sits above the dot in the standard mark position (matching
   threat stations), right-anchored so it grows leftward from the
   column's right edge instead of half-hanging into the margin. No meta
   — the projected terminus's age already supplies the right-edge
   anchor; a second number here would just create label noise. */
.lhz-mark-ceiling .lhz-mark-dot {
  width: 9px;
  height: 9px;
  background: var(--leverage);
  box-shadow: 0 0 0 3px var(--bg-panel);
}
.lhz-mark-ceiling .lhz-mark-name {
  left: auto;
  right: -4px;
  transform: none;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--leverage);
  font-variant-numeric: tabular-nums;
}
.lhz-mark-ceiling .lhz-mark-meta { display: none; }

/* When the ceiling is a button (clickable → Plan), strip the default
   button chrome and give it the same hover affordance as the LE
   block's "+Ny reachable" link — a tiny right/up nudge on the label. */
button.lhz-mark-ceiling {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font: inherit;
}
button.lhz-mark-ceiling .lhz-mark-name {
  transition: transform 0.15s;
}
button.lhz-mark-ceiling:hover .lhz-mark-name,
button.lhz-mark-ceiling:focus-visible .lhz-mark-name {
  transform: translate(2px, -2px);
}
button.lhz-mark-ceiling:focus-visible {
  outline: none;
}
button.lhz-mark-ceiling:focus-visible .lhz-mark-dot {
  box-shadow: 0 0 0 3px var(--bg-panel), 0 0 0 5px var(--leverage);
}

/* Year ticks — sparse 25y calendar pins beneath the anchor metas.
   Tick is a short vertical mark just below the line; label sits in
   its own row beneath the anchor-meta row so the two label tiers
   don't share a baseline (which was the source of the prior
   "1992 looks weird next to 34" problem).
   When a pin lands close to an anchor, .lhz-decade-bare drops the
   label so the pin still carries the 25y cadence silently without
   stacking a year directly beneath the anchor's age. */
.lhz-decade {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 1px;
  height: 1px;
  pointer-events: none;
}
.lhz-decade-tick {
  position: absolute;
  top: 1px;
  left: 50%;
  transform: translateX(-50%);
  width: 1px;
  height: 8px;
  background: var(--text-dim);
  opacity: 0.5;
}
.lhz-decade-label {
  position: absolute;
  /* Sits on the same baseline as the anchor metas — one unified label
     row beneath the line, year-vs-age distinguished by content (4
     digits vs 1-2 digits) and weight (year labels are dimmer/lighter).
     Earlier we ran them in a separate row, but with the retire-near-
     anchor logic dropping any tick within 3y of an anchor, the
     horizontal positions never collide — and a single row reads as a
     ruler instead of two competing tiers. */
  top: calc(var(--lhz-mark-size) / 2 + 12px);
  left: 50%;
  transform: translateX(-50%);
  font-size: 9.5px;
  font-weight: 400;
  color: var(--text-dim);
  opacity: 0.75;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.4px;
}

/* Responsive. */
@media (max-width: 980px) {
  .lhz { --lhz-axis-height: 84px; }
  .lhz-mark-name { font-size: 13px; }
}
@media (max-width: 640px) {
  .lhz { --lhz-axis-height: 72px; }
  .lhz-axis { margin: 0 16px; }
  .lhz-mark-name { font-size: 12px; }
  .lhz-mark-now .lhz-mark-name,
  .lhz-mark-birth .lhz-mark-name,
  .lhz-mark-end .lhz-mark-name { letter-spacing: 1.2px; font-size: 9.5px; }
}

/* ═══════════ RISKS VIEW ═══════════ */
.risks-rail-cite {
  margin-top: var(--space-sm);
  padding: 0 8px;
}

.risks-layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: var(--space-lg);
  align-items: start;
}

@media (max-width: 880px) {
  .risks-layout { grid-template-columns: 1fr; }
}

.risks-rail {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  position: sticky;
  top: var(--space-md);
}

.risks-rail-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Group head shares the row grid template — section label drops into the
   name column (col 2) so its left edge matches the row data below. The
   icon column (col 1) is empty for group heads, mirroring the indent
   of the row content rather than the rail edge. */
.risks-rail-group-head {
  display: grid;
  grid-template-columns: 18px 1fr auto;
  gap: 10px;
  align-items: end;
  padding: 0 8px 6px;
  border-bottom: 1px solid var(--border-subtle);
}

.risks-rail-group-head-name {
  grid-column: 2;
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
}

.risks-rail-group-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* "Reach" hint on the SIGNIFICANT group head only. Quiet small-caps so
   it reads as a column legend, not a competing header. The MINOR head
   omits it — the meaning is learned by the time the eye reaches that
   tier. */
.risks-rail-group-head-reach {
  text-align: right;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-dim);
  line-height: 1;
  cursor: help;
}

.risks-rail-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.risks-rail-row.group-minor {
  padding: 6px 10px;
  opacity: 0.78;
}

.risks-rail-row.group-minor .risks-rail-icon {
  opacity: 0.78;
}

.risks-rail-row.group-minor .watch-name {
  color: var(--text-secondary);
  font-weight: 400;
}

.risks-rail-row.group-minor:hover {
  opacity: 1;
}

.risks-rail-row.group-minor.selected {
  opacity: 1;
}

/* Two-column row: icon + name + neutral +Xy. Rows are already sorted
   by lifetime risk descending, so position is the rank cue — no need
   for a magnitude bar column on a navigation list. */
.risks-rail-row {
  display: grid;
  grid-template-columns: 18px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 9px 8px;
  font-family: var(--font-mono);
  text-align: left;
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  transition: background 0.15s;
}

/* Rail reach column — neutral ink. The drill-down's leverage tile owns
   the focal cyan +Xy moment; tinting every rail row dilutes the accent.
   The +Xy glyph (from formatYears) is self-evident at this point in
   the app, no column header needed. */
.risks-rail-leverage {
  font-size: 11px;
  font-weight: 400;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
  text-align: right;
  white-space: nowrap;
}

.risks-rail-leverage-empty {
  color: var(--text-dim);
  opacity: 0.5;
  font-weight: 400;
}

.risks-rail-row:hover {
  background: var(--bg-hover);
}

.risks-rail-row.selected {
  background: var(--accent-soft);
}

.risks-rail-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.risks-detail {
  min-width: 0;
}

.risk-detail-card {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: var(--space-xl);
  display: flex;
  flex-direction: column;
  /* Inter-section spacing — one notch tighter than --space-lg (24px).
     The drilldown stacks header → chart → Actions → Levers → Factors →
     footer; at 24px the page felt loose between sections, but stepping
     all the way down to --space-md (16px) made the section boundaries
     mush together. 20px keeps the rhythm without inflating the page. */
  gap: 20px;
}

.risk-detail-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "icon text leverage"
    "icon prose prose";
  column-gap: var(--space-xl);
  row-gap: 10px;
  align-items: start;
  padding-right: var(--space-md);
}

.risk-detail-head > .risk-detail-icon { grid-area: icon; align-self: start; }
.risk-detail-head > .risk-detail-head-text { grid-area: text; }
.risk-detail-head > .risk-detail-leverage { grid-area: leverage; align-self: start; padding-top: 2px; }
.risk-detail-head > .risk-detail-prose { grid-area: prose; }

/* Leverage tile — the third axis: where the user can move the curve.
   Sits flush right of the head, balancing the cause-of-death icon on the
   left. Gold = modifiable; muted = fate (little to nothing to do). */
.risk-detail-leverage {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  gap: 4px;
  min-width: 84px;
}

.risk-detail-leverage-num {
  font-size: 28px;
  font-weight: 300;
  letter-spacing: -0.5px;
  color: var(--leverage);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.risk-detail-leverage-label {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.risk-detail-leverage-fate .risk-detail-leverage-num {
  color: var(--text-dim);
  font-weight: 200;
}

.risk-detail-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
}

.risk-detail-name {
  font-size: 22px;
  font-weight: 500;
  color: var(--text-primary);
  line-height: 1.1;
  letter-spacing: 0.3px;
}

.risk-detail-meta {
  margin-top: 6px;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font-size: 11px;
  letter-spacing: 0.6px;
  color: var(--text-secondary);
  flex-wrap: wrap;
}

.risk-detail-prob {
  font-weight: 600;
  font-size: 12px;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.risk-detail-sep {
  color: var(--text-dim);
}

.risk-detail-revised {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  padding: 2px 6px;
  background: var(--amber-bg);
  color: var(--amber);
  border-radius: var(--radius-sm);
}

/* Lead paragraph for the threat — placed as a second row of the head
   grid, spanning the text + leverage columns. Starts at the right edge
   of the icon (which spans both rows on the left) and extends to the
   card's right edge — the eye gets a full reading measure without the
   prose drifting away from the threat name above it. */
.risk-detail-prose {
  font-size: 12px;
  line-height: 1.6;
  color: var(--text-primary);
  margin: 0;
}

/* Section heads — Actions / Levers / Anatomy share one rhythm.
   L1 typography: larger, primary-color title; one-line dek beneath it
   for orientation. No hairline rule under the head — the drilldown
   already has plenty of internal horizontals (action cards, lever
   header row, baseline anchor caps) and stacking another divider
   immediately under each dek made the page read as too-ruled. The
   section's identity is carried by the title's weight and the
   `margin-top: var(--space-xl)` between sections, not by an enclosing
   band. */
.risk-section-head {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: var(--space-md);
}

.risk-section-head-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
}

.risk-section-title {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--text-primary);
  line-height: 1;
}

.risk-section-takeaway {
  font-size: 12px;
  color: var(--text-secondary);
  letter-spacing: 0.2px;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.risk-section-dek {
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.2px;
  line-height: 1.45;
  margin: 0;
}

.risk-empty-line {
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.4px;
  font-style: italic;
}

/* Actions — one flat list, sorted by leverage descending. The bar anchor
   is the largest single live row, so the top action reads full-width and
   the eye gets a clean rank by visible bar length. Clinical urgency is
   carried by an inline row tag (Urgent / Rx), not by group dividers. */
.risk-actions-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Inline urgency / Rx tag inside the row title. Compact uppercase pill
   with a thin border in the priority's color; sits inline before the
   action text so a column of rows shows the urgency strip at a glance.
   Two semantic variants only — `soon` and `ongoing` carry no tag because
   the leverage bar already encodes "how much this matters." */
.risk-action-row-tag {
  display: inline-block;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  padding: 2px 6px;
  margin-right: 8px;
  border-radius: 3px;
  border: 1px solid currentColor;
  vertical-align: 2px;
  line-height: 1;
}

.risk-action-row-tag-urgent {
  color: var(--red);
  background: color-mix(in oklab, var(--red) 8%, transparent);
}

.risk-action-row-tag-pharmaceutical {
  color: var(--accent);
  background: color-mix(in oklab, var(--accent) 8%, transparent);
}

/* Row — no hairline divider; whitespace separates rows. The action's
   self-contained two-line block (title + why) plus the air around it carries
   the boundary without an explicit rule. */
.risk-action-row {
  display: grid;
  grid-template-columns: 1fr 88px;
  gap: var(--space-md);
  align-items: start;
  padding: 14px var(--space-md);
  background: transparent;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  text-align: left;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}

/* Row meter — fill only, no track. The fill segment at variable width is
   the entire visual artifact, so a 0.1y row is just a tiny accent tick and
   a 0.8y row is a long line. The empty-track ghosting that read as a glitch
   on small rows is gone. Tier-driven opacity tunes the number weight. */
.risk-action-row-meter {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  width: 88px;
  padding-top: 3px;
}

.risk-action-row-meter-num {
  font-size: 11px;
  font-weight: 600;
  color: var(--leverage);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
  text-align: right;
  line-height: 1;
}

.risk-action-row-meter-bar {
  position: relative;
  height: 1.5px;
  background: transparent;
  overflow: hidden;
}

.risk-action-row-meter-fill {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  background: var(--leverage);
  border-radius: 1px;
  transition: width 0.18s ease-out;
}

.risk-action-row-meter-high .risk-action-row-meter-num { opacity: 1; }
.risk-action-row-meter-mid  .risk-action-row-meter-num { opacity: 0.78; }
.risk-action-row-meter-low  .risk-action-row-meter-num { opacity: 0.55; font-weight: 500; }

.risk-action-row-meter-empty { visibility: hidden; }

.risk-action-row:hover {
  background: var(--bg-surface);
}

.risk-action-row-body {
  min-width: 0;
}

.risk-action-text {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-primary);
}

.risk-action-why {
  font-size: 11px;
  color: var(--text-secondary);
  margin-top: 2px;
  line-height: 1.5;
}

/* ── Mortality stream chart ───────────────────
   Stacked-area chart that replaced the per-threat curve. Shows the
   threat mix at every age, with the selected threat emphasized via
   full-opacity fill + status-color top edge. Other bands sit at low
   opacity as ambient context. The YOU rule + hover scrub turn it into
   an instrument: drag your eye from 30 to 90 and see the landscape
   morph. */
.mstream {
  position: relative;
  margin: var(--space-sm) 0 var(--space-md);
}

.mstream-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  margin-bottom: 6px;
  padding: 0 2px;
}

.mstream-title {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.mstream-explore {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-secondary);
  background: none;
  border: none;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  transition: color 0.12s, background 0.12s;
}

.mstream-explore:hover {
  color: var(--accent);
  background: var(--accent-soft);
}

.mstream-explore-chev {
  font-size: 12px;
  line-height: 1;
}

/* Overlay variant — used when the chart head row is suppressed (drilldown
   thumb mode). Floats in the chart-wrap top-right corner so the action
   stays co-located with the chart it acts on, without claiming a whole
   row of vertical space. Styled as a plain text link (matches the cite-
   link pattern used elsewhere in the drilldown) — no box, no border,
   no shadow. The chart's own light-gray surface is enough framing. */
.mstream-explore-overlay {
  position: absolute;
  /* top:6px + button padding-top:2px puts the text-content top edge at
     8px from chart-wrap top, aligned with the YOU caption above the
     plot (which sits at 8px via overlay inset 24 + top:-16). */
  top: 6px;
  right: 8px;
  z-index: 2;
  padding: 2px 0;
  background: none;
  border: none;
  box-shadow: none;
  color: var(--text-dim);
  font-weight: 500;
  letter-spacing: 0.8px;
}
.mstream-explore-overlay:hover,
.mstream-explore-overlay:focus-visible {
  color: var(--accent);
  background: none;
}

/* Modal-mode stream — landscape tab in the population slide-over. No
   selected threat means no band needs to defer to another, so every
   band runs at full opacity (the dim 0.22 was a foreground/background
   tool, not a base style). Layout stays flush with the slide-over body
   so the framing matches the Base rates tab. */
.mstream-modal {
  margin: var(--space-lg) 0 0;
}

/* Thumb mode — compact inline landscape rendered up near the threat
   header. The mortality landscape is *temporal* context for the
   headline percentage (when this threat fires across the lifespan),
   so it belongs next to the number it explains, not as a sibling of
   the Factors decomposition below. Trading chart real estate for an
   inline strip keeps Actions near the top; the EXPLORE button still
   routes the user to the full landscape in the slide-over. Title is
   suppressed (passed as ''); the threat name + percentage above
   already carry the framing. */
.mstream.mstream-thumb {
  margin: 0;
}
.mstream.mstream-thumb .mstream-chart {
  height: 96px;
  cursor: pointer;
}
/* Specificity bump: .mstream:not(.mstream-modal) .mstream-chart-wrap
   otherwise wins and reintroduces the indented frame + 8px top/bottom
   padding meant for the standalone inline mode. In thumb mode we want
   the chart flush to its parent so the breakdown row below aligns
   horizontally with the chart's data extent. */
/* `:not(.mstream-modal)` chained for specificity — the
   `.mstream:not(.mstream-modal) .mstream-chart-wrap` rule further down
   the file has equal specificity (0,3,0) and would otherwise win on
   source order, overriding our thumb padding back to the default 8px.
   The :not() bump pushes us to (0,4,0) so thumb wins cleanly. */
.mstream.mstream-thumb:not(.mstream-modal) .mstream-chart-wrap {
  margin: 0;
  /* Thumb mode is short (96px chart). Top padding carves a caption
     strip above the SVG so YOU + EXPLORE have somewhere to live above
     the plot data; overlay inset matches so y-axis labels stay aligned
     with the SVG plot. Bottom padding gives the x-axis tick labels
     room below the SVG (they're anchored at `bottom: 4px` of the
     overlay, which extends to chart-wrap bottom). */
  padding: 20px 0 8px;
}
.mstream.mstream-thumb:not(.mstream-modal) .mstream-overlay {
  inset: 20px 0 0 0;
}
.mstream.mstream-thumb .mstream-breakdown {
  /* Right padding matches the chart's viewBox padR (12/1000 = 1.2%) so
     the rightmost breakdown item aligns with the chart's rightmost tick
     (age 85) instead of overshooting into the chart-wrap's blank gutter.
     Left stays compact so the "AT N you" anchor sits flush with the
     y-axis label column. */
  padding-left: 4px;
  padding-right: 1.2%;
  font-size: 10px;
  gap: 10px;
}
.mstream-thumb .mstream-bd-age,
.mstream-thumb .mstream-bd-name,
.mstream-thumb .mstream-bd-pct {
  font-size: 10px;
}
.mstream-thumb .mstream-xlabel {
  font-size: 9px;
}
.mstream-thumb .mstream-ylabel {
  font-size: 9px;
}

.mstream-modal .mstream-band {
  opacity: 1;
}

.mstream-modal .mstream-chart {
  height: 360px;
}

/* Modal stretches the viewBox vertically with preserveAspectRatio="none",
   so the SVG's bottom-pad area (~36px below the plot baseline in a 360px
   chart) leaves the default `bottom: 4px` x-labels stranded far below
   the streams. Lift them closer to the baseline. */
.mstream-modal .mstream-xlabel {
  bottom: 18px;
}

.mstream-modal .mstream-head {
  margin-bottom: 12px;
}

/* Breakdown reads as an instrument strip — solid surface, comfortable
   padding, calm sizing. Gap on the bd-item bumps so the cause name and
   the percentage don't read as one squished token. */
.mstream-modal .mstream-breakdown {
  margin-top: 14px;
  padding: 11px 16px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 12px;
  gap: 22px;
}

.mstream-modal .mstream-bd-items {
  gap: 22px;
}

.mstream-modal .mstream-bd-item {
  gap: 8px;
}

.mstream-modal .mstream-bd-age {
  font-size: 12px;
}

.mstream-modal .mstream-bd-pct {
  font-size: 12px;
}

.src-landscape .src-data-meta {
  max-width: 70ch;
}

.mstream-chart-wrap {
  position: relative;
  width: 100%;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.mstream-chart {
  display: block;
  width: 100%;
  height: 200px;
  cursor: crosshair;
}

/* Inline drilldown only: add vertical breathing room around the chart
   frame and between x-axis labels and plot baseline. Chart and breakdown
   are indented under the head so the trio reads as header → child
   content rather than three flush-left peers. */
.mstream:not(.mstream-modal) .mstream-chart-wrap {
  margin-top: 8px;
  margin-bottom: 10px;
  margin-left: var(--space-md);
  padding-top: 8px;
  padding-bottom: 8px;
}
.mstream:not(.mstream-modal) .mstream-breakdown {
  padding-left: calc(var(--space-md) + 4px);
}

.mstream:not(.mstream-modal) .mstream-overlay {
  inset: 8px 0 0 0;
}

.mstream:not(.mstream-modal) .mstream-xlabel {
  bottom: 4px;
}

/* Ambient bands — quiet, low-opacity. The selected band overrides this
   to full opacity, so the eye lands on it first. */
.mstream-band {
  opacity: 0.22;
  transition: opacity 0.15s ease;
}

.mstream-band-selected {
  opacity: 1;
}

.mstream-grid {
  stroke: var(--border-subtle);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.mstream-xtick {
  stroke: var(--border);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

/* YOU rule — solid vertical at user's age, in accent (cyan = control axis) */
.mstream-you-rule {
  stroke: var(--accent);
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
  opacity: 0.85;
}

/* Axis labels live in an HTML overlay rather than SVG <text> — the chart
   uses preserveAspectRatio="none" so SVG text would non-uniformly stretch
   along with the geometry. Positions are set inline as percentages of the
   viewBox, which map 1:1 to wrap percentages under preserveAspectRatio="none". */
.mstream-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  font-family: var(--font-mono);
}

.mstream-ylabel {
  position: absolute;
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  transform: translateY(-50%);
  white-space: nowrap;
}

.mstream-xlabel {
  position: absolute;
  bottom: 4px;
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  transform: translateX(-50%);
  white-space: nowrap;
}

.mstream-you-label {
  position: absolute;
  top: 2px;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 1px;
  color: var(--accent);
  text-transform: uppercase;
  transform: translateX(-50%);
  white-space: nowrap;
}

.mstream:not(.mstream-modal) .mstream-you-label {
  top: -4px;
}
/* Thumb mode carves out a 20px caption strip above the SVG (see
   chart-wrap padding above); -12px from the 20px-inset overlay places
   YOU at 8px from the chart-wrap top — inside the strip with ~7px of
   clearance to the plot's top edge below. */
.mstream.mstream-thumb .mstream-you-label {
  top: -12px;
}

/* Peak annotation — replaces the old "{name} · peak X% at ~Y" header
   chrome with a small marker pinned to the band's top edge at its peak.
   The dot (::after) sits exactly on the peak point; the label floats
   just above it. Color comes from the selected status (red/amber/...)
   via the inline --peak-color variable so the dot reads as part of the
   band, not a separate chart element. */
.mstream-peak-anno {
  position: absolute;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.4px;
  color: var(--text-secondary);
  white-space: nowrap;
  transform: translate(-50%, calc(-100% - 8px));
  pointer-events: none;
  padding: 2px 6px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 3px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
}

.mstream-peak-anno::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -8px;
  transform: translate(-50%, 50%);
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--peak-color, currentColor);
}

/* Flip below the dot when the band's top sits near the chart ceiling
   (top-stacked causes peaking young). */
.mstream-peak-anno-below {
  transform: translate(-50%, 8px);
}

.mstream-peak-anno-below::after {
  bottom: auto;
  top: -8px;
  transform: translate(-50%, -50%);
}

/* Hover scrub rule — quiet dashed gray; YOU rule stays the star */
.mstream-hover-rule {
  stroke: var(--text-primary);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  vector-effect: non-scaling-stroke;
  opacity: 0.55;
  pointer-events: none;
}

/* Single-line breakdown beneath the chart. Updates on hover-scrub.
   Compact by design — adds no vertical bulk to the drilldown.
   Layout is locked to one row: the items area is a 5-column grid so
   the chip widths never reflow as the cause mix shifts under the
   cursor. Names truncate with ellipsis when a slot is too narrow. */
.mstream-breakdown {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 16px;
  margin-top: 6px;
  padding: 4px 4px 0;
  font-size: 11px;
  color: var(--text-secondary);
  letter-spacing: 0.3px;
  min-height: 22px;
}

/* Anchor block — "AT 34 (you)" — sits left of the divider; reads as
   the readout's frame, not as an item itself. */
.mstream-bd-anchor {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  white-space: nowrap;
}

.mstream-bd-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.mstream-bd-age {
  font-weight: 600;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.mstream-bd-you {
  font-weight: 500;
  color: var(--text-dim);
  font-size: 0.85em;
  margin-left: 2px;
}

/* Vertical rule between anchor and items. Replaces the old "▸" glyph
   with a quieter, more deliberate divider. */
.mstream-bd-divider {
  width: 1px;
  align-self: stretch;
  background: var(--border);
}

/* Items row — content-sized chips with the slack distributed
   between them. Equal-width columns waste space on short names and
   truncate long ones; space-between lets each chip take only what it
   needs and pushes extra space into the inter-chip gaps. min-width:0
   on the chip + name keeps ellipsis available as a graceful fallback
   when total content genuinely exceeds the row width. */
.mstream-bd-items {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}

.mstream-bd-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  flex: 0 1 auto;
}

/* Empty padding chip — keeps the slot count at 5 so the chip
   distribution doesn't jump when the cause mix shrinks under the
   threshold. Zero width, no visual presence. */
.mstream-bd-item-empty {
  visibility: hidden;
  flex: 0 0 0;
}

/* Color swatch — same hue as the cause's chart band, so a glance at
   the readout maps each percentage back to its band in the stream. */
.mstream-bd-swatch {
  width: 8px;
  height: 8px;
  border-radius: 2px;
  display: inline-block;
  flex-shrink: 0;
}

.mstream-bd-name {
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.mstream-bd-pct {
  color: var(--text-primary);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Card-closing band — owns everything below Factors: the Banked summary
   (✓ Secured here…) on the left, Explore-markers cite link top-right,
   sharing one row so the eye doesn't have to track a diagonal. The
   banked section always claims full band width — when it expands, its
   details body uses the whole row. The cite is absolutely positioned to
   the top-right so it doesn't reserve a column that would squeeze the
   expanded body. The summary text is short enough that it never reaches
   the cite. The divider above is slightly darker than the in-section
   hairlines (var(--border)) but lighter than text — a card-foot signal,
   not another mid-content rule. */
.risk-detail-footer {
  position: relative;
  min-height: 22px;
  margin-top: var(--space-md);
  padding-top: var(--space-md);
  border-top: 1px solid #d1d5db;
}
.risk-detail-footer-cite {
  position: absolute;
  top: var(--space-md);
  right: 0;
  padding-top: 2px;
}

/* ═══════════ RISKS · SHOW THE MATH ═══════════
   The credibility layer below Actions and Levers: the multiplicative
   baseline decomposition viz (FACTORS section) and the mortality
   landscape stream — siblings, not nested. Each gets its own block
   margin so they read as peer bands; an earlier umbrella section
   ("Anatomy") forced one title to cover both viz and felt strained. */
.risk-baseline > .risk-baseline-viz {
  margin-bottom: 0;
}

.risks-empty {
  padding: var(--space-2xl);
  text-align: center;
  color: var(--text-dim);
  font-size: 12px;
}

/* ═══════════ PLAN THREAT TAGS ═══════════ */
.plan-threat-tags {
  display: flex;
  gap: 6px;
  margin: 6px 0 8px;
  flex-wrap: wrap;
}

.plan-threat-tag {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  padding: 3px 8px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}

.plan-threat-tag:hover {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent);
}

/* ═══════════ TRENDS TOPBAR BUTTON ═══════════ */
.topbar-trends-btn {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  padding: 6px 14px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.topbar-trends-btn:hover,
.topbar-trends-btn.active {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent);
}

/* ───────────────────────────────────────────────
   Target pills — the goal layer surfaced under each action.
   `Label  current → goal` reads as "this is the variable this action
   moves." Cool/leverage axis (cyan goal) keeps targets on the agency
   side of the warm/cool split. Pills wrap to fill available width.

   Used by Risks (under each action row in the drill-down) and Plan
   (under each action card). Same primitive, same density.
   ─────────────────────────────────────────────── */

.target-pills {
  margin-top: var(--space-sm);
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: baseline;
}

.target-pill {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 3px 8px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.1px;
  line-height: 1.5;
  white-space: nowrap;
  cursor: default;
}

.target-pill-label {
  font-weight: 600;
  color: var(--text-secondary);
  letter-spacing: 0.3px;
}

.target-pill-current {
  color: var(--text-primary);
  font-weight: 500;
}
.target-pill-current-missing {
  color: var(--text-dim);
  font-weight: 400;
}

.target-pill-arrow {
  color: var(--text-dim);
}

.target-pill-goal {
  color: var(--leverage);
  font-weight: 500;
}

/* At-goal pill — current value goes green so an in-range marker reads
   as "good" without needing a separate ✓ glyph. Background gets a
   faint green wash so the pill itself feels positive. The goal value
   drops to a structural tone here: cyan signals "leverage to chase,"
   but there's no leverage left when the lever is already met — the
   threshold reads as background context, not as a call to action. */
.target-pill-met {
  background: var(--green-bg);
  border-color: rgba(5, 150, 105, 0.15);
}
.target-pill-met .target-pill-current {
  color: var(--green);
}
.target-pill-met .target-pill-goal {
  color: var(--text-dim);
  font-weight: 400;
}

.target-pill-empty .target-pill-goal {
  opacity: 0.75;
}

/* Binary pill: no current value, just the goal phrase. Allow wrap so
   long phrases ("annual flu + age-appropriate pneumococcal/RSV/COVID")
   don't blow out the row. */
.target-pill-binary {
  white-space: normal;
}
.target-pill-binary .target-pill-goal {
  font-weight: 400;
}

.target-pill-overflow {
  align-self: center;
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.3px;
  padding: 0 4px;
}

/* Plan action items have more breathing room; pills sit a touch lower. */
.action-item .target-pills,
.plan-card .target-pills,
.plan-row .target-pills {
  margin-top: var(--space-sm);
}

/* ───────────────────────────────────────────────
   Target chips — Risks-only "this action moves these levers" strip.

   The Levers section directly above already prints current → goal · gauge
   for every variable; under an action we deliberately drop all that and
   show only the graph edge plus one bit of state. A 6px dot encodes the
   user's current relationship to the lever (off / met / unmeasured), the
   label names it, and a title= tooltip carries the full reading on demand.

   No lead-in label — the strip's location under each action already says
   "this action moves these"; a repeated `MOVES` prefix down a column of
   actions becomes chrome.
   ─────────────────────────────────────────────── */

/* Indent past the action title/why so chips read as a subordinate
   third line, not a sibling block. The dotted-rail offset (var(--space-md))
   sits the first chip just inside the body's text edge, giving the strip
   a clear hierarchy without needing a left rule. */
.risk-action-row .target-chips {
  margin-top: 6px;
  padding-left: var(--space-md);
}

.target-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
}

.target-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-size: 11px;
  letter-spacing: 0.2px;
  font-variant-numeric: tabular-nums;
  cursor: default;
  white-space: nowrap;
}

.target-chip-label {
  font-weight: 500;
  color: var(--text-secondary);
}

/* The dot is the entire visual signal. 6px circle, nudged up so it sits
   visually aligned with the cap-height of the label rather than dropping
   to the descender. State drives fill vs. ring. */
.target-chip-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
  transform: translateY(-1px);
}

/* Off-target — filled amber. The actionable state. Label promotes one
   notch in weight/contrast so the eye lands on rows of off-target chips
   first when scanning a busy actions section. */
.target-chip-off .target-chip-dot {
  background: var(--amber);
}
.target-chip-off .target-chip-label {
  color: var(--text-primary);
  font-weight: 600;
}

/* At-goal — green ring, hollow. Communicates "this action keeps locking
   in something you're already at." Label drops to dim so secured chips
   recede; the ring is enough to note their presence. */
.target-chip-met .target-chip-dot {
  background: transparent;
  border: 1.5px solid var(--green);
  box-sizing: border-box;
}
.target-chip-met .target-chip-label {
  color: var(--text-dim);
}

/* Unmeasured / binary-not-assessed — dim hollow ring. Lowest weight; the
   chip is present so the user knows the action targets this variable, but
   nothing about state is being asserted. */
.target-chip-unmeasured .target-chip-dot {
  background: transparent;
  border: 1px solid var(--text-dim);
  box-sizing: border-box;
  opacity: 0.55;
}
.target-chip-unmeasured .target-chip-label {
  color: var(--text-dim);
}

.target-chip-overflow {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.3px;
  align-self: baseline;
}

/* ───────────────────────────────────────────────
   Risks · Levers — the goal layer surfaced as one ranked instrument list.

   Three layers of weight, top to bottom:
     1. Context strip   — demographics + genetics in one quiet line
     2. Lever rows      — all active levers sorted by impact desc
     3. Secured footer  — at-goal levers folded into one quiet line

   Every row uses the same grid:
     name · current · → · goal · gauge (or empty) · +Xy
   So a continuous biomarker, a vital, and a screening all read in the
   same rhythm. State varies by row class:
     .lever-row-off        — measured value + range bar + +Xy headline
     .lever-row-unmeasured — em dash for current, prose goal, +Xy if hit
   ─────────────────────────────────────────────── */

/* Drilldown sections share one rhythm: Actions → Levers → Factors.
   Equal top margin keeps the page reading as three peer bands.
   (`.risk-baseline` is the class on the Factors section — kept as
   `baseline` because the engine vocabulary anchors on it; the user-
   facing title is "Factors". The mortality landscape lives upstream
   in `mstream-thumb` mode, integrated into the threat-header context
   rather than rendered as a peer section.) */
.risk-actions,
.risk-levers,
.risk-baseline {
  margin-top: var(--space-xl);
}

/* Baseline attribution — derivation chain as instrument.
   BASELINE (sex-specific cause-of-death share) at top, YOU (post-
   everything `probMid`) at bottom. A vertical hairline spine threads
   through the chart axis between them; horizontal hairline borders
   under BASELINE / above YOU cap the structure as the summation
   boundary, forming clean cross intersections at the spine.

   All rows — anchors + modifier rows — share one 3-column grid so
   labels right-align to the chart edge and numeric values right-align
   to the same outer column edge. Direction is positional (right =
   additive, left = protective); color is semantic (warm = additive,
   cool = protective). Identity is carried by the modifier names alone
   — no group labels. Hover/focus surfaces a tooltip explainer. */

/* No enclosing panel — the widget reads as a continuous instrument on
   the parent's bg, structurally framed by the BASELINE / YOU hairlines.
   Avoids the nested-gray-box look and keeps it visually of-a-piece
   with the rest of the drilldown (Levers, Actions). */
.risk-baseline-viz {
  display: flex;
  flex-direction: column;
  padding: 0;
  background: transparent;
  border: none;
  min-width: 0;
  margin-bottom: var(--space-md);
}

/* Shared row grid (anchors + modifier rows). Right-aligned name in
   col 1, chart in col 2 (with axis at center), right-aligned numeric
   in col 3. Identical columns across all rows produce a strict tabular
   spine on both label and value sides. */
.risk-baseline-anchor,
.risk-baseline-row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(140px, 1.4fr) 3fr minmax(70px, auto);
  align-items: center;
  gap: 14px;
}

.risk-baseline-rows {
  display: flex;
  flex-direction: column;
  gap: 4px;            /* tight gap so per-row axes form a continuous spine */
  padding: 12px 0;
}

.risk-baseline-row {
  border-radius: 4px;
  cursor: default;
}
.risk-baseline-row[data-explainer] { cursor: help; outline: none; }
.risk-baseline-row[data-explainer]:hover,
.risk-baseline-row[data-explainer]:focus-visible {
  background: var(--bg-hover);
}

.risk-baseline-row-name {
  font-size: 12px;
  letter-spacing: 0.1px;
  color: var(--text-secondary);
  text-align: right;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 3px 0;
}

.risk-baseline-row-track {
  position: relative;
  height: 14px;
}

/* Per-row axis at chart-column center, extended ±2px to bridge the
   4px gap between rows — adjacent axes meet exactly mid-gap, making
   the spine read as one continuous hairline instead of stacked stubs. */
.risk-baseline-row-axis {
  position: absolute;
  left: 50%;
  top: -2px;
  bottom: -2px;
  width: 1px;
  background: var(--border);
  transform: translateX(-0.5px);
  z-index: 0;
}

/* Bar: solid semantic fill, flat at the axis end and softly rounded at
   the leading edge so it reads as a force extending outward from the
   spine. Warm (--amber) for additive risk; cool (--accent) for
   protective. */
.risk-baseline-row-bar {
  position: absolute;
  top: 50%;
  height: 5px;
  transform: translateY(-50%);
  width: var(--bar-width);
  z-index: 1;
}
.risk-baseline-row.is-up .risk-baseline-row-bar {
  left: 50%;
  background: var(--amber);
  opacity: 0.85;
  border-radius: 0 1.5px 1.5px 0;
}
.risk-baseline-row.is-down .risk-baseline-row-bar {
  right: 50%;
  background: var(--accent);
  opacity: 0.85;
  border-radius: 1.5px 0 0 1.5px;
}

.risk-baseline-row-value {
  font-size: 12px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.1px;
  text-align: right;
  padding: 3px 0;
}
.risk-baseline-row.is-up .risk-baseline-row-value { color: var(--amber); }
.risk-baseline-row.is-down .risk-baseline-row-value { color: var(--accent); }

/* Tooltip — appears above the row on hover/focus. Dark surface inverts
   the panel so it reads as a separate layer; pointer-events disabled
   so the tooltip never traps the cursor. */
.risk-baseline-row[data-explainer]::after {
  content: attr(data-explainer);
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(2px);
  width: max-content;
  max-width: 360px;
  padding: 9px 12px;
  font-size: 11px;
  line-height: 1.55;
  letter-spacing: 0.1px;
  white-space: normal;
  text-align: left;
  color: var(--bg-panel);
  background: var(--text-primary);
  border-radius: var(--radius-sm);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
  pointer-events: none;
  opacity: 0;
  transition: opacity 140ms ease-out, transform 140ms ease-out;
  z-index: 20;
}
.risk-baseline-row[data-explainer]::before {
  content: '';
  position: absolute;
  bottom: calc(100% + 2px);
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: var(--text-primary);
  pointer-events: none;
  opacity: 0;
  transition: opacity 140ms ease-out;
  z-index: 21;
}
.risk-baseline-row[data-explainer]:hover::after,
.risk-baseline-row[data-explainer]:focus-visible::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
.risk-baseline-row[data-explainer]:hover::before,
.risk-baseline-row[data-explainer]:focus-visible::before {
  opacity: 1;
}

/* Anchors — endpoints framing the bar viz. The horizontal hairlines
   under BASELINE and above YOU are the summation boundary; the cap
   dots in col 2 mark where the spine terminates. */
.risk-baseline-anchor-top {
  padding: 4px 0 12px;
  border-bottom: 1px solid var(--border-subtle);
}
.risk-baseline-anchor-bottom {
  padding: 12px 0 4px;
  border-top: 1px solid var(--border-subtle);
}

.risk-baseline-anchor-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-dim);
  text-align: right;
}

/* Cap holds the spine stub + terminal dot. Stub fills the half of the
   row toward the modifier-rows region (so the spine appears continuous
   across the border); the dot sits at the row content center. */
.risk-baseline-anchor-cap {
  position: relative;
  align-self: stretch;
}
.risk-baseline-anchor-cap::before {
  content: '';
  position: absolute;
  left: 50%;
  width: 1px;
  background: var(--border);
  transform: translateX(-0.5px);
  z-index: 1;
}
.risk-baseline-anchor-top .risk-baseline-anchor-cap::before {
  top: 50%;
  bottom: -12px;       /* extends through padding + border into the spine */
}
.risk-baseline-anchor-bottom .risk-baseline-anchor-cap::before {
  top: -12px;
  bottom: 50%;
}
.risk-baseline-anchor-cap::after {
  content: '';
  position: absolute;
  z-index: 2;
}

/* BASELINE marker: a short horizontal tick crossing the spine. Reads
   as a measurement datum — a horizon line, not a "step on the path."
   The spine stub passes through it forming a clean cross. */
.risk-baseline-anchor-top .risk-baseline-anchor-cap::after {
  left: 50%;
  top: 50%;
  width: 14px;
  height: 2px;
  background: var(--text-dim);
  border-radius: 1px;
  transform: translate(-50%, -50%);
}

/* YOU marker: filled dot, laterally offset along the row in proportion
   to log(YOU/BASELINE). Spine stub ends at center; the colored trail
   stretches from spine to dot, making the cumulative shift legible as
   a horizontal journey. Net direction colors the dot + trail. */
.risk-baseline-anchor-bottom .risk-baseline-anchor-cap::after {
  top: 50%;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--text-primary);
  box-shadow: 0 0 0 3px var(--bg-panel);
  transform: translate(-50%, -50%);
}
.risk-baseline-anchor-bottom.is-net-up .risk-baseline-anchor-cap::after {
  left: calc(50% + var(--you-offset, 0%));
  background: var(--amber);
}
.risk-baseline-anchor-bottom.is-net-down .risk-baseline-anchor-cap::after {
  left: calc(50% - var(--you-offset, 0%));
  background: var(--accent);
}
.risk-baseline-anchor-bottom.is-net-flat .risk-baseline-anchor-cap::after {
  left: 50%;
}

/* Trail: a colored hairline drawing the spine center to the YOU dot,
   so the eye reads the lateral offset as the "drift" away from the
   datum. Color matches the dot. */
.risk-baseline-you-trail {
  position: absolute;
  top: 50%;
  height: 2px;
  width: var(--you-offset, 0%);
  border-radius: 1px;
  transform: translateY(-50%);
  z-index: 1;
}
.risk-baseline-anchor-bottom.is-net-up .risk-baseline-you-trail {
  left: 50%;
  background: var(--amber);
  opacity: 0.5;
}
.risk-baseline-anchor-bottom.is-net-down .risk-baseline-you-trail {
  right: 50%;
  background: var(--accent);
  opacity: 0.5;
}
.risk-baseline-anchor-bottom.is-net-flat .risk-baseline-you-trail { display: none; }

.risk-baseline-anchor-top .risk-baseline-anchor-value {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
  text-align: right;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.1px;
}
.risk-baseline-anchor-bottom .risk-baseline-anchor-value {
  font-size: 17px;
  font-weight: 600;
  color: var(--text-primary);
  text-align: right;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.2px;
}
.risk-baseline-anchor-bottom.is-net-up .risk-baseline-anchor-value {
  color: var(--amber);
}
.risk-baseline-anchor-bottom.is-net-down .risk-baseline-anchor-value {
  color: var(--accent);
}

/* Rollup row — one-way expand button. Click reveals the hidden
   children and dissolves this row entirely; no collapse-back since
   threat re-render gives a fresh collapsed state for free. Italic
   dim treatment marks it as a folded summary; chevron is a pure
   affordance hint (no rotation — it's not a toggle). */
.risk-baseline-row.is-rollup {
  cursor: pointer;
  outline: none;
}
.risk-baseline-row.is-rollup:hover,
.risk-baseline-row.is-rollup:focus-visible {
  background: var(--bg-hover);
}
.risk-baseline-row.is-rollup .risk-baseline-row-name {
  font-style: italic;
  color: var(--text-dim);
}
.risk-baseline-row.is-rollup .risk-baseline-row-value {
  opacity: 0.75;
}
.risk-baseline-row.is-rollup .risk-baseline-row-bar {
  opacity: 0.55;
}
.risk-baseline-rollup-chevron {
  display: inline-block;
  margin-right: 6px;
  font-size: 9px;
  color: var(--text-dim);
  font-style: normal;
}
.risk-baseline-rows.is-expanded .risk-baseline-row.is-rollup {
  display: none;
}

/* Hidden child rows — collapsed by default, revealed once the parent
   container has .is-expanded. After the rollup summary dissolves the
   children are full peers of the other modifier rows: same row
   geometry, same typography. Their bars carry the same semantic
   color too — it's the only signal of how minor each one is. */
.risk-baseline-row.is-rollup-child {
  display: none;
}
.risk-baseline-rows.is-expanded .risk-baseline-row.is-rollup-child {
  display: grid;
}

/* Lever rows — single grid shared by all states. The columns:
     name (110-140px) · current (auto, tabular) · arrow · goal (auto)
     · gauge (1fr) · +Xy (right-aligned)
   The gauge column is the elastic one, so a row with a long name still
   leaves the gauge filling the available track between goal and years. */
.lever-rows {
  display: flex;
  flex-direction: column;
}

/* .lever-item wraps a .lever-row trigger and a sibling .lever-row-expansion.
   The wrapper is the per-lever unit; the trigger carries the click target
   and grid layout, the expansion sits below in the same flex column. */
.lever-item {
  display: flex;
  flex-direction: column;
}

.lever-row {
  display: grid;
  grid-template-columns: minmax(160px, 2fr) minmax(96px, 1fr) minmax(96px, 1.2fr) 64px;
  align-items: center;
  column-gap: var(--space-md);
  padding: 9px 4px;
  border-bottom: 1px solid var(--border-subtle);
  font-variant-numeric: tabular-nums;
}

/* Last lever's bottom border — now anchored on the wrapping .lever-item
   so the expansion sibling doesn't push the rule off the visible row. */
.lever-rows > .lever-row:last-child,
.lever-rows > .lever-item:last-child > .lever-row,
.lever-more-list > .lever-item:last-child > .lever-row { border-bottom: 0; }

/* Trigger affordance — every clickable lever row reads as interactive.
   Hover background mirrors the action-row hover pattern; cursor is the
   primary signal that the row is more than a static readout. */
.lever-row[data-target-id] {
  cursor: pointer;
  transition: background 0.12s;
}
.lever-row[data-target-id]:hover {
  background: var(--bg-surface);
}
.lever-row[data-target-id]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: 3px;
}

/* Caret — quiet chevron in the name cell that rotates 90° when expanded.
   Keeps the row's grid intact (lives inside .lever-row-name) while making
   "this is expandable" discoverable without a separate column. */
.lever-row-caret {
  display: inline-block;
  font-size: 12px;
  font-weight: 400;
  color: var(--text-dim);
  margin-right: 5px;
  transform: rotate(0deg);
  transition: transform 0.15s ease-out, color 0.12s;
  width: 8px;
  text-align: center;
  line-height: 1;
}
.lever-row[data-target-id]:hover .lever-row-caret { color: var(--text-secondary); }
.lever-item.expanded > .lever-row .lever-row-caret {
  transform: rotate(90deg);
  color: var(--accent);
}
/* Header row reuses the caret slot for column alignment, but the caret
   itself is invisible — only its bounding box reserves space so the
   "Target" header sits flush with the lever names below. */
.lever-row-caret-spacer { visibility: hidden; }

/* Expanded trigger — slightly cooler tint to read as "currently open" so
   the user can scan a long list and find the active one. */
.lever-item.expanded > .lever-row {
  background: var(--leverage-bg);
  border-bottom-color: transparent;
}

/* Header row — same grid, but every cell is a small dim caps label.
   No bottom rule beyond the standard one between rows. The data rows
   inherit alignment from this row's column tracks. */
/* Column header for the lever rows. Sized up from earlier (9px → 10.5px,
   text-dim → text-secondary, letter-spacing bumped) so it doesn't get
   visually drowned by the section dek immediately above it — the dek is
   11px text-dim, and at the prior 9px text-dim the column header read
   as an afterthought. Now it carries enough weight to anchor the data
   table beneath it. */
.lever-row-header {
  padding-top: 0;
  padding-bottom: 8px;
  border-bottom-color: var(--border);
}
.lever-row-header > span {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--text-secondary);
  line-height: 1;
}
.lever-row-header .lever-row-current,
.lever-row-header .lever-row-goal,
.lever-row-header .lever-row-years {
  /* Header text inherits from above; clear value/leverage colors so the
     header isn't styled like data. */
  color: var(--text-secondary);
  font-weight: 600;
}
.lever-row-name {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.lever-row-current {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: 0.2px;
}

.lever-row-current-num { line-height: 1; }

.lever-row-current-empty {
  font-size: 14px;
  color: var(--text-dim);
  font-weight: 400;
}

.lever-row-unit {
  font-size: 10px;
  font-weight: 400;
  color: var(--text-dim);
  letter-spacing: 0.2px;
}

.lever-row-trend {
  font-size: 11px;
  font-weight: 600;
  margin-left: 2px;
  letter-spacing: 0;
}
.lever-row-trend-good { color: var(--green); }
.lever-row-trend-bad  { color: var(--amber); }
.lever-row-trend-flat { color: var(--text-dim); }

.lever-row-goal {
  font-size: 12px;
  font-weight: 500;
  color: var(--leverage);
  letter-spacing: 0.2px;
  white-space: nowrap;
}

.lever-row-unmeasured .lever-row-goal {
  white-space: normal;
  line-height: 1.4;
  color: var(--text-secondary);
  opacity: 0.95;
}

.lever-row-years {
  font-size: 12px;
  font-weight: 600;
  color: var(--leverage);
  letter-spacing: 0.2px;
  text-align: right;
}

.lever-row-years-empty { color: transparent; }

/* Off-target rows: no extra ink — the gauge fill carries severity. */
.lever-row-off {
  background: transparent;
}

/* ───────────────────────────────────────────────
   Lever expansion — the inline "what moves this?" panel
   that drops in below a clicked lever row. Layered to
   read like a quiet drawer attached to the row, not a
   separate panel competing with the threat detail above.
   Hidden by default; .lever-item.expanded reveals it. */
.lever-row-expansion {
  display: none;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  padding: 14px var(--space-md) 16px calc(var(--space-md) + 4px);
  border-left: 2px solid var(--accent);
}
.lever-item.expanded > .lever-row-expansion {
  display: block;
}
.lever-rows > .lever-item:last-child > .lever-row-expansion,
.lever-more-list > .lever-item:last-child > .lever-row-expansion {
  border-bottom: 0;
}

.lever-exp-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 720px;
}

/* Range bar inside the lever expansion — full-width readout that used to
   live in a dedicated column on every default row. Demoted here so the
   resting list stays scannable; the bar's full width also gives the
   marker more breathing room than the cramped table cell allowed. */
.lever-exp-gauge {
  display: block;
  padding-bottom: 4px;
}
.lever-exp-gauge .scorecard-range {
  display: block;
  width: 100%;
}

.lever-exp-desc {
  font-size: 12px;
  line-height: 1.55;
  color: var(--text-secondary);
  letter-spacing: 0.1px;
}

/* Meta row — stretch goal + "also moves" sit on one line, separated by
   subtle vertical dividers. Both are quick-read facts; promoting either
   to its own row would over-emphasize them relative to the action list. */
.lever-exp-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: center;
  font-size: 11px;
}

.lever-exp-stretch,
.lever-exp-also {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-secondary);
}

.lever-exp-stretch-label,
.lever-exp-also-label {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.lever-exp-stretch {
  color: var(--leverage);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
}
.lever-exp-stretch .lever-exp-stretch-label { color: var(--text-dim); }

.lever-exp-also-item {
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
  margin-right: 8px;
  color: var(--text-secondary);
}
.lever-exp-also-item:last-child { margin-right: 0; }
.lever-exp-also-years {
  color: var(--leverage);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Sectioned body — small uppercase label sits flush-left, content stacks
   below. Same typographic treatment as section titles elsewhere so the
   expansion feels of-a-piece with the rest of the drill-down. */
.lever-exp-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.lever-exp-section-label {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* Action mini-list — buttons that jump to the Plan tab with the action
   focused. Compact: priority dot + text + +Xy + arrow. The dot reuses
   the same priority-color scheme used elsewhere so urgency reads instantly. */
.lever-exp-action-list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  list-style: none;
}

.lever-exp-action {
  display: grid;
  grid-template-columns: 8px 1fr auto 14px;
  align-items: center;
  column-gap: 10px;
  width: 100%;
  padding: 7px 10px 7px 8px;
  border: 0;
  background: transparent;
  border-radius: 4px;
  cursor: pointer;
  font-family: var(--font-mono);
  text-align: left;
  transition: background 0.12s;
}
.lever-exp-action:hover {
  background: var(--bg-hover);
}

.lever-exp-action-pri {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-dim);
}
.lever-exp-action.priority-urgent .lever-exp-action-pri { background: var(--red); }
.lever-exp-action.priority-soon .lever-exp-action-pri { background: var(--amber); }
.lever-exp-action.priority-ongoing .lever-exp-action-pri { background: var(--green); }
.lever-exp-action.priority-pharmaceutical .lever-exp-action-pri { background: var(--accent); }

.lever-exp-action-text {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-primary);
  letter-spacing: 0.1px;
  line-height: 1.4;
}

.lever-exp-action-years {
  font-size: 11px;
  font-weight: 600;
  color: var(--leverage);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
}

.lever-exp-action-arrow {
  font-size: 11px;
  color: var(--text-dim);
  transition: color 0.12s, transform 0.12s;
}
.lever-exp-action:hover .lever-exp-action-arrow {
  color: var(--accent);
  transform: translate(2px, -2px);
}

.lever-exp-action-more {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.4px;
  padding-left: 24px;
}

.lever-exp-empty {
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-secondary);
  padding: 6px 10px 6px 8px;
  border-left: 2px solid var(--amber);
  background: var(--amber-bg);
  border-radius: 0 3px 3px 0;
}
.lever-exp-empty strong {
  color: var(--leverage);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Evidence — literature one-liner + canonical source link. The link uses
   the same external-arrow treatment as other ↗ cites so the source pattern
   is consistent across surfaces. */
.lever-exp-evidence-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.lever-exp-evidence-text {
  font-size: 11px;
  line-height: 1.55;
  color: var(--text-secondary);
}

.lever-exp-evidence-link {
  font-size: 10px;
  color: var(--accent);
  text-decoration: none;
  letter-spacing: 0.3px;
  font-weight: 500;
  align-self: flex-start;
  border-bottom: 1px dotted transparent;
  transition: border-color 0.12s, color 0.12s;
}
.lever-exp-evidence-link:hover {
  color: var(--text-primary);
  border-bottom-color: var(--accent);
}

/* Cross-highlight — when a lever is expanded above, the action rows that
   the lever moves get a thin cyan left accent. Inset shadow so layout
   doesn't shift; the accent reads as "this is what your open lever
   reaches into." */
.risk-action-row.lever-linked {
  box-shadow: inset 3px 0 var(--accent);
  background: var(--leverage-bg);
}

/* Progressive disclosure for lower-impact levers. Summary line is the
   call-to-expand; expanded rows reuse the same lever-row grid. */
.lever-more {
  margin-top: 2px;
  display: flex;
  flex-direction: column;
}

.lever-more-head {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  padding: 10px 4px 10px var(--space-sm);
  border-top: 1px solid var(--border-subtle);
  cursor: pointer;
  list-style: none;
}
.lever-more-head::-webkit-details-marker { display: none; }

.lever-more-summary {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.lever-more-summary-open { display: none; }
.lever-more[open] .lever-more-summary-closed { display: none; }
.lever-more[open] .lever-more-summary-open { display: inline; }

.lever-more-arrow {
  font-size: 10px;
  color: var(--text-dim);
  transition: transform 0.15s;
  opacity: 0.9;
}

.lever-more[open] .lever-more-arrow {
  transform: rotate(-90deg);
}

/* When open, push the toggle below the revealed rows (accordion-style),
   so the trigger always sits at the boundary between shown and hidden
   content rather than fragmenting the table mid-stream. The list takes
   the head's old border-top so the divider stays put between the
   originally-visible rows and the expanded ones. */
.lever-more[open] .lever-more-list {
  order: 1;
  border-top: 1px solid var(--border-subtle);
}
.lever-more[open] .lever-more-head { order: 2; }

/* Secured-lever chip row — reused by the threat-level Banked footer
   (see `.risk-banked` below). The chips themselves are at-goal levers;
   each is a pill-like button so the eye reads them as discrete facts,
   not a sentence. No pill / details wrapper here anymore — Banked owns
   the disclosure shell and the section heading. */
.lever-secured-wrap {
  display: flex;
  flex-direction: column;
}
/* When the spotlight panel is open, square off the bottom corners of
   the chip row so the chip row + panel read as a single connected
   surface (chip row top, panel bottom). */
.lever-secured-wrap.has-open .lever-secured {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

.lever-secured {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 14px var(--space-md);
  background: var(--green-bg);
  border-radius: var(--radius-sm);
  font-variant-numeric: tabular-nums;
  flex-wrap: wrap;
}

.lever-secured-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--green);
}

.lever-secured-items {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  flex: 1;
}

.lever-secured-item {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 3px 10px;
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(5, 150, 105, 0.18);
  border-radius: 3px;
  font-size: 11px;
  letter-spacing: 0.2px;
  font-family: var(--font-mono);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, transform 0.12s;
}
.lever-secured-item:hover {
  background: rgba(255, 255, 255, 0.95);
  border-color: rgba(5, 150, 105, 0.42);
}
.lever-secured-item:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 2px;
}
/* Selected chip — filled green, white text. Reads as "this is the one
   the panel is about" without needing a literal connector line down to
   the panel below. */
.lever-secured-item[aria-expanded="true"] {
  background: var(--green);
  border-color: var(--green);
}
.lever-secured-item[aria-expanded="true"] .lever-secured-item-label,
.lever-secured-item[aria-expanded="true"] .lever-secured-item-value {
  color: #fff;
}

.lever-secured-item-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.lever-secured-item-value {
  font-weight: 500;
  color: var(--green);
  font-variant-numeric: tabular-nums;
}

.lever-secured-tail {
  margin-left: auto;
  color: var(--green);
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.3px;
}

/* ── Spotlight panel for a selected secured lever ──
   Sibling of the chip row, not a continuation. Different shape than
   the active-lever per-row expansion above (no left accent, slightly
   inset, white surface) — the visual change of medium signals the
   tonal change: past-tense, won, post-mortem rather than to-do. */
.lever-secured-panel {
  background: #fff;
  border: 1px solid rgba(5, 150, 105, 0.22);
  border-top: 0;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  padding: 14px var(--space-md) 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.lever-secured-panel-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border-subtle);
}

.lever-secured-panel-name {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: 0.2px;
}

.lever-secured-panel-check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--green);
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  transform: translateY(-1px);
}

.lever-secured-panel-current {
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
  color: var(--green);
  font-variant-numeric: tabular-nums;
}
.lever-secured-panel-num {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.3px;
}
.lever-secured-panel-unit {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.4px;
}

.lever-secured-panel-locked {
  margin-left: auto;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-dim);
}
.lever-secured-panel-locked strong {
  color: var(--green);
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.3px;
  margin-left: 4px;
  text-transform: none;
}

/* Range bar in the panel — full-width readout. The marker renders
   nominal (green) so the eye reads "comfortably won" rather than
   "borderline" — the value is past goal by definition here. */
.lever-secured-panel-gauge {
  display: block;
}
.lever-secured-panel-gauge .scorecard-range {
  display: block;
  width: 100%;
}

/* Stretch-gap hint sits inline next to the stretch goal, e.g.
   "Stretch <50 mg/dL · 12 to go". Quiet — it's a forward pointer,
   not a warning. */
.lever-secured-panel-stretch-gap {
  margin-left: 6px;
  color: var(--text-dim);
  font-weight: 500;
  font-size: 10px;
  letter-spacing: 0.3px;
}

/* Maintainers empty-state — softer than the active-lever empty.
   "Sustained by lifestyle" is a positive read, not a gap to close. */
.lever-secured-panel-empty {
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-secondary);
  padding: 6px 10px 6px 8px;
  border-left: 2px solid var(--green);
  background: var(--green-bg);
  border-radius: 0 3px 3px 0;
}

/* ═══════════════════════════════════════════════════════════════════
   LIFE CALENDAR
   A life-in-weeks grid: 52 cols × 95 rows. Lived weeks fill the top mass,
   the future is empty. The expected-death band is a band of cells the user
   can count. Reachable cells (gap to demographic ceiling) sit beyond.
   Threat-overlay and plan-on toggles re-classify cells in place.
   ═══════════════════════════════════════════════════════════════════ */

/* When the calendar is open, lock body scroll so the immersive view
   feels like its own context. */
body.lc-locked { overflow: hidden; }

.lc-frame {
  display: grid;
  grid-template-rows: auto 1fr auto;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
/* Body row (canvas + photo backdrop) shares grid-row 2 — they stack
   without competing for layout. Header/footer occupy rows 1 and 3 and
   stay on clean white because the photo never enters their cells. */
.lc-frame > .lc-seasons,
.lc-frame > .lc-canvas { grid-row: 2; grid-column: 1; }
.lc-frame > .lc-canvas { position: relative; z-index: 1; }

/* ── Header ──
   Frame on the corners (mark / close), hero block centered over the grid.
   The hero is the chapter title for this view: a giant numeral echoing the
   Overview entrypoint, label + meta beneath. Centering the hero unifies it
   with the visual center of the grid below. The mark/close are absolutely
   positioned so they don't pull the hero off-axis. */
.lc-head {
  position: relative;
  padding: 22px var(--space-lg) 26px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  text-align: center;
}

/* Aescle mark, top-left frame. Topbar is hidden in this view, so the
   wordmark comes along with the glyph to anchor identity at the same
   weight as the rest of the app. Accent color, not muted. */
.lc-head-mark {
  position: absolute;
  left: var(--space-lg);
  top: 20px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px;
  margin: -4px -6px;
  border: none;
  background: none;
  color: var(--accent);
  font-family: inherit;
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: opacity 0.15s ease;
}
.lc-head-mark:hover {
  opacity: 0.7;
}
.lc-head-mark:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 2px;
}
.lc-head-mark-word {
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 4px;
  color: var(--accent);
}

.lc-close {
  position: absolute;
  right: var(--space-lg);
  top: 18px;
  width: 28px;
  height: 28px;
  border: none;
  background: none;
  color: var(--text-dim);
  font-size: 15px;
  cursor: pointer;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.12s;
}
.lc-close:hover {
  color: var(--text-primary);
  background: var(--bg-hover);
}

/* Hero block — chapter title for the calendar.
   Stacked: kicker / numeral / label / meta. The numeral is the punch
   (echoes the Overview hero number); kicker gives identity; label and
   meta locate the number temporally (years remaining, age band, year). */
.lc-hero {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  line-height: 1;
}
.lc-hero-kicker {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 2.6px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 6px;
}
.lc-hero-num {
  font-size: 56px;
  font-weight: 600;
  letter-spacing: -1.5px;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  position: relative;
  display: inline-flex;
  align-items: baseline;
  /* Tiny crossfade when hero numbers swap on mode change. */
  transition: color 0.18s ease;
}
.lc-hero-num-delta {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.3px;
  color: var(--accent);
  margin-left: 8px;
  align-self: flex-start;
  margin-top: 4px;
}
.lc-hero-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--lc-hero-tint, var(--text-secondary));
  margin-top: 8px;
  transition: color 0.18s ease;
}
.lc-hero-meta {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.6px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  margin-top: 4px;
}

/* ── Canvas (scrolls only when viewport is genuinely tiny) ── */
.lc-canvas {
  flex: 1;
  overflow-y: auto;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: var(--space-xl) var(--space-lg);
  /* Cell sizing primitives — tweak in one place.

     --lc-cell scales with viewport height so the 105-row × 52-col grid
     fits without scrolling on any reasonable monitor. The 560px subtraction
     reserves chrome: ~165 head + ~143 foot + 64 canvas-padding + 24 col
     header + canvas-inner gap + 104 inter-row gaps + ~50px slack for
     scrollbar gutter, dvh quirks, and chip-row wrap. Above ~1600px tall
     the clamp caps cells at 9px (chart shouldn't bloat on huge externals).
     Below ~900px the clamp floors at 4px and overflow-y catches the rest
     — laptops below that height are uncommon enough that scroll is fine. */
  --lc-cell: clamp(4px, calc((100dvh - 560px) / 105), 9px);
  --lc-gap: 1px;
  --lc-axis-w: 40px;
  --lc-threat-color: transparent;
  /* Future-region base fill. Cells in the future use this directly;
     death-band and threat-overlay cells stack their semi-transparent
     wash *over* this color so their tail/shoulder rows fade into the
     same gray as the surrounding future, not into white. */
  --lc-future-bg: #e8ecf0;
}
.lc-canvas::-webkit-scrollbar { width: 6px; }
.lc-canvas::-webkit-scrollbar-track { background: transparent; }
.lc-canvas::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }

.lc-canvas-inner {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.lc-canvas-header {
  display: flex;
  gap: var(--space-sm);
}
.lc-axis-y-spacer {
  width: var(--lc-axis-w);
  flex-shrink: 0;
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 1px;
  font-weight: 600;
  text-align: right;
  padding-right: 6px;
  align-self: end;
  text-transform: uppercase;
}
.lc-axis-x {
  display: grid;
  grid-template-columns: repeat(52, var(--lc-cell));
  gap: var(--lc-gap);
  align-items: end;
  height: 16px;
}
.lc-axis-x-tick {
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.5px;
  white-space: nowrap;
  font-weight: 500;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  /* Anchor at the column's left edge so the digit reads above its mark. */
  position: relative;
  width: var(--lc-cell);
  overflow: visible;
}

.lc-canvas-body {
  display: flex;
  gap: var(--space-sm);
}
.lc-axis-y {
  display: grid;
  grid-template-rows: repeat(105, var(--lc-cell));
  gap: var(--lc-gap);
  width: var(--lc-axis-w);
  flex-shrink: 0;
}
.lc-axis-y-cell {
  font-size: 11px;
  color: transparent;
  text-align: right;
  padding-right: 6px;
  /* Font is taller than the cell — let the label overflow the empty
     transparent cells above/below so it reads at full height. */
  line-height: 1;
  align-self: center;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  font-weight: 500;
  letter-spacing: 0.4px;
  overflow: visible;
}
.lc-axis-y-decade {
  color: var(--text-secondary);
}
/* Right rail — calendar-year ticks at exact Jan-1 sub-row positions.
   Visually demoted vs. the AGE axis so the eye reads age as primary and
   year as ambient context: smaller, dimmer, regular weight, no header.
   The 20-year window containing NOW is the one anchor that earns
   secondary-text weight.
   Container height matches the grid exactly (105 cells + 104 gaps) so the
   absolute `top` values resolve to the same rows as the cells they sit
   beside. Keep in sync with MAX_AGE in js/components/life-calendar.js. */
.lc-axis-y-right {
  position: relative;
  width: var(--lc-axis-w);
  flex-shrink: 0;
  height: calc(105 * var(--lc-cell) + 104 * var(--lc-gap));
}
.lc-year-tick {
  position: absolute;
  left: 0;
  display: flex;
  align-items: center;
  gap: 5px;
  /* Vertical center sits on the year boundary. */
  transform: translateY(-50%);
  font-size: 10px;
  color: var(--text-dim);
  font-weight: 400;
  letter-spacing: 0.3px;
  line-height: 1;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.lc-year-tick-rule {
  display: block;
  width: 5px;
  height: 1px;
  background: currentColor;
  opacity: 0.6;
}
.lc-axis-y-spacer-right {
  text-align: left;
  padding-right: 0;
  padding-left: 6px;
}

/* ── Grid + cells ── */
.lc-grid {
  display: grid;
  grid-template-columns: repeat(52, var(--lc-cell));
  grid-template-rows: repeat(105, var(--lc-cell));
  gap: var(--lc-gap);
  position: relative;
}

/* In-grid markers — typographic flags pointing at now/end anchor cells.
   Absolute over the grid (out of grid flow); positioned via inline calc()
   against --lc-cell / --lc-gap so they track viewport-driven cell scaling.
   Now sits to the right of its cell (clean future cells in the same row);
   End sits above its cell (clean future zone above the band). */
.lc-marker {
  position: absolute;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  line-height: 1;
}
.lc-marker-now {
  /* Pill-backed for the same reason as lc-marker-end: threat overlays can
     wash the row in saturated tones (accidents-ochre, heart-crimson) that
     bleed against cyan text. The pill keeps the label readable on any
     backdrop. Padding offsets are compensated for in updateMarkers(). */
  color: var(--accent);
  display: flex;
  align-items: center;
  padding: 2px 5px;
  background: var(--bg-page);
  border-radius: 3px;
}
.lc-marker-end {
  /* Sits above the band's leading edge — without a fill it bleeds into the
     wash on threat overlays where the band runs to the cell row above. Use a
     page-bg pill so the text reads against any backdrop; padding/offset are
     compensated for in updateMarkers() so the label still anchors to the
     cell's leading edge. Color set inline (varies by mode). */
  padding: 2px 5px;
  background: var(--bg-page);
  border-radius: 3px;
}
.lc-cell {
  display: block;
  width: var(--lc-cell);
  height: var(--lc-cell);
  background: var(--border-subtle);
  border-radius: 1.5px;
  /* Background fades stay slow (mode switches); opacity is the legend-hover
     mass-fade and switches instantly — any easing reads as lag. */
  transition: background 0.25s ease, box-shadow 0.15s ease;
}

/* Past — the visceral mass at the top. Filled enough to read as
   "spent" without competing with the warm tones below.
   The lived block is broken into four soft chapters by season: each cell
   carries its season index in data-s (0=spring 1=summer 2=autumn 3=winter)
   and the base gray is mixed with ~12% of the season hue. Same lightness,
   tiny temperature shift — the past reads as a stacked landscape rather
   than an undifferentiated mass. Severity overlays live on different cell
   classes so this layer never competes with red/cause-color washes. */
.lc-cell-past { background: #b0b6bf; }
.lc-cell-past[data-s="0"] { background: color-mix(in srgb, #b0b6bf 82%, var(--season-spring)); }
.lc-cell-past[data-s="1"] { background: color-mix(in srgb, #b0b6bf 82%, var(--season-summer)); }
.lc-cell-past[data-s="2"] { background: color-mix(in srgb, #b0b6bf 82%, var(--season-autumn)); }
.lc-cell-past[data-s="3"] { background: color-mix(in srgb, #b0b6bf 82%, var(--season-winter)); }

/* Now — single bright cell, the "you are here" anchor. */
.lc-cell-now {
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--bg-panel), 0 0 0 3.5px var(--accent);
  position: relative;
  z-index: 2;
}

/* End — paired counterpart to 'now'. Single solid red cell at the LE
   midpoint, ringed the same way 'now' is, so the user can literally
   count the cells between the two anchors. The ring lifts it above
   the death-band wash so it's the focal point of that band. */
.lc-cell-end {
  background: var(--red);
  box-shadow: 0 0 0 2px var(--bg-panel), 0 0 0 3.5px var(--red);
  position: relative;
  z-index: 2;
}

/* Peak — same anchor concept, but in the active threat's color. Used in
   threat-overlay mode at the peak-age cell. */
.lc-cell-peak {
  background: var(--lc-threat-color);
  box-shadow: 0 0 0 2px var(--bg-panel), 0 0 0 3.5px var(--lc-threat-color);
  position: relative;
  z-index: 2;
}

/* Future, before the death band — empty page, the time you have.
   Slightly cooler/stronger than --bg-surface so the grid structure
   reads as a discrete data layer against the warm photographic wings,
   not as empty panel space. */
.lc-cell-future {
  background: var(--lc-future-bg);
}

/* Death band — warm wash. Aggregate mode (no threat selected). The actual
   LE band [leLo, leHi) renders at full strength; a few shoulder rows above
   carry a positional intensity ramp set in applyState, fading the band into
   the empty future. Peaks at 50% red — matches the prior fixed alpha so the
   focal density at the LE midpoint is unchanged. */
.lc-cell-death {
  background-color: var(--lc-future-bg);
  background-image: linear-gradient(
    color-mix(in srgb, var(--red) calc(var(--lc-cell-intensity, 1) * 50%), transparent),
    color-mix(in srgb, var(--red) calc(var(--lc-cell-intensity, 1) * 50%), transparent)
  );
}

/* Reachable — green tint, sits beyond the death band when biomarkers
   leave a gap to the demographic ceiling. */
.lc-cell-reachable {
  background: rgba(5, 150, 105, 0.4);
}

/* Recovered — cells the action plan reclaims (between the original
   death band and its shifted-right position). Cyan = leverage axis. */
.lc-cell-recovered {
  background: rgba(8, 145, 178, 0.55);
}

/* Beyond — past the demographic ceiling, dimmer than 'future'. */
.lc-cell-beyond {
  background: var(--bg-surface);
  opacity: 0.5;
}

/* Threat-overlay mode — cause-PDF gradient. Each cell carries an inline
   --lc-cell-intensity in [0,1] computed from P(alive at age) × cause share.
   The wash percentage scales 0 → 80% so peak cells match the old categorical
   peak-band, mid-band cells are similar to the old uniform band, and tail
   cells fade smoothly into the surrounding future. Color is the threat's
   identity hue via --lc-threat-color (set on .lc-grid). */
.lc-cell-threat {
  background-color: var(--lc-future-bg);
  background-image: linear-gradient(
    color-mix(in srgb, var(--lc-threat-color) calc(var(--lc-cell-intensity, 0) * 80%), transparent),
    color-mix(in srgb, var(--lc-threat-color) calc(var(--lc-cell-intensity, 0) * 80%), transparent)
  );
}

/* Cell focus — slim outline ring on the cell currently under the cursor.
   Inset shadow rather than outline/border, so the ring sits *inside* the
   cell footprint and never overlaps neighbors or shifts layout. The shadow
   color picks up the bg-page so the ring reads as a subtle inset frame
   regardless of which fill the cell happens to have. */
.lc-cell-focus {
  box-shadow: inset 0 0 0 1.5px var(--text-primary);
  z-index: 3;
  position: relative;
}
/* Anchor cells (now / end / peak) are already prominent. On focus, just
   thicken the existing outer ring slightly — adding an inner ring on top
   would read as a bullseye and fight the anchor's identity. */
.lc-cell-now.lc-cell-focus {
  box-shadow: 0 0 0 2px var(--bg-panel), 0 0 0 4.5px var(--accent);
}
.lc-cell-end.lc-cell-focus {
  box-shadow: 0 0 0 2px var(--bg-panel), 0 0 0 4.5px var(--red);
}
.lc-cell-peak.lc-cell-focus {
  box-shadow: 0 0 0 2px var(--bg-panel), 0 0 0 4.5px var(--lc-threat-color);
}

/* ── Readout (inline ghost text) ──
   Lives in the column-header row, right-aligned next to the WK ticks. No
   chrome, no border — just text on the same baseline as the axis labels so
   it reads as part of the chart's frame, not a separate widget. Hidden by
   default; opacity fades in only when a cell is hovered. The probability
   tail (`72% ALIVE`) is conditionally visible — past/now don't have an
   informative probability to report, so the third field disappears for them. */
.lc-readout {
  /* Own row above the canvas-header. Right-aligned within canvas-inner but
     pulled left by the right-rail's width so its trailing edge lands on the
     grid's right edge — flush with col 52, not the year column. Without
     that offset, short legend-mode strings ("LIVED") sit directly above the
     year ticks and read as a header for them. The bottom margin opens space
     between the readout and the WK column headers below it — they're two
     distinct strata of information and shouldn't crowd. */
  align-self: flex-end;
  margin-right: calc(var(--lc-axis-w) + var(--space-sm));
  margin-bottom: 12px;
  height: 16px;
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
}
.lc-readout-active {
  opacity: 1;
}
.lc-readout-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-dim);
}
.lc-readout-value {
  color: var(--text-primary);
  font-weight: 600;
  letter-spacing: 0.3px;
  transition: color 0.16s ease;
}
.lc-readout-sep {
  color: var(--text-dim);
  opacity: 0.6;
  margin: 0 1px;
}
/* Status tail — always populated (LIVED / NOW / Nx% ALIVE). Min-width keeps
   the slot's width stable across the different strings so AGE / DATE don't
   shift horizontally as the user moves between past, now, and future cells. */
.lc-readout-value-status {
  display: inline-block;
  min-width: 84px;
  text-align: right;
}
.lc-readout-status-past { color: var(--text-dim); }
.lc-readout-status-now { color: var(--accent); }
.lc-readout-status-future { color: var(--text-primary); }
.lc-readout-status-future-mid { color: var(--amber); }
.lc-readout-status-future-low { color: var(--red); }

/* ── Footer (legend / controls / caption) ── */
.lc-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg) var(--space-lg);
  border-top: 1px solid var(--border);
  flex-shrink: 0;
  background: var(--bg-page);
}

.lc-legend {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  justify-content: center;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 1.3px;
  text-transform: uppercase;
  color: var(--text-dim);
}
.lc-legend-item {
  /* The chip is a button so it's discoverable as interactive. Reset native
     button chrome — it should still read as quiet legend text. */
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: none;
  border: 0;
  padding: 4px 6px;
  margin: -4px -6px;
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
  cursor: pointer;
  border-radius: 4px;
}
.lc-legend-item:hover,
.lc-legend-item-active {
  color: var(--text-secondary);
  background: var(--bg-surface);
}
.lc-swatch {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 1.5px;
  background: var(--border-subtle);
}
.lc-legend-past .lc-swatch { background: #c8ccd2; }
.lc-legend-reachable .lc-swatch { background: rgba(5, 150, 105, 0.5); }

/* Grid emphasis — when a legend chip is hovered, fade every cell that isn't
   the targeted phase so the matching mass pops. Anchor cells (now / end /
   peak) keep their full saturation either way: they're identity, not phase.
   Heavy fade (0.08) so the matching mass really stands out — at 0.25 the
   non-matching cells still read as a competing surface. */
.lc-grid-emph-past .lc-cell:not(.lc-cell-past):not(.lc-cell-now):not(.lc-cell-end):not(.lc-cell-peak),
.lc-grid-emph-reachable .lc-cell:not(.lc-cell-reachable):not(.lc-cell-now):not(.lc-cell-end):not(.lc-cell-peak) {
  opacity: 0.08;
}

/* Legend mode for the HUD — hide the trailing status slot AND the
   separator that precedes it so the summary reads cleanly as
   `LABEL  COUNT · DESTINATION`, no orphan dot. The earlier
   `:last-of-type` selector failed because the status span is the
   last span (not the sep). `:has(+ status)` picks the right sep. */
.lc-readout-legend .lc-readout-sep:has(+ .lc-readout-value-status),
.lc-readout-legend .lc-readout-value-status {
  display: none;
}

/* Special-case rows (LIVED / REACHABLE) get pulled forward a notch — the
   summary is the only thing on screen at that moment, so it can carry a
   little more visual weight than the per-cell readout. */
.lc-readout-legend {
  font-size: 11px;
  letter-spacing: 0.4px;
}
.lc-readout-legend .lc-readout-label {
  font-size: 10px;
  color: var(--text-secondary);
}
.lc-readout-legend .lc-readout-value {
  color: var(--text-secondary);
  font-weight: 700;
}

/* Two stacked, centered rows. The OVERLAY row pairs a small label with
   the threat chips so the chips read as "lenses" rather than naked
   buttons. The plan-toggle row sits below on its own — it's a state
   modifier, not a filter, and the spatial gap keeps it from being
   confused for another chip. */
.lc-controls {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}
.lc-overlay-row {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  justify-content: center;
}
.lc-controls-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.lc-chips {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}
.lc-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 12px;
  border: 1px solid var(--border);
  background: var(--bg-panel);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.3px;
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: all 0.15s;
}
.lc-chip:hover {
  border-color: var(--text-dim);
  color: var(--text-primary);
}
.lc-chip-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--lc-chip-color, var(--text-dim));
}
.lc-chip-active {
  border-color: var(--text-primary);
  color: var(--text-primary);
  background: var(--bg-page);
}

/* Plan switch — deliberately a different shape language than the OVERLAY
   chips. Chips are *lenses* (which view do you want?); this is a *state
   modifier* (apply your levers or not). A capsule slide-switch with no
   text labels: the knob's position alone carries the binary state — same
   grammar as a hardware toggle. The "WITH PLAN" verb-phrase reads as a
   property to apply, not a category to pick. The +Ny delta is a separate
   framed badge so the three pieces (what / state / outcome) stay distinct.
   Delta is muted when off — visible as an "available" gain — and lights
   up cyan when applied. */
.lc-plan-row {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  justify-content: center;
}
.lc-plan-prefix {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.lc-plan-switch {
  display: inline-flex;
  align-items: center;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}
.lc-plan-switch:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.lc-plan-switch:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 4px;
  border-radius: 999px;
}
.lc-plan-switch-track {
  position: relative;
  width: 30px;
  height: 14px;
  background: var(--border-subtle);
  border: 1px solid var(--border);
  border-radius: 999px;
  transition: background 0.2s, border-color 0.2s;
}
.lc-plan-switch:hover:not(:disabled) .lc-plan-switch-track {
  border-color: var(--text-dim);
}
.lc-plan-switch-knob {
  position: absolute;
  top: 50%;
  left: 1px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--text-dim);
  transform: translate(0, -50%);
  /* Soft mechanical detent — accelerates into the destination, no overshoot. */
  transition: left 0.32s cubic-bezier(0.55, 0.05, 0.25, 1),
              background 0.2s;
}
.lc-plan-switch-on .lc-plan-switch-track {
  background: var(--accent);
  border-color: var(--accent);
}
.lc-plan-switch-on .lc-plan-switch-knob {
  left: calc(100% - 11px);
  background: #fff;
}
.lc-plan-delta {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  padding: 2px 7px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-panel);
  transition: color 0.2s, border-color 0.2s, background 0.2s;
}
.lc-plan-switch-on ~ .lc-plan-delta,
.lc-plan-row:has(.lc-plan-switch-on) .lc-plan-delta {
  color: var(--accent);
  border-color: var(--accent-soft);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}


/* Tighter cell size on shorter viewports so the full lifespan fits
   without scroll. The grid is the centerpiece — losing the asymmetry
   to a fold is worse than losing a pixel of cell size. */
@media (max-height: 940px) {
  .lc-canvas { --lc-cell: 8px; padding: var(--space-lg); }
}
@media (max-height: 820px) {
  .lc-canvas { --lc-cell: 7px; }
}
@media (max-height: 720px) {
  .lc-canvas { --lc-cell: 6px; padding: var(--space-md); }
}

/* ─── Self-report controls ───────────────────────────────────────────
   Two surfaces use these primitives: the lever-row Status block (inside
   risk-detail expansions) and action-card controls (Plan + Risks
   drilldown action rows). Same visual language so the user reads them
   as a coherent affordance pattern across surfaces. */

.lever-exp-status .self-report-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: 4px;
}

.self-report-prompt {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.4;
}

.self-report-buttons {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.self-report-summary {
  font-size: 11px;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
  flex: 1 1 auto;
}
.self-report-summary-empty { color: var(--text-dim); }
.self-report-summary-met   { color: var(--green); }
.self-report-summary-expired { color: var(--amber); }

.self-report-row-date {
  /* When the date affordance is open, the row often grows past one line
     on narrow widths — let it wrap cleanly. */
  align-items: stretch;
}

.sr-btn {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.3px;
  color: var(--text-primary);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  padding: 5px 10px;
  border-radius: 3px;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.sr-btn:hover {
  background: var(--bg-hover);
  border-color: var(--text-dim);
}
.sr-btn-on {
  background: var(--leverage-bg);
  border-color: var(--accent);
  color: var(--accent);
}
.sr-btn-on:hover {
  background: var(--leverage-bg);
}
.sr-btn-done {
  border-color: var(--green);
  color: var(--green);
}
.sr-btn-done:hover {
  background: var(--green-bg);
}

.sr-btn-ghost {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.3px;
  color: var(--text-dim);
  background: transparent;
  border: 0;
  padding: 5px 8px;
  border-radius: 3px;
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.sr-btn-ghost:hover {
  color: var(--text-primary);
  background: var(--bg-hover);
}

.sr-date-wrap {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 4px 0 6px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 3px;
  cursor: pointer;
  transition: border-color 0.12s;
}
.sr-date-wrap:hover { border-color: var(--text-dim); }

.sr-date-icon {
  font-size: 11px;
  opacity: 0.7;
}

.sr-date-input {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-primary);
  background: transparent;
  border: 0;
  padding: 4px 0;
  cursor: pointer;
}
.sr-date-input:focus { outline: none; }

/* ─── Action-card controls (Mark done / Dismiss) ────────────────────
   Quiet by default. Sit at the bottom of an action card, after refs.
   Hover lifts each control without making the whole row noisy. */

.action-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin-top: 10px;
}

/* Quiet the resting state of the Risks-drilldown action list. Controls
   stay in flow (no layout shift on hover) but fade to muted at rest, so
   the eye scans title + why + chip + meter first. Hover or keyboard focus
   on the row brings them to full opacity. Secured / dismissed strips and
   the at-goal "Undo" controls stay full opacity — those rows exist
   precisely so the user can act on them. */
.risk-action-row .action-controls {
  opacity: 0.45;
  transition: opacity 0.12s;
}
.risk-action-row:hover .action-controls,
.risk-action-row:focus-within .action-controls {
  opacity: 1;
}
.risk-banked-action-list .risk-action-row .action-controls,
.risk-action-row .action-controls-secured,
.risk-action-row .action-controls-dismissed {
  opacity: 1;
}

.action-controls-dismissed {
  /* Same chrome as .action-controls but the divider is replaced with a
     fill so the row reads as resting/locked rather than active. */
  background: var(--bg-surface);
  border-top: 0;
  padding: 6px 10px;
  border-radius: 3px;
}

.action-controls-status {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-right: auto;
}

/* Soft commitment ("✓ Done") reads positive, not neutral — same row chrome
   as Dismissed but tinted green so the user can scan the hidden footer and
   tell at a glance which entries they completed vs which they hid. */
.action-controls-status-done {
  color: var(--green);
}

/* Risks-drilldown action rows used to be <button>; they're now divs with
   role=button so inner control buttons are legal. Keep the same visual
   affordance (cursor + hover) on the wrapper. */
.risk-action-row[role="button"] {
  cursor: pointer;
}

/* ── Plan Secured / Dismissed disclosures ─────────────────────────────
   These are <details> blocks that ride the same `.action-group-head` /
   `.action-group-label` typographic chassis as Urgent/Soon/Ongoing
   priority groups, so the eye reads them as peers — not as a different
   kind of UI. The summary is the head; the disclosure list inside is
   styled like the priority group bodies (full action cards with
   threats / why / target pills / refs). */
.plan-secured,
.plan-dismissed {
  margin-top: var(--space-lg);
  border-top: 1px solid var(--border-subtle);
  padding-top: var(--space-md);
}
.plan-secured-head,
.plan-dismissed-head {
  cursor: pointer;
  list-style: none;
}
.plan-secured-head::-webkit-details-marker,
.plan-dismissed-head::-webkit-details-marker { display: none; }

/* Disclosure caret — small chevron, picks up the section's color via
   currentColor so the secured one reads green and the dismissed one
   reads dim without extra rules. */
.plan-disclosure-arrow {
  font-size: 11px;
  opacity: 0.7;
  color: var(--text-dim);
  transition: transform 0.15s;
}
.plan-secured[open] .plan-disclosure-arrow,
.plan-dismissed[open] .plan-disclosure-arrow {
  transform: rotate(90deg);
}

/* `+X.Yy banked` — banked-years tally on Secured. Sits in the
   `.action-group-leverage` slot; framed pill so it reads as the
   headline number for this section, distinct from per-row leverage.
   Green echoes the Risks-side banked footer — banked years are
   ground held, not still-open leverage. */
.plan-secured-tally {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 8px;
  border-radius: 10px;
  background: var(--green-bg);
  color: var(--green);
  font-size: 10px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
}

.plan-disclosure-list {
  margin-top: var(--space-md);
  animation: expand-in 0.15s ease-out;
}

/* Secured rows — thin green rail on the left so they read as ground
   held at a glance. Stays subtle; doesn't fight the row content.
   Same rail treatment as `.risk-banked-action-list .risk-action-row`
   so the eye reads "banked" as the same noun on both sides of the app. */
.action-item-secured {
  border-left: 2px solid var(--green);
  padding-left: var(--space-sm);
}

/* Dismissed rows stay faded — "ground given up." Per-row styling lives
   on .action-item-dismissed (opacity), not on the disclosure list, so
   focus / hover can lift individual rows without affecting siblings. */
.action-item-dismissed { opacity: 0.6; }
.action-item-dismissed:hover { opacity: 0.95; }

/* "At goal" status — replaces ✓ Done / Dismiss on engine-suppressed
   secured rows. The action was suppressed by data, not the user, so
   there's no Undo here; editing happens on the lever Status block.
   Wears the banked-green wash so it reads as the same noun the row's
   green left-rail and section header are already saying. */
.action-controls-secured {
  background: var(--green-bg);
  border-top: 0;
  padding: 6px 10px;
  border-radius: 3px;
}
.action-controls-status-atgoal {
  color: var(--green);
}

/* ── Risks drilldown Dismissed / "+N more" (per-threat) ───────────────
   Same idea on the Risks side, but the row chrome there is `.risk-action-row`
   not `.action-item`, and it lives inside `.risk-actions` not the Plan grid.
   The "Secured / Banked" disclosure used to live here too; it's been
   promoted to a single threat-level Banked footer (`.risk-banked` below)
   so the actions section stays focused on live work and one dismissed
   tail. */
.risk-actions-dismissed {
  margin-top: 12px;
  border-top: 1px solid var(--border-subtle);
  padding-top: 8px;
}
/* "+N more" is the tail of the same live list, not a separate state.
   Indented under the card body and rendered as a quiet list continuation
   so it doesn't read as a peer to Dismissed below. */
.risk-actions-more {
  margin-top: 6px;
  padding-left: var(--space-md);
}
.risk-actions-dismissed-head {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  list-style: none;
  padding: 4px 0;
}
.risk-actions-more-head {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  list-style: none;
  padding: 4px 0;
}
.risk-actions-dismissed-head::-webkit-details-marker,
.risk-actions-more-head::-webkit-details-marker { display: none; }
.risk-actions-dismissed-head { color: var(--text-dim); }

.risk-actions-dismissed-arrow,
.risk-actions-more-arrow {
  font-size: 10px;
  color: var(--text-dim);
  transition: transform 0.15s;
}
.risk-actions-dismissed[open] .risk-actions-dismissed-arrow,
.risk-actions-more[open] .risk-actions-more-arrow {
  transform: rotate(90deg);
}

/* "On the table" tally — neutral pill, reads as opportunity-cost, not
   a win. */
.risk-actions-more-tally {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 10px;
  background: var(--bg-surface);
  color: var(--text-dim);
  font-size: 10px;
  letter-spacing: 0.6px;
  font-weight: 500;
  text-transform: none;
  font-variant-numeric: tabular-nums;
}

.risk-actions-dismissed-list,
.risk-actions-more-list {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.risk-actions-dismissed-list { opacity: 0.85; }

/* ── Risks drilldown Banked footer (per-threat) ───────────────────────
   One quiet line at the foot of the drilldown that records what the user
   has already won on this threat. Replaces the per-section "+Xy banked"
   and "+Xy locked" pills that previously lived in Actions and Levers —
   those were the same fact in two places, both loud for content that is
   by definition not actionable.

   Collapsed: a subdued footnote — small caps lede, green years pulled
   forward as the only saturated token, dim counts trailing. No pill
   chrome; the page already has Actions / Levers / Baseline as the loud
   anchors, and this is meant to ride below them, not compete.
   Expanded: stacks the two lenses (actions completed, levers at goal)
   using the existing row + chip primitives. */
/* No own chrome — the wrapping `.risk-detail-footer` band carries the
   divider and top padding. This section is just the banked content. */
.risk-banked-section {
  margin: 0;
}
.risk-banked-summary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 4px;
  margin: -6px -4px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  letter-spacing: 0.2px;
  color: var(--text-dim);
  cursor: pointer;
  list-style: none;
  user-select: none;
  border-radius: 4px;
  transition: background 0.12s, color 0.12s;
}
.risk-banked-summary::-webkit-details-marker { display: none; }
.risk-banked-summary:hover {
  background: var(--bg-surface);
  color: var(--text-secondary);
}
.risk-banked-summary:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 2px;
}
.risk-banked-check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--green);
  color: #fff;
  font-size: 9px;
  font-weight: 700;
}
.risk-banked-lede {
  font-weight: 500;
  color: var(--text-secondary);
}
.risk-banked-years {
  font-weight: 600;
  color: var(--green);
  font-variant-numeric: tabular-nums;
}
.risk-banked-sep {
  color: var(--text-dim);
  opacity: 0.6;
}
.risk-banked-counts {
  color: var(--text-dim);
}
.risk-banked-caret {
  margin-left: 4px;
  font-size: 12px;
  color: var(--text-dim);
  transform: rotate(0deg);
  transition: transform 0.15s ease-out;
}
.risk-banked[open] .risk-banked-caret {
  transform: rotate(90deg);
  color: var(--green);
}

.risk-banked-body {
  margin-top: var(--space-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.risk-banked-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.risk-banked-group-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-dim);
}
.risk-banked-action-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.risk-banked-action-list .risk-action-row {
  border-left: 2px solid var(--green);
  padding-left: 8px;
}

/* ═══════════ ACTION RECEIPT ═══════════
   Acknowledgment card for self-report events. Lives at the top-center
   of the viewport as a soft floating slab — drop shadow for elevation
   (matching the slide-over's chrome language), generous padding, no
   hairline borders. The +Xy delta and the cyan accent carry the
   semantic; nothing else needs framing. */

.action-receipt-stack {
  position: fixed;
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 220;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  pointer-events: none;
  width: min(560px, calc(100vw - 32px));
}

.action-receipt {
  pointer-events: auto;
  position: relative;
  width: 100%;
  background: var(--bg-panel);
  border-radius: 14px;
  /* Two-layer shadow so the card reads as floating above the page
     without a hard edge: soft lift + tight contact. Tuned to match
     the slide-over's drop-shadow language but lighter, since the
     receipt is transient. */
  box-shadow:
    0 12px 32px rgba(15, 23, 42, 0.10),
    0 2px 8px  rgba(15, 23, 42, 0.06);
  font-family: var(--font-mono);
  color: var(--text-primary);
  overflow: hidden;
  opacity: 0;
  transform: translateY(-14px) scale(0.985);
  transition:
    opacity 280ms ease,
    transform 320ms cubic-bezier(0.2, 0.85, 0.3, 1);
}

.action-receipt-in {
  opacity: 1;
  transform: translateY(0) scale(1);
}

.action-receipt-out {
  opacity: 0;
  transform: translateY(-8px) scale(0.99);
  transition: opacity 240ms ease, transform 240ms ease;
}

.action-receipt-row {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 16px 20px;
}

/* Left badge — the +Xy delta sits as the visual lead, with the small
   "secured" tag below it. Green carries the semantic: the years just
   moved from open leverage into banked ground, which is exactly what
   the toast is acknowledging. Matches the Risks-side banked footer
   and the Plan-side secured pill. */
.action-receipt-lead {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  padding: 8px 14px 8px 0;
  border-right: 1px solid var(--border-subtle);
  min-width: 86px;
}

.action-receipt-delta {
  font-size: 24px;
  font-weight: 600;
  color: var(--green);
  letter-spacing: -0.4px;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.action-receipt-tag {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.6px;
  color: var(--green);
  text-transform: uppercase;
}

.action-receipt-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.action-receipt-text {
  font-size: 14px;
  font-weight: 500;
  color: var(--text-primary);
  line-height: 1.35;
  /* Cap to two lines — long actions wrap, then ellipsize. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.action-receipt-meta {
  font-size: 12px;
  color: var(--text-dim);
  letter-spacing: 0.2px;
}

.action-receipt-undo {
  flex: 0 0 auto;
  background: none;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--text-secondary);
  cursor: pointer;
  padding: 7px 14px;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}

.action-receipt-undo:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
}

.action-receipt-progress {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: transparent;
}

.action-receipt-progress-fill {
  display: block;
  height: 100%;
  width: 100%;
  background: var(--accent);
  opacity: 0.35;
  transform-origin: left center;
}

.action-receipt:hover .action-receipt-progress-fill {
  opacity: 0.6;
}

/* Tween flash — applied to any [data-tween-key] element whose value
   changed across a re-render. The text color shifts to cyan and a
   faint underline sweeps in briefly, so the change reads as a real
   movement of state and not just a text swap. */
.tween-active {
  position: relative;
  color: var(--accent) !important;
  transition: color 250ms ease;
}

.tween-active::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 1px;
  background: var(--accent);
  opacity: 0.8;
  animation: tween-underline 700ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

@keyframes tween-underline {
  0%   { transform: scaleX(0); transform-origin: left; opacity: 0; }
  20%  { opacity: 0.9; }
  100% { transform: scaleX(1); transform-origin: left; opacity: 0; }
}

@media (max-width: 640px) {
  .action-receipt-stack {
    top: 12px;
    width: calc(100vw - 24px);
  }
  .action-receipt-row {
    gap: 14px;
    padding: 14px 16px;
  }
  .action-receipt-lead {
    min-width: 72px;
    padding-right: 12px;
  }
  .action-receipt-delta { font-size: 20px; }
  .action-receipt-text { font-size: 13px; }
}

/* ═══════════════════════════════════════════
   FIRST-SESSION DASHBOARD REVEAL
   Cockpit "boot": fades in #dashboard, raises the Life Horizon band
   first (the headline), then the foot row (lever + context), then
   slides the bottom nav up. Activated by JS adding `is-first-reveal`
   to .app for the first session only.
   ═══════════════════════════════════════════ */

.app.is-first-reveal #dashboard {
  animation: dashboard-shell-fade 900ms ease-out 0ms both;
}
.app.is-first-reveal .ovh-headline {
  animation: dashboard-panel-rise 1500ms cubic-bezier(.2, .7, .2, 1) 700ms both;
}
.app.is-first-reveal .ovh-timeline-mount {
  animation: dashboard-panel-rise 1500ms cubic-bezier(.2, .7, .2, 1) 1150ms both;
}
.app.is-first-reveal #bottom-nav {
  animation: dashboard-nav-rise 1200ms cubic-bezier(.2, .7, .2, 1) 1750ms both;
}

@keyframes dashboard-shell-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes dashboard-panel-rise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes dashboard-nav-rise {
  from { opacity: 0; transform: translateY(110%); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .app.is-first-reveal #dashboard,
  .app.is-first-reveal .ovh-headline,
  .app.is-first-reveal .ovh-timeline-mount,
  .app.is-first-reveal #bottom-nav {
    animation: none;
  }
}

/* ═══════════ LANDING / PUBLIC ENTRANCE ═══════════
   Single-viewport pre-app surface with two motion systems:
   (1) ambient vector-field drift for sophisticated dynamism,
   (2) a domain trajectory path that evolves toward better outcomes. */

.landing {
  --lp-nav-h: 60px;
  position: relative;
  height: 100%;
  width: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  scroll-behavior: smooth;
  background: var(--bg-page);
  color: var(--text-primary);
}
/* While the user is inside the mortality scrollytelling, hide the
   scrollbar. The dwell-clamp and state-3 entry snap legitimately move
   scrollTop during beat transitions; without this, the user sees the
   scrollbar twitch and reads it as losing their place. Class is
   toggled by `applyState` in landing.js and removed when state resets. */
.landing.lp-mortality-locked {
  scrollbar-width: none;
}
.landing.lp-mortality-locked::-webkit-scrollbar {
  width: 0;
  height: 0;
}

/* Hero section — the cinematic top-of-page. Owns the particle swarm,
   ambient haze, trajectory layer, and the original landing-frame copy.
   Fills the viewport so the experience opens with the cinematic, then
   scrolls into the rest of the marketing page below. */
.landing-hero-section {
  position: relative;
  /* Sticky nav above sits in normal flow and pushes the section down,
     so subtract its height to keep the cinematic flush with the fold —
     otherwise .landing-scrollcue (pinned to the section's bottom) lands
     below the viewport on first paint. */
  min-height: calc(100vh - var(--lp-nav-h, 60px));
  width: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background:
    radial-gradient(ellipse 98% 64% at 44% -16%, rgba(8,145,178,0.018), transparent 74%),
    radial-gradient(ellipse 96% 68% at 84% 102%, rgba(22,163,74,0.008), transparent 78%),
    /* Bottom-of-hero gradient deepens toward the dark mortality
       backdrop below, so the seam reads as dusk rather than a cut.
       The lower wash stays under ~14% alpha — enough that a slow
       scroller sees something darkening below without breaking the
       airy cinematic above the fold. */
    linear-gradient(180deg, rgba(255,255,255,0.26) 0%, rgba(255,255,255,0) 28%, rgba(7,24,39,0.04) 72%, rgba(11,20,34,0.14) 100%),
    var(--bg-page);
}

/* ── Shared ambient scene (onboarding) ── */
.welcome-model-stage,
.landing-ambient {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
}

/* Particle swarm host — full-viewport canvas. Sits BELOW the chrome
   (z-index 0 vs the frame's z-index 2) so headline/pillars/CTA
   occlude the cloud rather than the canvas painting on top of them
   with additive blending. */
.landing-cloud {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.landing-swarm-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}

/* Click ripple — subtle expanding ring at the click point. Sits above
   the cloud but below the frame chrome so it never overlaps headline /
   pillars / CTA. Self-removes on animationend (see landing.js). */
.landing-ripple {
  position: absolute;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 1px solid rgba(16, 165, 200, 0.32);
  transform: translate(-50%, -50%) scale(0.3);
  opacity: 0;
  pointer-events: none;
  z-index: 1;
  animation: landing-ripple-out 800ms cubic-bezier(0.16, 0.84, 0.34, 1) forwards;
}

@keyframes landing-ripple-out {
  0% {
    transform: translate(-50%, -50%) scale(0.3);
    opacity: 0;
    border-width: 1px;
  }
  18% {
    opacity: 0.32;
  }
  100% {
    transform: translate(-50%, -50%) scale(2.6);
    opacity: 0;
    border-width: 0.4px;
  }
}

/* When detached as a bridge, the canvas is reparented to <body> with
   fixed positioning so it persists across the landing → welcome pane
   swap. z-index sits below welcome-card (z:2) but above the model
   stage (z:0) so the figure remains visible while the 3D model fades
   in beneath it. */
.landing-swarm-bridge {
  position: fixed !important;
  z-index: 1 !important;
  pointer-events: none;
  opacity: 1;
  transition: opacity 700ms ease;
}

.landing-ambient::before {
  content: "";
  position: absolute;
  z-index: 1;
  inset: -18% -14%;
  background:
    radial-gradient(ellipse 34% 54% at 18% 40%, rgba(8,145,178,0.15), rgba(8,145,178,0) 74%),
    radial-gradient(ellipse 30% 50% at 74% 30%, rgba(8,145,178,0.11), rgba(8,145,178,0) 74%),
    radial-gradient(ellipse 36% 56% at 66% 72%, rgba(8,145,178,0.09), rgba(8,145,178,0) 76%),
    radial-gradient(ellipse 24% 40% at 48% 52%, rgba(255,255,255,0.3), rgba(255,255,255,0) 78%);
  opacity: 0.24;
  filter: blur(12px);
  animation: landing-diffuse-a 14s ease-in-out infinite alternate;
}

.landing-ambient::after {
  content: "";
  position: absolute;
  z-index: 2;
  inset: -24% -20%;
  background:
    radial-gradient(ellipse 28% 46% at 22% 64%, rgba(8,145,178,0.1), rgba(8,145,178,0) 76%),
    radial-gradient(ellipse 30% 52% at 78% 62%, rgba(8,145,178,0.09), rgba(8,145,178,0) 78%),
    radial-gradient(ellipse 20% 34% at 54% 24%, rgba(255,255,255,0.24), rgba(255,255,255,0) 82%);
  opacity: 0.2;
  mix-blend-mode: multiply;
  filter: blur(18px);
  animation: landing-diffuse-b 18s ease-in-out infinite alternate;
}

.model-scene-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
/* Full-viewport rather than bound to `.welcome-model-stage` — the
   stage canvas itself is constrained by `.welcome`'s `max-width` on
   wide monitors, but the vignette's atmospheric wash needs to cover
   the entire viewport so the body's plain page color doesn't show as
   a darker stripe on either side of the constrained layout. z-index
   sits above the canvas (z:0 within stage) but below the card
   (z:2). pointer-events:none so the wash doesn't block clicks. */
.model-scene-vignette {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(ellipse 76% 54% at 50% 48%, rgba(255,255,255,0) 8%, rgba(255,255,255,0.4) 72%, rgba(255,255,255,0.82) 100%),
    linear-gradient(0deg, rgba(246, 249, 252, 0.96) 0%, rgba(246, 249, 252, 0.7) 16%, rgba(246, 249, 252, 0) 38%),
    linear-gradient(90deg, rgba(246, 249, 252, 0.9) 0%, rgba(246, 249, 252, 0) 16%, rgba(246, 249, 252, 0) 84%, rgba(246, 249, 252, 0.9) 100%),
    radial-gradient(ellipse 90% 64% at 52% 16%, rgba(8,145,178,0.07), rgba(8,145,178,0) 68%);
}

.landing-orb {
  position: absolute;
  width: min(46vw, 560px);
  aspect-ratio: 1;
  left: 70%;
  top: 46%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background:
    radial-gradient(circle at 38% 34%, rgba(255,255,255,0.4), rgba(255,255,255,0.02) 44%, rgba(255,255,255,0) 70%),
    radial-gradient(circle at 62% 64%, rgba(22,163,74,0.016), rgba(8,145,178,0.012) 42%, rgba(8,145,178,0) 74%);
  filter: blur(0.2px);
  opacity: 0.16;
  animation: landing-orb-breathe 14s ease-in-out infinite;
}

/* Domain-specific trajectory layer. Wrapper is sized + positioned;
   the inner svg + marker fill it via `inset: 0`. The wrapper exists so
   the marker (an HTML element with animated pseudo-children) can be
   positioned in the same coordinate space as the svg without using
   `position: fixed` — which would pin it to the viewport even after
   the user scrolls past the hero. */
.landing-trajectory-wrap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 44vh;
  min-height: 240px;
  pointer-events: none;
  z-index: 1;
}
.landing-trajectory {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0.86;
}

.landing-trajectory-track {
  stroke-dasharray: 2500;
  stroke-dashoffset: 2500;
  animation: landing-trace 2600ms cubic-bezier(.4, 0, .2, 1) 620ms forwards;
}

.landing-trajectory-area {
  opacity: 0;
  animation: landing-fade-soft 1800ms ease 1320ms forwards;
}

.landing-trajectory-marker {
  position: absolute;
  z-index: 2;
  pointer-events: none;
}

.landing-trajectory-dot {
  position: absolute;
  left: 0;
  top: 0;
  width: 11px;
  height: 11px;
  border-radius: 999px;
  background:
    radial-gradient(circle at 34% 30%, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0.12) 38%, rgba(255,255,255,0) 62%),
    color-mix(in srgb, var(--accent) 60%, var(--green) 40%);
  transform: translate(-50%, -50%);
  box-shadow:
    0 0 0 2px rgba(8,145,178,0.16),
    0 0 20px rgba(8,145,178,0.36),
    0 0 34px rgba(22,163,74,0.22);
  animation: landing-dot-breathe 3600ms ease-in-out infinite;
  opacity: var(--trajectory-intensity, 0.86);
}

.landing-trajectory-pulse {
  position: absolute;
  left: 0;
  top: 0;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  transform: translate(-50%, -50%) scale(0.2);
  background: radial-gradient(circle, rgba(8,145,178,0.24) 0%, rgba(8,145,178,0.02) 72%);
  opacity: 0;
  animation: landing-dot-pulse 2400ms ease-out infinite;
}

.landing-trajectory-ring {
  position: absolute;
  left: 0;
  top: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  border: 1px solid rgba(8,145,178,0.38);
  opacity: 0.7;
  animation: landing-ring-orbit 5200ms linear infinite;
}

.landing-trajectory-glint {
  position: absolute;
  left: 0;
  top: 0;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: rgba(255,255,255,0.94);
  transform: translate(-50%, -50%);
  box-shadow: 0 0 8px rgba(255,255,255,0.52);
  animation: landing-glint-orbit 5200ms linear infinite;
}

/* Content frame — flex column, generous breathing room, max-width to
   keep line lengths cockpit-tight. */
.landing-frame {
  position: relative;
  z-index: 2;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  max-width: 1040px;
  width: 100%;
  margin: 0 auto;
  padding: clamp(28px, 5vh, 64px) clamp(20px, 4vw, 52px);
  text-align: center;
}

/* ── Brand mark ── */
.landing-mark {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  color: var(--accent);
  opacity: 0;
  animation: landing-rise 1400ms cubic-bezier(.2, .7, .2, 1) 300ms forwards;
}
.landing-mark > svg { display: block; }
.landing-mark-word {
  font-family: var(--font-mono);
  font-size: 32px;
  font-weight: 600;
  letter-spacing: 14px;
  color: var(--accent);
}
.landing-mark-tag {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 3.2px;
  color: color-mix(in srgb, var(--text-secondary) 70%, var(--accent) 30%);
  text-transform: uppercase;
}

/* ── Hero ── */
.landing-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(28px, 4vh, 44px);
  max-width: 760px;
  margin: 0 auto;
}
.landing-headline {
  font-family: var(--font-mono);
  font-size: clamp(34px, 5vw, 60px);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.8px;
  color: var(--text-primary);
  /* Layered halo of the page background. Reads as scattered light in the
     fog rather than a card edge, but masks particles behind the glyphs. */
  text-shadow:
    0 0 14px rgba(255,255,255,0.95),
    0 0 28px rgba(255,255,255,0.78),
    0 0 56px rgba(255,255,255,0.55),
    0 10px 36px rgba(8,145,178,0.11);
  opacity: 0;
  animation: landing-rise 1600ms cubic-bezier(.2, .7, .2, 1) 800ms forwards;
}
.landing-sub {
  font-family: var(--font-mono);
  font-size: clamp(14px, 1.32vw, 19px);
  font-weight: 400;
  line-height: 1.72;
  color: color-mix(in srgb, var(--text-primary) 70%, var(--text-secondary) 30%);
  max-width: 690px;
  text-shadow:
    0 0 10px rgba(255,255,255,0.92),
    0 0 22px rgba(255,255,255,0.7),
    0 0 44px rgba(255,255,255,0.45);
  opacity: 0;
  animation: landing-rise 1600ms cubic-bezier(.2, .7, .2, 1) 1300ms forwards;
}
/* ── Footer / CTA ── */
.landing-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 28px;
  position: relative;
  margin-top: clamp(32px, 5.4vh, 78px);
  margin-bottom: clamp(36px, 5.4vh, 64px);
  opacity: 0;
  animation: landing-rise 1400ms cubic-bezier(.2, .7, .2, 1) 1900ms forwards;
}
.landing-cta {
  font-family: var(--font-mono);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 4.6px;
  color: #f8fbff;
  background: linear-gradient(90deg, #1f2a44 0%, #111827 100%);
  border: 1px solid #2c3b5d;
  padding: 19px 38px 19px 43px;
  border-radius: 999px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  position: relative;
  z-index: 6;
  opacity: 1;
  transition: background 200ms ease, color 200ms ease, border-color 200ms ease, transform 200ms ease, box-shadow 200ms ease;
  box-shadow:
    0 18px 38px -14px rgba(17, 24, 39, 0.5),
    0 0 0 9px rgba(232, 248, 252, 0.74);
}
.landing-cta:hover {
  background: linear-gradient(90deg, #2563eb 0%, #1d4ed8 100%);
  border-color: #2563eb;
  transform: translateY(-1px);
  box-shadow:
    0 18px 38px -12px rgba(37,99,235,0.42),
    0 0 0 8px rgba(232, 248, 252, 0.8);
}
.landing-cta:active { transform: translateY(0); }
.landing-cta-arrow {
  font-size: 16px;
  letter-spacing: 0;
  transition: transform 200ms ease;
}
.landing-cta:hover .landing-cta-arrow { transform: translateX(3px); }

.landing-foot-note {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 1px;
  color: color-mix(in srgb, var(--text-dim) 64%, var(--text-secondary) 36%);
  margin: 0;
  position: relative;
  z-index: 2;
}

/* ── Exit transition (clicked BEGIN) ── */
/* Reduced-motion fallback path: fade the entire landing immediately. */
.landing.is-leaving {
  opacity: 0;
  transition: opacity 360ms ease;
}

/* Cinematic path: only the chrome and decorative layers fade. The
   particle cloud keeps animating and morphs into the silhouette,
   which is then handed off to welcome as the bridge. The whole page
   below the hero fades too — we're leaving the marketing surface, not
   sliding off it. */
.landing.is-handing-off .landing-frame,
.landing.is-handing-off .landing-trajectory,
.landing.is-handing-off .landing-trajectory-marker,
.landing.is-handing-off .landing-ambient,
.landing.is-handing-off .landing-nav,
.landing.is-handing-off .lp-section,
.landing.is-handing-off .lp-footer {
  opacity: 0;
  transition: opacity 360ms ease;
}

/* ── Animations ── */
@keyframes landing-rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes landing-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes landing-fade-soft {
  from { opacity: 0; }
  to   { opacity: 0.72; }
}
@keyframes landing-trace {
  to { stroke-dashoffset: 0; }
}
@keyframes landing-dot-pulse {
  0% {
    opacity: 0;
    transform: scale(0.2);
  }
  26% {
    opacity: 0.48;
  }
  100% {
    opacity: 0;
    transform: scale(1.28);
  }
}
@keyframes landing-dot-breathe {
  0%, 100% { opacity: 0.94; transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 0.66; transform: translate(-50%, -50%) scale(1.08); }
}
@keyframes landing-orb-breathe {
  0%, 100% { transform: translate(-50%, -50%) scale(0.985); opacity: 0.24; }
  50%      { transform: translate(-50%, -50%) scale(1.02); opacity: 0.36; }
}
@keyframes landing-diffuse-a {
  0% {
    transform: translate3d(-9%, -3%, 0) rotate(-1.4deg) scale(1.05);
  }
  50% {
    transform: translate3d(3%, 4.6%, 0) rotate(0.6deg) scale(1.11);
  }
  100% {
    transform: translate3d(10%, -2.2%, 0) rotate(1.6deg) scale(1.06);
  }
}
@keyframes landing-diffuse-b {
  0% {
    transform: translate3d(8%, -6%, 0) rotate(1.2deg) scale(1.1);
  }
  50% {
    transform: translate3d(-6%, 3.6%, 0) rotate(-0.8deg) scale(1.03);
  }
  100% {
    transform: translate3d(-9%, 6.4%, 0) rotate(-1.8deg) scale(1.12);
  }
}
@keyframes landing-ring-orbit {
  0% { transform: translate(-50%, -50%) rotate(0deg) scale(0.96); opacity: 0.55; }
  50% { transform: translate(-50%, -50%) rotate(180deg) scale(1.04); opacity: 0.84; }
  100% { transform: translate(-50%, -50%) rotate(360deg) scale(0.96); opacity: 0.55; }
}
@keyframes landing-glint-orbit {
  0% {
    transform: translate(calc(-50% + 11px), calc(-50% - 2px));
    opacity: 0.22;
  }
  12% { opacity: 0.96; }
  50% {
    transform: translate(calc(-50% - 11px), calc(-50% + 2px));
    opacity: 0.34;
  }
  62% { opacity: 0.88; }
  100% {
    transform: translate(calc(-50% + 11px), calc(-50% - 2px));
    opacity: 0.22;
  }
}

/* ═══════════ LANDING PAGE — POST-HERO SECTIONS ═══════════
   The hero above is the cinematic. Below it: a conventional
   marketing-page cadence (social proof → value prop → FAQ → CTA →
   footer). Shared rhythm: each section uses `.lp-shell` for max-width
   centering, `.lp-eyebrow` for a small uppercase label, and
   `.lp-section-title` for the section h2. Everything stays on the
   technical-instrument typography of the rest of the app
   (JetBrains Mono, uppercase + tracking for headers). */

.lp-shell {
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 clamp(20px, 4vw, 52px);
  width: 100%;
}

.lp-section {
  position: relative;
  padding: clamp(96px, 14vh, 176px) 0;
  /* Anchor jumps from the sticky nav must clear it. */
  scroll-margin-top: var(--lp-nav-h, 60px);
}

.lp-eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 2.4px;
  color: var(--text-dim);
  margin: 0 0 16px;
  text-transform: uppercase;
}

.lp-section-head {
  max-width: 720px;
  margin: 0 auto clamp(40px, 6vh, 64px);
  text-align: center;
}

.lp-section-title {
  font-family: var(--font-mono);
  font-size: clamp(26px, 3.2vw, 38px);
  font-weight: 600;
  line-height: 1.18;
  letter-spacing: -0.4px;
  color: var(--text-primary);
  margin: 0 0 18px;
}

.lp-section-sub {
  font-family: var(--font-mono);
  font-size: clamp(13.5px, 1.05vw, 16px);
  line-height: 1.66;
  color: var(--text-secondary);
  margin: 0 auto;
  max-width: 560px;
  text-wrap: balance;
}

/* The "You're not average" and "A longer life" beats carry the
   heaviest visuals on the page (bell-curve callout + threat horizon),
   so they get extra vertical room — the visuals breathe and the
   sections feel like distinct chapters rather than stacked panels. */
.lp-section.lp-problem,
.lp-section.lp-what {
  padding: clamp(128px, 19vh, 232px) 0;
}

/* Middle "Add years back" beat sits on a soft cyan-tinted atmosphere
   — picks up the same accent-glow color story used by the hero and
   the final CTA, and thematically matches the cyan +5 YEARS extension
   in the visual itself. Breaks up three consecutive flat-white sections
   without going gray-and-bland like the FAQ band below. */
.lp-section.lp-what {
  background:
    radial-gradient(ellipse 80% 70% at 70% 78%, rgba(8, 145, 178, 0.035), transparent 70%),
    linear-gradient(180deg, #fdfeff 0%, #f7fbfc 100%);
}

/* ── Top nav ── */
/* Sits over the hero on load (transparent) and gets a subtle backdrop
   once the user scrolls. For a first-pass we keep it sticky with a
   light translucent panel — simple, works on every viewport. */
.landing-nav {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: clamp(16px, 3vw, 32px);
  padding: 14px clamp(20px, 4vw, 40px);
  background: rgba(255, 255, 255, 0.78);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
}
.landing-nav-mark {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 4px;
  color: var(--accent);
  text-decoration: none;
}
.landing-nav-links {
  display: flex;
  gap: clamp(14px, 2.4vw, 28px);
  margin-left: auto;
}
.landing-nav-links a {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-secondary);
  text-decoration: none;
  transition: color 160ms ease;
}
.landing-nav-links a:hover { color: var(--text-primary); }
.landing-nav-cta {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.8px;
  color: #f8fbff;
  background: linear-gradient(90deg, #1f2a44 0%, #111827 100%);
  border: 1px solid #2c3b5d;
  padding: 9px 18px;
  border-radius: 999px;
  cursor: pointer;
  transition: background 180ms ease, transform 180ms ease;
}
.landing-nav-cta:hover {
  background: linear-gradient(90deg, #2563eb 0%, #1d4ed8 100%);
  transform: translateY(-1px);
}

/* Scroll cue — small affordance under the hero CTA hinting that
   there's more to read. Bounces gently to draw the eye. */
.landing-scrollcue {
  position: absolute;
  bottom: 24px;
  left: 0;
  right: 0;
  margin-inline: auto;
  width: max-content;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 2.4px;
  color: var(--text-dim);
  text-decoration: none;
  opacity: 0;
  animation: landing-rise 1200ms cubic-bezier(.2, .7, .2, 1) 3600ms forwards;
}
.landing-scrollcue-arrow {
  font-size: 14px;
  letter-spacing: 0;
  animation: landing-scrollcue-bounce 2400ms ease-in-out infinite;
}
@keyframes landing-scrollcue-bounce {
  0%, 100% { transform: translateY(0); opacity: 0.6; }
  50%      { transform: translateY(4px); opacity: 1; }
}

/* ── Social proof / "grounded in" ──
   Now embedded inside the Meet Aescle beat (see `.lp-mortality-
   aescle-logos`) rather than living as its own section after the
   pinned stage. The logo grid and individual mark styles below are
   reused inside that block — only the outer container, eyebrow
   spacing, and tonal weighting change. */
.lp-social-grid {
  /* 5 logos with variable widths wrap to 4+1 in a flex row. Use grid so
     the wrap is always balanced — 5 across when there's room, 3+2 below
     880px (the second row centers), 2+2+1 below 560px, 1-per-row mobile. */
  display: grid;
  grid-template-columns: repeat(5, auto);
  justify-content: center;
  align-items: center;
  column-gap: clamp(28px, 4.5vw, 60px);
  row-gap: clamp(28px, 4vh, 44px);
}
@media (max-width: 880px) {
  .lp-social-grid { grid-template-columns: repeat(3, auto); }
  .lp-social-mark:nth-child(4) { grid-column: 1 / span 2; justify-self: end; padding-right: 6px; }
  .lp-social-mark:nth-child(5) { grid-column: 3 / span 1; justify-self: start; padding-left: 6px; }
}
@media (max-width: 480px) {
  .lp-social-grid { grid-template-columns: 1fr; }
  .lp-social-mark:nth-child(n) { grid-column: auto; justify-self: center; padding: 0; }
}
/* Each logo carries its own brand color baked into the SVG (CDC navy,
   NEJM black, Lancet red banner, JAMA black, AHA red). A subtle
   opacity lift on hover keeps the row visually unified at rest while
   letting individual marks feel touchable. */
.lp-social-mark {
  display: inline-flex;
  align-items: center;
  opacity: 0.9;
  transition: opacity 200ms ease, transform 200ms ease;
}
.lp-social-mark:hover {
  opacity: 1;
  transform: translateY(-1px);
}
.lp-social-logo {
  display: block;
  height: 28px;
  width: auto;
}

/* ── "You're not average" problem section ──
   Sits between the mortality scrollytelling and the threat-horizon
   payoff. Section head is centered; body is a two-column grid
   (copy left, single bell-curve viz right). The viz is the
   centerpiece — the motion *is* the argument.

   Animation split:
     • One-shot on first reveal (0.0 – 2.1s):
         curve strokes in, area fills, then both dim to ghost gray;
         the mean ghost-dot fades in at the apex. After this the
         chart holds in its dimmed end state forever.
     • Infinite loop (8s cycle, starts ~1.0s after reveal):
         pin pops at the apex with a halo flash → dwells briefly →
         slides smoothly along the curve into the right tail
         (CSS offset-path, not stepped samples) → callout card
         fades in well above the chart with three "your" markers →
         hold while the halo pulses → callout + pin fade out → short
         pause → restart at the apex.

   Only the pin + callout loop. The bell curve only draws once.

   All animations are gated on `.lp-problem-viz.is-in-view` (added
   by the shared reveal observer). Class+class specificity beats
   the base `[data-reveal].is-in-view` rule. */

.lp-problem-grid {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: center;
}

.lp-problem-copy {
  display: flex;
  flex-direction: column;
}

/* Title lives inline with the visual (left column of the grid)
   instead of a centered header above — breaks the centered-title
   cadence shared with "A longer life" and "The 360° view". */
.lp-problem-title {
  margin: 0 0 16px;
  max-width: 11ch;
  text-wrap: balance;
}

.lp-problem-tagline {
  font-family: var(--font-mono);
  font-size: clamp(15px, 1.2vw, 18px);
  line-height: 1.5;
  font-weight: 500;
  color: var(--text-primary);
  margin: 0 0 18px;
  max-width: 480px;
}

.lp-problem-eyebrow {
  color: var(--accent);
  letter-spacing: 3.2px;
  margin: 0 0 20px;
}

.lp-problem-lede {
  font-family: var(--font-mono);
  font-size: clamp(15px, 1.2vw, 18px);
  line-height: 1.6;
  font-weight: 500;
  color: var(--text-primary);
  margin: 0 0 18px;
  max-width: 480px;
}

.lp-problem-sub {
  font-family: var(--font-mono);
  font-size: clamp(13.5px, 1.05vw, 16px);
  line-height: 1.68;
  color: var(--text-secondary);
  margin: 0;
  max-width: 480px;
}

.lp-problem-viz {
  width: 100%;
  max-width: 540px;
  margin-left: auto;
}

.lp-problem-curve-svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

/* ── Static layers ── */

.lp-problem-axis {
  stroke: var(--border);
  stroke-width: 1;
}

/* ── Initial (pre-reveal) state ── */

.lp-problem-area { opacity: 0; }
.lp-problem-curve-line {
  stroke: #94a3b8;
  stroke-width: 1.6;
  stroke-dasharray: 720;
  stroke-dashoffset: 720;
}
/* Pin rides a CSS motion path — a cubic Bezier sweeping from the
   apex (240, 80) down into the right tail at the landing point
   (395, 207). offset-rotate:0deg keeps the dot upright; offset
   positions the group's children (halo + dot) at the path point,
   so the inner circles can keep their own transforms (the halo
   pulse uses transform:scale). */
.lp-problem-pin {
  offset-path: path('M 240 80 C 287 80, 335 168, 395 207');
  offset-distance: 0%;
  offset-rotate: 0deg;
  opacity: 0;
}
.lp-problem-pin-dot {
  fill: var(--accent);
}
.lp-problem-pin-halo {
  fill: var(--accent);
  fill-opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
}

/* ── Flag — soft callout floating above the chart ──
   Higher up than the chart so it never occludes the curve, with
   no visible border and big rounded corners. The stem is a
   curved Bezier instead of a straight line. */
.lp-problem-flag {
  opacity: 0;
  transform: translateY(6px);
}
.lp-problem-flag-stem {
  stroke: var(--accent);
  stroke-width: 1.1;
  stroke-linecap: round;
  opacity: 0.55;
}
.lp-problem-flag-card {
  fill: var(--bg-surface);
  stroke: none;
}
.lp-problem-flag-eyebrow {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  fill: var(--accent);
  opacity: 0.85;
}
.lp-problem-flag-age {
  font-family: var(--font-mono);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.4px;
  fill: var(--text-primary);
}
.lp-problem-flag-row {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2px;
  fill: var(--text-secondary);
}

/* ── Animations (fire when .is-in-view is added) ──
   The curve, area, and ghost dot run ONE-SHOT forwards animations
   on reveal — they hold their end state forever. Only the pin,
   halo, and callout loop. */

.lp-problem-viz.is-in-view .lp-problem-curve-line {
  animation:
    lp-problem-curve-draw 0.9s cubic-bezier(.4, 0, .2, 1) forwards,
    lp-problem-curve-dim 0.6s 1.5s ease-out forwards;
}
@keyframes lp-problem-curve-draw {
  from { stroke-dashoffset: 720; }
  to   { stroke-dashoffset: 0; }
}
@keyframes lp-problem-curve-dim {
  from { opacity: 1;    stroke-width: 1.6; }
  to   { opacity: 0.42; stroke-width: 1.25; }
}

.lp-problem-viz.is-in-view .lp-problem-area {
  animation:
    lp-problem-area-in 0.9s 0.2s ease-out forwards,
    lp-problem-area-dim 0.6s 1.5s ease-out forwards;
}
@keyframes lp-problem-area-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes lp-problem-area-dim {
  from { opacity: 1;    }
  to   { opacity: 0.45; }
}

/* ── Looping layer (8s cycle, kicks in after the curve settles) ──
   The pin appears at the apex, slides smoothly along the motion
   path to the tail, the callout fades in above the chart, all
   hold, then everything fades out for a short pause before the
   next cycle. */

.lp-problem-viz.is-in-view .lp-problem-pin {
  animation: lp-problem-pin 8s cubic-bezier(.4, 0, .2, 1) 1.6s infinite;
}
@keyframes lp-problem-pin {
  0%    { offset-distance: 0%;   opacity: 0; }
  3%    { offset-distance: 0%;   opacity: 1; }
  10%   { offset-distance: 0%;   opacity: 1; }   /* dwell at apex */
  32%   { offset-distance: 100%; opacity: 1; }   /* slide to tail */
  88%   { offset-distance: 100%; opacity: 1; }   /* hold */
  95%   { offset-distance: 100%; opacity: 0; }
  100%  { offset-distance: 0%;   opacity: 0; }
}

/* Halo: pop at the apex when the pin appears, slow pulse around
   the settled pin during the hold phase. */
.lp-problem-viz.is-in-view .lp-problem-pin-halo {
  animation: lp-problem-halo 8s ease-out 1.6s infinite;
}
@keyframes lp-problem-halo {
  0%    { fill-opacity: 0;    transform: scale(0.6); }
  3%    { fill-opacity: 0;    transform: scale(0.6); }
  7%    { fill-opacity: 0.45; transform: scale(1.4); }   /* pop at apex */
  12%   { fill-opacity: 0;    transform: scale(2.0); }
  35%   { fill-opacity: 0;    transform: scale(0.85); }
  45%   { fill-opacity: 0.32; transform: scale(0.85); }  /* pulse begin */
  68%   { fill-opacity: 0;    transform: scale(2.1);  }
  68.5% { fill-opacity: 0.28; transform: scale(0.85); }
  88%   { fill-opacity: 0;    transform: scale(2.1);  }
  100%  { fill-opacity: 0;    transform: scale(0.6);  }
}

.lp-problem-viz.is-in-view .lp-problem-flag {
  animation: lp-problem-flag 8s ease-out 1.6s infinite;
}
@keyframes lp-problem-flag {
  0%    { opacity: 0; transform: translateY(6px); }
  35%   { opacity: 0; transform: translateY(6px); }
  42%   { opacity: 1; transform: translateY(0); }
  88%   { opacity: 1; transform: translateY(0); }
  94%   { opacity: 0; transform: translateY(6px); }
  100%  { opacity: 0; transform: translateY(6px); }
}

@media (max-width: 820px) {
  .lp-problem-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(24px, 5vw, 40px);
  }
  .lp-problem-copy { order: 1; }
  .lp-problem-viz {
    order: 2;
    max-width: 480px;
    margin: 0 auto;
  }
  .lp-problem-lede,
  .lp-problem-sub {
    max-width: none;
  }
}

/* ── What it does (threat horizon) ──
   A horizontal life-line with three named threats hanging above,
   each carrying a cause-of-death icon and a severity color (red →
   amber → yellow). Loops on a 9s cycle, starting once the stage
   scrolls into view. Each cycle:
     1. Threats sit at full severity, line ends at AGE 82
     2. Each threat MITIGATES (halo retracts, group dims) in a
        left → right cascade — all three settle before the line moves
     3. Cyan extension draws as one smooth left → right stroke
     4. Default pin dims to AGE 82; AGE 87 + halo + "+ 5 YEARS" settle
     5. Long hold on payoff (~3s)
     6. Single-frame synchronous snap, then replay
   Critical: every element uses animation-delay 0 so they share the
   same loop boundary. The per-threat stagger is baked into the
   keyframe percentages (three separate keyframe rules, one per
   threat) — using animation-delay for stagger would make each
   threat reset at its own time, producing the sequential rewind
   we don't want. Lives directly on the page (no card chrome). */
.lp-horizon-stage {
  margin: 0 auto;
  max-width: 1080px;
  padding: clamp(8px, 1.5vh, 20px) clamp(16px, 2.5vw, 32px);
}
.lp-horizon {
  display: block;
  width: 100%;
  height: auto;
  max-height: 280px;
  font-family: var(--font-mono);
}

/* ─ Threats ─
   Each threat is a rounded vertical bar rising from the life-line.
   Bar height (set inline in the SVG) encodes mortality share — the
   second axis that breaks the otherwise-horizontal timeline.
   Mitigation scales the bar's Y down toward the baseline (the
   visual story of "threat reduced"); the icon and label stay parked
   above as a memory of the original altitude before fading with the
   parent group. Severity color (red→amber→yellow) is carried by
   --threat-color and inherited by every sub-element. */
.lp-horizon-threat {
  opacity: 1;
  --threat-color: var(--amber);
}
.lp-horizon-threat--high { --threat-color: var(--red); }
.lp-horizon-threat--mid  { --threat-color: var(--amber); }
.lp-horizon-threat--low  { --threat-color: var(--yellow); }

.lp-horizon-threat-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.8px;
  fill: var(--text-dim);
  text-transform: uppercase;
}

/* Magnitude bar: solid severity-color column rooted at the timeline.
   Mitigation retracts via scaleY() with bottom-anchored origin so the
   bar shrinks down into the baseline. */
.lp-horizon-threat-bar {
  fill: var(--threat-color);
  opacity: 0.7;
  transform-box: fill-box;
  transform-origin: bottom;
  pointer-events: none;
}

.lp-horizon-threat-icon {
  color: var(--threat-color);
  opacity: 0.85;
}

/* Icon + label ride along with the bar's top during mitigation. The
   bar scales down via scaleY (origin: bottom); the head translates
   down by the matching delta so the spacing label→icon→bar-top is
   preserved at every frame. */
.lp-horizon-threat-head {
  transform: translateY(0);
}

/* ─ Life-line ─
   Extension starts retracted (stroke-dashoffset: 300); when the stage
   is in view, the keyframe animation draws it in and `forwards` keeps
   it drawn. Same one-shot pattern for callout + pins. */
.lp-horizon-base {
  stroke: var(--text-secondary);
  stroke-width: 1.6;
  stroke-linecap: round;
  opacity: 0.55;
}
.lp-horizon-ext {
  stroke: #0891b2;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-dasharray: 220;
  stroke-dashoffset: 220;
  filter: drop-shadow(0 1px 4px rgba(8, 145, 178, 0.28));
}

/* ─ +5 YEARS payoff callout above the extension ─ */
.lp-horizon-extras {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 3px;
  fill: #0891b2;
  opacity: 0;
  filter: drop-shadow(0 1px 3px rgba(8, 145, 178, 0.25));
}

/* ─ Endpoint pins ─ */
.lp-horizon-pin-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 2.2px;
  fill: var(--text-dim);
  text-transform: uppercase;
}
.lp-horizon-pin--default .lp-horizon-pin-dot {
  fill: var(--text-secondary);
}
.lp-horizon-pin--default {
  opacity: 1;
}
.lp-horizon-pin--reach .lp-horizon-pin-dot {
  fill: #0891b2;
}
.lp-horizon-pin--reach .lp-horizon-pin-label {
  fill: #0891b2;
}
.lp-horizon-pin--reach {
  opacity: 0;
}

/* Halo behind the reachable dot — soft cyan pulse, infinite. It
   only becomes visible once the parent reach-pin fades in. */
.lp-horizon-pin-halo {
  fill: #0891b2;
  opacity: 0.22;
  transform-origin: center;
  transform-box: fill-box;
  animation: lp-horizon-halo-pulse 2.8s infinite ease-in-out;
}
@keyframes lp-horizon-halo-pulse {
  0%, 100% { transform: scale(1);    opacity: 0.22; }
  50%      { transform: scale(1.35); opacity: 0.08; }
}

.lp-horizon-stage[data-reveal] { transition-delay: 140ms; }

/* All animations: 9s cycle, infinite, animation-delay 0 → shared
   loop boundary. The three threats use three separate keyframe rules
   that fire at different keyframe percentages — that's the cascade,
   without offsetting the loop boundary. The threat-mitigate window
   (8% of cycle per threat, 4% stagger between them) compresses the
   cascade into 18% of the cycle: 10% → 28%. Then ext, pins, callout
   resolve by ~55%; the remaining 45% (~4s) holds the payoff. */
.lp-horizon-stage.is-in-view .lp-horizon-threats > .lp-horizon-threat:nth-child(1) {
  animation: lp-horizon-threat-mitigate-0 9s infinite ease-in-out;
}
.lp-horizon-stage.is-in-view .lp-horizon-threats > .lp-horizon-threat:nth-child(1) .lp-horizon-threat-bar {
  animation: lp-horizon-threat-bar-0 9s infinite ease-in-out;
}
.lp-horizon-stage.is-in-view .lp-horizon-threats > .lp-horizon-threat:nth-child(1) .lp-horizon-threat-head {
  animation: lp-horizon-threat-head-0 9s infinite ease-in-out;
}
.lp-horizon-stage.is-in-view .lp-horizon-threats > .lp-horizon-threat:nth-child(2) {
  animation: lp-horizon-threat-mitigate-1 9s infinite ease-in-out;
}
.lp-horizon-stage.is-in-view .lp-horizon-threats > .lp-horizon-threat:nth-child(2) .lp-horizon-threat-bar {
  animation: lp-horizon-threat-bar-1 9s infinite ease-in-out;
}
.lp-horizon-stage.is-in-view .lp-horizon-threats > .lp-horizon-threat:nth-child(2) .lp-horizon-threat-head {
  animation: lp-horizon-threat-head-1 9s infinite ease-in-out;
}
.lp-horizon-stage.is-in-view .lp-horizon-threats > .lp-horizon-threat:nth-child(3) {
  animation: lp-horizon-threat-mitigate-2 9s infinite ease-in-out;
}
.lp-horizon-stage.is-in-view .lp-horizon-threats > .lp-horizon-threat:nth-child(3) .lp-horizon-threat-bar {
  animation: lp-horizon-threat-bar-2 9s infinite ease-in-out;
}
.lp-horizon-stage.is-in-view .lp-horizon-threats > .lp-horizon-threat:nth-child(3) .lp-horizon-threat-head {
  animation: lp-horizon-threat-head-2 9s infinite ease-in-out;
}
.lp-horizon-stage.is-in-view .lp-horizon-ext {
  animation: lp-horizon-ext-draw 9s infinite ease-in-out;
}
.lp-horizon-stage.is-in-view .lp-horizon-pin--default {
  animation: lp-horizon-pin-default 9s infinite ease-in-out;
}
.lp-horizon-stage.is-in-view .lp-horizon-pin--reach {
  animation: lp-horizon-pin-reach 9s infinite ease-in-out;
}
.lp-horizon-stage.is-in-view .lp-horizon-extras {
  animation: lp-horizon-extras-show 9s infinite ease-in-out;
}

/* Three clean phases, no interleaving:
     A. Threats mitigate cascade left → right (10% → 38%, ~2.5s).
        Each threat transitions over 16% of the cycle (~1.44s) with a
        6% stagger between them. Mitigation amount is heterogeneous —
        cardiac shrinks most (highly modifiable via lipids / BP /
        fitness); cancer shrinks least (some unmodifiable baseline);
        metabolic sits between.
     B. Brief pause, then the extension draws as one smooth left →
        right stroke (42% → 58%, ~1.4s).
     C. AGE 82 fades, AGE 87 + "+5 YEARS" settle in (58% → 70%); then
        a long hold (~2.7s) on the payoff frame.
   Two layers fire per threat:
     - mitigate-N: group opacity eases into the resolved level
     - bar-N:     bar scaleY retracts toward the baseline (the dramatic
                  visual reduction)
   Every keyframe holds its end value to 100% so the loop snap is clean. */
@keyframes lp-horizon-threat-mitigate-0 {
  0%, 10%   { opacity: 1; }
  26%, 100% { opacity: 0.42; }
}
@keyframes lp-horizon-threat-bar-0 {
  0%, 10%   { transform: scaleY(1); }
  26%, 100% { transform: scaleY(0.18); }
}
/* head-N translateY = (1 - bar scaleY) × base bar height — slides the
   icon + label down to track the bar's new top edge. */
@keyframes lp-horizon-threat-head-0 {
  0%, 10%   { transform: translateY(0); }
  26%, 100% { transform: translateY(74px); }
}
@keyframes lp-horizon-threat-mitigate-1 {
  0%, 16%   { opacity: 1; }
  32%, 100% { opacity: 0.78; }
}
@keyframes lp-horizon-threat-bar-1 {
  0%, 16%   { transform: scaleY(1); }
  32%, 100% { transform: scaleY(0.65); }
}
@keyframes lp-horizon-threat-head-1 {
  0%, 16%   { transform: translateY(0); }
  32%, 100% { transform: translateY(22px); }
}
@keyframes lp-horizon-threat-mitigate-2 {
  0%, 22%   { opacity: 1; }
  38%, 100% { opacity: 0.58; }
}
@keyframes lp-horizon-threat-bar-2 {
  0%, 22%   { transform: scaleY(1); }
  38%, 100% { transform: scaleY(0.4); }
}
@keyframes lp-horizon-threat-head-2 {
  0%, 22%   { transform: translateY(0); }
  38%, 100% { transform: translateY(17px); }
}

/* Extension: one smooth left → right draw, starting after the last
   threat has settled (38%) plus a short breath (to 42%), then drawing
   in 16% (~1.4s). No interleaving with the mitigation cascade. */
@keyframes lp-horizon-ext-draw {
  0%, 42%   { stroke-dashoffset: 220; }
  58%, 100% { stroke-dashoffset: 0; }
}
@keyframes lp-horizon-pin-default {
  0%, 52%   { opacity: 1; }
  62%, 100% { opacity: 0.25; }
}
@keyframes lp-horizon-pin-reach {
  0%, 56%   { opacity: 0; }
  66%, 100% { opacity: 1; }
}
@keyframes lp-horizon-extras-show {
  0%, 60%   { opacity: 0; }
  68%, 100% { opacity: 1; }
}

/* Reduced motion: skip the cascade, render the final payoff frame
   directly. is-in-view is added immediately in that mode anyway. */
@media (prefers-reduced-motion: reduce) {
  .lp-horizon-stage.is-in-view .lp-horizon-threat,
  .lp-horizon-stage.is-in-view .lp-horizon-threat-bar,
  .lp-horizon-stage.is-in-view .lp-horizon-threat-head,
  .lp-horizon-stage.is-in-view .lp-horizon-ext,
  .lp-horizon-stage.is-in-view .lp-horizon-extras,
  .lp-horizon-stage.is-in-view .lp-horizon-pin--default,
  .lp-horizon-stage.is-in-view .lp-horizon-pin--reach,
  .lp-horizon-pin-halo {
    animation: none;
  }
  .lp-horizon-stage.is-in-view .lp-horizon-threat--high { opacity: 0.42; }
  .lp-horizon-stage.is-in-view .lp-horizon-threat--mid  { opacity: 0.78; }
  .lp-horizon-stage.is-in-view .lp-horizon-threat--low  { opacity: 0.58; }
  .lp-horizon-stage.is-in-view .lp-horizon-threat--high .lp-horizon-threat-bar { transform: scaleY(0.18); }
  .lp-horizon-stage.is-in-view .lp-horizon-threat--mid  .lp-horizon-threat-bar { transform: scaleY(0.65); }
  .lp-horizon-stage.is-in-view .lp-horizon-threat--low  .lp-horizon-threat-bar { transform: scaleY(0.4); }
  .lp-horizon-stage.is-in-view .lp-horizon-threat--high .lp-horizon-threat-head { transform: translateY(74px); }
  .lp-horizon-stage.is-in-view .lp-horizon-threat--mid  .lp-horizon-threat-head { transform: translateY(22px); }
  .lp-horizon-stage.is-in-view .lp-horizon-threat--low  .lp-horizon-threat-head { transform: translateY(17px); }
  .lp-horizon-stage.is-in-view .lp-horizon-ext { stroke-dashoffset: 0; }
  .lp-horizon-stage.is-in-view .lp-horizon-extras { opacity: 1; }
  .lp-horizon-stage.is-in-view .lp-horizon-pin--default { opacity: 0.25; }
  .lp-horizon-stage.is-in-view .lp-horizon-pin--reach { opacity: 1; }
}

/* ── Value prop / framework diagram ── */
/* Three stages, left → right:
     - Inputs column: 5 chips (demographics, history, genome, vitals, bloodwork)
     - Aescle engine: a hexagonal processing module that turns inputs into
       a profile. Hex = chip / computational unit; round dots are states.
     - Mortality profile: a framed region containing the threats / targets /
       actions triangle, with the "+ MORE YEARS" payoff chip at its centroid.
       The frame + top-center tab badge are what unify the triangle as a
       single named unit.
   Edges:
     - reveal (engine → profile): horizontal arrow into the frame
     - lever  (targets → threats): the causal claim — targets move threats
     - act    (actions → targets): the user's lever
     - frame  (threats → actions): closes the triangle into a directed cycle
   Per-card hover lights a discrete beat of the diagram via :has().
   Card colors are uniform (brand cyan) — only the diagram carries the
   semantic warm/cool color language. */
.lp-howflow-wrap {
  margin: 0 auto clamp(56px, 8vh, 88px);
  max-width: 1040px;
  padding: clamp(20px, 3vh, 36px) clamp(8px, 2vw, 24px);
  background:
    radial-gradient(ellipse 70% 80% at 50% 50%, rgba(255,255,255,0.6), transparent 80%),
    linear-gradient(180deg, var(--bg-panel) 0%, var(--bg-surface) 100%);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.lp-howflow {
  display: block;
  width: 100%;
  height: auto;
  max-height: 760px;
  font-family: var(--font-mono);
}
/* Input pills (horizontal row across the top of the diagram). */
.lp-flow-input rect {
  fill: var(--bg-panel);
  stroke: var(--border);
  stroke-width: 1;
}
.lp-flow-input text {
  font-size: 11px;
  font-weight: 500;
  fill: var(--text-primary);
  letter-spacing: 0.3px;
}

/* Convergent inputs → profile edges (idle: gradient stroke) */
.lp-flow-converge path { transition: stroke 240ms ease; }

/* Aescle engine — a hexagonal processing module and the visual focal
   point of the diagram. Hex shape reads as a chip / computational unit;
   the inner hex + vertex registration ticks sell the "machined
   instrument" feel. Label is the focal text; the pillar glyphs (bars /
   dots / lines) sit beneath as a quiet sub-row. */
.lp-engine-hex-outer {
  fill: #ffffff;
  stroke: #0891b2;
  stroke-width: 1.7;
  stroke-linejoin: round;
  transition: stroke-width 240ms ease, filter 240ms ease;
}
.lp-engine-hex-inner {
  fill: none;
  stroke: #0891b2;
  stroke-opacity: 0.34;
  stroke-width: 1.1;
  stroke-linejoin: round;
}
.lp-engine-ticks circle {
  fill: #0891b2;
}
.lp-engine-line1,
.lp-engine-line2 {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 2px;
  fill: var(--accent);
}
.lp-engine-glyphs {
  color: #0891b2;
  opacity: 0.55;
  pointer-events: none;
}
.lp-engine-glyph rect,
.lp-engine-glyph circle {
  fill: currentColor;
}
.lp-engine-glyph line {
  stroke: currentColor;
  stroke-width: 1.1;
  stroke-linecap: round;
}

/* Engine "processing" motion — keyframes only; applied in the card 02
   block below so the engine animates only while step 2 is the active
   beat (the moment the engine is the focal point). Three coordinated
   cues: dashed energy flow along the inner hex, cycling pillar
   glyphs, and a cyan glow on the outer hex. */
@keyframes lp-engine-hex-flow {
  to { stroke-dashoffset: -8; }
}
@keyframes lp-engine-glyph-pulse {
  0%, 70%, 100% { opacity: 0.4; }
  20%, 45%      { opacity: 1; }
}
@keyframes lp-engine-hex-glow {
  0%, 100% { filter: drop-shadow(0 0 0 rgba(8, 145, 178, 0)); }
  50%      { filter: drop-shadow(0 0 14px rgba(8, 145, 178, 0.45)); }
}

/* Mortality profile frame — wraps the triangle composition as a single
   named region. Subtle surface fill + a 1px border define the container;
   the pill badge is bisected by the frame's top edge so it reads as a
   panel title (fieldset-legend style). */
.lp-profile-frame {
  fill: rgba(8, 145, 178, 0.025);
  stroke: var(--border);
  stroke-width: 1;
  transition: stroke 240ms ease, fill 240ms ease, stroke-opacity 240ms ease;
}

/* Shared pill styling — used for both the YOUR HEALTH DATA pill above
   the inputs column and the MORTALITY PROFILE pill bisecting the
   profile frame's top edge. Identical dimensions / typography give the
   two columns matching headers. */
.lp-flow-pill-bg {
  fill: var(--bg-panel);
  stroke: var(--border);
  stroke-width: 1;
  transition: fill 240ms ease, stroke 240ms ease, stroke-opacity 240ms ease, filter 240ms ease;
}
.lp-flow-pill-text {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 2px;
  fill: var(--text-secondary);
  text-transform: uppercase;
  transition: fill 240ms ease, font-weight 240ms ease;
}

/* All four edges (reveal + three triangle sides) share at-rest styling.
   Bumped from 55% to 72% text-dim so the triangle closes visibly at
   rest, especially the long horizontal base which used to vanish into
   the page. */
.lp-flow-edge {
  stroke: color-mix(in srgb, var(--text-dim) 72%, transparent);
  stroke-width: 1.4;
  fill: none;
  transition: stroke 240ms ease, stroke-width 240ms ease, stroke-opacity 240ms ease;
}

/* Engine → profile reveal edge inherits the cyan tint of the incoming
   converge trunk so the engine's input and output read as the same
   flow. Matches the converge arrowhead's #0891b2 @ 0.7. */
.lp-flow-edge[data-edge="reveal"] {
  stroke: #0891b2;
  stroke-opacity: 0.7;
}

/* Vertex visuals — halo + dot + uppercase label + tiny example list */
.lp-flow-vertex .v-halo {
  fill: transparent;
  transition: r 280ms ease, fill 280ms ease;
}
.lp-flow-vertex[data-vertex="threats"] .v-halo { fill: rgba(220, 38, 38, 0.08); }
.lp-flow-vertex[data-vertex="targets"] .v-halo { fill: rgba(217, 119, 6, 0.10); }
.lp-flow-vertex[data-vertex="actions"] .v-halo { fill: rgba(22, 163, 74, 0.10); }
.lp-flow-vertex .v-dot { transition: r 280ms ease; }
.lp-flow-vertex .v-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 2px;
  fill: var(--text-secondary);
  text-transform: uppercase;
  transition: fill 200ms ease;
}
/* Payoff chip — the profile's output. Anchored at the triangle's
   centroid so the cycle (threats / targets / actions) runs around it.
   Text is green-700 against a faint green-tinted fill so the chip
   reads as the diagram's prize without losing legibility. The cycle
   itself is the centerpiece; the chip is the result, not the hero. */
.lp-payoff-pill {
  fill: rgba(22, 163, 74, 0.12);
  stroke: #16a34a;
  stroke-opacity: 0.7;
  stroke-width: 1.4;
  transition: stroke-opacity 240ms ease, fill 240ms ease, filter 240ms ease;
}
.lp-payoff-text {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.8px;
  fill: #15803d;
  transition: fill 240ms ease;
}

/* Opacity transition for every role-tagged element so dim-others is
   smooth across all card-hover states. */
.lp-howflow [data-role] { transition: opacity 280ms ease; }

/* Reverse-hover hit-zones: transparent SVG rects above the three
   diagram regions that share the same data-card attribute as the
   value-prop cards below. Hovering either side fires the same :has()
   rules — diagram-hover and card-hover are now interchangeable. */
.lp-flow-hitzone {
  fill: transparent;
  pointer-events: all;
}

/* ─── Card-driven hover (CSS-only via :has()) ─────────────────────── */
/* Each beat lights a discrete moment of the diagram, triggered by
   either hovering the matching value-prop card OR hovering the
   matching diagram region (the hit-zones above). All selectors target
   [data-card="X"]:hover unqualified, so both sources work:
     01 (SEE):  inputs converge into the engine. Nothing past the engine.
     02 (FIND): engine → profile. The whole profile lights as the
                "thing delivered"; payoff stays dim — that's card 03's job.
     03 (ACT):  the profile's internal cycle. Engine + reveal drop out,
                triangle perimeter animates, all three vertices light,
                payoff chip pulses + brightens. */
@keyframes lp-flow-dash { to { stroke-dashoffset: -22; } }
/* Soft outer halo only — never darkens the pill's fill, so the
   "+ MORE YEARS" text keeps its contrast against the chip during the
   pulse. (Earlier version brightened the fill on hover, which sank
   the green text into a greener background.) */
@keyframes lp-payoff-glow {
  0%, 100% { filter: drop-shadow(0 0 4px rgba(22, 163, 74, 0.18)); }
  50%      { filter: drop-shadow(0 0 16px rgba(22, 163, 74, 0.55)); }
}

/* ── Dim rules (per card) ── */
/* Card 01: engine is the destination — everything downstream of it
   fades back. The user reads "inputs become a single engine input." */
.lp-value[data-active="01"] [data-role="reveal"],
.lp-value[data-active="01"] [data-role="profile-frame"],
.lp-value[data-active="01"] [data-role="lever"],
.lp-value[data-active="01"] [data-role="act"],
.lp-value[data-active="01"] [data-role="frame"],
.lp-value[data-active="01"] [data-role="threats"],
.lp-value[data-active="01"] [data-role="targets"],
.lp-value[data-active="01"] [data-role="actions"],
.lp-value[data-active="01"] [data-role="payoff"] {
  opacity: 0.22;
}

/* Card 02: the reveal beat. The engine emits the profile and threats
   is the headline output (ranked list of what's actually shortening
   your life). Frame + threats vertex + triangle edges stay lit;
   targets / actions vertices dim with the payoff so threats is the
   single focal point inside the cycle. Card 03 brings the targets /
   actions vertices back as the levers and the things that move them. */
.lp-value[data-active="02"] [data-role="input"],
.lp-value[data-active="02"] [data-role="converge"],
.lp-value[data-active="02"] [data-role="payoff"],
.lp-value[data-active="02"] [data-role="targets"],
.lp-value[data-active="02"] [data-role="actions"] {
  opacity: 0.22;
}
/* Triangle edges also recede on card 02 — the profile container and
   the threats vertex inside it are the focal points; the cycle's
   internal wiring is card 03's job. Slightly less dim than the
   vertices (0.32 vs 0.22) so the triangle still reads as a
   continuous boundary around threats rather than three orphaned
   stubs anchored to the lit vertex. */
.lp-value[data-active="02"] [data-edge="lever"],
.lp-value[data-active="02"] [data-edge="act"],
.lp-value[data-active="02"] [data-edge="frame"] {
  opacity: 0.32;
}

/* Card 03: the profile's internal cycle. Engine + reveal drop out —
   by this beat the user is inside the profile, not entering it. */
.lp-value[data-active="03"] [data-role="input"],
.lp-value[data-active="03"] [data-role="converge"],
.lp-value[data-active="03"] [data-role="engine"],
.lp-value[data-active="03"] [data-role="reveal"] {
  opacity: 0.22;
}

/* ── Card 01 (SEE YOUR RISKS) ── */
/* Inputs animate into the engine. No reveal edge, no triangle lighting. */
.lp-value[data-active="01"] .lp-flow-converge path {
  stroke: #0891b2;
  stroke-opacity: 0.85;
  stroke-dasharray: 5 7;
  animation: lp-flow-dash 1.6s linear infinite;
}

/* ── Card 02 (FIND WHAT MOVES) ── */
/* Reveal edge animates from engine → profile, and the profile frame
   border picks up a cyan tint to mark it as the destination. No
   single-vertex emphasis — the whole profile reads as the thing
   being delivered. */
.lp-value[data-active="02"] [data-edge="reveal"] {
  stroke: #0891b2;
  stroke-width: 1.7;
  stroke-dasharray: 5 6;
  animation: lp-flow-dash 1.4s linear infinite;
}
.lp-value[data-active="02"] .lp-profile-frame {
  stroke: #0891b2;
  stroke-opacity: 0.55;
  fill: rgba(8, 145, 178, 0.045);
}
/* LONGEVITY PROFILE legend reads as the region's name on this beat —
   cyan-tinted pill, deeper text color, soft outer glow. The frame
   above tints the region; the pill labels what just arrived. */
.lp-value[data-active="02"] .lp-profile-tab .lp-flow-pill-bg {
  fill: var(--bg-panel);
  stroke: #0891b2;
  stroke-opacity: 0.75;
  filter: drop-shadow(0 0 8px rgba(8, 145, 178, 0.25));
}
.lp-value[data-active="02"] .lp-profile-tab .lp-flow-pill-text {
  fill: #0e7490;
  font-weight: 700;
}
/* Engine reads as "actively processing" while this beat is active.
   Three coordinated motions: dashed energy flow along the inner hex,
   cycling pillar glyphs, and a cyan glow on the outer hex. Engine is
   otherwise still at rest. */
.lp-value[data-active="02"] .lp-engine-hex-inner {
  stroke-dasharray: 4 4;
  stroke-opacity: 0.55;
  animation: lp-engine-hex-flow 0.7s linear infinite;
}
.lp-value[data-active="02"] .lp-engine-glyph {
  animation: lp-engine-glyph-pulse 3.6s ease-in-out infinite;
}
.lp-value[data-active="02"] .lp-engine-glyph[data-pillar="actuarial"] { animation-delay: 0s; }
.lp-value[data-active="02"] .lp-engine-glyph[data-pillar="cohort"]    { animation-delay: 1.2s; }
.lp-value[data-active="02"] .lp-engine-glyph[data-pillar="clinical"]  { animation-delay: 2.4s; }
.lp-value[data-active="02"] .lp-engine-hex-outer {
  animation: lp-engine-hex-glow 2.4s ease-in-out infinite;
}
/* Threats is the engine's headline output: the ranked list of what
   actually shortens your life. Emphasize the threats vertex alone so
   step 2 has a discrete focal point (parallel to card 03's actions
   emphasis). Targets/actions stay at rest — their moment is card 03. */
.lp-value[data-active="02"] [data-vertex="threats"] .v-dot { r: 10; }
.lp-value[data-active="02"] [data-vertex="threats"] .v-halo { r: 28; }
.lp-value[data-active="02"] [data-vertex="threats"] .v-label { fill: var(--text-primary); }

/* ── Card 03 (TAKE ACTION) ── */
/* All three triangle edges animate as a single clockwise perimeter loop:
   lever (targets→threats), frame (threats→actions), act (actions→targets)
   are each drawn in clockwise direction, so a synced dash animation reads
   as dashes chasing around the triangle. All three vertices register;
   the payoff chip pulses as the cycle's downstream output, and its text
   darkens for extra punch. */
.lp-value[data-active="03"] [data-edge="lever"],
.lp-value[data-active="03"] [data-edge="act"],
.lp-value[data-active="03"] [data-edge="frame"] {
  stroke: #16a34a;
  stroke-width: 1.7;
  stroke-dasharray: 5 6;
  animation: lp-flow-dash 1.4s linear infinite;
}
.lp-value[data-active="03"] [data-vertex="actions"] .v-dot { r: 10; }
.lp-value[data-active="03"] [data-vertex="actions"] .v-halo { r: 28; }
.lp-value[data-active="03"] [data-vertex="actions"] .v-label { fill: var(--text-primary); }
.lp-value[data-active="03"] [data-vertex="targets"] .v-halo { r: 26; }
.lp-value[data-active="03"] [data-vertex="targets"] .v-label { fill: var(--text-primary); }
.lp-value[data-active="03"] [data-vertex="threats"] .v-halo { r: 26; }
.lp-value[data-active="03"] [data-vertex="threats"] .v-label { fill: var(--text-primary); }
.lp-value[data-active="03"] .lp-payoff-pill {
  stroke-opacity: 1;
  stroke-width: 1.7;
  animation: lp-payoff-glow 1.6s ease-in-out infinite;
}
.lp-value[data-active="03"] .lp-payoff-text {
  fill: #14532d;
}


/* ── Diagram + accordion (side-by-side coordinated view) ──
   The diagram sits on the left; an accordion-style step list sits on
   the right. Each row in the aside collapses to its title; the active
   row expands inline to reveal body copy. Hovering or clicking a row
   — or any of the diagram's three hit-zones — sets the active beat
   on the parent section, which CSS uses to (a) highlight the matching
   diagram zone and (b) expand the matching row's body.

   The expand/collapse uses the `grid-template-rows: 0fr → 1fr` trick
   so the body height animates from its natural rendered height — no
   measured max-height, no JS height syncing. */
/* The diagram is vertically oriented (480×660 viewBox), so it fits in
   a narrow column without compressing labels. The section uses the
   default 1080px shell; the diagram column is capped at 460px while the
   accordion claims the remaining width via 1fr — wider rows balance the
   diagram's vertical mass and give the body copy room to breathe. */
.lp-howflow-pair {
  display: grid;
  grid-template-columns: minmax(0, 460px) minmax(360px, 1fr);
  gap: clamp(24px, 4vw, 64px);
  align-items: center;
  justify-content: center;
}
/* Inside the pair, the diagram wrapper drops its standalone-block
   styling: no bottom margin (the section handles spacing), no
   max-width (the grid column controls width), tighter padding. */
.lp-howflow-pair > .lp-howflow-wrap {
  margin: 0;
  max-width: none;
  padding: clamp(16px, 2vh, 28px) clamp(8px, 1.5vw, 20px);
}

/* Aside (accordion column) */
.lp-howflow-aside {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.lp-howflow-row {
  border-top: 1px solid var(--border);
  transition: border-color 240ms ease;
}
.lp-howflow-row:last-child {
  border-bottom: 1px solid var(--border);
}
.lp-howflow-row-head {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  background: transparent;
  border: 0;
  padding: 40px 4px;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-mono);
  color: var(--text-dim);
  transition:
    color 220ms ease,
    padding-top 360ms cubic-bezier(.2, .7, .2, 1),
    padding-bottom 360ms cubic-bezier(.2, .7, .2, 1);
}
.lp-howflow-row-head:hover { color: var(--text-secondary); }
.lp-howflow-row-head:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--accent) 60%, transparent);
  outline-offset: 2px;
  border-radius: 6px;
}
.lp-howflow-row-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--bg-panel);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  flex-shrink: 0;
  transition:
    background 240ms ease,
    border-color 240ms ease,
    color 240ms ease,
    box-shadow 240ms ease;
}
.lp-howflow-row-title {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
}
.lp-howflow-row-body-wrap {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 360ms cubic-bezier(.2, .7, .2, 1);
}
.lp-howflow-row-body-wrap > .lp-howflow-row-body {
  overflow: hidden;
  margin: 0;
  padding: 0 4px 0 46px; /* align body under title (28 num + 14 gap + 4 head pad) */
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.66;
  color: var(--text-secondary);
  opacity: 0;
  transform: translateY(-4px);
  transition:
    opacity 320ms ease 80ms,
    transform 320ms ease 80ms,
    padding-bottom 360ms cubic-bezier(.2, .7, .2, 1);
}

/* Active row: number badge fills cyan, title brightens, body expands. */
.lp-value[data-active="01"] .lp-howflow-row[data-card="01"],
.lp-value[data-active="02"] .lp-howflow-row[data-card="02"],
.lp-value[data-active="03"] .lp-howflow-row[data-card="03"] {
  border-top-color: color-mix(in srgb, var(--accent) 35%, var(--border));
}
.lp-value[data-active="01"] .lp-howflow-row[data-card="01"] + .lp-howflow-row,
.lp-value[data-active="02"] .lp-howflow-row[data-card="02"] + .lp-howflow-row,
.lp-value[data-active="03"] .lp-howflow-row[data-card="03"] + .lp-howflow-row {
  border-top-color: color-mix(in srgb, var(--accent) 35%, var(--border));
}
.lp-value[data-active="01"] .lp-howflow-row[data-card="01"] .lp-howflow-row-head,
.lp-value[data-active="02"] .lp-howflow-row[data-card="02"] .lp-howflow-row-head,
.lp-value[data-active="03"] .lp-howflow-row[data-card="03"] .lp-howflow-row-head {
  color: var(--accent);
}
.lp-value[data-active="01"] .lp-howflow-row[data-card="01"] .lp-howflow-row-num,
.lp-value[data-active="02"] .lp-howflow-row[data-card="02"] .lp-howflow-row-num,
.lp-value[data-active="03"] .lp-howflow-row[data-card="03"] .lp-howflow-row-num {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 14%, transparent);
}
.lp-value[data-active="01"] .lp-howflow-row[data-card="01"] .lp-howflow-row-body-wrap,
.lp-value[data-active="02"] .lp-howflow-row[data-card="02"] .lp-howflow-row-body-wrap,
.lp-value[data-active="03"] .lp-howflow-row[data-card="03"] .lp-howflow-row-body-wrap {
  grid-template-rows: 1fr;
}
.lp-value[data-active="01"] .lp-howflow-row[data-card="01"] .lp-howflow-row-head,
.lp-value[data-active="02"] .lp-howflow-row[data-card="02"] .lp-howflow-row-head,
.lp-value[data-active="03"] .lp-howflow-row[data-card="03"] .lp-howflow-row-head {
  padding-top: 64px;
  padding-bottom: 12px;
}
.lp-value[data-active="01"] .lp-howflow-row[data-card="01"] .lp-howflow-row-body,
.lp-value[data-active="02"] .lp-howflow-row[data-card="02"] .lp-howflow-row-body,
.lp-value[data-active="03"] .lp-howflow-row[data-card="03"] .lp-howflow-row-body {
  opacity: 1;
  transform: translateY(0);
  padding-bottom: 64px;
}

/* The last row has its own bottom border (no row-after to recolor),
   so highlight it directly when active. */
.lp-value[data-active="03"] .lp-howflow-row[data-card="03"]:last-child {
  border-bottom-color: color-mix(in srgb, var(--accent) 35%, var(--border));
}

/* Below the side-by-side breakpoint, stack the pair vertically. The
   diagram has its own ≤760px display:none rule (it needs lateral
   room to read), so under 760px only the accordion shows — and the
   active row's body keeps the story complete. */
@media (max-width: 1080px) {
  .lp-howflow-pair {
    grid-template-columns: 1fr;
    align-items: stretch;
  }
}
/* ── Mortality beat ──
   The somber existential one-two punch right after the hero. Two
   stacked beats, each occupying nearly its own viewport so the
   scroll-in lands as a dramatic page turn rather than a paragraph
   transition. Background is a deep slate band that tonally breaks
   from the bright page above; a soft accent vignette warms the
   second beat's region without breaking the band's continuity. */
/* Threshold-driven scrolljack. The section is transparent (no bg, no
   border) so the stage's dark backdrop is the *only* dark surface —
   that lets it fade in/out at the section's edges, dissolving into
   the page bg instead of having a hard section edge cross the
   viewport. */
.lp-mortality {
  position: relative;
  height: 200vh;
  padding: 0;
  /* Intentionally NO `overflow: hidden` here: that would make this
     section a CSS scroll container, which scopes the inner sticky
     stage to the section (which doesn't actually scroll) instead of
     the `.landing` scroll container outside. The stage handles its
     own clipping.

     Height is 200vh = 100vh sticky stage + 100vh of section travel.
     Sticky releases at p = (200-100)/200 = 0.50. Inside the section,
     JS toggles three state classes as scroll crosses each threshold:

         vh after pin → state class    → trigger p
              0vh     → .is-beat-1     = 0.000   (fires at pin)
             20vh     → .is-beat-2     = 0.100
             40vh     → .is-aescle     = 0.200
            100vh     → (release)      = 0.500

     Beat 1 fires the moment the stage pins so the user never lands
     on a stretch of empty dark. Threshold gaps are tight (20vh each)
     because pacing is enforced by FORCED DWELL — JS clamps scrollTop
     at each state's exit threshold until that beat's fade-in plays
     through (see `DWELL_MS` in landing.js: 1500ms / 2800ms / 3000ms
     for states 1 / 2 / 3). Fast scroll or trackpad inertia hits the
     clamp and stops. Sequential state advance means beats can't be
     skipped. Total forced pause from pin to release: ~7.3s, after
     which the user is freed and the section unpins on the next
     scroll. The fades themselves are CSS-timed (see state-class
     rules below), NOT scroll-scrubbed. Forward-only: scrolling back
     up inside the section does NOT un-activate a beat; state resets
     only when the section drops back below the fold. The backdrop
     fades in scroll-driven during entry (before pin), so the dark
     band rises from the page bg before any beat lands. The top nav
     fades back in (CSS-timed, ~800ms) on a transition-delay so it
     lands as the final cascade step rather than racing it. */
}
.lp-mortality-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
}
/* The dark backdrop is its own layer so JS can drive its opacity
   independently of the text on top. Fades in over the first 5% of
   scroll progress through the section and out over the last ~10%,
   so the dark band materializes from the page bg and dissolves back
   into it — no visible section edge. The vignette gradients sit on
   top of the solid dark fill in the same element. */
.lp-mortality-backdrop {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 70% 55% at 50% 60%, rgba(8,145,178,0.16), transparent 72%),
    radial-gradient(ellipse 90% 60% at 50% 8%, rgba(255,255,255,0.04), transparent 70%),
    #0b1422;
  opacity: 0;
  will-change: opacity;
  pointer-events: none;
}
/* Beats stack on top of each other inside the stage. The beat
   container is always fully visible — title and body fade in via the
   threshold-state rules below as a one-two punch (title first, body
   follows on a 350ms delay), and they fade out together when the
   state class is removed. */
.lp-mortality-beat {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(80px, 12vh, 140px) clamp(20px, 5vw, 60px);
  text-align: center;
}
.lp-mortality-inner {
  max-width: 820px;
  width: 100%;
}
.lp-mortality-title {
  font-family: var(--font-mono);
  font-size: clamp(32px, 4.6vw, 64px);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -0.6px;
  color: #f4f7fb;
  margin: 0 0 clamp(20px, 3vh, 36px);
  text-shadow:
    0 4px 36px rgba(0,0,0,0.45),
    0 0 64px rgba(8,145,178,0.08);
  opacity: 0;
  will-change: opacity;
}
.lp-mortality-body {
  font-family: var(--font-mono);
  font-size: clamp(13px, 1.1vw, 17px);
  line-height: 1.7;
  color: rgba(220, 230, 245, 0.7);
  margin: 0 auto;
  max-width: 640px;
  opacity: 0;
  will-change: opacity;
}
.lp-mortality-beat--meantime .lp-mortality-title {
  font-size: clamp(27px, 3.9vw, 54px);
  letter-spacing: -0.5px;
}
.lp-mortality-beat--meantime .lp-mortality-title-accent {
  color: color-mix(in srgb, var(--accent) 60%, #f4f7fb 40%);
  text-shadow: 0 0 80px rgba(8,145,178,0.22);
}
.lp-mortality-beat--meantime .lp-mortality-body {
  color: rgba(220, 230, 245, 0.82);
}

/* ── Meet Aescle beat ──
   The third beat in the sticky mortality stage. Crossfades against
   the dark backdrop (which is fading out at the same time) — so the
   user sees the dark band dissolve into the bright cyan glow as one
   continuous opacity motion, with no vertical scrolling. Sits on
   top of the page's normal light background; the glow layer beneath
   adds the soft cyan that previously lived in `.lp-intro`. */
.lp-mortality-aescle-glow {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 60% 70% at 50% 50%, rgba(8,145,178,0.10), transparent 70%),
    radial-gradient(ellipse 90% 30% at 50% 0%, rgba(8,145,178,0.05), transparent 70%);
  opacity: 0;
  will-change: opacity;
  pointer-events: none;
}
.lp-mortality-aescle-inner {
  max-width: 880px;
  width: 100%;
  text-align: center;
  /* No opacity here — children fade in sequentially via the
     `.is-aescle` cascade (mark+eyebrow → title +500ms → logos
     +1100ms) so each piece arrives on its own beat. */
}
.lp-mortality-aescle-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: clamp(64px, 6vw, 84px);
  height: clamp(64px, 6vw, 84px);
  color: var(--accent);
  margin: 0 0 clamp(16px, 2.2vh, 24px);
  filter: drop-shadow(0 6px 24px rgba(8,145,178,0.28));
  opacity: 0;
  will-change: opacity;
}
.lp-mortality-aescle-mark svg { width: 100%; height: 100%; }
.lp-mortality-aescle-eyebrow {
  font-size: 14px;
  color: var(--accent);
  letter-spacing: 3.6px;
  margin: 0 0 clamp(32px, 4.5vh, 44px);
  opacity: 0;
  will-change: opacity;
}
.lp-mortality-aescle-title {
  font-family: var(--font-mono);
  font-size: clamp(24px, 3vw, 40px);
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: 0;
  color: var(--text-primary);
  margin: 0 0 clamp(36px, 5.5vh, 60px);
  opacity: 0;
  will-change: opacity;
}
/* Research band, embedded as the final cascade step of Meet Aescle.
   Lives inside the pinned stage rather than as a sibling section, so
   the credentials feel structurally part of Aescle's identity — and
   the gap between the pinned beat and the next content section
   disappears. The eyebrow/grid sit on the same cyan glow as the title
   above. Container fades in as the final step of the `.is-aescle`
   cascade — see the threshold-state rules below. */
.lp-mortality-aescle-logos {
  margin-top: clamp(80px, 11vh, 128px);
  opacity: 0;
  will-change: opacity;
}
.lp-mortality-aescle-logos-eyebrow {
  text-align: center;
  margin: 0 0 clamp(20px, 3vh, 32px);
}

/* Threshold-driven beat activation. Each beat's elements default to
   opacity:0 (declared above with their layout rules); the state class
   on `.lp-mortality` flips them to 1, and the transition does the
   work. Transition-delay is set ONLY on the fade-in side, so removing
   the state class fades elements out together with no stagger.

   Timing model (default transition: 1000ms ease):
     - Beat 1 lands as soon as the stage pins (no fade-in delay), with
       its body 1000ms behind the title.
     - Each subsequent state waits 1300ms after activation before its
       first element starts fading in: that's enough for the prior
       beat's 1000ms fade-out plus a 300ms empty-dark pause.
     - Inside the aescle cascade, mark+eyebrow → title → logos each
       wait for the previous element to finish + a ~500ms breath, so
       the three pieces arrive on three distinct beats rather than as
       overlapping fades.
     - Total aescle cascade time after activation: ~2.5s + 1s last
       fade = ~3.5s for logos; nav restoration tops it off at ~4.7s.
       With 400vh of scroll budget after the threshold, even moderate
       scrollers see the full cascade before sticky releases.

   See the comment on `.lp-mortality` above for the threshold model
   and the policies (skip-ahead + forward-only). */
.lp-mortality-title,
.lp-mortality-body,
.lp-mortality-aescle-glow,
.lp-mortality-aescle-mark,
.lp-mortality-aescle-eyebrow,
.lp-mortality-aescle-title,
.lp-mortality-aescle-logos {
  transition: opacity 1000ms ease;
}
.lp-mortality.is-beat-1 .lp-mortality-beat--certain .lp-mortality-title { opacity: 1; }
.lp-mortality.is-beat-1 .lp-mortality-beat--certain .lp-mortality-body  { opacity: 1; transition-delay: 1000ms; }
.lp-mortality.is-beat-2 .lp-mortality-beat--meantime .lp-mortality-title { opacity: 1; transition-delay: 1300ms; }
.lp-mortality.is-beat-2 .lp-mortality-beat--meantime .lp-mortality-body  { opacity: 1; transition-delay: 2300ms; }
.lp-mortality.is-aescle .lp-mortality-aescle-glow,
.lp-mortality.is-aescle .lp-mortality-aescle-mark,
.lp-mortality.is-aescle .lp-mortality-aescle-eyebrow { opacity: 1; transition: opacity 500ms ease; transition-delay: 0ms; }
.lp-mortality.is-aescle .lp-mortality-aescle-title { opacity: 1; transition-delay: 1000ms; }
.lp-mortality.is-aescle .lp-mortality-aescle-logos { opacity: 1; transition-delay: 2500ms; }

/* When the cascade hands off to aescle, snap beat 2's title + body out
   instantly instead of fading them over 1s. The scroll-input lock leaks
   during the state 2→3 handoff (macOS scroll-latching defeats
   preventDefault for the rest of the gesture, and the dwell clamp yanks
   scrollTop back over the next few frames). With nothing to jostle on
   screen, the leaked scroll is invisible — the aescle backdrop + glow
   handle the gradual visual transition on their own. */
.lp-mortality.is-aescle .lp-mortality-beat--meantime .lp-mortality-title,
.lp-mortality.is-aescle .lp-mortality-beat--meantime .lp-mortality-body {
  transition: none;
  opacity: 0;
}

/* Nav restoration during aescle. The nav is held at opacity:0 by JS
   while the mortality stage is pinned and beats 1+2 are running.
   When `.is-aescle` activates, JS adds `.is-restored` to the nav and
   sets opacity:1 inline — this CSS rule provides the transition AND
   a deliberate delay so the nav fades in *after* the research logos
   have fully landed (logos start at 2500ms with a 1000ms fade, so
   they're done at 3500ms; nav waits an extra 400ms breath beyond
   that). Lands strictly last in the cascade — first the figure and
   eyebrow, then the title, then "Grounded in research from" + logos,
   THEN the nav returns. Class is removed when the section resets. */
.landing-nav.is-restored {
  transition: opacity 800ms ease 3900ms;
}
/* Skip path: when the user opts out of the cinematic, the cascade
   doesn't play out — there's nothing for the nav to wait behind. JS
   adds `.is-restored-instant` alongside `.is-restored` so the nav
   returns as part of the same crossfade as the aescle reveal itself
   (~260ms), not 4s later. */
.landing-nav.is-restored.is-restored-instant {
  transition: opacity 260ms ease;
}

/* Scroll affordance pinned at the bottom of the mortality stage. JS
   schedules it to surface `HINT_DELAY_AFTER_DWELL_MS` after each
   beat's dwell ends (see landing.js). It's a quiet fallback: if the
   user tried to scroll during the hard-locked dwell window, got
   blocked, and gave up — they'll see this nudge once the lock
   releases. Most users scroll on again on their own well before it
   appears, never seeing it. Bounces gently to draw the eye without
   stealing attention from the beat content above. */
.lp-mortality-scroll-hint {
  position: absolute;
  bottom: clamp(28px, 4vh, 56px);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 2.2px;
  text-transform: uppercase;
  /* Default color is light, for the dark beat 1 / beat 2 backdrops.
     During aescle the section flips to a light backdrop and we swap
     to a dim cyan (see the `.is-aescle` rule below). */
  color: rgba(220, 230, 245, 0.55);
  opacity: 0;
  pointer-events: none;
  /* Default (hidden) transition governs the fade-OUT when the class is
     removed — slow so dismissing the hint feels gentle, not snapped
     away. The fade-IN is overridden to a snappier 280ms on the visible
     rule below, so it lands inside the JS lead window. */
  transition: opacity 900ms ease, color 600ms ease;
  z-index: 4;
}
.lp-mortality-scroll-hint-arrow {
  width: 20px;
  height: 20px;
  animation: lp-mortality-scroll-hint-bounce 1.8s ease-in-out infinite;
}
.lp-mortality.is-hint-visible .lp-mortality-scroll-hint {
  opacity: 1;
  transition: opacity 280ms ease-out, color 600ms ease;
}
.lp-mortality.is-aescle .lp-mortality-scroll-hint {
  color: color-mix(in srgb, var(--accent) 70%, transparent);
}
@keyframes lp-mortality-scroll-hint-bounce {
  0%, 100% { transform: translateY(-2px); opacity: 0.55; }
  50%      { transform: translateY(3px);  opacity: 1; }
}

/* Skip-intro affordance. Surfaces in the top-right of the pinned stage
   for repeat viewers — users who reached the aescle reveal once and
   then scrolled back to the top. The state machine sets
   `.has-completed-once` on `.lp-mortality` the first time state 3
   activates and never clears it for the rest of the session. Visible
   only on beats 1 + 2; aescle is itself the natural exit. Click lands
   the user inside aescle with the cinematic disarmed — see
   `skipToReveal()` in landing.js.

   Sits in the corner the top nav vacates during the dark beats (nav
   fades to 0 while beats 1/2 are active), so there's no visual clash.
   Cyan border + cool-gray text — a quiet "you can leave" cue rather
   than something that competes with the title for the eye. */
.lp-mortality-skip {
  position: absolute;
  top: clamp(20px, 3vh, 32px);
  right: clamp(20px, 3vw, 36px);
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 7px 14px;
  border: 1px solid rgba(220, 230, 245, 0.18);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.03);
  color: rgba(220, 230, 245, 0.6);
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition: opacity 320ms ease, transform 320ms ease, color 180ms ease, border-color 180ms ease, background 180ms ease;
  z-index: 5;
}
.lp-mortality-skip:hover {
  color: rgba(240, 247, 255, 0.95);
  border-color: rgba(220, 230, 245, 0.42);
  background: rgba(255, 255, 255, 0.06);
}
.lp-mortality-skip:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--accent) 70%, transparent);
  outline-offset: 3px;
}
.lp-mortality-skip-x {
  font-size: 13px;
  line-height: 1;
  letter-spacing: 0;
  opacity: 0.85;
}
/* Only repeat viewers, and only on the dark beats. Aescle is already
   the cinematic's exit — no skip needed there. */
.lp-mortality.has-completed-once.is-beat-1 .lp-mortality-skip,
.lp-mortality.has-completed-once.is-beat-2 .lp-mortality-skip {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
  /* Brief delay so the button doesn't crash into the beat title's
     fade-in. Title is fully visible at ~1000ms post-state-activation
     for beat 1; landing at 600ms means the skip arrives just before
     the body copy, as a distinct beat. */
  transition-delay: 600ms;
}

/* `.is-skipped` overrides the normal aescle cascade so the reveal
   appears in a quick simultaneous fade instead of the 3s staggered
   choreography. Repeat viewers who hit Skip want the destination,
   not the journey. */
.lp-mortality.is-skipped .lp-mortality-aescle-glow,
.lp-mortality.is-skipped .lp-mortality-aescle-mark,
.lp-mortality.is-skipped .lp-mortality-aescle-eyebrow,
.lp-mortality.is-skipped .lp-mortality-aescle-title,
.lp-mortality.is-skipped .lp-mortality-aescle-logos {
  opacity: 1;
  transition: opacity 260ms ease;
  transition-delay: 0ms;
}
/* Snap both dark beats out instantly during a skip — the normal
   beat-2 snap-out rule above only covers `meantime`; in the skip
   path we may be coming from beat 1, so cover `certain` too. */
.lp-mortality.is-skipped .lp-mortality-beat--certain .lp-mortality-title,
.lp-mortality.is-skipped .lp-mortality-beat--certain .lp-mortality-body,
.lp-mortality.is-skipped .lp-mortality-beat--meantime .lp-mortality-title,
.lp-mortality.is-skipped .lp-mortality-beat--meantime .lp-mortality-body {
  transition: opacity 200ms ease;
  opacity: 0;
}
/* Smooth-fade the dark backdrop out during a skip rather than
   snapping it. The inline opacity is set to "0" by skipToReveal();
   without this transition, the dark band would cut instantly while
   aescle's cyan glow is still fading in (260ms) — a visible flash.
   Matching durations gives a clean crossfade between dark band and
   aescle reveal. */
.lp-mortality.is-skipped .lp-mortality-backdrop {
  transition: opacity 260ms ease;
}
/* Hide the skip button itself the moment a skip is in progress so it
   doesn't linger over the revealing aescle card. */
.lp-mortality.is-skipped .lp-mortality-skip {
  opacity: 0;
  pointer-events: none;
  transition-delay: 0ms;
}

/* ── Meet Aescle (intro) ── DEPRECATED.
   Meet Aescle now lives inside the mortality sticky stage as a
   third beat (see `.lp-mortality-beat--aescle` / `.lp-mortality-
   aescle-*` above). These classes are kept temporarily in case
   anything else still references them, but no element on the
   landing page uses them. Safe to delete in a follow-up. */
.lp-intro {
  position: relative;
  min-height: clamp(460px, 64vh, 680px);
  display: flex;
  align-items: center;
  padding: clamp(72px, 11vh, 128px) 0 clamp(28px, 4vh, 48px);
  overflow: hidden;
}
/* Research band reads as a credential footer to "Meet Aescle" — pull
   it tight against the intro instead of standing alone. */
.lp-intro + .lp-social {
  padding-top: 0;
  padding-bottom: clamp(72px, 11vh, 128px);
  border-bottom: 1px solid var(--border-subtle);
}
.lp-intro-glow {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 60% 70% at 50% 50%, rgba(8,145,178,0.10), transparent 70%),
    radial-gradient(ellipse 90% 30% at 50% 0%, rgba(8,145,178,0.05), transparent 70%);
  pointer-events: none;
}
.lp-intro-shell {
  position: relative;
}
.lp-intro-card {
  max-width: 880px;
  margin: 0 auto;
  text-align: center;
}
.lp-intro-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: clamp(64px, 6vw, 84px);
  height: clamp(64px, 6vw, 84px);
  color: var(--accent);
  margin: 0 0 clamp(20px, 3vh, 32px);
  filter: drop-shadow(0 6px 24px rgba(8,145,178,0.28));
}
.lp-intro-mark svg { width: 100%; height: 100%; }
.lp-intro-eyebrow {
  color: var(--accent);
  letter-spacing: 3.2px;
  margin-bottom: 22px;
}
.lp-intro-title {
  font-family: var(--font-mono);
  font-size: clamp(38px, 5.4vw, 72px);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -0.7px;
  color: var(--text-primary);
  margin: 0 0 clamp(20px, 3vh, 32px);
}
.lp-intro-sub {
  font-family: var(--font-mono);
  font-size: clamp(15px, 1.25vw, 19px);
  line-height: 1.72;
  color: var(--text-secondary);
  margin: 0 auto;
  max-width: 640px;
}

/* ── Reveal-on-scroll ──
   Any element with `data-reveal` starts shifted down + faded, then
   slides into place when an IntersectionObserver toggles
   `is-in-view`. Different element classes get slightly different
   distances / durations so headers feel lighter than full cards. */
[data-reveal] {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity 900ms cubic-bezier(.2, .7, .2, 1),
    transform 900ms cubic-bezier(.2, .7, .2, 1);
  will-change: opacity, transform;
}
[data-reveal].is-in-view {
  opacity: 1;
  transform: translateY(0);
}
/* Per-element overrides. Because the base `[data-reveal].is-in-view`
   rule has the same specificity as `.<class>[data-reveal]`, anything
   that overrides `transform` for the start state must also re-declare
   the end state — otherwise CSS source order pins the element to the
   shifted position even after `.is-in-view` is added. */
.lp-howflow-pair[data-reveal] { transition-delay: 140ms; }

.lp-intro-card[data-reveal] {
  transform: translateY(36px);
  transition-duration: 1100ms;
}
.lp-intro-card[data-reveal].is-in-view { transform: translateY(0); }

/* ── FAQ ── */
.lp-faq {
  background: var(--bg-surface);
  border-top: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-subtle);
  padding: clamp(96px, 14vh, 180px) 0;
}
.lp-faq-shell {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) minmax(0, 1.8fr);
  gap: clamp(32px, 5vw, 80px);
  align-items: start;
}
.lp-faq-head {
  align-self: start;
}
.lp-faq-head .lp-section-title {
  margin: 0 0 12px;
  font-size: clamp(24px, 2.6vw, 32px);
}
.lp-faq-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--border);
}
.lp-faq-list li {
  border-bottom: 1px solid var(--border);
}
.lp-faq-row {
  /* native <details> — no JS needed for collapse/expand */
}
.lp-faq-row > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 20px 0;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-mono);
  font-size: 15px;
  font-weight: 500;
  color: var(--text-primary);
  user-select: none;
}
.lp-faq-row > summary::-webkit-details-marker { display: none; }
.lp-faq-row > summary:hover { color: var(--accent); }
.lp-faq-chev {
  font-family: var(--font-mono);
  font-size: 18px;
  font-weight: 400;
  color: var(--text-dim);
  transition: transform 180ms ease;
  line-height: 1;
}
.lp-faq-row[open] .lp-faq-chev { transform: rotate(45deg); color: var(--accent); }
.lp-faq-body {
  font-family: var(--font-mono);
  font-size: 14px;
  line-height: 1.7;
  color: var(--text-secondary);
  padding: 0 0 22px;
  max-width: 60ch;
}

/* ── Final CTA card ── */
.lp-final {
  padding: clamp(56px, 9vh, 100px) 0;
}
.lp-final-card {
  /* Light closer: the page runs white end-to-end, so the final beat reads
     as the same atmosphere — fog clearing toward a horizon — rather than
     a foreign dark slab. The cyan glow rising from below carries the
     trajectory motif into the CTA itself. */
  position: relative;
  max-width: 760px;
  margin: 0 auto;
  border-radius: var(--radius-lg);
  padding: clamp(72px, 11vh, 112px) clamp(28px, 5vw, 64px) clamp(64px, 9vh, 96px);
  text-align: center;
  background:
    radial-gradient(ellipse 70% 60% at 50% 118%, rgba(8,145,178,0.16), transparent 68%),
    radial-gradient(ellipse 90% 70% at 50% -10%, rgba(8,145,178,0.04), transparent 60%),
    linear-gradient(180deg, #ffffff 0%, #f7fafb 100%);
  border: 1px solid color-mix(in srgb, var(--accent) 18%, var(--border));
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.9) inset,
    0 30px 80px -36px rgba(8, 47, 73, 0.18),
    0 0 0 1px rgba(255, 255, 255, 0.4) inset;
  overflow: hidden;
  isolation: isolate;
}
/* Tick marks at the four card corners — quiet instrument-panel detail. */
.lp-final-card::after {
  content: "";
  position: absolute;
  inset: 14px;
  border-radius: calc(var(--radius-lg) - 6px);
  pointer-events: none;
  z-index: 0;
  background:
    linear-gradient(90deg, var(--accent) 0 18px, transparent 18px) top left / 18px 1px no-repeat,
    linear-gradient(0deg,  var(--accent) 0 18px, transparent 18px) top left / 1px 18px no-repeat,
    linear-gradient(90deg, transparent calc(100% - 18px), var(--accent) calc(100% - 18px)) top right / 18px 1px no-repeat,
    linear-gradient(0deg,  var(--accent) 0 18px, transparent 18px) top right / 1px 18px no-repeat,
    linear-gradient(90deg, var(--accent) 0 18px, transparent 18px) bottom left / 18px 1px no-repeat,
    linear-gradient(180deg, transparent calc(100% - 18px), var(--accent) calc(100% - 18px)) bottom left / 1px 18px no-repeat,
    linear-gradient(90deg, transparent calc(100% - 18px), var(--accent) calc(100% - 18px)) bottom right / 18px 1px no-repeat,
    linear-gradient(180deg, transparent calc(100% - 18px), var(--accent) calc(100% - 18px)) bottom right / 1px 18px no-repeat;
  opacity: 0.32;
}
.lp-final-card > * { position: relative; z-index: 1; }

.lp-final-title {
  font-family: var(--font-mono);
  font-size: clamp(30px, 3.8vw, 48px);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.6px;
  color: var(--text-primary);
  margin: 0 0 16px;
  /* Same scattered-light halo the hero headline uses — keeps the page's
     atmosphere coherent from open to close. */
  text-shadow:
    0 0 14px rgba(255,255,255,0.95),
    0 0 28px rgba(255,255,255,0.7),
    0 10px 36px rgba(8,145,178,0.10);
}
.lp-final-sub {
  font-family: var(--font-mono);
  font-size: clamp(13px, 1.05vw, 16px);
  line-height: 1.66;
  color: var(--text-secondary);
  margin: 0 auto clamp(36px, 5vh, 48px);
  max-width: 520px;
}
.lp-final-card .landing-cta {
  /* Cyan CTA lifted on a soft halo — same color story as the hero CTA
     but inverted (cyan-on-white vs ink-on-white), marking the closer
     as the moment of commitment. */
  background: linear-gradient(180deg, var(--accent) 0%, color-mix(in srgb, var(--accent) 86%, #000) 100%);
  border-color: color-mix(in srgb, var(--accent) 65%, #000);
  color: #ffffff;
  box-shadow:
    0 18px 38px -14px color-mix(in srgb, var(--accent) 50%, transparent),
    0 0 0 9px color-mix(in srgb, var(--accent) 10%, rgba(232, 248, 252, 0.6)),
    0 1px 0 rgba(255, 255, 255, 0.28) inset;
}
.lp-final-card .landing-cta:hover {
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 92%, #fff) 0%, var(--accent) 100%);
  border-color: var(--accent);
  transform: translateY(-1px);
  box-shadow:
    0 22px 44px -12px color-mix(in srgb, var(--accent) 60%, transparent),
    0 0 0 9px color-mix(in srgb, var(--accent) 14%, rgba(232, 248, 252, 0.7)),
    0 1px 0 rgba(255, 255, 255, 0.32) inset;
}

/* ── Footer ── */
.lp-footer {
  padding: clamp(36px, 5vh, 56px) 0;
  border-top: 1px solid var(--border);
  background: var(--bg-panel);
}
.lp-footer-shell {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: clamp(28px, 4vw, 72px);
  flex-wrap: wrap;
}
.lp-footer-brand {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 360px;
}
.lp-footer-cols {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(36px, 5vw, 72px);
}
.lp-footer-col {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 140px;
}
.lp-footer-col-label {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 0 0 6px 0;
}
.lp-footer-col a {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--text-secondary);
  text-decoration: none;
  transition: color 160ms ease;
}
.lp-footer-col a:hover { color: var(--accent); }
.lp-footer-col-all {
  margin-top: 4px;
  color: var(--accent) !important;
  font-weight: 600 !important;
  letter-spacing: 1px !important;
  text-transform: uppercase;
  font-size: 10px !important;
}
.lp-footer-mark {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--accent);
}
.lp-footer-word {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 4px;
}
.lp-footer-tagline {
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-secondary);
  margin: 0;
}
.lp-footer-legal {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  margin: 6px 0 0;
}

@media (prefers-reduced-motion: reduce) {
  .landing-mark,
  .landing-headline,
  .landing-sub,
  .landing-foot,
  .landing-scrollcue {
    animation: none;
    opacity: 1;
    transform: none;
  }
  [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
  /* Engine "processing" motion (card 02 only) — disable per user preference. */
  .lp-value[data-active="02"] .lp-engine-hex-inner,
  .lp-value[data-active="02"] .lp-engine-glyph,
  .lp-value[data-active="02"] .lp-engine-hex-outer {
    animation: none;
  }
  /* Reduced-motion: undo the pinned scrollytelling on the mortality
     section. The outer section collapses to natural height, the stage
     becomes regular flow, the dark backdrop stays visible behind
     beats 1+2, and the beats stack vertically at full opacity. The
     Meet Aescle beat needs its own light background since the dark
     backdrop covers the whole stage. */
  .lp-mortality {
    height: auto;
  }
  .lp-mortality-stage {
    position: relative;
    height: auto;
  }
  .lp-mortality-backdrop {
    position: absolute;
    opacity: 1;
  }
  .lp-mortality-aescle-glow {
    display: none;
  }
  .lp-mortality-beat {
    position: relative;
    inset: auto;
    min-height: clamp(480px, 80vh, 820px);
  }
  .lp-mortality-beat--aescle {
    background:
      radial-gradient(ellipse 60% 70% at 50% 50%, rgba(8,145,178,0.10), transparent 70%),
      radial-gradient(ellipse 90% 30% at 50% 0%, rgba(8,145,178,0.05), transparent 70%),
      var(--bg-page);
    z-index: 1;
  }
  .lp-mortality-title,
  .lp-mortality-body,
  .lp-mortality-aescle-mark,
  .lp-mortality-aescle-eyebrow,
  .lp-mortality-aescle-title,
  .lp-mortality-aescle-logos {
    opacity: 1;
  }
  .landing-scrollcue-arrow { animation: none; }
  .landing-trajectory-track {
    animation: none;
    stroke-dashoffset: 0;
  }
  .landing-trajectory-area {
    animation: none;
    opacity: 1;
  }
  .landing-orb,
  .landing-trajectory-ring,
  .landing-trajectory-glint,
  .landing-trajectory-dot,
  .landing-trajectory-pulse {
    animation: none;
    opacity: 1;
  }
  /* Bell-curve problem section: snap to the settled state. The
     argument still reads — pin in the tail, ghost dot at the apex,
     flag visible — just without motion. */
  .lp-problem-curve-line {
    animation: none;
    stroke-dasharray: none;
    stroke-dashoffset: 0;
    opacity: 0.42;
    stroke-width: 1.25;
  }
  .lp-problem-area {
    animation: none;
    opacity: 0.45;
  }
  .lp-problem-pin {
    animation: none;
    opacity: 1;
    offset-distance: 100%;
  }
  .lp-problem-pin-halo {
    animation: none;
    fill-opacity: 0.18;
  }
  .lp-problem-flag {
    animation: none;
    opacity: 1;
    transform: translateY(0);
  }
}

/* Narrow viewports — collapse the multi-column grids, tighten the hero
   typography, and shrink the trajectory layer so the marker lands above
   the CTA rather than colliding with it. */
@media (max-width: 760px) {
  .lp-faq-shell { grid-template-columns: 1fr; gap: 32px; }
  .lp-faq-head { position: static; }
  .landing-nav-links { display: none; }
  .landing-nav { gap: 12px; }
}

@media (max-width: 640px) {
  .landing-mark-word { font-size: 26px; letter-spacing: 11px; }
  .landing-headline { font-size: clamp(30px, 10.2vw, 42px); }
  .landing-sub { font-size: 14px; line-height: 1.66; }
  .landing-trajectory-wrap { height: 35vh; min-height: 190px; }
  .landing-trajectory-dot {
    width: 9px;
    height: 9px;
  }
  .landing-trajectory-pulse {
    width: 30px;
    height: 30px;
  }
  .landing-foot {
    margin-bottom: clamp(20px, 3vh, 32px);
  }
  .lp-footer-shell { gap: 28px; }
}

/* ═══════════ TOUR ═══════════
   One-time product orientation. Three surfaces share the same bottom-
   right corner (toast → pill → narration card) so the user's eye never
   has to reacquire the affordance. The dim layer is intentionally light:
   the data should still be legible — the focus ring carries emphasis,
   not the dim. Severity reds and amber are reserved; the ring uses the
   leverage axis (cyan) since this is the "your axis of control" tour. */

/* — Shared button vocabulary — */
.tour-btn {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
  padding: 8px 14px;
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease, transform 140ms ease;
  background: transparent;
}
.tour-btn-primary {
  background: var(--text-primary);
  color: var(--bg-panel);
  border-color: var(--text-primary);
}
.tour-btn-primary:hover {
  background: #000;
  border-color: #000;
}
.tour-btn-text {
  color: var(--text-secondary);
  padding: 8px 10px;
}
.tour-btn-text:hover {
  color: var(--text-primary);
  background: var(--bg-hover);
}
.tour-btn-text:disabled {
  color: var(--text-dim);
  cursor: not-allowed;
  background: transparent;
}

/* — Toast: first-time prompt — */
.tour-toast {
  position: fixed;
  right: 20px;
  bottom: 84px;
  width: 332px;
  max-width: calc(100vw - 40px);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  padding: 16px 18px 14px;
  z-index: 80;
  box-shadow:
    0 1px 0 rgba(20, 25, 32, 0.04),
    0 12px 32px -8px rgba(20, 25, 32, 0.18),
    0 2px 6px rgba(20, 25, 32, 0.06);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 280ms ease, transform 280ms cubic-bezier(0.2, 0.7, 0.2, 1);
  pointer-events: auto;
}
.tour-toast.is-visible {
  opacity: 1;
  transform: translateY(0);
  animation: tourToastPulse 4.2s ease-out 0.4s infinite;
}
/* Slow breathing cycle: the ring blooms briefly at the start of each
   beat then holds quiet for ~2.5s. Long enough that peripheral vision
   catches motion without it becoming a billboard. */
@keyframes tourToastPulse {
  0%, 100% { box-shadow: 0 1px 0 rgba(20,25,32,0.04), 0 12px 32px -8px rgba(20,25,32,0.18), 0 2px 6px rgba(20,25,32,0.06), 0 0 0 0 transparent; }
  6%       { box-shadow: 0 1px 0 rgba(20,25,32,0.04), 0 12px 32px -8px rgba(20,25,32,0.18), 0 2px 6px rgba(20,25,32,0.06), 0 0 0 0 var(--accent-soft); }
  40%      { box-shadow: 0 1px 0 rgba(20,25,32,0.04), 0 12px 32px -8px rgba(20,25,32,0.18), 0 2px 6px rgba(20,25,32,0.06), 0 0 0 14px transparent; }
}
.tour-toast-arrow {
  display: inline-block;
  transition: transform 140ms ease;
}
.tour-toast.is-visible .tour-toast-arrow {
  animation: tourToastArrow 4.2s ease-in-out 0.4s infinite;
}
.tour-toast .tour-btn-primary:hover .tour-toast-arrow {
  animation: none;
  transform: translateX(3px);
}
@keyframes tourToastArrow {
  0%, 60%, 100% { transform: translateX(0); }
  12%           { transform: translateX(3px); }
  28%           { transform: translateX(0); }
}
.tour-toast-eyebrow {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 8px;
}
.tour-toast-body {
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-primary);
  margin-bottom: 14px;
}
.tour-toast-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  justify-content: flex-end;
}
.tour-toast-timer {
  position: absolute;
  top: 0;
  left: 0;
  height: 3px;
  width: 100%;
  background: var(--accent);
  transform-origin: left center;
  transform: scaleX(0);
  animation: tourTimerFill 3.2s linear forwards;
}
@keyframes tourTimerFill {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* — Pill: persistent / resume affordance — */
.tour-pill {
  position: fixed;
  right: 20px;
  bottom: 84px;
  display: inline-flex;
  align-items: stretch;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 999px;
  z-index: 79;
  box-shadow:
    0 1px 0 rgba(20, 25, 32, 0.04),
    0 6px 18px -6px rgba(20, 25, 32, 0.14);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 220ms ease, transform 220ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
.tour-pill.is-visible {
  opacity: 1;
  transform: translateY(0);
}
.tour-pill-main {
  background: transparent;
  border: none;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.6px;
  color: var(--text-secondary);
  padding: 8px 14px 8px 14px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  border-radius: 999px 0 0 999px;
  transition: color 140ms ease, background 140ms ease;
}
.tour-pill-main:hover {
  color: var(--text-primary);
  background: var(--bg-hover);
}
.tour-pill-glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  color: var(--accent);
  font-size: 13px;
}
.tour-pill-dismiss {
  background: transparent;
  border: none;
  border-left: 1px solid var(--border);
  padding: 0 12px;
  font-size: 12px;
  color: var(--text-dim);
  cursor: pointer;
  border-radius: 0 999px 999px 0;
  transition: color 140ms ease, background 140ms ease;
}
.tour-pill-dismiss:hover {
  color: var(--text-primary);
  background: var(--bg-hover);
}

/* — Focus ring: spotlights the focal element. The big outer box-shadow
     carries the dim outside the ring, so the focal area reads at full
     brightness — no separate dim layer compounds atop it. JS clamps the
     ring to the visible region between topbar and bottom-nav so tall
     sections don't slice across the chrome. pointer-events:none so the
     user can still click through if they want to. — */
.tour-focus-ring {
  position: fixed;
  z-index: 71;
  pointer-events: none;
  border-radius: var(--radius-md);
  border: 1.5px solid var(--accent);
  box-shadow:
    0 0 0 9999px rgba(20, 25, 32, 0.14),
    0 0 0 1px var(--bg-panel) inset,
    0 0 18px -2px var(--accent-soft);
  opacity: 0;
  transition: opacity 280ms ease,
              top 320ms cubic-bezier(0.2, 0.7, 0.2, 1),
              left 320ms cubic-bezier(0.2, 0.7, 0.2, 1),
              width 320ms cubic-bezier(0.2, 0.7, 0.2, 1),
              height 320ms cubic-bezier(0.2, 0.7, 0.2, 1);
}
.tour-focus-ring.is-visible { opacity: 1; }

/* Attention pulse — fires once per beat (JS toggles `.is-pulsing`).
   A second cyan ring expands outward and fades, drawing the eye to the
   newly highlighted region without leaving a continuous animation
   running (which would read as anxious next to actuarial data). */
.tour-focus-ring::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1.5px solid var(--accent);
  opacity: 0;
  pointer-events: none;
}
.tour-focus-ring.is-pulsing::after {
  /* Three back-to-back pulses (~3.6s total), then quiet. One pulse is
     too easy to miss when the user's eye is mid-narration; a continuous
     loop reads as anxious. Three is the sweet spot — definitely seen,
     definitely finite. */
  animation: tourRingPulse 1200ms cubic-bezier(0.2, 0.7, 0.2, 1) 3;
}
@keyframes tourRingPulse {
  0%   { inset: 0;     opacity: 0.7; }
  60%  { inset: -10px; opacity: 0.18; }
  100% { inset: -14px; opacity: 0; }
}

/* — Card: the active narration. Bottom-right, same horizontal slot as
     the toast / pill so the eye doesn't have to track. Slightly larger,
     more chrome (stepper, ticks, controls). — */
/* The card pairs with the focus ring — same cyan border + accent glow so
   both surfaces read as part of one highlighted state. Without this, the
   card's neutral border blends into the dimmed page backdrop and the card
   looks muted next to the spotlit focal element. */
.tour-card {
  position: fixed;
  right: 20px;
  bottom: 84px;
  width: 380px;
  max-width: calc(100vw - 40px);
  background: var(--bg-panel);
  border: 1.5px solid var(--accent);
  border-radius: var(--radius-md);
  padding: 14px 16px 12px;
  z-index: 82;
  box-shadow:
    0 1px 0 rgba(20, 25, 32, 0.04),
    0 16px 40px -8px rgba(20, 25, 32, 0.20),
    0 4px 10px rgba(20, 25, 32, 0.06),
    0 0 18px -2px var(--accent-soft);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 280ms ease, transform 280ms cubic-bezier(0.2, 0.7, 0.2, 1);
  pointer-events: auto;
}
.tour-card.is-visible {
  opacity: 1;
  transform: translateY(0);
}
.tour-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.tour-card-eyebrow {
  flex: 1;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-secondary);
  min-width: 0;
}
.tour-card-step {
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.tour-card-eyebrow-sep {
  color: var(--text-dim);
}
.tour-card-eyebrow-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tour-card-exit {
  background: transparent;
  border: none;
  padding: 4px 6px;
  margin: -4px -6px -4px 0;
  font-size: 13px;
  color: var(--text-dim);
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: color 140ms ease, background 140ms ease;
}
.tour-card-exit:hover {
  color: var(--text-primary);
  background: var(--bg-hover);
}
.tour-card-ticks {
  display: flex;
  gap: 4px;
  margin-bottom: 12px;
}
.tour-card-tick {
  flex: 1;
  height: 2px;
  background: var(--border);
  border-radius: 1px;
  transition: background 240ms ease;
}
.tour-card-tick.is-done { background: var(--accent); }
.tour-card-tick.is-current {
  background: var(--accent);
  position: relative;
}
.tour-card-tick.is-current::after {
  content: '';
  position: absolute;
  inset: -2px -3px;
  border-radius: 2px;
  background: var(--accent-soft);
  z-index: -1;
}
.tour-card-title {
  font-size: 18px;
  font-weight: 500;
  letter-spacing: -0.2px;
  line-height: 1.3;
  color: var(--text-primary);
  margin-bottom: 8px;
}
.tour-card-body {
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-primary);
  margin-bottom: 14px;
}
.tour-card-body em {
  font-style: normal;
  font-weight: 600;
  color: var(--text-primary);
  background: var(--accent-soft);
  padding: 0 4px;
  border-radius: 3px;
  font-variant-numeric: tabular-nums;
}
.tour-card-foot {
  display: flex;
  align-items: center;
  gap: 4px;
  padding-top: 8px;
  border-top: 1px solid var(--border-subtle);
}
.tour-card-foot-spacer { flex: 1; }

/* — Tour tab highlight. While a beat is active, the corresponding tab
     gets a persistent accent backplate so the user can see at a glance
     which surface the narration is anchored to. The existing
     `.nav-tab.active` cyan only signals "current tab" — during the tour
     we want a stronger affordance that reads as "tour focus". A one-shot
     icon scale on entry signals the *change*; a softly breathing halo
     keeps the focus visible for the duration of the beat. */
.nav-tab.is-tour-pulse {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--accent) 38%, transparent),
    0 0 0 2px color-mix(in srgb, var(--accent) 14%, transparent);
  animation: tourTabHalo 2400ms ease-in-out infinite;
}
.nav-tab.is-tour-pulse .nav-tab-icon {
  animation: tourIconPulse 1100ms cubic-bezier(0.2, 0.7, 0.2, 1) 1;
}
@keyframes tourIconPulse {
  0%   { transform: scale(1);    filter: drop-shadow(0 0 0 transparent); }
  35%  { transform: scale(1.22); filter: drop-shadow(0 0 8px var(--accent)); }
  100% { transform: scale(1);    filter: drop-shadow(0 0 0 transparent); }
}
@keyframes tourTabHalo {
  0%, 100% { box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--accent) 32%, transparent),
    0 0 0 2px color-mix(in srgb, var(--accent) 10%, transparent); }
  50%      { box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--accent) 48%, transparent),
    0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .nav-tab.is-tour-pulse { animation: none; }
  .nav-tab.is-tour-pulse .nav-tab-icon { animation: none; }
}

/* Narrow viewports — cards span the bottom edge, ring still tracks. */
@media (max-width: 540px) {
  .tour-toast,
  .tour-card {
    right: 12px;
    left: 12px;
    width: auto;
    bottom: 72px;
  }
  .tour-pill { right: 12px; bottom: 72px; }
}

@media (prefers-reduced-motion: reduce) {
  .tour-toast,
  .tour-pill,
  .tour-card,
  .tour-focus-ring {
    transition: none;
    animation: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   Continuous-value editors — inline number input + Update on a lever
   row; same shape on action cards (✓ Done expands the form). Same
   tokens as the binary self-report controls so the two flavours read
   as siblings, not strangers.
   ═══════════════════════════════════════════════════════════════════ */

.continuous-editor,
.action-continuous-editor {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  font-family: var(--font-mono);
}

.continuous-editor-input-wrap,
.action-continuous-input-wrap {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.continuous-editor-input,
.action-continuous-input {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-primary);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 4px 8px;
  width: 76px;
  text-align: right;
}
.continuous-editor-input:focus,
.action-continuous-input:focus {
  outline: none;
  border-color: var(--accent);
}

.continuous-editor-unit,
.action-continuous-unit {
  font-size: 11px;
  color: var(--text-dim);
}

.continuous-editor-source {
  font-size: 10px;
  color: var(--text-dim);
  letter-spacing: 0.4px;
  text-transform: uppercase;
}
.continuous-editor-source-stale {
  color: var(--amber);
}
.continuous-editor-source-stale::before {
  content: '⚠ ';
}

.action-continuous-prompt {
  font-size: 11px;
  color: var(--text-dim);
  margin-right: auto;
}

/* The mark-done expansion is collapsed by default; the toggle button
   sits in flow with Dismiss, and the form reveals below when the user
   clicks ✓ Done. Hides cleanly without a layout pop because the form
   takes the full row when open. */
.action-controls-continuous .action-continuous-editor {
  display: none;
  width: 100%;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border-subtle);
}
.action-controls-continuous.is-editing .action-continuous-editor {
  display: flex;
}

.sr-btn-confirm {
  border-color: var(--accent);
  color: var(--accent);
}
.sr-btn-confirm:hover {
  background: var(--leverage-bg);
}

/* Lever-drilldown empty-state variants — banked vs unreported vs
   genuine "no tactics." Banked is positive (green tint); unreported is
   a quiet nudge to the input above. */
.lever-exp-empty-banked {
  color: var(--green);
}
.lever-exp-empty-unreported {
  color: var(--text-dim);
  font-style: italic;
}

/* Committed tag on completed-but-not-yet-banked tactics — sits next to
   the leverage badge so the user reads "I've signaled intent on this"
   without the row losing its rank position. */
.lever-exp-action-committed-tag {
  display: inline-flex;
  align-items: center;
  font-size: 10px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--accent);
  margin-right: 6px;
}
.lever-exp-action-committed .lever-exp-action-text {
  text-decoration: line-through;
  text-decoration-color: var(--border);
}

/* ═══════════════════════════════════════════════════════════════════
   Calibrate act — final welcome step. Lives inside .welcome-card so
   chrome (logo, subtitle, progress, 3D model) matches the other acts.
   No per-row Save button: typing a number commits on debounce, picking
   a select option commits on change. The single primary control is the
   "Continue" button at the bottom.
   ═══════════════════════════════════════════════════════════════════ */

.welcome-card-calibrate {
  /* Calibrate has more vertical content than other acts (4 rows + foot).
     Stretch the card and let the slot scroll if the viewport is tight. */
  max-height: min(86vh, 720px);
}

.welcome-act-calibrate {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.calibrate-rows {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.calibrate-row {
  padding: 10px 0;
  border-bottom: 1px solid rgba(140, 154, 172, 0.18);
}
.calibrate-row:first-child { border-top: 1px solid rgba(140, 154, 172, 0.18); }

.calibrate-row-form {
  display: grid;
  grid-template-columns: minmax(86px, 1fr) auto;
  grid-template-rows: auto auto;
  align-items: baseline;
  gap: 4px 10px;
  text-align: left;
}

.calibrate-row-name {
  grid-column: 1;
  grid-row: 1;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: 0.2px;
}

.calibrate-row-input-wrap {
  grid-column: 2;
  grid-row: 1;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  justify-content: flex-end;
}

.calibrate-row-input-wrap input[type="number"],
.calibrate-row-input-wrap select {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-primary);
  background: rgba(255, 255, 255, 0.7);
  border: 1px solid rgba(140, 154, 172, 0.35);
  border-radius: 4px;
  padding: 5px 8px;
  transition: border-color 0.15s, background 0.15s;
}
.calibrate-row-input-wrap input[type="number"] {
  width: 76px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.calibrate-row-input-wrap input[type="number"]:focus,
.calibrate-row-input-wrap select:focus {
  outline: none;
  border-color: var(--accent);
  background: rgba(255, 255, 255, 0.95);
}

.calibrate-row-unit {
  font-size: 11px;
  color: var(--text-dim);
}

.calibrate-row-goal {
  grid-column: 1;
  grid-row: 2;
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.2px;
}

.calibrate-row-source {
  grid-column: 2;
  grid-row: 2;
  font-size: 9px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  text-align: right;
  color: var(--text-dim);
}
.calibrate-source-vital { color: var(--text-secondary); }

.calibrate-row-banked .calibrate-row-name {
  color: var(--green);
}
.calibrate-row-banked .calibrate-row-input-wrap input[type="number"] {
  border-color: rgba(16, 185, 129, 0.45);
}

.calibrate-continue {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: white;
  background: var(--accent);
  border: 0;
  border-radius: 4px;
  padding: 10px 28px;
  cursor: pointer;
  transition: background 0.15s, transform 0.12s;
}
.calibrate-continue:hover { background: #0e7490; }
.calibrate-continue:active { transform: scale(0.98); }

.calibrate-foot-hint {
  font-size: 11px;
  color: var(--text-dim);
  margin: 6px 0 0;
  text-align: center;
}

/* ═══════════ WAITLIST ═══════════
   Email-capture modal shown by every marketing CTA in soft-launch
   mode (config.WAITLIST_MODE === true). Mounted on demand via
   js/waitlist.js — see openWaitlist().
*/

body.waitlist-modal-open {
  overflow: hidden;
}

.waitlist-modal {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease;
}
.waitlist-modal.is-open {
  opacity: 1;
  pointer-events: auto;
}

.waitlist-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, 0.42);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.waitlist-card {
  position: relative;
  width: 100%;
  max-width: 420px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 24px 80px -16px rgba(15, 23, 42, 0.28),
              0 8px 24px -8px rgba(15, 23, 42, 0.12);
  padding: 32px 32px 28px;
  transform: translateY(8px);
  transition: transform 220ms ease;
}
.waitlist-modal.is-open .waitlist-card {
  transform: translateY(0);
}

.waitlist-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 28px;
  height: 28px;
  border: 0;
  background: transparent;
  color: var(--text-dim);
  font-size: 14px;
  cursor: pointer;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s, color 0.15s;
}
.waitlist-close:hover {
  background: var(--bg-surface);
  color: var(--text-primary);
}

.waitlist-eyebrow {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.5px;
  color: var(--text-dim);
  margin: 0 0 10px;
  text-transform: uppercase;
}

.waitlist-title {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.2px;
  color: var(--text-primary);
  margin: 0 0 10px;
}

.waitlist-sub {
  font-size: 13px;
  line-height: 1.55;
  color: var(--text-secondary);
  margin: 0 0 22px;
}
.waitlist-sub strong {
  color: var(--text-primary);
  font-weight: 600;
}

.waitlist-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.waitlist-input {
  width: 100%;
  font: inherit;
  font-size: 14px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text-primary);
  transition: border-color 0.15s, background 0.15s;
  box-sizing: border-box;
}
.waitlist-input:focus {
  outline: none;
  border-color: var(--accent);
  background: #fff;
}

.waitlist-submit {
  width: 100%;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: white;
  background: var(--accent);
  border: 0;
  border-radius: 8px;
  padding: 13px 18px;
  cursor: pointer;
  transition: background 0.15s, transform 0.12s, opacity 0.15s;
}
.waitlist-submit:hover { background: #0e7490; }
.waitlist-submit:active { transform: scale(0.98); }
.waitlist-submit:disabled {
  opacity: 0.6;
  cursor: progress;
}

.waitlist-error {
  font-size: 12px;
  color: var(--red);
  margin: 10px 0 0;
}

