/* ============================================================
   SB-HOME — FMC homepage · "ALWAYS ON AT FMC"
   Page layer over sb-core.css. Consumes the engine's raw --p
   (0–1 pin progress); authors --pin runways + per-prop progress
   WINDOWS so each act stages one thing at a time:
     --seg: clamp(0, calc((var(--p) - START) / (END - START)), 1)
   Every act's windows finish by ~0.72–0.80 of the pin, so the
   fully-developed frame HOLDS to 1 (the appreciation buffer).
   Animates transform + opacity ONLY. No images, no JS of its own.
   ============================================================ */

/* ---- no-JS / pre-engine default: every scene rests at its END
   state, so nothing is ever invisible without the engine.
   (Overrides sb-core's --p:0; the engine's inline props win.) ---- */
[data-scene] { --p: 1; --pc: 1; }

/* ============================================================
   ACT 0 · HERO — marquee cover
   ============================================================ */
.hero {
  position: relative;
  overflow: hidden;
  min-height: calc(100svh - 68px);
  display: grid;
  align-items: center;
  padding-block: var(--sp-6);
  background: linear-gradient(180deg, var(--paper) 40%, var(--c-peach) 145%);
}
.hero__ring {
  position: absolute;
  top: -24%;
  right: -14%;
  width: clamp(360px, 52vw, 760px);
  z-index: 0;
}
.hero__inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
}
.hero__art { position: relative; }
.hero__logo { --logo-size: clamp(210px, 26vw, 320px); margin-inline: auto; }
.hero__title { margin-block: 0.15em 0.3em; }
.hero__title span { display: inline-block; }
.hero__f { color: var(--film-ink); }
/* warm harvest-gold mustard — friendlier/more vintage than the old olive
   ochre (#855708), still 4.55:1 on cream so it stays AA-legible */
.hero__m { color: #96420C; }
.hero__c { color: var(--comedy-ink); }
.hero__cta {
  margin-top: var(--sp-4);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
}
.hero__hint {
  margin-top: var(--sp-5);
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-size: var(--fs-cap);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--c-avocado-ink);
}
@media (max-width: 760px) {
  .hero__inner { grid-template-columns: 1fr; }
  .hero__art { order: -1; }
  .hero__logo { --logo-size: clamp(170px, 44vw, 240px); }
}

/* hero assembly — load-time entrances, motion-gated so reduced-
   motion (and no-animation) users simply see the finished cover */
@media (prefers-reduced-motion: no-preference) {
  .hero__logo .rt-logo__disc {
    animation:
      home-disc-in 0.9s var(--ease-out) both,
      sb-spin 24s linear 0.9s infinite;
  }
  .hero__logo .rt-logo__ticket { animation: home-ticket-in 0.8s var(--ease-out) 0.5s both; }
  .hero__ring { animation: home-breathe 12s var(--ease-inout) infinite alternate; }
  .hi { animation: home-rise 0.7s var(--ease-out) both; }
  .hi-1 { animation-delay: 0.50s; }
  .hi-2 { animation-delay: 0.66s; }
  .hi-3 { animation-delay: 0.78s; }
  .hi-4 { animation-delay: 0.90s; }
  .hi-5 { animation-delay: 1.05s; }
  .hi-6 { animation-delay: 1.20s; }
  .hi-7 { animation-delay: 1.35s; }
  .hi-8 { animation-delay: 1.55s; }
}

/* ============================================================
   ACT 1 · THE THREE CRAFTS — Film, Music, Comedy introduced
   together before the journey diverges. Staggered reveals on
   the shared [data-reveal] system — no new machinery.
   ============================================================ */
.craft-trio {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-3);
  margin-top: var(--sp-5);
}
.craft {
  position: relative;
  padding: var(--sp-4);
  background: var(--paper);
  border: 2px solid var(--hair);
  border-top-width: 8px;
  border-radius: var(--radius-plate);
  box-shadow: var(--shadow-lift);
}
.craft--film   { border-top-color: var(--film); }
.craft--music  { border-top-color: var(--music); }
.craft--comedy { border-top-color: var(--comedy); }
.craft__glyph {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(3.2rem, 7vw, 4.6rem);
  line-height: 0.9;
}
.craft--film   .craft__glyph { color: var(--film); }
.craft--music  .craft__glyph { color: var(--music); }
.craft--comedy .craft__glyph { color: var(--comedy); }
.craft__name {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.35rem;
  line-height: 1.1;
  margin-top: 0.55rem;
}
.craft__line {
  margin-top: 0.5rem;
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--ink-panel);
}
@media (max-width: 760px) {
  .craft-trio { grid-template-columns: 1fr; }
}

/* ============================================================
   SHARED SCENE FURNITURE — stages, washes, beats
   The wash layer crossfades the backdrop as --pc advances:
   cream → amber → cocoa. Sunset-brand warmth, not a clock —
   each act's base gradient equals the previous act's wash end,
   so the palette deepens seamlessly across scene boundaries.
   ============================================================ */
/* ── SCENE FIT SCALE · RESPONSIVE-GUIDELINES.md G1–G3 ────────
   Every scene dimension is min(what the WIDTH allows, what the
   HEIGHT allows). The width term is the site's own type scale;
   the height term is an svh clamp with a rem floor. Whichever
   axis is tighter wins, so a phone is width-bound, a short
   laptop height-bound, and a desktop bound by neither and gets
   the full drawn size — one expression, no branches.

   This replaces the hand-tuned @media height tiers that used to
   live at the bottom of this sheet (≤899px and ≤700px). A tier
   is a cliff: at 899px tall the title was 38px, at 900px 51px,
   for the same content, and every viewport between two tiers was
   unverified. These ramp instead, and both ends stay pinned so
   nothing grows past what it was drawn for or shrinks past the
   a11y floors (body copy never computes under 1rem).           */
