/* ============================================================
   THE NOVA SIGNUP-FUNNEL DESIGN SYSTEM
   ★ Loaded by TWO pages now. Do not fork it for a third. ★

     assistant-signup.html   Nova Assistant  (ai.usenovaos.com)
     os-signup.html          Nova OS         (app.usenovaos.com)
                             + assets/css/os-signup.css on top, which adds the
                               .nqo-* pieces Nova OS needs and redefines nothing

   2026-08-16 — WHY IT IS SHARED NOW.
   Vincent, on the old Nova OS onboarding (signup.html, the dark statue
   configurator): "way too complex, way too much... copy the exact onboarding
   we have for Nova Assistant." The literal way to do that is to load this
   file, not to reimplement it. Everything below — the token layer, .nas-*,
   the whole .nq-* quiz shell — is therefore product-neutral. Anything that is
   NOT is in the product's own second stylesheet.

   WHY IT EXISTS AT ALL
   --------------------
   `signup.html` + `onboard.css` are the OLD Nova OS configurator: marble bust,
   "Let's build your Nova OS", work email, phone number. It still serves the
   magic links already sitting in people's inboxes, so it is untouched.
   A $39/mo personal AI assistant needed its own door, and this was it.

   2026-08-14 — RETUNED TO THE LIVE MARKETING BRAND.
   usenovaos.com/assistant was rebuilt from the Pencil comp into a LIGHT cyan
   system. This page used to be the older dark blue-black variant, so a buyer
   clicking "Try Nova" fell off a brightly lit page onto a black one and had to
   ask whether they were still on the same product. Same tokens as the marketing
   page now: #F2FCFF field, Exo 2 italic 800 for display, Inter Tight for the
   voice, one cyan gradient for the single primary action.

   HARD RULE — NO PURPLE / VIOLET / INDIGO ANYWHERE. Vincent, 2026-08-03:
   "Don't make it purple — that looks terrible. Use our normal colors."

   THE PLAN CARDS ARE NOT DRAWN HERE.
   They are `/b/fb54b3a94dac/assets/js/chat/plans.js` — the ONE plan component, shared with the
   onboarding chooser and Settings → Billing. That component is written against
   the app's token names, so the bridge at the bottom of this file re-points
   those names at the marketing palette. Same markup, same component, this
   page's clothes. Never fork a second set of plan cards.

   CSP: script-src is 'self' with NO 'unsafe-inline' on this host, so every
   behaviour lives in assets/js/assistant-signup-boot.js. Inline <style> is
   allowed (style-src carries 'unsafe-inline'); inline <script> is NOT.
   ============================================================ */

/* ---- the token layer -----------------------------------------------------
   This page ships on its own host and does not load tokens.css, so its scale
   lives here. 1:1 with the live /assistant marketing page — if a value here
   drifts from that page, this file is the one that is wrong. */
:root {
  /* ---- ink ---- */
  --ink: #0A1220;          /* headlines, the darkest thing on the page */
  --mut: #44536B;          /* body copy */
  --sub: #607086;          /* secondary copy */
  --faint: #8A97AB;        /* captions, fine print */

  /* ---- cyan ---- */
  --cy: #7FE0FF;
  --cy-lo: #9BE9FF;
  --cy-mid: #6FDCFF;
  --cy-pale: #CFF3FF;
  --cy-deep: #0E7FA6;      /* cyan that is legible AS TEXT on white */

  /* ---- surfaces ---- */
  --field: #F2FCFF;        /* the page itself */
  --paper: #FFFFFF;        /* cards */
  --line: #E1E8F0;
  --line-2: #EDF3F8;
  --tint: #F0FAFE;
  --dk: #06121D;           /* ink that sits ON the cyan gradient */
  --dk-card: #0C1B29;

  --grad: linear-gradient(110deg, #ABEEFF 31.5%, #6FDCFF 68.5%);
  --glow: 0 10px 30px -10px rgba(58, 190, 232, .55);
  --shadow-card: 0 1px 2px rgba(10, 18, 32, .04), 0 12px 32px -20px rgba(10, 18, 32, .28);
  --shadow-lift: 0 2px 4px rgba(10, 18, 32, .05), 0 22px 48px -26px rgba(10, 18, 32, .34);

  --ok: #0E7A5F;
  --ok-bg: #E6F7F1;
  --err: #B3261E;
  --err-bg: #FDECEA;
  --warn: #8A5A00;
  --warn-bg: #FFF4DE;

  /* ---- type ---- */
  --fs-root: 16px;
  --fs-hero: clamp(2.35rem, 5.6vw, 3.9rem);
  --fs-lede: clamp(1.02rem, 1.6vw, 1.16rem);
  --fs-h2: clamp(1.5rem, 2.6vw, 1.9rem);
  --fs-h2-card: 1.45rem;
  --fs-h3: 1.1rem;
  --fs-ctl: 1rem;
  --fs-md: .95rem;
  --fs-body: .92rem;
  --fs-sm: .875rem;
  --fs-note: .85rem;
  --fs-fine: .8rem;
  --fs-eyebrow: .72rem;
  --fs-label: .7rem;

  --disp: "Exo 2", ui-sans-serif, system-ui, sans-serif;
  --sans: "Inter Tight", ui-sans-serif, system-ui, -apple-system, sans-serif;

  --ease: cubic-bezier(.22, 1, .36, 1);
  --dur-pulse: 1.9s;

  --r-pill: 100px;
  --r-card: 20px;
  --r-ctl: 14px;
  --r-4: 4px;
  --r-12: 12px;
  --r-14: 14px;
  --r-16: 16px;
  --r-18: 18px;
  --r-22: 22px;
  --z-cta: 5;
  --z-dw-fade: 2;
  --z-confetti-burst: 3;                         /* over the page, under --z-cta */
  --fs-jumbo: 2.4rem;
  --fs-brand: 1.02rem;
  --fs-h-quiz: clamp(1.7rem, 7vw, 2.1rem);
  --fs-h-celebrate: clamp(2.5rem, 13vw, 3.6rem); /* the one screen allowed to shout */
  --fs-stat: 1.3rem;
  --fs-stat-big: clamp(1.9rem, 8.5vw, 2.5rem);   /* the personality success beat's three numbers */
  --r-10: 10px;
  --r-3: 3px;
  --fs-code: 1.5rem;
  --fs-scrub-btn: 1.25rem;  /* the check-in time scrubber's +/- buttons */
  --fs-scrub-lg: 1.8rem;    /* the check-in time scrubber's hour/minute digits */
  --fs-colon: 1.4rem;       /* the ":" between them */
  --fs-goal: 1.12rem;
  --cy-bright: #ABEEFF;
  --choice-on-bg: #F4FDFF;
  --switch-off: #D9E3EC;
  --spark-mid: #E5EEF4;
  --dark-surface: #13283A;
  --dark-border: #1D3850;
  --dark-text: #DCEFF9;
}

.nas {
  -webkit-tap-highlight-color: transparent;
  margin: 0;
  min-height: 100dvh;
  background: var(--field);
  color: var(--mut);
  font-family: var(--sans);
  font-size: var(--fs-root);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.nas *, .nas *::before, .nas *::after { box-sizing: border-box; }
/* :not(input):not(textarea) — text fields already carry their own radius
   (--r-ctl, 14px) via .nas-input; without this exclusion every input/textarea
   snapped to the generic --r-4 (4px) the instant it was clicked, reading as
   "going square" on focus. */
.nas :focus-visible:not(input):not(textarea) { outline: 2px solid var(--cy-deep); outline-offset: 3px; border-radius:var(--r-4); }
/* Text fields do NOT get the deep-cyan outline. .nas-input:focus already paints
   a cyan border plus a 3px glow ring; stacking the outline on top of that drew
   a second, darker ring 3px outside the glow — Vincent, 2026-08-15, on the name
   field: "remove the extra dark blue border." The border+glow IS the focus
   indicator (same call already made for .nq-code input). */
.nas input:focus-visible, .nas textarea:focus-visible { outline: none; }
/* the sliders have no border/glow of their own, so they keep the ring */
.nas input[type="range"]:focus-visible { outline: 2px solid var(--cy-deep); outline-offset: 3px; }
.nas [hidden] { display: none !important; }

/* ---- atmosphere ---------------------------------------------------------
   ONE continuous wash for the whole zone, fixed behind the content — never a
   gradient per section, which is what makes a long page look like four pages
   stapled together. */
.nas-scene {
  position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden;
  background:
    radial-gradient(62vw 48vw at 8% -10%, rgba(127, 224, 255, .34), transparent 62%),
    radial-gradient(54vw 44vw at 96% 2%, rgba(171, 238, 255, .40), transparent 60%),
    radial-gradient(80vw 56vw at 50% 108%, rgba(207, 243, 255, .55), transparent 66%);
}

.nas-page { position: relative; z-index: 1; }

/* ---- chrome ---- */
.nas-top {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 22px clamp(18px, 5vw, 56px);
}
.nas-mark { display: flex; align-items: center; gap: 10px; }
.nas-mark img { height: 20px; width: auto; display: block; }
.nas-mark b {
  font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-eyebrow); letter-spacing: .1em; text-transform: uppercase;
  color: var(--sub); padding-left: 11px; border-left: 1px solid var(--line);
}
.nas-topact { font-size: var(--fs-note); color: var(--sub); }
.nas-topact a { color: var(--cy-deep); text-decoration: none; font-weight: 600; }
.nas-topact a:hover { text-decoration: underline; }

.nas-wrap { max-width: 1080px; margin: 0 auto; padding: 0 clamp(18px, 5vw, 56px) 80px; }

/* ---- hero ---- */
.nas-eyebrow {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-eyebrow); letter-spacing: .13em; text-transform: uppercase;
  color: var(--cy-deep);
  padding: 7px 15px; border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--paper);
}
.nas-h1 {
  margin: 22px 0 0; max-width: 17ch; color: var(--ink);
  font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-hero); line-height: 1.02; letter-spacing: -.02em;
}
.nas-h1 em { font-style: italic; color: var(--cy-deep); }
.nas-lede { margin: 20px 0 0; max-width: 56ch; font-size: var(--fs-lede); color: var(--mut); }
.nas-lede b { color: var(--ink); font-weight: 620; }

/* ---- the two halves of the product ---- */
.nas-sides { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 40px; }
.nas-side {
  border: 1px solid var(--line); border-radius: var(--r-card);
  background: var(--paper); box-shadow: var(--shadow-card);
  padding: 24px 24px 22px;
}
.nas-side h2 {
  margin: 0 0 4px; color: var(--ink);
  font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-h3); letter-spacing: -.01em;
}
.nas-side p { margin: 0 0 16px; font-size: var(--fs-body); color: var(--sub); }
.nas-side ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 9px; }
.nas-side li {
  display: grid; grid-template-columns: 16px 1fr; gap: 10px; align-items: start;
  font-size: var(--fs-sm); color: var(--mut);
}
.nas-side li svg { width: 16px; height: 16px; margin-top: 4px; color: var(--cy-deep); }
.nas-side li b { color: var(--ink); font-weight: 620; }

/* ---- section heads ---- */
.nas-sec-h {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  margin: 52px 0 18px; flex-wrap: wrap;
}
.nas-sec-h h2 {
  margin: 0; color: var(--ink);
  font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-h2); letter-spacing: -.02em;
}
.nas-sec-h span { font-size: var(--fs-note); color: var(--sub); }

/* Honest small print about what a credit is + what happens at the edge. */
.nas-meter {
  margin-top: 14px; padding: 14px 16px; border-radius: var(--r-ctl);
  border: 1px solid var(--line); background: var(--tint);
  font-size: var(--fs-note); color: var(--mut);
}
.nas-meter b { color: var(--ink); font-weight: 620; }

