/* =====================================================================
   Product Building — styles
   Brand: one dominant accent (yellow) + neutrals. Dark bookends, light
   middle. Arial is the DEV PROXY for Söhne (see index.html header note):
     - weight 600  -> Söhne Dreiviertelfett  (headings, furniture)
     - weight 400  -> Söhne Buch             (body)
     - weight 700  -> Extrafett proxy         (hero/display; NOT 900/Arial Black)
   ===================================================================== */

:root {
  --black:    #000000;
  --charcoal: #2D2C37;
  --cream:    #FFFCF6;
  --yellow:   #E8FF5B;
  --grey-100: #EDEDEF;
  --grey-200: #C4C9DD;
  --grey-400: #928FAA;

  --pad: 6vmin;
  --hero: clamp(40px, 8.5vw, 168px);
  --h1:   clamp(28px, 4.4vw, 84px);
  --h2:   clamp(20px, 2.4vw, 44px);
  --body: clamp(15px, 1.45vw, 26px);
  --small: clamp(11px, 0.92vw, 16px);

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);

  /* motion tokens — one vocabulary for the whole deck (durations, stagger,
     easings). --ease-out aliases the legacy --ease so older refs still read. */
  --dur-fast:   180ms;   /* micro-interactions (nav press, dots) */
  --dur:        320ms;   /* standard entrance */
  --dur-slow:   520ms;   /* heroes, progress fill, count-ups, spine draw */
  --stagger:     55ms;   /* per-item delay step */
  --dot-travel: 460ms;   /* the dot puck hopping between positions */
  --bar-travel: 560ms;   /* the progress fill travelling to its new width (mirror BAR_TRAVEL_MS) */
  --ease-out:   cubic-bezier(0.22, 0.61, 0.36, 1);   /* = --ease */
  --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);       /* slide transitions */
  --ease-pop:   cubic-bezier(0.34, 1.40, 0.64, 1);    /* tiny overshoot, accents only */
  --ease-in:    cubic-bezier(0.4, 0, 1, 1);           /* accelerate — exits, things leaving */

  /* app-card standard — one radius + shadow for every card surface */
  --radius: 8px;
  --radius-sm: 6px;
  --shadow-card: 0 1px 2px rgba(45,44,55,.05), 0 6px 16px rgba(45,44,55,.05);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  width: 100%; height: 100%;
  overflow: hidden;
  background: var(--charcoal);
  font-family: Arial, sans-serif;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  color: var(--charcoal);
}

/* ---- section scaffolding ------------------------------------------- */
/* perspective gives the 3D-capable camera moves (card-swap tilt, spin) real
   depth; overflow:hidden keeps a zoom/push from spawning scrollbars. */
#stage { width: 100vw; height: 100vh; position: relative; overflow: hidden; perspective: 1600px; }

/* Prezi-style camera flight (deck-wide). During a flight both the outgoing and
   incoming slide stay mounted and transform as one continuous canvas move; the
   per-flight transform, opacity, rotation and easing are all driven by inline
   styles from app.js. This only sets the compositing hints. Reduced motion never
   enters this path — it falls back to the component-level cut. */
.section[data-cam] { will-change: transform, opacity; backface-visibility: hidden; }

.section {
  position: absolute; inset: 0;
  display: none;
  flex-direction: column;
  justify-content: center;
  padding: var(--pad);
  opacity: 0;
}
/* The slide container is a hard cut — no fade, no slide. All motion is at the
   component level (entrances on the incoming slide, exits on the outgoing one),
   so there's no whole-slide push and no charcoal flash between slides. */
.section.active { display: flex; opacity: 1; }

.section[data-tone="dark"]  { background: var(--charcoal); color: var(--cream); }
.section[data-tone="light"] { background: var(--cream);    color: var(--charcoal); }

/* headings sentence case, 600 proxy */
h1, h2, h3 { font-weight: 600; letter-spacing: 0; line-height: 1.04; }
.kicker {
  font-weight: 600;
  font-size: var(--small);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--grey-400);
  margin-bottom: 2.4vmin;
}
.section[data-tone="dark"] .kicker { color: var(--yellow); }

/* ---- hero / display moments ---------------------------------------- */
.hero {
  font-weight: 700;
  font-size: var(--hero);
  letter-spacing: 0;
  line-height: 0.98;
}
.subline {
  font-size: var(--h2);
  font-weight: 400;
  color: var(--grey-200);
  margin-top: 3vmin;
  max-width: 22ch;
}
.section[data-tone="light"] .subline { color: var(--grey-400); }

/* ===================================================================== */
/* App-chrome — deck-wide top progress bar                               */
/* ===================================================================== */
#progress {
  position: fixed; top: 0; left: 0; right: 0;
  height: 4px; z-index: 50;
  background: var(--grey-100);
  pointer-events: none;
  overflow: visible;            /* let the fill bulge below the track while travelling */
}
/* faint light track on dark slides */
body.tone-dark #progress { background: rgba(255,252,246,0.14); }
#progressFill {
  position: absolute; top: 0; left: 0;
  height: 4px; width: 0;
  background: var(--yellow);
}
/* The surge: while the fill travels (linear, so the edge is obvious) it swells
   taller and lifts on a glow, then snaps back to the thin line when the slide
   lands. Reduced motion keeps the bare width set, no surge. */
@media (prefers-reduced-motion: no-preference) {
  #progressFill {
    transition: width var(--bar-travel) linear,
                height 170ms var(--ease-pop),
                box-shadow 220ms var(--ease-out);
  }
  #progressFill.surge {
    height: 20px;
    box-shadow: 0 4px 22px rgba(225, 255, 80, 0.6);
  }
}

/* ===================================================================== */
/* Shared accent-bar header (Option B) — every working slide             */
/* ===================================================================== */
.slide-head {
  display: grid;
  grid-template-columns: 5px 1fr;
  gap: 20px;
  align-items: stretch;
  margin-bottom: 2.6vmin;
}
/* the signature device — a yellow active accent bar, full header height */
.slide-head .accent-bar {
  display: block;
  width: 5px;
  background: var(--yellow);
  border-radius: 2px;
  transform-origin: top;
}
.slide-head .head-text { min-width: 0; }
.slide-head .kicker { margin-bottom: 0.9vmin; }
/* charcoal (light) / cream (dark) title — NO yellow fill behind it */
.slide-title {
  font-weight: 600;
  font-size: var(--h1);
  letter-spacing: -0.01em;
  color: var(--charcoal);
}
.section[data-tone="dark"] .slide-title { color: var(--cream); }
.slide-desc {
  font-size: var(--body);
  font-weight: 600;
  color: var(--grey-400);
  margin-top: 1vmin;
  max-width: 70ch;
}