.act {
  /* the title ramp is offset, not proportional: it holds the full
     3.2rem down to 900px of viewport and then falls away quickly, so
     a short laptop spends its height on WHITESPACE rather than on
     display type. Passes through 38.4px at 720 — the value the old
     short tier used — while 900 and up keep the full 51.2px. */
  --fit-title:   min(var(--fs-d1),   clamp(2.2rem, calc(9.5svh - 30px), 3.2rem));
  --fit-lede:    min(var(--fs-lede), clamp(1.0625rem, 2.35svh, 1.3rem));
  --fit-beat:    clamp(1rem, 1.95svh, 1.0625rem);
  /* The SPACING ramps are steeper than the type ramps, and pass
     through the values the deleted tiers were measured at (24/16/14
     padding, 40/16/8 stage, 9/5.6/4.8 gap). Spacing carries the
     shortfall on short viewports because losing 8px of padding costs
     a reader nothing and losing 8px of type costs them plenty — and
     it is what the old tiers were really doing anyway. Still
     continuous, so there is no pixel where a step lands. */
  --fit-pad:     min(clamp(14px, calc(6.06svh - 30.5px), 24px), clamp(14px, 5vw, 24px));
  --fit-gap:     clamp(4px, calc(2.4svh - 12px), 9px);
  --fit-stage:   clamp(8px, calc(16.7svh - 98px), 38px);
  --fit-platter: min(clamp(150px, 40svh, 360px), clamp(150px, 28vw, 360px));
  --fit-frame:   min(clamp(112px, 20svh, 220px), clamp(112px, 20vw, 220px));
  --fit-ticket:  min(clamp(1rem, 2.05svh, 1.5rem), clamp(1rem, 2.4vw, 1.5rem));
}
.act .section-title { font-size: var(--fit-title); }
.stage { padding-block: var(--fit-stage); }
/* wash — each act names its own darkening window on raw --p */
.stage__wash {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: var(--wash, 1);
}
.act--film   .stage__wash { --wash: clamp(0, calc((var(--p, 1) - 0.02) / 0.28), 1); }
.act--studio .stage__wash { --wash: clamp(0, calc(var(--p, 1) / 0.45), 1); }
.act--stage  .stage__wash { --wash: clamp(0, calc(var(--p, 1) / 0.25), 1); }
.stage__grid, .stage__col, .stage__center { position: relative; z-index: 1; }

.stage--cream { background: linear-gradient(180deg, var(--c-cream) 0%, var(--c-peach) 100%); color: var(--ink); }
.stage__wash--gold { background: linear-gradient(180deg, var(--c-peach) 0%, var(--c-peach-2) 100%); }

.stage--amber { background: linear-gradient(180deg, var(--c-peach) 0%, var(--c-peach-2) 100%); color: var(--ink); }
.stage__wash--dusk { background: linear-gradient(180deg, var(--c-orange-ink) 0%, var(--c-cocoa-2) 100%); }

