/* 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;
  /* 5.65:1 on --black, WCAG AA for the 12-13px meta text that uses it (was #6E6E7A, 3.83:1). */
  --ink-3: #8A8A96;
  --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);
}

/* The /3d header lockup (the app's HomeTopBar: ic_teca_logo + ic_teca_wordmark)
   at the same sizes and breakpoints as /3d, so every Teca page shows one logo. */
.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
}
.wordmark .brand-mark { display: block; width: 34px; height: 34px; flex-shrink: 0; }
.wordmark .brand-wordmark { display: block; height: 22px; width: 76px; }
@media (max-width: 839px) {
  .wordmark .brand-mark { width: 31px; height: 31px; }
  .wordmark .brand-wordmark { height: 20px; width: 69px; }
}
@media (min-width: 760px) and (max-width: 839px) {
  .wordmark .brand-mark { width: 32px; height: 32px; }
  .wordmark .brand-wordmark { height: 21px; width: 72px; }
}

.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;
}

/* The still ──────────────────────────────────────────────────
   Sits over the video and dissolves out once the film is actually rendering
   frames (see app.js), so tapping play reads as the shop photo becoming the
   film rather than a cut to black while the first bytes arrive.
   `cover` rather than `contain`: the still is cut to 9:16 to match the film,
   so cover only absorbs sub-pixel rounding instead of cropping. */
.player-still {
  position: absolute;
  inset: 0 0 auto;
  width: 100%;
  height: auto;
  aspect-ratio: 9 / 16;
  max-height: 80dvh;
  object-fit: cover;
  border-radius: clamp(14px, 2vw, 22px);
  border: 1px solid var(--glass-border);
  z-index: 3;
  pointer-events: none;
  transition: opacity 520ms ease, visibility 0s linear 520ms;
}

/* Dropped out of compositing once the dissolve finishes — an opacity-0 layer
   the size of the film is still work for every frame after it. */
.player.is-revealed .player-still { opacity: 0; visibility: hidden; }

/* 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: 4;
  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;
}

/* Centred on the still, which puts it on the phone's dark screen between the
   Recently Added row and the wantlist — the one region where a blue orb reads
   at full contrast without covering a sleeve or the hand. */
.play-orb {
  position: absolute;
  top: 50%;
  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: under the orb
   it would land on the app's own UI, and the corner is the one place it sits
   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, .player-still, .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;
}
/* /privacy: the four promises above the full policy. */
.privacy-promises {
  list-style: none;
  margin: 20px 0 16px;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}
@media (min-width: 560px) {
  .privacy-promises { grid-template-columns: 1fr 1fr; }
}
.privacy-promise {
  padding: 18px;
  border-radius: 20px;
  border: 1px solid var(--glass-border);
  background: var(--glass-bg);
}
.privacy-promise svg {
  display: block;
  width: 26px;
  height: 26px;
  margin: 0 0 12px;
  color: var(--blue);
}
.privacy-promise p {
  margin: 0;
  color: var(--ink-2);
  font-size: 15px;
  line-height: 1.5;
}
.privacy-promise strong { color: var(--ink); font-weight: 600; }
.privacy-honest {
  margin: 0 0 40px;
  color: var(--ink-3);
  font-size: 14px;
  line-height: 1.55;
}
.privacy-policy-title {
  margin: 0 0 4px;
  color: var(--ink);
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.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;
}

/* ---------- /video ----------
   The launch film on a page of its own: its own link preview, its own
   demo_opened funnel. Layout only — the player itself reuses the landing
   page's .player rules above, so a change there stays in one place. */
.film-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.film-wrap .legal-title { margin-top: 0; }
.film-lede {
  max-width: 46ch;
  margin: 0 0 26px;
  color: var(--ink-2);
  font-size: 16px;
  line-height: 1.6;
}
.film-wrap .player { margin-bottom: 24px; }
.film-cta {
  max-width: 340px;
  margin-top: 0;
  text-decoration: none;
}
.film-note {
  margin: 12px 0 0;
  font-size: 13px;
  color: var(--ink-3);
}

/* ---------- Topbar links (legal pages) ----------
   "Support" sits beside "← Back" so help is one tap from every legal page.
   Both keep .back-link's 44px touch target. */
