/* Mobile-first. Calm, warm, unhurried. Designed for a phone held one-handed. */
:root {
  /* THE ROOM and THE PRESENCE WELL, the two grounds from design-language.md §2.
     This page used to be #000 edge to edge. That was the exception applied everywhere: the head
     image's own backdrop is literally #000, so painting the whole page black guaranteed no visible
     square around it. The cost was that the AI surfaces stopped matching the portal and the brand
     kit, and panels had nothing to sit on. Now the room is the brand near-black and true black is
     kept only where the presence sits, faded in with a radial so there is still no seam. */
  --bg: #0b0b14;
  --bg-true: #000000;     /* the presence well; anywhere the hologram sits */
  --bg-soft: #14142b;
  --bg-navy: #14142b;     /* the old navy, kept for cards/bubbles that want a touch more lift */
  --card: #26264a;
  --ink: #f4f2ee;
  --ink-soft: #b9b6c9;
  --accent: #c9a86a;      /* warm gold */
  --accent-ink: #14142b;
  --user: #2f2f57;
  --radius: 18px;
  --safe-b: env(safe-area-inset-bottom, 0px);
  --serif: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Palatino, Georgia, "Times New Roman", serif;
  --content-max: 1560px;   /* the centered content band inside the full-bleed background */
}

/* A subtle, static warm vignette so the page feels like an evening room, not a monitor.
   Fixed, non-animated, behind everything; never competes with the conversation. */
body::before { content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background: radial-gradient(120% 90% at 50% 42%, transparent 58%, rgba(40,26,10,.16) 82%, rgba(20,12,4,.42) 100%); }
.screen { position: relative; z-index: 1; }

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* TESTING ONLY — always-visible reset button (bottom-left). Low-key so it stays out of the way. */
html, body { margin: 0; height: 100%; }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 18px;
  line-height: 1.5;
}

.screen { min-height: 100dvh; display: flex; flex-direction: column; }
.hidden { display: none !important; }

/* Landing / done */
.landing-inner, .attraction-inner {
  margin: auto; max-width: 560px; width: 100%;
  padding: 32px 22px calc(32px + var(--safe-b));
  display: flex; flex-direction: column; gap: 18px;
}
h1 { font-size: 2rem; margin: 0; font-weight: 600; letter-spacing: -0.01em; }
h2 { font-size: 1.5rem; margin: 0 0 4px; font-weight: 600; }
h3 { font-size: 1.05rem; margin: 18px 0 6px; color: var(--ink-soft); font-weight: 600; }
.sub { color: var(--ink-soft); margin: 0; }
.sub.small { font-size: 0.9rem; }

/* Orientation card on the landing screen. */
.orient { background: var(--card); border-radius: var(--radius); padding: 18px 20px; }
.orient h3 { margin: 0 0 10px; color: var(--ink); }
.orient ul { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 10px; }
.orient li { color: var(--ink-soft); }
.orient li strong { color: var(--ink); font-weight: 600; }
.orient .sub.small { margin-top: 14px; }

button { font: inherit; cursor: pointer; border: none; border-radius: var(--radius); }
.primary {
  background: var(--accent); color: var(--accent-ink);
  padding: 15px 20px; font-weight: 600; border-radius: var(--radius);
}
.primary:active { transform: translateY(1px); }
.ghost {
  background: transparent; color: var(--ink-soft);
  padding: 12px 14px; border: 1px solid #3a3a63; border-radius: var(--radius);
  text-decoration: none; text-align: center; display: block;
}
.ghost.small { padding: 7px 12px; font-size: 0.85rem; }

/* Pre-interview setup screen */
.s-field { display: flex; flex-direction: column; gap: 7px; color: var(--ink-soft);
  font-size: 0.95rem; }
.s-field input { padding: 12px 14px; border-radius: var(--radius); border: 1px solid #3a3a63;
  background: var(--bg-soft); color: var(--ink); font: inherit; }
.s-label { color: var(--ink-soft); font-size: 0.95rem; }
.s-label .req { color: var(--accent); }
.chipset .chip { background: var(--bg-soft); color: var(--ink); border: 1px solid #3a3a63;
  border-radius: 999px; padding: 9px 16px; font-size: 0.9rem; cursor: pointer; }
.chipset .chip.sel { background: var(--accent); color: var(--accent-ink);
  border-color: var(--accent); font-weight: 600; }
.s-note { font-size: 0.82rem; color: var(--ink-soft); }
#su-continue:disabled { opacity: 0.5; cursor: not-allowed; }

.resume-box { display: flex; gap: 8px; margin-top: 4px; }
.resume-box input {
  flex: 1; padding: 13px 14px; border-radius: var(--radius);
  border: 1px solid #3a3a63; background: var(--bg-soft); color: var(--ink);
  text-transform: uppercase; letter-spacing: 0.08em;
}

.code-chip {
  align-self: flex-start; background: var(--card); color: var(--accent);
  padding: 12px 18px; border-radius: 12px; font-size: 1.4rem;
  letter-spacing: 0.12em; font-weight: 600;
}

/* Chat */
/* Full-width sticky bar; its content aligns to the centered content band. */
.chat-header {
  background: var(--bg-soft); position: sticky; top: 0; z-index: 5;
  border-bottom: 1px solid #2a2a4d; padding-top: env(safe-area-inset-top, 0px);
}
.header-inner {
  display: flex; align-items: center; gap: 10px 12px; flex-wrap: wrap;
  max-width: var(--content-max); margin: 0 auto; width: 100%; padding: 12px 16px;
}
.chat-header .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); }
/* The sign on the door: serif display title in warm gold, quiet subtitle beneath. */
.header-titles { flex: 1 1 auto; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.header-title { font-family: var(--serif); font-weight: 600; font-size: 1.14rem; letter-spacing: .3px;
  color: var(--accent); line-height: 1.15; }
.header-sub { font-size: .8rem; color: var(--ink-soft); letter-spacing: .2px; }
/* Mobile: actions drop to their own row so the serif title never collides with them. */
.header-actions { display: flex; align-items: center; gap: 8px; flex: 1 1 100%;
  flex-wrap: wrap; justify-content: flex-start; }

/* The living orb: dim idle, green listening, amber thinking, blue speaking. */
.orb { width: 16px; height: 16px; border-radius: 50%; flex: 0 0 auto; background: #4a4a70;
  transition: background .3s ease; }
/* Header dot = IDENTITY, always (state is shown by the head glow + status dots, not the dot).
   --id-rgb is set per screen: consultation gold, coach purple, sounding board grey. */
.orb.idle,
.orb.listening,
.orb.thinking,
.orb.speaking { background: rgb(var(--id-rgb, 138,147,166)); }
.orb.listening,
.orb.thinking { animation: orbpulse 1.1s ease-in-out infinite; }
.orb.speaking { animation: orbpulse .8s ease-in-out infinite; }
@keyframes orbpulse { 0%,100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.4); opacity: .65; } }