.stage--cocoa { background: linear-gradient(180deg, var(--c-orange-ink) -12%, var(--c-cocoa-2) 42%, var(--c-cocoa) 100%); color: var(--paper); }
.stage__wash--deep { background: linear-gradient(180deg, #0D1B3D 0%, #091430 100%); }

.stage__grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
}
.stage__col { display: grid; gap: var(--sp-4); justify-items: start; }
.stage__center { max-width: 660px; }
.stage__lede { margin-bottom: 0; font-size: var(--fit-lede); }

/* kicker recolours per surface. The studio kicker sits on a cream
   plate — deep mustard #8F3E0D reads 7.3:1 there. The decorative
   bar stays brand mustard. */
.kicker--music { color: #8F3E0D; }
.kicker--music::before { background: var(--music); }
.kicker--dark { color: var(--music); }
.kicker--dark::before { background: var(--music); }

/* copy beats — each consumes its OWN window of the scene's raw
   --p (staged choreography: one thing at a time). --b0 = window
   start; fully in ~0.083 later (×12). Rests visible (--p
   defaults to 1) for no-JS and reduced-motion readers.        */
.beats {
  display: grid;
  gap: var(--fit-gap);
  margin-top: clamp(0.5rem, calc(3svh - 12px), 1.5rem);
  max-width: 46ch;
}
.beat {
  --b0: 0;
  --bv: clamp(0, calc((var(--p, 1) - var(--b0)) * 12), 1);
  opacity: var(--bv);
  /* 12px, not 22: a hidden beat sits this far BELOW its resting place,
     so the travel has to stay under the clearance of whatever follows
     the list (the exec-room line at var(--sp-3)) or the last beat
     prints straight through it while it reveals. */
  transform: translate3d(0, calc((1 - var(--bv)) * 12px), 0);
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  font-size: var(--fit-beat);
  line-height: 1.45;
}
.beat::before {
  content: "";
  flex: none;
  width: 0.55em;
  height: 0.55em;
  border-radius: 2px;
  background: var(--beat-dot, var(--music));
  transform: translateY(-0.05em);
}

/* ── act deep-links · torn ticket stubs ─────────────────────
   One quiet booking path per act. Built on the .btn base;
   the dashed left edge is the tear. Plates use text-safe
   pairings only: cocoa-on-mustard 6.1:1, cream-on-film-ink
   5.5:1, cream-on-comedy-deep 6.1:1. Enters like a beat on
   the scene's --pc; rests visible for no-JS / reduced motion. */
.stage__cta {
  --b0: 0.72;
  --bv: clamp(0, calc((var(--p, 1) - var(--b0)) * 12), 1);
  margin-top: clamp(0.6rem, 2svh, 1.5rem);
  opacity: var(--bv);
  transform: translate3d(0, calc((1 - var(--bv)) * 22px), 0);
}
.act--film   .stage__cta { --b0: 0.64; margin-top: clamp(0.5rem, 1.5svh, 1rem); }
.act--studio .stage__cta { --b0: 0.62; }
.act--stage  .stage__cta { --b0: 0.72; }
.act-stub {
  border-radius: 4px 12px 12px 4px;
  border: 2px solid var(--stub-edge, var(--ink));
  border-left-style: dashed;
  background: var(--stub-plate, var(--music));
  color: var(--stub-ink, var(--ink));
  box-shadow: var(--shadow-lift);
}
.act-stub:hover { transform: translateY(-2px); }
.act-stub--music  { --stub-plate: var(--music);       --stub-ink: var(--ink);   --stub-edge: var(--ink); }
.act-stub--film   { --stub-plate: var(--film-ink);    --stub-ink: var(--paper); --stub-edge: var(--paper); }
.act-stub--comedy { --stub-plate: var(--comedy-deep); --stub-ink: var(--paper); --stub-edge: var(--paper); }
.stage--amber .act-stub:focus-visible,
.stage--cocoa .act-stub:focus-visible { outline-color: var(--music); }

/* ============================================================
   ACT 2 · FILM — the Main Room, staged one thing at a time on
   raw --p:  copy plate 0.02–0.18 → reel scrub 0.15–0.52 →
   ticket tear 0.55–0.72 → CTA 0.64–0.72 → HOLD 0.72–1.
   Window pattern: --seg: clamp(0, calc((--p - START) / (END - START)), 1)
   ============================================================ */
.act--film { --pin: 2.4; --beat-dot: var(--film); }
.act--film .beat:nth-child(1) { --b0: 0.04; }
.act--film .beat:nth-child(2) { --b0: 0.06; }
.act--film .beat:nth-child(3) { --b0: 0.08; }
.act--film .beat:nth-child(4) { --b0: 0.10; }
.act--film .beat:nth-child(5) { --b0: 0.12; }

/* the copy plate arrives first — fade + rise over 0.02–0.18 */
.act--film .plate-dark {
  --seg: clamp(0, calc((var(--p, 1) - 0.02) / 0.16), 1);
  opacity: var(--seg);
  transform: translate3d(0, calc((1 - var(--seg)) * 34px), 0);
}

.plate-dark {
  background: var(--ink-panel);
  color: var(--paper);
  padding: var(--fit-pad);
  border-radius: var(--radius-plate);
  box-shadow: var(--shadow-plate);
  max-width: 640px;
}
.plate-dark .section-title { margin-bottom: clamp(0.3rem, 1.1svh, 0.7rem); }
.plate-dark .beats { margin-top: clamp(0.5rem, 1.6svh, 1rem); max-width: none; }

/* the quiet exec line — cream on cocoa-2 panel 11:1; mustard
   hover on cocoa-2 ≈ 7:1 */
/* clearance here must exceed the .beat entrance travel (12px, above)
   — the last beat is transformed down over this line as it reveals. */
.plate-dark__quiet {
  margin-top: var(--sp-3);
  /* body copy, so it takes the 1rem floor like the beats do (G3) —
     it used to sit at --fs-sm / 15.2px, under the project minimum */
  font-size: var(--fit-beat);
  line-height: 1.55;
  max-width: 52ch;
}
.plate-dark__quiet a {
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--hair-cream);
  transition: color 0.2s var(--ease);
}
.plate-dark__quiet a:hover { color: var(--music); }
.plate-dark__quiet a:focus-visible { outline-color: var(--music); }

/* cream plate — the studio's copy sleeve; keeps ink text ≥ AA
   while the dusk wash deepens the backdrop behind it */
.plate-light {
  background: var(--paper);
  color: var(--ink);
  padding: var(--fit-pad);
  border-radius: var(--radius-plate);
  box-shadow: var(--shadow-plate);
}

.reel {
  position: relative;
  width: 100%;
  overflow: hidden;
  background: var(--ink);
  color: var(--paper);
  border-radius: var(--radius-plate);
  padding-block: clamp(8px, 1.5svh, 16px);
  box-shadow: var(--shadow-lift);
}
.reel__perfs {
  position: absolute;
  left: 0;
  width: 100%;
  height: 10px;
  color: rgba(243, 231, 206, 0.75);
}
.reel__perfs--top { top: 3px; }
.reel__perfs--btm { bottom: 3px; }
.reel__strip {
  /* the star: scrubs across in its own 0.15–0.52 window */
  --reel: clamp(0, calc((var(--p, 1) - 0.15) / 0.37), 1);
  display: flex;
  gap: 10px;
  width: max-content;
  padding-inline: 10px;
  transform: translate3d(calc(var(--reel) * -45%), 0, 0);
}
.frame {
  flex: none;
  width: var(--fit-frame);
  aspect-ratio: 3 / 2;
  display: grid;
  place-items: center;
  padding: 0.6rem;
  background: var(--ink-panel);
  border: 2px solid rgba(243, 231, 206, 0.28);
  border-radius: 6px;
  font-family: var(--font-display);
  /* the reel is aria-hidden decoration, so this one may floor
     below 1rem — it rides the frame it sits in (G3) */
  font-size: min(clamp(0.85rem, 1.55svh, 1.25rem), clamp(0.85rem, 1.8vw, 1.25rem));
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: center;
  line-height: 1.15;
}
.frame--accent { color: var(--music); }

.ticket-tear {
  display: flex;
  justify-content: center;
  justify-self: center;
}
.ticket-half {
  /* the tear: rips apart over 0.55–0.72, then holds torn */
  --t: clamp(0, calc((var(--p, 1) - 0.55) / 0.17), 1);
  display: inline-flex;
  align-items: center;
  padding: 0.55em 1.05em;
  background: var(--music);
  color: var(--ink);
  border: 2px solid var(--ink);
  font-family: var(--font-display);
  font-size: var(--fit-ticket);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  box-shadow: var(--shadow-lift);
}
.ticket-half--l {
  border-radius: 12px 0 0 12px;
  border-right: 2px dashed var(--ink);
  transform: translate3d(calc(var(--t) * -30px), calc(var(--t) * 8px), 0) rotate(calc(var(--t) * -6deg));
}
.ticket-half--r {
  border-radius: 0 12px 12px 0;
  border-left: 0;
  transform: translate3d(calc(var(--t) * 30px), calc(var(--t) * -6px), 0) rotate(calc(var(--t) * 5deg));
}

/* ============================================================
   ACT 3 · MUSIC — the Studio, staged on raw --p:
   copy plate 0.02–0.18 → tonearm drops 0.12–0.30 → seven beats
   0.22–0.66 → CTA 0.62–0.70 → HOLD ~0.70–1. The platter spin
   alone rides full --p — a continuous spin feels right.
   ============================================================ */
.act--studio { --pin: 2.1; --beat-dot: var(--music); }
.act--studio .beat:nth-child(1) { --b0: 0.22; }
.act--studio .beat:nth-child(2) { --b0: 0.28; }
.act--studio .beat:nth-child(3) { --b0: 0.34; }
.act--studio .beat:nth-child(4) { --b0: 0.40; }
.act--studio .beat:nth-child(5) { --b0: 0.46; }
.act--studio .beat:nth-child(6) { --b0: 0.52; }
.act--studio .beat:nth-child(7) { --b0: 0.58; }

/* the copy sleeve arrives first — 0.02–0.18 */
.act--studio .plate-light {
  --seg: clamp(0, calc((var(--p, 1) - 0.02) / 0.16), 1);
  opacity: var(--seg);
  transform: translate3d(0, calc((1 - var(--seg)) * 34px), 0);
}

.platter {
  position: relative;
  width: var(--fit-platter);
  margin-inline: auto;
}
.platter__disc {
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  background:
    radial-gradient(circle closest-side,
      var(--c-cocoa) 0 4%,
      var(--music) 4% 27%,
      var(--c-mustard-ink) 27% calc(27% + 2px),
      transparent calc(27% + 2px)),
    repeating-radial-gradient(circle closest-side,
      transparent 0 5px,
      rgba(243, 231, 206, 0.13) 5px 7px),
    var(--ink);
  box-shadow: var(--shadow-plate);
  transform: rotate(calc(var(--p, 1) * 480deg));
}
.tonearm {
  /* drops onto the record in its own 0.12–0.30 window */
  --ta: clamp(0, calc((var(--p, 1) - 0.12) / 0.18), 1);
  position: absolute;
  top: 4%;
  right: -8%;
  width: 52%;
  height: 0.5rem;
  border-radius: 0.3rem;
  background: var(--ink);
  transform-origin: 100% 50%;
  transform: rotate(calc(-16deg + var(--ta) * 28deg));
}
.tonearm::before { /* pivot base */
  content: "";
  position: absolute;
  right: -0.55rem;
  top: 50%;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  background: var(--ink);
  border: 3px solid var(--c-cocoa-2);
  transform: translateY(-50%);
}
.tonearm::after { /* cartridge head */
  content: "";
  position: absolute;
  left: -0.2rem;
  top: 50%;
  width: 0.9rem;
  height: 1.35rem;
  border-radius: 3px;
  background: var(--film);
  transform: translateY(-50%);
}

/* ============================================================
   ACT 4 · COMEDY — the Stage, staged on raw --p:
   darkening wash 0–0.25 → marquee bulbs pop one-by-one
   0.18–0.54 (staggered nth-child windows) → copy 0.48–0.65 →
   beats 0.56–0.77 → CTA 0.72–0.80 → HOLD ~0.80–1.
   The bulb strips live in RESERVED bands (stage padding-block)
   so they can never overlap copy at any --p or viewport.
   ============================================================ */
.act--stage { --pin: 2; --beat-dot: var(--comedy); }
.act--stage .beat:nth-child(1) { --b0: 0.56; }
.act--stage .beat:nth-child(2) { --b0: 0.60; }
.act--stage .beat:nth-child(3) { --b0: 0.64; }
.act--stage .beat:nth-child(4) { --b0: 0.68; }
.act--stage .beat:nth-child(5) { --b0: 0.71; }
.act--stage .beat:nth-child(6) { --b0: 0.74; }

/* copy waits for the bulbs to settle, then lands in sequence */
.act--stage .stage__center .kicker {
  --seg: clamp(0, calc((var(--p, 1) - 0.48) / 0.10), 1);
  opacity: var(--seg);
  transform: translate3d(0, calc((1 - var(--seg)) * 22px), 0);
}
.act--stage .stage__center .section-title {
  /* the comedy title is centred inside the bulb band with no plate
     to sit on, so it takes a tighter WIDTH term than the plated
     titles — the height term is shared (G2) */
  font-size: min(var(--fit-title), clamp(1.7rem, 6.6vw, 3.2rem));
  --seg: clamp(0, calc((var(--p, 1) - 0.51) / 0.11), 1);
  opacity: var(--seg);
  transform: translate3d(0, calc((1 - var(--seg)) * 26px), 0);
}
.act--stage .stage__center .stage__lede {
  --seg: clamp(0, calc((var(--p, 1) - 0.54) / 0.11), 1);
  opacity: var(--seg);
  transform: translate3d(0, calc((1 - var(--seg)) * 22px), 0);
}

/* reserved marquee bands: the stage's padding-block IS the bulb
   zone. Content centres inside the content box, so copy can
   never reach the bulbs — at any width or height. */
.act--stage .scene-stage {
  /* same ramp shape as --fit-stage: 44px of clearance either side of
     the bulbs where there is room, 22 on a 720px laptop, 18 floor */
  --bulb-band: calc(28px + clamp(18px, calc(12.2svh - 70px), 44px));
  padding-block: var(--bulb-band);
}
.bulbs {
  position: absolute;
  left: 0;
  right: 0;
  height: 28px;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  padding-inline: 2vw;
}
.bulbs--top { top: calc((var(--bulb-band, 56px) - 28px) / 2); }
.bulbs--btm { bottom: calc((var(--bulb-band, 56px) - 28px) / 2); }

/* each bulb pops in its own --p window (start = 0.18 + i·0.022,
   bottom row offset +0.01, each ~0.06 wide). The glow is a
   pre-rendered box-shadow baked into the static class — only
   opacity + scale ever animate. */
.bulb {
  --bl0: calc(0.18 + var(--bi, 0) * 0.022 + var(--boff, 0));
  --bl: clamp(0, calc((var(--p, 1) - var(--bl0)) / 0.06), 1);
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--music);
  box-shadow: 0 0 9px 3px rgba(252, 164, 52, 0.42);
  opacity: var(--bl);
  transform: scale(calc(0.35 + var(--bl) * 0.65));
}
.bulbs--btm { --boff: 0.01; }
.bulb:nth-child(1)  { --bi: 0; }
.bulb:nth-child(2)  { --bi: 1; }
.bulb:nth-child(3)  { --bi: 2; }
.bulb:nth-child(4)  { --bi: 3; }
.bulb:nth-child(5)  { --bi: 4; }
.bulb:nth-child(6)  { --bi: 5; }
.bulb:nth-child(7)  { --bi: 6; }
.bulb:nth-child(8)  { --bi: 7; }
.bulb:nth-child(9)  { --bi: 8; }
.bulb:nth-child(10) { --bi: 9; }
.bulb:nth-child(11) { --bi: 10; }
.bulb:nth-child(12) { --bi: 11; }
.bulb:nth-child(13) { --bi: 12; }
.bulb:nth-child(14) { --bi: 13; }

