/* ==========================================================================
   The waitlist page
   ==========================================================================
   Three beats over one pinned frame, and then the page is over.

   Same construction as the story page's hero, for the same reason: the beats
   are an empty scroll track and the pin is what you see, so nothing has to
   scroll through its own height before the next state can arrive. Every beat
   is a snap point with `scroll-snap-stop: always`, which is what stops a fast
   flick taking the whole page in one gesture.
   ========================================================================== */

.waitlist {
  background: var(--tunas-black);
}

.wl {
  position: relative;
}
.wl__beat {
  height: 100vh;
  height: 100dvh;
  pointer-events: none;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

.wl__pin {
  position: fixed;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  display: grid;
  place-items: center;
  background: var(--tunas-black);
}

/* --- the clip ------------------------------------------------------------
   Playing, not scrubbed — it is a backdrop here, not the subject.

   The blur is on the video itself rather than a backdrop-filter over it. A
   full-frame backdrop-filter re-samples everything behind it every frame, and
   behind it is a video that is already decoding every frame; blurring the
   element directly is the same picture for one pass instead of two. The scale
   is what hides the transparent edge a blur leaves behind. */
.wl__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.02);
  filter: blur(0px);
  transition: filter 0.75s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.75s cubic-bezier(0.22, 1, 0.36, 1);
}
/* Two films in the same frame, crossfaded rather than cut. The hero holds the
   opening and the line; the second arrives with the form and arrives ALREADY
   soft, so the change of picture and the change of focus are one movement
   rather than two inside the same beat. */
.wl__video {
  opacity: 1;
  transition: opacity 0.9s ease, filter 0.75s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.75s cubic-bezier(0.22, 1, 0.36, 1);
}
.wl__video--film {
  opacity: 0;
  filter: blur(22px) saturate(112%) brightness(0.6);
  transform: scale(1.12);
}
body.wl-b2 .wl__video--hero {
  opacity: 0;
}
body.wl-b2 .wl__video--film {
  opacity: 1;
}

/* Keeps the header and the line legible over whatever frame is up. */
.wl__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
      to bottom,
      rgb(5 5 6 / 0.55) 0%,
      rgb(5 5 6 / 0.1) 34%,
      rgb(5 5 6 / 0.35) 100%
    ),
    radial-gradient(120% 90% at 50% 50%, transparent 45%, rgb(5 5 6 / 0.5) 100%);
}

/* The sheet the last beat puts between you and the clip. Tint and a rim only
   — the blur is on the video, above. */
.wl__glass {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(
    to bottom,
    rgb(9 11 13 / 0.34),
    rgb(9 11 13 / 0.52)
  );
  transition: opacity 0.6s ease;
}
body.wl-b2 .wl__glass {
  opacity: 1;
}

/* --- beat 1: the line ---------------------------------------------------- */
/* The same line at the same size as the story page's hero, deliberately: it is
   the same line. Set edge to edge rather than as a wrapped block — one
   unbroken measure across the window is the whole shape of it, so the width is
   full, the line does not wrap, and the size is the tuned vw figure from that
   page rather than a clamp. Kept as literal numbers instead of borrowing
   .stage__title, which carries a grid area and a --v opacity that belong to a
   pin this page does not have. */
.wl__tag {
  position: relative;
  z-index: 2;
  margin: 0;
  width: 100%;
  padding: 0 2vw;
  font-family: var(--font-family-display);
  font-weight: 700;
  font-size: 10.85vw;
  line-height: 1;
  letter-spacing: -0.035em;
  text-align: center;
  white-space: nowrap;
  color: var(--tunas-paper);
  text-shadow: 0 2px 40px rgba(17, 24, 31, 0.35);
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.6s ease, transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}
@media (max-width: 640px) {
  .wl__tag {
    padding: 0 1vw;
    font-size: 11.6vw;
    letter-spacing: -0.04em;
  }
}
body.wl-b1 .wl__tag {
  opacity: 1;
  transform: translateY(0);
}
/* Leaves upward as the card arrives, so the two never share the middle. */
body.wl-b2 .wl__tag {
  opacity: 0;
  transform: translateY(-52px) scale(0.96);
}

