/* PHASE·TIMELINE (603) — the milestone year, five checkpoint cards. D-652 · D-653 · D-654.
 *
 * EXTRACTED 2026-07-30 from workbench/phase/_bakeoffs/timeline-v2.html's inline <style>,
 * VERBATIM. Nothing was redesigned on the way across: the settled design is nine live
 * rounds old (the record lives in the card page header, workbench/phase/timeline.html)
 * and a promotion is not a design round. Selector for selector, value for value, this is
 * the bake-off's stylesheet — which is what makes the extraction checkable by diff.
 *
 * ONE DEFINITION. The bake-off page is DELETED; the WB card page and a future app mount
 * both link this file. There is no second copy to drift from.
 *
 * ⚠️ TOKENISATION OWED (the debt travels with the code, unchanged from the bake-off, and
 * it is the same debt 602/604 carry): the 6px trail stroke, pebble radii (48), glyph sizes
 * (20px), the 2.5px/1.5px pebble and button borders, the popup art height and the pill's
 * 70px park are ENGINE numbers in JS or bare px here, not CSS tokens. Marked
 * token-exception where the linter sees them; owed properly at the app build.
 *
 * ⚠️ THE ONE SCOPING DELTA. The bake-off page ended with a bare `svg { display: block; }`,
 * which was legal inside one page's <style> and is not legal in shared card CSS — it would
 * restyle every <svg> in the app. It is scoped to `.mtl-wrap svg` here, which covers exactly
 * the elements it covered before (the river, every glyph, the popup art) and nothing else.
 *
 * ⚠️ NOT FIXED, DELIBERATELY (flagged, per the promotion rule — see the card page header):
 * `.mtl-cta` and `.mtl-pill` carry `:active` but NO `:focus-visible`, while every other
 * control in this card carries both. Check 18 (G6 micro-states) does not bite at `designed`;
 * it will the day 603 moves to `draft`. Adding the two rules here would have been a design
 * change smuggled in under a port, so they are named instead.
 *
 * ⚠️ THE ONE ADDITION AT PORT — `.mtl-calm`. The reduced-motion state has to be REVIEWABLE,
 * which means forced in the frame rather than left to the reviewer's OS setting (a state
 * nobody can see is not a state). `.mtl-calm` is a class-selector MIRROR of the
 * @media (prefers-reduced-motion: reduce) block below — same declarations, same order, no
 * new behaviour. The media query stays and still governs real users. If you edit one block
 * you edit both, or the forced state stops telling the truth about the real one.
 *
 * SaMD (D-396 r5/7, D-630): parent-led marking only; no counts, scores, percentages or
 * on-track verdicts; checkpoints never gate; unmarked pebbles sit calm forever.
 */

/* ── the surface ─────────────────────────────────────────────────────────── */
.mtl-wrap { height: 100%; display: flex; flex-direction: column; background: var(--surface-ground); position: relative; }
.mtl-sc { flex: 1; overflow-y: auto; scrollbar-width: none; position: relative; }
.mtl-sc::-webkit-scrollbar { display: none; }

/* ⚠️ 603 AT DESKTOP WIDTH — added 2026-07-30, Gate 2 item 1. Two rules, both scoped to
   603's own boxes. Do not generalise either to #app or any shared container.

   (1) THE COLUMN. Every other surface in the app sits in the shared content column
   (`#app { max-width: var(--layout-col-max) }`, and `.wkr` / `.tqp` repeat it for the
   full-screen drill-ins they own). 603 is a drill-in too, but `.mtl-host` mounts it
   `position: fixed; inset: 0`, so it escaped the column and ran the full width of the
   window. It joins the same column here, by the same token — not a new max-width, the
   app's existing one.

   (2) THE DRAWING BLOCK. `.mtl-cw` is the one box on this surface that cannot reflow:
   the engine draws at a FIXED card width (timeline.render.js `VW = 338`), so pebble
   centres, the river's sample points and every label box are absolute card-local
   pixels — and left-side labels are laid from the card's RIGHT edge (`right: VW - edge`).
   Drawing and labels only line up when the block is ACTUALLY 338 wide. They came apart
   by the width of a window at desktop width (trail pinned at 338 on the left, every
   label flung to the right margin) and — quieter, and the reason item 3's collisions
   were real while `verify()` read zero — by 20px at phone width too: the WB app frame
   gives 603 a 370px screen (390 − 2×10 bezel), the app has no bezel and handed it 390.
   `verify()` measures in the engine's own 338 frame, so it could not see the gap between
   what it planned and what the browser drew. Pinning the block closes it: 18 rendered
   trail×label overlaps at 390 → 0.

   Everything else on the surface — the intro carousel, the safety band, the checkpoint
   prose, the sources box — keeps reflowing exactly as it does now.

   🔴 DO NOT make the timeline responsive instead. Its geometry was tuned at this one
   width and `verify()` has only ever measured collisions here; a fluid timeline is a
   separate piece of work with its own bake-off. */
