/* ==========================================================================
   APLAI landing, r05 viewport correction (Opus 5.5); r07 header/motion pass
   on accepted r09 (Opus 5.5): quiet non-sticky header and a composer that
   starts compact and grows on a real copy. r10 (Opus 5.5): the name card
   flips as a whole paper card again (the Sep 30 fold, minus its centre
   line), and Watch the demo is centred in the room under the compact card,
   settling to the bottom once a real copy fills that room. r11 (Opus 5.5):
   the helper under Copy is always its natural height, one --step below the
   button; no reserved blank lines inside the card.
   --------------------------------------------------------------------------
   Sits on the original Paper & Ink styles.css (tokens, base, nav, brand,
   .visually-hidden, .js-only). Scoped to body.lp. One surface language:
   paper card + hairline ring; ink is reserved for the headline card, the
   Copy button and outline controls.

   The closed page is one fitted frame: nav + .hero = exactly the visible
   viewport (dvh, safe areas included). Every vertical size below is a token,
   so each layout pass only retunes the budget. The alpaca is the one
   flexible piece: it grows into whatever room is left, between a floor and
   a ceiling. Nothing is hidden or clipped to make the frame fit; a screen
   too small for the floor simply scrolls.

     1. Tokens + shell          6. Demo (inline section)
     2. Nav + Pro               7. Pro waitlist dialog
     3. Frame + alpaca          8. Hover (fine pointers only)
     4. Composer + paste row    9. Layout passes
     5. Watch the demo         10. Motion + reduced motion
   ========================================================================== */

/* 1. Tokens + shell -------------------------------------------------------- */
/* Registered so it computes to px: landing.js reads it to size the room the
   step-two area may take, and gives back whatever the current text doesn't
   use as air below the card (r11). */
@property --paste-room { syntax: '<length>'; inherits: true; initial-value: 0px; }
.lp {
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --ease-icon: cubic-bezier(0.2, 0, 0, 1);
  --err: #B3261E;
  --ring: 0 0 0 1px oklch(0 0 0 / 0.08);
  --lift: 0 1px 2px -1px oklch(0 0 0 / 0.08), 0 24px 48px -30px rgb(17 17 17 / 0.32);
  --gutter: max(clamp(18px, 5vw, 48px), env(safe-area-inset-left), env(safe-area-inset-right));

  /* The vertical budget of the closed frame. */
  --nav-h: 64px;                                   /* integer on purpose: frame + nav sum to 100dvh exactly */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --frame-h: calc(100dvh - var(--nav-h) - var(--safe-top));
  --top-air: clamp(6px, 1.6dvh, 20px);
  --bottom-air: clamp(12px, 2.4dvh, 28px);         /* the little space under Watch the demo */
  --gap: clamp(10px, 2dvh, 22px);
  --alpaca-min: clamp(56px, 9dvh, 88px);
  --alpaca-max: clamp(96px, 22dvh, 210px);
  --h1: clamp(1.75rem, min(8.6vw, 4.9dvh), 4.6rem);
  --promise: clamp(1rem, 1.3vw, 1.15rem);
  --card-pad: 14px;                                /* card radius = this + 14 (concentric) */
  --title-size: 15px;
  --title-pad: 4px 6px 12px;
  --msg-size: clamp(15.5px, 1.4vw, 17px);
  --msg-lh: 1.45;
  --msg-pad: 12px 14px;
  --step: 12px;
  --copy-h: 54px;
  --paste-size: 14.5px;
  --paste-lines: 2;                                /* the paste box is this many lines tall from the start */
  --tile-h: 48px;
  --watch-h: 50px;
  /* What a successful copy adds to the card: the extra lines the next-step
     sentence may need, plus the row of choices. Held as air under the card
     until then, so the card can grow without moving the alpaca or headline.
     Watch is centred in this air until the card fills it (section 5). */
  --reveal-room: calc((var(--paste-lines) - 1) * 1.4 * var(--paste-size) + var(--step) + var(--tile-h));
  /* The most the step-two area can ever need: padding, the longest helper
     (--paste-lines), the gap and the row of choices. The card plus its bottom
     margin always add up to this, whatever the helper actually says. */
  --paste-room: calc(2 * var(--step) + var(--paste-lines) * 1.4 * var(--paste-size) + var(--tile-h));
}
.lp [hidden] { display: none !important; }
html:has(.dialog[open]) { overflow: hidden; }
html:has(> body.lp) { scroll-padding-top: 0; }   /* the header no longer covers anything */