/* ---- the card (form / status) ---- */
.nas-card {
  margin-top: 32px; border: 1px solid var(--line); border-radius: var(--r-card);
  background: var(--paper); box-shadow: var(--shadow-lift);
  padding: clamp(24px, 3.4vw, 36px);
  max-width: 620px;
}
.nas-card h2 {
  margin: 0 0 6px; color: var(--ink);
  font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-h2-card); letter-spacing: -.02em;
}
.nas-card h2 em { font-style: italic; color: var(--cy-deep); }
.nas-card > p { margin: 0 0 20px; font-size: var(--fs-md); color: var(--sub); }

.nas-field { display: block; margin-bottom: 14px; }
/* Terms + Privacy agreement (rEmail) — the SAME custom checkbox component
   as signup.html's .o4-consent (main Nova OS signup), re-toned from that
   page's dark palette to this one's light cyan system rather than left as
   a bare `accent-color` native box. Vincent, 2026-08-19, on the native-box
   version: "the UI is fucking terrible... take the component from the
   normal Nova website... literally that same component." Required to
   continue — this is Terms/Privacy agreement, not an opt-in marketing ask,
   so gating signup on it is standard, not a dark pattern. */
.nq-consent {
  display: flex; align-items: flex-start; gap: 10px; margin-top: 4px;
  font-size: var(--fs-fine); line-height: 1.5; color: var(--sub); cursor: pointer;
}
.nq-consent input {
  appearance: none; -webkit-appearance: none; flex: none; width: 24px; height: 24px;
  margin: 0; border-radius: var(--r-4); border: 1px solid var(--line); background: var(--paper);
  /* an invisible halo takes the touch target past 44px without moving pixels */
  outline: 12px solid transparent; outline-offset: 0;
  cursor: pointer; display: grid; place-items: center;
  transition: background .2s var(--ease), border-color .2s var(--ease);
}
.nq-consent input:checked { background: var(--grad); border-color: transparent; }
.nq-consent input:checked::after {
  content: ""; width: 11px; height: 11px; background: var(--dk);
  clip-path: polygon(14% 44%, 0 65%, 40% 100%, 100% 16%, 84% 0, 38% 66%);
}
.nq-consent input:focus-visible { outline: 2px solid var(--cy-mid); outline-offset: 2px; }
.nq-consent a { color: var(--cy-deep); text-decoration: none; }
.nq-consent a:hover { text-decoration: underline; }
.nas-flabel {
  display: block; margin-bottom: 7px;
  font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-label); letter-spacing: .12em; text-transform: uppercase;
  color: var(--sub);
}
.nas-input {
  width: 100%; height: 52px; padding: 0 18px;
  border-radius: var(--r-ctl); border: 1px solid var(--line);
  background: var(--paper); color: var(--ink);
  font: inherit; font-size: var(--fs-ctl);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.nas-input::placeholder { color: var(--faint); }
.nas-input:focus {
  outline: none; border-color: var(--cy-mid);
  box-shadow: 0 0 0 3px rgba(111, 220, 255, .3);
}
.nas-input[aria-invalid="true"] { border-color: var(--err); }

/* The one primary action, straight off the marketing page. */
.nas-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  padding: 17px 30px; border-radius: var(--r-ctl);
  border: 1.5px solid rgba(255, 255, 255, .7);
  background: var(--grad); color: var(--accent-fg);
  font-family: var(--disp); font-style: italic; font-weight: 800;
  /* Vincent, 2026-08-15: "make all the continue buttons CAPITALIZED." Set on
     the button, not in the label strings, so every primary action — the quiz
     CTA, the account form, the retry buttons — reads the same and no future
     label can drift out of it. Uppercase needs air, hence the positive
     tracking replacing the -.01em the sentence-case version wanted. */
  text-transform: uppercase;
  font-size: var(--fs-ctl); letter-spacing: .045em; line-height: 1;
  text-decoration: none;   /* the "sign in and continue" variant is an <a> */
  cursor: pointer;
  transition: transform .2s var(--ease), box-shadow .2s var(--ease), filter .2s var(--ease);
}
.nas-btn:hover:not(:disabled) { transform: translateY(-2px); box-shadow: var(--glow); }
.nas-btn:disabled { opacity: .5; cursor: not-allowed; transform: none; box-shadow: none; }
.nas-btn--block { width: 100%; }
.nas-btn--ghost {
  background: var(--paper); color: var(--ink); border: 1px solid var(--line);
  padding: 13px 22px; font-size: var(--fs-body);
}
.nas-btn--ghost:hover:not(:disabled) { border-color: var(--cy-mid); box-shadow: none; transform: translateY(-1px); }

.nas-acts { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.nas-acts .nas-btn--block { width: 100%; }

.nas-fine { margin: 14px 0 0; font-size: var(--fs-fine); color: var(--faint); display: flex; align-items: flex-start; gap: 8px; }
.nas-fine svg { width: 14px; height: 14px; margin-top: 3px; flex: none; }
.nas-hint { margin: 18px 0 0; font-size: var(--fs-note); color: var(--sub); }
.nas-hint a { color: var(--cy-deep); text-decoration: none; font-weight: 600; }
.nas-hint a:hover { text-decoration: underline; }

.nas-msg {
  display: none; margin-bottom: 16px; padding: 11px 15px; border-radius: var(--r-ctl);
  font-size: var(--fs-note); border: 1px solid transparent;
}
.nas-msg.show { display: block; }
.nas-msg--err { color: var(--err); border-color: rgba(179, 38, 30, .28); background: var(--err-bg); }
.nas-msg--ok { color: var(--ok); border-color: rgba(14, 122, 95, .28); background: var(--ok-bg); }
.nas-msg--warn { color: var(--warn); border-color: rgba(138, 90, 0, .28); background: var(--warn-bg); }

/* summary row shown on the confirm/status screens */
.nas-chip {
  display: inline-flex; align-items: center; gap: 8px; margin-bottom: 18px;
  padding: 7px 14px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--tint);
  font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-label); letter-spacing: .1em;
  text-transform: uppercase; color: var(--cy-deep);
}

.nas-live {
  width: 7px; height: 7px; border-radius: 50%; background: var(--cy-deep); flex: none;
  box-shadow: 0 0 0 0 rgba(14, 127, 166, .5); animation: nas-pulse var(--dur-pulse) infinite;
}
/* #cardBody's default first-paint content, before boot() replaces it — see
   assistant-signup.html's comment. Same .nas-live dot the rest of the page
   already uses for "this is live", just paired with plain loading copy. */
.nas-loading { display: flex; align-items: center; gap: 9px; margin: 0; font-size: var(--fs-note); color: var(--sub); }
@keyframes nas-pulse {
  70% { box-shadow: 0 0 0 9px rgba(14, 127, 166, 0); }
  100% { box-shadow: 0 0 0 0 rgba(14, 127, 166, 0); }
}

/* A determinate "we are handing you to Stripe" bar. Not a spinner: the buyer
   is about to leave the page and needs to know that is on purpose. */
.nas-bar { height: 4px; border-radius:var(--r-pill); background: var(--line-2); overflow: hidden; margin-top: 18px; }
.nas-bar i { display: block; height: 100%; width: 35%; border-radius:var(--r-pill); background: var(--grad); animation: nas-slide 1.1s var(--ease) infinite; }
@keyframes nas-slide { 0% { transform: translateX(-110%); } 100% { transform: translateX(320%); } }

/* ---- reveal ---- */
.nas-rv { opacity: 0; transform: translateY(12px); animation: nas-in .62s var(--ease) forwards; animation-delay: calc(var(--i, 0) * 70ms); }
@keyframes nas-in { to { opacity: 1; transform: none; } }

.nas-foot {
  margin: 46px 0 0; padding-top: 22px; border-top: 1px solid var(--line);
  font-size: var(--fs-fine); color: var(--faint);
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.nas-foot a { color: var(--sub); text-decoration: none; }
.nas-foot a:hover { color: var(--cy-deep); text-decoration: underline; }
.nas-foot-sep { width: 3px; height: 3px; border-radius: 50%; background: currentColor; opacity: .5; }

/* ============================================================
   THE PLAN-CARD TOKEN BRIDGE
   ------------------------------------------------------------
   assets/js/chat/plans.js injects its own unscoped `.np-*` stylesheet, written
   against the APP's token names (--surface-1, --accent, --text-100, --fs-xs…).
   This page does not load tokens.css. Rather than fork the component into a
   second set of plan cards that would immediately drift from the one Settings
   renders, we define those names here in the marketing palette. One component,
   two sets of clothes.
   ============================================================ */
.nas {
  --surface-0: var(--paper);
  --surface-1: var(--paper);
  --surface-2: var(--tint);
  --surface-3: var(--tint);
  --border: var(--line);
  --border-soft: var(--line-2);
  --border-strong: var(--line);
  --accent: var(--cy-deep);
  --accent-fg: var(--dk);
  --text-100: var(--ink);
  --text-200: var(--ink);
  --text-300: var(--mut);
  --text-400: var(--sub);
  --text-500: var(--faint);
  --r-sm: 10px; --r-md: 12px; --r-lg: var(--r-card); --r-pill: 100px;
  --sp-3: 12px; --sp-4: 16px; --sp-5: 20px;
  --fs-body: 1rem; --fs-sm: .875rem; --fs-xs: .82rem; --fs-2xs: .75rem; --fs-lg: 1.15rem;
  --shadow-md: var(--shadow-card);
  --shadow-xl: var(--shadow-lift);
}
/* The component's own look, nudged (not rewritten) to sit in the light system:
   a taller card, the brand gradient on the selected rail, display type on the
   price. Everything else is inherited from plans.js so the two stay in step. */
.nas .np-plans { gap: 16px; }
.nas .np-plan { padding: 24px; box-shadow: var(--shadow-card); gap: 10px;
  transition: transform .2s var(--ease), border-color .2s var(--ease), box-shadow .2s var(--ease); }
.nas .np-plan::before { height: 4px; background: var(--line); }
.nas .np-plan.is-pick:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); background: var(--paper); }
.nas .np-plan.is-sel { box-shadow: 0 0 0 2px var(--cy-mid), var(--shadow-lift); border-color: var(--cy-mid); }
.nas .np-plan.is-sel::before { background: var(--grad); }
.nas .np-name { font-family: var(--disp); font-style: italic; font-weight: 800; letter-spacing: -.01em; }
.nas .np-price b { font-family: var(--disp); font-style: italic; font-weight: 800; font-size:var(--fs-jumbo); }
.nas .np-badge { font-family: var(--disp); font-style: italic; }

/* ---- responsive ---- */
@media (max-width: 840px) {
  .nas-sides { grid-template-columns: 1fr; }
  .nas-h1 { max-width: none; }
}
@media (max-width: 480px) {
  .nas-top { padding: 18px 18px; }
  .nas-topact { font-size: var(--fs-fine); }
  .nas-btn { width: 100%; }
  .nas-card { padding: 22px 18px; }
}

@media (prefers-reduced-motion: reduce) {
  .nas-rv { animation: none; opacity: 1; transform: none; }
  .nas-live, .nas-bar i { animation: none; }
  .nas-plan:hover, .nas-btn:hover:not(:disabled), .nas .np-plan.is-pick:hover { transform: none; }
}

/* ============================================================
   THE QUIZ (#quiz / .nq-*) — the 17-screen setup funnel
   ------------------------------------------------------------
   A full-viewport, single-column takeover: one question a screen, a top brand
   bar with a live orb, a thin gradient progress bar, and a sticky bottom CTA.
   Mobile-first at 390px; on desktop the SAME column sits centered — it is
   never stretched into a desktop form. Everything here wears the page's own
   cyan tokens above; no new colors, no new fonts.
   ============================================================ */
body.nas--quiz .nas-top, body.nas--quiz .nas-wrap { display: none; }

.nq {
  position: relative; z-index: 1;
  display: flex; flex-direction: column;
  min-height: 100dvh; max-width: 480px;
  margin: 0 auto; padding: 0 22px;
}