/* ============================================================
   ACT 5 · THE PROMISE — cards + counters on cocoa
   ============================================================ */
.promise-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--sp-3);
}
/* keep craft accents on cocoa (core flattens card borders there) */
.band--cocoa .promise-grid .card--film   { border-top-color: var(--film); }
.band--cocoa .promise-grid .card--music  { border-top-color: var(--music); }
.band--cocoa .promise-grid .card--comedy { border-top-color: var(--comedy); }
@media (max-width: 1000px) { .promise-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px)  { .promise-grid { grid-template-columns: 1fr; } }

.stats {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1.5rem, 6vw, 4.5rem);
  margin: var(--sp-6) 0 0;
}
.stats dd { margin: 0; }
.stat { display: flex; flex-direction: column-reverse; gap: 0.3rem; }
.stat__num {
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 7vw, 4.2rem);
  line-height: 1;
  color: var(--music);
}
.stat__label {
  font-size: var(--fs-cap);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

/* rooms teaser — one editorial line after the stats.
   Mustard link on cocoa 6.1:1; hover swaps to cream 11:1. */
.promise-rooms {
  margin-top: var(--sp-5);
  font-size: var(--fs-lede);
  font-weight: 600;
}
.promise-rooms a {
  color: var(--music);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 2px;
  text-decoration-color: var(--hair-cream);
  transition: color 0.2s var(--ease);
}
.promise-rooms a:hover { color: var(--paper); }
.promise-rooms a:focus-visible { outline-color: var(--music); }

/* ============================================================
   ACT 6 · PARTNER WALL + marquee + closing CTA
   ============================================================ */
.partner-wall {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-3);
  margin-top: var(--sp-5);
}
@media (max-width: 900px) { .partner-wall { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .partner-wall { grid-template-columns: 1fr; } }

.tile {
  padding: var(--sp-4);
  background: var(--paper);
  border: 2px solid var(--hair);
  border-radius: var(--radius-plate);
}
.tile__name {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.15rem;
  line-height: 1.15;
  margin-bottom: 0.35rem;
}
.tile__role { font-size: var(--fs-sm); font-weight: 500; color: var(--c-avocado-ink); }
.tile__body { margin-top: 0.6rem; font-size: var(--fs-sm); line-height: 1.55; max-width: 52ch; }

.tile--host {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  background: var(--ink);
  color: var(--paper);
  border-color: transparent;
  box-shadow: var(--shadow-plate);
}
.tile__seal { flex: none; }
.tile--host .seal__ring { stroke: var(--paper); }
.tile--host .seal__ring--thin { stroke: var(--film); }
.tile--host .seal__type,
.tile--host .seal__est { fill: var(--paper); }
.tile--host .seal__mark { fill: var(--music); font-size: 30px; }
.tile--host .tile__role { color: var(--music); }
@media (max-width: 560px) {
  .tile--host { flex-direction: column; align-items: flex-start; gap: var(--sp-4); }
}

.marquee--fmc { --marquee-dur: 32s; }
.marquee--fmc .marquee__group { text-transform: uppercase; }
.marquee__group span { flex: none; }
/* the catchphrase gets the marquee light — mustard slab on cocoa */
.marquee__phrase { color: var(--music); }

.cta-band .section-title { max-width: 22ch; }
/* "Meet me at the FMC!" — big slab send-off over the booking CTA */
.cta-band__title.section-title {
  font-size: clamp(2.6rem, 6.5vw, 4.4rem);
  max-width: none;
}
.cta__actions {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin-top: var(--sp-4);
}
.cta__mail { margin-top: var(--sp-4); }
.cta__mail a {
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--hair-cream);
}
/* hover on the avocado band: mustard is only 3.3:1 there — deep
   cream keeps the state change AND stays 5.4:1 */
