/* ═══════════ 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-sans);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0;
  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;
}

/* On a phone, center the whole unit vertically. Top-align stuffs
   everything under the topbar with a blank lower half; full
   space-between distribution pins the beats to the extremes and reads
   bizarrely. Centering the column as one group — with comfortable fixed
   gaps between the beats (see .ovh-headline in the 720px block) — splits
   the slack evenly above and below, so the panel sits balanced in the
   viewport. Scrolls normally if it ever outgrows the pane. */
@media (max-width: 720px) {
  .overview-shell {
    align-items: center;
    padding: var(--space-md) var(--space-md) var(--space-lg);
  }
}

/* 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) {
  /* Center the hero on a phone: eyebrow → 79 → meta as one centered
     unit. This balances the narrow number over the wider meta row (they
     share a center axis instead of an unbalanced left L), makes the
     number the clear focal point, and the center-vs-left contrast with
     the cards below breaks the flat left edge. Full width so there's
     room to center within. */
  .ovh-le-block {
    /* flex: 0 0 auto (not 100%) — the headline is a COLUMN here, so a
       100% basis would mean 100% height and blow each block up to a full
       screen. Content-height; space-between distributes the slack. */
    flex: 0 0 auto;
    width: 100%;
    padding: 8px 0 4px;
    align-items: center;
    text-align: center;
  }
  .ovh-le-num-wrap { justify-content: center; }
  .ovh-le-meta { justify-content: center; }
}

.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;
  /* Hover feedback is COLOR ONLY — deliberately no transform. A hover
     transform (even scale(1.012)) promotes the element to a GPU compositor
     layer; promoting it re-rasterizes overlapping siblings and repaints the
     layer itself, which shows as a one-frame flash on the hovered element AND
     on neighbors (the URGENT card, the horizon). Confirmed via DevTools paint-
     flashing: the transform hovers invalidated whole neighboring regions. Keep
     every overview hover transform-free (lever card + all horizon marks below
     follow the same rule) — main-thread paint changes don't flash.
     (Earlier dead ends: will-change / translateZ pin a PERMANENT layer, which
     the GPU-heavy life-calendar modal evicts on close → the element paints
     blank. Don't reintroduce either.) */
}
button.ovh-le-num:hover,
button.ovh-le-num:focus-visible {
  color: var(--accent);
  outline: none;
}
button.ovh-le-num:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 6px;
  border-radius: 4px;
}

/* 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;
  /* No transform on hover — see button.ovh-le-num. The shadow + border shift
     reads as a lift without promoting a compositor layer (the translateY that
     used to do the lift was flashing the horizon on every hover). */
  transition: border-color 0.2s, box-shadow 0.2s;
}
.ovh-lever-card:hover {
  border-color: color-mix(in srgb, var(--accent) 28%, var(--border));
  box-shadow: var(--shadow-card-hover);
}
.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;
}

/* ── Mobile horizon — threat cards (phone only) ──
   Hidden by default; the phone breakpoint flips it on. Desktop keeps the
   full Life Horizon below the headline. */
.ovh-mobile-horizon-block {
  display: none;
  width: 100%;
}

.ovh-mobile-horizon-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: fit-content;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-secondary);
  font-family: var(--font-mono);
  text-align: left;
  cursor: pointer;
}
.ovh-mobile-horizon-title:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 4px;
  border-radius: 2px;
}
/* Arrow as its own span (mirrors the reachable link): smaller than the
   caps, no inherited letter-spacing, so it optically centers instead of
   riding low on the glyph's own baseline. */
.ovh-mobile-horizon-arrow {
  display: inline-flex;
  font-size: 11px;
  line-height: 1;
  letter-spacing: 0;
  transition: transform 0.15s;
}
.ovh-mobile-horizon-title:hover .ovh-mobile-horizon-arrow,
.ovh-mobile-horizon-title:focus-visible .ovh-mobile-horizon-arrow {
  transform: translate(2px, -2px);
}

.ovh-mobile-threat-rail {
  display: flex;
  gap: 12px;
  width: 100%;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x proximity;
  padding: 3px 0 6px;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.ovh-mobile-threat-rail::-webkit-scrollbar {
  display: none;
}
/* Outlined (not filled) so the rail stays distinct from the single filled
   cyan action panel below — that remains the one emphasized element. The
   responsive clamp lets two cards sit comfortably while the third peeks
   past the edge (the scroll cue). */
.ovh-mobile-threat-card {
  flex: 0 0 clamp(132px, 40%, 168px);
  min-width: 0;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  padding: 14px 14px 15px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: none;
  color: inherit;
  font-family: var(--font-mono);
  text-align: left;
  box-shadow: none;
  cursor: pointer;
  transition: border-color 0.18s;
}
.ovh-mobile-threat-card:hover {
  border-color: color-mix(in srgb, var(--text-dim) 30%, var(--border));
}
.ovh-mobile-threat-card:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 3px;
}
/* The icon carries the threat's severity hue — the warm pop the old
   percentage tiles had, now that the figure is a calm dim age. */
.ovh-mobile-threat-icon {
  display: inline-flex;
  color: var(--text-secondary);
  line-height: 0;
}
.ovh-mobile-threat-card[data-severity="red"] .ovh-mobile-threat-icon { color: var(--red); }
.ovh-mobile-threat-card[data-severity="amber"] .ovh-mobile-threat-icon { color: var(--amber); }
.ovh-mobile-threat-card[data-severity="yellow"] .ovh-mobile-threat-icon { color: var(--yellow); }
.ovh-mobile-threat-card[data-severity="green"] .ovh-mobile-threat-icon { color: var(--green); }
.ovh-mobile-threat-name {
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: 0.1px;
}
.ovh-mobile-threat-age {
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0;
}

/* ── 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%; }

/* ── Performance lane — fake-front, locked ──
   The barbell's short-term half, sitting below the longevity arc. A
   single LOCKED experiment teaser: it names the second register without
   pretending the loop is live. Visually MUTED (surface fill, dim border,
   no card shadow) so it reads as gated/preview against the white,
   shadowed "Top action" card above — locked-but-inviting, not a peer
   CTA. Neutral throughout: performance is its own axis, so it borrows
   neither leverage-cyan nor severity-warm. */
.ovh-performance {
  display: flex;
  flex-direction: column;
}

.ovh-perf-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.ovh-perf-soon {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-dim);
  /* The eyebrow is uppercase/tracked; the lock glyph and tail label ride
     that rhythm but at the eyebrow's own weight. */
}
.ovh-perf-soon svg { display: block; }
.ovh-perf-soon-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.5px;
}

.ovh-perf-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 18px 24px;
  align-items: center;
  width: 100%;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: none;
  padding: 18px 22px;
  font-family: var(--font-mono);
  text-align: left;
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s;
}
.ovh-perf-card:hover {
  border-color: color-mix(in srgb, var(--text-dim) 32%, var(--border));
  background: color-mix(in srgb, var(--bg-surface) 55%, var(--bg-panel));
}
.ovh-perf-card:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 2px;
}

.ovh-perf-main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  min-width: 0;
}
.ovh-perf-tag {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 3px 8px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg-panel);
}
/* Placeholder hypothesis prose — the proposed experiment. Reads in the
   instrument face like the lever text; muted because the lane is gated. */
.ovh-perf-text {
  font-size: 13.5px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--text-secondary);
  letter-spacing: 0.1px;
}
/* Chips carry the daily register's signature: timing/duration (the
   intra-day, protocol allusion) and the verdict the coach will measure.
   Dim, quiet — they hint at structure, not data. */
.ovh-perf-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.ovh-perf-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.3px;
  color: var(--text-dim);
  padding: 4px 9px;
  border-radius: var(--radius-md);
  background: var(--bg-panel);
  border: 1px solid var(--border);
}
.ovh-perf-chip svg { display: block; opacity: 0.85; }

.ovh-perf-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-secondary);
  align-self: center;
}
.ovh-perf-cta-arrow {
  font-size: 12px;
  letter-spacing: 0;
  transition: transform 0.15s;
}
.ovh-perf-card:hover .ovh-perf-cta,
.ovh-perf-card:focus-visible .ovh-perf-cta {
  color: var(--accent);
}
.ovh-perf-card:hover .ovh-perf-cta-arrow,
.ovh-perf-card:focus-visible .ovh-perf-cta-arrow {
  transform: translate(2px, -2px);
}

@media (max-width: 720px) {
  /* Stack the CTA under the body on a phone — the two-column grid would
     squeeze the placeholder prose against the CTA in the narrow viewport. */
  .ovh-perf-card {
    grid-template-columns: 1fr;
    gap: 14px;
    padding: 16px 18px;
  }
  .ovh-perf-cta { align-self: flex-start; }
}

/* 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) {
  /* Phone hero: the number is still the focal point, with the compact
     horizon and action below it as quick scan targets. */
  .ovh-le-num { font-size: 116px; letter-spacing: -3px; }

  /* Direction-1 mobile overview: ONE dominant hero, then a quiet
     threat→lever readout. The hero is the only heavy element on the
     screen; risk and action are de-chromed into two ledger rows below
     (see further down). This kills the "three equal modules" busyness —
     hierarchy, not chrome, organizes the scan. */

  /* Meta: two single-purpose lines, not one overloaded row. The neutral
     projection (duration · year) reads dim on top; the cyan reachable
     upside drops to its own line below as the single leverage cue (plain
     text, no pill — it stays on the leverage axis but never competes with
     the number). One row crammed three numbers, a pipe divider, and a
     color/axis switch together — that was the "mouthful". The vertical
     divider retires; the line break separates the two axes now. */
  .ovh-le-meta {
    flex-direction: column;
    gap: 7px;
    padding-top: 14px;
  }
  .ovh-le-meta-divider { display: none; }
  .ovh-le-upside { font-size: 13px; font-weight: 600; }
  .ovh-le-upside-arrow { font-size: 11px; }

  /* Stack the three beats as one centered group with a comfortable,
     fixed gap between them — not a full-height distribution. The shell
     centers this group vertically; the gap sets the rhythm within it.
     Generous enough to read as distinct beats, restrained enough that
     they stay a single unit rather than drifting to the screen edges. */
  .ovh-headline {
    flex-direction: column;
    flex-wrap: nowrap;
    gap: 52px;
  }
  .ovh-section-title { margin-bottom: 10px; }

  /* The desktop Life Horizon is a horizontal ruler: it needs width to
     spread threat stations and 25y ticks. A phone gets the compressed
     horizon strip above instead; the full detail opens from that strip. */
  .ovh-timeline-block { display: none; }

  /* Threat Horizon surfaces only on the phone (desktop has the full ruler).
     flex: 0 0 auto — content-height in the column headline, not a full
     screen each (see .ovh-le-block note). */
  .ovh-mobile-horizon-block {
    display: flex;
    flex-direction: column;
    flex: 0 0 auto;
    width: 100%;
    padding: 0;
  }
  .ovh-lever-block { padding: 0; }

  /* ── Top Action — the one emphasized element ──
     A faint leverage-tinted panel marks it as the thing to act on (the
     mobile echo of the desktop "only the lever is framed" rule); the
     compact horizon stays bare. The leverage gain is the payoff, so it reads as a metric:
     big and cyan on the RIGHT, vertically centered against the left block
     (status tag over the action name). That uses the card's full width and
     balances it — instead of the gain stranded alone on a half-empty row. */
  .ovh-lever-card {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: auto auto;
    column-gap: 16px;
    row-gap: 8px;
    align-items: center;
    background: var(--leverage-bg);
    border: 0;
    border-radius: var(--radius-lg);
    box-shadow: none;
    padding: 16px 18px;
  }
  .ovh-lever-card:hover { box-shadow: none; transform: none; }
  .ovh-lever-tag {
    grid-column: 1;
    grid-row: 1;
    justify-self: start;
    align-self: start;
    top: 0;
  }
  .ovh-lever-text { grid-column: 1; grid-row: 2; }
  .ovh-lever-gain {
    grid-column: 2;
    grid-row: 1 / span 2;
    justify-self: end;
    align-self: center;
    font-size: 26px;
  }

  .ovh-lever-foot { display: none; }
}
@media (max-width: 520px) {
  .ovh-le-num { font-size: 84px; 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;
  /* Real vertical body now: cards live above the spine, bookend stats below.
     --lhz-spine-y sits below center so the taller cards get the room. */
  --lhz-axis-height: 118px;
  --lhz-spine-y: 62%;
  --lhz-track-h: 3px;
  --lhz-mark-size: 11px;
  --lhz-anchor-size: 13px;
}

.lhz-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.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-title-button {
  display: inline-block;
  padding: 0;
  border: 0;
  background: transparent;
  font-family: var(--font-mono);
  cursor: pointer;
  transition: color 0.15s;
}
.lhz-title-button:hover,
.lhz-title-button:focus-visible {
  color: var(--accent);
  outline: none;
}
.lhz-title-button:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 4px;
  border-radius: 2px;
}

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

/* The spine: a thin, quiet reference line from Birth to the projected
   terminus. It is deliberately chrome now — the signal moved up into the
   threat cards and out to the bookend stats — so it recedes rather than
   competing. The axis owns --lhz-projected, so the spine ends exactly
   where the dashed cyan upside tail begins. */
.lhz-line {
  position: absolute;
  top: var(--lhz-spine-y, 50%);
  left: 0;
  width: var(--lhz-projected, 100%);
  height: var(--lhz-track-h, 3px);
  background: #dcdfe4;
  transform: translateY(-50%);
  border-radius: 999px;
}

/* Dashed extension — modifiable upside is hypothetical territory, not
   realized projection. Dashing signals "potential, not fact." Runs from the
   spine's end out to the leverage ceiling pip; only present when there's
   upside to show. Cyan (leverage), matching the app-wide convention that
   --leverage marks years the user can still bank. */
.lhz-line-extension {
  position: absolute;
  top: var(--lhz-spine-y, 50%);
  left: var(--lhz-projected);
  right: 0;
  height: var(--lhz-track-h, 3px);
  transform: translateY(-50%);
  border-radius: 999px;
  background: repeating-linear-gradient(
    to right,
    color-mix(in srgb, var(--leverage) 55%, transparent) 0,
    color-mix(in srgb, var(--leverage) 55%, transparent) 5px,
    transparent 5px,
    transparent 11px
  );
}

/* Rail hit-band — clicking the open space expands the compressed horizon
   into the detailed Threat Timeline. Born / Now / End remain calendar
   targets. A transparent full-width band centered on the line lane,
   sitting beneath the marks (z-index 1 < the marks' 2) so threat stations
   and the ceiling pip — which branch to Risks / Plan — always win the
   click; the band only collects the inert space between them. Pointer-only
   (tabindex -1): keyboard users reach the timeline through the title
   dots, so the band never adds a full-width focus stop. */
.lhz-rail-hit {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--lhz-spine-y, 50%);
  height: 28px;
  transform: translateY(-50%);
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  z-index: 1;
}

/* Quiet reward on hover: the spine deepens a shade so the click feels
   acknowledged, without the band ever announcing itself as a button. */
.lhz-axis:has(.lhz-rail-hit:hover) .lhz-line {
  background: #ccd0d7;
}

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

/* Marks are knobs that sit ABOVE the recessed groove: the 3px panel ring
   punches a clean gap in the track, and a soft contact shadow lifts the
   knob off the surface — the consumer-slider depth, tuned to the app's
   restrained shadow language (combined opacity well under the card token). */
.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),
    0 1px 2px rgba(15, 23, 42, 0.16);
  z-index: 1;
}

/* Threat knobs add a soft severity halo — a quiet wash of the threat's own
   color — so the eye lands on what's coming without shouting. Only the
   stations carry --dot, so the halo lives here, not on the neutral anchors. */
.lhz-mark-station .lhz-mark-dot {
  box-shadow:
    0 0 0 3px var(--bg-panel),
    0 0 0 5px color-mix(in srgb, var(--dot) 15%, transparent),
    0 1px 2px rgba(15, 23, 42, 0.14);
}

.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;
}

/* ── Threat callout card ───────────────────────────────────────────
   The substance of the strip. A small panel-on-panel card floating above
   the spine, joined to its dot by a short leader. Carries icon + full
   threat name and the lifetime-risk figure — real content where there
   used to be a 10px dot. Card lift uses the shared card-shadow token so
   it sits in the same material language as the rest of the app. */
.lhz-card {
  position: absolute;
  bottom: calc(var(--lhz-mark-size) / 2 + 18px);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px 10px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 9px;
  box-shadow: var(--shadow-card);
  white-space: nowrap;
  text-align: left;
  z-index: 3;
  transition: box-shadow 0.15s, border-color 0.15s, transform 0.15s;
}
/* Edge cards grow inward instead of overflowing the panel: the anchor flag
   shifts the card's horizontal origin and re-points the leader (below). */
.lhz-mark-station[data-card-anchor="end"] .lhz-card {
  left: auto; right: -1px; transform: none;
}
.lhz-mark-station[data-card-anchor="start"] .lhz-card {
  left: -1px; transform: none;
}
.lhz-card-head {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.lhz-card-name {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.1px;
  color: var(--text-primary);
}
/* Leader — a thin line dropping from the card down to the dot on the spine.
   Severity-colored (inherits --dot) but quiet, so it reads as a tether. */
.lhz-card-leader {
  position: absolute;
  left: 50%;
  bottom: calc(var(--lhz-mark-size) / 2);
  transform: translateX(-50%);
  width: 1.5px;
  height: 16px;
  background: var(--dot, var(--text-dim));
  opacity: 0.5;
  border-radius: 1px;
  z-index: 0;
}
.lhz-card-icon {
  display: inline-flex;
  align-items: center;
  line-height: 0;
}
.lhz-card-icon .cod-icon {
  display: block;
}

/* Mark meta — the number under a dot. Threats show their peak age small;
   the NOW/END bookends override this to a large numeral (see .lhz-mark-anchor
   below). Born shows its calendar year, recessive. */
.lhz-mark-meta {
  position: absolute;
  top: calc(var(--lhz-mark-size) / 2 + 11px);
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.6px;
  line-height: 1;
}

/* Bookend stats — NOW and END frame the arc with their age a touch larger
   than the threat ages and in a darker ink, so the two ends carry a little
   more weight without shouting. */
.lhz-mark-anchor .lhz-mark-meta {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--text-secondary);
}

