/* Teca — Vinyl Glass landing
   Dark-only. PastelBlue on VinylBlack with Lavender accents. Display type
   is Instrument Serif italic — liner-note energy, not tech-bro energy. */

:root {
  --black: #0E0E10;
  --black-2: #141419;
  --black-3: #1B1B22;
  --ink: #EAEAF0;
  --ink-2: #B7B7C2;
  --ink-3: #6E6E7A;
  --blue: #89B4FA;
  --blue-soft: #89B4FA1A;
  --lav: #C9B4FA;
  --lav-soft: #C9B4FA26;
  --mint: #7FD9A5;
  --red: #F38BA8;
  --glass-bg: rgba(255,255,255,0.035);
  --glass-border: rgba(137,180,250,0.18);
  --glass-border-strong: rgba(137,180,250,0.35);
  --radius-lg: 24px;
  --radius-md: 14px;

  /* Responsive shell. One gutter and one container width shared by the
     topbar, the hero and the footer so the wordmark, the headline and the
     copyright all sit on the same left edge at every size. `--page-max`
     is the *outer* width including gutters: past ~1320px the layout stops
     growing and centres instead, which is what keeps the two hero columns
     a constant ~565px from 1280px up rather than ballooning to 860px and
     stranding a 560px copy column beside a 480px form. */
  --gutter: clamp(20px, 4vw, 64px);
  --page-max: 1320px;
  /* Reading measure for the single-column (phone + tablet-portrait)
     layout. The copy block and the hero shot both snap to it so the
     stack reads as one centred column instead of two unrelated ones. */
  --measure: 560px;
}

* { box-sizing: border-box; }

/* Root layout — kept deliberately simple to keep mobile Safari scroll
   smooth. Earlier versions wrapped the body in a full-height flex column
   with `.hero { flex: 1 }` + a fixed+blend-mode grain; the combination
   forced a viewport-sized recomposite per scroll frame on iOS and the
   page felt "sticky". Now body is a plain block element with normal
   document flow, `min-height: 100dvh` on html (dvh is iOS-safe), and no
   blend-mode on the grain. */
html {
  min-height: 100dvh;
  background: var(--black);
  -webkit-text-size-adjust: 100%;
}
html, body {
  margin: 0;
  padding: 0;
  color: var(--ink);
  font-family: 'Geist', ui-sans-serif, system-ui, -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* `overflow-x: hidden` silently promotes the other axis to
     `overflow-y: auto`, which turns <body> into its own scroll container —
     so any decorative bleed past the content box became scrollable dead
     space. `clip` is a legal pair with `overflow-y: visible`, so the body
     stays a plain block. `hidden` first as the pre-Safari-16 fallback. */
  overflow-x: hidden;
  overflow-x: clip;
}

body {
  background:
    radial-gradient(1200px 600px at 85% -10%, rgba(137,180,250,0.14), transparent 55%),
    radial-gradient(900px 500px at -10% 110%, rgba(201,180,250,0.10), transparent 60%),
    radial-gradient(700px 400px at 50% 120%, rgba(137,180,250,0.06), transparent 60%),
    var(--black);
  position: relative;
  min-height: 100dvh;
}

/* Landing page only: topbar / hero / footer as three grid rows so the
   footer settles on the viewport bottom on tall screens instead of
   floating with dead space beneath it. The `1fr` row has an auto minimum,
   so on short viewports the hero keeps its natural height and the page
   simply scrolls — nothing is squashed. This is layout-only; it does not
   reintroduce the per-scroll-frame recomposite that the old fixed +
   blend-mode grain caused. */
body.landing {
  display: grid;
  grid-template-rows: auto 1fr auto;
}

/* Grain: the fixed+mix-blend-mode combo was janking scroll on mobile
   Safari (every scroll tick forced a full-frame recomposite of the
   blended layer). Now it's an absolutely-positioned sibling that
   repaints once and scrolls with the page. Bumped opacity slightly
   since we're no longer compositing through overlay. */