/* 2. Header + Pro -------------------------------------------------------------
   Deliberately still and not sticky. The closed page never scrolls, so a
   sticky bar only mattered once the demo opened, where it sat blurred over
   the film's head. Now it stays at the top of the page: no blur, no
   scroll-state hairline, nothing that moves. The demo gets the whole screen.
   It stays positioned (z 50) only so the alpaca's mid-hop lift can pass
   in front of it. */
.lp-nav {
  position: relative;
  padding-top: var(--safe-top);
  border-bottom: 0;                       /* no 1px border: the frame math stays exact */
  background: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  transition: none;
}
.lp .nav__inner { height: var(--nav-h); }
.lp .brand__mark { transition: transform 200ms var(--ease-out); }
.lp .brand:hover .brand__mark { transform: none; }   /* base hover is ungated; re-enabled in 8 */

/* Header face (Opus 5.5): the tiny mark beside APLAI leans toward the pointer
   and closes its eyes into smiling arcs when poked (landing.js, "Header
   face"). landing.js wraps the unchanged image in .brand__face only once the
   image has loaded; without that wrapper every rule below is inert and the
   original mark shows exactly as before. The wrapper is the same 24px box as
   the image, so the header, gap, wordmark and link hit area do not move.
   Only `translate` and `rotate` move (compositor-only), and they compose
   with the poke squash on `transform`.
   The sentinel below is what landing.js checks before touching the page:
   if this stylesheet (or this block) is missing, the face never mounts and
   the original mark stays exactly as the HTML has it. */
.lp { --aplai-face: on; }
.brand__face {
  position: relative;
  display: block;
  flex: none;
  transform-origin: 50% 90%;              /* the mark's existing pivot (styles.css) */
  transition: translate 240ms var(--ease-out), rotate 240ms var(--ease-out);
}
/* Closed eyes: paper over each drawn eye, an ink arc on top. Hidden unless
   a poke is showing; the PNG underneath is never changed. */
.brand__lids {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  visibility: hidden;
}
.brand__face[data-face="smile"] .brand__lids { visibility: visible; }
.brand__lid { fill: var(--paper); }
.brand__arc { fill: none; stroke: #000; stroke-width: 4; stroke-linecap: round; stroke-linejoin: round; }
/* With the live face, the face's own lean replaces the old hover tilt. */
@media (hover: hover) and (pointer: fine) {
  .lp .brand.is-face-live:hover .brand__mark { transform: none; }
}
/* Reduced motion: the face is completely still, exactly the original mark. */
@media (prefers-reduced-motion: reduce) {
  html .lp .brand:hover .brand__mark { transform: none; }
  .brand__face { translate: none !important; rotate: none !important; }
  .brand__face .brand__lids { visibility: hidden !important; }
}

/* Pro: a round outline button, the same language as Watch the demo. */
.pro {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--card);
  box-shadow: inset 0 0 0 1.5px var(--ink);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
  font-weight: 800;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 160ms var(--ease-out), background-color 150ms ease;
}
.pro:active { transform: scale(0.97); }
.pro:focus-visible { outline: 3px solid var(--ink); outline-offset: 4px; }

/* 3. Frame ------------------------------------------------------------------
   A flex column exactly the height left under the nav. The alpaca slot is
   the only item that grows; once it reaches its ceiling, the rest of the
   room is shared by the two auto margins (above the alpaca, above Watch). */
.lp .hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: var(--frame-h);
  /* Bottom padding is the safe area only, so its computed value marks the
     usable viewport bottom for landing.js; Watch carries the bottom air. */
  padding: var(--top-air) 0 var(--safe-bottom);
  overflow-x: clip;
  text-align: center;
}

.alpaca-slot {
  position: relative;
  flex: 1 1 0;
  width: 100%;
  min-height: var(--alpaca-min);
  max-height: var(--alpaca-max);
  margin: auto 0 clamp(4px, 1dvh, 12px);
}
.alpaca-slot.is-hopping { z-index: 60; }   /* only mid-hop: may leap in front of the nav bar */

/* The alpaca. A button: hand cursor, keyboard reachable. As tall as its
   slot, as wide as the drawing (never under 44px). Its hop is a JS spring on
   the image, so repeated taps add up instead of restarting. */