/* --- beat 2: the auto-cycling welcome ------------------------------------
   The marketing page's welcome section, driven by a TIMER instead of the
   scroll wheel. The stage is a centred column: the welcome row (phones + the
   cycling copy) on top, the join box fixed beneath. The stage rises in once on
   wl-b2; the copy and the phone screens then advance on their own clock — see
   waitlist.js. */
.wl__stage {
  position: absolute;
  z-index: 3;
  left: 50%;
  /* A little below centre — the fan is visually top-heavy, so a dead-centre
     stage sits a touch high; nudging it down settles the whole block. */
  top: 54%;
  /* Wide enough that the fan and a full-width copy column never fight for the
     same pixels — the old 1240 cap squeezed the copy and wrapped the headline.
     Capped at 1400 so it never spreads thin on an ultrawide. */
  width: min(1400px, 94vw);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(26px, 5vh, 56px);
  opacity: 0;
  transform: translate(-50%, calc(-50% + 46px));
  transition: opacity 0.55s ease 0.1s,
    transform 0.75s cubic-bezier(0.22, 1, 0.36, 1) 0.1s;
  pointer-events: none;
}
body.wl-b2 .wl__stage {
  opacity: 1;
  transform: translate(-50%, -50%);
  pointer-events: auto;
}

/* The row itself: phones on the left, cycling copy on the right, at the same
   proportions the marketing page uses so the headline gets the identical
   horizontal room. */
.wl__welcome {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(24px, 4vw, 76px);
}

/* --- the phones ---------------------------------------------------------
   Fanned as the story page fans them: middle taller and forward, outer two
   dropped back and pulled under it. Sized by HEIGHT so the three read as one
   object, and kept a touch smaller than the marketing set so the join box
   still has vertical room underneath. */
.wl__trio {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: clamp(-40px, -2vw, -18px);
}
.wl__phone {
  flex: 0 0 auto;
  transform: translateY(calc((1 - var(--in, 0)) * 3vh)) scale(var(--sc, 1));
  transform-origin: 50% 60%;
}
.wl__phone--left,
.wl__phone--right {
  height: clamp(280px, 44vh, 500px);
  aspect-ratio: 420 / 880;
}
.wl__phone--left {
  --sc: 0.94;
  z-index: 1;
  margin-right: -3%;
}
.wl__phone--right {
  --sc: 0.94;
  z-index: 1;
  margin-left: -3%;
}
.wl__phone--mid {
  height: clamp(340px, 56vh, 620px);
  aspect-ratio: 420 / 880;
  z-index: 2;
}
body.wl-b2 .wl__phone {
  --in: 1;
}

/* --- the device frame ---------------------------------------------------
   Copied from the story page's `.devphone` rather than shared, because this
   page loads none of that stylesheet. A real iPhone is 393x852pt; the frame
   is sized off the SCREEN and the bezel is percentage padding, so the iframe
   inside is always an exact 393x852 viewport scaled to fit. */
