/* ============================================================
   getnessi.ai v2 — film → home → setup → ceremony → handoff

   Sources, in order of authority:
     new_design/…/uploads/GETNESSI-SITE-SPEC.md   copy and sequence
     new_design/…/Nessi Site v2.dc.html           visual reference
     new_design/…/HANDOFF-v2.md                   the send CTA, the typewriter

   The styleguide.md shipped alongside those is the v1 document (cyan,
   IBM Plex Mono, twelve-card storm) and does NOT describe this design.
   v2 is Newsreader for headlines, Schibsted Grotesk for UI, no monospace
   anywhere, teal #7FDFDB for Nessi, coral #FD8360/#E2603A for the user's
   own action. iMessage blue appears exactly once — the handoff preview
   bubble depicts real software, so it carries real software's colour.

   Token names --cream/--body/--cyan etc. are inherited from v1 on
   purpose: /privacy links this sheet for tokens (see legal.v1.css) and
   keeps working unrenamed.
   ============================================================ */

:root{
  /* Base */
  --ink:#07090C;
  --raised:#0D1218;
  --field-focus:#101720;
  --panel:#0D1218;
  --surface:#10161D;
  --line:#151B22;
  --line-2:#1B222B;
  --line-3:#1B222B;
  --line-4:#253039;
  --line-hover:#2B3742;
  --line-skip:#212932;

  /* Text */
  --cream:#F4EFE7;
  --body:#D8D3CA;
  --body-2:#ADB5BF;
  --body-3:#F4EFE7;
  --lede:#ADB5BF;
  --muted:#8B949F;
  --faint:#6C7681;
  --ghost:#4F5966;

  /* Accent. Teal is Nessi; coral is the user acting. */
  --cyan:#7FDFDB;
  --cyan-deep:#2DD3CD;
  --cyan-light:#A8F5F1;
  --teal:#7FDFDB;
  --teal-deep:#2DD3CD;
  --on-cyan:#07171A;
  --coral:#FD8360;
  --coral-deep:#E2603A;
  --coral-text:#26100A;
  --send-dark:#2A1109;
  /* Semantic error red — not a brand accent. Used only for validation
     states (the phone field's error line and border); never decorative,
     the same carve-out shape as iMessage blue: errors are a state, and
     states get their conventional colour. Chosen for ≥4.5:1 on --ink. */
  --bad:#FF5F56;

  --grad-cta:linear-gradient(120deg,#FD8360,#E2603A);

  --font-sans:'Schibsted Grotesk',system-ui,-apple-system,sans-serif;
  --serif:'Newsreader',Georgia,serif;

  --col:430px;

  /* Entrances overshoot; consumption accelerates away. */
  --enter:cubic-bezier(.16,1,.3,1);
  --converge:cubic-bezier(.55,0,.35,1);
  --pop:cubic-bezier(.2,1.2,.3,1);
}

*{box-sizing:border-box}
html,body{margin:0;padding:0;background:var(--ink);-webkit-text-size-adjust:100%;overscroll-behavior-y:none}
button,a,input{-webkit-tap-highlight-color:transparent;touch-action:manipulation}
button{font-family:inherit}
input{font-family:inherit}
a{color:var(--teal);text-decoration:none}
a:hover{color:var(--cyan-light)}

body{
  font-family:var(--font-sans);
  color:var(--cream);
  -webkit-font-smoothing:antialiased;
}

.stage{display:flex;justify-content:center;min-height:100dvh}

/* The 430px column on a wider field. Light falls from above. */
.frame{
  position:relative;
  width:100%;max-width:var(--col);min-height:100dvh;
  overflow:hidden;
  background:radial-gradient(125% 72% at 50% -8%,#0E141A 0%,#07090C 60%);
}

.view{
  position:absolute;inset:0;
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .45s var(--enter),visibility 0s linear .45s;
}
.view.on{opacity:1;visibility:visible;pointer-events:auto;transition:opacity .45s var(--enter)}

:where(a,button,input):focus-visible{
  outline:2px solid var(--teal);
  outline-offset:3px;
  border-radius:6px;
}

/* ── keyframes (transcribed from Nessi Site v2.dc.html) ── */

@keyframes nsChipIn{from{opacity:0;transform:translate(var(--fx),var(--fy)) scale(.94)}to{opacity:1;transform:translate(0,0) rotate(var(--r))}}
@keyframes nsDrift{0%{transform:translate(0,0)}50%{transform:translate(-5px,-8px)}100%{transform:translate(0,0)}}
@keyframes nsDim{to{opacity:.42;filter:blur(2px) saturate(.65)}}
@keyframes nsConverge{0%{opacity:1;transform:translate(0,0) scale(1)}60%{opacity:.7}100%{opacity:0;transform:translate(var(--cx),var(--cy)) scale(.05);filter:blur(3px)}}
@keyframes nsBeat{0%{opacity:0;transform:translateY(12px)}16%{opacity:1;transform:translateY(0)}82%{opacity:1;transform:translateY(0)}100%{opacity:0;transform:translateY(-7px)}}
@keyframes nsUp{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:translateY(0)}}
@keyframes nsFade{from{opacity:0}to{opacity:1}}
@keyframes nsOut{to{opacity:0}}
@keyframes nsOrbIn{0%{opacity:0;transform:scale(.3)}100%{opacity:1;transform:scale(1)}}
@keyframes nsBreathe{0%,100%{transform:scale(1)}50%{transform:scale(1.045)}}
@keyframes nsSpin{to{transform:rotate(360deg)}}
@keyframes nsRing{0%{opacity:.38;transform:scale(.85)}100%{opacity:0;transform:scale(1.8)}}
@keyframes nsCheck{from{stroke-dashoffset:18}to{stroke-dashoffset:0}}
@keyframes nsPop{0%{opacity:0;transform:scale(.4)}60%{opacity:1;transform:scale(1.08)}100%{opacity:1;transform:scale(1)}}
@keyframes nsDot{0%,100%{box-shadow:0 0 0 0 rgba(127,223,219,.4)}50%{box-shadow:0 0 0 7px rgba(127,223,219,0)}}
@keyframes nsCaret{0%,46%{opacity:1}50%,96%{opacity:0}100%{opacity:1}}
@keyframes nsSweep{0%{transform:translateX(-130%)}55%,100%{transform:translateX(130%)}}
@keyframes nsHalo{0%{opacity:.5;transform:scale(.99)}100%{opacity:0;transform:scale(1.055)}}
@keyframes nsLift{0%,100%{transform:translateY(0);box-shadow:0 12px 30px rgba(226,96,58,.28)}50%{transform:translateY(-3px);box-shadow:0 20px 44px rgba(226,96,58,.4)}}
@keyframes nsNudge{0%,62%,100%{transform:translate(0,0)}74%{transform:translate(3px,-3px)}86%{transform:translate(1px,-1px)}}

/* ── the shared Skip ──────────────────────────────────── */

.skip{
  position:absolute;right:16px;top:calc(16px + env(safe-area-inset-top));
  padding:11px 18px;border-radius:999px;border:1px solid var(--line-skip);
  background:rgba(7,9,12,.72);backdrop-filter:blur(8px);color:var(--muted);
  font-family:inherit;font-size:12px;font-weight:500;letter-spacing:.08em;
  text-transform:uppercase;cursor:pointer;z-index:2;
}
.skip:hover{color:var(--cream);border-color:#37424E}

/* ═══════════════ A. FILM ═══════════════
   14.3s, auto-advances to Home. Skip is visible from 0.8s and
   always lands on Home. The chips are injected by app.js — geometry
   per chip goes in through the CSSOM (see the CSP note in _headers). */

#vFilm{overflow:hidden}
#vFilm.on{animation:nsOut .7s ease 27.30s both}
#vFilm.on .skip{animation:nsFade .8s ease .8s both}

.chips{position:absolute;inset:0}
#vFilm.on .chips{animation:nsDim 1s ease 2.25s both,nsDrift 18s ease-in-out 2s infinite}

.chip{position:absolute;left:var(--l);top:var(--t)}
#vFilm.on .chip{animation:nsChipIn .55s var(--enter) var(--d1) both}
.chip-cv{}
#vFilm.on .chip-cv{animation:nsConverge .95s var(--converge) var(--d2) both}

.chip-card{
  width:252px;padding:12px 14px;border-radius:17px;
  background:rgba(20,26,33,.94);border:1px solid var(--line-2);
  box-shadow:0 18px 44px rgba(0,0,0,.55);
  display:flex;gap:11px;align-items:flex-start;
}
.chip-tx{min-width:0}
.chip-l{font-size:10px;font-weight:600;letter-spacing:.13em;text-transform:uppercase;color:var(--faint)}
.chip-m{margin-top:4px;font-size:14px;line-height:1.35;color:var(--body)}

/* App tiles: original glyphs in each app's signature colour, not logos.
   Real software's colours, the one sanctioned exception to the palette. */
.tile{flex-shrink:0;width:34px;height:34px;border-radius:9px;box-shadow:0 3px 8px rgba(0,0,0,.35)}
.tile svg{display:block}
.t-mail{background:linear-gradient(160deg,#4DA3FF,#1471E8);display:flex;align-items:center;justify-content:center}
.t-mail svg{width:19px;height:19px}
.t-msg{background:linear-gradient(160deg,#5BE370,#21B93F);display:flex;align-items:center;justify-content:center}
.t-msg svg{width:19px;height:19px}
.t-cal{background:#FFF;overflow:hidden}
.t-cal .cb{height:11px;background:#FF3B30}
.t-cal .cd{height:23px;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:600;color:#16150F}
.t-rem{background:#FFF;display:flex;flex-direction:column;justify-content:center;gap:4px;padding:0 7px}
.t-rem .rl{display:flex;align-items:center;gap:4px}
.t-rem .rl i{width:5px;height:5px;border-radius:50%;flex-shrink:0}
.t-rem .rl b{flex:1;height:2px;border-radius:2px;background:#D6D0C6}
.t-rem .rl1 i{background:#FF3B30}
.t-rem .rl2 i{background:#FF9500}
.t-rem .rl3 i{background:#007AFF}
.t-notes{background:#FFF;overflow:hidden}
.t-notes .cb{height:10px;background:#FFD60A}
.t-notes .nl{padding:6px 7px;display:flex;flex-direction:column;gap:3px}
.t-notes .nl i{height:2px;background:#D6D0C6;border-radius:2px}
.t-notes .nl i.sh{width:60%}

/* The two film beats. Stacked in one grid cell so the block stays centred
   while only one is ever alive. */
.fbeats{
  position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);
  padding:0 34px;text-align:center;display:grid;
}
.fbeat{
  grid-area:1/1;
  font-family:var(--serif);font-size:clamp(25px,7.2vw,31px);
  line-height:1.24;font-weight:500;letter-spacing:-.01em;text-wrap:pretty;
  /* Weight 500 (up from the reference's 400) plus this soft dark halo: the
     beats sit ON TOP of the dimmed, blurred chip field and the regular
     weight was hard to read through it. The halo lifts the type off the
     background noise without turning the storm down. Brightness has no
     headroom left — cream is the palette's ceiling and pure white is
     banned by the design system. If 500 isn't enough, 600 is already
     loaded; if the halo feels wrong, it is this one declaration. */
  text-shadow:0 2px 16px rgba(7,9,12,.85);
}
#vFilm.on .f1{animation:nsBeat 4.43s ease 2.65s both}
#vFilm.on .f2{animation:nsBeat 4.43s ease 7.23s both}
.fbeat .coral{color:var(--coral)}
.fbeat .soft{color:var(--muted)}

/* The orb and the resolve line, one centred column. */
.forb{
  position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:40px;padding:0 34px;
  pointer-events:none;
}
.orb{position:relative;flex-shrink:0;width:128px;height:128px}
#vFilm.on .orb{animation:nsOrbIn 1.1s cubic-bezier(.2,.9,.25,1) 12.11s both}
.orb-b{position:absolute;inset:0}
#vFilm.on .orb-b{animation:nsBreathe 5s ease-in-out 13.26s infinite}
.o-glow{position:absolute;inset:-32px;border-radius:50%;background:radial-gradient(circle,rgba(45,211,205,.2) 0%,rgba(45,211,205,0) 68%)}
.o-ring{position:absolute;inset:0;border-radius:50%;background:conic-gradient(from 0deg,rgba(45,211,205,0),rgba(45,211,205,.85),rgba(45,211,205,0));opacity:.4;animation:nsSpin 14s linear infinite}
.o-core{position:absolute;inset:5px;border-radius:50%;background:radial-gradient(circle at 38% 32%,#101C22 0%,#07090C 74%);border:1px solid rgba(45,211,205,.24)}
.o-inner{position:absolute;inset:26px;border-radius:50%;background:radial-gradient(circle at 40% 36%,rgba(168,245,241,.42) 0%,rgba(45,211,205,.16) 52%,rgba(45,211,205,0) 80%);filter:blur(8px)}
.o-icon{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}
.o-icon img{width:58px;height:auto;display:block}
.o-pulse{position:absolute;inset:0;border-radius:50%;border:1px solid rgba(45,211,205,.4)}
#vFilm.on .o-pulse{animation:nsRing 4.5s ease-out 13.46s infinite}

/* The two resolve pages, stacked in one grid cell exactly like .fbeats —
   one thing per screen is the whole design, and the second line is a page
   of its own, not a caption under the first. */
.fresolve{width:100%;max-width:330px;text-align:center;display:grid}
.fr-card{grid-area:1/1;margin:0;font-family:var(--serif);text-wrap:pretty}
.fr-1{font-size:clamp(24px,7vw,29px);line-height:1.26;letter-spacing:-.01em;color:var(--cream)}
/* Smaller than the line before it, but serif and near-cream: it reads as
   the next page, never as small print. Its 31 words at the .fr-1 size
   would run seven lines of 29px and crowd the orb off a short phone. */
.fr-2{font-size:clamp(19px,5.4vw,23px);line-height:1.42;color:var(--body)}

/* nsBeat is the house in-hold-out cycle (see .f1/.f2). fr-1 uses it and
   clears the screen at 18.21s; fr-2 arrives after and simply stays, because
   the view's own fade is what takes it away. */
#vFilm.on .fr-1{animation:nsBeat 4.60s ease 13.61s both}
#vFilm.on .fr-2{animation:nsUp .9s var(--enter) 18.21s both}

/* ═══════════════ B. HOME ═══════════════ */

#vHome.on,#vSetup.on,#vCer.on,#vFin.on{overflow-y:auto}

.home{
  position:relative;min-height:100dvh;
  padding:calc(22px + env(safe-area-inset-top)) 24px calc(40px + env(safe-area-inset-bottom));
}

.h-logo{display:flex;align-items:center;gap:10px}
#vHome.on .h-logo{animation:nsUp .7s var(--enter) both}
.h-logo img{display:block}
.h-logo .ic{width:28px;height:auto}
.h-logo .wd{height:18px;width:auto}

.h-h1{
  margin:40px 0 0;font-family:var(--serif);
  font-size:clamp(38px,11.6vw,48px);line-height:1.04;font-weight:400;
  letter-spacing:-.02em;text-wrap:pretty;
}
#vHome.on .h-h1{animation:nsUp .8s var(--enter) .1s both}
/* The first clause is the setup, the second is the punchline AND the
   product name, so the light goes on the second. It also takes its own
   line: "Get Nessi." is an instruction, and an instruction should not
   trail off the end of the sentence that set it up. */
.h-h1 .lead{color:var(--muted)}
.h-h1 .punch{display:block;color:var(--cream)}

/* Two paragraphs doing two different jobs: the pitch, then the plain
   "here is what this actually is". Run together at one size and one
   grey they were six uniform lines and read as a wall, so the pitch
   speaks at body brightness and the description sits under it quieter
   and a step smaller. */
.h-sub{margin:20px 0 0;font-size:17.5px;line-height:1.5;color:var(--body);max-width:21em;text-wrap:pretty}
.h-what{margin:13px 0 0;font-size:15.5px;line-height:1.5;color:var(--muted);max-width:21em;text-wrap:pretty}
#vHome.on .h-sub{animation:nsUp .8s var(--enter) .2s both}
#vHome.on .h-what{animation:nsUp .8s var(--enter) .26s both}

/* Serif, one size up from the body around it. This is the emotional
   beat before the plan, and it is the only thing standing between the
   headline and a long run of sans — it has to read as a landmark, not
   as a third paragraph. Same setup/payoff shape as the h1: the busier
   world is stated quietly, what Nessi does about it carries the light. */
.h-guide{
  margin:30px 0 0;font-family:var(--serif);
  font-size:21px;line-height:1.36;letter-spacing:-.01em;
  color:var(--cream);max-width:20em;text-wrap:pretty;
}
.h-guide .lead{color:var(--muted)}
.h-guide .punch{color:var(--cream)}
#vHome.on .h-guide{animation:nsUp .8s var(--enter) .32s both}

.h-plan{margin:32px 0 0;padding:0;list-style:none;display:flex;flex-direction:column;gap:16px}
#vHome.on .h-plan{animation:nsUp .8s var(--enter) .38s both}
.h-plan li{display:flex;gap:14px;align-items:flex-start}
/* Filled, not just outlined. Three faint rings could not hold the block
   together against the prose above it; a countable 1-2-3 can, and teal
   is already the right colour for it — this is Nessi's side of the deal. */
.h-plan .n{
  flex-shrink:0;width:26px;height:26px;border-radius:50%;
  border:1px solid rgba(127,223,219,.3);background:rgba(127,223,219,.09);
  display:flex;align-items:center;justify-content:center;
  font-size:13px;font-weight:600;color:var(--teal);
}
.h-plan .t{font-size:16px;line-height:1.5;color:var(--body);text-wrap:pretty}
/* An annotation on the question, not another clause of it. Dropping it
   a shade stops the parenthetical from reading as part of the step. */
.h-plan .hint{color:var(--faint)}

.cta{
  width:100%;min-height:58px;padding:18px 22px;border:0;border-radius:16px;
  background:var(--grad-cta);color:var(--coral-text);
  font-family:inherit;font-size:17px;font-weight:600;letter-spacing:-.01em;
  cursor:pointer;box-shadow:0 14px 36px rgba(226,96,58,.26);
}
.cta:hover{filter:brightness(1.07)}
/* The margin lives on the element, not on the .on rule: without JS,
   #vHome never gets .on and the button was landing flush against the
   plan. */
.h-cta{margin:32px 0 0}
#vHome.on .h-cta{animation:nsUp .8s var(--enter) .46s both}

.h-alt{margin:16px 0 0;text-align:center;font-size:14px;color:var(--faint)}
#vHome.on .h-alt{animation:nsFade .8s ease .6s both}

/* Desktop bridge note. Shown by media query alone — no JS. */
.h-note{
  display:none;margin:22px 0 0;padding:14px 16px;border-radius:12px;
  border:1px solid var(--line-2);text-align:center;
  font-size:13.5px;line-height:1.5;color:var(--muted);
}
@media (min-width:900px){.h-note{display:block}}

/* Social proof, in place of the drawn lock screen. The glass chips in the
   film are mood; this is other people. Monogram avatars rather than
   photographs on purpose — see the note in index.html. */
.h-social{margin:56px 0 0;padding-top:34px;border-top:1px solid var(--line)}
#vHome.on .h-social{animation:nsFade 1s ease .8s both}

.h-social-h{
  margin:0 0 20px;font-family:var(--serif);font-weight:400;
  font-size:23px;line-height:1.32;color:var(--cream);text-wrap:pretty;
}

/* The star is defined once as a <symbol> and referenced five times per
   card. The sprite must not paint or take space, and must not be
   display:none — Safari has historically dropped <use> references into a
   fully hidden tree. */
.sprite{position:absolute;width:0;height:0;overflow:hidden}

.rvlist{display:flex;flex-direction:column;gap:14px}

.rv{
  margin:0;padding:17px 18px 15px;
  border:1px solid var(--line-3);border-radius:16px;background:var(--raised);
}

.rv-stars{display:flex;gap:3px}
/* Coral, not gold: in this system coral is the user's own side, and a
   review is the user speaking. There is no amber in the palette and this
   is not the place to introduce one. */
.rv-star{width:14px;height:14px;display:block;fill:var(--coral)}

.rv-q{margin:11px 0 0;font-size:15.5px;line-height:1.55;color:var(--body);text-wrap:pretty}
.rv-q p{margin:0}

.rv-by{margin-top:14px;display:flex;align-items:center;gap:10px}
.rv-av{
  flex-shrink:0;width:32px;height:32px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  font-size:12px;font-weight:600;letter-spacing:.04em;color:var(--body);
}
/* Four tints. They were four steps on one neutral ramp, which at 32px
   read as one avatar printed four times — the whole point of a monogram
   is that four people are speaking. Separated by hue instead of by
   lightness, held at roughly a tenth of the chroma of teal or coral so
   the accents stay spent on Nessi and on acting. */
.rv-av-a{background:#2B333F;color:#C7D2E1}
.rv-av-b{background:#3A342D;color:#E2D8C8}
.rv-av-c{background:#26362F;color:#C5DBD0}
.rv-av-d{background:#37303B;color:#DBCDDF}
.rv-name{font-size:14px;font-weight:500;color:var(--cream)}

/* The one outbound link. The opt-out switch lives on /privacy; the page
   must not strand it. */
.foot{margin-top:48px;padding-top:18px;border-top:1px solid var(--line);font-size:13px;color:var(--faint)}
.foot a{color:var(--muted)}
.foot a:hover{color:var(--cream)}

/* ═══════════════ C. SETUP ═══════════════
   Nothing is validated and nothing is required except the job choice,
   which arrives pre-selected. The submit is a local stage change for
   now — the workspace endpoint lands in a later pass (see app.js). */

.setup{
  position:relative;min-height:100dvh;
  padding:calc(24px + env(safe-area-inset-top)) 24px calc(40px + env(safe-area-inset-bottom));
  display:flex;flex-direction:column;
}
.s-back{
  align-self:flex-start;margin-bottom:34px;padding:8px 0;border:0;
  background:transparent;color:var(--faint);font-family:inherit;font-size:14px;
  cursor:pointer;
}
.s-back:hover{color:var(--cream)}

.s-lab{display:block;font-size:17px;font-weight:500;color:var(--cream)}
.s-in{
  margin-top:12px;width:100%;min-height:54px;padding:15px 18px;
  border-radius:14px;border:1px solid var(--line-4);background:var(--raised);
  color:var(--cream);font-size:16px;outline:none;
}
.s-in:focus{border-color:var(--teal);background:var(--field-focus)}
.s-help{margin:10px 0 0;font-size:13.5px;line-height:1.5;color:var(--faint)}

.s-name{}
.s-jobs{margin-top:34px}
.s-phone{margin-top:34px}
#vSetup.on .s-name{animation:nsUp .7s var(--enter) both}
#vSetup.on .s-jobs{animation:nsUp .7s var(--enter) .08s both}
#vSetup.on .s-phone{animation:nsUp .7s var(--enter) .16s both}

.joblist{margin-top:12px;display:flex;flex-direction:column;gap:10px}
.job{
  position:relative;width:100%;min-height:54px;padding:15px 18px;
  border:1px solid #1D252E;border-radius:14px;background:var(--raised);
  color:var(--body);font-family:inherit;font-size:16px;text-align:left;
  cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:12px;
}
.job:hover{border-color:var(--line-hover)}
.job .ck{
  flex-shrink:0;width:20px;height:20px;border-radius:50%;background:var(--teal);
  display:flex;align-items:center;justify-content:center;opacity:0;
}
.job.on .ck{opacity:1}
.job .ring{position:absolute;inset:-1px;border-radius:14px;border:1.5px solid var(--teal);pointer-events:none;opacity:0}
.job.on .ring{opacity:1}

.s-spacer{flex:1;min-height:40px}
.s-cta{margin-top:34px}
#vSetup.on .s-cta{animation:nsUp .7s var(--enter) .24s both}

/* The country selector. A REAL <select> — on iOS that is the native
   wheel picker, keyboard type-ahead and free accessibility — laid over
   the whole drawn row at opacity 0 (opacity does not affect
   hit-testing, so it is still what a tap lands on; font-size stays at
   16px for iOS's focus-zoom threshold). The drawn row exists because a
   native select renders its selected option's own text, which truncates
   the dial code first — and the dial code is the whole reason the
   control exists. Only the name truncates; flag, dial and chevron are
   shrink-0. The drawn row is aria-hidden — it duplicates what the
   select already announces. Ported from nessi-ui's CountrySelect. */
.s-selrow{
  position:relative;margin-top:12px;display:flex;align-items:center;gap:10px;
  min-height:54px;padding:15px 18px;border-radius:14px;
  border:1px solid var(--line-4);background:var(--raised);
}
.s-selrow:focus-within{border-color:var(--teal);background:var(--field-focus)}
.cs-flag{flex-shrink:0;font-size:20px;line-height:1}
.cs-name{
  min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  color:var(--body);font-size:16px;
}
.cs-dial{flex-shrink:0;color:var(--body);font-size:16px}
.cs-chev{flex-shrink:0;width:16px;height:16px;color:var(--muted)}
#country{
  position:absolute;inset:0;width:100%;height:100%;
  opacity:0;appearance:none;-webkit-appearance:none;border:0;padding:0 18px;
  background:transparent;font-family:inherit;font-size:16px;cursor:pointer;
}
#country:disabled{cursor:not-allowed}
/* The visibly-disabled state for a typed + (the ignoring must be
   visible, never silent). */
.s-selrow.dis .cs-flag,.s-selrow.dis .cs-name,.s-selrow.dis .cs-dial{opacity:.55}

/* The phone field's validation states. Phone is required: an invalid
   entry shows one short line in the semantic red (never the brand
   coral — an error must not read as marketing) and does not advance. */
.s-in[aria-invalid="true"]{border-color:var(--bad)}
.s-in[aria-invalid="true"]:focus{border-color:var(--bad)}
.s-err{margin:10px 0 0;font-size:13.5px;line-height:1.5;color:var(--bad)}

/* ═══════════════ D1. CEREMONY ═══════════════
   Five quiet checklist lines, ~7.4s, then the end card and the
   handoff. No terminal, no agent id — the spec bans both. */

.cer{
  position:relative;min-height:100dvh;
  padding:calc(24px + env(safe-area-inset-top)) 28px calc(40px + env(safe-area-inset-bottom));
  display:flex;flex-direction:column;justify-content:center;
}
.c-orb{position:relative;align-self:center;width:76px;height:76px;margin-bottom:44px}
#vCer.on .c-orb{animation:nsFade .8s ease both}
.c-orb-b{position:absolute;inset:0;animation:nsBreathe 5s ease-in-out infinite}
.c-glow{position:absolute;inset:-20px;border-radius:50%;background:radial-gradient(circle,rgba(45,211,205,.16) 0%,rgba(45,211,205,0) 70%)}
.c-ring{position:absolute;inset:0;border-radius:50%;border:1px solid rgba(45,211,205,.22)}
.c-icon{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}
.c-icon img{width:36px;height:auto;display:block}

.c-list{display:flex;flex-direction:column;gap:22px;max-width:21em;width:100%;align-self:center}
.c-row{display:flex;gap:14px;align-items:center;font-size:16px;line-height:1.45;color:var(--body)}
.c-ck{position:relative;flex-shrink:0;width:21px;height:21px}
.c-hole{position:absolute;inset:0;border-radius:50%;border:1px solid var(--line-4)}
.c-ck svg{position:absolute;inset:0;opacity:0}
#vCer.on .c-row.r1{animation:nsFade .6s ease .2s both}
#vCer.on .c-row.r2{animation:nsFade .6s ease 1.1s both}
#vCer.on .c-row.r3{animation:nsFade .6s ease 2s both}
#vCer.on .c-row.r4{animation:nsFade .6s ease 2.9s both}
#vCer.on .c-row.r5{animation:nsFade .6s ease 3.8s both}
#vCer.on .c-row.r1 .c-ck svg{animation:nsPop .5s var(--pop) 1s both}
#vCer.on .c-row.r2 .c-ck svg{animation:nsPop .5s var(--pop) 1.9s both}
#vCer.on .c-row.r3 .c-ck svg{animation:nsPop .5s var(--pop) 2.8s both}
#vCer.on .c-row.r4 .c-ck svg{animation:nsPop .5s var(--pop) 3.7s both}
#vCer.on .c-row.r5 .c-ck svg{animation:nsPop .5s var(--pop) 4.6s both}
#vCer.on .c-row.r1 .c-ck path{animation:nsCheck .45s ease 1.1s both}
#vCer.on .c-row.r2 .c-ck path{animation:nsCheck .45s ease 2s both}
#vCer.on .c-row.r3 .c-ck path{animation:nsCheck .45s ease 2.9s both}
#vCer.on .c-row.r4 .c-ck path{animation:nsCheck .45s ease 3.8s both}
#vCer.on .c-row.r5 .c-ck path{animation:nsCheck .45s ease 4.7s both}

.c-end{margin-top:44px;max-width:21em;width:100%;align-self:center}
#vCer.on .c-end{animation:nsUp .8s var(--enter) 5.2s both}
.c-end-t{font-family:var(--serif);font-size:26px;line-height:1.3;letter-spacing:-.01em}
.c-end-s{margin-top:10px;font-size:15.5px;line-height:1.5;color:var(--muted);text-wrap:pretty}
.c-meta{margin-top:22px;font-size:13px;letter-spacing:.04em;color:var(--ghost)}

/* ═══════════════ D2. HANDOFF ═══════════════ */

.fin{
  position:relative;min-height:100dvh;
  padding:calc(30px + env(safe-area-inset-top)) 24px calc(40px + env(safe-area-inset-bottom));
  display:flex;flex-direction:column;
}
.live{display:flex;align-items:center;gap:9px}
#vFin.on .live{animation:nsFade .7s ease both}
.live-dot{width:7px;height:7px;border-radius:50%;background:var(--teal);animation:nsDot 2.6s ease-in-out infinite}
.live-lbl{font-size:12px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--faint)}

.finh{
  margin:20px 0 0;font-family:var(--serif);
  font-size:clamp(28px,8.4vw,34px);line-height:1.15;font-weight:400;
  letter-spacing:-.015em;text-wrap:pretty;
}
#vFin.on .finh{animation:nsUp .8s var(--enter) .1s both}
.finh .soft{color:var(--muted)}

.fprev{margin-top:32px;border-radius:20px;border:1px solid var(--line-2);background:#0B1015;overflow:hidden}
#vFin.on .fprev{animation:nsUp .8s var(--enter) .2s both}
.fprev-to{padding:14px 16px;border-bottom:1px solid var(--line);display:flex;gap:8px;align-items:baseline}
.fprev-to .k{font-size:13.5px;color:var(--faint)}
.fprev-to .v{font-size:15px;font-weight:500;color:var(--cream)}
.fprev-body{padding:18px 16px 20px;display:flex;justify-content:flex-end}
.bubble{
  max-width:88%;padding:12px 16px;border-radius:20px 20px 6px 20px;
  background:linear-gradient(150deg,#2E9BFF,#1478E8);
  font-size:15.5px;line-height:1.45;color:#FFF;text-wrap:pretty;
}
.caret{
  display:inline-block;width:2px;height:1.05em;margin-left:1px;
  vertical-align:-.18em;background:#FFF;border-radius:1px;
  animation:nsCaret 1.05s steps(1,end) infinite;
}
.caret.off{display:none}

.f-spacer{flex:1;min-height:32px}

.send-wrap{position:relative}
#vFin.on .send-wrap{animation:nsUp .8s var(--enter) .3s both}
.halo{
  position:absolute;inset:0;border-radius:999px;
  border:1.5px solid rgba(253,131,96,.65);pointer-events:none;
  animation:nsHalo 2.6s ease-out .6s infinite;
}
.halo.h2a{border-color:rgba(253,131,96,.45);animation-delay:1.9s}
.send-a{
  position:relative;display:flex;align-items:center;justify-content:space-between;
  gap:14px;min-height:66px;padding:12px 12px 12px 24px;border-radius:999px;
  background:linear-gradient(112deg,#FFA07C 0%,#FD8360 42%,#E2603A 100%);
  color:var(--send-dark);font-size:17px;font-weight:600;letter-spacing:-.01em;
  overflow:hidden;animation:nsLift 3.4s ease-in-out 1s infinite;
}
.send-a:hover{filter:brightness(1.06);color:var(--send-dark)}
.sweep{
  position:absolute;top:0;bottom:0;left:0;width:46%;
  background:linear-gradient(100deg,rgba(255,255,255,0) 0%,rgba(255,255,255,.42) 50%,rgba(255,255,255,0) 100%);
  pointer-events:none;animation:nsSweep 3.4s cubic-bezier(.5,0,.5,1) 1s infinite;
}
.send-l{position:relative;text-align:left;line-height:1.25}
.send-ic{
  position:relative;flex-shrink:0;width:42px;height:42px;border-radius:50%;
  background:var(--send-dark);display:flex;align-items:center;justify-content:center;
}
.send-ic svg{width:19px;height:19px;animation:nsNudge 3.4s ease-in-out 1s infinite}

.fine{margin:16px 0 0;text-align:center;font-size:13.5px;line-height:1.5;color:var(--faint)}
#vFin.on .fine{animation:nsFade .8s ease .5s both}

/* Desktop only: sms: is inert on most desktops, so hand them a way
   across to the phone with the message already written. Rendered by
   app.js (qr.v6.js — never a third-party QR service), and .ready is
   only set on a successful render. */
.qrwrap{
  display:none;margin-top:26px;padding:22px;border-radius:18px;
  border:1px solid var(--line-2);align-items:center;gap:18px;
}
.qrwrap.ready{display:flex}
.qr{flex-shrink:0;width:104px;height:104px;border-radius:10px;background:#F4EFE7;display:flex;align-items:center;justify-content:center}
.qr svg{width:96px;height:96px;display:block}
.qrcap{font-size:14px;line-height:1.55;color:var(--muted);text-wrap:pretty}

/* ── reduced motion ─────────────────────────────────────
   Not the prototype's global collapse. The film is skipped outright
   in app.js; the plain entrances on home/setup/handoff resolve
   instantly; the ambient loops stop. The ceremony keeps its stagger —
   five quiet fades are the narrative, and collapsing them onto one
   frame removes the story rather than the motion. The typewriter
   keeps running: it is content, not decoration. */

@media (prefers-reduced-motion: reduce){
  #vHome.on *,#vSetup.on *,#vFin.on *{
    animation-duration:.01ms !important;
    animation-delay:0s !important;
  }
  .chips,.o-ring,.o-pulse,#vFilm.on .orb-b,.c-orb-b,.live-dot,.halo,.sweep,.send-a,.send-ic svg{
    animation:none !important;
  }
  .caret{animation:none !important}
}