.alpaca {
  position: absolute;
  bottom: 0;
  left: 50%;
  height: 100%;
  aspect-ratio: 300 / 429;
  min-width: 44px;
  padding: 0;
  border: 0;
  border-radius: 16px;
  background: none;
  transform: translateX(-50%);
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.alpaca:focus-visible { outline: 3px solid var(--ink); outline-offset: 6px; }
.alpaca__img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: contain;
  object-position: 50% 100%;
  transform-origin: 50% 100%;
}

/* The rotating names share one intrinsic slot; the canonical tagline is
   retained in the heading's accessible name and page metadata. */
.headline {
  flex: none;
  font-size: var(--h1);
  line-height: 1.1;
  letter-spacing: -0.045em;
}
.headline__line { display: block; }
.headline__line:first-child { font-size: 0.84em; white-space: nowrap; }
/* The brand's paper flap card, no hinge line. The whole card (border, paper,
   thickness shadow and name) is what flips: landing.js folds it about its
   horizontal centre to edge-on, swaps the name while it is invisible, and
   folds it back. It never passes 90deg, so the name is never mirrored. */
.headline__card {
  position: relative;
  display: inline-grid;
  padding: 0.02em 0.22em 0.08em;
  border: 0.05em solid var(--ink);
  border-radius: 0.18em;
  background: var(--card);
  box-shadow: 0 0.07em 0 var(--ink);
  transform-origin: 50% 50%;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
.headline__sizer,
.headline__name { grid-area: 1 / 1; white-space: nowrap; }
.headline__sizer { visibility: hidden; }  /* the longest name fixes the card's width */
.headline__name { text-align: center; }
.ink { position: relative; display: inline-block; }
.ink svg {
  position: absolute;
  left: 0;
  bottom: -0.06em;
  width: 100%;
  height: 0.16em;
  overflow: visible;
  fill: none;
  stroke: var(--ink);
  stroke-width: 5;
  stroke-linecap: round;
}
.ink svg path { vector-effect: non-scaling-stroke; }

.hero-subhead {
  flex: none;
  max-width: 100%;
  margin-top: clamp(12px, 1.8dvh, 18px);
  color: var(--ink-2);
  font-size: clamp(1.0625rem, 1.55vw, 1.375rem);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.promise {
  flex: none;
  margin-top: clamp(10px, 1.5dvh, 16px);
  color: var(--ink-2);
  font-size: var(--promise);
  font-weight: 650;
  line-height: 1.4;
  letter-spacing: -0.01em;
}

/* 4. Composer ----------------------------------------------------------------
   One paper card. Concentric corners: outside = padding + 14 inside. */
.composer {
  flex: none;
  width: min(100%, 600px);
  margin-block: var(--gap) calc(var(--gap) + var(--reveal-room));   /* compact: the reveal room waits below */
  padding: var(--card-pad);
  text-align: left;
  background: var(--card);
  border-radius: calc(var(--card-pad) + 14px);
  box-shadow: var(--ring), var(--lift);
}
.composer:focus { outline: none; }
.composer:focus-visible { outline: 3px solid var(--ink); outline-offset: 4px; }

.composer__title {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: var(--title-pad);
  font-size: var(--title-size);
  font-weight: 750;
  line-height: 1.3;
  letter-spacing: -0.01em;
}
.composer__title svg { width: 18px; height: 18px; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }

.composer__message {
  padding: var(--msg-pad);
  border-radius: 14px;
  background: var(--paper-2);
  color: var(--ink);
  font-size: var(--msg-size);
  font-weight: 550;
  line-height: var(--msg-lh);
  overflow-wrap: anywhere;
  text-wrap: pretty;
  -webkit-user-select: all;
  user-select: all;                       /* one long-press selects it all */
}
.composer__url {
  white-space: nowrap;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: rgb(17 17 17 / 0.4);
  text-underline-offset: 3px;
}

/* Copy and the no-script hint share one cell: no jump when JS arrives. */
.composer__action { display: grid; align-items: center; min-height: var(--copy-h); margin-top: var(--step); }
.composer__action > * { grid-area: 1 / 1; }
.composer__manual { color: var(--ink-2); font-size: 15px; font-weight: 600; text-align: center; }
.js .composer__manual { display: none; }

.copy {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: var(--copy-h);
  border: 0;
  border-radius: 14px;
  background: var(--ink);
  color: var(--paper);
  font: inherit;
  font-size: 17px;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 160ms var(--ease-out), background-color 150ms ease;
}
.copy:active { transform: scale(0.97); }
.copy:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }
.copy[aria-busy="true"] { cursor: progress; }
.copy__icons { position: relative; width: 22px; height: 22px; flex: none; }
.copy__icon {
  position: absolute;
  inset: 0;
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: opacity 300ms var(--ease-icon), transform 300ms var(--ease-icon), filter 300ms var(--ease-icon);
}
.copy__icon--done,
.copy[data-state="copied"] .copy__icon--copy { opacity: 0; transform: scale(0.25); filter: blur(4px); }
.copy[data-state="copied"] .copy__icon--done { opacity: 1; transform: none; filter: none; }

/* Step two. Three states on the card (data-step):
     copy    compact: one short hint line right under Copy, no empty row.
     open    a real copy succeeded: the helper, then the three choices.
     failed  honest manual guidance, choices hidden.
   r11: the helper box is always exactly its text. No reserved blank lines,
   so "Works best with Meta Muse." (one line) sits one --step under Copy,
   with one --step to the choices: the same rhythm as message -> Copy.
   Longer helpers (an alternative's sentence, the failure text) simply take
   their natural lines. landing.js keeps the alpaca and headline still by
   setting the card's bottom margin to --gap + (--paste-room - actual step-two
   height), and animates height and margin together on every change.
   The margins below are the no-script / unregistered-@property fallback. */
.paste { padding: var(--step) 2px 0; }
.paste.is-morphing { overflow: clip; }   /* only while the card is growing or settling */
.composer[data-step="open"] { margin-bottom: var(--gap); }
.composer[data-step="failed"] { margin-bottom: calc(var(--gap) + var(--step) + var(--tile-h)); }
.paste__line {
  padding: 0 4px;
  color: var(--ink-2);
  font-size: var(--paste-size);
  font-weight: 550;
  line-height: 1.4;
  text-wrap: pretty;
}
.paste__line strong { color: var(--ink); font-weight: 750; }
.paste__line[data-state="failed"] { color: var(--ink); font-weight: 650; }
.paste__apps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin-top: var(--step);
}
.go {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: var(--tile-h);
  height: 100%;
  padding: 6px 8px;
  border-radius: 14px;
  background: var(--card);
  box-shadow: inset 0 0 0 1.5px var(--line-strong);
  color: var(--ink);
  font-size: 14.5px;
  font-weight: 650;
  line-height: 1.15;
  text-align: center;
  text-decoration: none;
  text-wrap: balance;
  -webkit-tap-highlight-color: transparent;
  transition: transform 160ms var(--ease-out), background-color 150ms ease, box-shadow 150ms ease;
}
button.go {                               /* Meta Muse: same tile, a button not a link */
  width: 100%;
  border: 0;
  font-family: inherit;
  cursor: pointer;
}
.go:active { transform: scale(0.97); }
.go:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }
.go[data-selected] { box-shadow: inset 0 0 0 2px var(--ink); }
.go__out { width: 14px; height: 14px; flex: none; fill: none; stroke: var(--grey); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* 5. Watch the demo ------------------------------------------------------------
   Its layout home is the bottom of the frame (auto top margin, --bottom-air
   under it, then the safe area): that is where it rests once a real copy
   has grown the card. Before then, landing.js lifts it with `translate`
   (never layout) so it sits exactly midway between the card's bottom edge
   and the usable viewport bottom. `translate` is its own property, so it
   composes with the first-load rise and the press scale on `transform`. */
.watch {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 12px;
  min-height: var(--watch-h);
  margin-top: auto;
  margin-bottom: var(--bottom-air);
  padding: 0 20px 0 6px;
  border: 0;
  border-radius: 999px;
  background: var(--card);
  box-shadow: inset 0 0 0 1.5px var(--ink);
  color: var(--ink);
  font: inherit;
  font-size: 16px;
  font-weight: 650;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 160ms var(--ease-out), background-color 150ms ease;
}
.js .watch--file { display: none; }
.watch:active { transform: scale(0.97); }
.watch:focus-visible { outline: 3px solid var(--ink); outline-offset: 4px; }
.watch__disc {
  position: relative;
  display: grid;
  place-items: center;
  width: calc(var(--watch-h) - 12px);
  height: calc(var(--watch-h) - 12px);
  flex: none;
  border-radius: 50%;
  background: var(--ink);
}
.watch__icon { grid-area: 1 / 1; width: 15px; height: 15px; }
.watch__icon--play { fill: var(--paper); translate: 1.5px 0; }   /* optical centre */
.watch__icon--hide { width: 20px; height: 20px; fill: none; stroke: var(--paper); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
#watch-demo .watch__icon { transition: opacity 300ms var(--ease-icon), transform 300ms var(--ease-icon), filter 300ms var(--ease-icon); }
#watch-demo .watch__icon--hide,
#watch-demo[aria-expanded="true"] .watch__icon--play { opacity: 0; transform: scale(0.25); filter: blur(4px); }
#watch-demo[aria-expanded="true"] .watch__icon--hide { opacity: 1; transform: none; filter: none; }

/* 6. Demo: the inline section ----------------------------------------------------
   Under the frame, a full screen tall. The header is not sticky, so "scroll
   to it" lands with nothing above it: the head and the whole film (native
   controls included) fill the screen, with the safe areas respected. The
   film is sized by the room left after the head and paddings. Nothing crops it. */
.reel {
  --reel-chrome: 96px;                    /* 16 top + 44 head + 12 gap + 24 bottom */
  --reel-room: calc(100dvh - var(--safe-top) - var(--safe-bottom) - var(--reel-chrome));
  display: grid;
  align-content: center;
  min-height: 100dvh;
  padding: calc(16px + var(--safe-top)) 0 calc(24px + var(--safe-bottom));
  background: var(--paper-2);
  scroll-margin-top: 0;
  opacity: 1;
  transform: none;
  transition: opacity 240ms var(--ease-out), transform 240ms var(--ease-out);
}
@starting-style { .reel { opacity: 0; transform: translateY(16px); } }
.reel__inner {
  display: grid;
  gap: 12px;
  width: min(1000px, 100% - 2 * var(--gutter), calc(var(--reel-room) * 16 / 9));
  margin-inline: auto;
}
.reel[data-variant="portrait"] .reel__inner { width: min(100% - 2 * var(--gutter), calc(var(--reel-room) * 1080 / 2424)); }
.reel__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 44px; }
.reel__title { font-size: 17px; font-weight: 750; line-height: 1.3; letter-spacing: -0.01em; }
.reel__title:focus { outline: none; }
.reel__title:focus-visible { outline: 3px solid var(--ink); outline-offset: 4px; border-radius: 6px; }
.reel__close {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 44px;
  flex: none;
  padding: 0 16px 0 12px;
  border: 0;
  border-radius: 999px;
  background: var(--card);
  box-shadow: inset 0 0 0 1.5px var(--line-strong);
  color: var(--ink);
  font: inherit;
  font-size: 15px;
  font-weight: 650;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 160ms var(--ease-out), background-color 150ms ease;
}
.reel__close:active { transform: scale(0.97); }
.reel__close:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }
.reel__close svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
/* Radius on the video itself, no clipping wrapper: native controls stay whole. */
.reel__video { display: block; width: 100%; aspect-ratio: 16 / 9; border-radius: 12px; background: #111; object-fit: contain; }
.reel[data-variant="portrait"] .reel__video { aspect-ratio: 1080 / 2424; }
.reel__status { padding: 0 8px; font-size: 15px; font-weight: 600; text-align: center; }
.reel__status:empty { display: none; }
.reel__retry {
  justify-self: center;
  min-height: 44px;
  padding: 0 20px;
  border: 0;
  border-radius: 999px;
  background: var(--card);
  box-shadow: inset 0 0 0 1.5px var(--ink);
  color: var(--ink);
  font: inherit;
  font-size: 15px;
  font-weight: 650;
  cursor: pointer;
}

/* 7. Pro waitlist dialog ---------------------------------------------------------
   A centred modal (origin stays centred). CSS transitions with
   @starting-style, so a fast reopen retargets; older engines snap. */
.dialog {
  position: fixed;
  inset: 0;
  max-width: none;
  height: fit-content;
  max-height: calc(var(--vvh, 100dvh) - 24px);
  margin: auto;
  padding: 0;
  border: 0;
  border-radius: 28px;
  background: var(--paper);
  color: var(--ink);
  overflow-y: auto;
  overscroll-behavior: contain;
  box-shadow: var(--ring), 0 40px 80px -30px rgb(17 17 17 / 0.5);
  opacity: 0;
  transform: translateY(8px) scale(0.96);
  transition: opacity 160ms var(--ease-out), transform 160ms var(--ease-out), overlay 160ms allow-discrete, display 160ms allow-discrete;
}
.dialog[open] {
  opacity: 1;
  transform: none;
  transition: opacity 260ms var(--ease-out), transform 260ms var(--ease-out), overlay 260ms allow-discrete, display 260ms allow-discrete;
}
@starting-style { .dialog[open] { opacity: 0; transform: translateY(8px) scale(0.96); } }
.dialog::backdrop {
  background: rgb(17 17 17 / 0);
  -webkit-backdrop-filter: blur(0);
  backdrop-filter: blur(0);
  transition: background-color 160ms ease, backdrop-filter 160ms ease, overlay 160ms allow-discrete, display 160ms allow-discrete;
}
.dialog[open]::backdrop { background: rgb(17 17 17 / 0.42); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); transition-duration: 260ms; }
@starting-style { .dialog[open]::backdrop { background: rgb(17 17 17 / 0); -webkit-backdrop-filter: blur(0); backdrop-filter: blur(0); } }