.mtl-wrap { max-width: var(--layout-col-max); margin-inline: auto; }
.mtl-cw { width: 338px; margin-inline: auto; } /* token-exception */ /* = timeline.render.js VW */

/* slim drill-in TOP BAR — page chrome lives above the cards, never inside one
   (Andrew, round 6: "should we have something different at the top?") */
.mtl-top { flex: none; display: flex; align-items: center; gap: var(--space-8); padding: var(--space-12) var(--space-16) var(--space-4); }
.mtl-toplab { font-family: var(--font-label); font-size: var(--size-10, 10px); font-weight: var(--weight-700); letter-spacing: var(--tracking-05); text-transform: uppercase; color: var(--text-secondary); } /* token-exception */
.mtl-back { width: var(--dim-32); height: var(--dim-32); border-radius: var(--radius-round); border: 0; background: none; color: var(--text-secondary); cursor: pointer; display: flex; align-items: center; justify-content: center; margin-left: calc(-1 * var(--space-8)); }
.mtl-back svg { width: var(--dim-16); height: var(--dim-16); }
.mtl-back:active { opacity: 0.72; }
.mtl-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* token-exception */

/* hero (drill-in intro) — a 2-SLIDE CAROUSEL card (Andrew, round 7): slide 1 the
   intro, slide 2 Caïa's record. D-458 grammar: one-at-a-time, swipe/snap or tap a
   dot, stops at the ends, NO arrows. */