/* ---- lane-identity icons (custom monochrome set; mirror assets/lane-*.svg) ---
   One recurring mark per lane on the beat header, the machine row, and the grid
   cell. Single weight, recoloured by currentColor — charcoal on light, cream on
   dark — never yellow. Sized in the same clamp/vmin rhythm as the nearby text.
   Inline SVGs so they inherit currentColor; all aria-hidden in markup. */
.lane-icon {
  flex: 0 0 auto;
  display: block;
  color: var(--charcoal);
  overflow: visible;             /* round stroke caps near the 24-box edge */
}
.section[data-tone="dark"] .lane-icon { color: var(--cream); }
/* lane beat — leads the title block, charcoal, the largest of the three */
.lane-icon--head {
  width: clamp(26px, 2.7vw, 36px);
  height: clamp(26px, 2.7vw, 36px);
  margin-bottom: 1.3vmin;
}
/* machine row — small + secondary (grey), sitting just left of the yellow label */
.mlabel-row { display: flex; align-items: center; gap: 1vmin; }
.lane-icon--muted {
  width: clamp(16px, 1.5vw, 22px);
  height: clamp(16px, 1.5vw, 22px);
  color: var(--grey-400);
}
/* grid cell — in the title row, charcoal, smallest */
.ref-title-row { display: flex; align-items: center; gap: 0.9vmin; margin-bottom: 1.6vmin; }
.ref-title-row .ref-title { margin-bottom: 0; }
.lane-icon--cell {
  width: clamp(15px, 1.4vw, 20px);
  height: clamp(15px, 1.4vw, 20px);
}

/* lane stepper — 01 02 03 04 pills, top-right; current lane filled yellow.
   Persistent app-chrome: one element that stays mounted across the four lane
   beats (lives outside #stage), so only the active pill transitions between
   lanes instead of the whole stepper re-mounting. Shown via .show. */
#laneStepper {
  position: fixed;
  top: var(--pad); right: var(--pad);
  z-index: 45;
  display: flex; gap: 0.8vmin;
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur) var(--ease-out);
}
#laneStepper.show { opacity: 1; }
#laneStepper .step {
  font-size: clamp(11px, 0.92vw, 16px);
  font-weight: 600; letter-spacing: 0.04em;
  color: var(--grey-400);
  border: 1px solid var(--grey-200);
  border-radius: 999px;
  padding: 0.4vmin 1.2vmin;
  line-height: 1;
  /* the continuity beat — the active pill lights up with a hair of pop as you
     move lane→lane, the rest stay put */
  transition: background var(--dur) var(--ease-pop),
              border-color var(--dur) var(--ease-pop),
              color var(--dur) var(--ease-pop);
}
#laneStepper .step.on {
  background: var(--yellow);
  border-color: var(--yellow);
  color: var(--charcoal);
}

/* Immersive lane→grid echo — throwaway flying clones of the lane numbers,
   built in JS, that travel from the stepper pills into the grid columns. */
.flip-fly {
  position: fixed; z-index: 60;
  font-size: clamp(11px, 0.92vw, 16px);
  font-weight: 600; letter-spacing: 0.04em; line-height: 1;
  background: var(--yellow); color: var(--charcoal);
  border-radius: 999px;
  padding: 0.4vmin 1.2vmin;
  pointer-events: none;
  transition: transform var(--dur-slow) var(--ease-inout),
              opacity var(--dur-slow) var(--ease-out);
}