.cta__mail a:hover { color: var(--paper-2); }
.band--avocado :focus-visible { outline-color: var(--music); }

/* ============================================================
   RESPONSIVE COMPRESSION — every pinned act's visible stage
   must FIT inside 100svh: plate, reel, ticket halves, platter,
   bulbs, copy. Budgeted with clamp()/svh; tiers at ≤820px and
   ≤700px heights; the film act goes side-by-side on laptops so
   the reel + tear (the stars) are always fully on screen.
   ============================================================ */

/* film act: plate copy and art side-by-side on wide viewports —
   the vertical stack (plate + reel + tear) never fit 800px */
@media (min-width: 1024px) {
  .act--film .stage__col {
    grid-template-columns: minmax(0, 1.04fr) minmax(0, 0.96fr);
    align-items: center;
    justify-items: stretch;
  }
  .act--film .plate-dark { grid-row: 1 / 3; align-self: center; }
  .act--film .reel { grid-column: 2; align-self: end; }
  .act--film .ticket-tear { grid-column: 2; align-self: start; }

  /* studio act: seven beats made the tallest plate on the site — a
     wider copy column lets the title, lede and beats each drop a
     line instead of shrinking the type, so the whole act (disc CTA
     included) reads inside one screen on laptop heights. */
  .act--studio .stage__grid { grid-template-columns: minmax(0, 1.42fr) minmax(0, 0.58fr); }
  .act--studio .beats { max-width: none; }
}

/* Wherever the film act is height-constrained — narrow desktops,
   where its two columns are tightest, or any short viewport — give
   the copy column more width. This is the cheapest height in the
   whole layout: a wider column un-wraps beat lines, and every line
   it un-wraps is ~23px that goes straight back into WHITESPACE
   instead of being taken out of the type. It is layout, not size,
   so a media query is the right tool (G1). */
@media (min-width: 1024px) and (max-width: 1199px),
       (min-width: 1024px) and (max-height: 900px) {
  .act--film .stage__col { grid-template-columns: minmax(0, 1.24fr) minmax(0, 0.76fr); }
  /* the comedy copy is the same trade: it is capped at a 660px
     reading measure, which on a short screen costs more in wrapped
     beat lines than the longer measure costs in readability. Only
     where height is scarce — tall screens keep the tighter column. */
  .act--stage .stage__center { max-width: 820px; }
}

/* stacked layout only — every size that used to live here is now
   continuous in the fit scale at the top of this sheet (G1). */
@media (max-width: 760px) {
  .stage__grid { grid-template-columns: 1fr; gap: var(--sp-4); }
  .stage__art { order: -1; }
}

/* The two hand-tuned height tiers that used to sit here (≤899px and
   ≤700px, both min-width:1024px) are GONE — the fit scale at the top
   of this sheet does their job continuously, so there is no longer a
   pixel where the type snaps a whole step. What survives is the one
   thing a clamp genuinely cannot express: a decision to stop showing
   two optional lines. That is CONTENT, so it stays a media query (G1).
   Threshold measured — above 660px of viewport the act clears the
   fold with both lines in, so they only go on the real shorts.     */
@media (min-width: 1024px) and (max-height: 660px) {
  .stage__lede { display: none; }
  .plate-dark__quiet { display: none; }
}

/* ============================================================
   FLOW TIER (< 1024px) — a phone screen minus the sticky header is
   ~774px; the film stack alone needs ~930px and the studio plate
   ~1010px. Pinned, that clipped BOTH ends: the kicker + title sat
   behind the header and the ticket tear never appeared at all.
   So under 1024px the acts stop pinning and read as normal
   sections — nothing cropped, real space top and bottom.

   Flowing is NOT a downgrade and does not mean "rest everything
   visible" (G5). It means the engine publishes --p under its OTHER
   model, and every window in here is re-derived for that model:

     enter/exit:  p = 0 when the act's top hits the viewport BOTTOM
                  p = 1 when the act's bottom hits the viewport TOP
     so for an element at offset y in an act of height A, viewport V,
     span S = A + V:   enters bottom  y/S
                       centred       (y + V/2)/S
                       leaves top     (y_bottom + V)/S
     and a window is only playable if it sits inside [enters, leaves].

   Reusing the pin windows here is what made the ticket tear
   invisible on every phone ever built: its window is 0.55–0.72 and
   the element is gone by 0.58. Measured, not guessed — see
   CHANGELOG-ARIZONA.md.
   MUST stay last of the responsive tiers — it wins on order.
   ============================================================ */
