/* ============================================================
   THE REVEAL — the scroll-driven cinematic opening.

   Originally built as the opening of /first-to-know/ (then the pre-order
   page); now shared, unchanged in choreography, by the homepage hero and
   that page. One copy, two pages — edit here and both move together.

   The sequence, scrubbed off the engine's single damped --p:
     (on load) SURROGATE wipes in, then ENTREPRENEUR beneath it — a CSS
               entrance, so the page is never a blank screen waiting for input
     book   .08–.52 the lamp warms up, the words part and fall into the
                    dark, the book turns into the beam and settles
     open   .52–.86 the status lands, then the ask a beat later

   Every phase finishes inside its own beat band, so reduced motion — which
   quantises --p to each band's end — always receives a finished plate.
   ============================================================ */

/* --intro drives the title's entrance on load. The sequence used to wipe the
   title in on scroll, which meant the page opened on a near-black screen — it
   read as loading. Now the title arrives by itself and the scroll begins at the
   words parting, so the hero is alive before anyone touches anything. */
@property --intro { syntax:"<number>"; inherits:false; initial-value:0 }
@keyframes rv-intro { from { --intro:0 } to { --intro:1 } }

.rv{ position:relative; height:330vh; background:var(--stage); color:var(--on-stage) }
/* the homepage hero hands off to a paper section, so it carries extra scroll for that move */
.rv--hero{ height:420vh }
@media (max-width:47.99rem){ .rv{ height:260vh } .rv--hero{ height:330vh } }
.rv__stage{
  position:sticky; top:var(--masthead-h);
  height:calc(100dvh - var(--masthead-h));
  overflow:clip; contain:layout paint style;
  display:grid; place-items:center;
  perspective:1600px;
  --p:0;
}
/* ONE key light, upper-left-front. Every shadow on this page falls down-and-right; every highlight sits up-left.
   Phases, each finishing inside its beat band so reduced motion receives finished plates:
   dark 0–.04 · title .04–.45 · book .45–.78 · open .78–1 */