.devphone {
  position: relative;
  width: 100%;
  height: 100%;
  padding: 2.6%;
  border-radius: 13% / 6.2%;
  background: linear-gradient(150deg, #3a3d42 0%, #14161a 38%, #2b2e33 72%, #0e1013 100%);
  box-shadow: 0 2px 3px rgb(255 255 255 / 0.18) inset,
    0 -1px 2px rgb(255 255 255 / 0.08) inset,
    0 30px 70px rgb(0 0 0 / 0.55);
}
.devphone__win {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 11% / 5.4%;
  background: #000;
}
.devphone__screen {
  position: absolute;
  top: 0;
  left: 0;
  width: 393px;
  height: 852px;
  border: 0;
  transform: scale(var(--phone-scale, 0.5));
  transform-origin: 0 0;
  /* Decoration — a stray click must not land in the embedded app. */
  pointer-events: none;
}
.devphone__island {
  position: absolute;
  top: 4.2%;
  left: 50%;
  z-index: 2;
  width: 26%;
  height: 3.1%;
  transform: translateX(-50%);
  border-radius: 999px;
  background: #05060a;
}

/* The screens: three stills stacked in each window, one shown at a time and
   crossfaded on the cycle. `cover` fills the window with no letterbox, top-
   anchored so the status bar and header are never the part that gets cropped. */
.wl__screen {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  opacity: 0;
  transition: opacity 0.7s ease;
}
.wl__screen.is-on {
  opacity: 1;
}

/* The swipe screen is the live app itself, not a still — a 393x852 iframe scaled
   to fit the window, so the deck genuinely swipes at 60fps. `--fs` (the scale) is
   measured and set per phone in waitlist.js. Crossfades with the stills because it
   still carries .wl__screen. */
.wl__frame {
  width: 393px;
  height: 852px;
  border: 0;
  object-fit: unset;
  transform: scale(var(--fs, 0.5));
  transform-origin: top left;
  pointer-events: none;
  background: #000;
}

/* --- the cycling copy ---------------------------------------------------
   All three beats share one grid cell so the row height is the tallest beat's
   and never jumps as the copy changes. Sizes are lifted from the marketing
   `.welcome__*` so this reads identically. */
.wl__copy {
  position: relative;
  /* 0 0 — a fixed measure that never shrinks. As flex: 0 1 it collapsed under
     the phones on a wide screen, which is exactly what broke the wrapping. */
  flex: 0 0 auto;
  /* The marketing welcome column's own measure (680px), so the headlines get
     the same room and "Find your fans." / "Find your faves." each hold one line. */
  width: min(680px, 92vw);
  display: grid;
  text-align: left;
}
.wl__wbeat {
  grid-area: 1 / 1;
  /* Parked BELOW and invisible — this is where an incoming beat rises from and
     where an outgoing one is reset to (at opacity 0, so the reset is unseen). */
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.5s ease, transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}
.wl__wbeat.is-on {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
/* Leaving UPWARD — the vertical cycle. The script holds this for the length of
   the transition, then drops the class so the beat parks below for next time. */
.wl__wbeat.is-out {
  opacity: 0;
  transform: translateY(-28px);
}

.wl__eyebrow {
  margin: 0;
  font-family: var(--font-family-default);
  font-weight: var(--font-weight-medium);
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--tunas-aqua);
}
.wl__title {
  margin: 14px 0 0;
  max-width: 17ch;
  font-family: var(--font-family-display);
  font-weight: 700;
  font-size: clamp(32px, 4.2vw, 72px);
  line-height: 1.02;
  letter-spacing: -0.03em;
  text-wrap: balance;
  color: var(--tunas-cream);
}
.wl__lede {
  margin: 18px 0 0;
  font-family: var(--font-family-default);
  font-size: clamp(15px, 1.25vw, 20px);
  line-height: 1.55;
  color: rgb(238 231 224 / 0.74);
}

/* --- the join box, centred under the row -------------------------------- */
.wl__formwrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
}
.wl__sub {
  margin: 0;
  font-family: var(--font-family-default);
  font-weight: 600;
  font-size: clamp(15px, 1.4vw, 19px);
  line-height: 1.4;
  color: var(--tunas-cream);
}

/* --- the field ----------------------------------------------------------- */
/* Both states live in the same box and turn on the same axis, so one leaves
   as the other arrives instead of the layout jumping. The perspective has to
   sit on this element: it only reaches direct children, and the two states
   are exactly that. */
.wl__slot {
  position: relative;
  /* An explicit measure — the join box centres under the whole row, and its
     wrapper shrink-wraps to the short "Join the waitlist." line, so without a
     width of its own the field would collapse to that. */
  width: min(560px, 92vw);
  height: 62px;
  margin-top: 4px;
  perspective: 1000px;
}
.wl__form,
.wl__done {
  position: absolute;
  inset: 0;
  transition: opacity 0.45s cubic-bezier(0.23, 1, 0.32, 1),
    transform 0.5s cubic-bezier(0.23, 1, 0.32, 1);
}

.wl__form {
  display: flex;
  align-items: center;
  border-radius: 999px;
  background: rgb(255 252 250 / 0.1);
  box-shadow: inset 0 0 0 1px rgb(255 252 250 / 0.2);
  backdrop-filter: blur(10px) saturate(130%);
  -webkit-backdrop-filter: blur(10px) saturate(130%);
}
body.wl-sent .wl__form {
  opacity: 0;
  transform: rotateX(90deg);
  pointer-events: none;
}

