/* PHASE·HERO (602) — the Phases tab hero, the swipe deck. D-649.
 *
 * EXTRACTED 2026-07-26 from workbench/phase/hero.html's inline <style>, verbatim.
 * This file is now the ONE definition: the WB card page, the composed tab frame
 * (WBAppFrame.phases) and the app all link it. Nothing re-declares .phz-*.
 *
 * ⚠️ TOKENISATION STILL OWED (D-649's own note, carried across unchanged so the debt
 * travels with the code rather than being quietly dropped at extraction): the 1.5px
 * border, the glyph's 3px gap / 7px height / 68px width, the 96px tile and the 2px
 * nudges are raw px. They are marked token-exception and want --dim- / --space- stops
 * minting at the app build, not inventing here — an extraction that also redesigns is
 * an extraction you cannot verify with a pixel diff.
 *
 * ⚠️ NEVER WRITE A TOKEN GLOB IMMEDIATELY FOLLOWED BY A SLASH INSIDE A CSS COMMENT. The
 * first draft of this header abbreviated two token families as "dim-star-slash-space-star",
 * and the star-slash in the middle of that CLOSED THE COMMENT. The parser then choked on
 * the remaining prose and swallowed the very next rule — .phz-card — so the hero rendered
 * with no card behind it. It cost two render cycles to find, because the stylesheet loaded
 * fine, reported no error, and every OTHER rule in it applied normally. Write the families
 * out in words, or separate them with " or ".
 */
.phz-card { background: var(--surface-card); border-radius: var(--radius-card-lg); margin: 0 var(--space-16) var(--space-12); padding: var(--space-16) var(--space-20) 0; overflow: hidden; }
/* ⚠️ NO GAP, NO NEGATIVE MARGIN, NO PADDING — the slides are full width and nothing peeks.
   See the note on .phz-box: with the outline gone a peek is clipped text, not a card edge. */
.phz-deck { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; cursor: grab; }
.phz-deck::-webkit-scrollbar { display: none; }
.phz-slide { flex: 0 0 100%; scroll-snap-align: center; display: flex; }
.phz-slide > * { flex: 1; }
/* 🔴 THE BOX IS GONE (2026-07-26, Andrew: "I prefer the article as it's cleaner").
 * It was a 1.5px --teal-600 outline on --cream-50 INSIDE .phz-card, which is already a
 * raised surface — a box inside a box, which D-435 forbids ("ONE level of carding — no
 * boxes nested inside a card"). The article hero obeys that by having no box at all, and
 * aligning the deck to it is what this change is.
 * ⚠️ THE BOX WAS LOAD-BEARING FOR ONE THING: the deck peeks its neighbours at both edges,
 * and the outline was what made a clipped edge read as "another card". Without it a peek is
 * clipped floating TEXT, which reads as a rendering fault. That is why .phz-deck below drops
 * the peek entirely and .phz-bars becomes the only deck indicator. The two changes are not
 * separable — do not reinstate the peek without reinstating a visible slide edge. */
.phz-box { display: block; text-align: center; }
.phz-tile { display: block; text-align: center; }
.phz-tile .ey { font-family: var(--font-label); font-size: var(--text-label); font-weight: var(--weight-600); letter-spacing: var(--tracking-14); text-transform: uppercase; color: var(--accent); text-indent: var(--tracking-14); }
.phz-tile .n { font-family: var(--font-display); font-weight: var(--weight-600); font-size: var(--size-60); line-height: var(--leading-100); letter-spacing: var(--tracking-neg-015); color: var(--nav-active); margin-top: var(--space-4); }
/* 🔴 THE RANGE NOW LIVES IN THE BODY, UNDER THE NAME, IN GREY. Two changes in one:
 * ORDER — the article reads PHASE · numeral · NAME · weeks, and the deck read
 *   PHASE · numeral · WEEKS · name because the range sat inside the tile. CSS could not fix
 *   it: `order` only reorders siblings and these were in different parents, so slide() below
 *   emits the range as part of .phz-body instead.
 * COLOUR — it was --teal-700, the same value as the numeral and the name, so three different
 *   facts shared one voice. --text-secondary measures 6.74:1 on --cream-50 and passes AA.
 *   ⚠️ --text-tertiary was tried and REJECTED at 3.38:1 — it fails AA for normal text, and
 *   the range is not decoration: D-660 makes it hero-level information. If it reads heavy,
 *   drop the SIZE, never the contrast. */