/* Station — clickable threat callout. */
button.lhz-mark-station {
  cursor: pointer;
}
button.lhz-mark-station:hover .lhz-card {
  border-color: color-mix(in srgb, var(--dot) 40%, var(--border));
  box-shadow: var(--shadow-card-hover);
  transform: translateX(-50%) translateY(-1px);
}
.lhz-mark-station[data-card-anchor="end"]:hover .lhz-card {
  transform: translateY(-1px);
}
.lhz-mark-station[data-card-anchor="start"]:hover .lhz-card {
  transform: translateY(-1px);
}
button.lhz-mark-station:focus-visible {
  outline: none;
}
button.lhz-mark-station:focus-visible .lhz-card {
  border-color: var(--accent);
  box-shadow: var(--shadow-card), 0 0 0 2px var(--accent-soft);
}

/* BIRTH — origin anchor on the far left. The lightest, most recessive
   mark: it's lived past, immutable, the user can't act on it. Sits a
   notch smaller and dimmer than NOW/END so the row reads faint-origin →
   heavy-you → heavy-bookend, and the runway ahead keeps the weight. */
.lhz-mark-birth .lhz-mark-dot {
  width: 10px;
  height: 10px;
  background: var(--text-dim);
  box-shadow:
    0 0 0 3px var(--bg-panel),
    0 1px 1.5px rgba(15, 23, 42, 0.12);
  opacity: 0.8;
}
.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; }
button.lhz-mark-birth:hover .lhz-mark-name { color: var(--accent); }
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),
    0 2px 4px rgba(15, 23, 42, 0.26);
}
.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;
}
button.lhz-mark-now:hover .lhz-mark-name {
  color: var(--accent);
}
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),
    0 1.5px 3px rgba(15, 23, 42, 0.22);
}
.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;
}
button.lhz-mark-end:hover .lhz-mark-name {
  color: var(--accent);
}
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),
    0 0 0 5px color-mix(in srgb, var(--leverage) 15%, transparent),
    0 1px 2px rgba(15, 23, 42, 0.14);
}
.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;
}
/* No transform nudge on hover/focus — it flashed the 5y label and its
   neighbors (see button.ovh-le-num). The label is already prominent cyan and
   cursor:pointer signals the click; keyboard focus keeps the dot ring below. */
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: var(--lhz-spine-y, 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) {
  /* Cards need the body even on the small-tablet range where the desktop
     timeline still shows (it's hidden below 720px). */
  .lhz { --lhz-axis-height: 112px; }
}
@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; }
  /* Phone widths can't fit two tiers of labels (BORN/NOW/END +
     anchor ages + 25y year ticks) without collisions. The anchor
     ages already convey span; the year ticks are the dispensable
     tier. Keep the ticks (the cadence is useful) but drop their
     labels so 2050/2075 don't pile on top of 36/79. */
.lhz-decade-label { display: none; }
}

/* ═══════════ THREAT TIMELINE ═══════════ */
.tt-shell {
  /* Horizontal inset lives on the *content* (head, axis, rows), not the shell,
     so the scrollable .tt-body runs edge-to-edge and its scrollbar hugs the
     panel edge instead of floating inside the old shell padding. */
  --tt-inset: var(--space-lg);
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  padding: var(--space-lg) 0 var(--space-2xl);
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.tt-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: var(--space-lg);
  padding: 0 var(--tt-inset) var(--space-lg);
  border-bottom: 1px solid var(--border);
}

.tt-title {
  margin: 0;
}

.tt-dek {
  margin: 8px 0 0;
  max-width: 58ch;
  font-size: 11px;
  line-height: 1.55;
  color: var(--text-dim);
  letter-spacing: 0.2px;
}

.tt-close {
  width: 28px;
  height: 28px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-panel);
  color: var(--text-secondary);
  font-family: var(--font-mono);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.tt-close:hover,
.tt-close:focus-visible {
  color: var(--text-primary);
  border-color: var(--text-dim);
  background: var(--bg-surface);
  outline: none;
}

.tt-body {
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding-top: var(--space-lg);
}
.tt-body::-webkit-scrollbar { width: 4px; }
.tt-body::-webkit-scrollbar-track { background: transparent; }
.tt-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }

.tt-axis {
  position: sticky;
  top: calc(-1 * var(--space-lg));
  z-index: 2;
  height: calc(42px + var(--space-lg));
  /* 178px = icon column (154) + row gap (24); aligns the axis with the chart
     column. Plus the content inset so it tracks the padded rows. */
  margin-left: calc(var(--tt-inset) + 178px);
  margin-right: var(--tt-inset);
  margin-top: calc(-1 * var(--space-lg));
  padding-top: var(--space-lg);
  overflow: hidden;
  background: var(--bg-panel);
  font-family: var(--font-mono);
}
.tt-axis::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--space-lg) + 25px);
  height: 1px;
  background: var(--border);
}
.tt-axis-label,
.tt-axis-tick {
  position: absolute;
  top: var(--space-lg);
  transform: translateX(-50%);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.9px;
  color: var(--text-dim);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.tt-axis-label-now {
  color: var(--accent);
  transform: translateX(0);
}
.tt-axis-label-end {
  color: var(--text-secondary);
  transform: translateX(-100%);
}
.tt-axis-tick span {
  position: absolute;
  top: 22px;
  left: 50%;
  width: 1px;
  height: 7px;
  background: var(--border);
}

.tt-rows {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 0 var(--tt-inset);
}

.tt-row {
  display: grid;
  grid-template-columns: 154px minmax(0, 1fr);
  align-items: center;
  gap: 24px;
  min-height: 68px;
  padding: 7px 0;
  border: 0;
  border-bottom: 1px solid var(--border-subtle);
  background: transparent;
  font-family: var(--font-mono);
  text-align: left;
  cursor: pointer;
}
.tt-row:hover,
.tt-row:focus-visible {
  background: linear-gradient(to right, transparent, var(--bg-surface), transparent);
  outline: none;
}
.tt-row:hover .tt-line,
.tt-row:focus-visible .tt-line {
  stroke-width: 2.25;
}

.tt-row-id {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.tt-row-icon {
  display: inline-flex;
  flex: 0 0 auto;
  opacity: 0.9;
}
.tt-row-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-primary);
}
.tt-row-label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.tt-row-risk {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--text-dim);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.tt-row-chart {
  position: relative;
  min-width: 0;
  height: 54px;
  overflow: hidden;
}
.tt-chart {
  display: block;
  width: 100%;
  height: 54px;
  overflow: visible;
}
.tt-area {
  opacity: 0.11;
}
.tt-line {
  stroke-width: 1.55;
  vector-effect: non-scaling-stroke;
  opacity: 0.92;
  transition: stroke-width 0.12s;
}
.tt-peak-guide {
  stroke-width: 1;
  stroke-dasharray: 2 3;
  vector-effect: non-scaling-stroke;
  opacity: 0.34;
}
.tt-peak-dot {
  position: absolute;
  top: 50%;
  width: 8px;
  height: 8px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--tt-color);
  box-shadow: 0 0 0 3px var(--bg-panel);
}
.tt-peak-dot span {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 4px);
  transform: translateX(-50%);
  font-size: 10px;
  font-weight: 700;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 880px) {
  .tt-shell {
    --tt-inset: var(--space-md);
    padding: var(--space-md) 0 var(--space-xl);
  }
  .tt-head {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--space-md);
  }
  .tt-close {
    grid-column: 2;
    grid-row: 1;
  }
  /* Rows collapse to a single column on mobile, so the axis no longer offsets
     for the icon column — it tracks the content inset on both sides. */
  .tt-axis {
    margin-left: var(--tt-inset);
  }
  .tt-row {
    grid-template-columns: 1fr;
    gap: 12px;
    min-height: 108px;
    padding: 12px 0;
  }
  .tt-row-id {
    grid-column: 1;
    grid-row: 1;
  }
  .tt-row-chart {
    grid-column: 1;
    grid-row: 2;
  }
}

/* ═══════════ 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;
}

/* Mobile accordion — the active row's detail card injected inline
   directly beneath the row it belongs to. Hidden on desktop (the
   side detail pane is the active surface there); shown on mobile in
   place of the side pane via the @media block below. Same DOM is
   rendered into both surfaces; only one is visible per viewport. */
.risks-rail-inline-detail { display: none; }

/* Mobile-only orientation layer. Hidden on desktop so the two-pane rail
   keeps its existing density and rhythm. */
.risks-mobile-summary,
.risk-mobile-section-kicker { display: none; }

/* Mobile-only column header row (Threat / Risk / Modifiable). Lives in
   the rail aside but is hidden on desktop, where the existing per-tier
   group heads carry the section labels + MODIFIABLE hint instead. */
.risks-rail-cols { display: none; }

/* Risk-percent column — added to every row in markup, hidden on
   desktop because the desktop rail intentionally stays a two-column
   "rank + reach" navigator (probability lives in the detail header
   where the user is in deep-read mode). On mobile we have horizontal
   room and the rail IS the page, so it doubles as a real data table. */
.risks-rail-prob { display: none; }

.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: 20px 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: 3;
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
}

/* List-level sort caption — sits once above the first tier head, full
   width, so it describes the whole ranked list instead of crowding the
   two column labels (Significant / Modifiable) in the head row. The
   ordinals carry "this is ranked"; this names what they rank by. */
.risks-rail-sort-caption {
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.2px;
  color: var(--text-dim);
  padding: 0 8px 10px;
}

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

/* "Modifiable" hint on the SIGNIFICANT group head only — matches the
   drill-down hero label so the same word teaches the same concept
   across surfaces. 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-modifiable {
  text-align: right;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2px;
  color: var(--text-secondary);
  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 .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: 20px 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;
}

/* Rank ordinal — the desktop list's "this is ranked, top = worst" cue.
   A quiet dim numeral in its own leading column; the descending order is
   carried by the ordinal itself, so no competing magnitude is added to
   the reach column. The active row brightens it to mark "you are here".
   (Mobile re-styles this inside its media query.) */
.risks-rail-rank {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.5px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  align-self: center;
}
.risks-rail-row.selected .risks-rail-rank { color: var(--text-secondary); }

/* 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;
}

/* Fully-secured threat: reachable = 0 but banked > 0. A flat, muted green
   check — these are done and no longer demand attention, so it recedes to
   the same low emphasis as the dash rather than the footer's bright pill.
   Color alone (faint green vs. gray) carries "secured" vs. "fate". */
.risks-rail-leverage-secured {
  color: var(--green);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
}

/* Gate the hover wash behind a real pointer. Touch devices keep :hover
   "stuck" on the last-tapped element until the next tap elsewhere, so
   without this a tapped rail row (e.g. one just collapsed) keeps a gray
   --bg-hover wash that reads as a lingering selection. Width isn't the
   right signal — a narrow desktop window still has a mouse — so key off
   the pointer, not the breakpoint. */
@media (hover: hover) {
  .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;
  color: var(--text-secondary);
}

/* Active-row hedge: only the selected row's icon picks up its severity
   hue, marking "you are here" and matching the drill-down hero icon.
   Driven by .selected + data-severity so the row click handler
   (which only toggles .selected) is enough to retint without
   re-rendering the rail. */
.risks-rail-row.selected[data-severity="red"] .risks-rail-icon { color: var(--red); }
.risks-rail-row.selected[data-severity="amber"] .risks-rail-icon { color: var(--amber); }
.risks-rail-row.selected[data-severity="yellow"] .risks-rail-icon { color: var(--yellow); }
.risks-rail-row.selected[data-severity="green"] .risks-rail-icon { color: var(--green); }

/* Product tour, beat 2: the rail ring explains the region; these icon
   pulses identify the actual threats named in the card. The first three
   pulses are loud enough to acquire the eye; the sustained pulse is
   quieter so it can remain active without nagging. */
.risks-rail-row.is-tour-threat-focus .risks-rail-icon {
  color: var(--red);
  position: relative;
  animation: tourThreatIconPulse 1500ms cubic-bezier(0.2, 0.7, 0.2, 1)
    calc(var(--tour-threat-index, 0) * 180ms) 3;
}

.risks-rail-row.is-tour-threat-focus .risks-rail-icon::before,
.risks-rail-row.is-tour-threat-focus .risks-rail-icon::after {
  content: '';
  position: absolute;
  inset: -5px;
  border: 1px solid currentColor;
  border-radius: 999px;
  opacity: 0;
  pointer-events: none;
}

.risks-rail-row.is-tour-threat-focus .risks-rail-icon::after {
  animation: tourThreatIconRing 1500ms cubic-bezier(0.2, 0.7, 0.2, 1)
    calc(var(--tour-threat-index, 0) * 180ms) 3;
}

.risks-rail-row.is-tour-threat-focus .risks-rail-icon::before {
  animation: tourThreatIconRingQuiet 2200ms cubic-bezier(0.2, 0.7, 0.2, 1)
    calc(4500ms + (var(--tour-threat-index, 0) * 180ms)) infinite;
}

@keyframes tourThreatIconPulse {
  0%, 100% { transform: scale(1); filter: drop-shadow(0 0 0 transparent); }
  38%      { transform: scale(1.1); filter: drop-shadow(0 0 5px color-mix(in srgb, var(--red) 28%, transparent)); }
}

@keyframes tourThreatIconRing {
  0%   { transform: scale(0.9); opacity: 0.28; }
  70%  { transform: scale(1.2); opacity: 0.06; }
  100% { transform: scale(1.28); opacity: 0; }
}

@keyframes tourThreatIconRingQuiet {
  0%   { transform: scale(0.98); opacity: 0.18; }
  64%  { transform: scale(1.18); opacity: 0.05; }
  100% { transform: scale(1.24); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .risks-rail-row.is-tour-threat-focus .risks-rail-icon,
  .risks-rail-row.is-tour-threat-focus .risks-rail-icon::before,
  .risks-rail-row.is-tour-threat-focus .risks-rail-icon::after {
    animation: none;
  }
}

/* ───── Mobile risks rail ─────
   Sits AFTER all base `.risks-rail*` declarations so the cascade's
   later-wins rule naturally lets these mobile overrides take effect
   without needing escalated specificity. Earlier attempts placed this
   block above the base rules; equal specificity + later source order
   reinstated the desktop sizing/visibility on mobile and broke the
   layout (4th column wrap, tier heads reappearing). */
@media (max-width: 880px) {
  /* Layout collapses to a single column; the side detail pane folds
     down into the inline accordion expansion under the active row. */
  .risks-layout { grid-template-columns: 1fr; }
  .risks-detail { display: none; }
  .risks-rail {
    /* Rail IS the page on mobile, not a side navigator — sticky here
       would let the inline expansion scroll under a pinned header. */
    position: static;
    top: auto;
    /* The desktop --space-lg rail gap separates the column header and the
       two tier groups. On mobile the tiers are presented as one flat list
       (group heads hidden below), so collapse the gap to the 2px row
       cadence — the SIGNIFICANT/MINOR boundary then reads continuous with
       every other row break instead of as a section break. */
    gap: var(--space-sm);
  }
  .risks-rail-inline-detail {
    display: block;
    /* Breathing room from the active header above and the next row
       below. The injected card owns its own padding + shadow.
       Modest left inset: spine sits just inside the row's content edge
       and the content (+ spine 1.5 + card pad 12) lands ~midway between
       the row icon (8px in) and its label (36px in) — enough that the
       expansion reads as the row's nested body, not so deep that it
       aligns under the label and feels over-indented. */
    padding: 0 0 var(--space-lg) 0;
    /* Cancel the list's 4px row gap above the detail so the open row's
       left spine flows straight into the card's left rail with no break. */
    margin-top: calc(var(--space-xs) * -1);
  }
  .risks-rail-row.selected {
    /* Square the bottom corners so the active row reads as an open
       accordion header continuous with the unfolded detail below. */
    border-radius: var(--radius-md) var(--radius-md) 0 0;
  }

  /* Page title is redundant with the bottom tab bar's RISKS label;
     drop it so the list gets the first content slot. Scoped to
     .risks-view so other tabs' headers stay intact. */
  .risks-view .view-header { display: none; }

  /* No card chrome — the flat --bg-surface fill read as a muddy gray
     box. The summary is a briefing band: kicker, headline (with the one
     deliberate severity-colored number), sub, then a hairline handing
     off to the list. */
  .risks-mobile-summary {
    display: block;
    padding: 2px 10px 14px;
    margin: 0 0 var(--space-xs);
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--border);
    border-radius: 0;
  }
  .risks-mobile-summary-kicker {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    color: var(--text-secondary);
    margin-bottom: 10px;
  }
  .risks-mobile-summary-main {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-md);
    font-size: 13px;
    line-height: 1.35;
    color: var(--text-primary);
  }
  .risks-mobile-summary-main span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .risks-mobile-summary-main strong {
    font-size: 20px;
    font-weight: 500;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
  }
  .risks-mobile-summary-sub {
    margin-top: 8px;
    font-size: 11px;
    line-height: 1.5;
    color: var(--text-dim);
  }

  /* Mobile reads as a ranked briefing list. The desktop per-group head is
     dropped, but a single column key (.risks-rail-cols, below) is restored
     so the two numeric columns — lifetime risk and modifiable years — are
     named once at the top instead of floating unlabeled on every row. */
  .risks-rail-group-head { display: none; }
  .risks-rail-sort-caption { display: none; }
  .risks-rail-group + .risks-rail-group { margin-top: 0; }
  .risks-rail-row.group-minor {
    padding: 12px 10px;
    opacity: 1;
  }
  .risks-rail-row.group-minor .watch-name {
    color: var(--text-primary);
    font-weight: 500;
  }

  /* Column key — restored on mobile only (hidden on desktop, where the
     per-group head carries the labels). Shares the row grid template so
     "Risk" sits over the % and "Modifiable" over the +Xy column; the empty
     leading cells and the "Threat" label are suppressed (rows are
     self-evidently threats). Quiet small-caps so it reads as a legend, not
     a competing header. */
  .risks-rail-cols {
    display: grid;
    grid-template-columns: 26px 24px minmax(0, 1fr) auto;
    column-gap: 10px;
    align-items: end;
    /* No top margin — the rail's flex gap already separates it from the
       summary above. Tight padding below anchors it to the list it labels
       (a header belongs to its content, not the section before it). */
    margin-top: 0;
    padding: 0 10px 2px;
  }
  .risks-rail-cols > span {
    font-family: var(--font-sans);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.8px;
    text-transform: uppercase;
    color: var(--text-dim);
  }
  .risks-rail-cols > span:nth-child(2) { display: none; }      /* "Threat" */
  .risks-rail-cols > span:nth-child(3) { grid-column: 3; }      /* over the % */
  .risks-rail-cols > span:nth-child(4) {                        /* over the +Xy */
    grid-column: 4;
    justify-self: end;
    text-align: right;
  }

  .risks-rail-list { gap: 4px; }
  .risks-rail-row {
    grid-template-columns: 26px 24px minmax(0, 1fr) auto;
    grid-template-areas:
      "rank icon name leverage"
      "rank icon prob leverage";
    column-gap: 10px;
    row-gap: 4px;
    padding: 12px 10px;
    border: 0;
    border-radius: var(--radius-md);
  }
  /* Open row: the cyan agency wash (matches desktop's .selected), no gray
     fill, no full border. The inset spine sits at x=0 (the row has no
     border to push it in) so it lines up exactly with the detail card's
     left rail below — one continuous rail, not two misaligned ones. */
  .risks-rail-row.selected {
    background: var(--accent-soft);
    box-shadow: inset 2px 0 0 var(--accent);
  }
  .risks-rail-rank {
    display: inline;
    grid-area: rank;
    align-self: start;
    padding-top: 2px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 1px;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
  }
  .risks-rail-icon {
    grid-area: icon;
    align-self: start;
    padding-top: 1px;
  }
  .risks-rail-row .watch-name {
    grid-area: name;
    min-width: 0;
    font-size: 12px;
    line-height: 1.35;
    letter-spacing: 1.8px;
  }
  /* Lifetime-risk magnitude bar — a track-less fill whose length is the
     threat's share of the top threat (normalized in rail.js), so the
     dominance of the top one or two over the single-digit tail is visceral.
     This is the "where to focus" signal and the warm/risk axis that balances
     the cool cyan leverage on the right. No gray track: the % value rides at
     the bar's tip, so the whole cluster sits left, clear of the leverage
     years. Fill is tinted by severity — warm and cool axes stay separated. */
  .risks-rail-prob {
    grid-area: prob;
    display: flex;
    align-items: center;
    gap: 11px;
    margin-top: 4px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }
  .risks-rail-bar {
    flex: none;
    min-width: 2px;
    height: 4px;
    border-radius: 2px;
    background: var(--text-dim);
  }
  .risks-rail-row[data-severity="red"] .risks-rail-bar { background: var(--red); }
  .risks-rail-row[data-severity="amber"] .risks-rail-bar { background: var(--amber); }
  .risks-rail-row[data-severity="yellow"] .risks-rail-bar { background: var(--yellow); }
  .risks-rail-row[data-severity="green"] .risks-rail-bar { background: var(--green); }
  .risks-rail-prob-val {
    flex: none;
    font-size: 11px;
    color: var(--text-secondary);
  }
  .risks-rail-leverage {
    grid-area: leverage;
    display: inline-flex;
    flex-direction: column;
    align-items: flex-end;
    justify-self: end;
    align-self: center;
    gap: 3px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-primary);
  }
  /* Upside (+Xy) ramps in the cool/leverage hue (cyan) by MAGNITUDE — color
     here encodes the variable the sort order doesn't (the list is ranked by
     risk, not leverage), so the biggest opportunities surface even when they
     sit low in the list. strong (≥2y) vivid, moderate (1–2y) cyan, light
     (0.5–1y) neutral so marginal years recede instead of all reading alike.
     Distinct from the green ✓ (banked/done) and gray — (fate): cyan = still
     reachable. Mobile only — desktop keeps neutral ink (the drill tile owns
     the focal +Xy there). */
  .risks-rail-leverage-gain { color: var(--text-secondary); }
  .risks-rail-leverage-gain[data-leverage="moderate"] { color: var(--leverage); }
  .risks-rail-leverage-gain[data-leverage="strong"] {
    color: var(--leverage);
    font-weight: 600;
  }
  /* Truncate long threat names ("Neurodegen.") with an ellipsis so
     the row height stays uniform and the numeric columns don't get
     bumped out of alignment by a wrap. */
  .risks-rail-row .watch-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* "base rates by population ↗" is a desktop concession to fill the
     space below the sticky rail. On mobile the inline accordion
     already routes the user into the same population data per-threat;
     drop the standalone link as orphan chrome. */
  .risks-rail-cite { display: none; }
}