@media (max-width: 1023px) {
  [data-scene="pin"] { height: auto; }
  [data-scene="pin"] > .scene-stage { position: static; min-height: 0; }
  /* floor is 3.5rem on purpose: that is --sp-7's own floor, the
     padding every quiet band on this page uses, so a flowing act
     never breathes less than the sections around it. Nothing here
     has to fit a fold, so this is pure whitespace and it is the
     one place the page can afford to be generous. */
  .stage { padding-block: clamp(3.5rem, 7.6svh, 4rem); }
  /* ART FIRST (G4·1) — stacked, the film copy plate ran between the
     title and the reel+tear, so the title was 826px from the tear on
     a 390x844 phone and the two could never share a screen. Lifting
     the art above the plate puts them ~200px apart instead: title,
     reel and tear are co-visible on every phone down to 320x568, and
     the animation now plays where you are actually looking. Same
     move the studio act already makes with .stage__art. */
  .act--film .reel,
  .act--film .ticket-tear { order: -1; }
  /* comedy: the marquee band IS the whitespace — keep it even, so
     the bulb strips stay framing the copy instead of drifting off */
  .act--stage .scene-stage { --bulb-band: calc(28px + var(--sp-5)); }

  /* ---- FALLBACK REST STATE ------------------------------------
     Every act's --p is unusable for choreography down here and no
     retune can fix it, because the engine picks its model from the
     act's own height (sb-journey.js:91 — `s.pin && s.h > viewH`).
     Unpinning does not clear s.pin, so a phone-tall act still gets
     the PIN model, whose span is `h - viewH`. Measured at 390x844:

         film    h=1013  ->  span  169px
         studio  h=1174  ->  span  330px
         stage   h= 757  ->  enter/exit, span 1601px

     169px of scroll for a whole act means every window fires in a
     burst while the act's top edge is already under the header —
     which is precisely why the reel, the tear and the tonearm
     measured ZERO travel while on screen. So --p drives nothing
     here but the washes (which only need to be finished by the
     act's bottom edge, and are). Everything else rests readable,
     and the real choreography comes from per-element view()
     timelines below. */
  .act--film .plate-dark,
  .act--studio .plate-light,
  .beat,
  .stage__cta,
  .bulb,
  .act--stage .stage__center .kicker,
  .act--stage .stage__center .section-title,
  .act--stage .stage__center .stage__lede {
    opacity: 1;
    transform: none;
  }
  /* BOUNDARY LAW under flow — pinned, an act's own bottom edge is
     never on screen; flowing, it is, so every end colour has to be
     the next act's exact start colour. Three of the four seams
     already are (#E6AD3F film→studio, #091430 comedy→Promise). The
     fourth is fixed by holding the comedy navy steady here and
     ending the dusk wash on it (overrides §3, same media query) —
     flowing, the comedy act is shorter than the screen, so its wash
     is full before its top edge ever clears. */
  .act--stage .stage__wash { --wash: 1; }
  /* ...and the wash has to cover the stage's LAST fractional row. Act
     heights land on half pixels (svh maths), and the wash box rounds
     the same way, so that final row composited wash-over-base and the
     gold base showed through as a 1px line: #462B40 in navy, #E6B24E
     in gold. One pixel of overhang fixes it, and it is safe precisely
     because of the rule above — a wash ends on the exact colour the
     next section starts on, so the overhanging pixel is invisible.
     Bottom only: the film and studio washes do NOT start on their own
     base's top colour (#F0C468 and #9E0827 over deep gold), so
     overhanging those upward would draw a line instead of hiding one.
     The comedy act is the exception and needs it: its base starts on
     the wine #85152F, which showed through its first row as #2F1D3F,
     and its wash starts on the same #0D1B3D the studio wash ends on —
     so up there the overhanging pixel is invisible too. */
  .stage__wash { bottom: -1px; }
  .act--stage .stage__wash { top: -1px; }
}

/* ============================================================
   MOBILE CHOREOGRAPHY · per-element view() timelines
   ------------------------------------------------------------
   The acts keep every animation on a phone — they just stop
   taking their progress from the act. Each element rides its OWN
   view timeline, so "when does this play" is answered by where
   THAT element is on screen, which is the only question that was
   ever being asked. It makes the act's compressed --p span
   irrelevant, needs no engine change, and can't perturb any
   pinned scene: the whole block is inside the flow width.

   Same mechanism the light-leak and stamp effects above already
   use, so this is the sheet's established pattern, not a new
   dependency. Transform + opacity only. Gated on @supports and
   prefers-reduced-motion; without either, the flow tier's rest
   state above stands and every line is simply readable.
   ============================================================ */