.topnav {
  display: flex;
  align-items: center;
  gap: 4px 12px;
}
.topnav a[aria-current="page"] { color: var(--ink); }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- /support and its guides ---------- */
/* The filter hides items with [hidden]; flex and grid rules below would
   otherwise win over the UA's display: none. */
.support-page [hidden] { display: none !important; }

.support-lede {
  margin: 0 0 24px;
  color: var(--ink-2);
  font-size: 15px;
  line-height: 1.6;
}

.support-search {
  position: relative;
  margin: 0 0 8px;
}
.support-search-icon {
  position: absolute;
  left: 16px;
  top: 50%;
  width: 20px;
  height: 20px;
  transform: translateY(-50%);
  color: var(--ink-3);
  pointer-events: none;
}
.support-search input {
  width: 100%;
  min-height: 52px;
  padding: 0 16px 0 46px;
  border-radius: var(--radius-md);
  border: 1px solid var(--glass-border);
  background: var(--glass-bg);
  color: var(--ink);
  /* 16px keeps iOS Safari from zooming the page on focus. */
  font: 400 16px/1.2 'Geist', system-ui, -apple-system, sans-serif;
  /* Dark native chrome (the clear button, autofill) on a dark-only site. */
  color-scheme: dark;
  -webkit-appearance: none;
  appearance: none;
  transition: border-color 140ms ease, background 140ms ease;
}
.support-search input::placeholder { color: var(--ink-3); }
.support-search input:focus {
  outline: none;
  border-color: var(--blue);
  background: rgba(137,180,250,0.06);
  box-shadow: 0 0 0 3px rgba(137,180,250,0.18);
}
/* With JS off the search box stays hidden, and so does its empty status line. */
.support-search[hidden] + .support-status { display: none; }
.support-status {
  min-height: 20px;
  margin: 0 0 16px;
  font-size: 13px;
  color: var(--ink-2);
}

.support-guides { margin: 0 0 36px; }
.support-heading {
  margin: 0 0 12px;
  color: var(--ink);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.005em;
}
.support-cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
}
@media (min-width: 640px) {
  .support-cards { grid-template-columns: 1fr 1fr; }
}
.support-card {
  display: flex;
  align-items: center;
  gap: 16px;
  height: 100%;
  min-height: 52px;
  padding: 16px;
  border-radius: 20px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  color: inherit;
  text-decoration: none;
  transition: border-color 140ms ease, background 140ms ease;
}
.support-card:hover {
  border-color: var(--glass-border-strong);
  background: rgba(137,180,250,0.06);
}
.support-card:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
}
.support-card-art {
  flex-shrink: 0;
  width: 56px;
  height: 56px;
}
.support-card-text {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.support-card-title {
  color: var(--ink);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.3;
}
.support-card-sub {
  color: var(--ink-2);
  font-size: 14px;
  line-height: 1.4;
}
.support-card-meta,
.support-read-time {
  color: var(--lav);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.support-empty {
  margin: 0 0 32px;
  padding: 16px;
  border-radius: var(--radius-md);
  border: 1px solid var(--glass-border);
  background: var(--glass-bg);
  color: var(--ink-2);
  font-size: 15px;
  line-height: 1.6;
}
.support-empty p { margin: 0; }
.support-empty a { color: var(--blue); text-decoration: none; }

/* FAQ: one glass list per group, a row per question. */
.support-faq { margin: 0 0 32px; }
.support-q {
  border: 1px solid var(--glass-border);
  border-top-width: 0;
  background: var(--glass-bg);
}
.support-faq .support-heading + .support-q {
  border-top-width: 1px;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}
.support-q:last-child { border-radius: 0 0 var(--radius-md) var(--radius-md); }
.support-faq .support-heading + .support-q:last-child { border-radius: var(--radius-md); }
.support-q summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 52px;
  padding: 12px 16px;
  color: var(--ink);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.35;
  cursor: pointer;
  list-style: none;
}
.support-q summary::-webkit-details-marker { display: none; }
.support-q summary::after {
  content: "";
  flex-shrink: 0;
  width: 8px;
  height: 8px;
  margin-right: 4px;
  border-right: 1.8px solid var(--ink-3);
  border-bottom: 1.8px solid var(--ink-3);
  transform: rotate(45deg) translateY(-2px);
  transition: transform 160ms ease;
}
.support-q[open] summary::after { transform: rotate(-135deg) translateY(-2px); }
.support-q summary:hover { color: var(--blue); }
.support-q summary:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: -2px;
  border-radius: var(--radius-md);
}
.support-q p {
  margin: 0;
  padding: 0 16px 14px;
  color: var(--ink-2);
  font-size: 15px;
  line-height: 1.6;
}
.support-q p + p { padding-top: 0; }
.support-q a { color: var(--blue); text-decoration: none; }
.support-q a:hover { text-decoration: underline; }
.support-q strong { color: var(--ink); font-weight: 600; }