/* ---- chrome: back · brand · step label ---- */
/* Nova centered, no step counter (Vincent: "just put Nova in the top middle
   and then the question") — .nq-back floats absolute so it never pushes the
   centered brand off-axis, present or not. */
.nq-top { display: flex; align-items: center; justify-content: center; position: relative; padding: 16px 0 13px; min-height: 40px; }
.nq-back {
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 40px; height: 40px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 1px solid var(--line); background: var(--paper);
  color: var(--ink); cursor: pointer; padding: 0;
  transition: border-color .2s var(--ease), transform .2s var(--ease), box-shadow .2s var(--ease);
}
.nq-back:hover { border-color: var(--cy-mid); transform: translateY(-50%) translateX(-2px); box-shadow: var(--shadow-card); }
.nq-brand { display: flex; align-items: center; }
/* Matches assets/css/layout.css .brand-word-img exactly — the real app's own
   wordmark sizing, not an approximation. */
.nq-mark { height: 17px; width: auto; object-fit: contain; display: block; }
.nq-step {
  margin-left: auto; font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-label); letter-spacing: .12em; text-transform: uppercase; color: var(--sub);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

.nq-progress { height: 5px; border-radius:var(--r-pill); background: var(--line-2); overflow: hidden; flex: none; }
.nq-progress i { display: block; height: 100%; width: 0; border-radius:var(--r-pill); background: var(--grad); transition: width .55s var(--ease); }

/* body scrolls under the sticky CTA; the padding keeps the last card clear of it */
.nq-body { flex: 1; padding: 22px 0 168px; }
.nq-body .nas-msg { margin: 0 0 14px; }

/* ---- headline scale (one question a screen earns a bigger voice) ----
   There is no eyebrow/kicker above the headline any more — Vincent,
   2026-08-15: "remove the smaller text above the HEADLINES." Seven screens
   carried one; the headline is the first thing on every screen now. */
.nq-h {
  margin: 0 0 10px; color: var(--ink); text-transform: uppercase;
  font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size:var(--fs-h-quiz); line-height: 1.02; letter-spacing: -.01em;
}
.nq-h em { font-style: italic; color: var(--cy-deep); }
.nq-sub { margin: 0 0 20px; font-size: var(--fs-md); line-height: 1.55; color: var(--sub); }
.nq-sub b { color: var(--ink); font-weight: 620; }

/* ---- sticky bottom CTA ---- */
.nq-cta {
  position: fixed; left: 50%; bottom: 0; transform: translateX(-50%);
  width: 100%; max-width: 480px; z-index: var(--z-cta);
  padding: 30px 22px calc(16px + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(180deg, rgba(242, 252, 255, 0), var(--field) 40%);
  pointer-events: none;
}
.nq-cta > * { pointer-events: auto; }
.nq-cta .nas-btn { width: 100%; min-height: 54px; }
/* .is-gated — the "what you get" screen holds its button back until the list
   has been read to the bottom (gateCtaOnScroll in the boot JS). Blurred and
   inert, so it reads as something arriving rather than something broken. */
.nq-cta.is-gated .nas-btn {
  filter: blur(7px); opacity: .4; transform: translateY(8px); pointer-events: none;
  transition: filter .45s var(--ease), opacity .45s var(--ease), transform .45s var(--ease);
}
.nq-cta .nas-btn { transition: transform .2s var(--ease), box-shadow .2s var(--ease), filter .45s var(--ease), opacity .45s var(--ease); }
/* .is-holding / .nq-hold-bar — the click-to-confirm hold (holdCtaThenGo in the
   boot JS): the first click locks the button and drains this bar over --hold
   before the screen advances. A pause you can SEE, not a dead button. Scoped
   to .is-holding so the base .nas-btn keeps its box model everywhere else. */
.nas-btn.is-holding { position: relative; overflow: hidden; pointer-events: none; transform: none; }
.nq-hold-bar {
  position: absolute; inset: 0; background: rgba(255, 255, 255, .22);
  transform-origin: left; pointer-events: none;
  animation: nq-hold-drain var(--hold, 4500ms) linear forwards;
}
@keyframes nq-hold-drain { from { transform: scaleX(1); } to { transform: scaleX(0); } }
.nq-cta-foot { margin: 10px 0 0; text-align: center; font-size: var(--fs-fine); color: var(--faint); line-height: 1.5; }
.nq-cta-link {
  display: block; width: max-content; margin: 8px auto 0; padding: 12px 14px; min-height: 44px;
  font-size: var(--fs-note); font-weight: 600; color: var(--sub);
  background: none; border: 0; cursor: pointer; font-family: inherit;
  text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--line);
  transition: color .2s var(--ease);
}
.nq-cta-link:hover { color: var(--cy-deep); text-decoration-color: currentColor; }

/* ---- choice cards (use case, voice presets) ---- */
/* 2026-08-15 spacing pass — Vincent on the personality screen: "please space
   this a bit better to make this easier." The list was four 74px cards 10px
   apart with 43px of copy inside 14px padding, so the cards read as one dense
   block instead of four things to choose between. Roomier padding, a wider
   gutter between card and card, and the sub-line pushed off the title. */
.nq-choice {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left;
  padding: 16px; margin: 0 0 14px; min-height: 78px; /* one card's copy running
  longer than its siblings must not make that one card taller — every choice
  card (use-case, voice preset) sits on this same floor. */
  border: 1px solid var(--line); border-radius:var(--r-18); background: var(--paper);
  box-shadow: var(--shadow-card); cursor: pointer; font: inherit; color: inherit;
  transition: border-color .2s var(--ease), background .2s var(--ease), box-shadow .2s var(--ease), transform .2s var(--ease);
}
.nq-choice:hover { border-color: var(--cy-mid); transform: translateY(-1px); }
.nq-choice.is-on { border-color: var(--cy-mid); background: var(--choice-on-bg); box-shadow: 0 0 0 1px var(--cy-mid), var(--shadow-card); }
.nq-cico {
  width: 38px; height: 38px; flex: none; border-radius:var(--r-12);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  background: var(--paper); border: 1px solid var(--line); color: var(--sub);
  transition: background .2s var(--ease);
}
.nq-choice.is-on .nq-cico { background: var(--cy-pale); border-color: var(--cy-mid); color: var(--cy-deep); }
.nq-cmain { min-width: 0; flex: 1; }
.nq-cmain strong { display: block; font-size: var(--fs-md); font-weight: 640; color: var(--ink); letter-spacing: -.01em; }
.nq-cmain span { display: block; margin-top: 4px; font-size: var(--fs-note); color: var(--sub); line-height: 1.45; }
.nq-check {
  width: 24px; height: 24px; flex: none; margin-left: auto;
  border-radius: 50%; border: 1.5px solid var(--border-strong); background: var(--paper); color: var(--accent-fg);
  display: flex; align-items: center; justify-content: center;
  transition: background .2s var(--ease), border-color .2s var(--ease);
}
.nq-check svg { opacity: 0; transform: scale(.4); transition: opacity .18s var(--ease), transform .18s var(--ease); }
.nq-choice.is-on .nq-check { background: var(--grad); border-color: transparent; }
.nq-choice.is-on .nq-check svg { opacity: 1; transform: none; }

/* ---- the reply-length card (2026-08-19) ----
   A MODIFIER on .nq-choice, not a second choice card: the border, the hover
   lift, the is-on fill and the tick are all inherited, so a length card and a
   use-case card stay the same object. All this changes is the axis — title row
   on top, the example bubble thread underneath — because the whole point of
   the screen is that you judge the option by reading the example, not the
   label (Vincent: "it needs to give an example of how long the responses are
   going to be"). min-height goes back to auto because these cards are
   deliberately different heights; that difference IS the information. */
.nq-choice--len { flex-direction: column; align-items: stretch; gap: 12px; min-height: 0; }
.nq-len-head { display: flex; align-items: center; gap: 12px; }
.nq-choice--len .nq-hook-bubble { max-width: 100%; }
/* The question, sitting above the four answers rather than inside any of them. */
.nq-lenq { margin: 0 0 16px; }

/* ---- voice/personality screen: expandable rows ---------------------------
   Third pass, 2026-08-18, off the white-card version. Vincent, looking at
   that: "make it actually bigger, make the characters actually cooler...
   it's so small right now I can't see it. Maybe make the cards dark, like
   make the characters bigger." So the whole row is now a dark stage (the
   same dark-radial-plus-hologram language the old fighter-card grid used,
   see the git history on this block, brought back but on a row that still
   expands) instead of a white .nq-choice-style strip — and the photo itself
   is roughly 2x bigger at both sizes. Text flips to light-on-dark to match.
   The message bubble is the OTHER half of that note: "use actual graphics
   of the same chat bubble graphics we've used before, put them here too" —
   this is the exact shape (rounded corners, one 4px corner for the tail)
   and shadow the real Nova chat (.v-bubble, views/ai-chat.js) and the
   marketing homepage's texting mockup (.bub) both already use; a crisp
   near-white bubble floating on the dark card reads as an actual incoming
   text instead of a UI hint box. */
.nq-arow {
  border: 1px solid rgba(111, 220, 255, .22); border-radius: var(--r-18);
  background:
    radial-gradient(120% 90% at 50% 108%, rgba(111, 220, 255, .16) 0%, rgba(111, 220, 255, 0) 58%),
    radial-gradient(95% 75% at 28% 30%, color-mix(in srgb, var(--cy-deep) 20%, var(--dk)) 0%, var(--dk) 74%);
  box-shadow: var(--shadow-card); margin: 0 0 14px; overflow: hidden;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
/* CHOSEN, not merely open. Until 2026-08-23 one .is-on class meant both,
   because one tap did both. The glow is the SELECTED state and belongs to the
   radio; expanding a card to read its example must not look like a choice.
   Vincent: "you can expand the card. That's cool, but that shouldn't select
   the card." */
.nq-arow.is-picked {
  border-color: var(--cy-mid);
  box-shadow: 0 0 0 1px var(--cy-mid), 0 0 0 5px rgba(111, 220, 255, .16), 0 20px 40px -18px rgba(14, 127, 166, .55), var(--shadow-card);
}
/* The head is a plain row now, holding two SIBLING buttons. It used to be one
   button wrapping everything; a button inside a button is invalid markup and
   the frontend gate rejects it, so the radio could not simply be nested. */
.nq-arow-head { display: flex; align-items: center; }
/* THE RADIO — the only thing that selects. Its own 56px-wide column so the
   tap target clears the 44px minimum on a phone without the dot itself having
   to be huge. */
.nq-arow-pick {
  flex: none; width: 56px; align-self: stretch; min-height: 56px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; padding: 0; cursor: pointer; color: inherit;
}
.nq-arow-dot {
  width: 24px; height: 24px; border-radius: 50%;
  border: 2px solid rgba(207, 243, 255, .48);
  display: flex; align-items: center; justify-content: center;
  color: transparent;
  transition: border-color .18s var(--ease), background .18s var(--ease), color .18s var(--ease);
}
.nq-arow-pick:hover .nq-arow-dot { border-color: var(--cy-lo); }
.nq-arow.is-picked .nq-arow-dot { border-color: var(--cy-mid); background: var(--cy-mid); color: var(--dk); }
/* THE EXPANDER, trigger 1 — the card body. People tap the card, so something
   has to happen; what happens is that it opens, never that it selects. */
.nq-arow-open {
  background: none; border: 0; cursor: pointer; font: inherit; color: inherit; text-align: left;
}
.nq-arow-head .nq-arow-open {
  display: flex; align-items: center; gap: 16px; flex: 1; min-width: 0;
  padding: 16px 16px 16px 0;
}
.nq-arow-photo {
  width: 76px; height: 76px; flex: none; border-radius: var(--r-16); overflow: hidden;
  background: radial-gradient(90% 90% at 50% 30%, color-mix(in srgb, var(--cy-deep) 30%, var(--dk)) 0%, var(--dk) 72%);
  transition: width .28s var(--ease), height .28s var(--ease);
}
.nq-arow.is-open .nq-arow-photo { width: 128px; height: 128px; }
.nq-arow-photo img {
  width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block;
  filter: drop-shadow(0 6px 16px rgba(0, 0, 0, .5));
}
.nq-arow-fallback { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: var(--cy-lo); }
.nq-arow-main { min-width: 0; flex: 1; }
.nq-arow-main strong {
  display: block; font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-h3); letter-spacing: -.01em; color: var(--paper);
  text-shadow: 0 0 12px rgba(111, 220, 255, .5);
}
.nq-arow-main span { display: block; margin-top: 5px; font-size: var(--fs-note); color: rgba(207, 243, 255, .68); line-height: 1.45; }
/* THE EXPANDER, trigger 2 — "that expander should probably be more obvious
   and bigger." A bare chevron was not reading as one. This is a full-width bar
   across the bottom of the card that says what it does in words, so it is both
   the biggest tap target on the row and the only one that needs explaining.
   Full-width rather than a right-hand column on purpose: as a column it was
   95px wide, which squeezed every blurb to four lines and wrapped "The
   Lieutenant" onto two. Only the ARROW rotates; the label stays upright. */