.grain {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 2;
  opacity: 0.07;
  background-image: url("data:image/svg+xml;utf8,<svg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.6 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 240px 240px;
}

/* Ambience layer — clips the glows to the page box. The glows are
   deliberately positioned to bleed off the edges (glow-2 sits at
   `bottom: -180px`), and an absolutely-positioned element still
   contributes to its scroll container's scrollable overflow. Without this
   wrapper that produced exactly 180px of empty scrollable gap under the
   footer. Clipping costs nothing visually: the bleed was never on screen. */
.ambience {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
  overflow: clip;
}

.glow {
  position: absolute;
  border-radius: 50%;
  filter: blur(60px);
  pointer-events: none;
  z-index: 0;
  will-change: transform;
}
.glow-1 { top: -150px; right: -100px; width: 520px; height: 520px; background: rgba(137,180,250,0.22); }
.glow-2 { bottom: -180px; left: 10%;   width: 440px; height: 440px; background: rgba(201,180,250,0.14); }

/* Shared page shell — topbar, hero and footer are the three grid rows of
   `body.landing`, so they each need their own centring. Doing it here (one
   rule) instead of per-section keeps their left edges locked together. */
.topbar, .hero, .foot {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Top bar ────────────────────────────────────────── */
.topbar {
  position: relative;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding-block: clamp(14px, 2.2vw, 22px);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: var(--ink);
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1;
}
.wordmark .sigil {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Pushes the sigil optical centre to line up with the x-height midline
     of the Instrument Serif italic wordmark — serif italics sit visually
     higher than their geometric centre, so a 1px nudge keeps the pair
     looking balanced instead of sigil-on-baseline. */
  transform: translateY(-1px);
  filter: drop-shadow(0 0 6px rgba(137,180,250,0.35));
}
.wordmark .sigil svg { display: block; }
.wordmark .word {
  font-family: 'Instrument Serif', Georgia, serif;
  font-style: italic;
  font-size: 28px;
  line-height: 1;
  letter-spacing: -0.01em;
}

.status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding: 6px 12px;
  border: 1px solid var(--glass-border);
  border-radius: 999px;
  background: var(--glass-bg);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
.status .dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--mint);
  box-shadow: 0 0 8px rgba(127,217,165,0.8), 0 0 16px rgba(127,217,165,0.4);
  animation: pulse 2.4s ease-in-out infinite;
}
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.45} }

/* Hero ───────────────────────────────────────────── */
.hero {
  position: relative;
  z-index: 5;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(24px, 3.5vw, 56px);
  padding-block: clamp(8px, 2vw, 28px) clamp(24px, 3vw, 40px);
  align-items: center;
}

/* One centred column whenever side-by-side would be wrong. Two triggers:
   (1) under 1000px there isn't room for a copy column *and* a photo column
       without squeezing the form below ~420px;
   (2) any portrait viewport — a 1024x1366 iPad Pro clears the width test
       but a two-column hero there is only ~750px tall in a 1366px page, so
       it floats in the middle of a mostly-empty screen. Portrait wants the
       stack; landscape (incl. 1024x768 iPad) wants the two columns.
   The old breakpoint was a bare 900px and left everything left-aligned at
   its desktop max-width, which is what made iPad portrait look like a phone
   layout stranded in the corner of a 768px page. */
@media (max-width: 999px), (orientation: portrait) {
  /* Fluid measure for the stacked layout: full width on phones (the 560px
     floor is wider than a phone column, so `min(100%, …)` hands it back),
     then growing to 640px by the time the viewport is wide enough that a
     fixed 560px column would read as marooned. */
  :root { --measure: min(100%, clamp(560px, 68vw, 640px)); }
  /* Pull the topbar and footer in to the same measure so the wordmark and
     the copyright line up with the centred column instead of hugging the
     screen edges while everything between them is centred. */
  .topbar, .foot { max-width: calc(var(--measure) + 2 * var(--gutter)); }
  .hero {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    gap: clamp(20px, 3vw, 36px);
  }
  .copy { margin-inline: auto; }
  .showcase { width: 100%; }
  /* The form and the success card fill the measure on one column so the
     text block and the card share an edge instead of the card floating
     inside a wider column. Scoped to `.hero` for specificity — the base
     `max-width: 480px` is declared further down the sheet. */
  .hero .form, .hero .success { max-width: 100%; }
}