.dialog__close {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--card);
  box-shadow: inset 0 0 0 1.5px var(--line-strong);
  color: var(--ink);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 160ms var(--ease-out), background-color 150ms ease;
}
.dialog__close:active { transform: scale(0.97); }
.dialog__close svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.dialog__close--corner { position: absolute; top: 12px; right: 12px; }

.dialog--join { width: min(420px, 100% - 32px); }
.dialog--join .dialog__inner {
  position: relative;
  display: grid;
  justify-items: center;
  padding: 34px 26px max(26px, calc(env(safe-area-inset-bottom) + 12px));
  text-align: center;
}
.join__alpaca { width: 52px; margin-bottom: 12px; }
.join__title { max-width: 13ch; font-size: clamp(1.6rem, 6vw, 2rem); line-height: 1.06; letter-spacing: -0.04em; text-wrap: balance; }
.join__sub { margin-top: 8px; color: var(--grey); font-size: 15.5px; line-height: 1.5; text-wrap: pretty; }
.join { display: grid; gap: 10px; width: 100%; margin-top: 20px; text-align: left; }
.join__row { display: flex; flex-wrap: wrap; gap: 8px; }
.join__input {
  flex: 1 1 190px;
  min-width: 0;
  height: 52px;
  padding: 0 16px;
  border: 1.5px solid var(--line-strong);
  border-radius: 999px;
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: 17px;                        /* >= 16px: no iOS zoom on focus */
  transition: border-color 150ms ease, box-shadow 150ms ease;
}
.join__input::placeholder { color: var(--grey); opacity: 1; }
.join__input:focus { outline: none; border-color: var(--ink); box-shadow: 0 0 0 1px var(--ink); }
.join__input[aria-invalid="true"] { border-color: var(--err); box-shadow: 0 0 0 1px var(--err); }
.join__go {
  flex: 1 0 auto;
  height: 52px;
  padding: 0 22px;
  border: 0;
  border-radius: 999px;
  background: var(--ink);
  color: var(--paper);
  font: inherit;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
  transition: transform 160ms var(--ease-out), background-color 150ms ease;
}
.join__go:active { transform: scale(0.97); }
.join__go:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }
.join__error { display: flex; gap: 7px; color: var(--err); font-size: 14px; font-weight: 550; line-height: 1.45; }
.join__error::before {
  content: "!";
  display: grid;
  place-items: center;
  flex: none;
  width: 17px;
  height: 17px;
  margin-top: 1px;
  border: 1.8px solid currentColor;
  border-radius: 50%;
  font-size: 11px;
  font-weight: 800;
  line-height: 1;
}
.join__result { display: flex; gap: 8px; color: var(--ink-2); font-size: 14px; line-height: 1.45; }
.join__result::before {                   /* hollow dot: a status, never a success tick */
  content: "";
  flex: none;
  width: 9px;
  height: 9px;
  margin-top: 0.42em;
  border: 2px solid currentColor;
  border-radius: 50%;
}
.join__result:empty { display: none; }