.nq-arow-more {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  width: 100%; padding: 13px 16px;
  border-top: 1px solid rgba(111, 220, 255, .16);
  color: var(--cy-lo); transition: color .2s var(--ease), background .2s var(--ease);
}
.nq-arow-more:hover { color: var(--cy-bright); background: rgba(111, 220, 255, .07); }
.nq-arow-chev-tx { font-size: var(--fs-fine); font-weight: 560; line-height: 1; white-space: nowrap; }
.nq-arow-more .ico { transition: transform .25s var(--ease); }
.nq-arow.is-open .nq-arow-more .ico { transform: rotate(180deg); }
/* the grid-template-rows 0fr→1fr trick: no JS height measuring, and the
   inner wrapper's overflow:hidden clips its content to that animated track
   size regardless of the bubble's own intrinsic height. */
.nq-arow-panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .32s var(--ease); }
.nq-arow.is-open .nq-arow-panel { grid-template-rows: 1fr; }
.nq-arow-panel-in { overflow: hidden; padding: 14px 16px 0; }
/* fades out/in on each flip (startPersonaCycle / a tap on the bubble itself
   both call flipPersonaBubble, which toggles .is-out) — a quick opacity+lift
   dip reads as "new message", not a jarring text swap. Also the "skip
   through" tap target, so it keeps the pointer cursor even though it isn't
   a <button> (matches .nq-arow-open's own reset). */
.nq-arow-bubble {
  margin: 0 0 16px; padding: 12px 15px; border-radius: var(--r-16) var(--r-16) var(--r-16) var(--r-4);
  background: var(--paper); color: var(--ink); cursor: pointer;
  box-shadow: 0 10px 24px -12px rgba(0, 0, 0, .55);
  font-size: var(--fs-body); line-height: 1.45;
  transition: opacity .22s var(--ease), transform .22s var(--ease);
}
.nq-arow-bubble.is-out { opacity: 0; transform: translateY(3px); }

/* HEAR THIS CHARACTER — sits under the example lines inside the open card, so
   the voice is picked at the same moment as the personality rather than on a
   screen of its own (Vincent: "the voice should come with the personality").
   Built from the same tokens as every other control here; no new colour, no
   new radius, and deliberately not a bare icon — a labelled button reads as
   "press me" on a screen where every other tap selects something. */
.nq-arow-play {
  display: inline-flex; align-items: center; gap: 7px;
  margin: 9px 0 2px; padding: 6px 11px 6px 9px;
  font: inherit; font-size: var(--fs-sm); font-weight: 560; line-height: 1;
  color: var(--cy-deep); background: var(--cy-pale);
  border: 1px solid color-mix(in srgb, var(--cy-mid) 45%, transparent);
  border-radius: var(--r-pill); cursor: pointer;
  transition: background .16s var(--ease), border-color .16s var(--ease);
}
.nq-arow-play:hover { border-color: var(--cy-mid); }
.nq-arow-play-i { display: flex; }
.nq-arow-play-i--stop { display: none; }
.nq-arow-play[aria-pressed="true"] .nq-arow-play-i--play { display: none; }
.nq-arow-play[aria-pressed="true"] .nq-arow-play-i--stop { display: flex; }
.nq-arow-play[aria-pressed="true"] { border-color: var(--cy-mid); animation: nq-arow-play-ring 1.3s var(--ease) infinite; }
@keyframes nq-arow-play-ring {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--cy-mid) 40%, transparent); }
  50%      { box-shadow: 0 0 0 5px color-mix(in srgb, var(--cy-mid) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .nq-arow-play[aria-pressed="true"] { animation: none; } }
@media (prefers-reduced-motion: reduce) {
  .nq-arow-photo, .nq-arow-more, .nq-arow-more .ico, .nq-arow-panel, .nq-arow-bubble, .nq-arow-dot { transition: none; }
}
/* 2026-08-25: the reassurance line under the play button, once the flip-cycle
   second example went away and the panel had room to say the thing plainly
   instead of implying it — small and quiet, it is a footnote, not a claim
   that needs its own visual weight. */
.nq-arow-note { font-size: var(--fs-xs, 12px); margin: 6px 0 0; }

/* ---- "how should I sound when I call you?" (2026-08-20) -------------------
   MODIFIERS on .nq-choice, not a new card — border, hover lift, is-on fill
   and the tick are all inherited, same deal as .nq-choice--len. The rows are
   real <button>s (they play sound), so keyboard focus comes from the global
   `.nas :focus-visible` outline for free. What's added: the character's bust
   in the icon slot, on the same dark radial disc .nq-arow-photo uses one
   screen earlier (so "Daniel" visibly IS the Reginald you just picked), and a
   round play/pause chip that mirrors the shared Audio element's real playing
   state (.is-playing, set from the element's own events). */
.nq-choice--voice { min-height: 0; }
.nq-cico--photo {
  width: 54px; height: 54px; /* 44 read as an anonymous dark square — at 54 the
  face is recognizable as the character picked one screen ago */
  border-color: rgba(111, 220, 255, .22);
  background: radial-gradient(90% 90% at 50% 30%, color-mix(in srgb, var(--cy-deep) 30%, var(--dk)) 0%, var(--dk) 72%);
}
.nq-cico--photo img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }
.nq-vplay {
  width: 38px; height: 38px; flex: none; margin-left: auto; border-radius: 50%;
  border: 1.5px solid var(--border-strong); background: var(--paper); color: var(--sub);
  display: flex; align-items: center; justify-content: center;
  transition: background .2s var(--ease), border-color .2s var(--ease), color .2s var(--ease);
}
/* .nq-check's own margin-left:auto yields to the play chip's — the chip takes
   the stretch, the tick stays hugged to the right edge. */
.nq-choice--voice .nq-check { margin-left: 0; }
.nq-vplay-i { display: flex; }
.nq-vplay-i--play svg { margin-left: 2px; } /* optically center the triangle */
.nq-vplay-i--pause { display: none; }
.nq-choice--voice.is-playing .nq-vplay-i--play { display: none; }
.nq-choice--voice.is-playing .nq-vplay-i--pause { display: flex; }
.nq-choice--voice:hover .nq-vplay, .nq-choice--voice.is-on .nq-vplay { border-color: var(--cy-mid); background: var(--cy-pale); color: var(--cy-deep); }
/* A soft ring breathing off the chip while its sample is audible — status,
   not decoration (it reports "sound is playing right now", spinner-style),
   and it self-terminates with the ~5s clip. Stilled under reduced motion;
   the play→pause glyph swap carries the state on its own there. */
@keyframes nq-vplay-ring { from { box-shadow: 0 0 0 0 rgba(111, 220, 255, .5); } to { box-shadow: 0 0 0 10px rgba(111, 220, 255, 0); } }
.nq-choice--voice.is-playing .nq-vplay { animation: nq-vplay-ring 1.3s var(--ease) infinite; border-color: var(--cy-mid); background: var(--cy-pale); color: var(--cy-deep); }
@media (prefers-reduced-motion: reduce) {
  .nq-choice--voice.is-playing .nq-vplay { animation: none; }
}

/* ---- personality success beat: the chosen character's own hero art ------
   Vincent, round 1: "the stats somewhere there and the big image of the
   personality you chose." Round 2, looking at that: "that's absolutely not
   what I said — a cutout of the image big, the FULL VERTICAL HEIGHT should
   have that image, probably not the feet, hollowing in, see the upper body
   and it's big — and it should be really cool where it says 'great, nice
   to meet you.'" So: the source portrait is now cropped to a bust (head
   through waist, see extract-personas' bust variant — CSS alone can't turn
   "contained inside a padded box" into "fills the frame" when the padding
   is baked into the source pixels), object-fit:cover so it genuinely fills
   the card edge to edge with no letterboxing, and the headline moved OFF
   its own line and onto the image itself, scrim-anchored like the picker
   cards, instead of sitting underneath as plain text.
   Dropped the "[persona] configured" badge from round 1 entirely — "it
   shouldn't say 'XYZ person configured'". The stats below already say what
   this screen is; the image doesn't need to caption itself too.
   Named .nq-persona-hero, not .nq-hero — .nq-hero already belongs to the
   unrelated welcome-screen wordmark card further down this file, and its
   background/radius/shadow would have quietly won the cascade (same
   specificity, later in source) if this had reused the name. */
.nq-persona-hero {
  aspect-ratio: 4 / 5; border-radius: var(--r-18); overflow: hidden; position: relative;
  background: radial-gradient(90% 85% at 50% 30%, color-mix(in srgb, var(--cy-deep) 20%, var(--dk)) 0%, var(--dk) 70%);
  box-shadow: var(--shadow-card);
}
.nq-persona-hero img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: top center;
}
.nq-persona-hero-fallback { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--cy-mid); }
.nq-persona-hero-scrim {
  position: absolute; inset: auto 0 0 0; height: 44%;
  background: linear-gradient(to top, var(--dk) 0%, rgba(6, 18, 29, .82) 45%, rgba(6, 18, 29, 0) 100%);
  pointer-events: none;
}
.nq-persona-hero-h {
  position: absolute; inset: auto 20px 20px; margin: 0; color: var(--paper); text-transform: uppercase;
  font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-h-quiz); line-height: 1.04; letter-spacing: -.01em;
  text-shadow: 0 2px 16px rgba(0, 0, 0, .5);
}
.nq-persona-hero-h em { font-style: italic; color: var(--cy-mid); }

/* Vincent, 2026-08-19, on the celebrate screen: "the text is way too small
   there. It doesn't look cool." This is the one screen in the funnel that is
   allowed to shout, so the payoff line gets its own size rather than the
   ordinary quiz heading scale (~27px on a phone). */
.nq-persona-hero-h--big {
  font-size: var(--fs-h-celebrate); line-height: .95; letter-spacing: -.02em;
  inset: auto 18px 18px;
}

/* ---- celebrate screen: confetti burst --------------------------------
   No sparkle emoji, no chip — Vincent, 2026-08-18, looking at the old
   version of this screen: "I hate the sparkles emoji... doesn't sound
   exciting, doesn't do anything." A dozen-plus pieces fall past the persona
   photo once, cyan system only, then the layer sits empty — never a loop,
   never something the eye keeps snagging on while reading the plan copy
   below it. Clipped by .nq-persona-hero's own overflow:hidden. */
.nq-confetti { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: var(--z-dw-fade); }
/* THE BUG THIS FIXES (2026-08-19): a transform percentage resolves against
   the ELEMENT'S OWN box, not its container. Each piece is 12px tall, so
   translateY(340%) moved it 41px, starting from top:-12% of a ~470px card
   (-56px) and finishing at -15px: the confetti finished its fall while still
   above the top edge of the card it was clipped inside, so it was NEVER once
   visible on screen. Vincent, three times running: "the sparkles still don't
   come. They literally don't." Animating `top` instead resolves the
   percentage against the CARD, so the pieces genuinely cross it. */