/* Word being spoken, highlighted in sync with the voice. */
.bubble .w { transition: background .14s ease, color .14s ease; border-radius: 5px; padding: 0 1px; }
/* Softer, warm highlight following the voice — a gentle candlelight wash, not a hard block. */
.bubble .w.spoken-now { background: rgba(201,168,106,.28); color: #fdf6e6; }

/* (The old segmented .stepper bar was removed with the dial, 2026-07-21. Nothing referenced it,
   and a segment bar is the pattern the research argues hardest against on a long session.) */

.recap { background: #22223f; border-left: 3px solid var(--accent); padding: 12px 14px;
  border-radius: 10px; color: var(--ink-soft); align-self: stretch; }

/* Inline widget cards */
.widget-card { background: var(--card); border: 1px solid #3a3a63; border-radius: var(--radius);
  padding: 16px; display: flex; flex-direction: column; gap: 10px; align-self: stretch; }
.widget-card .w-title { font-weight: 600; color: var(--ink); }
.widget-card .w-sub, .widget-card .w-terms { font-size: 0.88rem; color: var(--ink-soft); }
.widget-card .w-terms { background: var(--bg-soft); padding: 10px 12px; border-radius: 10px; }
.w-row { display: flex; flex-direction: column; gap: 6px; }
.w-row-label { font-size: 0.85rem; color: var(--ink-soft); }
.w-opts { display: flex; flex-wrap: wrap; gap: 6px; }
.chip { background: var(--bg-soft); color: var(--ink); border: 1px solid #3a3a63;
  border-radius: 999px; padding: 7px 13px; font-size: 0.88rem; }
.chip.sel { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 600; }
.w-slider { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.w-slider input[type=range] { flex: 1 1 100%; accent-color: var(--accent); }
.w-val { color: var(--accent); font-weight: 600; font-size: 0.95rem; }
.w-pool { background: var(--bg-soft); padding: 10px 12px; border-radius: 10px;
  color: var(--ink-soft); font-size: 0.85rem; }
.w-grid { display: flex; flex-direction: column; gap: 5px; }
.w-grid-row { display: flex; gap: 5px; align-items: center; }
.w-grid-day { width: 42px; font-size: 0.82rem; color: var(--ink-soft); }
.w-cell { flex: 1; background: var(--bg-soft); color: var(--ink-soft); border: 1px solid #3a3a63;
  border-radius: 8px; padding: 8px 0; font-size: 0.8rem; }
.w-cell.sel { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.w-grid-row.blocked .w-blocked { flex: 1; text-align: center; color: #6b6890; font-size: 0.8rem;
  font-style: italic; padding: 8px 0; }
.w-submit { align-self: flex-start; margin-top: 4px; }
.w-skip { font-size: 0.8rem; color: #6b6890; font-style: italic; }

/* Pin the header + stepper: the chat box is exactly viewport-tall and ONLY #messages
   scrolls (min-height:0 lets the flex child shrink so it scrolls instead of the page). */
#chat { height: 100dvh; overflow: hidden; }
.messages { flex: 1; min-height: 0; overflow-y: auto; padding: 20px 16px 8px; display: flex;
  flex-direction: column; gap: 14px; }
.bubble {
  max-width: 85%; padding: 14px 18px; border-radius: 22px;
  line-height: 1.6; white-space: pre-wrap; word-wrap: break-word;
}
/* The shadchan's turns are NOT chat bubbles. She is a person speaking to you in a quiet room, so
   her words sit directly on the dark in serif, the way a letter does, with no container and no
   tail. The purple rounded bubble made the screen read as a messaging app, which is the exact
   thing this product is not (design-language.md §3: if it is a case, a story or a moment, it is
   serif; if it is a control, it is sans). Your own words keep a small quiet bubble, so the screen
   still has a clear speaker and a clear listener. */
.bubble.ai { background: none; padding: 2px 0 0; border-radius: 0; align-self: stretch;
  max-width: 100%; font-family: var(--serif); font-size: 1.12rem; line-height: 1.78;
  color: var(--ink); }
.bubble.me { background: var(--user); border-bottom-right-radius: 8px; align-self: flex-end;
  font-size: .98rem; }
.bubble.typing { color: var(--ink-soft); font-style: italic; font-family: var(--sans); }
/* Your words while you're still speaking — slightly dimmed until the turn is sent. */
.bubble.me.live { opacity: .72; animation: livePulse 1.4s ease-in-out infinite; }
/* Date+time under each message (via data-attr so word-highlighting can't erase it). */
.bubble[data-time]::after { content: attr(data-time); display: block; font-size: 0.66rem;
  opacity: 0.42; margin-top: 5px; letter-spacing: .2px; }
/* No timestamp under HER turns. A time under a letter is clerical noise; it belongs on your own
   messages, where it tells you when you paused and came back. */
.bubble.typing[data-time]::after, .bubble.me.live[data-time]::after,
.bubble.ai[data-time]::after { content: none; }
@keyframes livePulse { 0%,100% { opacity: .6; } 50% { opacity: .82; } }
/* THE SPOKEN LINE MUST NEVER RIDE THE BOTTOM EDGE.
   follow() calls scrollIntoView({block:"nearest"}) on each word as the voice reaches it, and
   "nearest" scrolls the minimum distance, which parks that word hard against the bottom of the
   window for the whole turn. Bottom padding cannot fix it: the scroller stops the instant the
   word is technically visible. scroll-margin is what scrollIntoView actually respects, so the
   word carries its own breathing room and the reading line sits comfortably above the edge. */
.bubble { scroll-margin-top: 24px; scroll-margin-bottom: clamp(90px, 16vh, 200px); }
.bubble .w { scroll-margin-top: 24px; scroll-margin-bottom: clamp(90px, 16vh, 200px); }

.composer {
  display: flex; flex-direction: column; align-items: center; gap: 8px; width: 100%;
  padding: 10px 12px calc(10px + var(--safe-b));
  background: var(--bg-soft); border-top: 1px solid #2a2a4d;
}
.composer textarea {
  flex: 1; resize: none; max-height: 200px;
  padding: 13px 14px; border-radius: var(--radius);
  border: 1px solid #3a3a63; background: var(--bg); color: var(--ink); font: inherit;
  line-height: 1.45;
}
.mic {
  background: var(--card); color: var(--ink); font-size: 1.3rem;
  width: 50px; height: 50px; border-radius: 50%; flex: 0 0 auto;
}
.mic.active { background: var(--accent); color: var(--accent-ink); animation: pulse 1.2s infinite; }
@keyframes pulse { 0%,100% { box-shadow: 0 0 0 0 rgba(201,168,106,.5); }
  50% { box-shadow: 0 0 0 10px rgba(201,168,106,0); } }
.send { flex: 0 0 auto; }
.listening { text-align: center; color: var(--accent); padding: 6px; font-size: 0.9rem; }

/* Hologram head — the FULL detailed image (no cutting, full quality). Its own dark backdrop
   is dropped with mix-blend-mode:screen and melts into the near-black radial stage set on
   #holo-col below, so there is no box and no circle edge anywhere: the head just glows on
   dark, the way Kevin's does. State colour follows the conversation. */
.hologram { position: relative; display: flex; align-items: center; justify-content: center;
  width: 130px; aspect-ratio: 1; --accent-rgb: var(--id-rgb, 138,147,166);   /* resting = identity tint.
  SQUARE so the measured eye/mouth percentages land exactly on the real features. */
  pointer-events: none;   /* decorative on desktop — its glow overflows onto the mic; never eat clicks */
  animation: holoHover 6s ease-in-out infinite; }
/* Four-state glow (2026-07-20, approved): "state wins" — same active colours on all three AIs;
   identity shows only at idle (the resting tint) and in the header dot. */
.hologram.idle      { --accent-rgb: var(--id-rgb, 138,147,166); } /* dim identity tint — resting */
.hologram.listening { --accent-rgb: 64,132,255; }  /* blue — hearing you */
.hologram.thinking  { --accent-rgb: 255,176,64; }  /* amber — thinking */
.hologram.speaking  { --accent-rgb: 52,211,120; }  /* green — responding (teal fallback 0,201,167) */
/* The image is left completely untouched — full quality. It reads as "no box" because the
   whole hologram panel behind it is the SAME black as the image's own backdrop (see #holo-col).
   That is exactly how Kevin's presence works: the head floats in a black field, not a box. */
.holo-img { width: 100%; height: 100%; object-fit: contain; position: relative; z-index: 1; }

/* Ambient aura rings (Kevin's): faint static rings + rings that pulse outward like sonar.
   Sit behind the head; colour follows state via --accent-rgb. */
.holo-aura { position: absolute; inset: 0; z-index: 0; display: grid; place-items: center; pointer-events: none; }
/* Aura + base share ONE slow rhythm: a pulse every 2.5s. Two rings offset by half the 5s
   cycle so exactly one ring is expanding at a time (calm, no clutter), in sync with the base. */
.holo-aura span { position: absolute; width: 78%; height: 78%; border-radius: 50%;
  border: 1px solid rgba(var(--accent-rgb), .38); opacity: 0;
  animation: auraPulse 5s ease-out infinite; }
.holo-aura span:nth-child(3) { animation-delay: 0s; }
.holo-aura span:nth-child(4) { animation-delay: 2.5s; }
/* No fixed ring boundary behind the head — only the outward pulses remain. */
.holo-aura span.static { display: none; }
@keyframes auraPulse { 0% { transform: scale(.55); opacity: 0; } 8% { opacity: .5; } 48% { transform: scale(1.4); opacity: 0; } 100% { transform: scale(1.4); opacity: 0; } }

/* Projector base: a glowing emitter core that breathes + bright ellipse rings pulsing outward
   across the platform under the neck (Kevin's base). */
.holo-base { position: absolute; left: 50%; top: 89.5%; transform: translate(-50%, -50%);
  width: 54%; height: 12%; z-index: 2; pointer-events: none; }
.holo-base span { position: absolute; left: 50%; top: 50%; width: 100%; height: 100%;
  transform: translate(-50%, -50%); border: 1.5px solid rgba(var(--accent-rgb), .8); border-radius: 50%;
  box-shadow: 0 0 9px rgba(var(--accent-rgb), .5); opacity: 0; animation: baseThrob 5s ease-out infinite; }
.holo-base span:nth-child(2) { animation-delay: 2.5s; }
/* the shiny emitter core — flashes bright on each pulse (every 2.5s), in sync with the rings */
.holo-base::after { content: ""; position: absolute; left: 50%; top: 50%; width: 34%; height: 62%;
  transform: translate(-50%, -50%); border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(220,250,255,.9), rgba(var(--accent-rgb), .5) 45%, transparent 75%);
  animation: emitBreathe 2.5s ease-in-out infinite; }
@keyframes baseThrob { 0% { transform: translate(-50%, -50%) scale(.28); opacity: .85; } 48% { transform: translate(-50%, -50%) scale(1.5); opacity: 0; } 100% { transform: translate(-50%, -50%) scale(1.5); opacity: 0; } }
@keyframes emitBreathe { 0%,100% { opacity: .92; transform: translate(-50%, -50%) scale(1.06); } 45% { opacity: .5; transform: translate(-50%, -50%) scale(.92); } }

/* Kevin's eye rig, ported verbatim from hq/os.html: glowing lenses mounted over the head's
   cut-out sockets. Positions are the rig's exact socket coords; blink is JS-driven (scaleY).
   Colour follows state. They live inside .hologram so they ride the float and stay locked on. */
.eye { position: absolute; top: 42.1%; width: 5.0%; height: 2.6%; transform: translate(-50%, -50%);
  z-index: 5; pointer-events: none; }
.eye.l { left: 39.3%; }
.eye.r { left: 60.4%; }
/* Eyes glow in the hologram's colour (--accent-rgb): per-state in Auto, or the picked colour. */
.eyecore { position: absolute; inset: 0; border-radius: 50%; transform-origin: center center;
  background: radial-gradient(ellipse at 48% 42%, #eafcff 0%, rgb(var(--accent-rgb)) 60%, rgba(var(--accent-rgb), .1) 100%);
  box-shadow: 0 0 11px 1px rgba(var(--accent-rgb), .82); transition: background .35s, box-shadow .35s; }

/* Mouth — ported from Kevin (hq/os.html). A soft bloom, a faint baseline, and the 11-bar
   equalizer over the rig's mouth line. Sizes are % of the head so they scale with it; bars
   grow up + down from centre (align-items:center) and are driven per-frame in app.js. */
.mouthglow { position: absolute; left: 50%; top: 61.5%; transform: translate(-50%, -50%);
  width: 42%; height: 17%; z-index: 3; pointer-events: none; border-radius: 50%; opacity: .1;
  background: radial-gradient(ellipse at center, rgba(var(--mouth-rgb, var(--accent-rgb)), .5), rgba(var(--mouth-rgb, var(--accent-rgb)), .16) 45%, transparent 72%);
  filter: blur(3px); transition: opacity .25s; }
.mouthbase { position: absolute; left: 50%; top: 61.3%; transform: translate(-50%, -50%);
  width: 30%; height: 1.5px; z-index: 5; pointer-events: none; opacity: .25;
  background: linear-gradient(90deg, transparent, rgba(var(--mouth-rgb, var(--accent-rgb)), .7), transparent); transition: opacity .2s; }
.eqbars { position: absolute; left: 50%; top: 61.3%; transform: translate(-50%, -50%);
  display: flex; gap: 1.5%; align-items: center; width: 23%; height: 12%; z-index: 6; }
/* Mouth bars stay a constant cyan in every state — exactly like Kevin (only the eyes + head
   glow change colour). Do NOT add per-state colour overrides here. */
.eqbars span { flex: 1 1 0; min-width: 0; height: 100%; border-radius: 2px; transform-origin: center center;
  background: linear-gradient(180deg, #eafcff, rgb(var(--mouth-rgb, var(--accent-rgb))) 50%, #eafcff); box-shadow: 0 0 7px rgba(var(--mouth-rgb, var(--accent-rgb)), .8);
  transform: scaleY(.15); }

/* Whole-head glow — takes the hologram's colour (--accent-rgb) and pulses when active. One
   keyframe using the live colour variable, so it follows the state (Auto) or the picked colour. */
/* --holo-hue rotates the wireframe image's colour when a custom colour is picked (0 in Auto,
   so the head keeps its native cyan and only glow/eyes shift per state). */
.holo-img { transition: filter .4s ease; }
.hologram.idle      .holo-img { filter: hue-rotate(var(--holo-hue, 0deg)) drop-shadow(0 0 20px rgba(var(--accent-rgb), .42)); }
.hologram.listening .holo-img { animation: holoStatePulse 2.1s ease-in-out infinite; }
.hologram.thinking  .holo-img { animation: holoStatePulse 1.5s ease-in-out infinite; }
.hologram.speaking  .holo-img { animation: holoStatePulse 1.3s ease-in-out infinite; }
@keyframes holoStatePulse { 0%,100% { filter: hue-rotate(var(--holo-hue, 0deg)) drop-shadow(0 0 26px rgba(var(--accent-rgb), .5)) brightness(1.05); } 50% { filter: hue-rotate(var(--holo-hue, 0deg)) drop-shadow(0 0 44px rgba(var(--accent-rgb), .88)) brightness(1.14); } }
/* soft state-coloured aura, widely feathered so it never reads as an edge */
/* A soft coloured aura — feathered wide with no hard edge, so it melts into the page black
   (no visible circular boundary behind the head). */
.hologram::after { content: ""; position: absolute; inset: -30%; z-index: -1; border-radius: 50%;
  background: radial-gradient(closest-side, rgba(var(--accent-rgb), 0.09), transparent 44%);
  transition: background .3s ease; }
.hologram.listening::after, .hologram.thinking::after { animation: holoPulse 1.3s ease-in-out infinite; }
.hologram.speaking::after { animation: holoPulse 0.7s ease-in-out infinite; }
@keyframes holoHover { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-2%); } }
@keyframes holoPulse { 0%,100% { opacity: .7; } 50% { opacity: 1; } }

/* Setup: presence colour choice — a live preview of the head + a row of curated tones. */
.presence-choose { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.presence-preview { width: 130px; height: 130px; flex: 0 0 auto; display: grid; place-items: center; }
#su-holo { width: 130px; aspect-ratio: 1; }
.tone-row { display: flex; flex-wrap: wrap; gap: 13px; align-items: center; }
.tone-row .tone { width: 30px; height: 30px; border-radius: 50%; cursor: pointer; padding: 0;
  border: 2px solid rgba(255,255,255,.2); background: rgb(var(--sw)); box-shadow: 0 0 10px rgba(var(--sw), .55);
  transition: transform .15s, border-color .15s; }
.tone-row .tone:hover { transform: scale(1.14); }
.tone-row .tone.sel { border-color: #fff; transform: scale(1.12); }

/* ==================================================================
   The contents list — the consultation dashboard.

   A table of contents, not a module list. No card, no border, no
   boxes, no checkmarks, no percentage: the three states are carried
   entirely by weight and opacity, which is also how the gold ration
   survives (design-language.md §2.1). The ONE gold element here is a
   2px hairline beside the current phase. A hairline is not a filled
   element, so the mic button keeps the screen's single gold fill.

   State changes are 320ms transitions on class swaps, never rebuilds.
   That transition is the visible half of the interviewer's spoken
   phase checkpoint: the finished line settles back and hands over.
   ================================================================== */
/* The contents column: far left, collapsible, quiet. It reads as a sidebar you can put away,
   not as a panel competing with the conversation. Hidden entirely on phones, which use the
   header sheet instead. */
.toc-rail { display: none; }
@media (min-width: 720px) {
  /* Flush to the left edge of the window, not inside a centered band. It is a sidebar, so it
     behaves like one: it starts where the screen starts. */
  .toc-rail { display: block; flex: 0 0 auto; width: clamp(320px, 24vw, 500px); align-self: stretch;
    padding: 34px 32px 24px 36px; border-right: 1px solid rgba(190,200,235,.055);
    overflow-y: auto; transition: width .32s ease-in-out, padding .32s ease-in-out; }
  .toc-rail:not([open]) { width: 54px; padding: 34px 0 0 20px; }
  .toc-rail > summary { list-style: none; cursor: pointer; color: var(--ink-soft);
    font-size: .74rem; letter-spacing: .16em; text-transform: uppercase;
    padding: 0 0 22px; user-select: none; transition: color .2s; }
  .toc-rail > summary::-webkit-details-marker { display: none; }
  .toc-rail > summary:hover { color: var(--accent); }
  .toc-rail > summary:focus-visible { outline: 2px solid var(--gold-2, #e0c789); outline-offset: 3px; }
  /* Collapsed: the label turns on its side so the column stays a hairline of itself. */
  .toc-rail:not([open]) > summary { padding: 0; }
  .toc-rail:not([open]) .tr-label { writing-mode: vertical-rl; transform: rotate(180deg); }

  /* Type scaled for a monitor, not a phone: the list is meant to be readable at a glance from
     across the desk, without ever competing with the conversation for attention. */
  .toc-rail .toc { max-width: none; }
  .toc-rail .toc-item { padding: 6px 0 6px 14px; }
  .toc-rail .t-name { font-size: 1.18rem; }
  .toc-rail .toc-item.now .t-name-here { font-size: 1.3rem; }
  .toc-rail .t-goal, .toc-rail .t-done { font-size: .96rem; line-height: 1.5; }
  .toc-rail > summary { font-size: .8rem; }
  .toc-rail .toc-item.now .t-goal, .toc-rail .toc-item.just-done .t-done { max-height: 6em; }
  .toc-rail .t-ticks { gap: 5px; margin-top: 8px; }
  .toc-rail .t-ticks i { width: 13px; height: 3px; border-radius: 2px; }

  /* Session actions at the foot of the sidebar: out of the header, into the column that was
     empty below "Close". Quiet, left-aligned, clearly not part of the conversation. */
  .toc-rail .rail-actions { display: flex; flex-direction: column; align-items: flex-start;
    gap: 7px; margin-top: 30px; padding-top: 18px; border-top: 1px solid var(--line); }
  .toc-rail .rail-actions > * { width: auto; text-align: left; }
  .toc-rail:not([open]) .rail-actions { display: none; }
}
.rail-actions { display: none; }   /* phones keep these in the kebab */

.toc { width: 100%; max-width: 236px; margin-bottom: 2px; }
.toc-list { list-style: none; margin: 0; padding: 0; }
.toc-item { display: block; padding: 3px 0 3px 11px; border-left: 2px solid transparent;
  transition: opacity .32s ease-in-out, border-color .32s ease-in-out; }
.t-name { display: none; font-family: var(--serif); font-size: 13px; line-height: 1.3;
  color: var(--ink); }
.t-goal, .t-done { display: block; overflow: hidden; color: var(--ink-soft);
  font-size: 10px; line-height: 1.35; max-height: 0; opacity: 0;
  transition: max-height .32s ease-in-out, opacity .32s ease-in-out, margin .32s ease-in-out; }

/* Still ahead: present, quiet, name only. Attraction shows its promise instead of its label. */
.toc-item.ahead { opacity: .42; }
.toc-item.ahead .t-name-here { display: block; }
.toc-item.ahead.has-alt .t-name-here { display: none; }
.toc-item.ahead.has-alt .t-name-ahead { display: block; }

/* Now: full ink, the goal underneath, the one gold hairline. */
.toc-item.now { opacity: 1; border-left-color: var(--accent); }
.toc-item.now .t-name-here { display: block; font-size: 14px; }
.toc-item.now .t-goal { max-height: 4.6em; opacity: 1; margin-top: 2px; }

/* Behind you: finished, never graded. The phase just finished keeps its receipt visible;
   older ones settle to the name alone so the list stays short and calm. */
.toc-item.done { opacity: .5; }
.toc-item.done .t-name-here { display: block; }
.toc-item.just-done { opacity: .62; }
.toc-item.just-done .t-done { max-height: 4.6em; opacity: 1; margin-top: 2px; }

/* Scenarios only: ten marks, filled from the scenario number the interviewer already reports.
   This is the one fine-grained element in the design, and it is here because Scenarios is 18 of
   the ~56 minutes: without it a member sits on an unmoving "2 of 8" through the exact window
   where people decide to quit. Every mark is real; none of them fill on a timer. */
.t-ticks { display: none; gap: 4px; margin-top: 5px; }
.toc-item.now .t-ticks { display: flex; }
.t-ticks i { width: 9px; height: 2px; border-radius: 1px; background: #3a3a63;
  transition: background .32s ease-in-out; }
.t-ticks i.on { background: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .toc-item, .t-goal, .t-done, .t-ticks i { transition: none; }
}

/* Two-column body: conversation left, hologram + input right (stacks on phones). */
#chat-body { flex: 1; min-height: 0; display: flex; flex-direction: column; position: relative; }
#convo-col { flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* The presence panel: a solid BLACK field, the exact black of the image's own backdrop, so the
   head's square edges are invisible and it floats in the darkness — Kevin's approach exactly.
   Never a floating box on navy; the whole side is black, full height. */
/* The presence well: true black immediately around the head, fading into the room before it
   reaches any edge. A hard-edged black panel would put a visible box around the hologram, which
   is the one failure this whole treatment exists to prevent. */
#holo-col { display: flex; flex-direction: column; align-items: center;
  background: radial-gradient(ellipse 78% 62% at 50% 46%,
    var(--bg-true) 0 46%, rgba(0,0,0,.86) 62%, rgba(0,0,0,.45) 78%, transparent 92%); }

/* Voice-first composer */
.voice-panel { display: flex; flex-direction: column; align-items: center; gap: 8px;
  width: 100%; padding: 4px 0; }
.mic.big { width: 68px; height: 68px; font-size: 1.7rem; }
.voice-status { color: var(--ink-soft); font-size: 0.95rem; min-height: 1.3em;
  text-align: center; max-width: 90%; }
.voice-status.live { color: var(--ink); }
.type-row { display: flex; gap: 8px; align-items: flex-end; width: 100%; }
.type-toggle { align-self: center; margin-top: 4px; }
.composer-tools { display: flex; align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap; margin-top: 4px; }
.composer-tools .type-toggle { margin-top: 0; }
/* Unified composer: mic + always-visible transcribing text box + Send, on one row. */
.input-row { display: flex; align-items: flex-end; gap: 8px; width: 100%; }
.input-row textarea { flex: 1; }
.input-row .mic { align-self: center; }

/* Advisor starter questions (grouped, clickable) — shown before the first turn. */
/* The advisor's "where to start" panel. On desktop it is the left column, mirroring the
   consultation's contents sidebar so both conversational surfaces have the same shape. On a
   phone it becomes a normal block above the conversation, because there is no room for a rail
   and hiding it would leave the greeting pointing at starters that are not on screen. */
.starters-rail { border-right: 1px solid rgba(190,200,235,.055); }
.sr-label { font-size: .74rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-soft); padding: 0 16px 4px; cursor: pointer; user-select: none;
  list-style: none; }
.sr-label::-webkit-details-marker { display: none; }
.sr-label::marker { content: ""; }
@media (min-width: 720px) {
  .starters-rail { flex: 0 0 auto; width: clamp(300px, 23vw, 460px); align-self: stretch;
    overflow-y: auto; padding: 34px 12px 24px 20px; }
  .sr-label { padding: 0 16px 10px; font-size: .8rem; }
  .starters-rail .starter { font-size: .98rem; }
}
@media (max-width: 719px) {
  .starters-rail { flex: 0 0 auto; border-right: none;
    border-bottom: 1px solid rgba(190,200,235,.055); max-height: 42vh; overflow-y: auto; }
  .sr-label { padding: 12px 16px 2px; }
}

.starters { display: flex; flex-direction: column; gap: 9px; padding: 16px 16px 4px; }
.starters.hidden { display: none; }
.starters-intro { color: var(--ink-soft); margin: 0 0 2px; font-size: 0.95rem; }
/* Your own question sits above the categories and reads as the primary way in, not a footnote. */
.starter-own { border-color: var(--accent); color: var(--accent); background: transparent;
  font-weight: 600; }
.starter-own:hover { background: rgba(201,168,106,.08); }
.starter-cat { display: flex; flex-direction: column; gap: 8px; }
.starter-group { font-family: var(--serif); color: var(--accent); font-size: 1.02rem;
  margin: 10px 0 0; cursor: pointer; list-style: none; user-select: none;
  display: flex; align-items: center; gap: 8px; }
.starter-group::-webkit-details-marker { display: none; }
/* A caret that turns, so a closed category reads as "there is more in here". */
.starter-group::after { content: ""; width: 6px; height: 6px; flex: 0 0 auto;
  border-right: 1.5px solid var(--accent); border-bottom: 1.5px solid var(--accent);
  transform: rotate(45deg) translate(-1px, -1px); transition: transform .2s ease; opacity: .7; }
.starter-cat[open] > .starter-group::after { transform: rotate(225deg) translate(-2px, -2px); }
.starter-group.first { margin-top: 2px; }
.starter { text-align: left; background: var(--card); border: 1px solid #3a3a63; color: var(--ink);
  border-radius: var(--radius); padding: 11px 14px; font: inherit; font-size: 0.98rem; cursor: pointer;
  transition: border-color .14s ease; }
.starter:hover { border-color: var(--accent); }
#convo-tools { padding: 10px 16px 0; }
#convo-tools.hidden { display: none; }
.voice-select { background: var(--bg-soft); color: var(--ink); border: 1px solid #3a3a63;
  border-radius: 12px; padding: 6px 8px; font-size: 0.82rem; max-width: 150px; }

/* Attraction */
#rating-area { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 10px 0; }
.rate-card { background: var(--card); border-radius: var(--radius); overflow: hidden; }
.rate-tile {
  height: 130px; display: flex; align-items: center; justify-content: center;
  color: var(--ink-soft); font-size: 0.8rem; text-align: center;
  background: linear-gradient(135deg, #2c2c52, #35355f);
}
.rate-controls { display: flex; flex-wrap: wrap; gap: 4px; padding: 8px; }
.rate-controls button {
  flex: 1 0 18%; padding: 6px 0; background: var(--bg-soft); color: var(--ink-soft);
  border-radius: 8px; font-size: 0.85rem;
}
.rate-controls button.sel { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.reassure { background: #22223f; border-left: 3px solid var(--accent);
  padding: 12px 14px; border-radius: 10px; color: var(--ink-soft); margin: 8px 0; }
.filters label { display: block; margin: 12px 0 0; color: var(--ink-soft); font-size: 0.95rem; }
.filters input {
  width: 100%; margin-top: 6px; padding: 12px 14px; border-radius: var(--radius);
  border: 1px solid #3a3a63; background: var(--bg); color: var(--ink); font: inherit;
}
.poolstat { margin-top: 8px; padding: 10px 12px; background: #22223f;
  border-radius: 10px; color: var(--ink-soft); font-size: 0.9rem; }
.filters .primary { margin-top: 22px; }

/* ------------------------------------------------------------------ */
/* Desktop / large screens — first-class typed experience.            */
/* Same one app; wider, centered, comfortable. Typing is primary,     */
/* voice still works where the browser supports it.                   */
/* ------------------------------------------------------------------ */
@media (min-width: 720px) {
  body { font-size: 17px; }

  /* FULL-BLEED: the dark background fills the whole window; no boxed container, no border
     strips. Content centers inside a max-width band. */
  #chat { max-width: none; margin: 0; width: 100%; border: none; }
  .chat-header { border-radius: 0; }
  .header-inner { flex-wrap: nowrap; padding: 16px 36px; }
  .header-title { font-size: 1.34rem; }
  .header-actions { flex: 0 0 auto; justify-content: flex-end; }

  /* FULL BLEED, on purpose. This used to sit inside a 1560px centered band, which on a large
     monitor left a wide empty margin on the left, pinned the conversation into a narrow ribbon,
     and pushed the presence to the right edge anyway: unbalanced and wasteful. Now the three
     zones own the whole viewport. Contents flush to the left edge, presence flush to the right,
     and the conversation takes everything between them. The READING MEASURE is capped inside
     the conversation column instead (see .messages), which is the correct place to cap it:
     long lines hurt reading, wide columns do not. */
  #chat-body { flex-direction: row; max-width: none; margin: 0; width: 100%;
    gap: 0; padding: 0 0 calc(8px + var(--safe-b)); }

  /* Conversation = the star: centered-left, vertically balanced (centered when short, scrolls
     when long) so the latest exchange sits near the middle, not stranded at the top. */
  /* Subtle lighting between the two zones — a low-opacity hairline, not a fence. */
  /* The composition is LEFT-WEIGHTED on purpose, on Ari's call: contents, then as much text as
     the measure allows, then the presence right beside it. Any surplus on a very wide monitor
     collects to the RIGHT of the head (the auto margin below) instead of being spread as gaps
     between the three zones. Empty space at the outer edge is calm; empty space between a
     speaker and her words is a canyon. */
  #convo-col { flex: 1 1 auto; min-width: 0; overflow-y: auto;
    border-right: 1px solid rgba(190,200,235,.055); }
  /* safe center: centered when the conversation fits, but falls back to top-aligned (fully
     scrollable, first message always reachable) the moment it overflows. This is the fix for
     the "can't scroll up to the start" bug that plain `center` caused. */
  /* The reading measure lives here: ~62 characters, centred in the middle column.
     The surplus width on a large monitor is spent on the SIDEBAR and the PRESENCE (see their
     clamps), never on the gaps. Pushing the text toward the face just moved the void to the other
     side; widening the two outer zones removes it instead, and the three zones end up roughly a
     screen-width apart from each other rather than three islands with canyons between them. */
  /* The text takes whatever the sidebar and the presence do not, up to 96 characters. Past that
     the eye loses its place coming back to the next line, and this is an hour of reading, so the
     cap stays. Everything under the cap is filled. */
  /* The last line must never sit against the bottom of the window. It read as cramped and, on a
     maximized window, as if the text were resting on the taskbar. Space at the foot of a page is
     what makes it feel like a page. */
  .messages { overflow: visible; min-height: 100%; justify-content: safe center;
    width: min(96ch, 100%); max-width: none; margin: 0 auto;
    padding: 48px clamp(28px, 3vw, 64px) clamp(96px, 12vh, 180px); gap: 32px; }
  .bubble.ai { font-size: 1.19rem; line-height: 1.62; }
  .bubble.me { max-width: 78%; font-size: 1.02rem; padding: 14px 18px; }

  /* Right rail: ONE composed panel — progress card (top), presence (center), mic (bottom).
     The head is the anchor tenant, sized by proportion (~45–55% of the rail height); balanced
     gaps come from space-between with a big-enough head. Scales down on short screens. */
  /* Presence sized by the RAIL HEIGHT so it hits 45–55% of it (a square head capped by rail
     WIDTH can't). --head ≈ 47% of the usable rail height (100vh minus header+padding ≈ 60px);
     the rail then widens to fit that head. Dial+mic (~310px) are subtracted implicitly by the
     -60px so the group never clips down to ~760px tall. One centered composed group. */
  /* The presence owns the right edge. It got bigger when the contents list moved out of this
     column: nothing is stacked above the head anymore, so the head is the only tenant and can
     take the height it deserves. */
  /* Matched breathing room on BOTH sides of the head, so it sits near the right edge without
     touching it and without stranding a field of black beside it. The padding is symmetric on
     purpose: whatever air the head gets on its left, it gets on its right. */
  /* The head sits off the right edge, not against it: the air on its right is about double the
     air on its left, which pulls it back toward the conversation without crowding the text. */
  #holo-col { --head: clamp(280px, min(calc(56vh - 32px), 30vw), 760px);
    --holo-air: clamp(28px, 3.2vw, 78px);
    /* Desktop keeps the well, sized to the head so the fade lands well outside its glow. */
    background: radial-gradient(circle at 50% 47%,
      var(--bg-true) 0 40%, rgba(0,0,0,.85) 56%, rgba(0,0,0,.4) 74%, transparent 90%);
    flex: 0 0 auto;
    width: calc(var(--head) + var(--holo-air) * 3);
    justify-content: center; align-items: center;
    gap: clamp(12px, 2vh, 40px);
    padding: 18px calc(var(--holo-air) * 3 - 8px) calc(16px + var(--safe-b)) 8px;
    background: transparent; }
  #holo-col .hologram { flex: 0 0 auto; width: var(--head); height: var(--head); aspect-ratio: 1; }
  /* Compact composer in the rail (frees vertical room so the head can be 45%+ and still fit
     on short screens). */
  #holo-col .composer { border-top: none; background: transparent; width: 100%; flex: 0 0 auto; gap: 6px; }
  #holo-col .voice-panel { gap: 5px; padding: 0; }
  #holo-col .type-toggle { margin-top: 2px; }
  .composer { padding: 4px 0 0; gap: 12px; }
  .composer textarea { min-height: 54px; max-height: 220px; padding: 15px 18px; }
  .mic { width: 52px; height: 52px; }
  #holo-col .mic.big { width: 56px; height: 56px; font-size: 1.5rem; }
  .send { padding: 15px 26px; }

  /* Centered cards for landing / attraction / done, with breathing room. */
  .landing-inner, .attraction-inner { max-width: 680px; padding-top: 7vh; gap: 20px; }
  h1 { font-size: 2.4rem; }
  h2 { font-size: 1.7rem; }

  /* Attraction grid scales up to 3–4 comfortable columns on a big screen. */
  #rating-area { grid-template-columns: repeat(auto-fit, minmax(165px, 1fr)); gap: 16px; }
  .rate-tile { height: 155px; }
  .rate-controls button { flex: 1 0 16%; padding: 8px 0; }

  /* Export links read as buttons in a row, not stacked full-width. */
  #done .ghost { display: inline-block; margin-right: 10px; }
}

/* Very wide screens: don't let the attraction grid tiles get huge. */
@media (min-width: 1100px) {
  .attraction-inner { max-width: 900px; }
  #rating-area { grid-template-columns: repeat(4, 1fr); }
}

/* ==================================================================
   Mobile declutter (2026-07-20): on phones the chat header used to
   spill two rows of buttons. Collapse them behind ONE kebab button,
   and shrink the message text. Desktop (>=720px) restores the inline
   row and full sizes.
   ================================================================== */
.header-menu { position: relative; flex: 0 0 auto; }
.header-menu > summary {
  list-style: none; cursor: pointer; width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid #3a3a63; border-radius: 12px; color: var(--ink-soft);
  font-size: 1.4rem; line-height: 1; user-select: none;
}
.header-menu > summary::-webkit-details-marker { display: none; }

@media (max-width: 719px) {
  .header-menu { margin-left: auto; }
  .header-menu[open] > summary { border-color: var(--accent); color: var(--accent); }
  /* The dropdown: stacked actions in a card under the button. */
  .header-menu > .header-actions {
    position: absolute; right: 0; top: calc(100% + 8px);
    flex: 0 0 auto; flex-direction: column; align-items: stretch; gap: 4px;
    width: max-content; min-width: 214px; max-width: 78vw;
    background: var(--bg-soft); border: 1px solid #2a2a4d; border-radius: 14px;
    padding: 8px; z-index: 30; box-shadow: 0 14px 34px rgba(0,0,0,.5);
  }
  .header-menu:not([open]) > .header-actions { display: none; }
  .header-menu > .header-actions > * { width: 100%; justify-content: flex-start; text-align: left; }
  .header-menu .voice-select { width: 100%; }

  /* Smaller conversation text so more of the exchange fits on a phone. */
  .bubble { font-size: 0.9rem; padding: 12px 15px; }
  .header-title { font-size: 1.02rem; }
  .header-sub { font-size: 0.72rem; }

  /* The in-rail contents list is replaced by the header sheet on phones. */
}

@media (min-width: 720px) {
  /* No collapse on wider screens: hide the kebab, lay the actions back out inline. */
  .header-menu { position: static; margin: 0; }
  .header-menu > summary { display: none; }
  .header-menu > .header-actions {
    display: flex !important; position: static; flex-direction: row; align-items: center;
    gap: 8px; width: auto; min-width: 0; background: none; border: none;
    padding: 0; box-shadow: none;
  }
}

/* ==================================================================
   MOBILE VOICE-AI LAYOUT (2026-07-20) — per docs/MOBILE_UI_SPEC.md.
   Phones only (<=719px); desktop (>=720px) untouched. Flex column:
   header + progress pinned top, composer at the bottom, conversation +
   hologram flex the middle. The hologram IS the mic. Status = text +
   dots. Typing mode swaps the head for a bottom input bar.
   ================================================================== */
/* Boot: keep every screen hidden until boot() picks one (kills the landing-screen flash on refresh). */
html.booting .screen { display: none !important; }

/* These two are mobile-only elements; hide them everywhere by default so desktop is untouched. */
.progress-strip { display: none; }
.status-dots { display: none; }

@keyframes dotBounce {
  0%, 80%, 100% { transform: translateY(0); opacity: .5; }
  40% { transform: translateY(-4px); opacity: 1; }
}

@media (max-width: 719px) {
  /* --- Slim one-line header: identity dot + title, no subtitle (target <=40pt tall) --- */
  .header-inner { padding: 8px 14px; gap: 9px; flex-wrap: nowrap; }
  .header-titles { flex-direction: row; align-items: center; gap: 0; }
  .header-title { font-size: 1.06rem; line-height: 1.2; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; }
  .header-sub { display: none; }
  .orb { width: 12px; height: 12px; }

  /* --- Consultation contents, phones: one line under the header, tap to open the full list ---
     There is deliberately no bar here. The old percentage bar was the single configuration the
     survey literature finds actively harmful on a long instrument, and it sat in the most
     load-bearing spot on the screen. One honest line replaces it. */
  .progress-strip.ready { display: block; background: var(--bg-soft);
    border-bottom: 1px solid #2a2a4d; }
  .toc-sheet > summary { list-style: none; cursor: pointer; display: flex; align-items: center;
    gap: 10px; padding: 7px 14px; user-select: none; }
  .toc-sheet > summary::-webkit-details-marker { display: none; }
  .ts-lines { flex: 1 1 auto; min-width: 0; }
  .ts-now { display: block; font-family: var(--serif); font-size: .88rem; color: var(--ink);
    line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .ts-next { display: block; font-size: .72rem; color: var(--ink-soft); }
  .ts-caret { flex: 0 0 auto; width: 8px; height: 8px; border-right: 1.5px solid var(--ink-soft);
    border-bottom: 1.5px solid var(--ink-soft); transform: rotate(45deg) translate(-2px, -2px);
    transition: transform .32s ease-in-out; }
  .toc-sheet[open] .ts-caret { transform: rotate(225deg) translate(-2px, -2px); }
  #toc-sheet-body { padding: 2px 14px 12px; }
  #toc-sheet-body .toc-item { padding: 4px 0 4px 11px; }
  #toc-sheet-body .t-name { font-size: 14px; }
  #toc-sheet-body .t-goal, #toc-sheet-body .t-done { font-size: 11.5px; }
  #toc-rail { display: none; }   /* the sidebar is replaced by the header sheet on phones */

  /* --- One flex column: conversation grows + scrolls, hologram+composer settle low --- */
  #chat-body { flex-direction: column; gap: 0; padding: 0; }
  #convo-col { flex: 1 1 auto; min-height: 0; overflow-y: auto; border-right: none; }
  .messages { padding: 14px 16px 28px; gap: 12px; }   /* room under the last line here too */
  .bubble { line-height: 1.6; font-size: .95rem; padding: 12px 15px; }

  /* Hologram column sits low on its own black so the head PNG's square edges stay hidden. */
  /* Phone: the head sits inline above the composer, so the well is a soft pool under it
     rather than a black band. */
  #holo-col { flex: 0 0 auto; gap: 4px; padding: 4px 0 calc(6px + var(--safe-b));
    background: radial-gradient(ellipse 62% 78% at 50% 38%,
      var(--bg-true) 0 44%, rgba(0,0,0,.8) 64%, transparent 88%); }

  /* --- Hologram = mic: tap the head; clamp 72-100pt; no separate mic button --- */
  #holo-col .hologram { pointer-events: auto; cursor: pointer;
    width: clamp(72px, 24vw, 100px); height: clamp(72px, 24vw, 100px); margin: 0 auto; }
  #mic-btn { display: none; }
  .voice-panel { gap: 3px; padding: 0; }

  /* --- Status = text + three animated dots (live states only) --- */
  .status-line { display: inline-flex; align-items: center; gap: 8px; }
  .voice-status { font-size: .92rem; }
  .status-dots { align-items: center; gap: 5px; }   /* shown per-state below */
  .status-dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-soft);
    animation: dotBounce 1.2s ease-in-out infinite; }
  .status-dots i:nth-child(2) { animation-delay: .18s; }
  .status-dots i:nth-child(3) { animation-delay: .36s; }
  html[data-holo="listening"] .status-dots,
  html[data-holo="thinking"]  .status-dots,
  html[data-holo="speaking"]  .status-dots { display: inline-flex; }
  html[data-holo="listening"] .status-dots i { background: #4084ff; }
  html[data-holo="thinking"]  .status-dots i { background: #ffb040; }
  html[data-holo="speaking"]  .status-dots i { background: #34d378; }

  /* Type-instead toggle = the footer in voice mode (no grey bar behind it). */
  #composer { padding: 6px 12px calc(8px + var(--safe-b)); background: transparent; border-top: none; gap: 6px; }
  .type-toggle { margin-top: 0; }

  /* --- Typing mode: hologram gone, conversation expands, input bar pinned to the bottom --- */
  body.typing #holo-col .hologram,
  body.typing .voice-panel { display: none; }
  body.typing #holo-col { background: transparent; padding: 0; }
  body.typing #composer { padding: 0; display: flex; flex-direction: column; }
  body.typing .composer-actions { order: -1; margin: 6px 0; }  /* "back to voice" row above the input */
  body.typing .clip-voice,
  body.typing .spk { display: none; }   /* voice-only controls: in-field paperclip takes over; no voice to mute */
  /* the type toggle is an icon circle in voice mode; in typing it becomes the "Back to voice" pill */
  body.typing .type-toggle { width: auto; height: 34px; border-radius: 999px; padding: 0 15px; font-size: .9rem; }
  body.typing .type-row { display: flex; align-items: flex-end; gap: 6px;
    padding: 8px 10px calc(8px + var(--safe-b)); background: var(--bg-soft); border-top: 1px solid #2a2a4d; }
  body.typing .type-row textarea { flex: 1; }
}

/* ==================================================================
   Attach paperclip (2026-07-20): replaces the old stacked "Attach" button.
   A small icon — inline with the Type-instead / Back-to-voice row in voice
   mode, and inside the input bar (left edge, iMessage-style) in typing mode.
   Never a full-width stacked button. Applies on mobile AND desktop.
   ================================================================== */
.composer-actions { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 4px; }
.clip {
  flex: 0 0 auto; width: 36px; height: 36px; padding: 0; border-radius: 50%;
  border: 1px solid #3a3a63; background: transparent; color: var(--ink-soft);
  font-size: 1.05rem; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: border-color .15s, color .15s;
}
.clip:hover { border-color: var(--accent); color: var(--accent); }
.type-row .clip { width: 34px; height: 38px; border: none; align-self: flex-end; } /* inside the input bar */
.attach-note { text-align: center; font-size: .8rem; color: var(--ink-soft); min-height: 1em; margin-top: 4px; }
.attach-note:empty { display: none; }

/* ==================================================================
   Jump-to-latest button (2026-07-20): shown only while the reader has
   scrolled up from the bottom of the conversation. Tapping it re-anchors
   and re-enables auto-follow. Applies on mobile AND desktop.
   ================================================================== */
#convo-col { position: relative; }   /* anchor for the floating button */

/* ONE RULE, THREE SCREENS: the consultation, the guide and the sounding board all load this file
   and all three use #convo-col as the conversation's scroll container.
   It has to be a real box in the flow rather than padding, for two reasons that both bite here:
   Chrome does not count a scroll container's own bottom padding in its scroll extent, and on
   desktop `.messages` is `overflow: visible`, so the bubbles spill past its box and past its
   padding entirely. A flex item after the last message is inside the scrolled content, so the
   last message still scrolls fully into view and simply stops short of the window edge. */
#messages::after { content: ""; display: block; flex: 0 0 auto; height: 48px; }
.jump-latest {
  position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%) translateY(6px);
  display: inline-flex; align-items: center; gap: 6px;
  background: linear-gradient(180deg, #e0be74, var(--accent)); color: var(--accent-ink);
  border: none; border-radius: 999px; padding: 8px 15px;
  font: inherit; font-size: .85rem; font-weight: 600; cursor: pointer;
  box-shadow: 0 6px 20px rgba(0,0,0,.5);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .18s ease, transform .18s ease, visibility .18s;
  z-index: 6;
}
.jump-latest.show { opacity: 1; visibility: visible; pointer-events: auto;
  transform: translateX(-50%) translateY(0); }

/* The portrait, on the done screen. A letter, so it reads like one: serif, generous measure,
   one gold hairline and nothing else. The gold is rationed to the rule — the title stays ink so
   the screen keeps a single filled accent (the resume-code chip). */
.portrait-wrap {
  margin-top: 34px; padding-top: 26px; border-top: 1px solid var(--accent);
  max-width: 62ch; align-self: flex-start; text-align: left;
}
.portrait-title {
  font-family: var(--serif); font-weight: 500; font-size: 1.35rem;
  letter-spacing: .01em; margin: 0 0 10px; color: var(--ink);
}
.portrait-body { font-family: var(--serif); color: var(--ink); }
.portrait-body p { margin: 0 0 1.05em; line-height: 1.72; font-size: 1.05rem; }
.portrait-body p:last-child { margin-bottom: 0; }