.phz-body .rg { font-family: var(--font-display); font-weight: var(--weight-500); font-size: var(--size-16); color: var(--text-secondary); margin-top: var(--space-4); }
.phz-body .rgy { font-family: var(--font-display); font-weight: var(--weight-500); font-size: var(--size-16); color: var(--text-secondary); margin-top: var(--space-4); }
/* 🔴 THE FOREST RING IS REMOVED (2026-07-26, Andrew: "remove the outline"). It encircled the
   live pill and drew in on swipe-on, matched to the milestone trail at 450ms.
   ⚠️ WHAT WENT WITH IT, so it is not rediscovered as a bug: paint() fills the bars by `idx`,
   the slide you have swiped TO — not by LIVE, the phase her baby is IN. So the fills are a
   position indicator and the ring was the ONLY thing on this card saying "this is the one
   she's on". Swipe away and nothing now distinguishes looking-at from being-in, which is the
   orientation job D-647 gives this tab. A 3px forest dot under her pill was built and shown
   (`hero-align.html`, C1m) and is NOT adopted here — reinstate it if the gap bites.
   ⚠️ RECORD OF WHAT WAS TRIED ON THE SLIDE, so none of it is re-run: the whole box in
   --accent (a slab, six colour overrides to stay legible) · in --phase-1 (but --phase-1
   MEANS phase one, on a box showing phase 3) · in --teal-100 (legible, silent about WHICH
   phase) · the TILE in its own phase colour (semantically right, but no type colour reached
   AA — teal 3.24/2.50/1.88/1.38/1.00, white 1.32/1.71/2.27/3.09/4.27, best 4.27 vs a 4.5
   threshold). */
.phz-bar { position: relative; }
@media (prefers-reduced-motion: reduce) { .phz-bar { transition: none; } }
/* 🔴 THESE WERE LOST AND RESTORED — FOR THE SECOND TIME, THE SAME WAY. (2026-07-26)
 * The header of this file already carries the warning from the first time; I did it again
 * while porting the redesign, by replacing everything between two string indexes to strike
 * the ring block. The cut swallowed .phz-glyph, .phz-glyph span, .phz-yspacer,
 * .phz-yglyphbox, .phz-body, .phz-body .nm, .phz-body .st and .phz-dot.
 * The page still rendered: the name and standfirst simply fell back to inherited Inter 16px
 * ink, the year glyph lost its stack, and NOTHING ERRORED. It was found by reading computed
 * styles, not by looking — .phz-body .rg was correct on screen while its two neighbours
 * silently were not.
 * ⚠️ INDEX-BASED BLOCK REPLACEMENT IS NOT EDITING, IT IS CUTTING BLIND. Replace a NAMED rule
 * or nothing. Third occurrence in one session; if it happens a fourth time the answer is a
 * check, not another comment. */
.phz-glyph { display: flex; flex-direction: column; align-items: center; gap: 5px; width: 9rem; height: auto; margin: 0 auto; } /* token-exception */
/* ⚠️ min-height, NOT height. Each span carries an INLINE `flex:9` (etc) from glyph() so the
   widths stay proportional. Inline beats stylesheet, and `flex:9` expands to `flex-basis:0%`
   — which in a COLUMN is the main-axis size and outranks `height`. Every row resolves to
   ZERO and the glyph measures 24px: four gaps and five invisible rows. min-height is not a
   main-size property, so it survives. */