.nq-confetti i {
  position: absolute; top: -12%; left: var(--x, 50%);
  width: 9px; height: 15px; border-radius: var(--r-4);
  background: var(--c, var(--cy-mid));
  opacity: 0;
  animation: nq-confetti-fall 1.7s var(--ease) var(--d, 0s) 1 both;
}
@keyframes nq-confetti-fall {
  0% { opacity: 0; top: -12%; transform: rotate(0deg); }
  10% { opacity: 1; }
  80% { opacity: 1; }
  100% { opacity: 0; top: 108%; transform: rotate(300deg); }
}
/* Reduced motion must not mean NO celebration. This used to be
   `display:none`, so anyone with "Reduce Motion" on (a default many people
   run on iOS) got a completely bare celebrate screen — Vincent, 2026-08-19,
   after paying and landing on it: "there was zero confetti, zero anything.
   It didn't seem excited at all." The honest reading of the preference is
   "do not animate", not "do not decorate", so the pieces still paint, they
   just arrive already settled instead of falling. --y is generated per piece
   by confettiHTML() purely for this branch. */
@media (prefers-reduced-motion: reduce) {
  .nq-confetti i {
    animation: none; opacity: .92;
    top: var(--y, 40%); transform: none; rotate: 24deg;
  }
}

/* ---- celebrate screen: the FULL-SCREEN burst ------------------------------
   Vincent, 2026-08-19: "Nothing is celebrating me... nothing like, whoa, this
   is cool." The in-card confetti above is clipped to the persona photo, which
   even working is a modest flourish. This is a separate FIXED layer that is
   not clipped by anything, so the burst actually fills the screen at the one
   moment in the funnel that has earned it. Same cyan system, no new colors,
   no sparkle emoji (Vincent: "I hate the sparkles emoji"). Plays once and
   then sits transparent; a celebration that loops is not a celebration.
   Distances are in vh, NOT %, for exactly the reason documented on
   nq-confetti-fall above. z-index sits under --z-cta so it never covers the
   button, and pointer-events:none keeps it out of the way entirely. */
.nq-confetti-burst { position: fixed; inset: 0; overflow: hidden; pointer-events: none; z-index: var(--z-confetti-burst); }
.nq-confetti-burst i {
  position: absolute; top: -12vh; left: var(--x, 50%);
  width: var(--w, 10px); height: var(--h, 16px); border-radius: var(--round, var(--r-4));
  background: var(--c, var(--cy-mid));
  opacity: 0;
  animation: nq-confetti-burst-fall 2.6s var(--ease) var(--d, 0s) 1 both;
}
@keyframes nq-confetti-burst-fall {
  0% { opacity: 0; transform: translate(0, 0) rotate(0deg); }
  7% { opacity: 1; }
  78% { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--sway, 0px), 122vh) rotate(var(--spin, 540deg)); }
}
/* Settled, not absent — same reasoning as the in-card layer's reduce block. */
@media (prefers-reduced-motion: reduce) {
  .nq-confetti-burst i {
    animation: none; opacity: .9;
    top: var(--y, 40%); transform: none; rotate: var(--spin, 540deg);
  }
}

/* ---- name screen: confirm flash on submit --------------------------------
   The input locks in visually for the confirm beat before the screen
   advances — cyan ring plus the check popping in, the same "picked" language
   .nq-choice.is-on speaks, worn by a text field. Driven by rName() adding
   .is-confirmed to the wrap. */
.nq-namewrap { position: relative; }
.nq-namewrap .nas-input { padding-right: 52px; }
.nq-nameok {
  position: absolute; right: 14px; top: 50%;
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--grad); color: var(--dk);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transform: translateY(-50%) scale(.4);
  transition: opacity .18s var(--ease), transform .18s var(--ease);
}
.nq-namewrap.is-confirmed .nas-input { border-color: var(--cy-mid); box-shadow: 0 0 0 3px rgba(111, 220, 255, .3); }
.nq-namewrap.is-confirmed .nq-nameok { opacity: 1; transform: translateY(-50%) scale(1); }

/* ---- panels (toggle rows, sliders) ---- */
.nq-panel {
  border: 1px solid var(--line); border-radius:var(--r-18); background: var(--paper);
  box-shadow: var(--shadow-card); padding: 15px 16px; margin: 0 0 10px;
}
.nq-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.nq-row strong { font-size: var(--fs-md); font-weight: 640; color: var(--ink); letter-spacing: -.01em; display: flex; align-items: center; gap: 9px; }
.nq-row strong svg { color: var(--cy-deep); flex: none; }
.nq-note { margin: 7px 0 0; font-size: var(--fs-note); color: var(--sub); line-height: 1.45; }
.nq-note a { color: var(--cy-deep); text-decoration: none; }
.nq-note a:hover { text-decoration: underline; }

.nq-quote { margin: 8px 0 0; padding: 9px 12px; border-radius:var(--r-12) var(--r-12) var(--r-12) var(--r-4); background: var(--tint); border: 1px solid var(--line-2); font-size: var(--fs-sm); color: var(--mut); line-height: 1.4; }

.nq-timelabel { display: flex; align-items: center; justify-content: center; gap: 6px; margin: 10px 0 4px; }

/* ---- custom timezone picker: a short button + search-to-filter panel,
   replacing the native <select> — Vincent, 2026-08-17: "the browser thing
   doesn't work well... too long" (zoneSelectOptions' label concatenates
   every city a zone covers, correct for search, unreadable as a closed
   label). Same card language as .nq-panel/.nq-datewheel. */
.nq-tzfield { margin-bottom: 16px; }
.nq-tzfield .nas-input { height: 44px; }
/* Secondary placement (check-in screens) — time is the dominant control up
   top now; timezone is a small "if you need to change it" field below the
   message preview, not competing for attention. Vincent, 2026-08-19: "time
   zone should be on the bottom somewhere if they need it to turn... that's
   the smaller component." */
.nq-tzfield--sub { margin: 18px 0 0; }
.nq-tzfield--sub .nas-flabel { font-size: var(--fs-fine); color: var(--faint); }
.nq-tzfield--sub .nas-input { height: 38px; font-size: var(--fs-note); box-shadow: none; }
.nq-tzbtn { display: flex; align-items: center; justify-content: space-between; gap: 10px; text-align: left; cursor: pointer; }
.nq-tzbtn svg { flex: none; color: var(--sub); transition: transform .18s var(--ease); }
.nq-tzbtn[aria-expanded="true"] svg { transform: rotate(180deg); }
.nq-tzpanel {
  margin-top: 8px; border: 1px solid var(--line); border-radius: var(--r-16);
  background: var(--paper); box-shadow: var(--shadow-card); padding: 10px;
}
.nq-tzsearch { height: 42px; margin-bottom: 8px; }
.nq-tzlist { max-height: 240px; overflow-y: auto; display: grid; gap: 2px; }
.nq-tzrow {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; padding: 10px; border: 0; border-radius: var(--r-12); background: transparent;
  font: inherit; font-size: var(--fs-note); color: var(--ink); text-align: left; cursor: pointer;
}
.nq-tzrow:hover, .nq-tzrow:focus-visible { background: var(--field); outline: none; }
.nq-tzrow.is-sel { background: var(--tint); color: var(--cy-deep); font-weight: 640; }
.nq-tzrow .nq-tzoff { color: var(--sub); font-size: var(--fs-sm); flex: none; }
.nq-tzempty { padding: 16px; text-align: center; color: var(--sub); font-size: var(--fs-note); }

/* ---- the switch ---- */
.nq-switch {
  width: 50px; height: 30px; flex: none; position: relative;
  border: 0; border-radius:var(--r-pill); background: var(--switch-off); cursor: pointer; padding: 0;
  transition: background .25s var(--ease);
}
.nq-switch::after {
  content: ""; position: absolute; width: 24px; height: 24px; left: 3px; top: 3px;
  border-radius: 50%; background: var(--paper); box-shadow: 0 2px 8px rgba(10, 18, 32, .22);
  transition: left .25s var(--ease);
}
.nq-switch[aria-checked="true"] { background: var(--grad); }
.nq-switch[aria-checked="true"]::after { left: 23px; }

/* ---- personality sliders ---- */
.nq-slide-val { font-family: var(--disp); font-style: italic; font-weight: 800; font-size: var(--fs-md); color: var(--cy-deep); font-variant-numeric: tabular-nums; }
.nq-range {
  -webkit-appearance: none; appearance: none; width: 100%; height: 7px; margin: 13px 0 4px;
  border-radius:var(--r-pill); outline-offset: 6px; cursor: pointer;
  background: linear-gradient(90deg, var(--cy-bright), var(--cy-mid)) 0 / var(--pct, 50%) 100% no-repeat, var(--spark-mid);
}
.nq-range::-webkit-slider-thumb {
  -webkit-appearance: none; width: 26px; height: 26px; border-radius: 50%;
  background: var(--paper); border: 2px solid var(--cy-mid);
  box-shadow: 0 2px 10px rgba(58, 190, 232, .45); cursor: grab;
  transition: transform .15s var(--ease);
}
.nq-range::-webkit-slider-thumb:active { cursor: grabbing; transform: scale(1.12); }
.nq-range::-moz-range-thumb {
  width: 26px; height: 26px; border-radius: 50%; background: var(--paper);
  border: 2px solid var(--cy-mid); box-shadow: 0 2px 10px rgba(58, 190, 232, .45); cursor: grab;
}
.nq-range::-moz-range-track { height: 7px; border-radius:var(--r-pill); background: transparent; }
/* the live microcopy under a slider — height reserved so it never shifts layout */
.nq-slide-note { margin: 4px 0 0; min-height: 1.45em; font-size: var(--fs-note); color: var(--sub); line-height: 1.45; }

/* ---- shared scrubber base (assets/js/scrubber.js) — up/down buttons,
   typeable value, scroll/drag. Ships no CSS of its own; two independent
   uses below (birthday, check-in time) each set their own widths, sized to
   fit their OWN column budget — the whole page caps out at 436px
   (.nq { max-width:480px; padding:0 22px }), measured directly, not
   guessed, after that budget is exactly what caused the original bug:
   3 birthday fields each needing 2×44px buttons didn't fit it, and the
   month field's text got squeezed to ~30px — invisible, though the value
   underneath was correct (Vincent: "it literally doesn't show the
   numbers"). Every width below is chosen to leave real room for buttons
   AND digits at whatever font size that context uses. */