.copy {
  width: 100%;
  max-width: var(--measure);
}

.eyebrow {
  margin: 0 0 18px;
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--lav);
}

/* Display scale: `clamp(40px, 5.6vw, 72px)` sat at its 40px floor for every
   viewport under 714px, so a 320px phone got the same headline as a 700px
   one — 40px of Instrument Serif in a 280px column, one bad word away from
   a four-line headline. A shallower slope with a +21px intercept keeps 320
   readable (34px), lands ~52px on iPad portrait (not "giant tablet type")
   and still reaches the 72px desktop cap by 1280px. */
.headline {
  font-family: 'Instrument Serif', Georgia, serif;
  font-weight: 400;
  font-size: clamp(34px, calc(4vw + 21px), 72px);
  line-height: 1.02;
  letter-spacing: -0.02em;
  margin: 0 0 22px;
  color: var(--ink);
}
.headline em {
  font-style: italic;
  background: linear-gradient(100deg, #89B4FA 0%, #C9B4FA 60%, #89B4FA 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.sub {
  margin: 0 0 22px;
  font-size: clamp(15px, 1.2vw, 17px);
  line-height: 1.5;
  color: var(--ink-2);
  max-width: 52ch;
}

/* Form ───────────────────────────────────────────── */
.form[hidden] { display: none; }
.form {
  display: grid;
  gap: 12px;
  width: 100%;
  max-width: 480px;
  /* 20px of card padding on a 320px screen leaves the inputs 240px wide;
     the fluid value gives them back 12px without changing the desktop look. */
  padding: clamp(14px, 4vw, 20px);
  border-radius: var(--radius-lg);
  background:
    linear-gradient(180deg, rgba(255,255,255,0.045), rgba(255,255,255,0.015)) padding-box,
    linear-gradient(180deg, rgba(137,180,250,0.4), rgba(137,180,250,0.06)) border-box;
  border: 1px solid transparent;
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  box-shadow: 0 10px 40px -10px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.04);
}

/* Floating-label inputs — the label sits at rest where a placeholder
   would, then floats up into the top-10px gutter on focus or when the
   field has content. WCAG: field-sizing is 16px at rest so iOS doesn't
   auto-zoom, and the floated label is 11px but against a high-contrast
   #B7B7C2-on-#000 background (ink-2 on inset-dark). */
.field.float {
  position: relative;
}
.field.float input {
  width: 100%;
  background: rgba(0,0,0,0.55);
  border: 1px solid rgba(255,255,255,0.18);
  color: var(--ink);
  padding: 22px 16px 10px;
  font: 500 16px/1.2 'Geist', sans-serif;
  border-radius: var(--radius-md);
  outline: none;
  transition: border-color 140ms ease, background 140ms ease, box-shadow 140ms ease;
}
.field.float input::placeholder { color: transparent; }
.field.float label {
  position: absolute;
  left: 16px;
  top: 50%;
  transform: translateY(-50%);
  font: 500 15px/1 'Geist', sans-serif;
  color: var(--ink-2);
  pointer-events: none;
  transition: transform 140ms ease, color 140ms ease, font-size 140ms ease, top 140ms ease;
  background: transparent;
  padding: 0 4px;
}
.field.float input:focus,
.field.float input:not(:placeholder-shown) {
  border-color: var(--blue);
  background: rgba(137,180,250,0.08);
  box-shadow: 0 0 0 3px rgba(137,180,250,0.18);
}
.field.float input:focus + label,
.field.float input:not(:placeholder-shown) + label {
  top: 10px;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--blue);
  transform: translateY(0);
}
.field-optional {
  font-style: italic;
  font-weight: 400;
  color: var(--ink-3);
  letter-spacing: 0;
  text-transform: none;
}