/* ===================================================================== */
/* Entrance-animation system — fires on the incoming [data-active] slide  */
/* Base styles are the visible end-state; the hidden-start animations are */
/* applied only under no-preference, so reduced-motion / print show all.  */
/* ===================================================================== */
@media (prefers-reduced-motion: no-preference) {
  /* generic staggered children: rise + fade, ordered by --i. The lead-in
     (--enter-delay, set per-arrival in JS) holds them until the incoming
     transition settles, so the build plays in front of the eye, not during the
     cut/flight. The both-fill keeps them hidden meanwhile (no flash). */
  .section[data-active] .anim {
    animation: riseIn var(--dur) var(--ease-out) both;
    animation-delay: calc(var(--enter-delay, 0ms) + var(--i, 0) * var(--stagger));
  }
  /* statement slides: eyebrow, then hero (slow hero rise), then subline */
  .section[data-active] .principle-kicker { animation: fadeIn var(--dur) var(--ease-out) both; animation-delay: 30ms; }
  .section[data-active] .hero        { animation: riseIn var(--dur-slow) var(--ease-out) both; animation-delay: 90ms; }
  .section[data-active] .subline     { animation: riseIn var(--dur) var(--ease-out) both; animation-delay: 160ms; }
  .t-title[data-active] .corner-note { animation: fadeIn var(--dur) var(--ease-out) both; }
  /* reframe II: the yellow headline half lands last with a hair of pop */
  #reframe-2[data-active] .hero .hl { animation: riseIn var(--dur) var(--ease-pop) both; animation-delay: 220ms; }
  /* working slides: the accent bar draws in (pop), title rises, then the desc */
  .section[data-active] .slide-head .accent-bar { animation: barIn var(--dur) var(--ease-pop) both; }
  .section[data-active] .slide-title { animation: riseIn var(--dur) var(--ease-out) both; animation-delay: 120ms; }
  .section[data-active] .slide-head .kicker { animation: fadeIn var(--dur) var(--ease-out) both; animation-delay: 60ms; }
  .section[data-active] .slide-desc  { animation: riseIn var(--dur) var(--ease-out) both; animation-delay: 200ms; }
  /* lane beat — the lane glyph leads the title block: it settles just after the
     accent bar with a hair of the same pop the bar + stepper use on these
     slides, then the title rises under it. (Machine + grid glyphs need no rule:
     they sit inside the .anim row/cell and ride its stagger.) */
  .section[data-active] .lane-icon--head {
    animation: iconIn var(--dur) var(--ease-pop) both;
    animation-delay: 90ms;
  }
  /* close: the three takeaways assemble (lock in, a hair of pop) one after
     another, THEN the yellow closing line lands with the punch — its delay sits
     past the last takeaway so "Let's build." closes the sequence, not mid-build. */
  .t-close[data-active] .takeaway {
    animation: lockIn var(--dur) var(--ease-pop) both;
    animation-delay: calc(var(--enter-delay, 0ms) + 140ms + var(--i, 0) * 150ms);
  }
  .t-close[data-active] .close-line { animation: riseIn var(--dur) var(--ease-pop) both; animation-delay: calc(var(--enter-delay, 0ms) + 760ms); }

  /* 11a — the diagnostic→trio connector draws between the cards */
  .t-list[data-active] .ex-arrow {
    animation: spineGrow var(--dur) var(--ease-out) both;
    animation-delay: calc(var(--enter-delay, 0ms) + var(--stagger) * 0.5);
  }
  /* 11b — the timeline spine draws top→bottom (per-step segments staggered so it
     reads as one line being drawn), then the node pops and the body rises. The
     per-step stagger is well above the deck default (160ms, not --stagger=55ms):
     at full speed the six steps land in ~0.8s and the draw is over before you
     register it. Slowed, each step draws → its node pops → its text rises, in
     turn, so the timeline reads as being built. The settle net (SETTLE_MS
     "s-runshow") is set past the last step so the net never cuts it short. */
  .t-timeline[data-active] .rail-step::before {
    animation: spineGrow var(--dur-slow) var(--ease-out) both;
    animation-delay: calc(var(--enter-delay, 0ms) + var(--i, 0) * 160ms);
  }
  .t-timeline[data-active] .rail-node {
    animation: popIn var(--dur) var(--ease-pop) both;
    animation-delay: calc(var(--enter-delay, 0ms) + var(--i, 0) * 160ms + 140ms);
  }
  .t-timeline[data-active] .rail-body {
    animation: riseIn var(--dur) var(--ease-out) both;
    animation-delay: calc(var(--enter-delay, 0ms) + var(--i, 0) * 160ms + 240ms);
  }

  /* 4 — the machine assembles chip by chip on one running --i (label then its
     chips, lane after lane), so the coordination scaffolding visibly piles up.
     30ms step keeps the whole fill ~1s; the settle net (SETTLE_MS "s-machine")
     sits past it. Rows carry no .anim — only labels + chips move. */
  .t-machine-grid[data-active] .mlane-label,
  .t-machine-grid[data-active] .chip,
  .t-machine-grid[data-active] .machine-more {
    animation: riseIn var(--dur) var(--ease-out) both;
    animation-delay: calc(var(--enter-delay, 0ms) + var(--i, 0) * 30ms);
  }

  /* ---- exits: the outgoing slide clears at the component level --------------
     Each content block of the leaving slide drops down and fades on an
     accelerating ease (a quick "clear"), staggered by --i where present. The
     container itself never moves. Furniture (logos, corner tag, divot) stays
     put as the constant the deck hangs off. The JS holds the slide on screen
     for its exit, then cuts to the next slide's entrance. */
  .section[data-leaving] > *:not(.logo):not(.divot):not(.corner-note) {
    animation: clearDown var(--dur) var(--ease-in) both;
    animation-delay: calc(var(--i, 0) * var(--stagger) * 0.6);
  }
  /* worries showcase — the sticky notes fall off the bottom of the screen,
     cascading, rather than the field clearing as one block. Tight per-note
     stagger so a full wall of ~30 notes pours off as one downpour (and clears
     inside the slide's data-exit hold) instead of a slow one-at-a-time drop. */
  .t-scatter-notes[data-leaving] .challenge-field { animation: none; }
  .t-scatter-notes[data-leaving] .challenge {
    animation: fallOff 0.5s var(--ease-in) both;
    animation-delay: calc(var(--i, 0) * 16ms);
  }
  /* machine — the scaffolding clears in a quick wave (same top-to-bottom --i as
     the entrance) right before the reframe, so the emptying sets it up. Suppress
     the generic block-level clear on .machine so the chips lift on their own. The
     section holds for its data-exit (760ms) while the wave plays. */
  .t-machine-grid[data-leaving] > .machine { animation: none; }
  .t-machine-grid[data-leaving] .mlane-label,
  .t-machine-grid[data-leaving] .chip,
  .t-machine-grid[data-leaving] .machine-more {
    animation: clearDown var(--dur) var(--ease-in) both;
    animation-delay: calc(var(--i, 0) * 14ms);
  }
}
@keyframes riseIn   { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes fadeIn   { from { opacity: 0; } to { opacity: 1; } }
@keyframes barIn    { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes spineGrow{ from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes popIn    { from { opacity: 0; transform: scale(0); } to { opacity: 1; transform: scale(1); } }
@keyframes iconIn   { from { opacity: 0; transform: translateY(8px) scale(0.6); } to { opacity: 1; transform: none; } }
/* close takeaways: rise + a touch of scale, locking into place with --ease-pop */
@keyframes lockIn   { from { opacity: 0; transform: translateY(18px) scale(0.94); } to { opacity: 1; transform: none; } }
@keyframes clearDown{ from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(40px); } }
/* falls off the bottom, keeping its sticky-note tilt the whole way down */
@keyframes fallOff  { from { opacity: 1; transform: rotate(var(--rot, 0deg)); }
                      to   { opacity: 0; transform: translateY(85vh) rotate(var(--rot, 0deg)); } }

/* ===================================================================== */
/* 1 — title                                                             */
/* ===================================================================== */
.t-title .hero { max-width: 16ch; }
.divot {
  position: absolute;
  left: 0; top: 50%;
  height: 16vmin; width: auto;
  transform: translateY(-50%) translateX(-46%);   /* flat side off-edge; only the bulge peeks in */
  opacity: 0.85;
  pointer-events: none;
}
/* on the title the divot is a quiet top-left accent — high up, smaller, faded,
   so it never crosses the headline (which sits vertically centred) */
.t-title .divot {
  top: 11vmin;
  height: 10vmin;
  transform: translateX(-42%);
  opacity: 0.5;
}
/* close slide: a quiet right-edge accent, mostly clipped off-edge and faded so
   it never bleeds into the "Build" takeaway (only the bulge peeks in) */
.divot-right {
  left: auto; right: 0;
  height: 12vmin;
  opacity: 0.4;
  transform: translateY(-50%) translateX(46%) rotate(180deg);
}

/* ===================================================================== */
/* 2 — the worries (scattered sticky tension)                            */
/* ===================================================================== */
.t-scatter-notes { justify-content: flex-start; }
.challenge-field {
  position: relative;
  flex: 1;
  margin-top: 2vmin;
}
/* base = the visible end-state (so print / reduced-motion show every note) */
.challenge {
  position: absolute;
  font-size: clamp(15px, 1.7vw, 30px);
  font-weight: 600;
  line-height: 1.18;
  max-width: 30ch;
  padding: 1.4vmin 2vmin;
  background: #fff;
  color: var(--charcoal);
  border-left: 4px solid var(--yellow);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  opacity: 1;
  transform: rotate(var(--rot, 0deg));
}
/* hidden-start + staggered reveal only while the slide is active under motion */
@media (prefers-reduced-motion: no-preference) {
  /* start above the resting spot and drop DOWN into place (was +18px, rising up
     from below) — reads as a sticky note landing on the wall from above */
  .t-scatter-notes[data-active] .challenge {
    opacity: 0;
    transform: translateY(-26px) rotate(var(--rot, 0deg)) scale(0.96);
    transition: opacity 0.5s var(--ease), transform 0.6s var(--ease);
  }
  .t-scatter-notes[data-active] .challenge.in {
    opacity: 1;
    transform: rotate(var(--rot, 0deg));
  }
}

/* ===================================================================== */
/* 3 — the animation                                                     */
/* ===================================================================== */
.t-data-flood { justify-content: flex-start; }
.anim-top {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 3vmin; margin-bottom: 1.6vmin;
}
.anim-head { display: flex; flex-direction: column; gap: 0.8vmin; max-width: 70ch; }
.beat-label { font-size: var(--h2); font-weight: 600; }
.counter { text-align: right; line-height: 1.1; }
.counter .n {
  font-weight: 700; font-size: clamp(28px, 4vw, 72px);
  font-variant-numeric: tabular-nums; letter-spacing: 0;
}
.counter .lbl { font-size: var(--small); color: var(--grey-400); display: block; }
.counter .ghost { font-size: var(--small); color: var(--grey-200); display: block; margin-top: 0.4vmin; }

#board {
  position: relative;
  flex: 1;
  border-top: 1px solid var(--grey-200);
  overflow: hidden;
}
.columns {
  position: absolute; inset: 0;
  display: grid; grid-template-columns: repeat(5, 1fr);
  pointer-events: none;
}
.col { position: relative; border-right: 1px solid var(--grey-100); }
.col:last-child { border-right: 0; }
.col .col-head {
  position: absolute; top: 1.4vmin; left: 0; right: 0;
  text-align: center;
  font-size: var(--small); font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--grey-400);
}
.col.learn { background: rgba(232,255,91,0.10); }