.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 stats"
    "prose prose prose";
  column-gap: var(--space-lg);
  row-gap: 18px;
  align-items: center;
  padding-right: var(--space-md);
}

.risk-detail-head > .risk-detail-icon { grid-area: icon; }
.risk-detail-head > .risk-detail-head-text { grid-area: text; }
.risk-detail-head > .risk-detail-stats { grid-area: stats; }
.risk-detail-head > .risk-detail-prose { grid-area: prose; align-self: start; }

/* Identity block — name with its badges (Personalized / Calibrate) stacked
   beneath, left-aligned. Sits between the cause-of-death icon (left) and the
   vitals strip (right). */
.risk-detail-head-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}

/* Top-line vitals readout — the three figures that answer the job: how
   likely (risk), when it comes (peak age), how much you can deflect it
   (modifiable). Anchored to the right of the head, balancing the icon on
   the left — the command-center "identity left, instruments right"
   composition. An airy strip, NOT a boxed KPI row: no cards, no borders,
   no divider rules — just grouped figures with whitespace, big light
   numerals, quiet labels, and a single semantic accent (cyan = agency,
   on modifiable only). */
.risk-detail-stats {
  display: flex;
  align-items: flex-start;
  gap: var(--space-xl);
}

.risk-stat {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.risk-stat-label {
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--text-dim);
}

.risk-stat-value {
  font-size: 28px;
  font-weight: 300;
  letter-spacing: -0.5px;
  line-height: 1;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

/* Modifiable is the agency figure — cyan. Secured (everything at goal)
   recedes to a flat green check; fate (nothing to move) dims out. The
   other two stats stay neutral ink so the one accent reads as "act here". */
.risk-stat-modifiable .risk-stat-value { color: var(--leverage); }
.risk-stat-secured .risk-stat-value { color: var(--green); font-weight: 400; }
.risk-stat-fate .risk-stat-value { 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 {
  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-sep {
  color: var(--text-dim);
}

/* Positive, informational status: this threat moved beyond the population
   baseline because one or more personal data layers contributed. It is NOT an
   edit door — attribution lives in Factors, inputs live behind Calibrate. */
.risk-detail-revised {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin: 0;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.1px;
  padding: 2px 8px;
  background: var(--green-bg);
  color: var(--green);
  border: 0;
  border-radius: var(--radius-sm);
}
.risk-detail-revised::before {
  content: "✓";
  font-size: 10px;
}

/* 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-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.65;
  letter-spacing: 0;
  color: var(--text-primary);
  margin: 0;
  /* First-line indent gives the block a deliberate "paragraph" start
     (reset on mobile, where the narrow column is already a tight measure). */
  text-indent: 1.5em;
}

/* 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;
}

/* Section-level Trends jump (Risks → Trends). Lives in the section
   head-top row opposite the title, so it reads as a peer-action on the
   section's content rather than a passive citation. Dim by default
   (the link is a navigational utility, not a call-to-action competing
   with the data below); accent surfaces on hover to confirm it's
   clickable. Matches the `lever-exp-trend-link` pattern. */
.risk-section-trends-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  border: 1px solid transparent;
  border-radius: 4px;
  background: transparent;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-dim);
  cursor: pointer;
  transition: color 0.12s, background 0.12s, border-color 0.12s;
}
.risk-section-trends-link:hover,
.risk-section-trends-link:focus-visible {
  color: var(--accent);
  background: var(--bg-surface);
  border-color: var(--border);
  outline: none;
}

.risk-section-dek {
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--text-secondary);
  letter-spacing: 0;
  line-height: 1.5;
  margin: 0;
}

.risk-section-icon {
  display: none;
}

/* "Sharpen this estimate" hook — a quiet text link that rides INLINE on the
   lifetime-risk meta row, right after "Revised by your data" (they're a pair:
   we used your data / add more to sharpen it). No pill, no badge, no dedicated
   row. It's a leverage affordance (make the number more accurate), so it reads
   cool/cyan, never as a severity flag. Conditional — only renders when there
   are fillable missing signals (see sharpen.js), so it's never dead space.
   Mobile reflows it to its own evenly-spaced block (see the 880 block). */
/* The opportunity CTA — a filled-soft cyan pill (the leverage/action axis), so
   it reads as "do this to improve," distinct from the green confirmation badge
   beside it. Sized to match the revised badge so the meta row stays even. */
.risk-detail-sharpen {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin: 0;
  padding: 2px 9px;
  background: var(--accent-soft);
  border: 0;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.3px;
  color: var(--accent);
  cursor: pointer;
  transition: background 160ms ease;
}
.risk-detail-sharpen:hover {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
}
.risk-detail-sharpen-edit {
  background: var(--bg-surface);
  color: var(--text-secondary);
}
.risk-detail-sharpen-edit:hover {
  color: var(--text-primary);
  background: var(--bg-hover);
}
/* Leading crosshair (precision = sharpen). Inherits the pill's cyan via
   currentColor; a hair thicker stroke so it reads at 12px. */
.risk-detail-sharpen-icon {
  flex: none;
  stroke-width: 2.25;
}

.risk-empty-line {
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.4px;
  font-style: italic;
}

/* Actions — ranked tactics, quiet until opened. This mirrors Goals'
   progressive disclosure but keeps a less table-like task-list shape:
   no column header, no boxed cards, just soft row boundaries and an
   impact value at the right. */
.risk-actions-list {
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* Inline Doctor / Rx cue inside the row title. This is categorical, not an
   alarm: neutral outline, small type, no red fill. The current biomarker
   values and goal dots own severity color elsewhere. */
.risk-action-row-tag {
  display: inline-block;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 1px 5px;
  margin-right: 8px;
  border-radius: 3px;
  border: 1px solid var(--border);
  color: var(--text-dim);
  background: var(--bg-surface);
  vertical-align: 1px;
  line-height: 1;
}

.risk-action-row-tag-urgent {
  color: var(--text-secondary);
}

.risk-action-row-tag-pharmaceutical {
  color: var(--text-dim);
}

/* Row — open-list treatment, not a spreadsheet. The leading chevron and
   soft row hover make it feel clickable; the absence of headers and hard
   vertical alignment keeps it from reading as a table. */
.risk-action-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  column-gap: var(--space-sm);
  align-items: start;
  padding: 12px 4px;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--border-subtle);
  border-radius: 0;
  font-family: var(--font-mono);
  text-align: left;
  cursor: pointer;
  transition: background 0.12s;
}

/* Row meter — the ranked list and number carry impact. Hide the old
   mini-bar by default; it was a second visual system competing with
   Goals' gauges directly above. */
.risk-action-row-meter {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0;
  width: auto;
  min-width: 38px;
  padding-top: 1px;
}

.risk-action-row-meter-num {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
  text-align: right;
  line-height: 1;
}

.risk-action-row-meter-bar {
  display: none;
}

.risk-action-row-meter-fill {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  background: var(--text-dim);
  opacity: 0.45;
  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-hover);
}

.risk-action-row-body {
  min-width: 0;
}

.risk-action-text {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  line-height: 1.45;
}

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

/* Disclosure caret on action rows — mirrors `.lever-row-caret` in the
   Goals section so both lists share the same expand affordance. */
.risk-action-row-caret {
  display: inline-block;
  color: var(--text-dim);
  font-size: 12px;
  line-height: 1.2;
  font-weight: 400;
  width: 8px;
  text-align: center;
  transform: rotate(0deg);
  transition: transform 0.15s ease-out, color 0.12s;
  align-self: start;
  padding-top: 2px;
}
.risk-action-row.expanded .risk-action-row-caret {
  transform: rotate(90deg);
  color: var(--accent);
}

/* Resting rows show only the scan layer. Opening a row restores the why,
   moved-goal chips, local controls, and Plan jump. */
.risk-action-row .risk-action-why,
.risk-action-row .target-chips,
.risk-action-row .risk-action-row-foot {
  display: none;
}
.risk-action-row.expanded {
  background: var(--bg-surface);
  padding-bottom: 14px;
  border-bottom-color: var(--border-subtle);
}
.risk-action-row.expanded .risk-action-why {
  display: block;
}
.risk-action-row.expanded .target-chips {
  display: flex;
  margin-top: 10px;
}
.risk-action-row.expanded .risk-action-row-foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-top: 12px;
}
.risk-action-row.expanded .risk-action-row-foot .action-controls {
  margin-top: 0;
  opacity: 1;
}
.risk-banked-action-list > .risk-action-row:last-child,
.risk-actions-dismissed-list > .risk-action-row:last-child {
  border-bottom: 0;
}
.risk-actions-more:not([open]) .risk-actions-more-list > .risk-action-row:last-child {
  border-bottom: 0;
}

/* Explicit Plan jump affordance. The row itself explains; the link
   jumps to the commit surface. */
.risk-action-plan-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  align-self: center;
}

/* ── 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;
}

/* On mobile the side-by-side header (title block + NVSR-style external
   link) crams the description into a ~250px column with phantom
   right-side whitespace; the external link sits flush-right in the
   reserved gutter. Stack the link below the title so the description
   spans the full width. */
@media (max-width: 720px) {
  .src-data-hd {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-xs);
  }
}

.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: 18px;
}
.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;
}

/* 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;
}

/* Touch viewports get no hover-scrub, and the single-row breakdown
   crams 5 swatches + percentages into ~340px — the part that goes
   ugliest on small screens. Hide it; EXPLORE ↗ carries detail-curious
   users into the modal. The y-axis labels also overflow the chart frame
   at this width (padL=60 of viewBox 1000 maps to ~19px on a 320px
   chart, smaller than the "100%" label). Drop them — the chart is
   obviously 0–100% and the gridlines stay for orientation. The EXPLORE
   button shrinks to a bare ↗ glyph in the corner; the whole chart-wrap
   becomes the tap target (data-mstream-explore on the wrap), matching
   iOS health-dashboard convention of tap-chart-to-expand. */
@media (max-width: 880px) {
  .mstream:not(.mstream-modal) .mstream-breakdown { display: none; }
  .mstream:not(.mstream-modal) .mstream-ylabel { display: none; }
  .mstream-chart-wrap[data-mstream-explore] { cursor: pointer; }
  .mstream:not(.mstream-modal) .mstream-explore-label { display: none; }
  .mstream:not(.mstream-modal) .mstream-explore-overlay {
    color: var(--text-dim);
    padding: 4px;
  }
}

/* Modal breakdown on a phone: the desktop single-row layout truncates
   cause names to nothing (min-width:0 ellipsis chips packed into a
   ~340px row), leaving a useless swatch+%-only readout. Restack as a
   vertical list: AT-anchor on top, then one row per cause with the name
   left-aligned and the percentage right-aligned. Full width per row, so
   names render in full. The y-axis labels (100%/75%/…) overflow the
   plot at this width for the same reason as the inline chart — hide
   them here too; gridlines keep the 0–100% scale legible. */
@media (max-width: 720px) {
  .mstream-modal .mstream-breakdown {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }
  .mstream-modal .mstream-bd-divider { display: none; }
  .mstream-modal .mstream-bd-items {
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
  }
  .mstream-modal .mstream-bd-item {
    display: flex;
    width: 100%;
    justify-content: flex-start;
    gap: 10px;
  }
  .mstream-modal .mstream-bd-name { flex: 1; }
  .mstream-modal .mstream-bd-pct { flex: 0 0 auto; }
  /* Empty padding slots collapse to nothing on desktop; on the vertical
     list they'd leave phantom blank rows. Hide them outright. */
  .mstream-modal .mstream-bd-item-empty { display: none; }
  .mstream-modal .mstream-ylabel { display: none; }
}

/* ── Threat density curve (per-threat drill chart) ────────────────────
   A single filled area: this cause's survival-weighted death-density over
   future age, cresting at its personalized peak (the dot). Reuses the
   mstream-explore overlay button. Like the stream, axis labels live in an
   HTML overlay because the SVG uses preserveAspectRatio="none". */
.tdens {
  position: relative;
  width: 100%;
  margin: 2px 0 var(--space-md);
}
.tdens-head {
  display: flex;
  align-items: baseline;
  justify-content: flex-start;
  gap: 6px;
  margin-bottom: 6px;
}
/* Chart title — names what the curve is, now that peak age moved to the
   header vitals strip. Figure-tier (quiet, not a bold section header): reads
   as a continuation of the head above it. Shown on both viewports. */
.tdens-title {
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.2px;
  line-height: 1.45;
  margin: 0;
}
.tdens-title[data-threat-timeline] {
  cursor: pointer;
}
.tdens-title[data-threat-timeline]:hover {
  color: var(--accent);
}
/* Peak-age annotation — figure-tier, mobile-only. Desktop hides it because
   the header vitals strip already carries PEAK AGE; on mobile that strip is
   gone, so this is where peak age lives (and it annotates the chart's crest). */