/* 8. Hover (fine pointers only) ------------------------------------------------- */
@media (hover: hover) and (pointer: fine) {
  .copy:hover,
  .join__go:hover { background: #2B2B2B; }
  .pro:hover,
  .watch:hover,
  .go:hover,
  .dialog__close:hover,
  .reel__close:hover,
  .reel__retry:hover { background: var(--paper-2); }
  .lp .brand:hover .brand__mark { transform: rotate(-8deg); }
}

/* 9. Layout passes ----------------------------------------------------------------
   Order matters: later passes retune the tokens set by earlier ones. */

/* Laptops and up: a larger tagline, tied to height so short laptops fit. */
@media (min-width: 900px) {
  .lp { --h1: clamp(2.5rem, min(5.2vw, 7.2dvh), 4.6rem); }
}
/* Short laptop windows (under 760px tall). */
@media (min-width: 900px) and (max-height: 760px) {
  .lp {
    --nav-h: 56px;
    --h1: clamp(2.2rem, min(5vw, 6.6dvh), 4rem);
    --alpaca-min: 60px;
    --card-pad: 12px;
    --msg-size: 16px;
    --copy-h: 50px;
  }
}

/* Phones. */
@media (max-width: 619px) {
  .lp { --nav-h: 56px; --card-pad: 12px; --paste-size: 14px; }
  .dialog--join .dialog__inner { padding-inline: 22px; }
}
/* Narrow phones: the copied sentence can need three lines; reserve them. */
@media (max-width: 439px) {
  .lp { --paste-lines: 3; }
}
/* Small phones (360-wide Galaxy/Pixel, iPhone SE/mini). */
@media (max-width: 380px) {
  .lp { --msg-size: 15px; --msg-pad: 11px 12px; --copy-h: 52px; }
  .copy { font-size: 16px; }
  .go { padding: 6px; font-size: 14px; }
  .go__out { display: none; }
  .dialog--join .dialog__inner { padding-inline: 18px; }
}

/* Short portrait screens (360x640, and any phone with tall browser chrome):
   the same frame, tighter. Text stays >= 14px, every control >= 44px. */
@media (orientation: portrait) and (max-height: 700px) {
  .lp {
    --nav-h: 52px;
    --gap: clamp(8px, 1.6dvh, 22px);
    --alpaca-min: 52px;
    --h1: clamp(1.55rem, min(8vw, 4.4dvh), 2rem);
    --promise: 0.9375rem;
    --card-pad: 10px;
    --title-size: 14px;
    --title-pad: 2px 4px 8px;
    --msg-size: 14.5px;
    --msg-lh: 1.4;
    --msg-pad: 9px 12px;
    --step: 8px;
    --copy-h: 46px;
    --watch-h: 46px;
  }
  .watch { font-size: 15px; }
  .hero-subhead { margin-top: 10px; font-size: 1rem; line-height: 1.35; }
  .promise { margin-top: 8px; }
}

/* Phones held sideways (844x390): two columns. Tagline, alpaca and Watch on
   the left; the card, centred, on the right. Empty 1fr rows above and below
   centre the left column against the card. */
@media (orientation: landscape) and (max-height: 560px) {
  .lp {
    --nav-h: 48px;
    --h1: clamp(1.4rem, min(3.4vw, 7.6dvh), 2.4rem);
    --promise: 0.9375rem;
    --card-pad: 10px;
    --title-size: 14px;
    --title-pad: 2px 4px 8px;
    --msg-size: 14.5px;
    --msg-lh: 1.4;
    --msg-pad: 9px 12px;
    --step: 8px;
    --copy-h: 46px;
    --paste-size: 14px;
    --watch-h: 46px;
  }
  .lp .hero {
    display: grid;
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    grid-template-rows: 1fr auto auto auto auto auto 1fr;
    align-content: center;
    justify-items: start;
    column-gap: clamp(20px, 4vw, 40px);
    padding-bottom: calc(var(--bottom-air) + var(--safe-bottom));
    text-align: left;
  }
  .alpaca-slot { grid-area: 2 / 1; height: clamp(72px, 26dvh, 132px); min-height: 0; max-height: none; margin: 0 0 6px; }
  .alpaca { left: 0; transform: none; }
  .alpaca__img { object-position: 0 100%; }
  .headline { grid-area: 3 / 1; }
  .hero-subhead { grid-area: 4 / 1; margin-top: 10px; font-size: 1rem; line-height: 1.35; }
  .promise { grid-area: 5 / 1; margin-top: 8px; }
  /* Watch sits in the left column, not under the card, so there is no
     card-to-bottom gap to balance here: landing.js leaves it unlifted. */
  .watch { grid-area: 6 / 1; margin-top: 14px; margin-bottom: 0; font-size: 15px; translate: none !important; }
  /* The card is centred in its own column here, so it simply grows about its
     centre; no reveal room is held below it. */
  .composer,
  .lp .composer[data-step] { grid-column: 2; grid-row: 1 / -1; align-self: center; width: 100%; margin: 0; }

  .reel { --reel-chrome: 72px; padding: calc(8px + var(--safe-top)) 0 calc(12px + var(--safe-bottom)); }   /* 8 + 44 + 8 + 12 */
  .reel__inner { gap: 8px; }

  .join__alpaca { display: none; }
  .dialog--join .dialog__inner { padding-top: 26px; }
}
/* Narrower sideways phones: the card column is narrow enough to need three lines. */
@media (orientation: landscape) and (max-height: 560px) and (max-width: 759px) {
  .lp { --paste-lines: 3; }
}

/* 10. Motion -------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  /* First load only: reading order, 60ms apart. CSS-only, never stuck hidden.
     The rise is on the alpaca's slot, not the button (which owns a centring
     transform). It stays inside the frame's bottom air, so it never adds scroll. */
  .alpaca-slot,
  .headline,
  .promise,
  .composer,
  .watch { animation: lp-rise 480ms var(--ease-out) both; }
  .headline { animation-delay: 60ms; }
  .promise { animation-delay: 120ms; }
  .composer { animation-delay: 180ms; }
  .watch { animation-delay: 240ms; }
  @keyframes lp-rise { from { opacity: 0; transform: translateY(10px); } }

  /* The ink line under APLAI draws itself once. */
  .ink svg path { stroke-dasharray: 1 2; animation: lp-ink 700ms var(--ease-in-out) 480ms both; }
  @keyframes lp-ink { from { stroke-dashoffset: 1.01; } to { stroke-dashoffset: 0; } }

  /* Status text appears rather than snapping in. */
  .join__error:not([hidden]),
  .join__result:not(:empty) { animation: lp-appear 200ms var(--ease-out) both; }
  @keyframes lp-appear { from { opacity: 0; transform: translateY(-4px); } }
}