#cardLayer { position: absolute; inset: 0; pointer-events: none; }
.card {
  position: absolute; top: 0; left: 0;
  width: 17%;
  background: #fff;
  border: 1px solid var(--grey-100);
  border-left: 3px solid var(--charcoal);
  padding: 1vmin 1.2vmin;
  box-shadow: 0 4px 12px rgba(45,44,55,0.10);
  will-change: transform, opacity;
  transform: translate3d(0,0,0);
}
.card.beatA { transition: transform 1.15s var(--ease); }
.card .t {
  font-size: clamp(10px, 0.95vw, 15px);
  font-weight: 600; line-height: 1.2;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.card .meta {
  margin-top: 0.6vmin;
  font-size: clamp(8px, 0.7vw, 11px);
  font-weight: 600; color: var(--grey-400);
}
.card .meta .tier { color: var(--charcoal); }
.card.shipped { border-left-color: var(--yellow); }
/* a flood card looping back upstream through Learn — tinted to read as a learn pass */
.card.looping {
  border-color: var(--yellow);
  border-left-color: var(--yellow);
  box-shadow: 0 4px 16px rgba(232,255,91,0.5);
}

/* loop arrow Ship->Learn->Idea (beat A feedback loop) */
#loopArc {
  position: absolute; inset: 0;
  pointer-events: none; opacity: 0;
  transition: opacity 0.5s var(--ease);
}
#loopArc.on { opacity: 0.55; }
#loopArc path { fill: none; stroke: var(--yellow); stroke-width: 2.5; }

/* 3b — the problem, on its own slide (s-problem) right after the animation. */
#problem .hero    { max-width: 16ch; }
#problem .subline { max-width: 36ch; line-height: 1.4; }

.beat-hint {
  font-size: var(--body); color: var(--grey-400);
  line-height: 1.3;
  /* reserve two lines so the board below doesn't jump as the narration changes
     between beats */
  min-height: 2.6em;
}

/* reduced-motion / fallback static compare */
#staticCompare { display: none; flex: 1; gap: 3vmin; }
#staticCompare.on { display: grid; grid-template-columns: 1fr 1fr; }
.sc-col { border-top: 3px solid var(--charcoal); padding-top: 1.6vmin; overflow: hidden; }
.sc-col.flood { border-top-color: var(--yellow); }
.sc-col h3 { font-size: var(--h2); margin-bottom: 0.6vmin; }
.sc-col .big { font-weight: 700; font-size: clamp(30px,5vw,90px); }
.sc-col .lbl { font-size: var(--small); color: var(--grey-400); margin-bottom: 1.4vmin; }
.sc-list { display: flex; flex-wrap: wrap; gap: 0.8vmin; }
.sc-list span {
  font-size: clamp(9px, 0.85vw, 13px); font-weight: 600;
  background: #fff; border: 1px solid var(--grey-100); border-left: 2px solid var(--charcoal);
  padding: 0.5vmin 0.9vmin;
}
.sc-col.flood .sc-list span { border-left-color: var(--yellow); }

/* ===================================================================== */
/* 4 — how we build today (the current machine)                          */
/* ===================================================================== */
/* padding-bottom keeps the last lane clear of the corner logo band (safe-zone) */
.t-machine-grid { justify-content: flex-start; padding-bottom: 11vmin; }
/* Horizontal lanes: each row is one lane (yellow label + flowing chips); the
   rows share the height evenly so the screen fills top-to-bottom and the
   density reads as a heavy machine — no ragged columns, no dead space. */