.tdens-peak {
  display: none;
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.2px;
  line-height: 1.45;
}
.tdens-peak[data-threat-timeline] {
  cursor: pointer;
}
.tdens-peak[data-threat-timeline]:hover {
  color: var(--accent);
}
.tdens-caption-value {
  color: var(--text-primary);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.tdens-explore {
  flex: none;
  padding: 1px 3px;
  color: var(--text-dim);
  font-weight: 500;
  letter-spacing: 0;
  transform: translateY(1px);
}
.tdens-explore:hover,
.tdens-explore:focus-visible {
  color: var(--accent);
  background: transparent;
  outline: none;
}
.tdens-chart-wrap {
  position: relative;
  width: 100%;
  padding: 12px 0 2px;
  cursor: pointer;
}
.tdens-chart {
  display: block;
  width: 100%;
  height: 118px;
  overflow: visible;
}
.tdens-area {
  opacity: 0.13;
}
.tdens-line {
  stroke-width: 1.75;
  vector-effect: non-scaling-stroke;
  opacity: 0.95;
  filter: drop-shadow(0 1px 0 rgba(255, 255, 255, 0.75));
}
.tdens-axis {
  stroke: var(--border);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.tdens-xtick {
  stroke: var(--border);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
/* Now anchor — the curve already starts at the user's age, so this is a
   baseline tick, not a full-height rule. Cyan stays reserved for "you",
   but no longer splits the figure into two visual columns. */
.tdens-now-rule {
  stroke: var(--accent);
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
  opacity: 0.75;
}
.tdens-overlay {
  position: absolute;
  inset: 12px 0 2px;
  pointer-events: none;
  font-family: var(--font-mono);
}
.tdens-xlabel {
  position: absolute;
  bottom: 2px;
  transform: translateX(-50%);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.tdens-xlabel-now {
  color: var(--accent);
  font-weight: 600;
  transform: translateX(-2px);
}
.tdens-peak-guide {
  stroke-width: 1;
  stroke-dasharray: 2 3;
  vector-effect: non-scaling-stroke;
  opacity: 0.42;
}
/* 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 ═══════════ */
/* The boxed threat-tag chip — the chrome the inline-text variants
   (.plan-row-threat / .plan-card-threat) strip back. Buttons; the click
   handler navigates to that threat in Risks. */
.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;
}

/* Section eyebrow for the goals readout. Hidden on desktop — there the
   strip sits inline beside the citations and the pill chrome already
   frames it. The mobile media query switches it on, where the strip
   restacks into a labeled one-per-row readout. */
.target-pills-label {
  display: none;
}

/* Value side (current → goal) is wrapped so mobile can right-align it as
   one unit. On desktop the wrapper is transparent — `contents` drops it
   so the current/arrow/goal spans flow straight into the pill's flex/
   inline-block layout exactly as before, leaving desktop untouched. */
.target-pill-value {
  display: contents;
}

.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;
}

/* Goal glyph leading the pill — the same icon that leads each Risks lever
   row, so a goal reads as one recognizable "character" wherever it's
   referenced. Neutral (--text-dim): a quiet wayfinding mark, not a third
   cyan element (the goal value already wears --leverage here). The stroked
   line shape, not color, is what marks it as a leverage-axis glyph.
   align-self:center overrides the pill's baseline alignment so the square
   glyph centers against the text rather than hanging from its baseline. */
.target-pill-icon {
  display: inline-flex;
  align-items: center;
  align-self: center;
  flex: none;
  width: 13px;
  height: 13px;
  color: var(--text-dim);
  vertical-align: -2px;
}
.target-pill-icon svg { display: block; }
/* Binary pills are inline-block (so long rules wrap), not flex — the pill's
   `gap` doesn't apply, so the icon needs its own trailing margin. */
.target-pill-binary .target-pill-icon { margin-right: 6px; }

.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 — single signal: the current value goes green ("this
   number is good"). Pill chrome stays neutral (no green-bg, no green
   border tint) so a row of mostly-at-goal pills doesn't flood the page
   with green. The earlier version filled the whole pill green; in
   secured-action contexts where most pills are at-goal, that turned
   every strip into a green wall. The goal value drops to a structural
   tone here — there's no leverage left to chase when the lever is met,
   so the threshold reads as background context, not as a call to action. */
.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. Flow children as
   inline text (display: inline-block) instead of inline-flex so a long
   rule (the colonoscopy "current ≤10y, or FIT/sDNA ≤1y, age ≥45"
   formulation) wraps uniformly across the chip width — not as a tall
   right-side tower next to a fixed label column. Max-width: 100% caps
   the chip at the parent row width so it never escapes its container,
   but lets short binaries (Diet pattern → Mediterranean) stay single-
   line where they fit. */
.target-pill-binary {
  display: inline-block;
  white-space: normal;
  max-width: 100%;
  line-height: 1.5;
}
.target-pill-binary .target-pill-goal {
  font-weight: 400;
}

/* Truncation behavior: pills past the 4th are hidden until the toggle
   expands the row. Selector uses :nth-of-type so the button (a different
   element type) isn't caught by the count. Only applies when the wrapper
   carries data-target-overflow="true" — short strips render without the
   toggle and skip this rule entirely. */
.target-pills[data-target-overflow="true"]:not(.target-pills-expanded) > .target-pill:nth-of-type(n+5) {
  display: none;
}

.target-pills-toggle {
  background: none;
  border: 0;
  padding: 2px 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  color: var(--text-dim);
  letter-spacing: 0.3px;
  cursor: pointer;
  border-radius: 3px;
  align-self: center;
}
.target-pills-toggle:hover {
  background: var(--bg-surface);
  color: var(--text-secondary);
}

/* Two labels in the DOM; CSS swaps visibility based on the wrapper's
   expanded state — same pattern as the hero card's Show/Hide details
   toggle. Avoids re-rendering or re-binding on every click. */
.target-pills-toggle-label[data-state="expanded"] { display: none; }
.target-pills.target-pills-expanded .target-pills-toggle-label[data-state="collapsed"] { display: none; }
.target-pills.target-pills-expanded .target-pills-toggle-label[data-state="expanded"] { display: inline; }

/* Plan action items have more breathing room; pills sit a touch lower. */
.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 dot is the entire signal. The label
   stays at secondary weight; promoting it to primary + bold turned
   every action row into a wall of dark chips and made the dot
   redundant. Now the dot carries the warning and the label just
   names what it points at. */
.target-chip-off .target-chip-dot {
  background: var(--amber);
}
.target-chip-off .target-chip-label {
  color: var(--text-secondary);
  font-weight: 500;
}

/* 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: neutral fill — direction is already carried by position (left
   vs right of the spine) and magnitude by length. Coloring the bar
   doubles up on the position signal and competes with the YOU
   verdict, which is the row that actually carries warm/cool meaning.
   Flat at the axis end, softly rounded at the leading edge so it
   still reads as a force extending outward. */
.risk-baseline-row-bar {
  position: absolute;
  top: 50%;
  height: 5px;
  transform: translateY(-50%);
  width: var(--bar-width);
  background: var(--text-dim);
  opacity: 0.45;
  z-index: 1;
}
.risk-baseline-row.is-up .risk-baseline-row-bar {
  left: 50%;
  border-radius: 0 1.5px 1.5px 0;
}
.risk-baseline-row.is-down .risk-baseline-row-bar {
  right: 50%;
  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;
  color: var(--text-secondary);
}

/* 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: 10px 13px;
  font-size: 12px;
  line-height: 1.5;
  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.28;
}
.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;
  /* Instrument table: every row is name · standing · stakes on shared tracks,
     so values, gauges, and the +Xy line up as columns down the list.
     `.lever-standing` is display:contents (below) — its children (value,
     gauge / state chip / ghost track) drop straight into these tracks, which
     is what makes every gauge ONE uniform width and every value share a left
     edge, instead of each row sizing its own cell.
       1 Target  · 2 value/chip · 3 gauge · 4 If hit
     The gauge column FLEXES (minmax 220px → 1fr): it has a generous floor so a
     short threat still reads as an instrument, and it soaks up the panel's
     remaining width so the table fills its container instead of packing left
     with a dead-air void on the right. The +Xy stakes ride col 4 at the right
     edge; the value (col 2) stays content-width so it pairs tightly with its
     gauge. The column mins (≥ the widest name / value) hold each column to one
     width down the list, so values, gauges, and the +Xy line up as columns.
     A roomy column-gap (lg, not md) keeps every junction — name↔value,
     value↔gauge, gauge↔years — from cramping. */
  grid-template-columns:
    minmax(150px, max-content) minmax(100px, max-content) minmax(220px, 1fr) max-content;
  align-items: center;
  column-gap: var(--space-lg);
  padding: 12px 4px;
  border-bottom: 1px solid var(--border-subtle);
  font-variant-numeric: tabular-nums;
}

/* Column placement for the shared grid. Name anchors col 1, the standing
   content fills cols 2–3, the +Xy stakes anchor col 4. The standing children
   inherit these via display:contents. */
.lever-row-name { grid-column: 1; }
.lever-standing-value,
.lever-standing-chip,
.lever-standing-latent-label { grid-column: 2; justify-self: start; min-width: 0; }
.lever-standing-bar,
.lever-standing-track { grid-column: 3; }
.lever-row-years { grid-column: 4; }

/* Checklist composition (all-binary threat, e.g. Cancer's screenings) — no
   gauge column. Target · state chip · stakes, packed tight so there's no
   empty gauge track leaving a void between the chip and the +Xy. Desktop-only:
   these outrank the mobile .lever-row grid on specificity, so they must not
   apply below the phone breakpoint (the 880px block restacks every row). */
@media (min-width: 881px) {
  .risk-levers-checklist .lever-row {
    grid-template-columns: minmax(150px, max-content) minmax(150px, max-content) max-content;
  }
  .risk-levers-checklist .lever-row-standing { grid-column: 2 / 3; }
  .risk-levers-checklist .lever-row-years { grid-column: 3; }
}

/* 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; }

/* Goal icon — the leverage-axis glyph that leads each lever name, the cool
   stroked counterpart to the threat axis's warm filled cod-icons. The
   stroke-vs-fill shape contrast carries the axis distinction, so the glyph
   sits NEUTRAL (--text-dim) at rest — wayfinding that recedes below the
   label, not a second cyan element competing with the +Xy and goal values.
   It picks up --leverage only when its row is expanded, mirroring how a
   threat cod-icon goes warm only when its rail row is selected: on both
   axes, color is a state signal, not the default. */
.lever-row-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 15px;
  height: 15px;
  margin-right: 7px;
  color: var(--text-dim);
  transition: color 0.12s;
  vertical-align: -3px;
}
.lever-row-icon svg { display: block; }
.lever-row[data-target-id]:hover .lever-row-icon { color: var(--text-secondary); }
.lever-item.expanded > .lever-row .lever-row-icon { color: var(--leverage); }
/* Header spacer — invisible glyph reserving the icon slot so "Target" aligns
   with the labels below (mirrors .lever-row-caret-spacer). */
.lever-row-icon-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-standing,
.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;
}
/* "Status" header spans the value + gauge tracks so it sits above the
   standing content (which starts at col 2). */
.lever-row-standing { grid-column: 2 / 4; }
.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;
}

/* ── Standing cell — the morphing middle column ──────────
   One flex container; its children differ by lever type, but the column
   track is shared so name / +Xy stay aligned down the list.
     measured: value · gap gauge (elastic) · goal
     binary:   state chip · prose goal
     latent:   "No reading" · ghost track · goal target */
/* display:contents — the standing cell has no box of its own; its children
   (value, gauge / state chip / ghost track) become grid items of .lever-row
   so they align to the shared column tracks. (Mobile re-boxes this to a flex
   row — see the 880px block.) */
.lever-standing {
  display: contents;
}

/* Measured value — text-primary; severity reads off the gauge marker on
   desktop (value stays neutral). On mobile the bar is hidden, so the value
   itself takes the severity color (sev classes in the mobile block). */
.lever-standing-value {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  flex-shrink: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: 0.2px;
}
.lever-standing-num { line-height: 1; }
.lever-standing-unit {
  font-size: 10px;
  font-weight: 400;
  color: var(--text-dim);
  letter-spacing: 0.2px;
}

/* Inline clinical gauge — the range bar promoted out of the expansion into
   the row. It's a grid item in the shared gauge column (col 3), so every
   gauge is one uniform width down the list; the inner range stretches to fill
   it. The precise goal is no longer shown beside it — it lives in the
   expansion. */
.lever-standing-bar {
  display: flex;
  align-items: center;
  min-width: 0;
}
.lever-standing-bar .scorecard-range {
  flex: 1;
  min-width: 0;
}
/* Hide the clinical threshold label ("100" / "75") in the row — it stacked
   below the bar, which pushed the bar above the row's vertical center, and
   the precise numbers now live in the expansion. Leaves a clean, centered
   marker-on-track gauge. */
.lever-standing-bar .range-labels { display: none; }

.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); }

/* Binary state chip — warm dot + short label (severity axis), sitting in the
   value column (col 2) so the eye scans one standing column whether the lever
   is continuous or binary. The label is the authored "no" option ("Standard",
   "Rarely") or a state word (Overdue / No record). The prose definition-of-
   done it reaches for is disclosed in the expansion, not on the row. */
.lever-standing-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  white-space: nowrap;
}
.lever-standing-chip-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}
/* Confirmed off-goal — warm (severity axis). */
.lever-standing-chip-off { color: var(--amber); }
.lever-standing-chip-off .lever-standing-chip-dot { background: var(--amber); }
/* Nothing on record — latent, neutral (mirrors the "No reading" ghost
   state); a hollow dot reads "unmeasured," not "bad." */
.lever-standing-chip-unknown { color: var(--text-dim); }
.lever-standing-chip-unknown .lever-standing-chip-dot {
  background: transparent;
  box-shadow: inset 0 0 0 1.5px var(--text-dim);
}

/* Latent (unmeasured continuous) — quiet "No reading" with a ghost track
   echoing the gauge's shape, so the column reads "empty, not yet measured"
   rather than blank. Solid faint rule (no dashed borders, per house style). */
.lever-standing-latent-label {
  flex-shrink: 0;
  font-size: 12px;
  font-weight: 400;
  color: var(--text-dim);
}
.lever-standing-track {
  height: 2px;
  border-radius: 1px;
  background: var(--border-subtle);
}

/* "If hit" years: drop the leverage cyan. Rows are already sorted by
   impact and a long list of cyan +Xy numbers becomes wallpaper instead
   of signal. Neutral keeps the column scannable; the threat-level
   headline and Banked footer are the surfaces that get to be loud. */
.lever-row-years {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  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;
}

/* Top row of the expansion: the Goal on the left, the per-lever Trend jump on
   the right (so the trend link rides this line instead of orphaning on its own
   row). Baseline-aligned; on binary levers there's no trend link, so the Goal
   simply sits alone. */
.lever-exp-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
}

/* Goal line — the precise threshold / definition-of-done, moved off the row
   to here. A dim uppercase "Goal" label leads the target value so opening a
   lever immediately states what it reaches for, next to the tactics below. */
.lever-exp-goal {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: 0.2px;
}
.lever-exp-goal-label {
  margin-right: 8px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* Range bar inside the lever expansion. Laid out as a flex row so the
   inline "Trend ↗" jump can ride at the right end, vertically aligned
   with the bar instead of dangling below it as a detached link. The
   bar still claims the available width via `.scorecard-range`'s
   flex: 1. */
.lever-exp-gauge {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding-bottom: 4px;
}
.lever-exp-gauge .scorecard-range {
  display: flex;
  flex: 1;
  min-width: 0;
}

/* Per-lever Trend jump — small, anchored to the right of the gauge row.
   Quiet at rest (text-dim, no chrome), warms to accent on hover. Mirrors
   `.baseline-cite`'s typographic weight so it reads as a tertiary action
   alongside the range bar, not a CTA. */
.lever-exp-trend-link {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  flex-shrink: 0;
  padding: 2px 4px;
  border: 0;
  background: transparent;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-dim);
  cursor: pointer;
  border-radius: 3px;
  transition: color 0.12s, background 0.12s;
}
.lever-exp-trend-link:hover,
.lever-exp-trend-link:focus-visible {
  color: var(--accent);
  background: var(--bg-surface);
  outline: none;
}

.lever-exp-desc {
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-secondary);
  letter-spacing: 0;
}

/* 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;
}

/* Lever evidence + sources now render via the shared, collapsed-by-default
   citations block (.cite* in components-base.css → js/views/_citations.js),
   replacing the old always-expanded .lever-exp-evidence-* treatment. */

/* 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);
}

/* Share sits just left of close. display:contents on the wrapper keeps
   both children behaving as direct, absolutely-positioned head elements
   (close stays pinned to the right edge; share offsets left of it). */
.lc-head-actions { display: contents; }
.lc-share {
  position: absolute;
  right: calc(var(--space-lg) + 38px);
  top: 18px;
  width: 28px;
  height: 28px;
  border: none;
  background: none;
  color: var(--text-dim);
  cursor: pointer;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.12s;
}
.lc-share:hover {
  color: var(--accent);
  background: var(--bg-hover);
}
.lc-share.is-busy { opacity: 0.45; pointer-events: none; }
.lc-share:focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 2px;
}

/* 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;
  /* Lift the grid above the side rails (.lc-axis-y / .lc-axis-y-right, which
     are later flex siblings). The now/end/legacy markers live inside the grid
     and overflow its edges into the rails; without this the year/age labels —
     painted later in DOM order — sit on top of the marker pills (e.g. "2040"
     bleeding through "ELON MUSK FOUNDED SPACEX"). */
  z-index: 1;
}

/* 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;
  /* Above every cell — including the z-index:3 legacy/focus cells — so a flag's
     opaque pill is never undercut. Markers are ordered among themselves by DOM
     order (legacy is last, so the hovered easter-egg flag wins over now/end). */
  z-index: 5;
}
.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;
}

/* Legacy marker — the easter-egg reveal, in the NOW / end-marker idiom: a small
   gold flag pinned above the hovered figure's band. Page-bg pill so it stays
   legible over any cell wash; hidden (opacity 0) until a band is hovered. Drives
   no layout — absolutely positioned in the grid like the other markers, so
   revealing it never shifts the chart. */
.lc-marker-legacy {
  color: var(--legacy);
  padding: 2px 5px;
  background: var(--bg-page);
  border-radius: 3px;
  /* Looser than a name-tag, tighter than the NOW/end ticks — the flag carries
     a short sentence, so the base 1.6px tracking would run it too wide. */
  letter-spacing: 0.5px;
  opacity: 0;
  /* Fade-out matches the cell cool-down so the flag lingers with the ember
     rather than snapping away — though shorter, since a stale name pinned over
     the grid reads as clutter sooner than a soft glow does. */
  transition: opacity 0.7s ease-out;
}
/* Reveal is quick — the override beats the slow fade-out above. */
.lc-marker-legacy-on { opacity: 1; transition: opacity 0.12s ease-out; }

.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);
  /* rgba over future-bg rather than color-mix. --red is #dc2626 (220,38,38);
     peak alpha 0.5 matches the prior color-mix 50%. Only ~5 rows of cells, so
     a gradient image here is cheap — unlike the thousands of threat cells. */
  background-image: linear-gradient(
    rgba(220, 38, 38, calc(var(--lc-cell-intensity, 1) * 0.5)),
    rgba(220, 38, 38, calc(var(--lc-cell-intensity, 1) * 0.5))
  );
}

/* 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 {
  /* Flat fallback — applyState overwrites background-color inline with the
     exact threatRGB-over-future-bg composite per cell. Kept as a plain solid
     (no gradient image) so the grid re-rasterizes cheaply on hover; the old
     gradient(color-mix(...)) wash was the threat-overlay hover jank. */
  background-color: var(--lc-future-bg);
}

/* 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);
}

/* Legacy bands — the "ghosts in the grid" easter egg. A historical figure's
   defining moment marks a ~15-cell band at the fractional age they reached it.
   No always-on marker — the band is invisible until hovered (a true easter
   egg); hovering any cell in it adds .lc-cell-legacy-lit to the whole band,
   igniting it in gold and revealing the echo. Gold is a third semantic, clear
   of the red/amber severity axis and the cyan/green leverage axis.

   Thermal afterglow: band cells (.lc-cell-legacy, set on every band member)
   carry a slow box-shadow transition — that IS the cool-down. When the cursor
   leaves and JS strips .lc-cell-legacy-lit, the gold glow eases back to nothing
   over ~1.6s, so a fast sweep leaves a fading ember the user can retrace. The
   elevated z-index lives on the membership class (not -lit) so the outer glow
   still paints over neighbors mid-cool, after -lit is gone. */