.scrub {
  display: flex; align-items: stretch; border: 1px solid var(--line); border-radius: var(--r-16);
  background: var(--paper); box-shadow: var(--shadow-card); overflow: hidden;
}
.scrub-btn {
  display: flex; align-items: center; justify-content: center; flex: none;
  border: none; background: transparent; color: var(--cy-deep); font-weight: 700; line-height: 1;
  cursor: pointer; transition: background .12s var(--ease);
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.scrub-btn:hover:not(:disabled) { background: var(--tint); }
.scrub-btn:active:not(:disabled) { background: var(--cy-pale); }
.scrub-btn:disabled { color: var(--faint); cursor: default; }
.scrub-val { flex: 1; min-width: 0; display: flex; align-items: center; justify-content: center; cursor: ns-resize; }
.scrub.is-dragging { background: var(--tint); }
.scrub-input {
  width: 100%; border: none; background: transparent; text-align: center;
  font: inherit; font-family: var(--disp); font-style: italic; font-weight: 640;
  color: var(--ink); padding: 0 2px; cursor: inherit;
}
.scrub-input:focus { outline: none; color: var(--cy-deep); }
.scrub-input::selection { background: var(--cy-pale); }

/* ---- birthday — Month/Day/Year, that fixed order (US audience, Vincent
   2026-08-19: "make sure that's the right input format for dates"). Month
   went on its own full-width row, day/year sharing the one below — 3
   fields crammed side by side (the first version of this fix) is
   inherently width-limited against the real 346px mobile column, and
   Vincent then asked for this "bigger, prettier, nicer." Off on its own,
   month can show the FULL name ("September") at a genuinely large size
   instead of a 3-letter abbreviation, and day/year each still get real
   room at 169px apiece. */
.nq-datepick { margin-top: 4px; display: flex; flex-direction: column; gap: 8px; }
/* The month buttons carry ‹ › arrows (a sequence, not arithmetic — see
   dateWheelHTML), and a guillemet's ink is a fraction of a +'s, so it takes
   the larger scrubber size (existing token, no new value) to read at all
   next to the jumbo month name. */
.nq-datepick-month .scrub-btn { width: 44px; font-size: var(--fs-scrub-lg); }
.nq-datepick-month .scrub-input { font-size: var(--fs-jumbo); font-weight: 700; }
.nq-datepick-dy { display: flex; gap: 8px; }
.nq-datepick-dy .scrub { flex: 1 1 0; }
.nq-datepick-dy .scrub-btn { width: 40px; font-size: var(--fs-scrub-btn); }
.nq-datepick-dy .scrub-input { font-size: var(--fs-scrub-lg); font-weight: 700; }

/* ---- check-in time — the dominant control on the screen, not the
   timezone (moved below, smaller) or the message preview. Vincent,
   2026-08-19: AM/PM belongs "on the right of the time... super easy...
   just switch so it just says one of them, not both" — one tap-to-flip
   button, not a 2-button toggle, which is also what makes a single-row
   layout fit: budget against the real 346px worst case (mobile) — 3 gaps
   at 6px (18px) + a ~40px toggle + a ~14px colon leaves 274px for the 2
   scrubbers, 137px each; each scrub's own 2 buttons take 36px×2=72px,
   leaving 65px for 2 digits — comfortable at 1.8rem, still meaningfully
   bigger than the old select version. Minute is a full 0-59 range now, not
   snapped to :05 (Vincent: "I should be able to put in like 59"). */
.nq-timepick { margin-top: 8px; }
.nq-timerow { display: flex; align-items: center; justify-content: center; gap: 6px; }
.nq-timerow .scrub { flex: 1 1 0; max-width: 137px; height: 52px; }
.nq-timerow .scrub-btn { width: 36px; font-size: var(--fs-scrub-btn); }
.nq-timerow .scrub-input { font-size: var(--fs-scrub-lg); font-weight: 700; }
.nq-timecolon {
  flex: none; font-family: var(--disp); font-style: italic; font-weight: 700;
  font-size: var(--fs-colon); color: var(--ink); padding-bottom: 2px;
}
.nq-ampm-toggle {
  flex: none; min-width: 46px; height: 52px; padding: 0 8px;
  border: 1px solid var(--line); border-radius: var(--r-12); background: var(--paper);
  color: var(--cy-deep); font-family: var(--disp); font-style: italic;
  font-weight: 800; font-size: var(--fs-sm); letter-spacing: .04em; cursor: pointer;
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.nq-ampm-toggle:hover { border-color: var(--cy-mid); }
.nq-ampm-toggle:active { background: var(--tint); }

/* ---- interest pills ---- */
.nq-pills { display: flex; flex-wrap: wrap; gap: 9px; margin: 0 0 6px; }
.nq-pill {
  display: inline-flex; align-items: center; gap: 7px; min-height: 44px;
  padding: 10px 16px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  font: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer;
  transition: background .2s var(--ease), border-color .2s var(--ease), color .2s var(--ease), transform .2s var(--ease);
}
.nq-pill:hover { border-color: var(--cy-mid); transform: translateY(-1px); }
.nq-pill.is-on { background: var(--dk); border-color: var(--dk); color: var(--cy-lo); }
.nq-pill.is-on:hover { border-color: var(--dk); }
.nq-pill svg { flex: none; }
.nq-addrow { display: flex; gap: 9px; margin-top: 12px; }
.nq-addrow .nas-input { height: 48px; flex: 1; }
.nq-addbtn {
  width: 48px; height: 48px; flex: none; border-radius: var(--r-ctl);
  border: 1px solid var(--line); background: var(--paper); color: var(--cy-deep); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.nq-addbtn:hover { border-color: var(--cy-mid); background: var(--tint); }

/* the freeform "one thing Nova should know" box wears the input's clothes.
   Sized for the sentence it asks for, not an essay — a 150px void read as a
   blank-page ask, which is the exact thing the screen's rewrite removed. */
.nq textarea.nas-input { height: auto; min-height: 112px; padding: 13px 16px; resize: vertical; line-height: 1.5; }

/* ---- live preview (the text Nova would send) — a real message bubble
   sitting on the page's own background, not a card floating on top of it
   (Vincent, 2026-08-18: "make it there, you know, like show the actual
   text message" — then, seeing the first pass, pointed at a genuine
   iMessage screenshot: muted timestamp caption above a plain gray received
   bubble, no card, no border). */
.nq-preview { margin-top: 18px; }
.nq-preview-label {
  display: flex; align-items: center; gap: 6px; margin-bottom: 7px;
  font-size: var(--fs-fine); color: var(--faint); font-weight: 500;
}
/* --tint (#F0FAFE) is 2-3 RGB units off the page's own --field (#F2FCFF) —
   invisible as a fill, which is why this "didn't render" (Vincent,
   2026-08-19) even though the DOM/shape were both already correct. --paper
   (real white) + a border is how every OTHER card on this page gets
   contrast against the light-cyan-tinted background — same fix here. */
.nq-bubble {
  display: inline-block; max-width: 88%;
  padding: 11px 14px; border-radius: var(--r-18) var(--r-18) var(--r-18) var(--r-4);
  background: var(--paper); border: 1px solid var(--line); box-shadow: var(--shadow-card);
  font-size: var(--fs-sm); color: var(--ink); line-height: 1.45;
}

/* ---- welcome hero: the real wordmark on a dark card, no invented dialogue ---- */
.nq-hero {
  background: var(--dk-card); border-radius:var(--r-22); padding: 28px 22px; margin: 22px 0 0;
  box-shadow: 0 26px 52px -22px rgba(6, 18, 29, .55);
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: 14px;
}
.nq-wordmark { height: 30px; width: auto; display: block; }
.nq-hero-tagline { margin: 0; max-width: 30ch; font-size: var(--fs-sm); line-height: 1.5; color: var(--dark-text); }
.nq-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; margin-top: 14px; }
.nq-stat { padding: 12px; border: 1px solid var(--line); border-radius:var(--r-16); background: var(--paper); box-shadow: var(--shadow-card); }
.nq-stat b { display: block; font-family: var(--disp); font-style: italic; font-weight: 800; font-size:var(--fs-stat); color: var(--ink); letter-spacing: -.02em; }
.nq-stat span { font-size: var(--fs-fine); color: var(--sub); line-height: 1.3; display: block; margin-top: 3px; font-weight: 600; }
/* --big: the personality success beat, where the three numbers ARE the screen.
   Vincent's markup, 2026-08-15, drew them roughly three times the size of the
   12px tiles they were, with the label under each — "make sure the numbers are
   bold". Centered, because with the sub-line gone there is nothing else on the
   screen for them to align to.
   Round 2 (2026-08-17): "the numbers shouldn't be in boxes, but they should
   just be there — that's the personality you configured." No card chrome —
   the number's own weight and size is what says "this matters", not a
   paper tile around it. */
.nq-stats--big { gap: 10px; margin-top: 22px; }
.nq-stats--big .nq-stat { padding: 0; border: none; background: none; box-shadow: none; text-align: center; }
.nq-stats--big .nq-stat b { font-size: var(--fs-stat-big); line-height: 1; }
.nq-stats--big .nq-stat span { margin-top: 8px; font-size: var(--fs-note); color: var(--sub); }

/* ---- SMS code boxes ---- */
.nq-code { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; margin: 22px 0 10px; }
.nq-code input {
  height: 58px; width: 100%; padding: 0; text-align: center;
  border-radius:var(--r-14); border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  font: inherit; font-size:var(--fs-code); font-weight: 700; font-variant-numeric: tabular-nums;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.nq-code input:focus { outline: none; border-color: var(--cy-mid); box-shadow: 0 0 0 3px rgba(111, 220, 255, .3); }
.nq-code.is-bad input { border-color: var(--err); animation: nq-shake .4s var(--ease); }
@keyframes nq-shake { 25% { transform: translateX(-4px); } 50% { transform: translateX(4px); } 75% { transform: translateX(-2px); } }

/* ---- building loader checklist ----
   Renamed from .nq-tl to .nq-bstep, 2026-08-18: a real class collision with
   the unrelated trial-timeline component below (.nq-tl there is deliberately
   flex-direction:column) — same class name, source order made the later
   rule win, so the tick stacked ABOVE the label instead of beside it on
   every row. Same fix pattern as the earlier .nq-hero collision. */
.nq-build { margin-top: 6px; }
.nq-bstep { display: flex; align-items: flex-start; gap: 13px; padding: 13px 2px; border-bottom: 1px solid var(--line-2); opacity: .35; transition: opacity .4s var(--ease); }
.nq-bstep:last-child { border-bottom: 0; }
.nq-tlx { min-width: 0; }
.nq-tlx strong { display: block; font-size: var(--fs-md); font-weight: 640; color: var(--ink); }
.nq-tlx span { display: block; margin-top: 1px; font-size: var(--fs-note); color: var(--sub); }
.nq-tick {
  width: 26px; height: 26px; flex: none; margin-top: 1px;
  border-radius: 50%; border: 1.5px solid var(--border-strong); background: var(--paper); color: var(--accent-fg);
  display: flex; align-items: center; justify-content: center;
  transition: background .25s var(--ease), border-color .25s var(--ease);
}
.nq-tick svg { opacity: 0; transform: scale(.4); transition: opacity .2s var(--ease), transform .2s var(--ease); }
.nq-bstep.is-now { opacity: 1; }
.nq-bstep.is-now .nq-tick { border-color: var(--cy-mid); box-shadow: 0 0 0 0 rgba(111, 220, 255, .5); animation: nas-pulse var(--dur-pulse) infinite; }
.nq-bstep.is-done { opacity: 1; }
.nq-bstep.is-done .nq-tick { background: var(--grad); border-color: transparent; animation: none; box-shadow: none; }
.nq-bstep.is-done .nq-tick svg { opacity: 1; transform: none; }

/* .nq-recap / .nq-rcard lived here for the "connected and ready" screen that
   Vincent deleted on 2026-08-15. Nothing rendered them afterwards, so they went
   with it — the recap that survives is .nq-knows, below.

   ---- "what I know about you": the last screen before the account step.
   One card: a tinted header with the spark mark and a live fact count, then one
   hairline row per answer — icon chip . micro-label . value . graphics (chips
   for the multi-picks, gradient meters for the personality dials). This is now
   the ONLY read-back of the buyer's answers in the whole funnel. ---- */
.nq-knows {
  margin: 4px 0 0; border: 1px solid var(--line); border-radius:var(--r-22);
  background: var(--paper); box-shadow: var(--shadow-card); overflow: hidden;
}
.nq-knows-top { display: flex; align-items: center; gap: 12px; padding: 14px 16px; background: var(--tint); border-bottom: 1px solid var(--line); }
.nq-knows-mark {
  width: 38px; height: 38px; flex: none; border-radius:var(--r-12);
  display: flex; align-items: center; justify-content: center;
  background: var(--grad); color: var(--dk); box-shadow: var(--glow);
}
/* THE SUCCESS MARK. Same disc, gradient and glow as .nq-knows-mark, just at
   the size a "this part is done" beat deserves. Vincent, 2026-08-23, on the
   phone step handing straight over to the next screen: "they should feel
   like, wow, this part is completed." */
.nq-winmark {
  width: 84px; height: 84px; margin: 6px auto 20px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--grad); color: var(--dk); box-shadow: var(--glow);
}
.nq-knows-tx { flex: 1; min-width: 0; }
.nq-knows-tx strong {
  display: block; font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-note); letter-spacing: .08em; text-transform: uppercase; color: var(--ink);
}
.nq-knows-tx span { display: block; margin-top: 2px; font-size: var(--fs-fine); color: var(--sub); line-height: 1.35; }
.nq-krow { display: flex; gap: 12px; padding: 13px 16px; border-bottom: 1px solid var(--line-2); }
.nq-krow:last-child { border-bottom: 0; }
.nq-kico {
  width: 32px; height: 32px; flex: none; border-radius:var(--r-12);
  display: flex; align-items: center; justify-content: center;
  background: var(--tint); border: 1px solid var(--cy-pale); color: var(--cy-deep);
}
.nq-kmain { flex: 1; min-width: 0; }
.nq-kmain > i {
  display: block; font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-label); letter-spacing: .12em; text-transform: uppercase; color: var(--faint);
}
.nq-kmain > b { display: block; margin-top: 3px; font-size: var(--fs-sm); font-weight: 640; color: var(--ink); line-height: 1.4; overflow-wrap: break-word; }
.nq-kchips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 7px; }
.nq-kchip {
  display: inline-flex; align-items: center; padding: 4px 9px; border-radius:var(--r-pill);
  background: var(--tint); border: 1px solid var(--cy-pale); color: var(--mut);
  font-size: var(--fs-fine); line-height: 1.3;
}
.nq-kmeters { display: grid; gap: 7px; margin-top: 8px; }
.nq-kmeter { display: grid; grid-template-columns: 68px 1fr 22px; align-items: center; gap: 9px; }
.nq-kmeter-l { font-size: var(--fs-fine); color: var(--sub); }
.nq-kmeter-t { height: 6px; border-radius:var(--r-pill); background: var(--line-2); overflow: hidden; }
.nq-kmeter-f { display: block; height: 100%; border-radius:var(--r-pill); background: var(--grad); }
.nq-kmeter-v { font-size: var(--fs-fine); color: var(--faint); text-align: right; font-variant-numeric: tabular-nums; }
.nq-kquote {
  margin: 7px 0 0; padding-left: 11px; border-left: 2px solid var(--cy-mid);
  font-size: var(--fs-sm); line-height: 1.5; color: var(--mut);
}