.machine { flex: 1; display: flex; flex-direction: column; margin-top: 1.4vmin; }
.mlane {
  flex: 1;
  /* number + icon + label inline on top, then the chips underneath full width
     (not a right-hand column) */
  display: flex; flex-direction: column;
  justify-content: center;
  gap: 1.6vmin;
  border-bottom: 1px solid var(--grey-100);
}
.mlane:last-child { border-bottom: 0; }
.mlane-label { display: flex; flex-direction: row; align-items: center; gap: 1.4vmin; }
.mnum { font-size: var(--small); font-weight: 600; color: var(--grey-400); letter-spacing: 0.04em; }
/* the ONE accent on this section — yellow only on the four lane labels */
.mlabel {
  font-weight: 600; font-size: clamp(14px, 1.4vw, 24px);
  line-height: 1.5;
  background: var(--yellow); color: var(--charcoal);
  border-radius: var(--radius-sm);
  padding: 0.5vmin 1.4vmin;
  /* every label stays on one line (the column sizes to fit it) */
  white-space: nowrap;
}
.chips { display: flex; flex-wrap: wrap; gap: 1vmin; align-content: center; }
/* chips stay neutral — greys / black on white */
.chip {
  font-size: clamp(12px, 1.05vw, 18px); font-weight: 400;
  line-height: 1.25;
  background: #fff; color: var(--charcoal);
  border: 1px solid var(--grey-100);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-card);
  padding: 0.7vmin 1.2vmin;
}
/* the "and more" line, once for the whole machine — a quiet dashed-top footer
   under the four lanes. It signals the rows are a sample, not the full machine,
   without stamping the same slot into every row. */
.machine-more {
  flex: 0 0 auto;
  margin-top: 1.6vmin;
  padding-top: 1.6vmin;
  border-top: 1.5px dashed var(--grey-200);
  font-size: clamp(12px, 1.05vw, 18px);
  font-style: italic;
  color: var(--grey-400);
}

/* ===================================================================== */
/* 5c — the lanes intro (bridge). Four rows that rhyme with the machine     */
/*      lanes (number + yellow label), stripped to a one-line purpose, then */
/*      a neutral dashed Wildcard strip — the same four lanes, now as the    */
/*      agenda.                                                             */
/* ===================================================================== */
/* padding-bottom keeps the Wildcard strip clear of the corner logos (which sit
   at bottom: var(--pad)); without it the desc's second line hides behind the
   wordmark. */
.lanes-intro { flex: 1; display: flex; flex-direction: column; margin-top: 1.4vmin; padding-bottom: 5vmin; }
.li-lane {
  flex: 1;
  /* number + icon + title inline on top, then the purpose line underneath
     (not a right-hand column) */
  display: flex; flex-direction: column;
  justify-content: center;
  gap: 1vmin;
  border-bottom: 1px solid var(--grey-100);
}
.li-label { display: flex; flex-direction: row; align-items: center; gap: 1.4vmin; }
.li-num { font-size: var(--small); font-weight: 600; color: var(--grey-400); letter-spacing: 0.04em; }
/* the ONE accent — yellow only on the four lane labels (mirrors the machine) */
.li-title {
  font-weight: 600; font-size: clamp(15px, 1.6vw, 28px);
  line-height: 1.5;
  background: var(--yellow); color: var(--charcoal);
  border-radius: var(--radius-sm);
  padding: 0.5vmin 1.4vmin;
  /* every label stays on one line (the column sizes to fit it) */
  white-space: nowrap;
}
/* the lane glyph leads its label here too, so the icon follows the lane onto the
   agenda exactly as it does on the beats and grid. Charcoal (the lane is the
   subject on this slide), sitting left of the yellow label; rides the row's
   stagger like the machine + grid glyphs. */
.li-title-row { display: flex; align-items: center; gap: 1.1vmin; }
.lane-icon--intro { width: clamp(18px, 1.7vw, 26px); height: clamp(18px, 1.7vw, 26px); }
.li-desc {
  font-size: var(--body); line-height: 1.3;
  color: var(--charcoal);
  /* each lane's purpose reads as a single line, no wrap */
  white-space: nowrap;
}
/* the Wildcard strip — neutral, dashed top rule, ghosted so it reads as the
   open wall (echoes .ref-cell.wild on the grid) */
.li-wild {
  margin-top: 2.4vmin; padding-top: 2.2vmin;
  border-top: 2px dashed var(--grey-200);
  display: flex; flex-direction: column; gap: 0.8vmin;
}
.li-wild-title {
  align-self: flex-start;
  font-weight: 600; font-size: clamp(14px, 1.4vw, 24px);
  color: var(--charcoal);
  padding-bottom: 0.6vmin;
  border-bottom: 3px solid var(--grey-200);
}
.li-wild-desc { font-size: var(--body); line-height: 1.3; color: var(--grey-400); white-space: nowrap; }

/* ===================================================================== */
/* 5 — the reframe                                                       */
/* ===================================================================== */
#reframe .hero { max-width: 14ch; }
/* the one marked word. This slide is light, so yellow text would wash out —
   use a marker chip (yellow fill, charcoal text) instead. Reads as a highlight
   here, and stays distinct from 5b's yellow-on-dark headline. */
#reframe .hero .hl {
  background: var(--yellow);
  color: var(--charcoal);
  padding: 0 0.12em;
  border-radius: 3px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
#reframe .subline { max-width: 34ch; }

/* ===================================================================== */
/* 5b — the second reframe (dark; the only yellow is the headline's       */
/*      second half + the input row of the contrast)                     */
/* ===================================================================== */
#reframe-2 .hero { max-width: 21ch; }   /* each sentence stays on its own line */
#reframe-2 .hero .hl { color: var(--yellow); }   /* the single dominant yellow */
#reframe-2 .subline { max-width: 40ch; }
/* the staff/context-layer line — lower + smaller than the sub, neutral so the
   headline keeps the only yellow. Reads as a quiet supporting caption. */
.reframe2-staff {
  margin-top: 2.4vmin;
  font-size: var(--body);
  font-weight: 600;
  color: var(--grey-400);
  max-width: 46ch;
}

/* fix-output vs fix-input — two spare rows, the manual way then the leveraged.
   Reads as a quiet two-column table: the leads share a fixed-width column so the
   tails (both starting "once") begin at the same x in both rows; tails never
   wrap, so each row stays on one line. */
.io-contrast {
  margin-top: 3.4vmin;
  display: flex; flex-direction: column;
  gap: 1.4vmin;
  width: max-content;
  max-width: 100%;
}
.io-row {
  display: flex; align-items: baseline;
  gap: 1.4vmin;
  font-size: var(--body);
  padding-left: 2vmin;
  border-left: 3px solid var(--grey-400);
}
.io-row .io-lead {
  font-weight: 600; white-space: nowrap;
  flex: 0 0 auto; width: 9.5em;   /* shared column so both tails line up */
}
.io-row .io-tail { color: var(--grey-200); white-space: nowrap; }
/* the manual way: receded. the leveraged way: the accent. */
.io-out { opacity: 0.6; }
/* The fix-output row arrives at full strength, holds white for a good beat, then
   fades slowly to its muted grey resting state — a deliberate "the old way
   recedes" moment, not a quick flick right after landing. Timed LINEAR so the
   keyframe percentages map straight to wall-clock (an ease curve warps the hold
   earlier): rise in by ~0.3s, hold white to ~2.2s, then ease down to grey by 4s.
   Overrides the generic .anim entrance (id specificity wins). The reframe-2
   settle (SETTLE_MS) is set past 4s so the safety net never cuts the fade.
   Base = 0.6, so reduced motion / print show the muted state directly. */