.lc-cell-legacy {
  position: relative;
  /* Above neighbors so the gap-bridging fill paints over their edges. */
  z-index: 3;
}
/* The whole highlight is a single gold overlay — no box-shadow, so it stays
   exactly one cell tall instead of blooming a soft ring above/below the row.
   It reads against any background (incl. the inline threat-color fill a class
   rule can't touch) because it sits on top, and it's extended horizontally by
   one gap each side so a contiguous band merges into one clean bar rather than
   dotted cells. Opacity carries the thermal afterglow: slow 1.6s cool here,
   near-instant heat on the -lit override below — so a fast sweep leaves a
   fading ember to retrace. */
.lc-cell-legacy::after {
  content: '';
  position: absolute;
  inset: 0 calc(-1 * var(--lc-gap));
  border-radius: inherit;
  background: var(--legacy-glow);
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.6s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.lc-cell-legacy-lit::after {
  opacity: 1;
  transition: opacity 0.1s ease-out;
}

/* ── 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 {
  /* Right-aligned within canvas-inner but pulled left by the right-rail's
     width so its trailing edge lands on the grid's col-52 edge, not the year
     column. The bottom margin opens space before the WK column headers. */
  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-legend-item:disabled,
.lc-legend-item-disabled {
  opacity: 0.42;
  cursor: default;
}
.lc-legend-item:disabled:hover,
.lc-legend-item-disabled:hover {
  color: var(--text-dim);
  background: transparent;
}
.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); }
}

@media (max-width: 720px) {
  .lc-frame {
    width: 100vw;
    max-width: 100vw;
    min-width: 0;
    grid-template-rows: auto minmax(0, 1fr) auto;
  }

  .lc-head,
  .lc-canvas,
  .lc-foot {
    box-sizing: border-box;
    width: 100%;
    max-width: 100vw;
    min-width: 0;
  }

  .lc-head {
    padding: 14px 44px 12px;
    min-height: 118px;
    overflow: hidden;
  }

  .lc-head-mark {
    left: 18px;
    top: 17px;
    padding: 3px;
    margin: -3px;
  }

  .lc-head-mark-word {
    display: none;
  }

  .lc-head-mark svg {
    width: 20px;
    height: 20px;
  }

  .lc-close {
    right: 18px;
    top: 15px;
    width: 32px;
    height: 32px;
  }

  .lc-hero {
    max-width: min(100%, 260px);
    gap: 2px;
  }

  .lc-hero-kicker {
    font-size: 10px;
    letter-spacing: 2.2px;
    line-height: 1.25;
    margin-bottom: 4px;
  }

  .lc-hero-num {
    font-size: 48px;
    letter-spacing: -1px;
  }

  .lc-hero-label {
    font-size: 11px;
    letter-spacing: 1.8px;
    margin-top: 6px;
  }

  .lc-hero-meta {
    font-size: 11px;
    letter-spacing: 0.4px;
    margin-top: 4px;
  }

  .lc-canvas {
    --lc-cell: 3px;
    --lc-gap: 1px;
    --lc-axis-w: 0px;
    overflow: hidden;
    align-items: center;
    padding: 8px 0 6px;
  }

  .lc-season {
    -webkit-mask-image: linear-gradient(
      to right,
      rgba(0,0,0,1) 0%,
      rgba(0,0,0,0.72) 18%,
      rgba(0,0,0,0.2) 38%,
      rgba(0,0,0,0.2) 62%,
      rgba(0,0,0,0.72) 82%,
      rgba(0,0,0,1) 100%
    );
            mask-image: linear-gradient(
      to right,
      rgba(0,0,0,1) 0%,
      rgba(0,0,0,0.72) 18%,
      rgba(0,0,0,0.2) 38%,
      rgba(0,0,0,0.2) 62%,
      rgba(0,0,0,0.72) 82%,
      rgba(0,0,0,1) 100%
    );
  }

  .lc-canvas-inner {
    width: calc((52 * var(--lc-cell)) + (51 * var(--lc-gap)));
    gap: 5px;
    padding: 7px 7px 8px;
    background: rgba(255, 255, 255, 0.58);
    box-shadow:
      0 0 0 1px rgba(255, 255, 255, 0.26),
      0 18px 54px rgba(255, 255, 255, 0.28);
    backdrop-filter: blur(1.5px);
    -webkit-backdrop-filter: blur(1.5px);
  }

  .lc-readout {
    display: none;
  }

  .lc-canvas-header,
  .lc-canvas-body {
    gap: 0;
  }

  .lc-axis-y,
  .lc-axis-y-right,
  .lc-axis-y-spacer,
  .lc-axis-y-spacer-right {
    display: none;
  }

  .lc-axis-x {
    width: 100%;
    height: 13px;
  }

  .lc-axis-x-tick {
    font-size: 10px;
    line-height: 1;
    letter-spacing: 0.2px;
  }

  .lc-cell {
    border-radius: 1px;
  }

  .lc-grid {
    background: rgba(255, 255, 255, 0.18);
  }

  .lc-marker {
    font-size: 9px;
    letter-spacing: 1px;
  }

  .lc-marker-now,
  .lc-marker-end,
  .lc-marker-legacy {
    padding: 2px 4px;
  }

  .lc-foot {
    gap: 9px;
    padding: 14px 0 max(12px, env(safe-area-inset-bottom));
    overflow: hidden;
  }

  .lc-legend {
    gap: 18px;
    font-size: 10px;
    letter-spacing: 1.2px;
  }

  .lc-legend-item {
    padding: 3px 5px;
    margin: -3px -5px;
  }

  .lc-swatch {
    width: 8px;
    height: 8px;
  }

  .lc-controls {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    gap: 8px;
  }

  .lc-overlay-row {
    width: 100%;
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 8px;
    justify-content: flex-start;
    box-sizing: border-box;
    padding-left: 18px;
    padding-right: 0;
    overflow: hidden;
  }

  .lc-controls-label {
    flex: 0 0 auto;
    font-size: 10px;
    letter-spacing: 1.5px;
  }

  .lc-chips {
    min-width: 0;
    flex: 1 1 auto;
    display: flex;
    flex-wrap: nowrap;
    justify-content: flex-start;
    gap: 5px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    padding: 0 18px 2px 0;
    scrollbar-width: none;
  }

  .lc-chips::-webkit-scrollbar {
    display: none;
  }

  .lc-chip {
    flex: 0 0 auto;
    scroll-snap-align: center;
    padding: 7px 11px;
    font-size: 10px;
    letter-spacing: 1.2px;
    white-space: nowrap;
  }

  .lc-plan-row {
    gap: 9px;
  }

  .lc-plan-prefix {
    font-size: 9px;
    letter-spacing: 1.3px;
  }

  .lc-plan-switch-track {
    width: 28px;
    height: 14px;
  }

  .lc-plan-delta {
    font-size: 9px;
    padding: 2px 6px;
  }
}

@media (max-width: 430px) {
  .lc-head {
    min-height: 112px;
    padding-top: 12px;
    padding-bottom: 10px;
  }

  .lc-hero-num {
    font-size: 44px;
  }

  .lc-canvas {
    padding-top: 2px;
    padding-bottom: 2px;
  }

  .lc-foot {
    gap: 8px;
    padding-top: 12px;
  }
}

/* ─── 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;
}

/* ── Screening-result control ──────────────────────────────────────
   The forked Negative/Positive (+treatment), CAC band, or cancer
   Clear/Finding control. Stacks a primary row and an optional secondary
   (treatment / finding) row; reuses .sr-btn pills for the buttons. */
.screening-result-control {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.screening-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.screening-row-sub {
  padding-top: 6px;
  border-top: 1px solid var(--border-subtle);
}
.screening-sub-label {
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.2px;
}
.screening-ack {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--amber);
  align-self: flex-start;
}

/* Calibrate-sheet variant. A screening sits inline like every other profile row —
   name left, result pills right — by inheriting .profile-row's row layout (no
   column override). When a follow-up appears (Positive → treatment, a cancer
   Finding's router, the diagnosed ack) it stacks as a tidy SECOND LINE on the
   right, under the primary pills, rather than a full-width block — so the row
   stays consistent with its neighbours and the name never sits alone on a line. */
.profile-row-screening {
  align-items: center;
}
.profile-row-screening-control {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}
.profile-row-screening-control .screening-row {
  gap: 6px;
}
/* The follow-up reads as a quiet second line here, not a divided section — drop
   the lever-exp divider/padding so it nests cleanly under the primary pills. */
.profile-row-screening-control .screening-row-sub {
  padding-top: 0;
  border-top: none;
}
.profile-row-screening-control .screening-ack {
  align-self: flex-end;
}

.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 controls (Mark done / Dismiss) ─────────────────────────
   Quiet by default. Sit at the bottom of an action detail, 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;
}

/* Report action — a reveal toggle (label + chevron) leads with Dismiss
   beside it on the summary row; the type-appropriate control (pills / number
   / date / tuner) discloses below on click, so the Plan's featured cards stay
   scannable instead of showing every editor by default. */
.action-controls-report .action-report-editor {
  display: none;
  width: 100%;
  margin-top: 8px;
}
.action-controls-report.is-editing .action-report-editor {
  display: block;
}

/* Disclosure-state cue on the report toggle, matching "Show details ▾" so
   both read as collapsible toggles with a clear open/closed state — not a
   terminal action vs. a disclosure. */
.action-disclose-chevron {
  display: inline-block;
  margin-left: 5px;
  font-size: 10px;
  line-height: 1;
  transition: transform 0.15s;
}
.action-controls-report.is-editing .action-disclose-chevron {
  transform: rotate(180deg);
}

.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);
}

/* Filled-primary CTA on the Plan hero's expanded action detail. The spacious
   detail card (RATIONALE / GOALS / SOURCES) is the desktop analog of the Risks
   bottom sheet, so it carries the same confident filled commit — accent fill
   for the measure/report primary, green for the ✓ Done commit — over a quiet
   "Not for me" secondary. Scoped to .plan-card-controls-expanded so the denser
   backlog list rows (.plan-row-details) keep their outlined controls. Mirrors
   the .risks-sheet primary treatment in the ≤880px block. */
.plan-card-controls-expanded .action-controls {
  gap: 8px 14px;
  /* No top margin: .plan-card-details already spaces this block from SOURCES
     with its --space-md flex gap, so it reads as a peer of the labeled
     sections rather than floating far below them. */
  margin-top: 0;
}
.plan-card-controls-expanded .action-report-toggle,
.plan-card-controls-expanded .sr-btn-done {
  border: 0;
  border-radius: var(--radius-md);
  padding: 11px 20px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.4px;
  color: #fff;
  background: var(--accent);
  transition: background 0.15s, transform 0.12s;
}
.plan-card-controls-expanded .action-report-toggle:hover,
.plan-card-controls-expanded .sr-btn-done:hover { background: #0e7490; }
.plan-card-controls-expanded .action-report-toggle:active,
.plan-card-controls-expanded .sr-btn-done:active { transform: scale(0.98); }
.plan-card-controls-expanded .sr-btn-done { background: var(--green); }
.plan-card-controls-expanded .sr-btn-done:hover { background: #047857; }

/* Primary and "Not for me" stay paired on one row (natural DOM order, no
   reordering); the reveal editor wraps full-width below BOTH via the base
   .action-report-editor width:100%. Avoids orphaning "Not for me" beneath
   the disclosed control — which reads disconnected on a phone. */
.plan-card-controls-expanded .sr-btn-ghost {
  padding: 11px 14px;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
  border-radius: var(--radius-md);
}
.plan-card-controls-expanded .sr-btn-ghost:hover {
  color: var(--text-primary);
  background: var(--bg-hover);
}

/* 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).

   The section break is owned by WHITESPACE, not a hairline. When collapsed,
   these heads are bare (no enclosing card), so a border-top floats as a lone
   rule against the carded backlog above it — a doubled boundary (the backlog
   card's own bottom edge sits ~one gap away). The space-xl margin — the
   shared rhythm with .plan-backlog — carries the boundary instead, on every
   viewport. When expanded, the `.plan-disclosure-list` panel draws its own
   border, so the section never lacks definition where content actually is. */
.plan-secured,
.plan-dismissed {
  margin-top: var(--space-xl);
}
.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;
}

/* Same panel chassis as .plan-rows (the backlog container). Wrapping the
   secured/dismissed list in the same chrome makes the page read as one
   action-list visual system in different states, not two disjoint
   sections. No rank gutter inside — rank is moot once banked — so
   content runs to the panel inset on both sides.

   Holds the same scan-thin `.plan-row` <details> rows the backlog uses
   (renderSecuredRow), so secured / dismissed read as the same click-in
   action list, just banked / set aside. Row dividers + the last-row
   suppression come from the shared `.plan-row` rules; this panel only
   supplies the card chrome + open animation. */
.plan-disclosure-list {
  margin-top: var(--space-md);
  animation: expand-in 0.15s ease-out;
  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);
}
/* Mobile: shed the card chrome so secured / dismissed rows go flat and
   hairline-divided, matching the backlog (.plan-rows flattens in
   components-base.css). Authored here — not in that file's mobile block —
   because the base border above loads after it and would otherwise win. */
@media (max-width: 720px) {
  .plan-disclosure-list {
    margin-top: 0;
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    overflow: visible;
  }
}

/* ═══════════ WEEKLY-PLAN DELIVERY CARD (fake-front) ═══════════
   Plan-view footer CTA — a feature card that presents recurring delivery
   of this action plan (inbox / push) as a near-shipping feature. It is a
   fake door: the entire card is one button that opens the iOS waitlist
   modal; the channel chips are presentational only. Cool/accent leverage
   tone (this is agency — "get your plan"), not a warning. The topbar
   variant follows further down. */
.ios-notify-wrap {
  margin-top: var(--space-lg);
  display: flex;
  justify-content: center;
}
.plan-deliver {
  font-family: inherit;
  width: 100%;
  max-width: 440px;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-md) var(--space-lg);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  cursor: pointer;
  text-align: left;
  transition: border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease;
}
.plan-deliver:hover {
  border-color: var(--accent);
  transform: translateY(-1px);
  box-shadow: 0 12px 28px -16px rgba(8, 145, 178, 0.45);
}
.plan-deliver:active { transform: translateY(0); }

.plan-deliver-glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--leverage-bg);
  color: var(--leverage);
  flex-shrink: 0;
}
.plan-deliver-glyph svg { width: 18px; height: 18px; }

.plan-deliver-copy {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.plan-deliver-title {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.3px;
  color: var(--text-primary);
}
.plan-deliver-channels {
  display: flex;
  gap: var(--space-xs);
  margin-top: 6px;
}
.plan-deliver-chip {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-dim);
  background: var(--bg-surface);
}
.plan-deliver-chip-on {
  border-color: var(--leverage);
  color: var(--leverage);
  background: var(--leverage-bg);
}

.plan-deliver-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--accent);
}
.plan-deliver-arrow {
  font-size: 14px;
  transition: transform 0.18s ease;
}
.plan-deliver:hover .plan-deliver-arrow { transform: translateX(3px); }

/* Phone: drop the weekly-delivery prompt from a card to a quiet footer ROW.
   It's a secondary, cross-product CTA (the iOS waitlist) — on a phone the
   bordered, filled, shadowed card was the single heaviest element on the
   screen and anchored the eye to the bottom (the plan read as "bottom-
   heavy"). Shed the enclosure (no border / fill / shadow / lift) and the
   channel chips, and hold it to ONE line: glyph · title ········ NOTIFY ME →.
   But keep it alive, not ghosted — the leverage-tinted glyph, primary-weight
   title, and accent CTA give it presence as a tappable row without the card's
   bulk. A hairline + top margin set it apart as the terminal footer
   affordance. Still a full-width tap target. Scoped ≤720px so desktop's card
   (where the wide column tames it) stands. The base auto/1fr/auto columns are
   kept (single row) — no narrower-phone two-row fallback, since a borderless
   row of glyph + short title + "NOTIFY ME →" fits the gutter on its own. */
@media (max-width: 720px) {
  .ios-notify-wrap {
    margin-top: var(--space-lg);
    padding-top: var(--space-md);
    border-top: 1px solid var(--border-subtle);
  }
  .plan-deliver {
    max-width: none;
    gap: var(--space-sm);
    padding: 4px 2px;
    background: none;
    border: 0;
    border-radius: 0;
  }
  .plan-deliver:hover {
    border-color: transparent;
    transform: none;
    box-shadow: none;
  }
  /* Smaller than the card's 38px circle, but keep the base leverage tint —
     the one spot of colour that keeps the row from reading dead. */
  .plan-deliver-glyph {
    width: 28px;
    height: 28px;
  }
  .plan-deliver-glyph svg { width: 16px; height: 16px; }
  /* Title + CTA keep the card's weights (primary title, accent CTA); only
     the chips drop. That's the "presence without bulk" the ghost lost. */
  .plan-deliver-channels { display: none; }
  .plan-deliver-cta {
    font-size: 11px;
  }
}

/* Topbar iOS availability badge — NOT a peer of TRENDS / DATA / ?.
   Those three are controls for the dashboard you're in; this is a
   cross-product CTA pointing to a different surface (the iOS app
   waitlist). Recast as a low-key text pill (glyph + label + dot) so
   it reads as a platform availability badge — same family as App Store
   plaques — rather than a fourth utility button. Margin-left puts a
   visible gap between the action cluster and the badge so the
   category break is legible. */
.topbar-ios {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 10px 0 9px;
  margin-left: var(--space-sm);
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.topbar-ios:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 5%, transparent);
}
.topbar-ios-glyph {
  display: block;
  flex-shrink: 0;
  margin-top: -1px;
}
.topbar-ios-label {
  line-height: 1;
}
.topbar-ios-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
  margin-left: 1px;
}

/* ── Phone topbar: collapse secondary chrome behind a ⋮ overflow ──
   On a 390px viewport the topbar's five chips (TRENDS / data / ? / iOS
   + logo) crowd or overflow the bar. Mobile best practice: brand + one
   primary-signal control + overflow. The data icon stays inline because
   it's the only chrome carrying live signal (the freshness dot); the
   rest move into a dropdown reachable via the kebab. The bottom-nav
   already carries primary navigation, so this loses nothing essential. */