/* Telephone variant: inline "+1" prefix that appears only when the
   field is active (focused or filled). At rest the field matches the
   email field 1:1 — same background, same border, same floating
   label position — so the two inputs read as a pair instead of
   different components. */
.field.float.tel input { padding-left: 50px; }
.field.float.tel .tel-prefix {
  position: absolute;
  left: 16px;
  bottom: 10px;
  font: 500 16px/1.2 'Geist', sans-serif;
  color: var(--ink-2);
  pointer-events: none;
  opacity: 0;
  transition: opacity 140ms ease;
}
.field.float.tel input:focus ~ .tel-prefix,
.field.float.tel input:not(:placeholder-shown) ~ .tel-prefix {
  opacity: 1;
}
/* International mode — the value carries its own "+cc", so the fixed "+1"
   prefix (and the space reserved for it) would be wrong. */
.field.float.tel.intl input { padding-left: 16px; }
.field.float.tel.intl input:focus ~ .tel-prefix,
.field.float.tel.intl input:not(:placeholder-shown) ~ .tel-prefix {
  opacity: 0;
}
/* Keep the legacy .field-label selector harmless for pages that still
   use the stacked style (privacy/support/about don't, but be safe). */
.field-label {
  display: block;
  font-size: 12px;
  color: var(--ink-3);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin: 2px 0 8px;
}

.challenge {
  display: grid;
  grid-template-columns: 1fr 88px;
  gap: 12px;
  align-items: center;
  padding: 12px 4px 0;
}
.challenge-q {
  /* Bumped from 12px/ink-3 → 16px/ink so the captcha question is
     actually readable. Previous styling was near-invisible at the 11%
     contrast ratio. This hits WCAG AA at ~9:1 on VinylBlack. */
  font-size: 16px;
  font-weight: 500;
  color: var(--ink);
  letter-spacing: 0;
  line-height: 1.25;
  margin: 0;
}
.challenge input {
  background: rgba(0,0,0,0.55);
  border: 1px solid rgba(255,255,255,0.18);
  color: var(--ink);
  padding: 12px 12px;
  font: 600 18px/1.2 'Geist', sans-serif;
  font-variant-numeric: tabular-nums;
  text-align: center;
  border-radius: 10px;
  outline: none;
  transition: border-color 140ms ease, background 140ms ease, box-shadow 140ms ease;
}
.challenge input:focus {
  border-color: var(--blue);
  background: rgba(137,180,250,0.08);
  box-shadow: 0 0 0 3px rgba(137,180,250,0.18);
}

.cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 2px;
  padding: 15px 20px;
  width: 100%;
  font: 500 15px/1 'Geist', sans-serif;
  letter-spacing: 0.01em;
  color: var(--black);
  background: radial-gradient(120% 120% at 30% 20%, #FFFFFF 0%, #EAEAF0 35%, #B7B7C2 100%);
  border: 1px solid rgba(255,255,255,0.5);
  border-radius: var(--radius-md);
  cursor: pointer;
  box-shadow:
    0 10px 30px -8px rgba(137,180,250,0.55),
    0 4px 10px -2px rgba(0,0,0,0.5),
    inset 0 1px 0 rgba(255,255,255,0.9);
  transition: transform 120ms ease, box-shadow 120ms ease;
}
.cta:hover { transform: translateY(-1px); box-shadow: 0 14px 36px -6px rgba(137,180,250,0.65), 0 6px 14px -2px rgba(0,0,0,0.55); }
.cta:active { transform: translateY(0); }
.cta:disabled { opacity: 0.55; cursor: progress; }

.trust {
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--ink-3);
  line-height: 1.4;
}

/* Success state — replaces the form once the waitlist POST succeeds.
   The Pookie Lottie plays in the top slot; an italic-serif "You're in."
   headline sits below. `[hidden]` wins over `display: grid` so the HTML
   attribute controls visibility. */