@media (prefers-reduced-motion: no-preference) {
  #reframe-2[data-active] .io-out {
    animation: riseRecede 4000ms linear both;
  }
}
@keyframes riseRecede {
  0%   { opacity: 0;   transform: translateY(14px); }
  7%   { opacity: 1;   transform: none; }
  55%  { opacity: 1;   transform: none; }
  100% { opacity: 0.6; transform: none; }
}
.io-in  { border-left-color: var(--yellow); }
.io-in .io-lead { color: var(--yellow); }

/* ===================================================================== */
/* 6–9 — lane beats (one narration slide per lane)                       */
/* ===================================================================== */
/* keep the filled list clear of the bottom logo band (logos sit at 6vmin +
   ~4.4vmin tall), the way the machine slide does. */
.lane-detail { justify-content: flex-start; padding-bottom: 11vmin; }
/* seed questions as a two-column list, NOT boxes. Each item is the same light
   list item as the grid's reference cells — a 2px left rule in --grey-200, no
   all-round border / shadow / radius — so the beat and the grid read as one
   component. The rows sit tight under the header (no frame-fill spread), which
   keeps the points close together and the "+ add your own" line well clear of
   the bottom logo band. */
.seeds {
  list-style: none;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: min-content;
  column-gap: 6vmin;
  row-gap: 3.4vmin;
  align-content: start;
  margin-top: 7.5vmin;
}
.seeds li {
  display: flex; align-items: center;
  font-size: clamp(14px, 1.4vw, 24px);
  font-weight: 400; line-height: 1.3; color: var(--charcoal);
  padding: 0 0 0 1.8vmin;
  border-left: 2px solid var(--grey-200);
  /* every question reserves two lines, content vertically centred, so a one-line
     and a two-line question read as exactly the same height — consistent left
     rules, even rhythm. Pure em (not vmin) so it stays uniform across all four
     lanes under the deck's transform scaling. */
  min-height: 2.8em;
}
/* trailing prompt — a quiet "+ add your own" line, no rule and no box, so it
   reads as a gentle invitation rather than a finished seed or a form field.
   Spans the full width so it sits as the list's closing line. */
.seeds li.seed-other {
  grid-column: 1 / -1;
  margin-top: 1.4vmin;   /* a hair of separation from the last seed row */
  border-left: 0;
  padding-left: 0;
  color: var(--grey-400);
}

/* ===================================================================== */
/* 10 — combined reference grid (left up during the session)             */
/* ===================================================================== */
/* glanceability first: five walls visible at once, yellow titles anchor */
.ref-grid { justify-content: stretch; padding-bottom: calc(var(--pad) + 3vmin); }

/* the header band: the slide header on the left, the two principles on the
   right, sharing one row. The walls below are tuned to fill the slide exactly,
   so the principles ride the header's horizontal slack instead of stacking
   above the walls (which would clip the densest lane). */
.ref-top {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 5vmin;
  margin-bottom: 2.6vmin;   /* the gap to the walls, moved off the inner header */
}
/* the header's own bottom margin would stack on top of the row's, pushing the
   walls down and clipping the densest lane. Zero it here; the row owns the gap. */
.ref-top .slide-head { flex: 0 1 auto; margin-bottom: 0; }
/* the two named principles as the lens for the work. Quiet: no card, just the
   "Principle 0x" eyebrow + the line, with the same yellow-marked phrase the
   reframe slides use, so the wall calls back to slides 5 / 5b. A left hairline
   sets them apart from the title without a heavy box. */
.ref-principles {
  flex: 0 1 auto;
  display: flex; flex-direction: column; gap: 1.4vmin;
  padding-left: 3vmin;
  border-left: 1px solid var(--grey-100);
}
.ref-principle { display: flex; flex-direction: column; gap: 0.4vmin; }
.ref-principle-num {
  font-weight: 600; font-size: clamp(10px, 0.8vw, 14px);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--grey-400);
}
.ref-principle-text {
  font-weight: 600; line-height: 1.1;
  font-size: clamp(14px, 1.3vw, 24px);
  color: var(--charcoal);
}
/* same marker chip as slide 5's headline highlight (light slide, so a yellow
   fill with charcoal text rather than yellow text that would wash out). */
.ref-principle-text .hl {
  background: var(--yellow); color: var(--charcoal);
  padding: 0 0.12em; border-radius: 3px;
  box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
/* two rows: the five collapsed walls across the top (content-height, so the
   cells stay short), then the single Produce bar spanning the four lanes
   underneath. The whole block is centred in the leftover vertical space. */
.ref-cells {
  flex: 1; min-height: 0;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-auto-rows: auto;
  align-content: center;
  gap: 1.6vmin;
}
.ref-cell {
  background: #fff; border: 1px solid var(--grey-100);
  border-radius: var(--radius); box-shadow: var(--shadow-card);
  padding: 1.8vmin 1.6vmin;
  display: flex; flex-direction: column; overflow: hidden;
}
.ref-num {
  font-weight: 600; font-size: clamp(10px, 0.8vw, 14px);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--grey-400); margin-bottom: 1vmin;
}
/* charcoal title + a short yellow underline (no filled-yellow block) */
.ref-title {
  align-self: flex-start;
  font-weight: 600; line-height: 1.05;
  font-size: clamp(15px, 1.4vw, 26px);
  color: var(--charcoal);
  padding-bottom: 0.8vmin; margin-bottom: 1.6vmin;
  border-bottom: 3px solid var(--yellow);
}
.ref-seeds { list-style: none; display: flex; flex-direction: column; gap: 0.9vmin; }
.ref-seeds li {
  font-size: clamp(11px, 0.92vw, 17px);
  font-weight: 400; line-height: 1.2; color: var(--charcoal);
  padding-left: 1.2vmin; border-left: 2px solid var(--grey-200);
}
/* the Wildcard — neutral title, dashed border, left unseeded (ghosted: no card
   shadow, so it reads as the open wall) */