.topbar-overflow {
  display: none;
  align-items: center;
  justify-content: center;
  position: relative;
  width: 32px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.topbar-overflow:hover,
.topbar-overflow[aria-expanded="true"] {
  color: var(--accent);
  border-color: var(--accent);
}
.topbar-overflow-dot {
  position: absolute;
  top: 3px;
  right: 3px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}

.topbar-overflow-menu {
  position: absolute;
  top: 52px;
  right: var(--space-md);
  min-width: 200px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
  z-index: 99;
  padding: 6px 0;
  font-family: var(--font-mono);
}

.topbar-overflow-item {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  background: transparent;
  border: 0;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.3px;
  color: var(--text-primary);
  text-align: left;
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.topbar-overflow-item:hover,
.topbar-overflow-item:focus-visible {
  background: var(--bg-surface);
  color: var(--accent);
  outline: none;
}
.topbar-overflow-item-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}

/* Tour final step pulses the How It Works entry point. On desktop the
   pulse lands on the `?` button directly; on mobile the same effect
   targets the menu row (the overflow auto-opens via tour.js). */
.topbar-overflow-item.is-tour-help-pulse {
  background: var(--accent-soft);
  color: var(--accent);
  animation: topbar-overflow-pulse 1800ms ease-out infinite;
}
@keyframes topbar-overflow-pulse {
  0%, 100% { background: var(--accent-soft); }
  50% { background: color-mix(in srgb, var(--accent) 18%, var(--bg-panel)); }
}
@media (prefers-reduced-motion: reduce) {
  .topbar-overflow-item.is-tour-help-pulse { animation: none; }
}

@media (max-width: 600px) {
  .topbar-trends-btn,
  .topbar-help,
  .topbar-ios { display: none !important; }
  .topbar-overflow:not([hidden]) { display: inline-flex; }
}

/* Landing-page secondary CTA — outlined pill under the primary CTA
   when WAITLIST_MODE is off. Shares the primary's pill geometry but
   smaller and outline-style so it reads as a sibling affordance, not
   a footnote. The Apple glyph signals "native app" at a glance. */
.landing-ios-chip {
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 1.6px;
  color: var(--text-secondary);
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid var(--border);
  padding: 10px 18px;
  border-radius: 999px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  position: relative;
  z-index: 6;
  transition: background 180ms ease, color 180ms ease, border-color 180ms ease, transform 180ms ease, box-shadow 180ms ease;
}
.landing-ios-chip:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: #fff;
  transform: translateY(-1px);
  box-shadow: 0 6px 18px -10px rgba(8, 145, 178, 0.45);
}
.landing-ios-chip:active { transform: translateY(0); }
.landing-ios-chip-glyph {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  margin-top: -1px;
  transition: color 180ms ease;
}
.landing-ios-chip-label {
  text-transform: uppercase;
}
/* In the closing card the primary CTA is inline-flex, so a sibling
   inline-flex chip would float up next to it. Wrap in a block-level
   div that gives the chip its own line beneath the primary. */
.lp-final-card-secondary {
  display: block;
  margin-top: 18px;
}

/* "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.
   Neutral chip chrome (same as .action-controls-dismissed and the
   meta-line priority chip) so secured/dismissed/priority share one
   pill grammar. The row's green left-rail and section header already
   carry the "banked" semantics; the chip doesn't need to re-declare
   them in green-on-green. */
.action-controls-secured {
  background: var(--bg-surface);
  border-top: 0;
  padding: 6px 10px;
  border-radius: 3px;
}

/* ── 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: 0;
  display: flex;
  flex-direction: column;
}
.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;
  justify-content: flex-start;
  gap: 8px;
  cursor: pointer;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--text-secondary);
  list-style: none;
  padding: 10px 4px;
  border-top: 0;
  font-variant-numeric: tabular-nums;
}
.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 {
  color: var(--text-dim);
  transition: transform 0.15s;
}
.risk-actions-dismissed-arrow { font-size: 10px; }
.risk-actions-dismissed[open] .risk-actions-dismissed-arrow {
  transform: rotate(90deg);
}
.risk-actions-more-arrow {
  margin-left: auto;
  font-size: 12px;
  opacity: 0.9;
}
.risk-actions-more[open] .risk-actions-more-arrow {
  transform: rotate(-90deg);
}

/* Closed/open label toggle — closed shows "+N more", open shows "Show less".
   Mirrors the goals/levers expansion pattern. */
.risk-actions-more-label-open { display: none; }
.risk-actions-more[open] .risk-actions-more-label-closed { display: none; }
.risk-actions-more[open] .risk-actions-more-label-open { display: inline; }

/* When open, push the toggle below the revealed rows so the trigger sits
   at the boundary between shown and hidden content — same accordion idiom
   as the goals section. */
.risk-actions-more[open] .risk-actions-more-list {
  order: 1;
}
.risk-actions-more[open] .risk-actions-more-head { order: 2; }

.risk-actions-dismissed-list,
.risk-actions-more-list {
  margin-top: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
}
.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;
}

/* Potential (not-yet-banked) variant — a soft-commit acknowledges a
   behavioral action without realizing its years. Drop the green "gain"
   coloring to neutral so the figure reads as unrealized stake, not a
   secured win. */
.action-receipt-delta-potential {
  color: var(--text-secondary);
}
.action-receipt-tag-potential {
  color: var(--text-dim);
}

.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 three lines — long actions wrap, then ellipsize. (The recency
     meta line is gone for immediate kinds, so the body has the room.) */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.action-receipt-meta {
  font-size: 12px;
  color: var(--text-dim);
  letter-spacing: 0.2px;
}

/* Action cluster on the right of the receipt — "Add results" (when offered)
   sits ahead of Undo. */
.action-receipt-actions {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

/* "Add results" — the optional log-your-numbers door on an unbanked receipt.
   Accent-filled so it reads as the forward action, with Undo staying the
   quiet secondary beside it. */
.action-receipt-add {
  flex: 0 0 auto;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 8px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.4px;
  color: #fff;
  cursor: pointer;
  padding: 7px 14px;
  white-space: nowrap;
  transition: opacity 120ms ease;
}
.action-receipt-add:hover {
  opacity: 0.88;
}

.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; }
  /* Stack the right-side actions so "Add results" + "Undo" don't squeeze the
     body text on a narrow phone. */
  .action-receipt-actions {
    flex-direction: column;
    gap: 6px;
  }
}

/* ═══════════════════════════════════════════
   FIRST-SESSION DASHBOARD REVEAL
   The dashboard fades in as a single plane (one gesture, no per-panel
   stagger); the bottom nav is the one distinct beat, sliding up just
   behind it. Activated by JS adding `is-first-reveal` to .app for the
   first session only.
   ═══════════════════════════════════════════ */

.app.is-first-reveal #dashboard {
  /* 260ms delay: the calibration hairline (app.js) exits first, on its
     own beat — the plane stays invisible until the line is gone. */
  animation: dashboard-shell-fade 600ms cubic-bezier(.2, .7, .2, 1) 260ms both;
}
.app.is-first-reveal #bottom-nav {
  animation: dashboard-nav-rise 900ms cubic-bezier(.2, .7, .2, 1) 600ms both;
}

@keyframes dashboard-shell-fade {
  from { opacity: 0; transform: translateY(6px); }
  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 #bottom-nav {
    animation: none;
  }
}


/* ═══════════════════════════════════════════════════════════════════════
   RISKS — mobile detail sheet
   Phone replacement for the 3rd inline-accordion layer: tapping a Goal or
   Action row on mobile opens a bottom sheet instead of expanding in place,
   so the list underneath stays still. Rendered inside .risks-view (by
   risks.js) so the container's delegated listeners reach the sheet's
   controls. Default-hidden; only the ≤880px block paints it, and openSheet
   is itself viewport-gated, so desktop never sees it. Reused row primitives
   (.lever-row-expansion, .risk-action-row, .lever-standing) keep their
   existing styling inside the sheet; only the host chrome is new here.
   ═══════════════════════════════════════════════════════════════════════ */

.risks-sheet,
.risks-sheet-backdrop,
.plan-sheet,
.plan-sheet-backdrop { display: none; }

@media (max-width: 880px) {
  .risks-sheet-backdrop,
  .plan-sheet-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(15, 18, 23, 0.34);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.26s ease;
    /* Above the bottom-nav (z-index 100) so the sheet reads as a true modal
       over the tab bar, matching the slide-over's modal layer. */
    z-index: 300;
  }
  .risks-sheet-backdrop.open,
  .plan-sheet-backdrop.open {
    opacity: 1;
    pointer-events: auto;
  }

  .risks-sheet,
  .plan-sheet {
    display: flex;
    flex-direction: column;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    max-height: 88vh;
    background: var(--bg-panel);
    border-top-left-radius: var(--radius-lg);
    border-top-right-radius: var(--radius-lg);
    box-shadow: 0 -10px 44px rgba(15, 18, 23, 0.20);
    z-index: 301;
    transform: translateY(101%);
    transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1);
  }
  .risks-sheet.open,
  .plan-sheet.open { transform: translateY(0); }

  /* Grab handle — the universal "this is a sheet you can dismiss" cue. */
  .risks-sheet::before,
  .plan-sheet::before {
    content: '';
    flex: 0 0 auto;
    width: 36px;
    height: 4px;
    border-radius: 999px;
    background: var(--border);
    margin: 10px auto 2px;
  }

  .risks-sheet-body,
  .plan-sheet-body {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* Generous top room so content breathes below the grab handle. */
    padding: 16px var(--space-md) calc(env(safe-area-inset-bottom, 0px) + var(--space-lg));
  }

  /* ── Goal sheet ───────────────────────────────────────────────── */
  .risk-sheet-lever-head {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding: 0 0 10px;
  }
  .risk-sheet-lever-icon {
    display: inline-flex;
    flex: 0 0 auto;
    color: var(--leverage);
  }
  .risk-sheet-lever-icon svg { display: block; }
  .risk-sheet-lever-name {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 15px;
    font-weight: 600;
    letter-spacing: 0.2px;
    color: var(--text-primary);
  }
  .risk-sheet-lever-years {
    flex: 0 0 auto;
    font-size: 15px;
    font-weight: 700;
    color: var(--leverage);
  }
  /* Gauge + goal read as ONE standing block: no border between the gauge
     (here) and the goal target sub-line (the expansion's first row). The
     break before the body comes from the Tactics section label instead. */
  .risk-sheet-lever-standing {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding-bottom: 6px;
  }
  .risk-sheet-lever-standing:empty { display: none; }

  /* The sheet hosts the expansion directly (no .lever-item.expanded gate),
     and it's already inside the sheet's own padded body — so reveal it and
     strip the row-nested drawer chrome (left accent, surface fill, insets). */
  .risk-sheet-lever .lever-row-expansion {
    display: block;
    border: 0;
    background: transparent;
    padding: 0;
  }
  /* Goal target sub-line sits tight under the gauge as part of the standing
     block; the description that follows opens up the "what is this." */
  .risk-sheet-lever .lever-exp-top { margin-bottom: var(--space-sm); }
  .risk-sheet-lever .lever-exp-desc { margin: 0 0 var(--space-md); }

  /* Action rows in the sheet expand to controls-only on the clean white
     sheet — no gray surface fill (the "ugly gray box"), no left accent. */
  .risk-sheet-lever .risk-action-row.expanded {
    background: transparent;
    padding-bottom: 10px;
  }
  /* Strip the last action row's divider so the list ends clean into Sources,
     instead of a stray hairline floating below the final action. */
  .risk-sheet-lever .risk-actions-list > .risk-action-row:last-child {
    border-bottom: 0;
  }

  /* ── Action sheet ─────────────────────────────────────────────── */
  /* Priority kicker — only rendered when an action carries a Doctor/Rx tag,
     so the common untagged action starts flush at the title with no waste. */
  .risk-sheet-action-kicker {
    margin-bottom: 7px;
  }
  /* Title + impact share a grid: title wraps in its own column, the +Xy pins
     top-right (its column, top-aligned) so a long name never rides the number. */
  .risk-sheet-action-head {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: start;
    column-gap: var(--space-sm);
    margin-bottom: var(--space-sm);
  }
  .risk-sheet-action-title {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    line-height: 1.3;
    letter-spacing: 0.2px;
    color: var(--text-primary);
  }
  .risk-sheet-action-years {
    padding-top: 2px; /* optical align to the title cap height */
    font-size: 13px;
    font-weight: 700;
    color: var(--leverage);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }
  .risk-sheet-action-why {
    margin: 0 0 var(--space-md);
    font-family: var(--font-sans);
    font-size: 13px;
    line-height: 1.55;
    color: var(--text-secondary);
  }
  /* "Also helps" — the action's reach across other threats, the breadth the
     threat-scoped +Xy hides. Threat names read as the values (secondary);
     the label recedes (dim, smaller, uppercase) so the two don't blend. */
  .risk-sheet-action-also {
    margin: 0 0 var(--space-md);
    font-size: 11px;
    line-height: 1.6;
    letter-spacing: 0.2px;
    color: var(--text-secondary);
  }
  .risk-sheet-action-also-label {
    margin-right: 8px;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--text-dim);
  }

  /* ── Sheet control row (Action sheet + Goal-sheet tactic) ─────────
     A restrained, left-aligned primary button with Dismiss / Open-in-Plan
     grouped quietly beside it — not a full-bleed CTA with the two links flung
     to opposite edges. display:contents flattens .action-controls so its
     primary, Dismiss, reveal editor, and the Plan link become one flex group
     we can order together (and drops its rest-state opacity, so buttons read
     full-strength on a touch surface with no hover). */
  .risks-sheet .action-controls { display: contents; }

  .risk-sheet-action-foot,
  .risks-sheet .risk-action-row.expanded .risk-action-row-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 14px;
  }
  .risk-sheet-action-foot {
    margin-top: var(--space-lg);
  }

  /* Primary CTA (Log a reading / Update value / Report status / ✓ Done) — a
     filled, confident button in the house CTA language (cf. .waitlist-submit),
     not a thin outlined chip. Accent fill for measure/report; green for the
     positive Done commit. Targeted by the two authored primaries so buttons
     inside the reveal tuner are never caught. */
  .risks-sheet .action-report-toggle,
  .risks-sheet .sr-btn-done {
    display: inline-flex;
    align-items: center;
    border: 0;
    border-radius: var(--radius-md);
    padding: 11px 20px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.4px;
    color: #fff;
    background: var(--accent);
    transition: background 0.15s, transform 0.12s;
  }
  .risks-sheet .action-report-toggle:hover,
  .risks-sheet .sr-btn-done:hover { background: #0e7490; }
  .risks-sheet .action-report-toggle:active,
  .risks-sheet .sr-btn-done:active { transform: scale(0.98); }
  .risks-sheet .sr-btn-done { background: var(--green); }
  .risks-sheet .sr-btn-done:hover { background: #047857; }

  /* Primary and "Not for me" stay paired on one row (natural DOM order); the
     reveal editor wraps full-width below BOTH. Reordering it between them
     orphaned "Not for me" beneath the disclosed control — disconnected on a
     phone. */
  .risks-sheet .action-report-editor { flex: 1 1 100%; margin-top: 0; }
  /* "Not for me" — quiet secondary, refined: readable, height-matched to the
     primary, with a soft hover fill. Clearly subordinate to the filled CTA. */
  .risks-sheet .risk-sheet-action-foot .sr-btn-ghost,
  .risks-sheet .risk-action-row-foot .sr-btn-ghost {
    padding: 11px 14px;
    font-size: 12px;
    font-weight: 500;
    color: var(--text-secondary);
    border-radius: var(--radius-md);
  }
  .risks-sheet .risk-sheet-action-foot .sr-btn-ghost:hover,
  .risks-sheet .risk-action-row-foot .sr-btn-ghost:hover {
    color: var(--text-primary);
    background: var(--bg-hover);
  }

  /* ── Plan action sheet ────────────────────────────────────────────
     The phone detail view for a Plan action — same chassis as the Risks
     Action sheet above, so the two tabs read as one interaction. Title +
     impact, priority/threat meta, a filled CTA row, then the why /
     goals / sources block (the .action-why eyebrow and .target-pills
     restack are shared via .plan-sheet selectors in components-base). */
  .plan-sheet-action-head {
    display: flex;
    align-items: baseline;
    gap: var(--space-md);
    padding: 0 0 10px;
  }
  .plan-sheet-action-title {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    line-height: 1.35;
    color: var(--text-primary);
    letter-spacing: 0.2px;
  }
  .plan-sheet-action-years {
    flex: 0 0 auto;
    font-size: 17px;
    font-weight: 600;
    color: var(--leverage);
    font-variant-numeric: tabular-nums;
  }
  .plan-sheet-action-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 10px;
    padding-bottom: var(--space-md);
    border-bottom: 1px solid var(--border-subtle);
  }
  .plan-sheet-action-threat {
    font-size: 11px;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: var(--text-dim);
  }

  /* Controls row — flatten .action-controls (display:contents) so the
     primary CTA + "Not for me" sit on one flex line, with the reveal
     editor wrapping full-width below, exactly like the Risks sheet. */
  .plan-sheet-action-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 14px;
    margin: var(--space-md) 0;
  }
  .plan-sheet .action-controls { display: contents; }
  .plan-sheet .action-report-toggle,
  .plan-sheet .sr-btn-done {
    display: inline-flex;
    align-items: center;
    border: 0;
    border-radius: var(--radius-md);
    padding: 11px 20px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.4px;
    color: #fff;
    background: var(--accent);
    transition: background 0.15s, transform 0.12s;
  }
  .plan-sheet .action-report-toggle:hover,
  .plan-sheet .sr-btn-done:hover { background: #0e7490; }
  .plan-sheet .action-report-toggle:active,
  .plan-sheet .sr-btn-done:active { transform: scale(0.98); }
  .plan-sheet .sr-btn-done { background: var(--green); }
  .plan-sheet .sr-btn-done:hover { background: #047857; }
  .plan-sheet .action-report-editor { flex: 1 1 100%; margin-top: 0; }
  .plan-sheet .sr-btn-ghost {
    padding: 11px 14px;
    font-size: 12px;
    font-weight: 500;
    color: var(--text-secondary);
    border-radius: var(--radius-md);
  }
  .plan-sheet .sr-btn-ghost:hover {
    color: var(--text-primary);
    background: var(--bg-hover);
  }

  /* ── Plan filter sheet ────────────────────────────────────────────
     Threat picker — the chips restack as full-width tap rows (label left,
     count right) with hairline dividers, the native list-sheet pattern. */
  .plan-sheet-filter-title,
  .plan-sheet-goalmix-title {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    color: var(--text-secondary);
    padding: 0 0 12px;
  }

  /* Breakdown sheet body — donut over the ranked goal list, mirroring the
     desktop aside's .plan-goal-sheet-body stack. */
  .plan-sheet-goalmix {
    display: flex;
    flex-direction: column;
    gap: 18px;
  }
  .plan-sheet-goalmix-title { padding: 0; }
  .plan-sheet-goalmix .plan-goal-donut-wrap { align-self: center; }
  .plan-sheet-filter-list {
    display: flex;
    flex-direction: column;
  }
  .plan-sheet-filter-list .plan-filter-chip {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
    gap: 12px;
    padding: 14px 4px;
    font-size: 11px;
    letter-spacing: 0.9px;
    background: none;
    border: 0;
    border-bottom: 1px solid var(--border-subtle);
    border-radius: 0;
    color: var(--text-secondary);
  }
  .plan-sheet-filter-list .plan-filter-chip:last-child {
    border-bottom: 0;
  }
  .plan-sheet-filter-list .plan-filter-chip.active {
    color: var(--accent);
  }
  .plan-sheet-filter-list .plan-filter-chip.active .plan-filter-chip-count {
    color: var(--accent);
    opacity: 0.8;
  }

  /* ── Nested-sheet back nav (Risks + Plan) ─────────────────────────
     When a sheet is pushed onto another (a goal's tactic → its Action
     sheet; an action's goal pill → its Goal sheet), a back row sits above
     the body and returns to the parent, labeled with the parent entity's
     name (iOS nav-stack idiom). Only rendered at stack depth > 1. */
  .risks-sheet-nav,
  .plan-sheet-nav {
    flex: 0 0 auto;
    padding: 2px var(--space-md) 4px;
  }
  .risks-sheet-back,
  .plan-sheet-back {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    max-width: 100%;
    background: none;
    border: 0;
    padding: 4px 0;
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.3px;
    /* Quiet breadcrumb: the parent label sits in dim so it reads as
       secondary context, never louder than the current detail title.
       Only the chevron keeps the accent, as a small interactive cue. */
    color: var(--text-dim);
    cursor: pointer;
  }
  .risks-sheet-back-chevron,
  .plan-sheet-back-chevron {
    font-size: 17px;
    line-height: 1;
    margin-top: -1px;
    color: var(--accent);
  }
  .risks-sheet-back-label,
  .plan-sheet-back-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* ── Action sheet: "Goals it moves" ───────────────────────────────
     Tappable goal pills — each drills into that goal's own sheet. The
     restacked one-per-row readout is shared with Plan (components-base);
     here we add the drill affordance: pointer + a trailing chevron, dim
     until pressed. */
  .risk-sheet-action-goals { margin-bottom: var(--space-md); }
  .risks-sheet .target-pill-tappable,
  .plan-sheet .target-pill-tappable {
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .risks-sheet .target-pill-tappable:active,
  .plan-sheet .target-pill-tappable:active {
    background: var(--bg-surface);
  }
  .target-pill-chevron {
    flex: 0 0 auto;
    font-size: 15px;
    line-height: 1;
    color: var(--text-dim);
  }
  /* Binary goals drop their goal phrase onto a wrapped line, which would
     strand the chevron — hide it there; the row stays tappable. */
  .risks-sheet .target-pill-binary .target-pill-chevron,
  .plan-sheet .target-pill-binary .target-pill-chevron { display: none; }
}

/* ═══════════ SOL TAB (conversational-agent fake-front) ═══════════
   Inert preview of the upcoming AI coach, modeled on a chat empty state (à
   la ChatGPT): a greeting, an input, and suggestion pills directly under the
   input. The "coming soon" signal lives in the greeting subline — no banner.
   Desktop: greeting + input + a centered row of pills, vertically centered.
   Mobile: greeting on top; the input group (pills STACKED ABOVE the input)
   pinned to the bottom. The tab pane is a fixed-height scroll area, so
   flexbox pins the dock without fixed positioning fighting the bottom nav.
   Pills inject a demo Q&A locally; the input routes to the iOS waitlist.
   Sol stays in the COOL accent — warm tones are reserved for severity (see
   ETHOS) — so its identity never reads as a risk signal. See js/views/sol.js. */
.sol-view {
  position: relative;
  display: flex;
  justify-content: center;
  min-height: 100%;
  padding: var(--space-xl) var(--space-lg) var(--space-2xl);
}

body.sol-overlay-open {
  overflow: hidden;
}

.sol-overlay {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: none;
  isolation: isolate;
  background: rgba(15, 23, 42, 0.16);
  color: var(--text-primary);
}

.sol-overlay.open {
  display: block;
}

.sol-overlay-mount {
  position: absolute;
  z-index: 1;
  top: 0;
  right: 0;
  width: min(560px, 100vw);
  height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--bg-page);
  border-left: 1px solid var(--border);
  box-shadow: -18px 0 44px rgba(15, 23, 42, 0.12);
}

.sol-overlay .sol-view {
  min-height: 100%;
  background: var(--bg-page);
  padding: 76px var(--space-lg) var(--space-xl);
}

.sol-overlay-close {
  position: absolute;
  top: 24px;
  right: 24px;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--bg-panel);
  box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
  color: var(--text-secondary);
  font-family: var(--font-sans);
  font-size: 26px;
  font-weight: 300;
  line-height: 1;
  cursor: pointer;
}