.success[hidden] { display: none; }
.success {
  display: grid;
  justify-items: center;
  gap: 12px;
  padding: 28px clamp(14px, 4vw, 20px) 24px;
  width: 100%;
  max-width: 480px;
  border-radius: var(--radius-lg);
  background:
    linear-gradient(180deg, rgba(127,217,165,0.08), rgba(137,180,250,0.03)) padding-box,
    linear-gradient(180deg, rgba(127,217,165,0.5), rgba(137,180,250,0.15)) border-box;
  border: 1px solid transparent;
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  box-shadow: 0 10px 40px -10px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.04);
  animation: success-in 520ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.success-lottie {
  width: min(200px, 60vw);
  aspect-ratio: 1;
  height: auto;
  margin-top: -8px;
}
.success-title {
  margin: 0;
  font-family: 'Instrument Serif', Georgia, serif;
  font-weight: 400;
  font-size: 40px;
  line-height: 1;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.success-title em {
  font-style: italic;
  background: linear-gradient(100deg, #7FD9A5 0%, #89B4FA 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.success-sub {
  margin: 0;
  font-size: 14px;
  color: var(--ink-2);
  text-align: center;
}
@keyframes success-in {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.ghost-demo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 12px;
  padding: 15px 20px;
  width: 100%;
  max-width: 480px;
  min-height: 52px;
  font: 500 15px/1 'Geist', sans-serif;
  letter-spacing: 0.01em;
  color: var(--blue);
  background: rgba(137,180,250,0.06);
  border: 1px solid var(--glass-border-strong);
  border-radius: var(--radius-md);
  cursor: pointer;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  transition: transform 120ms ease, background 140ms ease, border-color 140ms ease;
}
.ghost-demo:hover {
  transform: translateY(-1px);
  background: rgba(137,180,250,0.12);
  border-color: rgba(137,180,250,0.55);
}
.ghost-demo:active { transform: translateY(0); }

@media (max-width: 999px), (orientation: portrait) {
  .hero .ghost-demo { max-width: 100%; }
}

.form-msg {
  margin: 0;
  font-size: 13px;
  color: var(--ink-2);
  min-height: 0;
}
.form-msg.success { color: var(--mint); }
.form-msg.error { color: var(--red); }

.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; opacity: 0; }

/* Showcase — inline launch film ──────────────────────────────── */
.showcase {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  justify-self: stretch;
  min-height: 0;
  width: 100%;
}

.player {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin: 0;
  width: 100%;
  /* The film is 9:16. Capping on height as well as width keeps a portrait
     video from running past the fold on a short laptop viewport, where an
     unconstrained 9:16 box is nearly twice the window height. */
  max-width: min(420px, calc(80dvh * 9 / 16));
}

.player-video {
  display: block;
  width: 100%;
  height: auto;
  /* Intrinsic ratio so the column reserves the right height before the
     poster decodes — no layout shift while the caption is already laid out. */
  aspect-ratio: 9 / 16;
  max-height: 80dvh;
  object-fit: contain;
  background: #000;
  border-radius: clamp(14px, 2vw, 22px);
  border: 1px solid var(--glass-border);
  box-shadow:
    0 30px 60px -20px rgba(0,0,0,0.65),
    0 0 80px -20px rgba(137,180,250,0.22);
  z-index: 2;
}

/* Play overlay ───────────────────────────────────────────────
   A transparent button covering the poster, so the whole still is the tap
   target rather than just the orb. It is removed from the flow the moment
   playback starts (`.is-playing`) — leaving it would swallow taps meant for
   the native controls. */
.play-overlay {
  position: absolute;
  inset: 0 0 auto;
  /* Match the video box, not the figure: the caption sits below and must
     stay clickable/selectable. */
  height: auto;
  aspect-ratio: 9 / 16;
  max-height: 80dvh;
  z-index: 3;
  /* The orb and its label are placed rather than centred: the poster's
     subject (Pookie on the turntable) sits just below the middle of the
     frame, and a geometrically centred orb lands on the cat's face. 42%
     drops it into the empty groove above, which reads as centred without
     covering the thing the still is selling. */
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  border-radius: clamp(14px, 2vw, 22px);
  background: linear-gradient(
    180deg,
    rgba(14,14,16,0) 40%,
    rgba(14,14,16,0.34) 100%
  );
  color: var(--blue);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: opacity 220ms ease;
}

.play-orb {
  position: absolute;
  top: 42%;
  left: 50%;
  display: grid;
  place-items: center;
  /* 76px clears the 44px iOS minimum with room to spare — this is the one
     control the page is asking a visitor to touch. */
  width: 76px;
  height: 76px;
  border-radius: 999px;
  color: var(--blue);
  background: rgba(137,180,250,0.14);
  border: 1px solid rgba(137,180,250,0.55);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow:
    0 18px 40px -18px rgba(0,0,0,0.8),
    0 0 34px -6px rgba(137,180,250,0.45);
  /* The triangle reads off-centre inside a circle unless nudged; optical
     centring is 8% of the width to the right of geometric centre. */
  padding-left: 6px;
  /* The centring translate has to live inside the keyframes: an animation on
     `transform` replaces the declared one outright. */
  transform: translate(-50%, -50%);
  animation: play-breathe 2600ms ease-in-out infinite;
}

/* The idle tell: a ring that expands and fades out of the orb, like a
   record's lead-in groove. Purely decorative, so it lives on its own layer
   and never affects the orb's own hit box. */
.play-ring {
  position: absolute;
  inset: -1px;
  border-radius: 999px;
  border: 1px solid rgba(137,180,250,0.6);
  animation: play-pulse 2600ms ease-out infinite;
  pointer-events: none;
}

@keyframes play-breathe {
  0%, 100% { transform: translate(-50%, -50%) scale(1); box-shadow: 0 18px 40px -18px rgba(0,0,0,0.8), 0 0 34px -6px rgba(137,180,250,0.45); }
  50%      { transform: translate(-50%, -50%) scale(1.045); box-shadow: 0 18px 40px -18px rgba(0,0,0,0.8), 0 0 46px -4px rgba(137,180,250,0.7); }
}

@keyframes play-pulse {
  0%   { transform: scale(1); opacity: 0.75; }
  70%  { transform: scale(1.7); opacity: 0; }
  100% { transform: scale(1.7); opacity: 0; }
}

/* Runtime as a corner chip rather than a caption under the orb. Every
   position near the centre of this poster collides with either the counter
   above or the cat below; the corner is the one place it can sit at any card
   size without fighting the still. */
.play-label {
  position: absolute;
  top: 12px;
  right: 12px;
  white-space: nowrap;
  font: 500 12px/1 'Geist', sans-serif;
  letter-spacing: 0.02em;
  color: var(--ink);
  padding: 8px 12px;
  border-radius: 999px;
  background: rgba(14,14,16,0.62);
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.play-overlay:hover .play-orb,
.play-overlay:focus-visible .play-orb {
  background: rgba(137,180,250,0.22);
  border-color: rgba(137,180,250,0.85);
}
.play-overlay:active .play-orb { transform: scale(0.96); }
.play-overlay:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
}

/* Playing: the overlay is gone and the idle animation stops with it, so
   nothing keeps compositing behind the film. */
.player.is-playing .play-overlay {
  opacity: 0;
  pointer-events: none;
}

@media (max-width: 999px), (orientation: portrait) {
  /* Stacked layout: the film shares the copy column's measure but stays
     capped so a tall phone doesn't hand it the whole screen. */
  .player { max-width: min(var(--measure), 420px, calc(72dvh * 9 / 16)); }
  .player-video, .play-overlay { max-height: 72dvh; }
}

.caption {
  margin: 0;
  max-width: 34ch;
  text-align: center;
  font-family: 'Instrument Serif', Georgia, serif;
  font-style: italic;
  font-size: clamp(18px, 1.5vw, 22px);
  line-height: 1.25;
  color: var(--ink);
  letter-spacing: -0.01em;
  min-height: 1.25em;
  transition: opacity 240ms ease;
}
.caption.is-fading { opacity: 0; }

@media (max-width: 999px), (orientation: portrait) {
  .showcase { gap: 10px; }
}

/* Footer ─────────────────────────────────────────── */
.foot {
  position: relative;
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 6px 16px;
  padding-block: 18px 22px;
  font-size: 12px;
  color: var(--ink-3);
}
.foot-right { font-style: italic; font-family: 'Instrument Serif', Georgia, serif; font-size: 14px; color: var(--ink-2); }
@media (max-width: 720px) {
  .foot { flex-direction: column; align-items: flex-start; padding-block: 14px 20px; }
}

@media (prefers-reduced-motion: reduce) {
  .status .dot { animation: none; }
  .cta { transition: none; }
  .ghost-demo { transition: none; }
  .play-orb { animation: none; }
  .play-ring { animation: none; opacity: 0.5; }
}

/* Legal pages (/about, /privacy, /support) ───────── */
/* Same three-row grid as the landing page. As `display: block` these pages
   inherited `body { min-height: 100dvh }` with nothing to absorb the slack,
   so on any viewport taller than the prose the footer stopped where the
   text ended and left the remainder as empty page below it — 209px of it on
   1920x1080 /about. The `1fr` middle row eats the slack instead. */
body.legal {
  display: grid;
  grid-template-rows: auto 1fr auto;
}
.legal-wrap {
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: clamp(20px, 3vw, 32px) clamp(20px, 5vw, 28px) clamp(40px, 6vw, 64px);
  position: relative;
  z-index: 5;
}
.legal-title {
  font-family: 'Instrument Serif', Georgia, serif;
  font-weight: 400;
  font-size: clamp(36px, 5vw, 56px);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 8px 0 6px;
  color: var(--ink);
}
.legal-meta {
  color: var(--ink-3);
  font-size: 13px;
  margin: 0 0 28px;
}
.legal-section {
  margin: 0 0 32px;
  color: var(--ink-2);
  line-height: 1.62;
  font-size: 15px;
}
.legal-section h2 {
  color: var(--ink);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.005em;
  margin: 0 0 10px;
}
.legal-section p { margin: 0 0 12px; }
.legal-section ul {
  padding-left: 18px;
  margin: 0 0 12px;
}
.legal-section li { margin: 0 0 8px; }
.legal-section a { color: var(--blue); text-decoration: none; }
.legal-section a:hover { text-decoration: underline; }
.legal-section strong { color: var(--ink); font-weight: 600; }
.big-link {
  font-size: 18px;
  font-weight: 500;
  color: var(--blue) !important;
}
/* Admin ───────────────────────────────────────────── */
.admin-wrap { max-width: 960px; padding-top: 40px; }
.admin-login { max-width: 360px; margin: 0 0 24px; }
.admin-view[hidden] { display: none; }
.admin-view { display: block; margin-top: 8px; }
.admin-view-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin: 0 0 14px;
}
.admin-count {
  font-family: 'Instrument Serif', Georgia, serif;
  font-size: 24px; font-weight: 400; color: var(--ink); margin-right: 6px;
}
.admin-actions { display: inline-flex; gap: 8px; }
.admin-btn {
  appearance: none;
  padding: 8px 12px;
  font: 500 12px/1 'Geist', sans-serif;
  letter-spacing: 0.04em;
  border-radius: 8px;
  background: var(--glass-bg);
  color: var(--ink);
  border: 1px solid var(--glass-border);
  cursor: pointer;
  transition: border-color 140ms, background 140ms;
}
.admin-btn:hover { border-color: var(--blue); background: rgba(137,180,250,0.08); }
.admin-btn-danger { color: var(--red); }
.admin-btn-danger:hover { border-color: var(--red); background: rgba(243,139,168,0.08); }
.admin-table-wrap { overflow-x: auto; border: 1px solid var(--glass-border); border-radius: 14px; }
.admin-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.admin-table th, .admin-table td { padding: 10px 14px; text-align: left; }
.admin-table th { font-weight: 600; color: var(--ink-2); letter-spacing: 0.04em; text-transform: uppercase; font-size: 11px; background: rgba(255,255,255,0.02); }
.admin-table tbody tr { border-top: 1px solid rgba(255,255,255,0.05); }
.admin-table td.mono { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12px; color: var(--ink); }
.admin-table td.muted { color: var(--ink-3); }

