/* Nova's console on intellnova.ai (assets/nova.js), and what only the landing uses: the header dock and the ignition.
   Built on site.css's tokens, AA in both themes. Motion is transform and opacity only (one stroke on the counter ring),
   and none with prefers-reduced-motion (site.css). The ids, classes and states nova.js relies on are listed in the
   brief's section 9 and in the work notes' dom-contract.md. */

/* nova.js keeps the composer in view itself as the conversation grows; the browser's own anchoring would scroll a
   second time. */
html { overflow-anchor: none; }

.nova [hidden] { display: none !important; }
button.btn { border: 0; }

/* ---------- the console: one HUD frame with its mono label tab ---------- */
.nova {
  position: relative;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  justify-items: start;
  min-width: 0;
  margin-top: 16px;
}
.nova-tab { font-weight: 500; }
/* Her panel is the one lit by her: its rim brightest near her star and fading down, a faint wash of her light at the
   top (still 93 % or more panel under any text). */
.nova-frame {
  --frame-bg: linear-gradient(180deg, color-mix(in srgb, var(--surface) 93%, #5aa6e9 7%) 0, var(--surface) 180px);
  --frame-rim: linear-gradient(180deg, color-mix(in srgb, var(--rim) 55%, transparent) 0, var(--line-strong) 160px);
  justify-self: stretch;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

/* Listening: a 1 px beam joins her to the caret (the field has the focus). The script may set --beam-top and
   --beam-bottom to the exact geometry. */
.nova-beam {
  position: absolute;
  left: 37.5px;
  top: var(--beam-top, 58px);
  bottom: var(--beam-bottom, 132px);
  z-index: 0;
  width: 1px;
  background: linear-gradient(#73b6f0, #73b6f04d);
  opacity: 0;
  transition: opacity .3s var(--ease);
  pointer-events: none;
}
.nova:has(#nova-q:focus) .nova-beam { opacity: 1; }

.nova-head { position: relative; display: flex; align-items: center; gap: 16px; min-height: 64px; padding: 12px 18px 2px 26px; }
/* The faint dot grid, only inside her light. */
.nova-head::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 240px;
  height: 150px;
  background-image: radial-gradient(var(--dot) 1px, transparent 1.3px);
  background-size: 12px 12px;
  -webkit-mask-image: radial-gradient(circle at 38px 38px, #000 0, transparent 70%);
  mask-image: radial-gradient(circle at 38px 38px, #000 0, transparent 70%);
  pointer-events: none;
}
.nstar-console { --s: 24px; }
/* Her states from nova.js's own classes, so the console's star follows her even before any other script runs. */
.nova:has(#nova-q:focus) .nstar-console .nstar-bloom { transform: scale(.62); }
.nova.is-typing .nstar-console .nstar-orbit { animation-duration: 1.4s; }
.nova.is-joined .nstar-console .nstar-ring { animation: nstar-ring .9s var(--ease) both; }

/* The counter: the O ring fills once to the real ratio (--nova-ratio, 0 to 1, on #nova-bar); it never counts up. */
.nova-count {
  flex: 1 1 auto;
  min-width: 0;
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 8px 10px;
  text-align: right;
}
.nova-bar { flex: none; width: 34px; height: 34px; }
.nova-ring { width: 100%; height: 100%; transform: rotate(-90deg); }
.nova-ring circle { fill: none; stroke-width: 6; }
.nova-ring-track { stroke: var(--line-strong); }
.nova-bar-fill {
  stroke: var(--gold);
  stroke-dasharray: 1;
  stroke-dashoffset: calc(1 - var(--nova-ratio, 0));
  transition: stroke-dashoffset 1.4s var(--ease);
}
.nova-count-line { min-width: 0; font-size: 14px; line-height: 1.35; color: var(--ink); }
.nova-num { font: 500 15px/1 var(--mono); color: var(--gold); font-variant-numeric: tabular-nums; }
.nova-timer { flex-basis: 100%; font: 500 20px/1.1 var(--mono); letter-spacing: .02em; color: var(--gold); font-variant-numeric: tabular-nums; }

/* ---------- the conversation ---------- */
.nova-scroll { position: relative; padding: 8px 18px 6px 37px; }
.nova-feed, .nova-log { display: grid; gap: 16px; }
.nova-msg { position: relative; min-width: 0; }
.nova-text { margin: 0; font-size: 16px; line-height: 1.55; color: var(--ink); overflow-wrap: break-word; }
/* Her lines: no bubble, a 2 px light edge, her star and NOVA (on the text itself, so the word-by-word copy that
   nova.js lays over it lines up exactly). */
.nova-msg.from-nova {
  padding-left: 16px;
  background: linear-gradient(#73b6f0, #4f9fe6 55%, #4f9fe600) 0 3px / 2px calc(100% - 6px) no-repeat;
}
.nova-msg.from-nova > .nova-ghost { left: 16px; }
.nova-text a { padding: 12px 0; } /* a 44 px tap area for the contact address; the line itself does not move */
.nova-noscript { padding-left: 16px; font-size: 15px; color: var(--muted); }
.nova-noscript a { padding: 12px 0; }

/* The visitor's lines: right-aligned panels marked YOU. */
.nova-msg.from-you {
  justify-self: end;
  display: grid;
  justify-items: end;
  gap: 6px;
  max-width: min(88%, 30em);
  animation: nova-in .3s var(--ease) both;
}
.nova-msg.from-you::before {
  content: "You";
  content: "You" / "";
  font: 500 11px/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
}
.nova-msg.from-you .nova-text {
  --cx: 8px;
  --cy: 11px;
  padding: 10px 14px;
  background: var(--surface-3);
  line-height: 1.45;
  overflow-wrap: anywhere;
  clip-path: polygon(0 0, calc(100% - var(--cx)) 0, 100% var(--cy), 100% 100%, var(--cx) 100%, 0 calc(100% - var(--cy)));
}

/* Word by word: the real text waits invisible underneath, an aria-hidden copy fades in on top of it. */
@media (prefers-reduced-motion: no-preference) {
  /* The greeting is in the HTML; it waits for its reveal, or shows by itself if nova.js never runs. */
  html.js .nova-greet:not(.is-shown) { opacity: 0; animation: nova-show .4s var(--ease) 1.8s forwards; }
}
html.js .nova-text.is-revealing { opacity: 0; animation: none; }
.nova-ghost { position: absolute; top: 0; left: 0; right: 0; pointer-events: none; }
.nova-ghost span { opacity: 0; animation: nova-show .17s var(--ease) forwards; }
.nova-nb { white-space: nowrap; }
.nova-caret, .nova-ghost .nova-caret {
  display: inline-block;
  width: .5em;
  height: 1em;
  margin: 0 -.58em 0 .08em;
  vertical-align: -.14em;
  background: var(--rim);
  opacity: 1;
  animation: none;
}
.nova-caret.is-resting, .nova-ghost .nova-caret.is-resting { animation: caret .8s steps(1) infinite; }
.nova-ghost .nova-nb { opacity: 1; animation: none; }
.nova-ghost .nova-ghost-link {
  opacity: 1;
  animation: none;
  color: var(--link);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

/* Nova is typing: only while a typed question is with the brain. Her line, then three points of light. */
.nova-typing { display: flex; align-items: center; gap: 5px; min-height: 26px; }
.nova-typing::before { content: attr(aria-label); margin-right: 6px; font-size: 16px; color: var(--muted); }
.nova-typing i { width: 4px; height: 4px; background: var(--rim); animation: nova-dot 1s var(--ease) infinite; }
.nova-typing i:nth-child(2) { animation-delay: .18s; }
.nova-typing i:nth-child(3) { animation-delay: .36s; }

/* Under an answer: the source tag, a docked module, or the small "Get your invite" key. */
.nova-source {
  margin-top: 8px;
  font: 500 11px/1.4 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--faint);
}
.nova-offer { margin-top: 12px; animation: nova-in .35s var(--ease) .45s both; }

/* Joined: a short glow of star light behind her line, where the card was. Opacity only. */
.nova-msg.is-done::before {
  content: "";
  position: absolute;
  inset: -14px -10px;
  z-index: -1;
  background: radial-gradient(70% 90% at 12% 50%, #5aa6e940, transparent 72%);
  opacity: 0;
  animation: nova-glow 1.6s var(--ease) both;
  pointer-events: none;
}

/* The docked modules (templates in index.html): the example room, the escrow flow, the posters. */
.nova-dock {
  --cx: 9px;
  --cy: 12px;
  position: relative;
  isolation: isolate;
  margin-top: 12px;
  padding: 14px;
  background: var(--line-strong);
  font: 500 11px/1.45 var(--mono);
  letter-spacing: .06em;
  clip-path: polygon(0 0, calc(100% - var(--cx)) 0, 100% var(--cy), 100% 100%, var(--cx) 100%, 0 calc(100% - var(--cy)));
}
.nova-dock::before {
  content: "";
  position: absolute;
  inset: 1px;
  z-index: -1;
  background: var(--surface-2);
  clip-path: polygon(0 0, calc(100% - var(--cx)) 0, 100% var(--cy), 100% 100%, var(--cx) 100%, 0 calc(100% - var(--cy)));
}
.nova-dock-cap { margin-bottom: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.nova-dock-seats { display: flex; gap: 6px; margin-bottom: 12px; }
.nova-dock-seats span { flex: 1; display: grid; place-items: center; height: 34px; border: 1px solid var(--line-strong); font-size: 12px; color: var(--muted); }
.nova-dock-rows { display: grid; gap: 6px; margin: 0; }
.nova-dock-rows div { display: flex; justify-content: space-between; gap: 12px; }
.nova-dock-rows dt { letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.nova-dock-rows dd { margin: 0; }
.nova-dock-flow { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.nova-dock-flow svg { flex: none; width: 7px; height: 10px; color: var(--rim); }
.nova-dock-o { display: inline-flex; align-items: center; gap: 6px; text-transform: uppercase; }
.nova-dock-o::before { content: ""; width: 14px; height: 14px; border-radius: 50%; border: 3px solid var(--ink); }
.nova-dock-split { display: grid; gap: 4px; text-transform: uppercase; }
.nova-dock-split span:first-child { color: var(--muted); }
.nova-dock-posters { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 0; background: none; clip-path: none; }
.nova-dock-posters::before { display: none; }
.nova-dock-posters figure { display: grid; align-content: start; gap: 6px; margin: 0; }
.nova-dock-posters img {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  background: var(--media);
  clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 11px, 100% 100%, 0 100%);
}
.nova-dock-posters figcaption { font: 700 11px/1.2 var(--display); text-transform: uppercase; }

/* ---------- the keys: "Get your invite" first, then her questions; the composer; the note ---------- */
.nova-controls { position: relative; display: grid; gap: 12px; padding: 10px 18px 16px; }
.nova-controls.is-in { animation: nova-in .5s var(--ease) .3s both; }
/* Phone: one sideways row that reaches the frame's edges, so a key cut there says there is more this way. */
.nova-menu {
  --fade-a: 0px;
  --fade-b: 0px;
  display: flex;
  gap: 6px;
  margin: 0 -17px;
  padding: 2px 17px 4px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 17px;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(var(--mask-dir, 90deg), transparent, #000 var(--fade-a), #000 calc(100% - var(--fade-b)), transparent);
  mask-image: linear-gradient(var(--mask-dir, 90deg), transparent, #000 var(--fade-a), #000 calc(100% - var(--fade-b)), transparent);
}
.nova-menu.is-more-before { --fade-a: 17px; }
.nova-menu.is-more-after { --fade-b: 48px; }
.nova-menu::-webkit-scrollbar { display: none; }
.nova-chips { display: contents; }
.nova-menu > .btn { flex: none; scroll-snap-align: start; }
.nova-chip {
  --cx: 9px;
  --cy: 12px;
  --chip-rim: var(--line-strong);
  position: relative;
  isolation: isolate;
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin: 0;
  padding: 10px 16px 10px 14px;
  border: 0;
  background: var(--chip-rim);
  color: var(--ink);
  font: 700 12px/1.35 var(--display);
  letter-spacing: .03em;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  scroll-snap-align: start;
  clip-path: polygon(0 0, calc(100% - var(--cx)) 0, 100% var(--cy), 100% 100%, var(--cx) 100%, 0 calc(100% - var(--cy)));
  -webkit-tap-highlight-color: transparent;
  transition: transform .15s var(--ease);
}
.nova-chip::before {
  content: "";
  position: absolute;
  inset: 1px;
  z-index: -1;
  background: var(--surface-2);
  clip-path: polygon(0 0, calc(100% - var(--cx)) 0, 100% var(--cy), 100% 100%, var(--cx) 100%, 0 calc(100% - var(--cy)));
}
/* The run of light along its edge when it is pressed (or, in the attract loop, simply lit: assets/nova.js). */
.nova-chip::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--rim), transparent);
  opacity: 0;
  transform: translateX(-100%);
  pointer-events: none;
}
.nova-chip:active, .nova-chip.is-pressed { transform: translateY(1px); }
.nova-chip:is(:active, .is-pressed, .is-attract)::after { animation: edge-run .45s var(--ease); }
.nova-key.is-attract::after { animation: edge-run .45s var(--ease); }
.nova-chip:focus-visible { outline: 2px solid var(--ink); outline-offset: -5px; }
@media (hover: hover) { .nova-chip:hover { --chip-rim: color-mix(in srgb, var(--rim) 55%, transparent); } } /* no hover left behind by a tap */
.nova.is-busy .nova-chip { cursor: progress; }
.nova-chip.is-queued { --chip-rim: var(--rim); }

.nova-askbox { display: grid; gap: 10px; }
.nova-ask {
  --cx: 10px;
  --cy: 13px;
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 50px;
  padding: 3px 3px 3px 16px;
  background: var(--field-line);
  clip-path: polygon(0 0, calc(100% - var(--cx)) 0, 100% var(--cy), 100% 100%, var(--cx) 100%, 0 calc(100% - var(--cy)));
  transition: background-color .2s var(--ease);
}
.nova-ask::before {
  content: "";
  position: absolute;
  inset: 1px;
  z-index: -1;
  background: var(--bg);
  clip-path: polygon(0 0, calc(100% - var(--cx)) 0, 100% var(--cy), 100% 100%, var(--cx) 100%, 0 calc(100% - var(--cy)));
}
.nova-ask:focus-within { background: var(--rim); }
.nova-ask:focus-within::before { inset: 2px; }
.nova-prompt { flex: none; width: 8px; height: 11px; color: var(--rim); }
.nova-ask input {
  flex: 1;
  min-width: 0;
  min-height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  font: 400 17px/1.3 var(--sans); /* 16 px or more: Safari on the iPhone does not zoom the page on focus */
  caret-color: #73b6f0;
}
.nova-ask input:focus { outline: none; } /* the rim of the whole composer lights instead */
.nova-ask input::placeholder { color: var(--muted); opacity: 1; }
.nova-left { flex: none; font: 500 12px/1 var(--mono); color: var(--muted); font-variant-numeric: tabular-nums; }
.nova-send {
  --cx: 8px;
  --cy: 11px;
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: var(--accent);
  color: var(--on-accent);
  cursor: pointer;
  clip-path: polygon(0 0, calc(100% - var(--cx)) 0, 100% var(--cy), 100% 100%, var(--cx) 100%, 0 calc(100% - var(--cy)));
  -webkit-tap-highlight-color: transparent;
  transition: transform .15s var(--ease), opacity .2s var(--ease);
}
.nova-send svg { width: 10px; height: 13px; }
.nova-send:active { transform: translateY(1px); }
.nova-send:focus-visible { outline: 2px solid currentColor; outline-offset: -5px; }
.nova-send[aria-disabled="true"] { opacity: .45; cursor: progress; }
.nova-note { font-size: 13px; line-height: 1.5; color: var(--muted); text-wrap: pretty; }

/* A Turnstile box stays out of the layout until Turnstile puts something in it. */
.nova-check:empty { display: none; }

/* ---------- the invite card, inside the conversation ---------- */
.nova-invite { display: grid; justify-items: start; transform-origin: 50% 0; animation: nova-in .4s var(--ease) both; }
.nova-invite:focus { outline: none; }
.nova-invite-tab { background: color-mix(in srgb, var(--rim) 26%, transparent); }
.nova-invite-frame { --frame-rim: var(--rim); --frame-bg: var(--surface-2); justify-self: stretch; display: grid; gap: 14px; padding: 18px 18px 20px; }
/* Joined: the card folds to a line (then assets/nova.js sends that line up into her star). */
.nova-invite.is-leaving { transform: scaleY(.004); transition: transform .22s var(--ease); }
.nova-field { display: grid; gap: 6px; }
.nova-field label { font: 500 11px/1.4 var(--mono); letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.nova-field .opt { color: var(--faint); }
/* The rim is the wrapper's background, the field sits 1 px inside it (2 px, lit, with the focus). */
.nova-input {
  --cx: 8px;
  --cy: 11px;
  position: relative;
  isolation: isolate;
  display: block;
  padding: 1px;
  background: var(--field-line);
  clip-path: polygon(0 0, calc(100% - var(--cx)) 0, 100% var(--cy), 100% 100%, var(--cx) 100%, 0 calc(100% - var(--cy)));
}
.nova-input::before {
  content: "";
  position: absolute;
  inset: 1px;
  z-index: -1;
  background: var(--bg);
  clip-path: polygon(0 0, calc(100% - var(--cx)) 0, 100% var(--cy), 100% 100%, var(--cx) 100%, 0 calc(100% - var(--cy)));
}
.nova-input:focus-within { background: var(--rim); }
.nova-input:focus-within::before { inset: 2px; }
.nova-field input {
  display: block;
  width: 100%;
  min-height: 46px;
  padding: 0 14px;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: 400 17px/1.3 var(--sans);
}
.nova-field input:focus { outline: none; } /* the wrapper's rim lights instead */
/* An error is one of her lines; the field it is about gets a quiet outline. */
.nova-input:has(input[aria-invalid="true"]) { background: var(--err); }
.nova-consent {
  position: relative;
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 12px;
  align-items: start;
  min-height: 44px;
  padding: 4px 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink);
  cursor: pointer;
}
/* The box: the real checkbox is invisible on top of a chamfered one drawn here (which lets every tap through). */
.nova-consent input { position: absolute; left: 0; top: 6px; width: 22px; height: 22px; margin: 0; opacity: 0; cursor: pointer; }
.nova-box {
  --cx: 5px;
  --cy: 7px;
  position: relative;
  isolation: isolate;
  width: 22px;
  height: 22px;
  margin-top: 2px;
  background: var(--field-line);
  pointer-events: none;
  clip-path: polygon(0 0, calc(100% - var(--cx)) 0, 100% var(--cy), 100% 100%, var(--cx) 100%, 0 calc(100% - var(--cy)));
}
.nova-box::before, .nova-box::after { content: ""; position: absolute; }
.nova-box::before {
  inset: 1px;
  z-index: -1;
  background: var(--surface-2);
  clip-path: polygon(0 0, calc(100% - var(--cx)) 0, 100% var(--cy), 100% 100%, var(--cx) 100%, 0 calc(100% - var(--cy)));
}
.nova-box::after { inset: 7px; background: var(--on-accent); opacity: 0; }
.nova-consent input:checked + .nova-box { background: var(--accent); }
.nova-consent input:checked + .nova-box::before { opacity: 0; }
.nova-consent input:checked + .nova-box::after { opacity: 1; }
.nova-consent input[aria-invalid="true"] + .nova-box { background: var(--err); }
.nova-consent:has(input:focus-visible) { outline: 2px solid var(--rim); outline-offset: 4px; }
/* Its own line, outside the box's label: a tap meant for the box can never land on it. */
.nova-privacy { margin-top: -8px; font-size: 15px; color: var(--muted); }
.nova-privacy a { display: inline-flex; align-items: center; min-height: 44px; }
.nova-submit { display: grid; gap: 12px; justify-items: stretch; }
/* Its check box keeps its gap from the start, so the card does not grow when Turnstile fills the box. */
.nova-submit .nova-check:empty { display: block; }
.nova-submit .btn { min-height: 48px; padding: 0 22px; font-size: 12px; }
/* Busy: the label keeps its full contrast; a light slides along the bottom edge (still, with reduced motion). */
.nova-submit .btn[aria-busy="true"] { cursor: progress; }
.nova-submit .btn[aria-busy="true"]::after {
  right: auto;
  width: 40%;
  height: 3px;
  background: var(--on-accent);
  opacity: .55;
  transform: none;
  animation: nova-busy 1.1s linear infinite;
}

@keyframes nova-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes nova-show { to { opacity: 1; } }
@keyframes nova-dot { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }
@keyframes nova-glow { 0% { opacity: 0; } 25% { opacity: 1; } 100% { opacity: 0; } }
@keyframes nova-busy { from { transform: translateX(-100%); } to { transform: translateX(250%); } }

/* ---------- wide: the console stays on the right, the height of the screen; the conversation scrolls inside it and
   stays pinned to its newest line (column-reverse), so nova.js's own scrolling is never needed there ---------- */
@media (min-width: 1000px) {
  .stage > .nova {
    grid-column: 2;
    grid-row: 1 / span 8;
    position: sticky;
    top: calc(var(--top-h) + var(--status-h) + 20px);
    align-self: start;
    height: calc(100dvh - var(--top-h) - var(--status-h) - 40px);
    min-height: 520px;
    max-height: 920px;
    margin-top: 32px;
  }
  .nova-frame { height: 100%; }
  .nova-head { flex: none; min-height: 58px; padding-top: 8px; }
  .nova-scroll {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column-reverse;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--line-strong) transparent;
    -webkit-mask-image: linear-gradient(transparent, #000 14px);
    mask-image: linear-gradient(transparent, #000 14px);
  }
  .nova-feed { margin-bottom: auto; padding-bottom: 4px; }
  .nova-scroll:focus-visible { outline-offset: -2px; }
  .nova-controls { flex: none; }
  .nova-beam { --beam-bottom: 108px; }
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-scroll { flex: 0 0 auto; padding-bottom: 2px; }
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-controls { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: 10px; padding-top: 6px; padding-bottom: 14px; }
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-askbox { margin-top: auto; }
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-menu {
    --mask-dir: 180deg;
    flex: 0 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 0;
    margin: 0;
    padding: 0;
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
    scroll-snap-type: none;
    scrollbar-width: none;
  }
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-menu.is-more-before { --fade-a: 14px; }
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-menu.is-more-after { --fade-b: 28px; }
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-menu > .btn { flex: none; justify-content: flex-start; min-height: 44px; margin-bottom: 2px; padding-left: 14px; }
  /* One list, not ten slabs: a hairline over each row; the run of light goes along it. */
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-chip {
    width: 100%;
    min-height: 44px;
    padding: 0 16px 0 30px;
    background: linear-gradient(var(--line), var(--line)) 0 0 / 100% 1px no-repeat;
    clip-path: none;
  }
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-chip::before {
    inset: 0 0 auto;
    z-index: 1;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--rim), transparent);
    clip-path: none;
    opacity: 0;
    transform: translateX(-100%);
  }
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-chip:is(:active, .is-pressed, .is-attract)::before { animation: edge-run .45s var(--ease); }
  /* The cursor: the chevron, on the key that has the pointer or the keyboard. */
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-chip::after {
    left: 13px;
    right: auto;
    top: 50%;
    bottom: auto;
    width: 7px;
    height: 10px;
    margin-top: -5px;
    background: var(--rim);
    clip-path: var(--chev);
    transform: translateX(-4px);
    transition: opacity .15s var(--ease), transform .15s var(--ease);
    animation: none;
  }
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-chip:is(:hover, :focus-visible, .is-cursor, .is-queued, .is-attract) { background-color: color-mix(in srgb, var(--surface-3) 75%, transparent); }
  .nova:not(:has(.nova-log > * + *, .nova-invite)) .nova-chip:is(:hover, :focus-visible, .is-cursor, .is-queued, .is-attract)::after { opacity: 1; transform: none; }
  /* Once the conversation has started it needs the room: the keys dock into one sideways row (its thin scrollbar shows
     a mouse there is more), and while the card is open they step aside until it is done. */
  .nova:has(.nova-log > * + *) .nova-menu {
    margin: 0 -17px;
    padding: 2px 17px 6px;
    scroll-snap-type: x proximity;
    scrollbar-width: thin;
    scrollbar-color: var(--line-strong) transparent;
  }
  .nova:has(.nova-log > * + *) .nova-menu::-webkit-scrollbar { display: block; height: 6px; }
  .nova:has(.nova-log > * + *) .nova-menu::-webkit-scrollbar-thumb { background: var(--line-strong); }
  .nova:has(.nova-invite) .nova-menu { display: none !important; }
  /* The card itself, compact enough to sit under her line in the console: the two fields side by side, and the head
     (her star, the counter) out of the way until the card is done. */
  .nova:has(.nova-invite) .nova-head { display: none; }
  .nova:has(.nova-invite) .nova-beam { display: none; }
  .nova-invite-frame { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; padding: 16px 16px 18px; }
  .nova-invite-frame > :not(.nova-field) { grid-column: 1 / -1; }
  .nova-consent { font-size: 14px; line-height: 1.45; }
}

/* ---------- the header dock (landing only): with the scroll, her star travels from the film into the header's lockup,
   and the wordmark slides over to make room (--dock, 0 to 1, on header.top: the script sets it frame by frame as she
   flies, and .is-docked once she has landed; with motion off the class alone does it); until then the wordmark stands
   alone ---------- */
.top { --dock: 1; }
html.js body[data-page="home"] .top:not(.is-docked) { --dock: 0; }
.top .brand .nstar { opacity: var(--dock); transform: scale(calc(.4 + .6 * var(--dock))); transition: none; }
.top :is(.brand .wordmark, .arena, .beta) { transform: translateX(calc(-28px * (1 - var(--dock)))); }
html.js body[data-page="home"] .top:not(.is-docked) .brand .nstar * { animation-play-state: paused; } /* unseen: at rest */

/* ---------- the ignition, the "press start" (landing only): theme.js sets html[data-ignite="on"] before the first
   paint (motion allowed, the top of the page); it plays by itself in 2.1 s and leaves every element in its normal
   state, and the script ends it early on any input by setting data-ignite="done" ---------- */
@media (prefers-reduced-motion: no-preference) {
  html[data-ignite="on"] .film .nstar { animation: ign-star .9s var(--ease) .1s backwards; }
  html[data-ignite="on"] .film-rimlight { animation: ign-rim 1.1s var(--ease) .55s backwards; }
  html[data-ignite="on"] .film-mark { animation: ign-mark .9s var(--ease) .7s backwards; }
  html[data-ignite="on"] .film-dots { animation: ign-fade .8s var(--ease) .7s backwards; }
  html[data-ignite="on"] .ttl-hero { animation: ign-fade .45s var(--ease) 1.05s backwards; }
  html[data-ignite="on"] .stage > .nova { animation: ign-rise .5s var(--ease) 1.55s backwards; }
}
@keyframes ign-star { 0% { opacity: 0; transform: scale(.04); } 35% { opacity: 1; } 100% { opacity: 1; transform: none; } }
@keyframes ign-rim { 0% { opacity: 0; transform: scale(.08); } 60% { opacity: 1; } 100% { opacity: .55; transform: none; } }
@keyframes ign-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes ign-mark { from { opacity: 0; transform: scale(1.035); } to { opacity: .055; transform: none; } }
@keyframes ign-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