/* Reduced motion: styles.css zeroes transitions/animations. The dialog and
   the demo section keep a short fade with no movement; the alpaca answers a
   tap with a soft fade instead of a hop. */
@media (prefers-reduced-motion: reduce) {
  .lp .dialog,
  .lp .dialog[open],
  .lp .reel { transform: none !important; }
  .lp .dialog {
    transition-property: opacity, overlay, display !important;
    transition-duration: 160ms !important;
  }
  @starting-style { .lp .dialog[open] { opacity: 0; } }
  .lp .reel { transition: opacity 200ms ease !important; }
  @starting-style { .lp .reel { opacity: 0; } }
  .lp .alpaca__img { transition: opacity 220ms ease !important; }
  .lp .alpaca.is-tapped .alpaca__img { opacity: 0.55; transition-duration: 80ms !important; }
}

/* Native privacy disclosure: a quiet control with immediate feedback. */
#waitlist-privacy-details > summary {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  width: fit-content;
  min-height: 32px;
  padding: 5px 12px;
  border: 1px solid #deddd8;
  border-radius: 999px;
  background: #efeee9;
  color: #5c5c5c;
  font-size: 13px;
  font-weight: 500;
  line-height: 20px;
  list-style: none;
  cursor: pointer;
  outline: none;
  transition: none;
  animation: none;
  transform: none;
}
#waitlist-privacy-details > summary::-webkit-details-marker { display: none; }
#waitlist-privacy-details > summary::before {
  content: "";
  width: 5px;
  height: 5px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
}
#waitlist-privacy-details[open] > summary::before { transform: rotate(45deg); }
#waitlist-privacy-details > summary:focus-visible {
  outline: 2px solid #292929;
  outline-offset: 3px;
}
@media (hover: hover) and (pointer: fine) {
  #waitlist-privacy-details > summary:hover { background: #e5e4de; }
}
#waitlist-privacy-details > summary:active { background: #deddd8; }
@media (pointer: coarse) {
  #waitlist-privacy-details > summary { min-height: 44px; }
}
@media (prefers-reduced-motion: reduce) {
  #waitlist-privacy-details > summary {
    transition: none !important;
    animation: none !important;
    transform: none !important;
  }
}

/* Quiet privacy access; collection details stay on the short privacy page. */
.join__policy-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  justify-self: end;
  min-width: 44px;
  min-height: 44px;
  padding: 0 4px;
  border: 0;
  border-radius: 4px;
  background: none;
  color: #666;
  font-size: 12px;
  font-weight: 400;
  line-height: 20px;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.join__policy-link:focus-visible { outline: 2px solid #292929; outline-offset: 3px; }
@media (hover: hover) and (pointer: fine) {
  .join__policy-link:hover { color: #111; }
}
.join__policy-link:active { color: #111; }