.sol-overlay-close:hover,
.sol-overlay-close:focus-visible {
  color: var(--text-primary);
  border-color: color-mix(in srgb, var(--accent) 28%, var(--border));
  outline: none;
}

/* Back affordance — a quiet icon-only chevron at the top-left of the column,
   shown only while an example is showing. Returns to the greeting empty
   state (no chat history to be "new" from — this just goes back). While
   chatting the stage top-anchors so this sits at the top. */
.sol-reset {
  display: none;
  align-self: flex-start;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  margin: 0 0 var(--space-sm) calc(-1 * var(--space-xs)); /* optical: nudge to column edge */
  color: var(--text-dim);
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: color 120ms ease, background 120ms ease;
}
.sol-reset svg { width: 18px; height: 18px; }
.sol-reset:hover { color: var(--accent); background: var(--bg-surface); }
.sol-view.is-chatting .sol-reset { display: inline-flex; }
.sol-view.is-chatting .sol-greet { display: none; }
.sol-view.is-chatting .sol-stage { justify-content: flex-start; }
/* While chatting, the input docks to the bottom (like a real chat) — the
   conversation sits up top, input pinned below it. Mobile already does this. */
.sol-view.is-chatting .sol-dock { margin-top: auto; }
/* Pills are empty-state starter suggestions — once an example is showing the
   conversation takes over, so they're hidden (the back chevron returns to the
   empty state to pick another). */
.sol-view.is-chatting .sol-pills { display: none; }

/* Fills the view height so the cluster can center (desktop) and the dock can
   pin to the bottom (mobile). */
.sol-stage {
  width: 100%;
  max-width: 600px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* ── Greeting: opening line + coming-soon subline ── */
.sol-greet {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-sm);
  padding-bottom: var(--space-xl);
}
.sol-greet-title {
  margin: var(--space-xs) 0 0;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.3px;
  color: var(--text-primary);
}
.sol-greet-sub {
  margin: 0;
  max-width: 40ch;
  font-family: var(--font-sans); /* reading face — see typography-reading-face test */
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-dim);
}

/* ── Thread: starts empty; grows as pills are tapped ── */
.sol-thread {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.sol-thread:not(:empty) { padding-bottom: var(--space-lg); }

/* Conversation uses the reading face (Inter) — mono is for data/labels, not
   multi-line prose. The user's question is the only styled bubble; Sol's
   answer is plain text on the page (the modern assistant pattern — no bubble,
   no avatar chrome), which reads cleaner and removes the cheesy avatar puck. */
.sol-msg p { font-family: var(--font-sans); }

/* User question — right-aligned, accent fill. */
.sol-msg--user {
  align-self: flex-end;
  max-width: 82%;
  background: var(--accent);
  border-radius: var(--radius-lg);
  border-bottom-right-radius: var(--radius-sm);
  padding: 10px var(--space-md);
}
.sol-msg--user p { margin: 0; font-size: 14px; line-height: 1.5; color: #fff; }

/* Sol's answer — plain text, left-aligned, no bubble. */
.sol-msg--agent {
  align-self: flex-start;
  max-width: 100%;
  padding: 2px 0;
}
.sol-msg--agent p { margin: 0; font-size: 14px; line-height: 1.7; color: var(--text-primary); }
.sol-msg--agent p strong { font-weight: 600; }

/* Thinking indicator — three bouncing dots while Sol "responds". */
.sol-typing { display: inline-flex; align-items: center; gap: 5px; padding: 6px 0; }
.sol-typing i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-dim);
  animation: sol-typing-dot 1.2s infinite ease-in-out;
}
.sol-typing i:nth-child(2) { animation-delay: 0.16s; }
.sol-typing i:nth-child(3) { animation-delay: 0.32s; }
@keyframes sol-typing-dot {
  0%, 75%, 100% { opacity: 0.3; transform: translateY(0); }
  35% { opacity: 1; transform: translateY(-3px); }
}

@media (prefers-reduced-motion: reduce) {
  .sol-typing i { animation: none; }
}

/* ── Dock: input + pills. Desktop = input, then pills below it. ── */
.sol-dock {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

/* Composer — styled like a live input (inert; tap → waitlist), not greyed. */
.sol-composer {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-panel);
  cursor: pointer;
  transition: border-color 120ms ease;
}
.sol-composer:hover { border-color: var(--accent); }
.sol-input {
  flex: 1 1 auto;
  border: none;
  background: transparent;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--text-primary);
  padding: 6px 8px;
  cursor: pointer;
}
.sol-input::placeholder { color: var(--text-dim); }
.sol-input:focus { outline: none; }
.sol-send {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: var(--radius-md);
  background: var(--accent);
  color: #fff;
  cursor: pointer;
  transition: opacity 120ms ease;
}
.sol-send:hover { opacity: 0.88; }
.sol-send svg { width: 18px; height: 18px; }

/* Pills — desktop: a centered row directly under the input. */
.sol-pills {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-sm);
}
.sol-pills:empty { display: none; }
.sol-pill {
  flex: 0 0 auto;
  white-space: nowrap;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--text-secondary);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 7px 14px;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.sol-pill:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

@media (max-width: 720px) {
  .sol-view { padding: var(--space-lg) var(--space-md) var(--space-xl); }
  .sol-overlay {
    background: rgba(15, 23, 42, 0.22);
  }
  .sol-overlay-mount {
    top: auto;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 92dvh;
    border-left: 0;
    border-top: 1px solid var(--border);
    border-radius: 24px 24px 0 0;
    box-shadow: 0 -18px 44px rgba(15, 23, 42, 0.16);
  }

  .sol-overlay-mount::before {
    content: '';
    position: sticky;
    top: 10px;
    z-index: 2;
    display: block;
    width: 42px;
    height: 4px;
    margin: 10px auto 0;
    border-radius: 999px;
    background: var(--border);
  }

  .sol-overlay .sol-view {
    min-height: calc(92dvh - 14px);
    padding: 42px var(--space-md) var(--space-xl);
  }
  .sol-overlay-close {
    top: calc(8dvh + 18px);
    right: 16px;
    width: 38px;
    height: 38px;
  }
  /* Dock pinned to the bottom; greeting centered in the space above it.
     Two auto top-margins split the free space evenly → the greeting sits
     centered above the dock while the dock stays bottom-anchored. */
  .sol-stage { justify-content: flex-start; }
  .sol-greet { margin-top: auto; }
  .sol-dock {
    margin-top: auto;
    flex-direction: column-reverse; /* pills ABOVE the input */
  }
  /* Pills stacked but CONTENT-WIDTH (not full-width) — small outlined chips,
     left-aligned. A chip's shape/size reads clearly as a suggestion, distinct
     from the full-width input field below (which also carries the send
     button); differentiating by silhouette beats differentiating by fill. */
  .sol-pills {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    gap: var(--space-sm);
    margin-bottom: var(--space-sm);
  }
}

/* ── Product surfaces: Today / Perform / Live ───────────────────── */
.product-surface {
  box-sizing: border-box;
  padding: var(--space-lg);
  overflow-x: clip;
}

.surface-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(280px, 0.85fr);
  gap: var(--space-lg);
  align-items: start;
}

.today-grid {
  display: grid;
  grid-template-columns: minmax(0, 760px) minmax(340px, 480px);
  grid-template-areas:
    "hero hero"
    "today ahead";
  justify-content: center;
  gap: 62px 64px;
  align-items: start;
  max-width: 1360px;
  margin: 0 auto;
}

.today-hero {
  grid-area: hero;
  max-width: none;
}

.today-brief,
.today-checkin,
.today-hero,
.today-hero-action,
.today-coach-panel,
.today-primary-move,
.today-actions,
.today-now-column,
.today-lookahead-column,
.today-next,
.today-queue,
.today-grounding,
.perform-patterns,
.perform-week,
.perform-body-state,
.live-threat-board,
.live-plan-board {
  min-width: 0;
}

.today-action-stack,
.today-queue-list,
.perform-week-list,
.perform-driver-list,
.live-plan-list,
.live-plan-empty {
  display: flex;
  flex-direction: column;
}

.today-primary-empty,
.today-queue-empty,
.live-plan-empty {
  font-family: var(--font-sans);
  letter-spacing: 0;
}

.today-primary-action,
.today-action-card,
.today-primary-empty,
.grounding-card,
.checkin-box,
.today-coach-panel,
.today-coach-composer,
.perform-pattern-card,
.perform-week-row,
.live-plan-list,
.live-plan-empty {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-panel);
  color: inherit;
}

.today-primary-action,
.today-action-card,
.grounding-card,
.checkin-box,
.perform-pattern-card,
.perform-week-row,
.today-queue-row,
.live-plan-row,
.live-threat-preview-row,
.live-age,
.live-panel-link {
  font-family: var(--font-mono);
}

.today-primary-action,
.today-action-card,
.today-queue-row,
.grounding-card,
.checkin-box,
.today-coach-composer,
.perform-pattern-card,
.perform-week-row,
.live-plan-row,
.live-threat-preview-row,
.live-age,
.live-panel-link {
  cursor: pointer;
}

.today-action-stack {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
  padding: 0;
}

.today-primary-action,
.today-action-card,
.today-primary-empty {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  min-width: 0;
  min-height: 128px;
  padding: 18px;
  text-align: left;
}

.today-primary-action,
.today-action-card {
  box-shadow: var(--shadow-card);
  transition: border-color 0.16s, box-shadow 0.16s;
}

.today-primary-action:hover,
.today-primary-action:focus-visible,
.today-action-card:hover,
.today-action-card:focus-visible,
.grounding-card:hover,
.grounding-card:focus-visible,
.checkin-box:hover,
.checkin-box:focus-visible,
.today-coach-composer:hover,
.today-coach-composer:focus-visible,
.perform-pattern-card:hover,
.perform-pattern-card:focus-visible,
.perform-week-row:hover,
.perform-week-row:focus-visible,
.live-plan-row:hover,
.live-plan-row:focus-visible,
.live-threat-preview-row:hover,
.live-threat-preview-row:focus-visible {
  border-color: color-mix(in srgb, var(--accent) 28%, var(--border));
  outline: none;
}

.today-action-top,
.perform-pattern-head,
.live-panel-head,
.live-index-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.today-action-command,
.today-action-index,
.live-plan-title,
.perform-pattern-title,
.live-threat-preview-name,
.live-threat-preview-title {
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
}

.today-action-reason,
.today-queue-meta,
.today-queue-empty,
.checkin-placeholder,
.perform-pattern-body,
.perform-empty,
.perform-driver-meta,
.perform-week-arrow,
.live-plan-meta,
.live-plan-text,
.live-threat-preview-meta,
.live-age-label,
.live-age-meta {
  color: var(--text-dim);
  font-size: 11px;
  line-height: 1.45;
}

.today-action-reason,
.perform-pattern-body,
.perform-empty,
.live-plan-text {
  font-family: var(--font-sans);
  font-size: 13px;
  letter-spacing: 0;
}

.today-action-gain,
.live-plan-gain {
  color: var(--leverage);
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.today-action-card-primary {
  grid-column: auto;
}

.today-action-card:not(.today-action-card-primary) {
  min-height: 132px;
  padding: 16px;
  gap: 10px;
}

.today-action-card:not(.today-action-card-primary) .today-action-reason {
  display: none;
}

.today-primary-move {
  min-width: 0;
}

.today-plan-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 0;
}

.today-stance {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 24px;
  padding: 0 9px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg-surface);
  color: var(--text-dim);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1px;
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
}

.today-stance-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}

.today-stance[data-tone="recover"] .today-stance-dot { background: var(--amber); }
.today-stance[data-tone="hold"] .today-stance-dot { background: var(--yellow); }
.today-stance[data-tone="build"] .today-stance-dot { background: var(--accent); }
.today-stance[data-tone="push"] .today-stance-dot { background: var(--green); }

.today-primary-move .today-action-card,
.today-primary-move .today-primary-empty {
  min-height: 188px;
  padding: 24px;
}

.today-primary-move .today-action-command {
  font-size: 15px;
}

.today-primary-move .today-action-reason {
  display: block;
  max-width: 620px;
}

.today-now-column {
  grid-area: today;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.today-action-index {
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.today-coach-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 280px;
  padding: 0;
  overflow: visible;
  border: 0;
  background: transparent;
  box-shadow: none;
}

.today-coach-panel::after {
  content: none;
}

.today-coach-main {
  position: relative;
  z-index: 1;
  display: flex;
  flex: 1;
  flex-direction: column;
  justify-content: center;
  gap: 18px;
  width: 100%;
  min-width: 0;
  min-height: 92px;
}

.today-greeting,
.perform-intro,
.live-intro {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-left: 2px;
}

.today-greeting {
  margin-bottom: 38px;
}

.today-greeting-kicker,
.perform-intro-kicker,
.live-intro-kicker {
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.2px;
  line-height: 1.45;
  text-transform: uppercase;
}

.today-greeting-line,
.perform-intro-line,
.live-intro-line {
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: clamp(26px, 3.6vw, 36px);
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1.05;
}

.today-coach-composer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: 100%;
  min-height: 84px;
  padding: 14px 14px 14px 24px;
  text-align: left;
  background: var(--bg-panel);
  box-shadow: 0 10px 26px rgba(15, 23, 42, 0.06);
}

.today-coach-placeholder {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--text-secondary);
  font-family: var(--font-sans);
  font-size: 15px;
  letter-spacing: 0;
  line-height: 1.35;
}

.today-coach-actions {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex: none;
}

.today-coach-send {
  display: inline-grid;
  place-items: center;
  width: 36px;
  height: 36px;
  color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--bg-panel);
}

.today-coach-send svg {
  width: 18px;
  height: 18px;
}

.today-coach-mic {
  display: inline-grid;
  place-items: center;
  width: 42px;
  height: 42px;
  color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--bg-panel);
}