/* Article pages. */
.support-article .legal-title { margin-bottom: 8px; }
.support-read-time { margin: 0 0 20px; }
.support-figure {
  margin: 0 0 24px;
  padding: 16px;
  border-radius: 20px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
}
.support-visual {
  display: block;
  width: 100%;
  max-width: 520px;
  height: auto;
  margin: 0 auto;
  font-family: 'Geist', system-ui, -apple-system, sans-serif;
}
.support-article-body {
  color: var(--ink-2);
  font-size: 16px;
  line-height: 1.6;
}
.support-article-lede {
  margin: 0 0 16px;
  color: var(--ink);
  font-size: 17px;
}
.support-steps {
  list-style: none;
  counter-reset: support-step;
  margin: 0 0 16px;
  padding: 0;
  display: grid;
  gap: 12px;
}
.support-steps li {
  counter-increment: support-step;
  display: grid;
  grid-template-columns: 28px 1fr;
  gap: 12px;
  align-items: start;
}
.support-steps li::before {
  content: counter(support-step);
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1px solid var(--glass-border-strong);
  color: var(--blue);
  font-size: 13px;
  font-weight: 600;
}
.support-article-note {
  margin: 0;
  color: var(--ink-2);
  font-size: 15px;
}
.support-article-foot {
  margin-top: 32px;
  padding-top: 20px;
  border-top: 1px solid rgba(137,180,250,0.14);
  color: var(--ink-2);
  font-size: 15px;
  line-height: 1.6;
}
.support-article-foot p { margin: 8px 0 0; }
.support-article-foot a { color: var(--blue); text-decoration: none; }
.support-article-foot a:hover { text-decoration: underline; }
.support-back {
  display: inline-flex;
  align-items: center;
  min-height: 48px;
  font-weight: 500;
}
/* "← Support" under the topbar, before the title: the way back is where
   the eye starts, and the link after the content stays for readers who
   finish the guide. */
.support-back-top {
  margin: 0 0 4px;
  color: var(--blue);
  font-size: 15px;
  text-decoration: none;
}
.support-back-top:hover { text-decoration: underline; }
.support-back-top:focus-visible,
.support-back:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
  border-radius: 6px;
}

/* Visual motion: the runout ring breathes and a glint travels round it; a
   fingertip sifts the crate and one sleeve rises. Decoration only, so
   reduced motion gets the still frame with the sleeve already pulled. */