.wl__input {
  flex: 1;
  min-width: 0;
  height: 100%;
  padding: 0 8px 0 24px;
  border: 0;
  border-radius: inherit;
  background: none;
  font-family: var(--font-family-default);
  font-size: 16px;
  color: var(--tunas-paper);
  outline: none;
}
.wl__input::placeholder {
  color: rgb(255 252 250 / 0.45);
}
.wl__input:focus-visible {
  outline: none;
}
/* The ring goes on the pill, not the input, or it would be a rectangle inside
   a rounded shape. */
.wl__form:focus-within {
  box-shadow: inset 0 0 0 1px rgb(255 252 250 / 0.34),
    0 0 0 3px rgb(15 143 134 / 0.4);
}
.wl__input:disabled {
  opacity: 0.7;
}

.wl__go {
  flex: none;
  height: 50px;
  margin-right: 6px;
  padding: 0 26px;
  border: 0;
  border-radius: 999px;
  background: var(--tunas-teal);
  font-family: var(--font-family-default);
  font-size: 15px;
  font-weight: var(--font-weight-bold);
  color: var(--tunas-paper);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: filter 0.2s ease, transform 0.12s ease;
}
.wl__go:hover {
  filter: brightness(1.1);
}
.wl__go:active {
  transform: scale(0.96);
}
.wl__go:disabled {
  cursor: progress;
}
.wl__go:disabled:hover {
  filter: none;
}
.wl__go:disabled:active {
  transform: none;
}
/* Label and spinner are stacked in one cell, so swapping them cannot change
   the button's width — a button that resizes mid-submit reads as a glitch. */
.wl__go-label,
.wl__spinner {
  grid-area: 1 / 1;
}
.wl__spinner {
  width: 20px;
  height: 20px;
  opacity: 0;
  animation: wl-spin 0.8s linear infinite;
}
body.wl-sending .wl__go-label {
  opacity: 0;
}
body.wl-sending .wl__spinner {
  opacity: 1;
}
@keyframes wl-spin {
  to {
    transform: rotate(360deg);
  }
}

/* --- the answer ---------------------------------------------------------- */
.wl__done {
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: var(--tunas-teal);
  opacity: 0;
  transform: rotateX(-90deg);
  pointer-events: none;
}
body.wl-sent .wl__done {
  opacity: 1;
  transform: rotateX(0);
  animation: wl-land 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275),
    wl-glow 2.4s ease-in-out 0.6s infinite;
}
.wl__done-inner {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-family-default);
  font-size: 17px;
  font-weight: var(--font-weight-bold);
  color: var(--tunas-paper);
}
body.wl-sent .wl__done-inner {
  animation: wl-pop 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275) backwards;
}
.wl__tick {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgb(255 255 255 / 0.22);
}
.wl__tick svg {
  width: 16px;
  height: 16px;
  color: var(--tunas-paper);
}
/* Drawn rather than shown — the stroke arriving is what sells it. */
body.wl-sent .wl__tick path {
  stroke-dasharray: 24;
  stroke-dashoffset: 24;
  animation: wl-draw 0.4s ease-out 0.28s forwards;
}

/* Three rings leaving at a stagger. */
.wl__ring {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  border: 2px solid rgb(15 143 134 / 0.8);
  opacity: 0;
  pointer-events: none;
}
body.wl-sent .wl__ring {
  animation: wl-ring 0.85s ease-out calc(var(--i) * 0.15s) forwards;
}

@keyframes wl-land {
  0% {
    transform: rotateX(-90deg) scale(0.6);
    opacity: 0;
  }
  55% {
    transform: rotateX(0) scale(1.06);
  }
  75% {
    transform: rotateX(0) scale(0.97);
  }
  100% {
    transform: rotateX(0) scale(1);
    opacity: 1;
  }
}
@keyframes wl-pop {
  from {
    transform: scale(0.82);
    opacity: 0;
  }
}
@keyframes wl-glow {
  0%,
  100% {
    box-shadow: 0 0 22px rgb(15 143 134 / 0.42);
  }
  50% {
    box-shadow: 0 0 62px rgb(15 143 134 / 0.8), 0 0 110px rgb(15 143 134 / 0.4);
  }
}
@keyframes wl-draw {
  to {
    stroke-dashoffset: 0;
  }
}
@keyframes wl-ring {
  from {
    transform: scale(0.86);
    opacity: 0.9;
  }
  to {
    transform: scale(1.9);
    opacity: 0;
  }
}