.today-coach-mic svg {
  width: 19px;
  height: 19px;
}

.today-coach-quick-pills {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
  width: fit-content;
  max-width: 100%;
  margin: 0 auto;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}

.today-coach-quick-pills::-webkit-scrollbar {
  display: none;
}

.today-coach-quick-pill {
  flex: 0 0 auto;
  min-height: 34px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg-panel);
  color: var(--text-secondary);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
  cursor: pointer;
}

.today-coach-quick-pill:hover,
.today-coach-quick-pill:focus-visible {
  border-color: color-mix(in srgb, var(--accent) 28%, var(--border));
  color: var(--text-primary);
  outline: none;
}

.today-actions {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.today-queue {
  min-width: 0;
}

.today-grounding {
  min-width: 0;
}

.today-lookahead-column {
  grid-area: ahead;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 22px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--accent) 3%, var(--bg-surface)), var(--bg-page) 46%);
  opacity: 0.92;
}

.today-lookahead-title {
  margin-bottom: 2px;
}

.today-section-rule {
  display: flex;
  align-items: center;
  margin-bottom: 12px;
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
}

.today-section-rule::after {
  content: none;
}

.today-queue-list,
.live-plan-list {
  overflow: hidden;
}

.today-queue-row,
.live-plan-row,
.perform-week-row,
.live-threat-preview-row {
  display: grid;
  align-items: center;
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--border-subtle);
  background: transparent;
  color: inherit;
  text-align: left;
}

.today-queue-row {
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 10px;
  padding: 14px 0;
}

.today-queue-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 58px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-panel);
  color: inherit;
  font-family: var(--font-mono);
  padding: 12px 14px;
  text-align: left;
  cursor: pointer;
}

.today-queue-card:hover,
.today-queue-card:focus-visible {
  outline: none;
  border-color: color-mix(in srgb, var(--accent) 28%, var(--border));
}

.today-queue-label,
.perform-pattern-label,
.perform-pattern-state,
.live-plan-rank,
.live-threat-preview-rank,
.live-panel-link,
.live-back {
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
}

.today-queue-text {
  min-width: 0;
  color: var(--text-primary);
  font-size: 13px;
  line-height: 1.35;
}

.today-queue-card .today-queue-text {
  flex: 1;
}

.today-queue-card + .today-queue-card {
  margin-top: 8px;
}

.today-lookahead-column .today-queue-card {
  min-height: 48px;
  padding: 12px 0;
  border: 0;
  border-top: 1px solid var(--border-subtle);
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}

.today-lookahead-column .today-queue-card:first-child {
  border-top: 0;
}

.today-lookahead-column .today-queue-card:hover,
.today-lookahead-column .today-queue-card:focus-visible {
  border-color: var(--border-subtle);
  color: var(--text-primary);
}

.today-queue-row.is-fake .today-queue-label {
  color: var(--accent);
}

.checkin-box {
  display: flex;
  min-height: 170px;
  width: 100%;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 18px;
  text-align: left;
}

.checkin-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.checkin-chip {
  color: var(--text-secondary);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
}

.checkin-mic {
  display: inline-grid;
  place-items: center;
  width: 34px;
  height: 34px;
  color: var(--accent);
  border: 1px solid var(--border);
  border-radius: 50%;
}

.checkin-mic svg {
  width: 18px;
  height: 18px;
}

.grounding-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: 100%;
  min-height: auto;
  justify-content: center;
  padding: 0 0 2px;
  margin: 0;
  overflow: hidden;
  border: 0;
  background: transparent;
  box-shadow: none;
  text-align: center;
}

.grounding-label {
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
}

.grounding-value {
  margin-top: 8px;
  color: var(--text-primary);
  font-size: clamp(82px, 9vw, 108px);
  font-weight: 300;
  line-height: 0.86;
  font-variant-numeric: tabular-nums;
}

.grounding-detail {
  margin-top: 8px;
  color: var(--text-dim);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 1.1px;
  text-transform: uppercase;
}

.today-on-deck {
  padding: 0;
  overflow: visible;
  background: transparent;
}

.perform-driver {
  display: grid;
  grid-template-columns: 8px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 11px 0;
  border-bottom: 1px solid var(--border-subtle);
}

.perform-driver-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--green);
}

.perform-driver[data-tone="red"] .perform-driver-dot { background: var(--red); }
.perform-driver[data-tone="amber"] .perform-driver-dot { background: var(--amber); }
.perform-driver[data-tone="yellow"] .perform-driver-dot { background: var(--yellow); }

.perform-driver-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.perform-driver-name {
  color: var(--text-primary);
  font-size: 13px;
}

.perform-driver-system {
  color: var(--text-dim);
  font-size: 11px;
  white-space: nowrap;
}

.perform-shell {
  display: flex;
  flex-direction: column;
  gap: 44px;
  width: 100%;
  max-width: 1360px;
  margin: 0 auto;
}

.perform-grid {
  display: grid;
  grid-template-columns: minmax(0, 760px) minmax(340px, 480px);
  grid-template-areas:
    "read week"
    "patterns state";
  justify-content: center;
  align-items: start;
  gap: 62px 64px;
}

.perform-read-card {
  grid-area: read;
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-width: 0;
  min-height: 260px;
  padding: 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-panel);
  box-shadow: var(--shadow-card);
}

.perform-read-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
}

.perform-readout {
  max-width: 620px;
  margin-top: 14px;
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: clamp(24px, 3.2vw, 38px);
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: 0;
}

.perform-score {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  color: var(--accent);
  font-family: var(--font-mono);
  text-align: right;
}

.perform-score-value {
  font-size: clamp(42px, 5vw, 64px);
  font-weight: 500;
  line-height: 0.9;
  font-variant-numeric: tabular-nums;
}

.perform-score-label,
.perform-signal-label,
.perform-week-label,
.perform-experiment-label,
.perform-experiment-status,
.perform-log-action {
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1px;
  text-transform: uppercase;
}

.perform-signal-strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  margin-top: auto;
}

.perform-signal {
  min-width: 0;
  padding: 12px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
}

.perform-signal[data-tone="strained"] {
  border-color: color-mix(in srgb, var(--amber) 36%, var(--border));
}

.perform-signal[data-tone="missing"] {
  opacity: 0.72;
}

.perform-signal-label,
.perform-signal-value {
  display: block;
  min-width: 0;
}

.perform-signal-value {
  margin-top: 6px;
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.perform-experiment {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  width: 100%;
  margin-top: 2px;
  padding: 14px 0 0;
  border: 0;
  border-top: 1px solid var(--border-subtle);
  background: transparent;
  color: inherit;
  font-family: var(--font-mono);
  text-align: left;
  cursor: pointer;
}

.perform-experiment:hover,
.perform-experiment:focus-visible {
  outline: none;
}

.perform-experiment-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 4px;
}

.perform-experiment-title {
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
}

.perform-experiment-detail {
  color: var(--text-dim);
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.45;
  letter-spacing: 0;
}

.perform-experiment-status {
  justify-self: end;
  color: var(--accent);
  white-space: nowrap;
}

.perform-week {
  grid-area: week;
}

.perform-patterns {
  grid-area: patterns;
}

.perform-body-state {
  grid-area: state;
}

.perform-week,
.perform-patterns,
.perform-body-state {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.perform-pattern-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-md);
}

.perform-pattern-card {
  display: flex;
  flex-direction: column;
  min-height: 156px;
  gap: 11px;
  padding: 18px;
  box-shadow: 0 8px 22px rgba(15, 23, 42, 0.035);
  text-align: left;
}

.perform-pattern-state {
  color: var(--accent);
}

.perform-week-list,
.perform-driver-list {
  border-top: 1px solid var(--border-subtle);
}

.perform-empty {
  padding: 14px 0;
}

.perform-week-row {
  grid-template-columns: 68px minmax(0, 1fr) auto;
  gap: 12px;
  padding: 15px 0;
  border-radius: 0;
  border-right: 0;
  border-left: 0;
  border-top: 0;
  background: transparent;
}

.perform-week-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 4px;
}

.perform-week-title {
  min-width: 0;
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
}

.perform-week-reason,
.perform-body-caption,
.perform-log-copy {
  color: var(--text-dim);
  font-family: var(--font-sans);
  font-size: 13px;
  line-height: 1.45;
  letter-spacing: 0;
}

.perform-body-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}

.perform-body-caption {
  text-align: right;
}

.perform-log-prompt {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  min-width: 0;
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-panel);
}

.perform-log-icon {
  display: inline-grid;
  place-items: center;
  width: 34px;
  height: 34px;
  color: var(--accent);
  border-radius: 50%;
  background: var(--leverage-bg);
  font-family: var(--font-sans);
  font-size: 26px;
  line-height: 1;
}

.perform-log-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 2px;
}

.perform-log-title {
  color: var(--text-primary);
  font-size: 14px;
  font-weight: 600;
}

.perform-log-action {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border: 0;
  background: transparent;
  color: var(--accent);
  font-family: var(--font-mono);
  white-space: nowrap;
  cursor: pointer;
}

.perform-log-action:hover,
.perform-log-action:focus-visible {
  color: var(--text-primary);
  outline: none;
}

.live-view {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 360px);
  gap: var(--space-lg);
  align-items: start;
}

.live-root-view {
  padding: 0;
}

.live-intro {
  width: calc(100% - (var(--space-lg) * 2));
  max-width: 1360px;
  margin: 0 auto;
  padding: var(--space-lg) 0 0;
  box-sizing: border-box;
}

.live-arc-shell {
  min-height: 0;
  align-items: flex-start;
  padding-top: 44px;
}

.live-arc {
  max-width: 900px;
  gap: 32px;
}

.live-arc-headline {
  align-items: flex-start;
  gap: 64px;
}

.live-arc-life {
  min-width: min(340px, 100%);
  align-self: flex-start;
}

.live-arc-actions {
  flex: 0 0 auto;
  width: 440px;
  max-width: 100%;
  min-width: 0;
  padding: 32px 0;
  display: flex;
  flex-direction: column;
}

.live-arc-action-stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 14px;
}

.live-arc-supporting-action {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-panel);
  box-shadow: 0 8px 22px rgba(15, 23, 42, 0.035);
}

.live-arc-featured-action {
  background: var(--leverage-bg);
}

.live-arc-plan-link {
  margin-top: 14px;
}

.live-arc-timeline {
  position: relative;
  width: 100%;
}

.live-arc-timeline-head {
  position: relative;
}

.live-arc-threat-link {
  position: absolute;
  top: 0;
  right: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
}

.live-hero {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  gap: var(--space-xl);
  align-items: center;
  padding-bottom: var(--space-md);
}

.live-age {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  border: 0;
  background: transparent;
  color: inherit;
  padding: 0;
}

.live-age-number {
  color: var(--text-primary);
  font-size: 112px;
  font-weight: 300;
  line-height: 0.88;
  font-variant-numeric: tabular-nums;
}

.live-age-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 16px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
}

.live-panel-link {
  border: 0;
  background: transparent;
  padding: 0;
}

.live-panel-link:hover,
.live-panel-link:focus-visible {
  color: var(--text-primary);
  outline: none;
}

.live-threats {
  grid-row: span 2;
}

.live-threat-preview,
.live-threat-preview-blank {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--border-subtle);
}

.live-threat-preview-blank {
  gap: 4px;
  padding: 14px 0 0;
}

.live-threat-preview-row {
  grid-template-columns: 26px 28px minmax(0, 1fr);
  gap: 10px;
  padding: 13px 0;
}

.live-threat-preview-row:last-child,
.live-plan-row:last-child,
.perform-week-row:last-child,
.perform-driver:last-child,
.today-queue-row:last-child {
  border-bottom: 0;
}

.live-arc-supporting-action.live-plan-row,
.live-arc-supporting-action.live-plan-row:last-child {
  border: 1px solid var(--border);
}

.live-threat-preview-icon {
  display: inline-flex;
  color: var(--text-dim);
}

.live-threat-preview-main,
.live-plan-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 3px;
}

.live-threat-preview-name,
.live-threat-preview-title {
  letter-spacing: 1.4px;
  text-transform: uppercase;
}

.live-plan-list,
.live-plan-empty {
  background: var(--bg-panel);
}

.live-plan-empty {
  gap: 8px;
  padding: 18px 20px;
}

.live-plan-row {
  grid-template-columns: 34px minmax(0, 1fr) auto;
  gap: 12px;
  padding: 14px 16px;
}

.live-detail-view {
  max-width: 1040px;
  margin: 0 auto;
  padding-top: 16px;
}

.live-threat-index-shell .risks-view .view-header,
.live-plan-index-shell .plan-view .view-header {
  display: none;
}

.live-threat-index-shell .risks-view,
.live-plan-index-shell .plan-view {
  padding: 0;
}

@media (max-width: 720px) {
  .product-surface {
    padding: 34px var(--space-md) var(--space-lg);
  }

  .surface-grid,
  .today-grid,
  .today-hero,
  .today-now-column,
  .today-lookahead-column,
  .perform-shell,
  .live-view,
  .live-hero {
    display: flex;
    flex-direction: column;
    gap: 28px;
  }

  .perform-pattern-grid {
    grid-template-columns: 1fr;
  }

  .perform-grid {
    display: flex;
    flex-direction: column;
    gap: 28px;
  }

  .perform-read-card {
    min-height: 0;
    padding: 20px;
  }

  .perform-read-top {
    flex-direction: column;
    gap: 22px;
  }

  .perform-readout {
    font-size: clamp(26px, 8vw, 34px);
  }

  .perform-score {
    align-items: flex-start;
    text-align: left;
  }

  .perform-signal-strip {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .perform-experiment {
    grid-template-columns: minmax(0, 1fr);
    gap: 8px;
  }

  .perform-experiment-status {
    justify-self: start;
  }

  .perform-week-row {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 10px;
  }

  .perform-week-label {
    grid-column: 1 / -1;
  }

  .perform-body-head {
    align-items: flex-start;
    flex-direction: column;
    gap: 4px;
  }

  .perform-body-caption {
    text-align: left;
  }

  .perform-driver {
    grid-template-columns: 8px minmax(0, 1fr);
  }

  .perform-driver-system {
    grid-column: 2;
    white-space: normal;
  }

  .perform-log-prompt {
    grid-template-columns: auto minmax(0, 1fr);
    align-items: start;
  }

  .perform-log-action {
    grid-column: 2;
    justify-self: start;
  }

  .today-action-stack {
    display: flex;
    flex-direction: column;
    gap: 10px;
  }

  .today-hero {
    gap: var(--space-md);
    width: 100%;
  }

  .today-grid {
    align-items: stretch;
  }

  .today-coach-panel {
    min-height: 390px;
  }

  .today-coach-main {
    flex: 0 0 auto;
    margin: 68px 0 34px;
    min-height: 0;
  }

  .today-greeting {
    margin-bottom: 0;
    padding-left: 4px;
  }

  .today-coach-composer {
    width: 100%;
    min-height: 76px;
    padding: 12px 12px 12px 18px;
  }

  .today-coach-placeholder {
    max-width: none;
    font-size: 14px;
  }

  .today-coach-send {
    width: 34px;
    height: 34px;
  }

  .today-coach-mic {
    width: 38px;
    height: 38px;
  }

  .today-coach-quick-pills {
    width: 100%;
    margin: 0;
    padding-bottom: 2px;
  }

  .today-primary-move .today-action-card,
  .today-primary-move .today-primary-empty {
    min-height: 174px;
    padding: 20px;
  }

  .today-actions .today-action-card,
  .today-actions .today-primary-empty {
    min-height: 92px;
    padding: 15px 16px;
  }

  .today-actions {
    padding-bottom: 0;
    border-bottom: 0;
  }

  .today-lookahead-column {
    gap: 14px;
    margin: 22px calc(var(--space-md) * -1) calc(var(--space-lg) * -1);
    padding: 34px var(--space-md) var(--space-lg);
    border: 0;
    border-top: 1px solid var(--border);
    border-radius: 0;
    background:
      linear-gradient(180deg, color-mix(in srgb, var(--accent) 4%, var(--bg-surface)), var(--bg-page) 42%);
  }

  .today-grounding,
  .today-queue {
    padding-left: 0;
    border-left: 0;
  }

  .today-section-rule {
    margin-bottom: 14px;
  }

  .grounding-value {
    font-size: 118px;
  }

  .grounding-card {
    align-items: center;
    width: 100%;
    min-height: auto;
    padding: 14px 0 18px;
    border: 0;
    background: transparent;
    box-shadow: none;
    text-align: center;
  }

  .today-grounding {
    padding-top: 8px;
    padding-bottom: 8px;
  }

  .today-queue-card {
    min-height: 64px;
    background: var(--bg-panel);
    box-shadow: 0 10px 24px rgba(15, 23, 42, 0.04);
  }

  .today-lookahead-column .today-queue-card {
    padding: 14px 16px;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--bg-panel);
  }

  .today-lookahead-column .today-queue-card:first-child {
    border-top: 1px solid var(--border);
  }

  .today-lookahead-column .today-queue-card + .today-queue-card {
    margin-top: 10px;
  }

  .today-queue-meta {
    display: none;
  }

  .live-age-number {
    font-size: 88px;
  }

  .live-intro {
    width: 100%;
    margin: 0;
    padding: 0;
  }

  .live-arc-shell {
    min-height: 0;
    padding: 34px var(--space-md) 0;
  }

  .live-arc {
    gap: 30px;
  }

  .live-arc-headline {
    gap: 34px;
  }

  .live-arc-life {
    width: min(300px, 100%);
    max-width: 100%;
    min-width: 0;
    align-self: center;
    padding: 24px 18px 28px;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--bg-panel);
    box-shadow: 0 10px 24px rgba(15, 23, 42, 0.035);
    box-sizing: border-box;
  }

  .live-arc-actions {
    width: 100%;
    padding: 0 0 8px;
  }

  .live-arc-featured-action {
    background: var(--bg-panel);
  }

  .live-arc-plan-link {
    display: inline-flex;
    align-self: flex-end;
    justify-content: flex-end;
    margin-top: 14px;
    padding: 0;
    border-top: 0;
    text-align: right;
  }

  .live-arc-timeline {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding-top: 4px;
    border-top: 0;
  }

  .live-arc-timeline-head {
    display: flex;
    flex-direction: column;
  }

  .live-arc-threat-link {
    position: static;
    align-self: flex-end;
    justify-content: flex-end;
    margin: 16px 0 0;
    padding: 0;
    border-top: 0;
    text-align: right;
  }

  .live-detail-view {
    padding: 14px var(--space-md) var(--space-lg);
  }
}