.runout-pulse { animation: runout-pulse 1.4s ease-in-out infinite alternate; }
@keyframes runout-pulse { from { opacity: 0.55; } to { opacity: 1; } }
.runout-glint {
  transform-box: view-box;
  transform-origin: 134px 196px;
  animation: runout-glint 3.2s linear infinite;
}
@keyframes runout-glint {
  0%   { transform: rotate(-190deg); opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { transform: rotate(10deg); opacity: 0; }
}
/* The 3D crates coach, redrawn from CrateVizCoachOverlay.kt: same
   1.6-second loop as the app's infinite transition. Only the current step's
   drawing moves, and only while the live crate is not carrying the hint
   (crate-guide.js sets .is-animating). Otherwise each drawing holds the still
   frame reduced motion gets: fingertip mid-sift, the sleeve pulled. */
.is-animating .coach-slide { animation: coach-slide 0.8s ease-in-out infinite alternate; }
@keyframes coach-slide { from { transform: translateX(-19px); } to { transform: translateX(19px); } }
.is-animating .coach-pinch-left { animation: coach-pinch-left 0.8s ease-in-out infinite alternate; }
.is-animating .coach-pinch-right { animation: coach-pinch-right 0.8s ease-in-out infinite alternate; }
@keyframes coach-pinch-left { from { transform: translateX(8px); } to { transform: translateX(-6px); } }
@keyframes coach-pinch-right { from { transform: translateX(-8px); } to { transform: translateX(6px); } }
.coach-pinch-line { transform-box: fill-box; transform-origin: center; }
.is-animating .coach-pinch-line { animation: coach-pinch-line 0.8s ease-in-out infinite alternate; }
@keyframes coach-pinch-line { from { transform: scaleX(0.36); } to { transform: scaleX(1.36); } }
.coach-sift-finger { transform: translateX(27px); }
.is-animating .coach-sift-finger { animation: coach-sift-finger 1.6s linear infinite; }
@keyframes coach-sift-finger { from { transform: translateX(0); } to { transform: translateX(54px); } }
.coach-spine-rise { transform: translateY(-20px); }
.is-animating .coach-spine-rise { animation: coach-spine-rise 1.6s ease-in-out infinite; }
@keyframes coach-spine-rise {
  0%        { transform: translateY(0); }
  25%       { transform: translateY(-20px); }
  50%, 100% { transform: translateY(0); }
}
.is-animating .coach-orbit { animation: coach-orbit 0.8s ease-in-out infinite alternate; }
@keyframes coach-orbit { from { transform: translateX(-12.8px); } to { transform: translateX(12.8px); } }
.is-animating .coach-ar rect { animation: coach-ar 0.8s ease-in-out infinite alternate; }
@keyframes coach-ar { from { opacity: 0.55; } to { opacity: 0.9; } }

@media (prefers-reduced-motion: reduce) {
  .runout-pulse { animation: none; }
  .runout-glint { animation: none; opacity: 0; }
  .is-animating .coach-slide, .is-animating .coach-pinch-left, .is-animating .coach-pinch-right,
  .is-animating .coach-pinch-line, .is-animating .coach-orbit, .is-animating .coach-ar rect,
  .is-animating .coach-sift-finger, .is-animating .coach-spine-rise { animation: none; }
  .support-card, .support-search input, .support-action { transition: none; }
  .support-widget { animation: none; }
}

/* ---------- /support: report an issue, share feedback ---------- */
.support-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin: 0 0 24px;
}
.support-action {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 72px;
  padding: 16px;
  border-radius: 20px;
  background: rgba(137,180,250,0.07);
  border: 1px solid var(--glass-border-strong);
  color: inherit;
  text-decoration: none;
  transition: border-color 140ms ease, background 140ms ease;
}
.support-action:hover { background: rgba(137,180,250,0.12); border-color: var(--blue); }
.support-action:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }
.support-action-icon { flex-shrink: 0; width: 24px; height: 24px; color: var(--blue); }
.support-action-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.support-action-title { color: var(--ink); font-size: 16px; font-weight: 600; line-height: 1.3; }
.support-action-sub { color: var(--ink-2); font-size: 13px; line-height: 1.35; }
/* Two tiles side by side at every width; on phones the glyph sits above the words. */
@media (max-width: 519px) {
  .support-action { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* The in-page forms: a sheet from the bottom on phones, a card on wider screens. */
.support-dialog {
  width: min(560px, 100%);
  max-width: 100%;
  max-height: min(92dvh, 860px);
  margin: auto auto 0;
  padding: 0;
  border: 0;
  border-radius: 28px 28px 0 0;
  background: #141418;
  color: var(--ink);
  font-family: 'Geist', ui-sans-serif, system-ui, -apple-system, sans-serif;
  box-shadow: 0 -1px 0 rgba(255,255,255,0.08), 0 -30px 80px rgba(137,180,250,0.10);
  overflow: auto;
  overscroll-behavior: contain;
}
.support-dialog::backdrop {
  background: rgba(8,8,10,0.62);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
@media (min-width: 700px) {
  .support-dialog {
    margin: auto;
    border-radius: 28px;
    box-shadow: inset 0 0 0 1px rgba(255,255,255,0.10), 0 30px 90px rgba(137,180,250,0.14);
  }
}
.support-form,
.support-done,
.support-fallback {
  padding: 20px 20px calc(20px + env(safe-area-inset-bottom));
}
.support-done:focus,
.support-fallback:focus { outline: none; }
.support-dialog-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 12px;
}
.support-dialog-title {
  margin: 6px 0 0;
  font-family: 'Instrument Serif', Georgia, serif;
  font-weight: 400;
  font-size: 32px;
  line-height: 1.1;
  letter-spacing: -0.01em;
}
.support-dialog-close,
.support-widget-close {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  margin: -4px -8px 0 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--ink-2);
  font: 400 26px/1 'Geist', system-ui, sans-serif;
  cursor: pointer;
}
.support-dialog-close:hover,
.support-widget-close:hover { color: var(--ink); background: rgba(255,255,255,0.06); }
.support-field {
  min-width: 0;
  margin: 0 0 16px;
  padding: 0;
  border: 0;
}
.support-field label,
.support-field legend {
  display: block;
  margin: 0 0 8px;
  padding: 0;
  color: var(--ink);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.4;
}
.support-optional { margin-left: 4px; color: var(--ink-2); font-size: 13px; font-weight: 400; }
.support-field textarea,
.support-field input[type="text"],
.support-field input[type="email"] {
  display: block;
  width: 100%;
  min-height: 52px;
  padding: 14px 16px;
  border-radius: var(--radius-md);
  border: 1px solid var(--glass-border);
  background: rgba(255,255,255,0.04);
  color: var(--ink);
  /* 16px keeps iOS Safari from zooming the page on focus. */
  font: 400 16px/1.45 'Geist', system-ui, -apple-system, sans-serif;
  color-scheme: dark;
  resize: vertical;
}
.support-field textarea::placeholder,
.support-field input::placeholder { color: var(--ink-3); }
.support-field textarea:focus,
.support-field input:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(137,180,250,0.18);
}
.support-field [aria-invalid="true"],
.support-field [aria-invalid="true"]:focus { border-color: var(--red); box-shadow: 0 0 0 3px rgba(243,139,168,0.18); }
.support-field-hint { margin: 6px 0 0; color: var(--ink-2); font-size: 13px; line-height: 1.4; }
.support-field-error { margin: 6px 0 0; color: var(--red); font-size: 14px; font-weight: 500; }
.support-field-error::before { content: "! "; font-weight: 700; }
.support-form-note { margin: 0 0 16px; color: var(--ink-2); font-size: 14px; line-height: 1.5; }
.support-form-note a { color: var(--blue); }
.support-form-actions { display: flex; justify-content: flex-end; margin-top: 8px; }
.support-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  padding: 0 24px;
  border: 0;
  border-radius: 999px;
  background: #fff;
  color: var(--black);
  font: 600 16px/1 'Geist', system-ui, -apple-system, sans-serif;
  text-decoration: none;
  cursor: pointer;
}
.support-submit:disabled { opacity: 0.5; cursor: default; }
.support-submit:focus-visible,
.support-dialog-close:focus-visible,
.support-widget-close:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
@media (max-width: 699px) {
  .support-submit { width: 100%; }
}
.support-done-title {
  margin: 8px 0 8px;
  font-family: 'Instrument Serif', Georgia, serif;
  font-size: 32px;
  line-height: 1.1;
}
.support-done-body { margin: 0 0 20px; color: var(--ink-2); font-size: 15px; line-height: 1.6; }