.phz-glyph span { min-height: 6px; height: 6px; border-radius: var(--radius-pill); } /* token-exception */
/* proportional by real week count — 9/8/9/13/13 of 52 (D-660: at this scale "the SHAPE OF
   THE YEAR becomes readable, which is the whole reason the widths are proportional") */
.phz-glyph span:nth-child(1) { width: 17.3%; }
.phz-glyph span:nth-child(2) { width: 15.4%; }
.phz-glyph span:nth-child(3) { width: 17.3%; }
.phz-glyph span:nth-child(4) { width: 25%; }
.phz-glyph span:nth-child(5) { width: 25%; }
/* 🔴 THE YEAR SLIDE SITS ON THE PHASE SLIDES' BASELINE. A phase slide's identity block is a
 * 17px eyebrow plus a 60px numeral; the year slide has neither, so without these its name
 * started 27px high and the deck jumped on every swipe past slide 0. COMPOSED, NOT HARD-CODED
 * to the 77px they currently sum to: an invisible eyebrow reserves exactly what the real one
 * does, and the stack sits on the floor of a box the height of the numeral's own line, so
 * both track any future change to either token.
 * `visibility: hidden` not `display: none` — display:none reserves nothing. */
.phz-yspacer { visibility: hidden; }
.phz-yglyphbox { height: var(--size-60); display: flex; align-items: flex-end; justify-content: center; }
.phz-body { text-align: center; margin-top: var(--space-8); }
.phz-body .nm { font-family: var(--font-display); font-weight: var(--weight-500); font-size: var(--size-20); line-height: var(--leading-115); color: var(--nav-active); }
.phz-body .st { font-size: var(--text-caption); line-height: var(--leading-140); color: var(--text-secondary); margin: var(--space-8) 0 0; }
.phz-dot { color: var(--teal-700); }

/* THE INDICATOR — the article hero's object, brought here so the two surfaces carry one
   component: 9rem wide (≈ the phase name's measure), proportional, with two 10px carets
   either side saying there is more on both sides. ⚠️ The carets are `pointer-events: none`
   and 10px on purpose: D-458 ruled "stops at the ends, NO arrows" for this shape, so they
   may POINT but must never invite a tap. */
.phz-tickrow { display: flex; align-items: center; justify-content: center; gap: var(--space-12); margin-top: var(--space-16); pointer-events: none; }
.phz-tickrow svg { width: 10px; height: 10px; flex: none; color: var(--brand); } /* token-exception */
.phz-bars { display: flex; gap: var(--space-4); width: 9rem; flex: none; margin: 0; pointer-events: auto; } /* token-exception */
.phz-bars .phz-bar { cursor: pointer; }   /* the deck's own hook — see hero.render.js */
/* THE DOOR — a tinted band at the foot of the card, not a button (F1t, 2026-07-26).
 *
 * ⚠️ IT WAS A SOLID --green-900 BUTTON, AND THAT WAS RIGHT UNTIL THE TAB EXISTED AS ONE
 * SCREEN. In isolation this card has exactly one affordance, so a full-weight button is
 * correct. Composed with 604 below it (workbench/phase/tab.html, same day), that button
 * became the heaviest object on the Phases tab — pointing at the phase ARTICLE, while
 * marking a milestone sat under a text link. Nobody chose that hierarchy; it fell out of
 * the seam between two locally-correct card decisions.
 *
 * EQUAL WEIGHT WAS CHOSEN OVER PICKING A WINNER because the tab's own decisions conflict:
 * D-647 ranks it perspective-first and visited AT MOMENTS; D-653 says the milestones card
 * serves marking, "the frequent act". Both cannot hold. Emphasis can be added once that is
 * settled; taking it back after parents have learned it is much harder.
 *
 * 🔴 THIS REMOVED THE LAST --green-900 FROM THE PHASES TAB. D-650 sanctions it as the ONE
 * non-teal accent in the system and this door was where it lived (D-649). It now survives
 * only in the wordmark's leaf and the active nav tab. Filed as a palette question, not
 * silently absorbed — see the note on card 600.
 *
 * The band breaks out of .phz-card's side padding to reach the card edge.
 *
 * ⚠️ TWO TRAPS, BOTH HIT WHILE BUILDING THIS:
 * 1. `width: auto` does NOT fill. A <button> is shrink-to-fit even once it is a flex
 *    container, so the band stopped ~10% short of the right edge and read as a bug.
 * 2. The obvious fix — `width: calc(100% + var(--space-20) + var(--space-20))` — is
 *    REJECTED BY CHECK 23, correctly: a space token may not set a size, because that is
 *    exactly what made the space scale unsnappable (bump a gap, resize the furniture).
 *    There is no --dim-20 to reach for either. The fix is `box-sizing: content-box`:
 *    width:100% then means the card's CONTENT width, the two var(--space-20) paddings sit
 *    outside it, and the two negative margins pull the result flush to both card edges.
 *    No token sets a size, no calc, and it stays correct if .phz-card's side padding ever
 *    changes — which a hard-coded calc would not. (`-webkit-fill-available` also worked
 *    and was rejected: a non-standard property holding a locked layout together.) */