.mtl-intro { margin: var(--space-4) var(--space-16) var(--space-12); background: var(--surface-card); border-radius: var(--radius-card-lg); padding: 0; overflow: hidden; }
.mtl-cart { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; }
.mtl-cart::-webkit-scrollbar { display: none; }
.mtl-slide { flex: 0 0 100%; scroll-snap-align: start; padding: var(--space-16) var(--space-16) var(--space-8); }
.mtl-dots { display: flex; gap: var(--space-8); justify-content: center; padding: var(--space-8) 0 var(--space-12); }
.mtl-dot { width: var(--dim-6); height: var(--dim-6); border-radius: var(--radius-round); background: var(--cream-450); border: 0; padding: 0; cursor: pointer; } /* token-exception */
.mtl-dot.on { background: var(--accent); }
/* slide 2 — the record's CTA */
.mtl-cta { display: inline-flex; align-items: center; gap: var(--space-8); margin-top: var(--space-12); border: 0; border-radius: var(--radius-pill); background: var(--accent); color: var(--text-on-accent); font-family: var(--font-body); font-size: var(--size-12); font-weight: var(--weight-600); padding: var(--space-8) var(--space-16); cursor: pointer; }
.mtl-cta svg { width: var(--dim-12, 12px); height: var(--dim-12, 12px); } /* token-exception */
.mtl-cta:active { background: var(--accent-hover); }
.mtl-ie { font-family: var(--font-label); font-size: var(--size-10, 10px); font-weight: var(--weight-700); letter-spacing: var(--tracking-05); text-transform: uppercase; color: var(--accent); } /* token-exception */
.mtl-it { font-family: var(--font-display); font-weight: var(--weight-600); font-size: var(--size-20); margin-top: var(--space-4); line-height: var(--leading-120, 1.2); } /* token-exception */
.mtl-it i, .mtl-hm i, .mtl-pt i, .mtl-cft i { color: var(--accent); font-style: normal; }
.mtl-ib { font-size: var(--size-12); line-height: var(--leading-140); color: var(--text-secondary); margin-top: var(--space-4); }
/* sources box — the weeks-article grammar (read.css .wkr-sources), verbatim */
.mtl-sources { margin: var(--space-4) var(--space-16) var(--space-16); background: var(--surface-card); border: 1px solid var(--border-hairline); border-radius: var(--radius-card); padding: var(--space-16); } /* token-exception */
.mtl-sources .ttl { font-family: var(--font-label); font-size: var(--size-10, 10px); font-weight: var(--weight-600); text-transform: uppercase; letter-spacing: var(--tracking-05); color: var(--text-tertiary); margin-bottom: var(--space-8); } /* token-exception */
.mtl-sources ul { list-style: none; margin: 0; padding: 0; }
.mtl-sources li { font-size: var(--size-12); line-height: var(--leading-150); padding-left: var(--space-16); position: relative; margin-bottom: var(--space-4); color: var(--text-secondary); }
.mtl-sources li::before { content: "—"; position: absolute; left: 0; color: var(--text-tertiary); }
.mtl-sources li b { color: var(--text-primary); }
.mtl-sources a { color: var(--accent); }
.mtl-sources b a { color: var(--text-primary); text-decoration-color: var(--accent); }
/* the act-early band — the D-614 watchbox grammar, verbatim from read.css */
.mtl-safe { margin: 0 var(--space-16) var(--space-12); background: var(--gold-90); border: 1px solid var(--gold-200); border-radius: var(--radius-card); padding: var(--space-16); } /* token-exception */
.mtl-sqk { display: flex; align-items: center; gap: var(--space-4); font-family: var(--font-label); font-size: var(--size-10, 10px); font-weight: var(--weight-700); letter-spacing: var(--tracking-05); text-transform: uppercase; color: var(--gold-700); } /* token-exception */
.mtl-sqk svg { width: var(--dim-16); height: var(--dim-16); }
.mtl-safe p { font-size: var(--size-13); line-height: var(--leading-150); margin: var(--space-8) 0 0; }

/* ── ONE CHECKPOINT PER CARD (Andrew: "one checkpoint per card, split — 5 cards").
   Header in flow at the card's top, the river restarting below it. D-435: tone
   lift, one level of carding. */
.mtl-card { margin: 0 var(--space-16) var(--space-16); background: var(--surface-card); border-radius: var(--radius-card-lg); overflow: hidden; }
.mtl-hd { padding: var(--space-16) var(--space-16) var(--space-8); }
.mtl-hk { font-family: var(--font-label); font-size: var(--size-10, 10px); font-weight: var(--weight-700); letter-spacing: var(--tracking-05); color: var(--accent-hover); text-transform: uppercase; } /* token-exception */
.mtl-hm { font-family: var(--font-display); font-weight: var(--weight-600); font-size: var(--size-20); margin-top: 2px; line-height: var(--leading-120, 1.2); } /* token-exception */
.mtl-ho { font-size: var(--size-12); line-height: var(--leading-140); color: var(--text-secondary); margin-top: var(--space-4); }
.mtl-how { font-size: var(--size-12); line-height: var(--leading-140); color: var(--accent); font-weight: var(--weight-700); margin-top: var(--space-8); }
.mtl-cw { position: relative; }

/* the river svg inside each card */
.mtl-svg { position: absolute; left: 0; top: 0; z-index: 2; pointer-events: none; }