/* Five stars: the radio buttons stay in the page for keyboards and screen
   readers; each label draws its star. */
.support-stars { display: flex; gap: 4px; }
.support-stars input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}
.support-stars label {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  margin: 0;
  border-radius: 12px;
  cursor: pointer;
}
.support-stars label::before {
  content: "";
  width: 34px;
  height: 34px;
  background: rgba(255,255,255,0.45);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 2.5l2.9 6.1 6.6.8-4.9 4.6 1.3 6.6L12 17.3l-5.9 3.3 1.3-6.6-4.9-4.6 6.6-.8z'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 2.5l2.9 6.1 6.6.8-4.9 4.6 1.3 6.6L12 17.3l-5.9 3.3 1.3-6.6-4.9-4.6 6.6-.8z'/></svg>") center / contain no-repeat;
}
.support-stars input:focus-visible + label { outline: 2px solid var(--blue); outline-offset: -2px; }
.support-stars[data-rating="1"] label:nth-of-type(-n+1)::before,
.support-stars[data-rating="2"] label:nth-of-type(-n+2)::before,
.support-stars[data-rating="3"] label:nth-of-type(-n+3)::before,
.support-stars[data-rating="4"] label:nth-of-type(-n+4)::before,
.support-stars[data-rating="5"] label:nth-of-type(-n+5)::before { background: var(--blue); }
.support-rating-scale {
  display: flex;
  justify-content: space-between;
  width: 256px;
  margin: 4px 0 0;
  color: var(--ink-2);
  font-size: 12px;
}