.rv [data-p]{
  --b:  clamp(0, (var(--p) - .005) * 24,   1);   /* SURROGATE    — mostly handled by the load-in */
  --c:  clamp(0, (var(--p) - .02)  * 24,   1);   /* ENTREPRENEUR — likewise */
  --dl: clamp(0, (var(--p) - .08)  * 6,    1);   /* the lamp warms up      (.08–.25) */
  --d:  clamp(0, (var(--p) - .08)  * 2.6,  1);   /* the book's beat master (.08–.46) */
  --de: calc(1 - (1 - var(--d)) * (1 - var(--d)));                          /* ease-out: the book decelerates into rest */
  --dw: clamp(0, (var(--p) - .10)  * 3.2,  1);   /* the words make room    (.10–.41) */
  --dwe:calc(1 - (1 - var(--dw)) * (1 - var(--dw)) * (1 - var(--dw)));      /* cubic ease-out: a quick give, then settle */
  --dg: clamp(0, (var(--p) - .22)  * 3.4,  1);   /* they fall into the dark (.22–.51) */
  --e:  clamp(0, (var(--p) - .52)  * 4.2,  1);   /* the ask                (.52–.76) */
  --es: clamp(0, var(--e) * 1.6, 1);              /*   the status first (done .669) */
  --ese:calc(1 - (1 - var(--es)) * (1 - var(--es)) * (1 - var(--es)));
  --ec: clamp(0, var(--e) * 1.25, 1);             /*   the button a beat later (done .711) */
  --ece:calc(1 - (1 - var(--ec)) * (1 - var(--ec)) * (1 - var(--ec)));
}
/* the title: two words, stacked, enormous. they part to make room for the book; letters arrive solid (the clip does the reveal) */
.rv__w{
  position:absolute; left:50%;
  font-family:var(--sans); font-weight:800; font-stretch:103%;
  font-size:clamp(3.2rem, 1rem + 11.6vw, 13rem);
  line-height:.84; letter-spacing:-.05em; white-space:nowrap;
  color:var(--on-stage); pointer-events:none;
  -webkit-user-select:none; user-select:none;
  will-change:transform, opacity;
}
/* recession as light falloff, not a dimmer: the letters nearest the pool stay lit, the far ends drop into the dark */
.rv__w--1{
  z-index:1; top:50%;
  translate:-50% calc(-100% - 1vh - 16vh * var(--dwe));
  animation:rv-intro .85s var(--ease-out) .2s both;
  /* whichever is further along: the entrance, or the scroll */
  opacity:calc(max(clamp(0, var(--b) * 6, 1), var(--intro)) * (1 - var(--ese)));
  clip-path:inset(0 calc((1 - max(var(--b), var(--intro))) * 100%) 0 0);
  -webkit-mask-image:radial-gradient(ellipse 60% 400% at 50% 50%, #000 calc(42% - 30% * var(--dg)), rgba(0,0,0,calc(1 - .62 * var(--dg))) 100%);
          mask-image:radial-gradient(ellipse 60% 400% at 50% 50%, #000 calc(42% - 30% * var(--dg)), rgba(0,0,0,calc(1 - .62 * var(--dg))) 100%);
}
.rv__w--2{
  z-index:1; top:50%;
  translate:-50% calc(1vh + 16vh * var(--dwe));
  animation:rv-intro .85s var(--ease-out) .62s both;
  opacity:calc(max(clamp(0, var(--c) * 6, 1), var(--intro)) * (1 - var(--ese)));
  clip-path:inset(0 calc((1 - max(var(--c), var(--intro))) * 100%) 0 0);
  -webkit-mask-image:radial-gradient(ellipse 60% 400% at 50% 50%, #000 calc(42% - 30% * var(--dg)), rgba(0,0,0,calc(1 - .48 * var(--dg))) 100%);
          mask-image:radial-gradient(ellipse 60% 400% at 50% 50%, #000 calc(42% - 30% * var(--dg)), rgba(0,0,0,calc(1 - .48 * var(--dg))) 100%);
}
/* the room: the lamp spills on the wall behind the book and pools on the floor under it */
.rv__room{
  position:absolute; left:50%; top:50%; z-index:2; pointer-events:none;
  width:calc(clamp(10rem, 26vw, 19rem) * 3); aspect-ratio:3/2.2;
  translate:-50% -38%;
  background:
    radial-gradient(ellipse 22% 7% at 52% 90%, rgba(244,241,234,.22), transparent 70%),
    radial-gradient(ellipse 34% 40% at 50% 46%, rgba(244,241,234,.10), transparent 70%);
  opacity:var(--dl);
  transform:translateY(calc((1 - var(--de)) * 4vh - 3vh * var(--ese))) scale(calc(.6 + .4 * var(--de)));
}
/* the book is found by the light: nothing of it exists until the pool reaches it (the mask is on the object, not on the world) */
.rv__book{
  position:relative; z-index:2;
  width:clamp(10rem, 26vw, 19rem);
  transform-style:preserve-3d;
  --block:9%;
  transform:
    translateY(calc((1 - var(--de)) * 4vh - 3vh * var(--ese)))
    rotateY(calc(-28deg + 28deg * var(--de)))
    scale(calc(.84 + .16 * var(--de)));
  opacity:clamp(0, var(--d) * 12, 1);          /* a 2.5% anti-pop ramp only; the light does the revealing */
  will-change:transform;
}
.rv__mask{
  margin:-32%; padding:32%;                    /* the masked box is wider than the cover so the cast shadow survives the mask */
  --r: calc(var(--dl) * var(--dl));            /* ease-in: a lamp warming up, not a curtain */
  -webkit-mask:radial-gradient(circle at 50% 44%, #000 calc(3% + 97% * var(--r)), transparent calc(9% + 97% * var(--r)));
          mask:radial-gradient(circle at 50% 44%, #000 calc(3% + 97% * var(--r)), transparent calc(9% + 97% * var(--r)));
  position:relative;
}
.rv__mask img{
  display:block; width:100%; height:auto; position:relative;
  box-shadow:0 1px 1px rgba(0,0,0,.5), 6px 26px 60px -22px rgba(0,0,0,.9);
}
/* the jacket as an object: a cut edge with a lit lip up-left, a hinge groove by the spine, the foot falling away from the lamp */
.rv__mask::after{
  /* the wrapper is padded 32% of the cover's width on every side; the cover is 800×1235, so the overlay sits at these insets */
  content:""; position:absolute; inset:14.65% 19.51%; pointer-events:none;
  background:
    linear-gradient(90deg, rgba(0,0,0,.42) 0, rgba(0,0,0,.10) 1.2%, rgba(0,0,0,0) 2.4%, rgba(255,255,255,.10) 4.2%, rgba(0,0,0,.22) 5.4%, rgba(0,0,0,0) 8%),
    linear-gradient(180deg, rgba(13,12,11,0) 45%, rgba(13,12,11,.16) 100%);
  box-shadow:inset 0 0 0 1px rgba(13,12,11,.18), inset 1px 1px 0 rgba(255,255,255,.6);
}
/* the page block: a fore-edge face at the right, front-facing while the book is turned toward us, edge-on once it squares up */
.rv__book::after{
  content:""; position:absolute; top:.6%; bottom:.6%; left:100%; width:var(--block); pointer-events:none;
  background:
    linear-gradient(90deg, rgba(0,0,0,.08), rgba(0,0,0,.55)),
    repeating-linear-gradient(180deg, #EFEAE0 0 1.5px, #D9D2C4 1.5px 3px);
  transform-origin:0 50%; transform:rotateY(90deg);
  backface-visibility:hidden;
  opacity:clamp(0, (var(--dl) - .55) * 3, 1);   /* it, too, waits for the light to reach the edge */
}
/* the ask lands: the status first, the button a beat later, each decelerating into place */
.rv__open{
  position:absolute; left:50%; bottom:clamp(2rem,6vh,4rem); translate:-50% 0; z-index:4;
  width:max-content; max-width:calc(100vw - 2rem);
  display:grid; justify-items:center; gap:var(--s-4); text-align:center;
}
.rv__status{ color:var(--accent-lift); letter-spacing:.3em; opacity:var(--ese); transform:translateY(calc((1 - var(--ese)) * 14px)) }
/* The ask sizes itself to its widest child, and a WRAPPING flex row reports its
   widest button as that width — which stacked two retailers on desktop. Keeping
   the row on one line makes the group report the full width it needs; letting the
   buttons keep their natural width stops the labels breaking onto two lines.
   Two buttons need ~458px, so they become a column below 34rem rather than the
   component's usual 30rem. */
.rv__open .buy--multi{ flex-wrap:nowrap }
.rv__open .buy--multi .buy__btn{ flex:0 0 auto }
@media (max-width:33.99rem){
  .rv__open .buy--multi{ flex-direction:column }
  .rv__open .buy--multi .buy__btn{ width:100% }
}
.rv__open > p:last-child{ opacity:var(--ece); transform:translateY(calc((1 - var(--ece)) * 22px)) }
.rv__stage[data-beat]:not([data-beat="open"]) .rv__open{ visibility:hidden }   /* nothing invisible is ever focusable (no data-beat = no JS = keep it) */
.rv__hint{
  position:absolute; left:50%; bottom:1.5rem; translate:-50% 0;
  color:var(--on-stage-dim); letter-spacing:.24em;
  opacity:calc(1 - clamp(0, (var(--p) - .04) * 8, 1));
  display:grid; justify-items:center; gap:.55rem; text-align:center;
  font-size:.6875rem;
}
/* a dot travelling down a rail: motion is what tells someone the page is ready,
   not loading. It is the only looping animation on the site, and it stops the
   moment the sequence starts moving. */
.rv__hintrail{
  display:block; width:1px; height:2.1rem; position:relative;
  background:linear-gradient(var(--stage-line), transparent);
}
.rv__hintdot{
  position:absolute; left:50%; top:0; width:3px; height:3px; border-radius:50%;
  background:var(--accent-lift); translate:-50% 0;
  animation:rv-cue 1.9s var(--ease-io) infinite;
}
@keyframes rv-cue{
  0%   { transform:translateY(0);      opacity:0 }
  22%  { opacity:1 }
  78%  { opacity:1 }
  100% { transform:translateY(2.1rem); opacity:0 }
}
/* nobody has moved: say it louder rather than move the page for them */
.rv__stage.is-waiting .rv__hint{ color:var(--on-stage) }
.rv__stage.is-waiting .rv__hinttext{ animation:rv-nudge 2.4s var(--ease-io) infinite }
@keyframes rv-nudge{ 0%,100%{ opacity:.65 } 50%{ opacity:1 } }
@media (prefers-reduced-motion:reduce){
  .rv__hintdot{ animation:none; top:auto; bottom:0 }
  .rv__stage.is-waiting .rv__hinttext{ animation:none }
}
@media (max-width:47.99rem){
  .rv__w{ font-size:min(clamp(2.4rem, 1rem + 13.5vw, 5.6rem), 15vw) }   /* 'Entrepreneur' is 6.06em wide: keep it inside the phone */
  /* phones have no room for the words beside the book: they part further and all but leave once the ask lands */
  .rv__w--1{ translate:-50% calc(-100% - 1vh - 22vh * var(--dwe)) }
  .rv__w--2{ translate:-50% calc(1vh + 22vh * var(--dwe)) }
  .rv__book{ width:min(56vw, 14rem); --block:8% }
  .rv__room{ width:calc(min(56vw, 14rem) * 3) }
  .rv__open .cta{ font-size:.9375rem; padding-inline:1.2rem }
}
/* short viewports (laptops at 768px tall included): the cover is sized from the stage, lifts and shrinks as the ask arrives, and the ghost title leaves */
@media (max-height:52rem){
  .rv__book{
    width:min(clamp(10rem, 26vw, 19rem), calc((100dvh - var(--masthead-h) - 13rem) * 800 / 1235));
    transform:
      translateY(calc((1 - var(--de)) * 4vh - 10vh * var(--ese)))
      rotateY(calc(-28deg + 28deg * var(--de)))
      scale(calc(.84 + .16 * var(--de) - .12 * var(--ese)));
  }
  .rv__room{ width:calc(min(clamp(10rem, 26vw, 19rem), calc((100dvh - var(--masthead-h) - 13rem) * 800 / 1235)) * 3) }
}


/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion:reduce){
  .rv__w{ animation:none }
  .rv__w--1, .rv__w--2{ --intro:1 }
  .rv [data-p]{ --b:1; --c:1 }   /* the first plate is the title, not a black screen */
  .rv__book, .rv__w{ will-change:auto }
}

/* ---------- no JavaScript: the sequence becomes a plain readable plate ---------- */
html:not(.seq-ready) .rv{ height:auto }
html:not(.seq-ready) .rv__stage{ position:static; height:auto; padding-block:12vh 6vh; display:grid; gap:1.5rem; place-items:center }
html:not(.seq-ready) .rv [data-p]{ --b:1; --c:1; --d:1; --de:1; --dl:1; --dw:1; --dwe:1; --dg:1; --e:1; --es:1; --ese:1; --ec:1; --ece:1 }
html:not(.seq-ready) .rv__hint, html:not(.seq-ready) .rv__room, html:not(.seq-ready) .rv__book::after{ display:none }
html:not(.seq-ready) .rv__w{ animation:none; --intro:1; position:static; translate:none; clip-path:none; -webkit-mask:none; mask:none; opacity:1; font-size:clamp(2.4rem, 1rem + 6vw, 6rem); line-height:1 }
html:not(.seq-ready) .rv__w--1{ margin-bottom:-1.5rem }
html:not(.seq-ready) .rv__open{ position:static; translate:none; visibility:visible; width:auto; max-width:none }
html:not(.seq-ready) .rv__status, html:not(.seq-ready) .rv__open > p:last-child{ opacity:1; transform:none }
html:not(.seq-ready) .rv__mask{ -webkit-mask:none; mask:none }

/* ---------- the hero variant ----------
   A sticky stage always scrolls out over its own height once --p reaches 1.
   A paper wipe used to cover the stage at that point, which made the exit frame
   a blank sheet — half a screen of nothing before Act 2 arrived. The hero now
   ends on its own last frame, the book and the ask, and that is what scrolls
   away. The masthead crosses to paper when the hero actually leaves the top of
   the screen (body.is-past-hero, set by assets/js/cue.js), not on a beat: --p
   sits at 1 for the whole exit and so cannot express "the hero has gone". */

/* ---------- reduced motion ---------- */
@media (prefers-reduced-motion:reduce){
  .rv__w{ animation:none }
  .rv__w--1, .rv__w--2{ --intro:1 }
  .rv [data-p]{ --b:1; --c:1 }   /* the first plate is the title, not a black screen */
  .rv__book, .rv__w{ will-change:auto }
}

/* ---------- no JavaScript: the sequence becomes a plain readable plate ---------- */
html:not(.seq-ready) .rv{ height:auto }
html:not(.seq-ready) .rv__stage{ position:static; height:auto; padding-block:12vh 6vh; display:grid; gap:1.5rem; place-items:center }
html:not(.seq-ready) .rv [data-p]{ --b:1; --c:1; --d:1; --de:1; --dl:1; --dw:1; --dwe:1; --dg:1; --e:1; --es:1; --ese:1; --ec:1; --ece:1 }
html:not(.seq-ready) .rv__hint, html:not(.seq-ready) .rv__room, html:not(.seq-ready) .rv__book::after{ display:none }
html:not(.seq-ready) .rv__w{ animation:none; --intro:1; position:static; translate:none; clip-path:none; -webkit-mask:none; mask:none; opacity:1; font-size:clamp(2.4rem, 1rem + 6vw, 6rem); line-height:1 }
html:not(.seq-ready) .rv__w--1{ margin-bottom:-1.5rem }
html:not(.seq-ready) .rv__open{ position:static; translate:none; visibility:visible; width:auto; max-width:none }
html:not(.seq-ready) .rv__status, html:not(.seq-ready) .rv__open > p:last-child{ opacity:1; transform:none }
html:not(.seq-ready) .rv__mask{ -webkit-mask:none; mask:none }