@supports (animation-timeline: view()) {
  @media (max-width: 1023px) and (prefers-reduced-motion: no-preference) {
    .act--film .plate-dark,
    .act--studio .plate-light,
    .act--stage .stage__center .kicker,
    .act--stage .stage__center .section-title,
    .act--stage .stage__center .stage__lede,
    .beat,
    .stage__cta {
      animation: home-vt-in both;
      animation-timeline: view();
      animation-range: entry 10% entry 62%;
    }
    /* the plates carry a whole column of copy, so they lead a beat
       earlier and travel a little further than the lines inside them */
    .act--film .plate-dark,
    .act--studio .plate-light {
      animation-name: home-vt-plate;
      animation-range: entry 4% entry 54%;
    }
    /* THE REEL — scrubs across as it crosses, and because the art now
       sits at the top of the film stack (G4) it is doing this directly
       under its own title instead of 600px below it */
    /* the strip needs its PARENT's timeline, not its own: .reel is
       overflow:hidden, which makes it a scroll container, so view()
       on the strip resolves against the reel's 100px scrollport
       instead of the page and sits pinned at 100% forever (measured:
       -157.58px at every scroll position). Naming the timeline on
       .reel — whose own nearest scroll container IS the page, since
       .scene-stage uses overflow:clip and clip is not a scroll
       container — gives the strip the crossing it actually wants. */
    .reel { view-timeline-name: --fmc-reel; }
    .reel__strip {
      animation: home-vt-reel both;
      animation-timeline: --fmc-reel;
      animation-range: entry 18% cover 52%;
    }
    /* THE TEAR — the punchline, and on a phone it had literally never
       played: its --p window was 0.55-0.72 and the element was off
       screen by 0.58. On its own timeline it rips in full view. */
    .ticket-half--l,
    .ticket-half--r {
      animation: home-vt-tear-l both;
      animation-timeline: view();
      animation-range: entry 46% cover 62%;
    }
    .ticket-half--r { animation-name: home-vt-tear-r; }
    .tonearm {
      animation: home-vt-tonearm both;
      animation-timeline: view();
      animation-range: entry 22% cover 50%;
    }
    .platter__disc {
      animation: home-vt-platter both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
    /* the marquee chase — each strip has its own timeline, so the top
       row lights as it enters and the bottom row lights as IT enters,
       which is what stops the half-dark marquee the act's single --p
       could never avoid (the two strips are ~760px apart) */
    .bulb {
      animation: home-vt-bulb both;
      animation-timeline: view();
      animation-range: entry calc(8% + var(--bi, 0) * 3.2%) entry calc(30% + var(--bi, 0) * 3.2%);
    }
  }
}

/* ============================================================
   SIGNATURE MOMENTS — additive, gated, transform/opacity only.
   NONE touches an act's --p windows, the scrub choreography,
   the two canon lines, or index.html. Each is motion-gated so
   reduced-motion / no-JS readers get the finished, static page.
   (a) film-burn wipe as static sections enter
   (c) hand-stamped section titles on the quiet bands
   (e) hero F·M·C flicker on like marquee bulbs (enhances .hi)
   [ (d) breathing grain lives in sb-core §9 — global texture ]
   ============================================================ */

/* ---- (e) HERO MARQUEE FLICKER ------------------------------
   The F·M·C keep their existing .hi rise entrance; AFTER each
   letter settles it flickers up to full like a marquee bulb
   warming — irregular opacity keyframe, a hair of imperfection.
   Two animations in one list: home-rise (unchanged timing) THEN
   home-bulb-flicker. Opacity-only flicker composes cleanly with
   the rise's transform; each letter gets its own start beat so
   they don't warm in lockstep. Gated + .sb-static safe. */
@media (prefers-reduced-motion: no-preference) {
  .hero__f.hi { animation: home-rise 0.7s var(--ease-out) 0.66s both, home-bulb-flicker 0.9s steps(1, end) 1.30s 1 both; }
  .hero__m.hi { animation: home-rise 0.7s var(--ease-out) 0.78s both, home-bulb-flicker 0.9s steps(1, end) 1.52s 1 both; }
  .hero__c.hi { animation: home-rise 0.7s var(--ease-out) 0.90s both, home-bulb-flicker 0.8s steps(1, end) 1.44s 1 both; }
}
.sb-static .hero__f.hi,
.sb-static .hero__m.hi,
.sb-static .hero__c.hi { animation: none !important; opacity: 1 !important; transform: none !important; }

/* ---- (a) FILM-BURN / LIGHT-LEAK WIPE -----------------------
   As each quiet static band scrolls in, a warm peach→orange
   light-leak sweeps across once and burns off — like a frame
   catching the projector lamp. Pure opacity overlay on a ::before,
   driven by scroll-driven CSS where supported (same mechanism as
   core's .sb-va reveals) so it needs no engine hook and can't
   perturb any act's --p. Non-interactive; RM-gated by @supports+MQ. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .craft-intro,
    #the-promise,
    #partners { position: relative; }
    .craft-intro::after,
    #the-promise::after,
    #partners::after {
      content: "";
      position: absolute;
      inset: 0;
      z-index: 4;
      pointer-events: none;
      opacity: 0;
      mix-blend-mode: screen;
      background:
        linear-gradient(105deg,
          transparent 0 30%,
          rgba(252, 164, 52, 0.55) 44%,
          rgba(191, 10, 48, 0.42) 50%,
          rgba(252, 164, 52, 0.5) 56%,
          transparent 70% 100%);
      /* opacity-only wipe (brand rule: only transform + opacity ever
         animate) — the warm bar blooms in and burns off as the band
         enters. transform:translateX gives it lateral drift, still
         within the transform/opacity envelope. */
      animation: home-lightleak both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }
  }
}

/* ---- (c) HAND-STAMPED SECTION TITLES -----------------------
   The two quietest section titles (The Promise, In Good Company)
   stop plainly appearing and instead land like a rubber stamp:
   scale 1.06→1 with a ~1.4deg rotate settle and a touch of
   overshoot, thunking onto the page. Scroll-driven so it fires
   on entry with zero engine/act coupling; kicker gets a lighter
   companion stamp a beat earlier. Transform+opacity only. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    #the-promise .kicker,
    #partners .kicker {
      transform-origin: left center;
      animation: home-stamp-kicker both;
      animation-timeline: view();
      animation-range: entry 8% entry 34%;
    }
    #the-promise > .wrap > .section-title,
    #partners > .wrap > .section-title {
      transform-origin: left center;
      animation: home-stamp both;
      animation-timeline: view();
      animation-range: entry 12% entry 44%;
    }
  }
}
.sb-static #the-promise .kicker,
.sb-static #partners .kicker,
.sb-static #the-promise > .wrap > .section-title,
.sb-static #partners > .wrap > .section-title {
  animation: none !important; transform: none !important; opacity: 1 !important;
}

/* ---- (f) PROJECTOR-FLICKER SHEEN on the film reel ----------
   A faint warm lamp-sheen washes the reel and flickers like a
   running projector. It's a NEW ::after on .reel (which was
   pseudo-free), clipped by the reel's own overflow:hidden, and
   drives OPACITY ONLY on its own idle loop — completely
   independent of .reel__strip's --p scrub window, so the film
   act's choreography is untouched. Gated + .sb-static safe. */
@media (prefers-reduced-motion: no-preference) {
  .reel::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    mix-blend-mode: soft-light;
    background: linear-gradient(100deg,
      transparent 0 20%,
      rgba(252, 164, 52, 0.5) 44% 56%,
      transparent 80% 100%);
    opacity: 0.15;
    animation: home-projector 3.7s steps(1, end) infinite;
  }
}
.sb-static .reel::after { animation: none !important; opacity: 0 !important; }

/* ---- (b) MARQUEE-BULB CHASE on the FMC ticker dots ---------
   The mustard dot separators in the closing FMC marquee glow in
   sequence — a marquee chase running down the ticker. Shared
   animation, nth-of-type delays; transform (scale) + opacity
   ONLY (the pre-baked box-shadow glow stays static, per the
   transform/opacity rule). Pauses with the marquee when the
   engine flags it offscreen (.is-off), respecting the layer
   budget. Not an act — no --p coupling. */
@media (prefers-reduced-motion: no-preference) {
  .marquee--fmc .marquee__dot {
    animation: home-bulb-chase 1.6s var(--ease-inout) infinite;
  }
  .marquee--fmc .marquee__dot:nth-of-type(3n + 1) { animation-delay: 0s; }
  .marquee--fmc .marquee__dot:nth-of-type(3n + 2) { animation-delay: 0.2s; }
  .marquee--fmc .marquee__dot:nth-of-type(3n)     { animation-delay: 0.4s; }
  [data-marquee].is-off .marquee--fmc .marquee__dot,
  .marquee--fmc[data-marquee].is-off .marquee__dot { animation-play-state: paused; }
}
.sb-static .marquee--fmc .marquee__dot { animation: none !important; }

/* ============================================================
   MOTION KEYFRAMES (hero only — everything else is scrubbed)
   ============================================================ */
/* ---- mobile view()-timeline choreography (flow tier) --------
   End states are byte-identical to the pinned versions' full-scrub
   values, so a phone and a laptop land on the same picture:
   reel -16% (the flow throw), tear ±30px/8px/-6deg and 5deg,
   tonearm -16deg->12deg, platter 480deg.                        */