/* "What's on your mind?" in the bottom-right corner. */
.support-widget {
  position: fixed;
  right: max(16px, env(safe-area-inset-right));
  bottom: max(16px, env(safe-area-inset-bottom));
  z-index: 30;
  width: min(300px, calc(100vw - 32px));
  padding: 12px;
  border-radius: 20px;
  background: rgba(20,20,25,0.94);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border: 1px solid var(--glass-border-strong);
  box-shadow: 0 18px 50px rgba(0,0,0,0.55), 0 10px 40px rgba(137,180,250,0.10);
  animation: support-widget-in 220ms ease-out;
}
@keyframes support-widget-in { from { opacity: 0; transform: translateY(12px); } }
/* Room to scroll the page's last lines clear of the box. */
body.has-support-widget { padding-bottom: 188px; }
.support-widget-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: -4px 0 8px 4px;
}
.support-widget-title { margin: 0; color: var(--ink); font-size: 15px; font-weight: 600; }
.support-widget-close { margin: 0 -4px 0 0; }
.support-widget-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.support-widget-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 72px;
  padding: 8px 4px;
  border-radius: var(--radius-md);
  border: 1px solid var(--glass-border);
  background: var(--glass-bg);
  color: var(--ink);
  font: 500 14px/1.2 'Geist', system-ui, -apple-system, sans-serif;
  text-decoration: none;
  cursor: pointer;
}
.support-widget-tile:hover { border-color: var(--glass-border-strong); background: rgba(137,180,250,0.08); }
.support-widget-tile:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.support-widget-tile svg { width: 24px; height: 24px; color: var(--blue); }

/* ---------- Guide articles: sections, the runout anatomy, crate steps ---------- */
.support-article .legal-title { margin-top: 0; }
.support-article-heading {
  margin: 32px 0 12px;
  color: var(--ink);
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.005em;
}
.support-article-body p { margin: 0 0 12px; }
.support-article-body strong { color: var(--ink); font-weight: 600; }
.support-quick {
  padding: 16px 16px 4px;
  border-radius: 20px;
  background: rgba(137,180,250,0.06);
  border: 1px solid var(--glass-border-strong);
}
.support-quick .support-article-heading { margin-top: 0; }
.support-list { margin: 0 0 12px; padding-left: 20px; }
.support-list li { margin: 0 0 8px; padding-left: 4px; }
.support-list li::marker { color: var(--blue); }