.about-wrap { max-width: 640px; padding-top: clamp(28px, 5vw, 56px); }
.about-lede p {
  font-family: 'Instrument Serif', Georgia, serif;
  font-size: clamp(19px, 2.2vw, 24px);
  line-height: 1.5;
  color: var(--ink);
  letter-spacing: -0.005em;
}
.about-lede em {
  font-style: italic;
  background: linear-gradient(100deg, #89B4FA 0%, #C9B4FA 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.about-lede p.about-sign {
  font-family: 'Geist', system-ui, -apple-system, sans-serif;
  font-style: normal;
  font-size: clamp(14px, 1.4vw, 15px);
  color: var(--ink-3);
  letter-spacing: 0.01em;
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid rgba(137, 180, 250, 0.14);
}
/* Closing CTA: the page used to dead-end, so this is the one way back to
   the waitlist. Sans-serif and pill-shaped so it reads as an action next
   to the serif prose rather than another sentence. */
.about-lede p.about-cta {
  font-family: 'Geist', system-ui, -apple-system, sans-serif;
  font-style: normal;
  font-size: 15px;
  margin-top: 22px;
}
.about-lede p.about-cta a {
  display: inline-block;
  padding: 12px 22px;
  border-radius: 999px;
  background: rgba(137, 180, 250, 0.10);
  border: 1px solid rgba(137, 180, 250, 0.30);
  color: var(--blue);
  font-weight: 500;
  letter-spacing: 0.01em;
  text-decoration: none;
  transition: background 140ms ease, border-color 140ms ease;
}
.about-lede p.about-cta a:hover {
  background: rgba(137, 180, 250, 0.18);
  border-color: rgba(137, 180, 250, 0.55);
  text-decoration: none;
}
/* The only way back on the legal pages, and at 13px it was a 17px-tall hit
   area. inline-flex + min-height gives it a 44px touch target without
   changing where the text sits. */
.back-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: 4px;
  color: var(--ink-3);
  text-decoration: none;
  font-size: 13px;
  letter-spacing: 0.02em;
  transition: color 140ms ease;
}
.back-link:hover { color: var(--ink); }

/* Footer links shared by all pages */
.foot a { color: inherit; text-decoration: none; border-bottom: 1px dotted rgba(183,183,194,0.35); }
.foot a:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* About-page FAQ. Collapsed by default so the page still reads as a short
   manifesto rather than a support doc — the questions are there for someone
   who came looking for them, not in the way of someone who didn't. */
.about-faq-title {
  margin-top: 2.5rem;
}

.about-faq {
  border-top: 1px solid rgba(255, 255, 255, 0.09);
  padding: 0.9rem 0;
}

.about-faq summary {
  cursor: pointer;
  font-weight: 600;
  list-style: none;
  padding-right: 1.5rem;
  position: relative;
}

.about-faq summary::-webkit-details-marker {
  display: none;
}

.about-faq summary::after {
  content: "+";
  position: absolute;
  right: 0;
  top: 0;
  opacity: 0.5;
  font-weight: 400;
}

.about-faq[open] summary::after {
  content: "\2212"; /* minus */
}

.about-faq summary:focus-visible {
  outline: 2px solid #89b4fa;
  outline-offset: 3px;
  border-radius: 3px;
}

.about-faq p {
  margin-top: 0.7rem;
}