@keyframes home-vt-in {
  from { opacity: 0; transform: translate3d(0, 16px, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes home-vt-plate {
  from { opacity: 0; transform: translate3d(0, 30px, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes home-vt-reel {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-16%, 0, 0); }
}
@keyframes home-vt-tear-l {
  from { transform: translate3d(0, 0, 0) rotate(0deg); }
  to   { transform: translate3d(-30px, 8px, 0) rotate(-6deg); }
}
@keyframes home-vt-tear-r {
  from { transform: translate3d(0, 0, 0) rotate(0deg); }
  to   { transform: translate3d(30px, -6px, 0) rotate(5deg); }
}
@keyframes home-vt-tonearm {
  from { transform: rotate(-16deg); }
  to   { transform: rotate(12deg); }
}
@keyframes home-vt-platter {
  from { transform: rotate(0deg); }
  to   { transform: rotate(480deg); }
}
@keyframes home-vt-bulb {
  from { opacity: 0; transform: scale(0.35); }
  to   { opacity: 1; transform: scale(1); }
}

@keyframes home-rise {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: none; }
}
@keyframes home-disc-in {
  from { opacity: 0; transform: scale(0.55) rotate(-80deg); }
  to   { opacity: 1; transform: none; }
}
@keyframes home-ticket-in {
  from { opacity: 0; transform: translate(-120%, -50%) rotate(-20deg); }
  to   { opacity: 1; transform: translateY(-50%) rotate(-9deg); }
}
@keyframes home-breathe {
  from { transform: scale(1); }
  to   { transform: scale(1.06); }
}
/* marquee-bulb warm-up: irregular flicker to full, like a filament
   catching. steps(1,end) timing makes the on/off snaps read as a
   real bulb, not a smooth fade. Opacity only — the letter's rise
   transform is untouched. Ends solid at full. */
@keyframes home-bulb-flicker {
  0%   { opacity: 1; }
  8%   { opacity: 0.35; }
  16%  { opacity: 1; }
  26%  { opacity: 0.4; }
  34%  { opacity: 1; }
  40%  { opacity: 0.6; }
  52%  { opacity: 1; }
  62%  { opacity: 0.85; }
  100% { opacity: 1; }
}
/* light-leak wipe: a warm bar blooms in and burns off as the band
   enters — opacity + a little lateral drift (transform), nothing else. */
@keyframes home-lightleak {
  0%   { opacity: 0;   transform: translateX(8%); }
  35%  { opacity: 0.9; transform: translateX(0); }
  100% { opacity: 0;   transform: translateX(-8%); }
}
/* hand-stamp: bigger + tilted, thunks down to true with a hair of
   overshoot on the settle. Transform + opacity only. */
@keyframes home-stamp {
  0%   { opacity: 0; transform: scale(1.06) rotate(1.4deg); }
  60%  { opacity: 1; transform: scale(0.992) rotate(-0.5deg); }
  100% { opacity: 1; transform: scale(1) rotate(0deg); }
}
@keyframes home-stamp-kicker {
  0%   { opacity: 0; transform: translateX(-6px) rotate(-1deg); }
  70%  { opacity: 1; transform: translateX(1px) rotate(0.3deg); }
  100% { opacity: 1; transform: none; }
}
/* projector lamp flicker — irregular opacity, snap timing. */
@keyframes home-projector {
  0%   { opacity: 0.10; }
  12%  { opacity: 0.22; }
  24%  { opacity: 0.08; }
  40%  { opacity: 0.18; }
  55%  { opacity: 0.12; }
  70%  { opacity: 0.24; }
  85%  { opacity: 0.10; }
  100% { opacity: 0.16; }
}
/* marquee bulb chase — each dot swells to full then dims; the glow
   (box-shadow) is static, only scale + opacity move. */
@keyframes home-bulb-chase {
  0%,
  100% { opacity: 0.45; transform: scale(0.82); }
  45%  { opacity: 1;    transform: scale(1.12); }
}

/* ============================================================
   REDUCED MOTION + .sb-static — the journey reads as a finished
   editorial page: end-state backdrops, all copy visible,
   nothing transformed, nothing animating.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .beat, .stage__wash, .bulbs, .bulb, .stage__cta,
  .plate-dark, .plate-light,
  .act--stage .stage__center .kicker,
  .act--stage .stage__center .section-title,
  .act--stage .stage__center .stage__lede { opacity: 1 !important; }
  .beat, .platter__disc, .tonearm, .reel__strip, .ticket-half, .stage__cta,
  .bulb, .plate-dark, .plate-light,
  .act--stage .stage__center .kicker,
  .act--stage .stage__center .section-title,
  .act--stage .stage__center .stage__lede { transform: none !important; }
  .act-stub, .act-stub:hover { transition: none; transform: none; }
  .hero__ring, .hero__logo .rt-logo__disc { animation: none !important; }
  /* nothing pins here, so the pin-fit height tiers' tight padding buys
     nothing — take the same breathing room the flow tier gives */
  .stage { padding-block: var(--sp-6); }
  .act--stage .scene-stage { --bulb-band: calc(28px + var(--sp-5)); }
}
.sb-static .beat, .sb-static .stage__wash, .sb-static .bulbs, .sb-static .bulb,
.sb-static .stage__cta, .sb-static .plate-dark, .sb-static .plate-light,
.sb-static .act--stage .stage__center .kicker,
.sb-static .act--stage .stage__center .section-title,
.sb-static .act--stage .stage__center .stage__lede { opacity: 1 !important; }
.sb-static .beat, .sb-static .platter__disc, .sb-static .tonearm,
.sb-static .reel__strip, .sb-static .ticket-half, .sb-static .stage__cta,
.sb-static .bulb, .sb-static .plate-dark, .sb-static .plate-light,
.sb-static .act--stage .stage__center .kicker,
.sb-static .act--stage .stage__center .section-title,
.sb-static .act--stage .stage__center .stage__lede { transform: none !important; }
.sb-static .hi, .sb-static .hero__ring,
.sb-static .hero__logo .rt-logo__disc,
.sb-static .hero__logo .rt-logo__ticket { animation: none !important; }
.sb-static .stage { padding-block: var(--sp-6); }
.sb-static .act--stage .scene-stage { --bulb-band: calc(28px + var(--sp-5)); }

/* ============================================================
   IDLE PAUSE — engine toggles .is-off on [data-idle-pause] when
   the scope leaves the viewport; the hero's infinite loops
   (disc spin, ring breathe) stop ticking instead of running
   forever below the fold. One-shot .hi entrances are long done
   by then, so freezing them is a no-op.
   ============================================================ */
[data-idle-pause].is-off :is(.hero__ring, .rt-logo__disc, .rt-logo__ticket, .hi) {
  animation-play-state: paused;
}