.runout-line {
  margin: 16px 0 16px;
  padding: 20px 16px 8px;
  border-radius: 20px;
  background: radial-gradient(120% 140% at 30% 0%, #34343E 0%, #26262D 55%, #1E1E24 100%);
  border: 1px solid rgba(255,255,255,0.08);
  line-height: 1;
}
.runout-word {
  display: inline-flex;
  margin: 0 10px 12px 0;
  vertical-align: top;
}
.runout-part {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  margin-right: 2px;
}
.runout-etch {
  padding: 0 1px 6px;
  border-bottom: 2px solid rgba(255,255,255,0.28);
  color: rgba(255,255,255,0.9);
  font-size: clamp(21px, 5.8vw, 30px);
  font-style: italic;
  font-weight: 500;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.runout-tag,
.runout-legend li::before {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.3);
  color: var(--ink);
  font: 600 12px/1 'Geist', system-ui, sans-serif;
  font-style: normal;
}
.runout-tag { margin-top: 6px; }
.runout-part-key .runout-etch { color: var(--blue); border-bottom-color: var(--blue); }
.runout-part-key .runout-tag,
.runout-legend .runout-legend-key::before { background: var(--blue); border-color: var(--blue); color: var(--black); }
.runout-legend {
  list-style: none;
  counter-reset: runout-part;
  display: grid;
  gap: 12px;
  margin: 0 0 8px;
  padding: 0;
}
.runout-legend li {
  counter-increment: runout-part;
  display: grid;
  grid-template-columns: 22px 1fr;
  column-gap: 12px;
  align-items: start;
}
.runout-legend li::before { content: counter(runout-part); grid-row: 1 / span 2; margin-top: 1px; }
.runout-legend-parts { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 2px; }
.runout-code {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 6px;
  background: rgba(255,255,255,0.07);
  color: var(--ink);
  font-style: italic;
  font-weight: 500;
  white-space: nowrap;
}
.runout-key-note { display: block; margin-top: 2px; color: var(--blue); font-size: 14px; font-style: normal; font-weight: 500; }

.crate-steps { gap: 12px; }
.crate-steps li {
  position: relative;
  grid-template-columns: 128px 1fr;
  gap: 16px;
  align-items: center;
  padding: 12px;
  border-radius: 20px;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
}
/* The step number sits on the drawing's corner. */
.crate-steps li::before {
  position: absolute;
  top: 8px;
  left: 8px;
  width: 24px;
  height: 24px;
  background: var(--black);
  font-size: 12px;
}
.crate-steps p { margin: 0; }
@media (min-width: 640px) {
  .crate-steps li { grid-template-columns: 160px 1fr; gap: 20px; }
  .crate-coach { width: 160px; }
}
.crate-coach {
  display: block;
  width: 128px;
  height: auto;
  border-radius: var(--radius-md);
  background: #141419;
}

/* ---------- /support/3d-crates: the live crate and the step carousel ---------- */
.crate-live {
  position: relative;
  min-width: 0;
  max-width: 100%;
  box-sizing: border-box;
  margin: 0 0 16px;
  aspect-ratio: 10 / 9;
  max-height: 440px;
  width: 100%;
  overflow: hidden;
  border-radius: 20px;
  background: #0E0E10;
  border: 1px solid var(--glass-border);
}
@media (min-width: 640px) { .crate-live { aspect-ratio: 4 / 3; } }
.crate-poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transition: opacity 300ms ease;
}
.crate-viz-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0;
  transition: opacity 300ms ease;
  /* Sideways drags, taps and pinches belong to the crate; a vertical finger
     off the spines still scrolls the page. */
  touch-action: pan-y;
  cursor: grab;
  outline: none;
}
.crate-viz-canvas:focus-visible { box-shadow: inset 0 0 0 2px var(--blue); border-radius: 20px; }
.crate-live.is-live .crate-viz-canvas { opacity: 1; }
.crate-live.is-live .crate-poster { opacity: 0; }
.crate-live-chip {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 12px;
  margin: 0;
  padding: 6px 12px;
  width: fit-content;
  max-width: calc(100% - 24px);
  border-radius: 999px;
  background: rgba(14,14,16,0.78);
  border: 1px solid var(--glass-border);
  color: var(--ink);
  font-size: 14px;
  line-height: 20px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
}
.crate-live-chip.is-on { opacity: 1; }

/* The current step's hint over the live crate: fingertips like the app's
   coach, a few passes, then still. */