/* pebbles — 3D flip pin */
.mtl-pw { position: absolute; z-index: 4; background: none; border: 0; padding: 0; cursor: pointer; transform: scale(0); transition: transform var(--duration-400, .45s) cubic-bezier(.34,1.56,.5,1); perspective: 300px; } /* token-exception */
.mtl-pw.on { transform: scale(1); }
.mtl-pw:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-round); } /* token-exception */
.mtl-pin { display: block; position: relative; width: 100%; height: 100%; transform-style: preserve-3d; transition: transform var(--duration-500, .6s) cubic-bezier(.4,.1,.2,1); } /* token-exception */
.mtl-pw.met .mtl-pin { transform: rotateY(180deg); }
.mtl-face { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-round); backface-visibility: hidden; -webkit-backface-visibility: hidden; }
/* unmet: the OUTLINE carries colour(t) (the shade the met face flips to), but the
   GLYPH is always dark ink — legibility beats the gradient (Andrew: "the question
   mark is not legible enough. should be in dark, as well as other icons not
   marked") */
.mtl-f0 { background: var(--cream-50); border: 2.5px solid var(--teal-200); color: var(--ink-900); } /* token-exception */
.mtl-f1 { color: var(--text-on-accent); transform: rotateY(180deg); }
.mtl-face svg { width: 20px; height: 20px; } /* glyph up from 16 (Andrew: "slightly bigger") */ /* token-exception */

/* labels — bare text, close to their pebbles, balanced lines (Andrew feedback:
   pills gone is fine · closer to the circles · no line massively longer · ≤4 lines).
   font-size/line-height ON the label: without them the line boxes carry the
   parent's 16px strut and every line silently advances ~18px instead of 15. */
.mtl-lab { position: absolute; z-index: 3; cursor: pointer; opacity: 0; transform: translateY(var(--dim-12)); transition: opacity var(--duration-400, .4s), transform var(--duration-400, .4s); padding: 2px 0; font-size: var(--size-12); line-height: var(--leading-125, 1.25); text-wrap: balance; } /* token-exception */
.mtl-lab.on { opacity: 1; transform: none; }
.mtl-nm { font-size: var(--size-12); font-weight: var(--weight-600); line-height: var(--leading-125, 1.25); } /* token-exception */

/* Now — visually removed (round 6); a zero-height anchor keeps open-at-now +
   the Back-to-today pill working while the presence question stays open */
.mtl-nowanchor { height: 0; margin: 0; visibility: hidden; }