/* 🔴 THE DOORS ARE SOLID AGAIN — AND THIS REVERSES D-657 (Andrew, 2026-07-26).
 * D-657 made both doors equal-weight tinted BANDS, deliberately, because the tab's own
 * decisions conflict (D-647 perspective-first and visited at moments vs D-653 marking as
 * "the frequent act") and equal weight was the only treatment that did not bet on an
 * unreached answer. Andrew has now taken the bet: three FILLED buttons, one per card.
 * ⚠️ What that costs, recorded so it is not rediscovered: the tab no longer has a single
 * loud affordance, it has three, and three primaries is the same as none — the hierarchy
 * question D-657 deferred is now answered "all of them". D-657 needs amending or superseding.
 * ⚠️ And the colours now carry a THIRD meaning: teal, forest and slate here are per-CARD
 * identity, not kind (D-454) and not state (D-404). */
/* 🔴 THE TOP CORNERS ARE ROUNDED TO MATCH THE BOTTOM (2026-07-26, Andrew: "with rounded top
 * edges to make it symmetrical vertically"). The band STAYS full-bleed — D-659's shape is
 * unchanged — and the symmetry asked for is VERTICAL.
 * ⚠️ THE RADIUS IS NOT A FREE CHOICE AND MUST STAY A TOKEN. The bottom corners are not this
 * element's: they are the CARD's, produced by .phz-card's `overflow: hidden` clipping the
 * band to --radius-card-lg. Match that value or the band is symmetrical in intent and
 * visibly not in fact — 12px was tried and is only 6px out, and it reads as a mistake. If
 * .phz-card's radius ever changes, this moves with it, which a hard-coded 18px would not.
 * ⚠️ AN INSET ROUNDED RECTANGLE WAS BUILT TWICE AND IS NOT THIS. "Symmetrical" was misread
 * as equal air on four sides; the variants live in `_bakeoffs/door-shape.html` as the record. */
.phz-door { display: flex; align-items: center; justify-content: space-between; gap: var(--space-8); text-align: left; background: var(--teal-600); color: var(--text-on-accent); border: 0; border-radius: var(--radius-card-lg) var(--radius-card-lg) 0 0; box-sizing: content-box; width: 100%; margin: var(--space-16) calc(-1 * var(--space-20)) 0; padding: var(--space-16) var(--space-20); font-family: var(--font-body); font-size: var(--size-15); font-weight: var(--weight-600); cursor: pointer; }
.phz-door svg { width: var(--dim-16); height: var(--dim-16); }
.phz-door:active { filter: brightness(0.96); }
.phz-door:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); }
.phz-anchor { font-family: var(--font-display); font-size: var(--text-caption); color: var(--text-tertiary); text-align: center; margin-top: var(--space-12); }