.crate-hint {
  position: absolute;
  pointer-events: none;
  opacity: 0;
  transition: opacity 250ms ease;
}
.crate-hint.is-on { opacity: 1; }
.crate-hint-dot {
  position: absolute;
  width: 26px;
  height: 26px;
  margin: -13px 0 0 -13px;
  border-radius: 50%;
  background: rgba(255,255,255,0.85);
  box-shadow: 0 0 0 6px rgba(137,180,250,0.28), 0 2px 10px rgba(0,0,0,0.45);
}
.crate-hint-dot + .crate-hint-dot { display: none; }
.crate-hint[data-kind="slide"] .crate-hint-dot { top: 78%; left: 50%; }
.crate-hint.is-on[data-kind="slide"] .crate-hint-dot { animation: crate-hint-slide 1.6s ease-in-out 4 alternate; }
@keyframes crate-hint-slide { from { transform: translateX(-70px); } to { transform: translateX(70px); } }
.crate-hint[data-kind="sift"] .crate-hint-dot { left: 50%; top: 0; }
.crate-hint.is-on[data-kind="sift"] .crate-hint-dot { animation: crate-hint-sift 1.8s ease-in-out 4; }
@keyframes crate-hint-sift {
  0%   { top: 0; opacity: 0; }
  15%  { opacity: 1; }
  80%  { top: 100%; opacity: 1; }
  100% { top: 100%; opacity: 0; }
}
.crate-hint[data-kind="pinch"] .crate-hint-dot { top: 45%; left: 50%; }
.crate-hint[data-kind="pinch"] .crate-hint-dot + .crate-hint-dot { display: block; }
.crate-hint.is-on[data-kind="pinch"] .crate-hint-dot { animation: crate-hint-pinch-a 0.9s ease-in-out 8 alternate; }
.crate-hint.is-on[data-kind="pinch"] .crate-hint-dot + .crate-hint-dot { animation-name: crate-hint-pinch-b; }
@keyframes crate-hint-pinch-a { from { transform: translate(-14px, 10px); } to { transform: translate(-54px, 34px); } }
@keyframes crate-hint-pinch-b { from { transform: translate(14px, -10px); } to { transform: translate(54px, -34px); } }

.crate-guide { position: relative; min-width: 0; max-width: 100%; }
/* The track is three steps wide; contain it so it never widens the article. */
.crate-guide.is-carousel { overflow: hidden; border-radius: 20px; contain: inline-size; }
.crate-guide.is-carousel + .crate-steps-controls { contain: none; }
.crate-guide:focus { outline: none; }
.is-carousel .crate-steps {
  display: flex;
  gap: 0;
  margin: 0;
  transition: transform 320ms cubic-bezier(0.2, 0.8, 0.2, 1);
  touch-action: pan-y;
}
.is-carousel .crate-steps.is-dragging { transition: none; }
.is-carousel .crate-steps li { flex: 0 0 100%; min-width: 0; box-sizing: border-box; }
.crate-steps-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 12px 0 16px;
}
.crate-steps-button {
  min-height: 48px;
  min-width: 96px;
  padding: 0 16px;
  border-radius: 999px;
  border: 1px solid var(--glass-border-strong);
  background: var(--glass-bg);
  color: var(--ink);
  font: inherit;
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
}
.crate-steps-button:hover:not(:disabled) { border-color: var(--blue); }
.crate-steps-button:disabled { opacity: 0.35; cursor: default; }
.crate-steps-button:focus-visible, .crate-steps-dot:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.crate-steps-dots { display: flex; }
.crate-steps-dot {
  width: 32px;
  height: 48px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  display: grid;
  place-items: center;
}
.crate-steps-dot::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ink-3);
  transition: background 200ms ease, transform 200ms ease;
}
.crate-steps-dot[aria-current="step"]::before { background: var(--blue); transform: scale(1.25); }
@media (prefers-reduced-motion: reduce) {
  .is-carousel .crate-steps, .crate-poster, .crate-viz-canvas, .crate-steps-dot::before { transition: none; }
  .crate-hint { display: none; }
}

/* Opened from the app (the inline script on every page that offers a download sets .in-app): Teca is already
   installed, so no TestFlight or App Store link shows. !important because pages restyle their buttons by
   breakpoint. Wrap running text, or a QR block that goes with a link, in [data-download-cta]. */
.in-app .pill-download,
.in-app [data-download-cta],
.in-app a[href*="testflight.apple.com"],
.in-app a[href*="apps.apple.com"] { display: none !important; }