/* back-to-today pill */
.mtl-pill { position: absolute; bottom: var(--space-16); left: 50%; transform: translateX(-50%) translateY(70px); background: var(--surface-inverse); color: var(--text-on-dark, #fffbf2); border: 0; border-radius: var(--radius-pill); padding: var(--space-8) var(--space-16); font-family: var(--font-body); font-size: var(--size-12); font-weight: var(--weight-600); cursor: pointer; transition: transform var(--duration-200); z-index: 8; } /* token-exception */
.mtl-pill.show { transform: translateX(-50%); }
.mtl-pill:active { opacity: 0.85; }

/* anchored popup */
.mtl-pop { position: absolute; left: var(--space-16); right: var(--space-16); background: var(--cream-50); border-radius: var(--radius-card-lg); padding: var(--space-16); box-shadow: var(--shadow-sheet); z-index: 9; display: none; }
.mtl-pop.show { display: block; }
.mtl-art { height: 120px; border-radius: var(--radius-12); background: var(--teal-100); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-8); margin-bottom: var(--space-12); color: var(--accent-hover); } /* token-exception */
.mtl-art svg { width: var(--dim-40); height: var(--dim-40); }
.mtl-artc { font-family: var(--font-display); font-size: var(--size-11); font-style: italic; color: var(--text-secondary); text-align: center; padding: 0 var(--space-16); }
/* the labelled area chip — icon + area name together, icon in the pebble's own
   colour(t); sits bare above the title (D-435: chips are not boxed) */
.mtl-chip { display: inline-flex; align-items: center; gap: var(--space-8); font-family: var(--font-label); font-size: var(--size-10, 10px); font-weight: var(--weight-700); letter-spacing: var(--tracking-05); text-transform: uppercase; color: var(--text-secondary); margin-bottom: var(--space-4); } /* token-exception */
.mtl-chip-ic { display: inline-flex; }
.mtl-chip-ic svg { width: var(--dim-16); height: var(--dim-16); }
.mtl-pt { font-family: var(--font-display); font-weight: var(--weight-600); font-size: var(--size-16); }
.mtl-pdesc { font-size: var(--size-12); line-height: var(--leading-140); color: var(--text-secondary); margin-top: var(--space-4); }
.mtl-pr { font-family: var(--font-display); font-size: var(--size-12); color: var(--text-secondary); margin-top: var(--space-8); }
.mtl-pq { font-family: var(--font-display); font-weight: var(--weight-600); font-size: var(--size-14); margin-top: var(--space-12); }
.mtl-pb { display: flex; gap: var(--space-8); margin-top: var(--space-12); }
.mtl-btn { flex: 1; border-radius: var(--radius-12); padding: var(--space-12); font-family: var(--font-body); font-size: var(--size-12); font-weight: var(--weight-600); cursor: pointer; }
.mtl-btn:active { opacity: 0.85; }
.mtl-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* token-exception */
/* Yes + any selected answer wear the pebble's own colour(t) — set per-popup as --sel-col */
.mtl-yes { background: var(--sel-col, var(--accent)); color: var(--sel-on, var(--text-on-accent)); border: 0; }
.mtl-no { background: none; border: 1.5px solid var(--cream-450); color: var(--text-primary); } /* token-exception */
.mtl-no:active { border-color: var(--sel-col, var(--accent)); color: var(--sel-col, var(--accent)); opacity: 1; }
.mtl-conf { text-align: center; padding: var(--space-4) 0; animation: mtlIn var(--duration-150) ease-out; }
@keyframes mtlIn { from { opacity: 0; } to { opacity: 1; } }
.mtl-pop.hide { opacity: 0; transform: translateY(8px); transition: opacity var(--duration-150), transform var(--duration-150); } /* token-exception */
.mtl-cft { font-family: var(--font-display); font-weight: var(--weight-600); font-size: var(--size-16); }
.mtl-cfs { font-size: var(--size-11); color: var(--text-secondary); margin-top: var(--space-4); }
/* WHEN did it happen (Andrew: parents may not have been in for a while) — a
   persistent SLIDER on the confirmation (a stepper is 18 taps for months-ago);
   annotates, never nags. When? + Week N in teal bold. */
.mtl-when { display: flex; align-items: center; justify-content: center; gap: var(--space-12); margin-top: var(--space-12); }
.mtl-whenq { font-size: var(--size-11); font-weight: var(--weight-700); color: var(--accent); }
.mtl-range { flex: 1; min-width: 0; accent-color: var(--sel-col, var(--accent)); }
.mtl-wlab { min-width: 64px; text-align: right; font-size: var(--size-12); font-weight: var(--weight-700); color: var(--accent); } /* token-exception */
.mtl-done { flex: none; display: block; margin: var(--space-12) auto 0; padding: var(--space-8) var(--space-24); }
/* the reopened, already-marked popup states it plainly, in teal */
.mtl-ach { font-family: var(--font-display); font-weight: var(--weight-600); font-size: var(--size-12); color: var(--accent); margin-top: var(--space-12); }

/* the ONE gold moment (D-440) */
.mtl-ring { position: absolute; border-radius: var(--radius-round); border: 3px solid var(--gold-500); pointer-events: none; z-index: 7; animation: mtlRg .7s ease-out forwards; } /* token-exception */
@keyframes mtlRg { from { opacity: .9; transform: scale(.4); } to { opacity: 0; transform: scale(1.9); } }

/* reduced motion: same DOM, laid out static, fully inked (JS also skips choreography) */
@media (prefers-reduced-motion: reduce) {
  .mtl-pw, .mtl-lab { opacity: 1; transform: none; transition: none; }
  .mtl-pin { transition: none; }
  .mtl-ring { animation: none; opacity: 0; }
}
/* ⚠️ THE FORCED MIRROR of the block above — see the header. Same declarations, same
   order. Edit both or neither: a forced state that has drifted from the real one is a
   state review passes and a real parent never sees. */
.mtl-calm .mtl-pw, .mtl-calm .mtl-lab { opacity: 1; transform: none; transition: none; }
.mtl-calm .mtl-pin { transition: none; }
.mtl-calm .mtl-ring { animation: none; opacity: 0; }
.mtl-wrap svg { display: block; }