.ref-cell.wild { background: transparent; border: 2px dashed var(--grey-200); box-shadow: none; }
.ref-cell.wild .ref-title {
  background: transparent; color: var(--charcoal);
  padding: 0 0 0.8vmin 0; border-bottom: 3px solid var(--grey-200);
}
.ref-wild-desc {
  font-size: clamp(12px, 1vw, 18px); line-height: 1.3;
  color: var(--grey-400); margin-top: 0.4vmin; max-width: 26ch;
}
/* the Produce bar — what EVERY wall must produce, shown once. Spans the full
   width of the wall (all five columns), sitting under the lanes and Wildcard as
   the reference footer. Breaks (the diagnostic) on the left, a connector, then
   the Stop / Start / Build trio it drives laid out left-to-right. Yellow only
   on the labels. */
.ref-produce {
  grid-column: 1 / 6;
  display: flex; align-items: center; gap: 3vmin;
  background: #fff; border: 1px solid var(--grey-100);
  border-radius: var(--radius); box-shadow: var(--shadow-card);
  padding: 2.4vmin 2.8vmin;
}
.asks-head {
  flex: 0 0 auto;
  font-weight: 600; font-size: clamp(11px, 0.85vw, 15px);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--grey-400);
}
.ref-produce .ask { flex: 0 0 auto; }
.ask { display: flex; align-items: baseline; gap: 0.9vmin; }
.ask-label {
  flex: 0 0 auto;
  font-weight: 600; font-size: clamp(12px, 1.05vw, 19px);
  background: var(--yellow); color: var(--charcoal);
  border-radius: var(--radius-sm);
  padding: 0.3vmin 0.9vmin;
}
.ask-desc { font-size: clamp(11px, 0.92vw, 16px); color: var(--grey-400); line-height: 1.2; }
/* the diagnostic sits apart — a short connector runs across to the trio */
.produce-arrow {
  flex: 0 0 auto;
  width: 3.2vmin; height: 2px;
  background: var(--grey-200);
}
.produce-trio { display: flex; align-items: center; gap: 3vmin; flex-wrap: wrap; }

/* ===================================================================== */
/* 11a — the four questions (Breaks diagnostic + Stop / Start / Build)    */
/* ===================================================================== */
.t-list { justify-content: flex-start; }
/* the four questions are the core. Breaks (diagnostic) sits full-width on top;
   a connector drops to the Stop / Start / Build trio it drives — the same shape
   as the grid + close. The trio sizes to its content (it used to stretch to fill
   the frame, which left the bottom cards absurdly tall); the block is centred in
   the frame so the space reads as deliberate. */
.ex-questions { flex: 1; min-height: 0; display: flex; flex-direction: column; justify-content: center; padding-bottom: 4vmin; }
.ex-trio { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.8vmin; }
.ex-q {
  display: flex; gap: 1.6vmin; align-items: flex-start;
  background: #fff; border: 1px solid var(--grey-100);
  border-radius: var(--radius); box-shadow: var(--shadow-card);
  padding: 2vmin 2.2vmin;
}
.ex-trio .ex-q { flex-direction: column; }
/* the diagnostic card is set apart by a charcoal anchor + full width */
.ex-diag .ex-q { border-left: 4px solid var(--charcoal); }
/* the connector from the diagnostic down to the trio (draws from the top) */
.ex-arrow {
  width: 3px; height: 3vmin;
  background: var(--grey-200);
  margin: 1.4vmin 0 1.4vmin 2.4vmin;
  transform-origin: top;
}
/* the ONE accent on this section — yellow only on the canonical-term labels */
.ex-q .ex-qlabel {
  flex: 0 0 auto;
  font-weight: 700; line-height: 1.15;
  font-size: clamp(15px, 1.6vw, 28px);
  background: var(--yellow); color: var(--charcoal);
  border-radius: var(--radius-sm);
  padding: 0.3vmin 1.1vmin;
}
.ex-q .qt {
  font-size: clamp(14px, 1.4vw, 24px);
  font-weight: 600; line-height: 1.22;
}

/* ===================================================================== */
/* 11b — the run of show (the timed agenda as a vertical timeline rail)   */
/* ===================================================================== */
/* padding-bottom reserves the corner logo band so the foot never overlaps it */
.t-timeline { justify-content: flex-start; padding-bottom: 9vmin; }
.rail {
  list-style: none;
  flex: 1; min-height: 0;
  display: flex; flex-direction: column;
  gap: 0;
  padding-bottom: 1.6vmin;
}
.rail-step {
  position: relative;
  flex: 1;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2vmin;
  padding: 0 0 0 0.4vmin;
}
/* the vertical line + node down the left of each step */
.rail-step .rail-node {
  position: relative;
  width: 14px; height: 14px; margin-top: 0.4vmin;
  border-radius: 50%;
  background: var(--yellow);
  box-shadow: 0 0 0 4px var(--cream);
  z-index: 1;
}
.rail-step::before {
  content: ""; position: absolute;
  left: 6px; top: 0; bottom: 0;
  width: 2px; background: var(--grey-200);
  transform-origin: top;
}
.rail-step:first-child::before { top: 0.8vmin; }
.rail-step:last-child::before  { bottom: calc(100% - 1.4vmin); }
.rail-body {
  padding-bottom: 1.6vmin;
  border-bottom: 1px solid var(--grey-100);
}
.rail-step:last-child .rail-body { border-bottom: 0; }
.rail-head {
  display: flex; align-items: baseline; gap: 1.4vmin;
  margin-bottom: 0.5vmin; flex-wrap: wrap;
}
.rail-name { font-weight: 600; font-size: clamp(16px, 1.7vw, 30px); }
/* small dark duration chip */
.rail-min {
  flex: 0 0 auto;
  font-weight: 700; font-size: var(--small);
  background: var(--charcoal); color: var(--cream);
  border-radius: var(--radius-sm);
  padding: 0.3vmin 1vmin; white-space: nowrap;
}
.rail-what { font-size: clamp(13px, 1.2vw, 21px); color: var(--grey-400); line-height: 1.25; max-width: 88ch; }

/* ===================================================================== */
/* 12 — close (the three takeaways; Breaks is the diagnostic in the frame) */
/* ===================================================================== */
.t-close { justify-content: center; }
.close-h { font-size: var(--h1); margin-bottom: 1.2vmin; max-width: 18ch; }
/* the framing line keeps Breaks visible as the diagnostic above the trio */
.close-frame { font-size: var(--body); font-weight: 600; color: var(--grey-200); margin-bottom: 1.8vmin; max-width: 56ch; }
/* constrained width keeps the takeaways clear of the right-edge divot */
.takeaways { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2.4vmin; max-width: 92ch; }
/* the close line is the single yellow emphasis — so the takeaway top-rule is
   neutral here, not yellow */