/* Centered-composition modifier for full-screen "moment" screens (goal,
   encourage1, encourage2). Safe with tall content: .nq-body is flex:1 inside
   a min-height column, so when content outgrows the viewport the body just
   grows and the centering becomes a no-op instead of clipping. */
.nq-body--center { display: flex; flex-direction: column; justify-content: center; text-align: center; }
.nq-body--center .nq-sub { margin-left: auto; margin-right: auto; max-width: 36ch; }
/* The recap card is left-aligned even on a centred screen: it is a list of
   facts, and centred label/value rows read as a poster. Used by the
   phone-verified success beat (screenPhoneVerified), which centres its mark
   and headline but not this. */
.nq-body--center .nq-knows { text-align: left; }
/* no sticky CTA on the goal screen, so no need for the 168px clearance */
.nq-body--nocta { padding-bottom: 64px; }
/* encourage2: one huge true claim, nothing else (Vincent: "make that bold and
   big and easy"). Steps one size up from .nq-h, capped at the same
   --fs-jumbo the plan price already uses — no new size invented. */
.nq-h--big { font-size: clamp(1.9rem, 8.6vw, var(--fs-jumbo)); }

/* ---- goal screen, third pass (2026-08-15) — Vincent: "make both of those
   bigger and like more in the middle... maybe make a left and right... make
   it not an arrow, but a round thing." Two tall side-by-side tiles, the whole
   composition vertically centered (.nq-body--center), each with a round
   radio-style pick indicator instead of the old trailing arrow. On tap the
   radio fills with the brand gradient (selectThenAdvance in the boot JS holds
   ~220ms so the fill is SEEN) and the other tile dims, then the quiz moves.
   Chips: the assistant tile wears the gradient message-bubble chip; the
   all-models tile wears .nq-models, a fanned trio of real provider tiles. ---- */
.nq-goal-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 14px; }
.nq-goal {
  position: relative; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 14px;
  padding: 30px 14px 24px; border: 1.5px solid var(--line);
  border-radius:var(--r-22); background: var(--paper); box-shadow: var(--shadow-card);
  cursor: pointer; font: inherit;
  transition: border-color .18s var(--ease), background .18s var(--ease), transform .18s var(--ease), box-shadow .18s var(--ease), opacity .2s var(--ease);
}
.nq-goal:hover { border-color: var(--cy-mid); transform: translateY(-2px); box-shadow: var(--shadow-lift); }
.nq-goal:active { transform: scale(.98); }
.nq-goal-ico {
  width: 58px; height: 58px; flex: none; border-radius:var(--r-18); display: flex; align-items: center; justify-content: center;
  background: var(--grad); color: var(--dk); box-shadow: var(--glow);
}
.nq-goal-main { min-width: 0; }
.nq-goal-main strong { display: block; font-size: var(--fs-md); font-weight: 720; color: var(--ink); letter-spacing: -.01em; line-height: 1.3; }
.nq-goal-main span { display: block; margin-top: 6px; font-size: var(--fs-fine); color: var(--sub); line-height: 1.5; }

/* the round choice thing — same recipe as .nq-check, sized for a tile corner */
.nq-goal-radio {
  position: absolute; top: 12px; right: 12px; width: 26px; height: 26px;
  border-radius: 50%; border: 1.5px solid var(--line); background: var(--paper); color: var(--dk);
  display: flex; align-items: center; justify-content: center;
  transition: background .18s var(--ease), border-color .18s var(--ease);
}
.nq-goal-radio svg { opacity: 0; transform: scale(.4); transition: opacity .16s var(--ease), transform .16s var(--ease); }
.nq-goal:hover .nq-goal-radio { border-color: var(--cy-mid); }
.nq-goal.is-selected { border-color: var(--cy-mid); background: var(--choice-on-bg); box-shadow: 0 0 0 1px var(--cy-mid), var(--shadow-lift); }
.nq-goal.is-selected .nq-goal-radio { background: var(--grad); border-color: transparent; }
.nq-goal.is-selected .nq-goal-radio svg { opacity: 1; transform: none; }
.nq-goal.is-dimmed { opacity: .45; }

/* the fanned trio of provider tiles (Claude · OpenAI · Gemini) — real logos
   from assets/img/providers/, black marks on small white app-icon tiles, the
   middle one raised. Same 58px footprint as .nq-goal-ico so the two goal
   cards stay level. */
.nq-models { display: flex; align-items: center; justify-content: center; height: 58px; flex: none; }
.nq-model {
  width: 34px; height: 34px; flex: none; border-radius:var(--r-12);
  background: var(--paper); border: 1px solid var(--line); box-shadow: var(--shadow-card);
  display: flex; align-items: center; justify-content: center;
}
.nq-model + .nq-model { margin-left: -8px; }
.nq-model:nth-child(1) { transform: rotate(-8deg) translateY(3px); }
.nq-model:nth-child(2) { position: relative; z-index: 1; transform: translateY(-3px); }
.nq-model:nth-child(3) { transform: rotate(8deg) translateY(3px); }
.nq-model img { width: 18px; height: 18px; display: block; }

/* the two-bubble exchange worn by the assistant goal — them on the left in
   paper white with a tail bottom-left, Nova on the right in the brand gradient
   with a tail bottom-right, overlapping and counter-tilted so it reads as a
   back-and-forth rather than two loose shapes. Vincent, 2026-08-15: "a graphic
   showing two chat bubbles left and right." Same 58px footprint (64px in
   .nq-split) as .nq-models so the two goal cards stay level. */
.nq-bubbles { display: flex; align-items: center; justify-content: center; height: 58px; flex: none; }
.nq-bub {
  display: flex; align-items: center; justify-content: center; gap: 3px;
  width: 34px; height: 26px; flex: none;
  box-shadow: var(--shadow-card);
}
.nq-bub i { display: block; border-radius: var(--r-pill); }
.nq-bub--in {
  border-radius: var(--r-10) var(--r-10) var(--r-10) var(--r-3);
  background: var(--paper); border: 1px solid var(--line);
  transform: rotate(-6deg) translateY(-6px);
}
.nq-bub--in i { width: 4px; height: 4px; background: var(--cy-mid); }
.nq-bub--out {
  flex-direction: column; align-items: flex-end; gap: 4px; padding-right: 8px;
  border-radius: var(--r-10) var(--r-10) var(--r-3) var(--r-10);
  background: var(--grad); box-shadow: var(--glow);
  transform: rotate(6deg) translateY(7px);
  margin-left: -9px; position: relative; z-index: 1;
}
.nq-bub--out i { height: 3px; background: rgba(6, 18, 29, .55); }
.nq-bub--out i:nth-child(1) { width: 16px; }
.nq-bub--out i:nth-child(2) { width: 10px; }

/* ---- the memory beat's rows (rEncourage2) ----
   Ported straight from the marketing homepage's "YOUR PLATE. I'VE GOT IT."
   section (usenovaos.com /assistant, .plate-row + .pill) — same recipe,
   same tokens, just renamed into the quiz's nq- namespace: a compact
   single-line row (shared icon, italic Exo2 label, a pill on the right)
   instead of the old two-line .nq-feat blocks with a 52px gradient mark per
   row. Left-aligned inside a centred screen, same reasoning as before — a
   left-aligned list reads as "what you get", centred reads as decoration. */
.nq-plate { display: grid; margin-top: 26px; text-align: left; }
.nq-plate-row { display: flex; align-items: center; gap: 14px; padding: 13px 4px; border-bottom: 1px solid var(--line-2); }
.nq-plate-row:last-child { border-bottom: 0; }
.nq-plate-row svg { width: 18px; height: 18px; flex: none; color: var(--cy-deep); }
.nq-plate-row .t {
  flex: 1 1 0; min-width: 0; font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-md); line-height: 1.2; color: var(--ink);
}
.nq-plate-row .pill {
  flex: none; padding: 4px 12px; border-radius:var(--r-pill); background: var(--tint);
  font-size: var(--fs-fine); font-weight: 700; color: var(--cy-deep);
}

/* ---- encourage1's split visual: their pick + the other one, joined by a
   plus — the SAME two marks the goal cards wear, for continuity ---- */
.nq-split { display: flex; align-items: center; justify-content: center; gap: 16px; margin: 0 0 24px; }
.nq-split .nq-goal-ico { width: 64px; height: 64px; }
.nq-split .nq-models { height: 64px; }
.nq-split .nq-model { width: 38px; height: 38px; }
.nq-split .nq-model img { width: 21px; height: 21px; }
/* the bubble pair scales with it, so the "your pick + the other one" row keeps
   both marks the same weight */
.nq-split .nq-bubbles { height: 64px; }
.nq-split .nq-bub { width: 38px; height: 29px; }
.nq-split .nq-bub--in i { width: 4.5px; height: 4.5px; }
.nq-split .nq-bub--out i:nth-child(1) { width: 18px; }
.nq-split .nq-bub--out i:nth-child(2) { width: 11px; }
.nq-split-plus { display: flex; color: var(--faint); flex: none; }

/* ---- paywall pricing tiers: bespoke to this screen (see rPaywall's own
   comment for why this isn't the shared mountPlanGrid here). 2026-08-15 pass:
   Vincent wants the paywall to feel like the premium moment it is ("make it
   exciting... both plans clear"), so each card now carries the struck anchor
   price, a percent-off chip, and its real feature list. The REAL price is the
   biggest thing on the card, in display type; the anchor is small, struck, and
   faint beside it — additive context, never a replacement. ---- */
/* ---- paywall: the timeline, the billing switch, then the tiers ---- */
/* the monthly / yearly switch — same segmented-control language as the
   marketing page's, rebuilt on this page's tokens rather than imported */