/* Over the whole card and catching nothing. Sized in script. */
.wl__confetti {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 620px;
  height: 620px;
  transform: translate(-50%, -50%);
  z-index: 5;
  pointer-events: none;
}

.wl__legal {
  margin: 6px 0 0;
  font-family: var(--font-family-default);
  font-size: 12.5px;
  line-height: 1.5;
  color: rgb(255 252 250 / 0.5);
}

/* Below ~900px the phones and the card can no longer sit side by side. Stack
   them: the fan on top, the pitch and the form beneath, both centred. */
@media (max-width: 960px) {
  /* The welcome row can no longer sit side by side; stack it — phones over the
     copy — and centre the copy so it sits under the fan rather than beside it. */
  .wl__welcome {
    flex-direction: column;
    gap: clamp(16px, 3vh, 30px);
  }
  .wl__copy {
    width: 100%;
    text-align: center;
    justify-items: center;
  }
  .wl__title,
  .wl__lede {
    max-width: none;
  }
  .wl__phone--left,
  .wl__phone--right {
    height: clamp(200px, 28vh, 320px);
  }
  .wl__phone--mid {
    height: clamp(240px, 36vh, 380px);
  }
}

/* On a phone the three-wide fan won't fit, but one big screen carries the pitch
   better than none. Keep just the front phone — cycling through its dashboard,
   swipe and profile states — and let the stage flow from the top so the phone
   sits up top and the join box lands lower down the screen. */
@media (max-width: 560px) {
  .wl__trio {
    display: flex;
  }
  .wl__phone--left,
  .wl__phone--right {
    display: none;
  }
  /* One big hero phone, full-size — it fills most of the first screen. */
  .wl__phone--mid {
    height: clamp(440px, 62vh, 600px);
    margin: 0;
  }
  /* The join beat SCROLLS on mobile: the phone fills the first screen, and the
     pitch + form sit below it with real breathing room, reached by scrolling
     within the stage. `overflow-y:auto` turns the stage into its own scroller;
     the beat is the last one, so page scroll has nowhere else to go and hands
     off cleanly. */
  .wl__stage {
    top: 0;
    height: 100dvh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    justify-content: flex-start;
    padding: 106px 0 48px;
    gap: clamp(26px, 6vh, 56px);
    transform: translate(-50%, 0);
  }
  body.wl-b2 .wl__stage {
    transform: translate(-50%, 0);
  }
  /* Copy at full size again now that there's scroll room. */
  .wl__eyebrow {
    font-size: 13px;
  }
  .wl__title {
    margin-top: 12px;
    font-size: clamp(30px, 8.5vw, 40px);
  }
  .wl__lede {
    margin-top: 12px;
    font-size: 15px;
    line-height: 1.5;
  }
  /* A clear gap before the form, as in the reference — its own moment below the
     pitch rather than crowding it. */
  .wl__formwrap {
    margin-top: clamp(28px, 8vh, 80px);
  }
}

@media (max-width: 620px) {
  .wl__slot {
    height: 58px;
  }
  .wl__go {
    height: 46px;
    padding: 0 18px;
    font-size: 14px;
  }
  .wl__input {
    padding-left: 18px;
    font-size: 16px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .wl__video,
  .wl__glass,
  .wl__tag,
  .wl__stage,
  .wl__wbeat,
  .wl__screen,
  .wl__form,
  .wl__done {
    transition: none;
  }
  .wl__spinner,
  body.wl-sent .wl__done,
  body.wl-sent .wl__done-inner,
  body.wl-sent .wl__ring,
  body.wl-sent .wl__tick path {
    animation: none;
  }
  body.wl-sent .wl__tick path {
    stroke-dashoffset: 0;
  }
}