.takeaway {
  border-top: 3px solid var(--grey-200);
  background: rgba(255,255,255,0.04);
  border-radius: var(--radius);
  padding: 2vmin 2.4vmin;
}
.takeaway h3 { font-size: clamp(18px, 2vw, 36px); margin-bottom: 1vmin; }
.takeaway p { font-size: var(--body); color: var(--grey-200); }
.close-align { margin-top: 1.8vmin; font-size: var(--body); color: var(--grey-200); max-width: 72ch; }
/* the final word — the one strong, bold line that closes the room */
.close-line { margin-top: 1.8vmin; font-weight: 700; font-size: var(--h1); line-height: 0.98; color: var(--yellow); }

/* ===================================================================== */
/* Furniture — logos, dots, hints                                        */
/* ===================================================================== */
.logo {
  position: absolute; bottom: var(--pad);
  height: 2.6vmin; width: auto; opacity: 0.92; pointer-events: none;
}
.logo.wordmark { left: var(--pad); }
.logo.icon     { right: var(--pad); height: 4.4vmin; }

.corner-note {
  position: absolute; top: var(--pad); right: var(--pad);
  font-size: var(--small); font-weight: 600;
  color: var(--grey-400); letter-spacing: 0.08em;
}
.section[data-tone="dark"] .corner-note { color: var(--grey-200); }

#dots {
  /* vertically centred on the logo band, not sitting lower. The logos sit at
     bottom:var(--pad); the wordmark (2.6vmin) and icon (4.4vmin) are bottom-
     aligned, so their centres are var(--pad)+1.3vmin and +2.2vmin — the band
     midpoint is var(--pad)+1.75vmin. Drop half a dot (9px/2) to align the row's
     own centre to that line. */
  position: fixed; bottom: calc(var(--pad) + 1.75vmin - 4.5px); left: 50%;
  transform: translateX(-50%);
  display: flex; gap: 1.2vmin; z-index: 40;
}
/* the dots are now a static track; the puck below is the active marker */
#dots button {
  width: 9px; height: 9px; border-radius: 50%;
  border: 0; padding: 0; cursor: pointer;
  background: var(--grey-200); opacity: 0.55;
  transition: opacity var(--dur-fast) var(--ease-out);
}
/* The travelling puck — the active marker, on its own plane above the dot track
   and the content. It slides its `left` between dots; the one-shot hop animation
   pops it up and out mid-travel, then snaps it back down on arrival. Reduced
   motion drops both (it just jumps to the dot, via JS skipping .hop). */
#dotPuck {
  position: absolute; top: 50%; left: 0;
  width: 13px; height: 13px; margin: -6.5px 0 0 -6.5px;   /* centre on `left`, leaving transform free */
  border-radius: 50%;
  background: var(--yellow);
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  #dotPuck { transition: left var(--dot-travel) var(--ease-inout); }
  #dotPuck.hop { animation: dotHop var(--dot-travel) var(--ease-inout); }
}
/* The hop is a jump toward the viewer, not up the screen: perspective + a big
   translateZ brings the puck off the surface and back, so it reads as 3D depth
   over the camera moves behind it. No translateY — nothing lifts vertically. */
@keyframes dotHop {
  0%   { transform: perspective(460px) translateZ(0)     scale(1);    box-shadow: 0 0 0 rgba(225, 255, 80, 0); }
  50%  { transform: perspective(460px) translateZ(240px) scale(1.3);  box-shadow: 0 0 38px rgba(225, 255, 80, 0.7); }
  82%  { transform: perspective(460px) translateZ(-18px) scale(0.9);  box-shadow: 0 0 7px rgba(225, 255, 80, 0.2); }
  100% { transform: perspective(460px) translateZ(0)     scale(1);    box-shadow: 0 0 0 rgba(225, 255, 80, 0); }
}

/* minimal controls — unobtrusive in the top-left corner, low-contrast until hover */
#nav {
  position: fixed; top: 2vmin; left: 2.4vmin; z-index: 40;
  display: flex; gap: 0.6vmin;
  opacity: 0.22; transition: opacity 0.25s var(--ease);
}
#nav:hover, #nav:focus-within { opacity: 0.95; }
#nav button {
  width: 3.4vmin; height: 3.4vmin; min-width: 28px; min-height: 28px;
  display: grid; place-items: center;
  background: transparent; border: 0; padding: 0; cursor: pointer;
  color: var(--grey-400);
  transition: color 0.2s, transform var(--dur-fast) var(--ease-out);
}
#nav.dark button { color: var(--grey-200); }
#nav button:hover { color: var(--charcoal); }
#nav.dark button:hover { color: var(--cream); }
/* press feedback — a small dip on tap */
#nav button:active { transform: scale(0.92); }
#nav button svg { width: 64%; height: 64%; display: block; }

/* reduced motion AND print: everything sits at its visible end-state, no
   transitions or animations anywhere. The entrance keyframes live under
   (no-preference) so they never apply under reduce; killing animations here
   also drops every element back to its base (visible) styles — so a print/PDF
   pass can never catch content stuck mid-entrance at opacity:0. Intentionally
   hidden elements (inactive sections, the loop arc, the static fallback) stay
   hidden because that comes from their base rules, not an animation. Chrome
   transitions (progress fill, stepper, nav, dots) stop too; count-ups snap in JS. */
@media (prefers-reduced-motion: reduce), print {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ── audience poll (M7) ─────────────────────────────────────────────── */
.t-audience-poll { justify-content: flex-start; }
.poll { width: min(1100px, 86vw); margin: 4vmin auto 0; display: flex; flex-direction: column; gap: 2.4vmin; }
.poll-opt { position: relative; }
.poll-bar { height: 8.4vmin; border-radius: 12px; background: var(--grey-100); overflow: hidden; }
.poll-bar span { display: block; height: 100%; width: 0; background: var(--yellow);
  transition: width 420ms cubic-bezier(0.22,0.61,0.36,1); }
.poll-row { position: absolute; inset: 0; display: flex; align-items: center; justify-content: space-between;
  padding: 0 3vmin; }
.poll-label { font-size: var(--body); font-weight: 600; color: var(--charcoal); }
.poll-count { font-size: var(--body); font-weight: 600; color: var(--charcoal); font-variant-numeric: tabular-nums; }
.poll-hint { margin-top: 2vmin; color: var(--grey-400); font-size: calc(var(--body) * 0.8); }