.nq-cyc {
  display: flex; gap: 4px; margin: 22px 0 0; padding: 4px;
  border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--paper);
}
.nq-cyc-opt {
  flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 10px 12px; border: 0; border-radius: var(--r-pill); background: none;
  font: inherit; font-size: var(--fs-note); font-weight: 640; color: var(--sub);
  cursor: pointer; transition: background .18s var(--ease), color .18s var(--ease);
}
.nq-cyc-opt.is-on { background: var(--grad); color: var(--dk); box-shadow: var(--shadow-card); }
.nq-cyc-save {
  font-size: var(--fs-fine); font-weight: 700; padding: 2px 7px; border-radius: var(--r-pill);
  background: var(--cy-pale); color: var(--cy-deep);
}
.nq-cyc-opt.is-on .nq-cyc-save { background: rgba(255, 255, 255, .75); }

.nq-tiers { display: flex; flex-direction: column; gap: 14px; margin-top: 8px; }
/* The paywall's fixed CTA is taller than most (label + foot line) — without
   extra floor the yearly card's "billed once a year" fine print slid half
   underneath it on a 390px phone. */
.nq-body:has(.nq-tier) { padding-bottom: 240px; }

.nq-tier {
  position: relative; width: 100%; text-align: left; padding: 18px;
  border: 1.5px solid var(--line); border-radius:var(--r-18); background: var(--paper);
  box-shadow: var(--shadow-card); font: inherit; color: inherit;
  cursor: pointer; transition: border-color .18s var(--ease), box-shadow .18s var(--ease), transform .18s var(--ease);
}
.nq-tier:hover { border-color: var(--cy-mid); transform: translateY(-1px); }
.nq-tier.is-on {
  border-color: var(--cy-mid); background: var(--choice-on-bg);
  box-shadow: 0 0 0 1px var(--cy-mid), var(--shadow-lift);
}
.nq-tier.is-popular { margin-top: 6px; }   /* room for the floating badge */
.nq-tier-badge {
  position: absolute; top: -11px; left: 16px; padding: 3px 10px; border-radius:var(--r-pill);
  background: var(--grad); color: var(--accent-fg); font-size: var(--fs-label);
  font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  box-shadow: var(--glow);
}
.nq-tier-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.nq-tier-top strong {
  font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-h3); letter-spacing: -.01em; color: var(--ink);
}
.nq-tier-price { display: flex; align-items: baseline; gap: 6px; }
.nq-tier-price b { font-family: var(--disp); font-style: italic; font-weight: 800; font-size:var(--fs-jumbo); line-height: 1; color: var(--cy-deep); }
.nq-tier-price > span { font-size: var(--fs-note); color: var(--sub); }
/* Design feedback, 2026-08-18: "remove the fake discount on all plans and
   put a clear monthly price underneath the daily price." Replaces the old
   .nq-tier-was (struck "usually" price) / .nq-tier-deal ("% off" badge). */
.nq-tier-monthly { display: block; margin-top: 6px; font-size: var(--fs-note); color: var(--sub); font-weight: 560; }
.nq-tier-feats {
  list-style: none; margin: 12px 0 0; padding: 12px 0 0;
  border-top: 1px solid var(--line-2);
  display: flex; flex-direction: column; gap: 7px;
}
.nq-tier-feats li { display: flex; align-items: center; gap: 8px; font-size: var(--fs-sm); color: var(--mut); }
.nq-tier-feats li svg { flex: none; color: var(--cy-deep); }
.nq-tier-full { display: block; margin-top: 10px; font-size: var(--fs-fine); color: var(--faint); }

/* ---- "What you get with Nova" — the Zeely-structure feature rows.
   Each feature is its OWN generous bordered card: a big gradient icon chip
   (same recipe as .nq-goal-ico, the chip Vincent already approved on the goal
   screen) · bold title · one-line gray sub. Structure from the Zeely
   reference; every color strictly from this page's light cyan tokens — the
   reference is dark-mode, this page is not. NOT .nq-panel: those toggle rows
   are deliberately plain, this screen is the bigger, premium moment. ---- */
.nq-wyg-row {
  display: flex; align-items: center; gap: 16px;
  padding: 18px; margin: 0 0 12px;
  border: 1px solid var(--line); border-radius:var(--r-22);
  background: var(--paper); box-shadow: var(--shadow-card);
}
.nq-wyg-ico {
  width: 58px; height: 58px; flex: none; border-radius:var(--r-16);
  display: flex; align-items: center; justify-content: center;
  background: var(--grad); color: var(--dk); box-shadow: var(--glow);
}
.nq-wyg-main { flex: 1; min-width: 0; }
.nq-wyg-main strong {
  display: block; font-size:var(--fs-goal); font-weight: 720;
  color: var(--ink); letter-spacing: -.01em; line-height: 1.3;
}
.nq-wyg-main span { display: block; margin-top: 4px; font-size: var(--fs-sm); color: var(--sub); line-height: 1.45; }

/* ---- paywall's condensed "what you get" reinforcement ---- */
/* the scroll sentinel that ungates the CTA on the "what you get" screen */
#nqWygEnd, #nqStepsEnd { height: 1px; }
.nq-wyg-mini { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.nq-wyg-mini li { display: flex; align-items: center; gap: 9px; font-size: var(--fs-sm); color: var(--mut); }
.nq-wyg-mini li svg { flex: none; color: var(--cy-deep); }

/* ---- paywall extras ---- */
.nq-promo-link {
  background: none; border: 0; padding: 4px 0; cursor: pointer; font: inherit;
  font-size: var(--fs-note); font-weight: 600; color: var(--cy-deep);
  text-decoration: underline; text-underline-offset: 3px;
}
.nq-promo { margin-top: 10px; }
.nq-who { display: flex; align-items: center; gap: 10px; margin: 0 0 16px; flex-wrap: wrap; }
.nq-who .nas-chip { margin: 0; }
.nq-who a { font-size: var(--fs-note); color: var(--sub); }

/* ---- trial hook (screen 17): "we want you to try Nova for free" ----
   2026-08-17. A minimal beat ahead of the explainer — one line, one taste of
   what texting Nova actually sounds like, one CTA. Deliberately NOT another
   feature list; whatyouget already did that job one screen ago. */
.nq-hook-card {
  border: 1px solid var(--line); border-radius: var(--r-16);
  background: var(--paper); padding: 18px 18px 14px; margin-top: 22px;
  box-shadow: var(--shadow-card);
}
.nq-hook-thread { display: flex; flex-direction: column; gap: 8px; }
.nq-hook-bubble {
  align-self: flex-start; max-width: 86%; padding: 10px 14px;
  border-radius: var(--r-14) var(--r-14) var(--r-14) var(--r-4);
  background: var(--choice-on-bg);
  color: var(--ink); font-size: var(--fs-sm); line-height: 1.42;
}
/* The reply-length screen (2026-08-19) asked for the bubbles by colour —
   Vincent: "it should be a blue texting bubble". The default fill above is
   --choice-on-bg, which is also what .nq-choice.is-on paints itself, so a
   bubble sitting inside a SELECTED length card would vanish into it. --cy-pale
   is the page's own light blue and reads on both --paper and --choice-on-bg. */
.nq-hook-bubble--blue { background: var(--cy-pale); }
/* The one bubble on that screen that is the USER talking, not Nova: flipped to
   the right with the tail on the other corner, white like the outgoing bubbles
   in .nq-bubble, so "same question, four answers" is legible as a thread. */
.nq-hook-bubble--out {
  align-self: flex-end;
  border-radius: var(--r-14) var(--r-14) var(--r-4) var(--r-14);
  background: var(--paper); border: 1px solid var(--line);
}
.nq-hook-caption {
  display: flex; align-items: center; gap: 6px; margin-top: 14px;
  padding-top: 12px; border-top: 1px dashed var(--line);
  font-size: var(--fs-fine); color: var(--faint);
}
.nq-hook-caption svg { flex: none; color: var(--cy-deep); }
.nq-hook-checks { list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.nq-hook-checks li { display: flex; align-items: center; gap: 9px; font-size: var(--fs-sm); color: var(--mut); font-weight: 560; }
.nq-hook-checks li svg { flex: none; color: var(--cy-deep); }

/* ---- trial timeline (paywall / explainer screen) ----
   Vincent, 2026-08-17, handed over a CalAI-style reference: a vertical
   "Today / in 2 days / in 3 days" timeline replacing a flat value list as the
   thing that actually explains the trial. Ours differs from the reference in
   the one place honesty requires it: there is no card on file, so day 3 is
   "your access locks", never "billing starts" — nothing can auto-charge
   someone who was never asked for a card. */
.nq-tl { display: flex; flex-direction: column; margin: 20px 0 4px; }
.nq-tl-step { position: relative; display: flex; gap: 14px; padding-bottom: 22px; }
.nq-tl-step.is-last { padding-bottom: 0; }
.nq-tl-step:not(.is-last)::before {
  content: ''; position: absolute; left: 15px; top: 32px; bottom: 2px; width: 2px;
  background: linear-gradient(var(--cy-mid), var(--line));
}
.nq-tl-ico {
  position: relative; z-index: 1; flex: none; width: 32px; height: 32px;
  border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: var(--grad); color: var(--dk);
}
.nq-tl-ico--mid { background: var(--cy-pale); color: var(--cy-deep); }
.nq-tl-ico--dark { background: var(--ink); color: var(--cy-lo); }
.nq-tl-main { padding-top: 4px; }
.nq-tl-main strong { display: block; font-size: var(--fs-sm); font-weight: 720; color: var(--ink); margin-bottom: 2px; }
.nq-tl-main span { display: block; font-size: var(--fs-note); color: var(--sub); line-height: 1.45; }
/* ---- the same timeline, used on the celebration screen ----
   Three lines around it, no new component: the payoff (the biggest thing on
   the screen after the name), the one-line lead-in that tells you the list is
   an order of events, and the billing fact demoted to the quiet note it should
   always have been — it is true and has to be on screen, but it is not what
   somebody who just paid is here to read. */
.nq-payoff {
  margin: 14px 0 0; font-family: var(--disp); font-style: italic; font-weight: 800;
  font-size: var(--fs-h-quiz); line-height: 1.05; letter-spacing: -.01em;
  text-transform: uppercase; color: var(--ink);
}
.nq-tl-lead { margin: 12px 0 0; font-size: var(--fs-md); line-height: 1.5; color: var(--sub); }
.nq-celebrate-billing {
  margin-top: 16px; padding-top: 13px; border-top: 1px solid var(--line); color: var(--faint);
}
.nq-nocard {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  margin: 14px 0 0; font-size: var(--fs-note); color: var(--faint); font-weight: 560;
}
.nq-nocard svg { flex: none; color: var(--cy-deep); }

/* ---- screen transitions: reuse the page's own reveal ---- */
.nq [data-nq-screen] > * { opacity: 0; transform: translateY(12px); animation: nas-in .5s var(--ease) forwards; animation-delay: calc(var(--i, 0) * 55ms); }

@media (max-width: 400px) {
  .nq { padding: 0 16px; }
  .nq-cta { padding-left: 16px; padding-right: 16px; }
  .nq-code { gap: 6px; }
  .nq-code input { height: 52px; font-size:var(--fs-stat); }
}
@media (min-width: 720px) {
  .nq-top { padding-top: 26px; }
  .nq-body { padding-top: 30px; }
}
@media (prefers-reduced-motion: reduce) {
  .nq [data-nq-screen] > * { animation: none; opacity: 1; transform: none; }
  .nq-bstep.is-now .nq-tick, .nq-code.is-bad input { animation: none; }
  .nq-nameok { transition: opacity .18s var(--ease); transform: translateY(-50%); }
  .nq-progress i, .nq-switch, .nq-switch::after { transition: none; }
  .nq-choice:hover, .nq-pill:hover, .nq-goal:hover, .nq-goal:active, .nq-tier:hover { transform: none; }
  .nq-back:hover { transform: translateY(-50%); }
}
