/* OPSY on-device interviewer styles. The font stack is local and system-based
   so interview and governance surfaces remain usable without third-party calls. */

:root {
  --font-display: 'Segoe UI Variable Display', 'Segoe UI', Helvetica, Arial, sans-serif;
  --font-body: 'Segoe UI Variable Text', 'Segoe UI', Helvetica, Arial, sans-serif;
  --font-mono: 'Cascadia Mono', 'SFMono-Regular', Consolas, ui-monospace, monospace;
  --radius-sm: .375rem; --radius-md: .5rem; --radius-lg: .75rem; --radius-xl: 1rem; --radius-2xl: 1.5rem; --radius-full: 9999px;
  --space-1:.25rem; --space-2:.5rem; --space-3:.75rem; --space-4:1rem; --space-5:1.25rem; --space-6:1.5rem; --space-8:2rem; --space-10:2.5rem; --space-12:3rem;
  --transition: 180ms cubic-bezier(.16,1,.3,1);

  --color-bg:#FFFFFF; --color-surface:#F8FAFC; --color-surface-2:#F1F5F9;
  --color-divider:rgba(15,23,42,.08); --color-border:rgba(15,23,42,.12);
  --color-text:#0F172A; --color-text-muted:#334155; --color-text-faint:#5A6675; --color-text-inverse:#FFFFFF;
  --color-primary:#0D6B4E; --color-primary-hover:#0A5A40; --color-primary-active:#084832; --color-primary-light:#E6F3EE;
  --color-secondary:#1F3A68; --color-secondary-light:rgba(31,58,104,.08);
  --color-gold:#B8960C; --color-gold-light:#FBF5E0;
  --color-success:#437A22; --color-warning:#964219; --color-error:#A12C4D;
  --shadow-sm:0 1px 2px rgba(26,26,24,.05); --shadow-md:0 4px 16px rgba(26,26,24,.08); --shadow-lg:0 12px 40px rgba(26,26,24,.12);
}
[data-theme="dark"] {
  --color-bg:#0F1410; --color-surface:#161B18; --color-surface-2:#1C221F;
  --color-divider:rgba(232,232,228,.06); --color-border:rgba(232,232,228,.16);
  --color-text:#E8E8E4; --color-text-muted:#9A9A94; --color-text-faint:#7C7C74; --color-text-inverse:#0F1410;
  --color-primary:#3ECF7A; --color-primary-hover:#4ADE80; --color-primary-active:#2DB866; --color-primary-light:rgba(62,207,122,.1);
  --color-secondary:#5CADE4; --color-secondary-light:rgba(92,173,228,.1);
  --color-gold:#E8B931; --color-gold-light:rgba(232,185,49,.1);
  --color-success:#6DAA45; --color-warning:#F0A778; --color-error:#DD6974;
  --shadow-sm:0 1px 2px rgba(0,0,0,.2); --shadow-md:0 4px 16px rgba(0,0,0,.3); --shadow-lg:0 12px 40px rgba(0,0,0,.4);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --color-bg:#0F1410; --color-surface:#161B18; --color-surface-2:#1C221F;
    --color-divider:rgba(232,232,228,.06); --color-border:rgba(232,232,228,.16);
    --color-text:#E8E8E4; --color-text-muted:#9A9A94; --color-text-faint:#7C7C74; --color-text-inverse:#0F1410;
    --color-primary:#3ECF7A; --color-primary-hover:#4ADE80; --color-primary-active:#2DB866; --color-primary-light:rgba(62,207,122,.1);
    --color-secondary:#5CADE4; --color-gold:#E8B931; --color-gold-light:rgba(232,185,49,.1);
    --color-success:#6DAA45; --color-warning:#F0A778; --color-error:#DD6974;
    --shadow-md:0 4px 16px rgba(0,0,0,.3); --shadow-lg:0 12px 40px rgba(0,0,0,.4);
  }
}

/* ==========================================================================
   Neon emerald — the chat accent.
   ==========================================================================
   The talk surface used to borrow --color-primary, a flat mid-green that read
   as "system OK" rather than as a voice. This is a separate accent ramp so the
   conversation can be neon without repainting governance surfaces, badges, or
   the Recovery tiers, which all still mean something specific in green.

   Dark is the design target: neon needs darkness to glow. Light mode is NOT the
   same colour turned down — a neon green on white is both illegible and ugly,
   so light mode uses a deep jewel emerald with no glow at all and earns its
   contrast from depth instead of luminance. The two are designed as a pair.

   Contrast, measured against the surfaces they actually sit on:
     dark   #7CFFC8 on #0F1410 ≈ 14.8:1      light  #05674A on #FFFFFF ≈ 7.4:1
     dark   #00E58F on #0F1410 ≈ 11.3:1      light  #0A8A61 on #F8FAFC ≈ 4.6:1
   All above the 4.5:1 floor for body text, most above the 7:1 AAA line. */
:root {
  --neon:#0A8A61;
  --neon-strong:#05674A;
  --neon-text:#05674A;
  --neon-edge:rgba(10,138,97,.34);
  --neon-edge-hover:rgba(10,138,97,.85);
  --neon-fill:rgba(10,138,97,.07);
  --neon-fill-strong:rgba(10,138,97,.13);
  --neon-sheen:rgba(15,23,42,.04);
  /* No glow in light mode: a halo on white reads as a rendering artefact. */
  --neon-glow:none;
  --neon-glow-hover:none;
  --neon-glow-strong:none;
  /* A SOLID neon surface and the text that sits on it. Light mode cannot reuse
     the dark stops: near-black on a light-mode emerald measures 2.4:1, so the
     polarity flips — white text on a deeper emerald, which is 5.4:1 and 6.9:1
     at the two gradient stops. This is why the pair is a token and not two
     hardcoded hex values in the variant. */
  --neon-solid-a:#087A56;
  --neon-solid-b:#05674A;
  --neon-on:#FFFFFF;
}
[data-theme="dark"] {
  --neon:#00E58F;
  --neon-strong:#3DFFB0;
  --neon-text:#7CFFC8;
  --neon-edge:rgba(0,229,143,.38);
  --neon-edge-hover:rgba(61,255,176,.95);
  --neon-fill:rgba(0,229,143,.07);
  --neon-fill-strong:rgba(0,229,143,.14);
  --neon-sheen:rgba(255,255,255,.055);
  /* Two shadows doing different jobs: a tight ring that reads as emitted light,
     and a wide diffuse bloom. Both stay under 20% alpha — the difference
     between "high-tech" and "arcade" is entirely restraint. */
  --neon-glow:0 0 0 1px rgba(0,229,143,.06), 0 0 14px -2px rgba(0,229,143,.16);
  --neon-glow-hover:0 0 0 1px rgba(61,255,176,.14), 0 0 22px -2px rgba(61,255,176,.30);
  --neon-glow-strong:0 0 0 1px rgba(0,229,143,.18), 0 0 26px -4px rgba(0,229,143,.34);
  --neon-solid-a:#3DFFB0;
  --neon-solid-b:#00E58F;
  --neon-on:#04231A;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --neon:#00E58F;
    --neon-strong:#3DFFB0;
    --neon-text:#7CFFC8;
    --neon-edge:rgba(0,229,143,.38);
    --neon-edge-hover:rgba(61,255,176,.95);
    --neon-fill:rgba(0,229,143,.07);
    --neon-fill-strong:rgba(0,229,143,.14);
    --neon-sheen:rgba(255,255,255,.055);
    --neon-glow:0 0 0 1px rgba(0,229,143,.06), 0 0 14px -2px rgba(0,229,143,.16);
    --neon-glow-hover:0 0 0 1px rgba(61,255,176,.14), 0 0 22px -2px rgba(61,255,176,.30);
    --neon-glow-strong:0 0 0 1px rgba(0,229,143,.18), 0 0 26px -4px rgba(0,229,143,.34);
    --neon-solid-a:#3DFFB0;
    --neon-solid-b:#00E58F;
    --neon-on:#04231A;
  }
}

/* Recovery Registry semantic pairs (RARE-01). Derived from the adaptive
   primitives above via color-mix, so they follow light/dark automatically and
   need defining only once: custom properties are late-bound, so a theme block
   that overrides --color-error re-tints --nr-bg at use time. Registry surfaces
   previously hardcoded pastel hex here, which stayed pale in dark mode next to a
   token-driven tier badge. */
:root {
  --rec-fg: var(--color-primary);
  --rec-bg: color-mix(in oklab, var(--color-primary) 12%, transparent);
  --nr-fg: var(--color-error);
  --nr-bg: color-mix(in oklab, var(--color-error) 12%, transparent);
  --ae-fg: var(--color-warning);
  --ae-bg: color-mix(in oklab, var(--color-warning) 12%, transparent);
  --sig-fg: var(--color-success);
  --sig-bg: color-mix(in oklab, var(--color-success) 12%, transparent);
  --unsig-fg: var(--color-text-muted);
  --unsig-bg: var(--color-surface-2);
}

*,*::before,*::after { box-sizing:border-box; margin:0; padding:0; }
[hidden] { display:none !important; }
html { -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility; }
body { min-height:100dvh; font-family:var(--font-body); font-size:16px; line-height:1.6; color:var(--color-text); background:var(--color-bg); }
@media (prefers-reduced-motion: reduce){ *{animation-duration:.01ms!important;transition-duration:.01ms!important;} }
button{ font:inherit; color:inherit; cursor:pointer; background:none; border:none; }
input,textarea,select{ font:inherit; color:inherit; }
h1,h2,h3{ font-family:var(--font-display); font-weight:600; line-height:1.15; }
a{ color:var(--color-primary); }
:focus-visible{ outline:2px solid var(--color-primary); outline-offset:3px; border-radius:var(--radius-sm); }
.sr-only{ position:absolute!important; width:1px!important; height:1px!important; padding:0!important; margin:-1px!important; overflow:hidden!important; clip:rect(0,0,0,0)!important; white-space:nowrap!important; border:0!important; }
.skip-link{ position:fixed; z-index:1000; top:var(--space-2); left:var(--space-2); display:flex; min-height:44px; align-items:center; padding:0 var(--space-4); border-radius:var(--radius-sm); background:var(--color-primary); color:var(--color-text-inverse); font-weight:700; text-decoration:none; transform:translateY(calc(-100% - var(--space-4))); transition:transform var(--transition); }
.skip-link:focus{ transform:translateY(0); }

/* ---- layout ---- */
.app{ max-width:760px; margin:0 auto; height:100dvh; display:flex; flex-direction:column; }
.topbar{ display:flex; align-items:center; gap:var(--space-3); flex-wrap:wrap; row-gap:var(--space-2); padding:calc(var(--space-4) + env(safe-area-inset-top)) var(--space-5) var(--space-4); border-bottom:1px solid var(--color-divider); position:sticky; top:0; background:color-mix(in oklab,var(--color-bg) 88%, transparent); backdrop-filter:blur(8px); z-index:10; }
.brand{ display:flex; min-height:44px; align-items:center; gap:var(--space-2); font-family:var(--font-display); font-size:1.25rem; font-weight:600; color:var(--color-text); text-decoration:none; }
.brand svg{ width:26px; height:26px; }
/* The Rose of Venus mark (brand/logo/opsy-rose-mark-reversed.svg): cream line
   on forest green, cropped round. The source square is the brand green, so the
   circle crop IS the round badge — no extra background needed. */
/* The brand medallion (Codex prototype): green disc, cream rose, slight
   overscale so the outer rings kiss the circular crop — never theme-grey. */
.brand-rose{ width:28px; height:28px; border-radius:50%; display:block; overflow:hidden; flex:none; }
.brand .brand-rose svg, .brand-rose svg, .brand .brand-rose img, .brand-rose img{ width:100%; height:100%; display:block; transform:scale(1.12); }
.ob-icon img{ width:52px; height:52px; }

/* ---- THEME-01: the bar during an interview ---- */
/* One row still. The word OPSY steps aside for the theme she is on, and the
   medallion becomes the progress rose. Nothing is added to the bar: the topic
   REPLACES the wordmark rather than joining it, because the four icon buttons
   already fill a 375px row (see .iconbtn above).

   min-width:0 twice, and both are load-bearing: a flex item's default
   min-width:auto refuses to shrink below its content, so without it the long
   theme name pushes the help button off the right edge instead of ellipsizing. */
.brand .bar-topic{ min-width:0; flex:1 1 auto; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-family:var(--font-body); font-size:1rem; font-weight:600; color:var(--color-text); }
.brand .bar-topic[hidden]{ display:none; }
body.interview-active .brand{ flex:1 1 auto; min-width:0; }
/* The word follows the LABEL, not the body class: theme-bar.js sets has-topic
   exactly while #barTopic is showing. Keyed on body.interview-active, the bar
   was wordless on Home during a live interview (review round 1, F1). */
.brand.has-topic .brand-word{ display:none; }

/* The progress rose: a green line drawn over a faint ghost, the same drawing
   the opsy.me site does on scroll (home.html). The transition smooths the
   one-second wall-clock steps into a continuous draw; it is NOT what advances
   the rose, so removing it removes the animation and keeps the state. */
.brand-rose .rose-trace{ transition:stroke-dashoffset 1s linear; }
@media (prefers-reduced-motion: reduce){
  .brand-rose .rose-trace{ transition:none; }
}
/* The ghost at 22% of currentColor is faint on near-black (review round 1,
   F2). A CSS rule outranks the SVG's presentation attribute, so the generated
   file stays as it is; same two-selector pattern as .rc-pill below. */
[data-theme="dark"] .brand-rose .rose-ghost{ opacity:.34; }
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]) .brand-rose .rose-ghost{ opacity:.34; } }
.topbar .spacer{ flex:1; }
/* flex:none on the BASE rule, not per instance. These are round fixed-size
   buttons living in flex rows (.topbar, .modal .panel header). In the bar they
   were saved only by flex-wrap:wrap — and body.interview-active .topbar sets
   flex-wrap:nowrap on narrow phones, which is exactly when all four bar icons
   are on screen at once. Without this they would ellipse together, the same
   failure .talk-send just shipped with. */
.iconbtn{ flex:none; width:44px; height:44px; border-radius:var(--radius-full); display:grid; place-items:center; color:var(--color-text-muted); }
.iconbtn:hover{ background:var(--color-surface-2); color:var(--color-text); }
.muted{ color:var(--color-text-faint); }
.small{ font-size:.82rem; }

/* ---- screens ---- */
.screen{ display:none; flex:1; min-height:0; flex-direction:column; overflow-y:auto; }
.screen.active{ display:flex; }
#view-interview, #view-onboarding{ overflow:hidden; }
.pad{ padding:var(--space-6) var(--space-5); }

/* ---- picker ---- */
.lead{ font-family:var(--font-display); font-size:clamp(1.8rem,1.2rem+2vw,2.6rem); margin-bottom:var(--space-2); }
.sub{ color:var(--color-text-muted); max-width:60ch; margin-bottom:var(--space-6); }
.cards{ display:grid; gap:var(--space-4); }
.card{ background:var(--color-surface); border:1px solid var(--color-border); border-radius:var(--radius-xl); padding:var(--space-5); box-shadow:var(--shadow-sm); }
.card h3{ font-size:1.4rem; margin-bottom:var(--space-2); }
.card .summary{ color:var(--color-text-muted); font-size:.95rem; margin-bottom:var(--space-4); }
.card .row{ display:flex; gap:var(--space-3); align-items:center; flex-wrap:wrap; }

/* ---- pills / badges ---- */
.pill{ display:inline-flex; align-items:center; gap:6px; font-size:.74rem; font-weight:600; letter-spacing:.02em; text-transform:uppercase; padding:4px 10px; border-radius:var(--radius-full); background:var(--color-surface-2); color:var(--color-text-muted); }
.pill.ok{ background:var(--color-primary-light); color:var(--color-primary); }
.pill.warn{ background:var(--color-gold-light); color:var(--color-gold); }
.pill.err{ background:color-mix(in oklab,var(--color-error) 14%,transparent); color:var(--color-error); }
/* A pill that is a control. The chevron lives in ::after because the engine
   badge's label is rewritten with textContent, which would swallow any glyph
   kept in the markup. */
/* .pill sets display, so it would beat the UA's [hidden] rule on specificity
   and a hidden pill would still show. */
.pill[hidden]{ display:none; }
.pill-btn{ cursor:pointer; }
.pill-btn::after{ content:''; width:5px; height:5px; margin-left:1px; border-right:1.6px solid currentColor; border-bottom:1.6px solid currentColor; transform:translateY(-1.5px) rotate(45deg); opacity:.75; }
.pill-btn:hover::after{ opacity:1; }
.pill-btn:focus-visible{ outline:2px solid var(--color-primary); outline-offset:2px; }
.verify{ display:flex; flex-wrap:wrap; gap:var(--space-2); margin:var(--space-3) 0; }

/* ---- buttons ---- */
.btn{ display:inline-flex; align-items:center; justify-content:center; gap:8px; padding:11px 20px; border-radius:var(--radius-full); font-weight:600; font-size:.95rem; text-decoration:none; transition:background var(--transition),transform var(--transition),box-shadow var(--transition); }
.btn-primary{ background:var(--color-primary); color:var(--color-text-inverse); box-shadow:var(--shadow-sm); }
.btn-primary:hover{ background:var(--color-primary-hover); }
.btn-primary:active{ transform:translateY(1px); }
.btn-ghost{ background:transparent; border:1px solid var(--color-border); color:var(--color-text); }
.btn-ghost:hover{ background:var(--color-surface-2); }
.btn-soft{ background:var(--color-surface-2); color:var(--color-text); }
.btn-soft:hover{ background:var(--color-surface); }
.btn[disabled]{ opacity:.5; cursor:not-allowed; }
.btn-block{ width:100%; }

/* ---- toggle ---- */
.toggle{ display:inline-flex; align-items:center; gap:10px; font-size:.9rem; color:var(--color-text-muted); cursor:pointer; }
.toggle input{ position:absolute; opacity:0; }
.track{ width:40px; height:24px; border-radius:var(--radius-full); background:var(--color-surface-2); border:1px solid var(--color-border); position:relative; transition:background var(--transition); }
.track::after{ content:""; position:absolute; top:2px; left:2px; width:18px; height:18px; border-radius:50%; background:#fff; box-shadow:var(--shadow-sm); transition:transform var(--transition); }
.toggle input:checked + .track{ background:var(--color-primary); }
.toggle input:checked + .track::after{ transform:translateX(16px); }

/* ---- progress ---- */
.progress{ height:4px; background:var(--color-surface-2); }
.progress > div{ height:100%; background:linear-gradient(90deg,var(--color-primary),var(--color-gold)); transition:width var(--transition); }

/* ---- chat ---- */
/* "Quiet" presentation (design handoff, option 1a): no avatars anywhere.
   The interviewer speaks as full-width prose under a dot+label sender row;
   the participant answers in a right-aligned compact bubble — the alignment
   alone names the speaker on that side. */
.chat{ flex:1; min-height:0; overflow-y:auto; padding:var(--space-5); display:flex; flex-direction:column; gap:26px; }
.msg{ display:flex; gap:var(--space-3); max-width:100%; animation:rise .3s ease both; }
@keyframes rise{ from{opacity:0; transform:translateY(8px);} to{opacity:1; transform:none;} }
/* Some renderers still emit the avatar element; the base hides it so every
   surface (dev pages included) follows without a markup pass. */
.msg .av{ display:none; }
.msg.participant{ flex-direction:row-reverse; }
/* Interviewer/default turn: no box — prose flush with the container's left
   edge, running its full width. */
.bubble{ padding:0; border:0; background:transparent; color:var(--color-text); line-height:1.8; white-space:pre-wrap; min-width:0; }
.bubble p + p{ margin-top:12px; }
/* The sender row, drawn by the bubble so no renderer changes: a 7px emerald
   dot (soft-edged radial gradient) beside an 11px uppercase label. Suppressed
   on participant turns and on status/typing rows — the status row carries the
   one dot on screen, never two stacked. */
.msg:not(.participant) .bubble:not(.status):not(.typing-dots)::before{
  content:"OPSY";
  display:block;
  padding-left:15px;             /* 7px dot + the sender row's 8px gap */
  margin-bottom:10px;
  font-size:11px; font-weight:700;
  letter-spacing:.08em; text-transform:uppercase;
  line-height:1.4;
  color:var(--color-text-faint);
  white-space:nowrap;
  background:radial-gradient(circle, var(--color-primary) 3.5px, transparent 4px) no-repeat left center / 7px 7px;
}

/* Pre-first-token status row ("Thinking…"): the sender dot, pulsing, beside
   12px muted text. Also restyles the legacy .typing-dots markup — its three
   bouncing spans are hidden, leaving the same pulsing dot. */
.bubble.status, .bubble.typing-dots{ display:flex; align-items:center; gap:8px; padding:2px 0; font-size:12px; color:var(--color-text-faint); }
.bubble.typing-dots span{ display:none; }
.bubble.status::before, .bubble.typing-dots::before{
  content:""; width:7px; height:7px; flex:none; border-radius:50%;
  background:var(--color-primary);
  animation:statusPulse 1.5s ease-in-out infinite;
}
@keyframes statusPulse{ 0%,100%{opacity:1;} 50%{opacity:.3;} }
/* Participant turn: a compact messenger bubble, clipped at the speaker corner.
   The border is deliberately one step softer than the border token (spec). */
.msg.participant .bubble{
  max-width:76%;
  padding:12px 18px;
  border-radius:18px 18px 6px 18px;
  line-height:1.7;
  background:var(--color-surface);
  border:1px solid color-mix(in srgb, var(--color-border) 85%, transparent);
  color:var(--color-text);
}
@media (max-width:640px){ .msg.participant .bubble{ max-width:88%; } }
/* A quiet bubble has no border, so callers that need a flagged turn (the lab's
   safety highlight) or a boxed document (the transparency modal's verbatim
   consent text) opt back into a frame by class. */
.bubble.safety{ border:1px solid var(--color-error); border-radius:var(--radius-md); padding:8px 12px; }
.bubble.consent-doc{ background:var(--color-surface); border:1px solid var(--color-divider); border-radius:var(--radius-lg); padding:12px 16px; }
.bubble.md h1{ font-size:1.3rem; margin:.2em 0 .4em; } .bubble.md h2{ font-size:1.1rem; } .bubble.md ul{ padding-left:1.2em; } .bubble.md li{ margin:.2em 0; } .bubble.md p{ margin:.4em 0; } .bubble.md strong{ color:var(--color-text); } .bubble.md hr{ border:none; border-top:1px solid var(--color-divider); margin:.6em 0; } .bubble.md em{ color:var(--color-text-muted); }

/* ---- dock (input area) ---- */
.dock{ border-top:1px solid var(--color-divider); padding:var(--space-4) var(--space-5) calc(var(--space-4) + env(safe-area-inset-bottom)); background:var(--color-bg); position:sticky; bottom:0; display:flex; flex-direction:column; gap:var(--space-3); }
/* Native keyboard open: the shell has already shrunk the viewport, so the
   home-indicator inset no longer applies and would read as dead space above
   the keyboard. (native-bridge.js toggles .kb-open on <html>.) */
.kb-open .dock{ padding-bottom:var(--space-4); }
.dock-row{ display:flex; gap:var(--space-2); align-items:flex-end; }
.input{ flex:1; resize:none; min-height:48px; max-height:160px; padding:12px 14px; border-radius:var(--radius-lg); background:var(--color-surface); border:1px solid var(--color-border); }
.input:focus{ outline:none; border-color:var(--color-primary); box-shadow:0 0 0 3px var(--color-primary-light); }
.send{ width:48px; height:48px; border-radius:var(--radius-full); background:var(--color-primary); color:var(--color-text-inverse); display:grid; place-items:center; flex:none; }
.send:hover{ background:var(--color-primary-hover); }
.mic{ width:48px; height:48px; border-radius:var(--radius-full); background:var(--color-surface-2); color:var(--color-text); display:grid; place-items:center; flex:none; border:1px solid var(--color-border); }
.mic.rec{ background:var(--color-error); color:#fff; border-color:transparent; animation:pulse 1.4s infinite; }
@keyframes pulse{ 0%{box-shadow:0 0 0 0 color-mix(in oklab,var(--color-error) 50%,transparent);} 70%{box-shadow:0 0 0 12px transparent;} 100%{box-shadow:0 0 0 0 transparent;} }

/* ---- likert ----
   The scale must read as a row of BUTTONS at arm's length on a phone, in dark
   mode, in sunlight (the 2026-08 mobile review found bare numbers). Affordance
   comes from three cues, so losing any one still leaves two: a filled surface
   clearly lighter than the page, a border strong enough to draw a shape, and a
   real shadow lifting the row off the background. Anchors sit ABOVE the row —
   below it they fell under the fold or the keyboard and the numbers meant
   nothing. */
.likert{ display:flex; flex-direction:column; gap:var(--space-2); }
.likert .scale{ display:flex; gap:6px; flex-wrap:wrap; }
/* `.scale .pt`, NOT `.likert .pt`: the batched-instrument matrix (.ibatch)
   builds the identical buttons inside its own container, and the .likert-scoped
   rules never reached them — against the global button reset that rendered a
   validated instrument as bare floating numbers (the 2026-08 mobile bug). One
   selector, every scale. */
.scale .pt{
  flex:1; min-width:44px; min-height:52px; padding:12px 0;
  border-radius:var(--radius-md);
  background:var(--color-surface-2);
  border:1.5px solid color-mix(in oklab, var(--color-border) 60%, var(--color-text-faint));
  box-shadow:var(--shadow-sm);
  font-weight:650; font-size:1.05rem; font-variant-numeric:tabular-nums;
  touch-action:manipulation;
  transition:background var(--transition), border-color var(--transition), transform 90ms ease;
}
.scale .pt:hover{ border-color:var(--color-primary); background:var(--color-primary-light); }
.scale .pt:active{ transform:scale(.96); }
.scale .pt.sel{ background:var(--color-primary); color:var(--color-text-inverse); border-color:var(--color-primary); box-shadow:0 0 0 3px var(--color-primary-light); }
.likert .ends{ display:flex; justify-content:space-between; gap:var(--space-4); font-size:.85rem; font-weight:600; color:var(--color-text-muted); }
.likert .ends span:last-child{ text-align:right; }
.likert .ctx{ font-size:.78rem; color:var(--color-text-faint); }
/* The minutes slider ("how long today?"). The only input[type=range] in the
   app, so it carries its own reset: browsers style the native track and thumb
   from the OS accent colour, which ignores both our themes.
   The track fill is driven by a --fill percentage set from widgets.js on every
   input event, because there is no CSS-only way to paint a range up to its
   thumb. */
.duration{ display:flex; flex-direction:column; gap:var(--space-2); }
.duration-readout{ text-align:center; line-height:1.1; }
.duration-readout strong{ font-size:2.4rem; font-weight:700; font-variant-numeric:tabular-nums; color:var(--color-primary); }
.duration-readout span{ font-size:1rem; font-weight:600; color:var(--color-text-muted); margin-left:.15em; }
.duration-range{
  -webkit-appearance:none; appearance:none;
  width:100%; height:34px; margin:0; padding:0;
  background:transparent; touch-action:manipulation; cursor:pointer;
}
.duration-range::-webkit-slider-runnable-track{
  height:10px; border-radius:999px;
  background:linear-gradient(to right, var(--color-primary) var(--fill,50%), var(--color-surface-2) var(--fill,50%));
  border:1.5px solid color-mix(in oklab, var(--color-border) 60%, var(--color-text-faint));
}
.duration-range::-moz-range-track{
  height:10px; border-radius:999px; background:var(--color-surface-2);
  border:1.5px solid color-mix(in oklab, var(--color-border) 60%, var(--color-text-faint));
}
.duration-range::-moz-range-progress{ height:10px; border-radius:999px; background:var(--color-primary); }
/* 30px thumbs: this is dragged with a thumb on a phone, and 44px of touch
   target comes from the 34px control height plus the thumb's own margin. */
.duration-range::-webkit-slider-thumb{
  -webkit-appearance:none; appearance:none;
  width:30px; height:30px; margin-top:-11px;
  border-radius:50%; background:var(--color-primary);
  border:3px solid var(--color-surface); box-shadow:var(--shadow-sm);
}
.duration-range::-moz-range-thumb{
  width:30px; height:30px; border-radius:50%;
  background:var(--color-primary); border:3px solid var(--color-surface); box-shadow:var(--shadow-sm);
}
.duration-range:focus-visible{ outline:none; }
.duration-range:focus-visible::-webkit-slider-thumb{ box-shadow:0 0 0 4px var(--color-primary-light); }
.duration-range:focus-visible::-moz-range-thumb{ box-shadow:0 0 0 4px var(--color-primary-light); }
.duration-range[disabled]{ opacity:.55; cursor:default; }
.duration-ends{ display:flex; justify-content:space-between; font-size:.85rem; font-weight:600; color:var(--color-text-muted); }
.duration-actions{ display:flex; gap:var(--space-2); margin-top:var(--space-1); }
.duration-actions button{ flex:1; min-height:48px; }

/* ---- number (MICRO-01 WP4) ----
   A quantity with its unit. The field opens EMPTY: no default value and no
   placeholder, because a prefilled amount reads as a suggested amount. The unit
   is a suffix inside the field when the study knows it, and a second small
   input when the participant has to type it. */
.numfield{ display:flex; flex-direction:column; gap:var(--space-2); min-width:0; }
/* min-width:0 on both the wrapper and the inputs is the whole fix (review
   round 1, F3). An <input> is a flex item whose default min-width:auto
   resolves to its INTRINSIC size (~20 characters), so `flex:1` cannot shrink
   it: on the "something else" path, where a second input appears for the
   typed unit, the row measured 454px inside a 351px slot at 375x812 and
   pushed the send button off-screen. Measured again after this: 351/351. */
.numfield-row{ min-width:0; }
.numfield-input{
  flex:1 1 0; min-width:0; display:flex; align-items:center; gap:var(--space-2);
  min-height:48px; padding:0 14px; border-radius:var(--radius-lg);
  background:var(--color-surface); border:1px solid var(--color-border);
}
.numfield-input:focus-within{ border-color:var(--color-primary); box-shadow:0 0 0 3px var(--color-primary-light); }
/* The inputs sit inside the bordered wrapper, so they carry none of their own. */
.numfield-input .input{
  flex:1 1 0; min-width:0; width:100%;
  min-height:46px; padding:0; border:0; background:transparent; border-radius:0;
  font-size:1.05rem; font-variant-numeric:tabular-nums;
}
.numfield-input .input:focus{ outline:none; box-shadow:none; }
.numfield-unit{ flex:none; font-weight:600; color:var(--color-text-muted); }
/* The typed unit keeps a fixed share and may shrink no further; the number
   field beside it absorbs the remainder. */
.numfield-input .numfield-unit-input{ flex:0 1 5.5rem; min-width:3.5rem; text-align:right; color:var(--color-text-muted); }
.numfield-actions{ display:flex; gap:var(--space-2); }
.numfield-actions button{ flex:1; min-height:48px; }

/* ---- the dosing diary's Data card (MICRO-01 WP8) ---- */
.dose-bands{ display:flex; flex-wrap:wrap; gap:var(--space-2); margin-top:var(--space-2); }
.dose-band{
  padding:4px 10px; border-radius:var(--radius-md);
  background:var(--color-surface); border:1px solid var(--color-border);
  font-size:.85rem; color:var(--color-text-muted);
}
.dose-band b{ color:var(--color-text); font-variant-numeric:tabular-nums; }
.dose-series{ margin-top:var(--space-3); }

/* The stacked yes/no (consent, and anywhere two exclusive answers stack). */
.binary{ display:flex; flex-direction:column; gap:var(--space-2); }
.btn-outline{ background:var(--color-surface); border:1.5px solid color-mix(in oklab, var(--color-border) 55%, var(--color-text-faint)); color:var(--color-text); }
.btn-outline:hover{ border-color:var(--color-primary); background:var(--color-primary-light); }

/* ---- choices ---- */
.choices{ display:flex; flex-direction:column; gap:var(--space-2); }
.choice{ text-align:left; padding:12px 14px; border-radius:var(--radius-md); background:var(--color-surface); border:1px solid var(--color-border); }
.choice:hover{ border-color:var(--color-primary); background:var(--color-primary-light); }
.choice.sel{ border-color:var(--color-primary); background:var(--color-primary-light); }
.choice.sel::after{ content:"✓"; float:right; color:var(--color-primary); font-weight:700; }

/* ---- shared conversational response cards ---- */
.conversation-response{
  margin:0;
  max-width:min(620px,100%);
  display:flex;
  flex-direction:column;
  gap:var(--space-3);
}
.conversation-response.submitted{ opacity:.72; }
.conversation-response-prompt{ font-weight:600; }
.conversation-options{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; }
.conversation-option{
  min-height:52px;
  display:flex;
  align-items:flex-start;
  gap:12px;
  padding:12px 14px;
  text-align:left;
  border:1px solid var(--color-border);
  border-radius:var(--radius-lg);
  background:var(--color-surface);
  transition:background var(--transition),border-color var(--transition),box-shadow var(--transition);
  touch-action:manipulation;
}
.conversation-option:hover{ border-color:var(--color-primary); background:var(--color-primary-light); }
.conversation-option.selected{
  border-color:var(--color-primary);
  background:var(--color-primary-light);
  box-shadow:0 0 0 1px var(--color-primary);
}
.conversation-option-mark{
  width:20px;
  height:20px;
  flex:none;
  margin-top:2px;
  border:1.5px solid var(--color-border);
  border-radius:6px;
  background:var(--color-bg);
  position:relative;
}
[data-response-type="single_select"] .conversation-option-mark{ border-radius:50%; }
.conversation-option.selected .conversation-option-mark{ border-color:var(--color-primary); background:var(--color-primary); }
.conversation-option.selected .conversation-option-mark::after{
  content:"";
  position:absolute;
  left:6px;
  top:2px;
  width:5px;
  height:10px;
  border:solid var(--color-text-inverse);
  border-width:0 2px 2px 0;
  transform:rotate(45deg);
}
[data-response-type="single_select"] .conversation-option.selected .conversation-option-mark::after{
  left:5px;
  top:5px;
  width:8px;
  height:8px;
  border:0;
  border-radius:50%;
  background:var(--color-text-inverse);
  transform:none;
}
.conversation-option-copy{ display:flex; flex-direction:column; gap:2px; }
.conversation-option-label{ font-weight:650; line-height:1.35; overflow-wrap:anywhere; }
.conversation-option-description{ color:var(--color-text-muted); font-size:.82rem; line-height:1.4; }
.conversation-continue{ align-self:flex-start; min-height:44px; min-width:120px; }
.conversation-response-actions{ display:flex; flex-direction:column; align-items:flex-start; gap:var(--space-2); }
.conversation-write-in{ width:min(100%,320px); }
.conversation-write-in > summary{
  min-height:44px;
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:var(--space-2);
  padding:0 var(--space-4);
  border:1px solid var(--color-border);
  border-radius:var(--radius-full);
  color:var(--color-text-muted);
  font-size:.9rem;
  font-weight:600;
  cursor:pointer;
  list-style:none;
  touch-action:manipulation;
}
.conversation-write-in > summary::-webkit-details-marker{ display:none; }
.conversation-write-in > summary::after{
  content:"";
  width:7px;
  height:7px;
  flex:none;
  border-right:1.5px solid currentColor;
  border-bottom:1.5px solid currentColor;
  transform:rotate(45deg) translateY(-2px);
  transition:transform var(--transition);
}
.conversation-write-in[open] > summary::after{ transform:rotate(225deg) translate(-1px,-1px); }
.conversation-write-in > .conversation-inline-form{ margin-top:var(--space-2); }
.conversation-response-hint{ color:var(--color-text-faint); font-size:.82rem; }
.prefill-confirm{ display:flex; flex-direction:column; gap:10px; padding:14px; border:1px solid var(--color-border); border-radius:var(--radius-lg); background:var(--color-surface); }
.prefill-eyebrow{ color:var(--color-text-faint); font-size:.78rem; font-weight:650; text-transform:uppercase; letter-spacing:.04em; }
.prefill-value{ font-size:1rem; font-weight:650; line-height:1.45; overflow-wrap:anywhere; }
.prefill-evidence{ margin:0; padding:9px 11px; border-left:3px solid var(--color-primary); border-radius:0 var(--radius-md) var(--radius-md) 0; background:var(--color-primary-light); color:var(--color-text-muted); font-size:.88rem; line-height:1.45; }
.prefill-actions{ display:flex; flex-wrap:wrap; gap:8px; }
.prefill-actions .btn{ min-height:44px; }
.conversation-response-divider{ display:flex; align-items:center; gap:10px; color:var(--color-text-muted); font-size:.78rem; }
.conversation-response-divider::before,.conversation-response-divider::after{ content:""; height:1px; flex:1; background:var(--color-divider); }
.conversation-self-describe label{ display:flex; flex-direction:column; gap:6px; font-weight:600; font-size:.86rem; }
.conversation-self-describe input,
.conversation-inline-form input,
.conversation-inline-form textarea{
  width:100%;
  min-height:48px;
  padding:11px 13px;
  border:1px solid var(--color-border);
  border-radius:var(--radius-md);
  background:var(--color-surface);
}
.conversation-inline-form{ display:flex; align-items:flex-end; gap:10px; }
.conversation-inline-form textarea{ resize:vertical; }
.conversation-inline-form .btn{ min-height:48px; }
.conversation-likert{ display:flex; flex-direction:column; gap:8px; }
.conversation-likert-points{ display:flex; flex-wrap:wrap; gap:8px; }
.conversation-likert-points button{
  min-width:48px;
  min-height:48px;
  border:1px solid var(--color-border);
  border-radius:var(--radius-md);
  background:var(--color-surface);
  font-weight:650;
}
.conversation-likert-points button:hover{ border-color:var(--color-primary); background:var(--color-primary-light); }
.conversation-likert-labels{ display:flex; justify-content:space-between; gap:16px; color:var(--color-text-faint); font-size:.8rem; }
.conversation-consent-actions{ display:flex; flex-wrap:wrap; gap:10px; }
.conversation-consent-actions .btn{ min-height:44px; }
@media (max-width:560px), (max-width:58rem) and (max-height:34rem) and (orientation:landscape){
  .conversation-response{ margin-left:0; max-width:100%; }
  .conversation-options{ grid-template-columns:1fr; }
  .conversation-inline-form{ flex-direction:column; align-items:stretch; }

  /* Participant choices are a compact response tray, not a second page. Short
     labels use two columns; described/long labels keep a readable list. Only
     the options scroll, leaving Continue and the write-in escape reachable. */
  body.interview-active #view-talk .chat{
    padding:var(--space-3);
    gap:var(--space-3);
  }
  body.interview-active #view-talk .dock{
    padding:var(--space-3) var(--space-3) calc(var(--space-3) + env(safe-area-inset-bottom));
    gap:var(--space-2);
  }
  body.interview-active #view-talk .conversation-response{ gap:var(--space-2); }
  body.interview-active #view-talk .conversation-options{
    max-height:min(36dvh,18rem);
    overflow-y:auto;
    overscroll-behavior:contain;
    -webkit-overflow-scrolling:touch;
    gap:var(--space-2);
    padding:1px;
  }
  body.interview-active #view-talk .conversation-options-compact{
    grid-template-columns:repeat(2,minmax(0,1fr));
  }
  body.interview-active #view-talk .conversation-options-list{ grid-template-columns:1fr; }
  body.interview-active #view-talk .conversation-option{
    min-height:44px;
    align-items:center;
    gap:var(--space-2);
    padding:8px 10px;
  }
  body.interview-active #view-talk .conversation-option-mark{
    width:18px;
    height:18px;
    margin-top:0;
  }
  body.interview-active #view-talk .conversation-option.selected .conversation-option-mark::after{
    left:5px;
    top:1px;
  }
  body.interview-active #view-talk [data-response-type="single_select"] .conversation-option.selected .conversation-option-mark::after{
    left:4px;
    top:4px;
  }
  /* This used to reserve a 48px right gutter for the floating help button.
     That button now lives in the top bar, so the gutter was reserving space
     against nothing and narrowing Continue for no reason. */
  body.interview-active #view-talk .conversation-response-actions{
    width:100%;
    flex-direction:row;
    flex-wrap:wrap;
    align-items:stretch;
  }
  body.interview-active #view-talk .conversation-response-actions > .conversation-continue,
  body.interview-active #view-talk .conversation-response-actions > .conversation-write-in{
    flex:1 1 0;
    min-width:0;
  }
  body.interview-active #view-talk .conversation-response-actions > .conversation-continue{ width:auto; }
  body.interview-active #view-talk .conversation-response > .conversation-continue{
    width:100%;
  }
  body.interview-active #view-talk .conversation-response-actions > .conversation-write-in[open]{
    flex-basis:100%;
  }
  body.interview-active #view-talk .conversation-inline-form{
    flex-direction:row;
    align-items:center;
  }
  body.interview-active #view-talk .conversation-inline-form input{ min-width:0; }
  body.interview-active #view-talk .conversation-inline-form .btn{
    flex:none;
    padding-inline:var(--space-4);
  }

  /* Keep the interview chrome to one row on narrow phones. The engine badge is
     informational and its context is already available in About and the study
     record, while every actionable control keeps its full 44px target. */
  body.interview-active .topbar{
    flex-wrap:nowrap;
    gap:var(--space-2);
    padding:calc(var(--space-2) + env(safe-area-inset-top)) var(--space-3) var(--space-2);
  }
  body.interview-active #engineBadge{ display:none; }
  body.interview-active .interview-meta{
    gap:var(--space-2);
    padding-inline:var(--space-3);
  }
  body.interview-active .pause-leave{ padding-inline:var(--space-2); }

}
@media(hover:none){
  .conversation-option:hover{ border-color:var(--color-border); background:var(--color-surface); }
  .conversation-option.selected:hover{ border-color:var(--color-primary); background:var(--color-primary-light); }
}

/* A phone held sideways has plenty of width but very little height. The global
   top bar still provides leave and transparency controls, so the duplicate
   study-context strip yields to the active question and response tray. */
@media (max-width:58rem) and (max-height:34rem) and (orientation:landscape){
  body.interview-active .topbar{
    padding-top:calc(var(--space-1) + env(safe-area-inset-top));
    padding-bottom:var(--space-1);
  }
  body.interview-active .interview-meta{ display:none; }
  body.interview-active #view-talk .conversation-options{ max-height:min(32dvh,8rem); }
}

/* ---- consent / safety cards ---- */
.notice{ border-radius:var(--radius-lg); padding:var(--space-4); border:1px solid var(--color-border); background:var(--color-surface); }
/* The check-in card is CARE, not an error. A calm secondary accent + soft
   left rule, never the red warning-triangle language used for real faults. */
.notice.safety{ border-color:var(--color-secondary); border-left:3px solid var(--color-secondary); background:color-mix(in oklab,var(--color-secondary) 8%,var(--color-bg)); }
.notice.safety h3{ color:var(--color-secondary); display:flex; align-items:center; gap:8px; }
/* The time bell. Deliberately quieter than .notice.safety: this is a gentle
   "your time is up, your call", not an alert. */
.notice.time-up{ border-color:var(--color-primary); border-left:3px solid var(--color-primary); background:color-mix(in oklab,var(--color-primary) 6%,var(--color-bg)); margin-top:var(--space-3); }
.notice.time-up h3{ color:var(--color-primary); font-size:1rem; }
.resources{ display:flex; flex-direction:column; gap:8px; margin:var(--space-3) 0; }
.resource{ padding:10px 12px; border-radius:var(--radius-md); background:var(--color-bg); border:1px solid var(--color-divider); }
.resource b{ display:block; }

/* ---- end / data panel ---- */
.end-card{ text-align:center; }
.rc{ font-family:var(--font-mono); font-size:1.1rem; background:var(--color-surface-2); border:1px dashed var(--color-border); padding:10px 16px; border-radius:var(--radius-md); display:inline-block; margin:var(--space-2) 0; user-select:all; }

/* ---- modal ---- */
.modal{ position:fixed; inset:0; background:rgba(0,0,0,.5); display:none; place-items:center; padding:var(--space-4); z-index:50; }
.modal.open{ display:grid; }
.modal .panel{ background:var(--color-bg); border:1px solid var(--color-border); border-radius:var(--radius-xl); max-width:680px; width:100%; max-height:86dvh; overflow:auto; box-shadow:var(--shadow-lg); }
.modal .panel header{ display:flex; align-items:center; padding:var(--space-4) var(--space-5); border-bottom:1px solid var(--color-divider); position:sticky; top:0; background:var(--color-bg); }
.modal .panel header h3{ flex:1; }

/* ---- recording consent card (consent/session-recording.v1.md) ----
   Shown once, before the first question. The two buttons are deliberately of
   DIFFERENT weight but the same size: "No thanks" is a full button on the same
   row, not a link in small print, because the notice promises that declining
   costs nothing and a decline that is hard to find would make that untrue. */
.rec-consent{ position:fixed; inset:0; background:rgba(0,0,0,.5); display:grid; place-items:center; padding:var(--space-4); z-index:60; }
.rec-consent-card{ background:var(--color-bg); border:1px solid var(--color-border); border-radius:var(--radius-xl); box-shadow:var(--shadow-lg); max-width:520px; width:100%; max-height:86dvh; overflow:auto; padding:var(--space-5); }
.rec-consent-card h2{ font-size:1.15rem; line-height:1.35; margin-bottom:var(--space-3); }
.rec-consent-card p{ font-size:.92rem; line-height:1.55; color:var(--color-text-muted); }
.rec-consent-card ul{ list-style:none; display:grid; gap:var(--space-2); margin:var(--space-4) 0; }
.rec-consent-card li{ font-size:.88rem; line-height:1.5; padding-left:var(--space-4); position:relative; }
.rec-consent-card li::before{ content:"·"; position:absolute; left:6px; color:var(--color-primary); font-weight:700; }
.rec-consent-note{ font-size:.82rem !important; margin-top:var(--space-3); }
.rec-consent-actions{ display:flex; gap:var(--space-3); flex-wrap:wrap; margin-top:var(--space-5); }
.rec-consent-actions .btn{ flex:1 1 auto; min-width:140px; justify-content:center; }
/* The self-service delete list on the privacy screen. */
.rec-list{ display:grid; gap:var(--space-2); margin-top:var(--space-3); }
.rec-row{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); padding:var(--space-3); border:1px solid var(--color-border); border-radius:var(--radius-lg); background:var(--color-surface); }
.rec-row .rc{ font-family:var(--font-mono); font-size:.8rem; word-break:break-all; }

/* ---- the three preferences on the Data screen (FLOW-01 WP2) ----
   The only place they change after the cards asked once. A row per preference:
   its name, then the answers as a group of small buttons with the current one
   pressed. Buttons rather than a <select> because a select hides the answer
   until it is opened, and the whole point of this card is being able to see at
   a glance what the app is going to do. */
.pref-row{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); flex-wrap:wrap; padding:var(--space-3) 0; border-top:1px solid var(--color-border); }
.pref-row:first-of-type{ border-top:none; }
.pref-options{ display:flex; gap:var(--space-2); flex-wrap:wrap; }
.pref-option{ min-height:44px; padding:0 var(--space-4); font-size:.85rem; border:1px solid var(--color-border); border-radius:var(--radius-full); background:var(--color-surface); }
.pref-option[aria-pressed="true"]{ border-color:var(--color-primary); color:var(--color-primary); font-weight:600; }

/* ---- lane picker (which model runs the interview) ---- */
.lane-panel{ max-width:460px; }
.lane-intro{ padding:var(--space-4) var(--space-5) 0; font-size:.9rem; color:var(--color-text-muted); }
.lane-list{ display:grid; gap:var(--space-3); padding:var(--space-4) var(--space-5); }
.lane-opt{ display:flex; flex-direction:column; gap:5px; text-align:left; width:100%; padding:var(--space-4); border-radius:var(--radius-lg); border:1.5px solid var(--color-border); background:var(--color-surface); transition:border-color var(--transition), background var(--transition); }
.lane-opt .lane-top{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-2); }
.lane-opt b{ font-size:1.02rem; }
.lane-opt .lane-sub{ font-size:.86rem; line-height:1.45; color:var(--color-text-muted); text-transform:none; }
.lane-opt.live{ border-color:var(--color-primary); background:var(--color-primary-light); }
.lane-opt.available:hover{ border-color:var(--color-primary); }
/* Announced, not wired. Greyed enough to read as unavailable, legible enough
   to read as a plan. */
.lane-opt.soon{ opacity:.5; cursor:not-allowed; }
.lane-note{ padding:0 var(--space-5) var(--space-5); font-size:.8rem; line-height:1.5; color:var(--color-text-muted); }
.modal pre{ font-family:var(--font-mono); font-size:.8rem; white-space:pre-wrap; background:var(--color-surface-2); padding:var(--space-3); border-radius:var(--radius-md); overflow:auto; }
.tabs{ display:flex; gap:6px; padding:var(--space-3) var(--space-5) 0; flex-wrap:wrap; }
.tab{ padding:7px 12px; border-radius:var(--radius-full); font-size:.85rem; background:var(--color-surface-2); color:var(--color-text-muted); }
.tab.active{ background:var(--color-primary); color:var(--color-text-inverse); }
.tabpanel{ padding:var(--space-4) var(--space-5); display:none; } .tabpanel.active{ display:block; }

/* researcher */
#list{ overflow-x:auto; }
table{ width:100%; border-collapse:collapse; font-size:.9rem; }
th,td{ text-align:left; padding:8px 10px; border-bottom:1px solid var(--color-divider); }
th{ color:var(--color-text-faint); font-weight:600; font-size:.8rem; text-transform:uppercase; letter-spacing:.03em; }
.kpi{ display:flex; gap:var(--space-4); flex-wrap:wrap; margin:var(--space-3) 0; }
.kpi .box{ background:var(--color-surface); border:1px solid var(--color-border); border-radius:var(--radius-lg); padding:var(--space-3) var(--space-4); min-width:120px; }
.kpi .box .n{ font-family:var(--font-display); font-size:1.8rem; color:var(--color-primary); }
.kpi .box .l{ font-size:.78rem; color:var(--color-text-faint); }
.factorbar{ height:8px; border-radius:var(--radius-full); background:var(--color-surface-2); overflow:hidden; margin-top:4px; }
.factorbar > div{ height:100%; background:var(--color-primary); }

/* ============================================================= *
 *  App shell v2 — onboarding, bottom nav, home, insights, data  *
 * ============================================================= */

/* ---- bottom tab bar ---- */
.tabbar{ display:flex; border-top:1px solid var(--color-divider); background:color-mix(in oklab,var(--color-bg) 92%, transparent); backdrop-filter:blur(10px); }
.tabbar a{ flex:1; display:flex; flex-direction:column; align-items:center; gap:3px; padding:9px 4px calc(9px + env(safe-area-inset-bottom)); color:var(--color-text-faint); font-size:.7rem; font-weight:600; letter-spacing:.02em; text-decoration:none; transition:color var(--transition); }
.tabbar a svg{ width:22px; height:22px; }
.tabbar a.active{ color:var(--color-primary); }
.tabbar a:hover{ color:var(--color-text); }

/* ---- onboarding ---- */
.onboarding{ background:radial-gradient(130% 80% at 50% -10%, var(--color-primary-light), var(--color-bg) 55%); }
.ob-wrap{ max-width:480px; width:100%; margin:0 auto; padding:var(--space-5); display:flex; flex-direction:column; flex:1; min-height:0; }
.ob-skip{ align-self:flex-end; color:var(--color-text-faint); font-size:.9rem; min-height:44px; padding:0 var(--space-3); display:inline-flex; align-items:center; }
.ob-slides{ flex:1; display:grid; place-items:center; text-align:center; min-height:0; overflow-y:auto; }
.ob-slide{ display:none; }
.ob-slide.active{ display:block; animation:rise .4s ease both; }
.ob-icon{ width:88px; height:88px; margin:0 auto var(--space-5); border-radius:var(--radius-2xl); display:grid; place-items:center; background:var(--color-primary-light); color:var(--color-primary); box-shadow:var(--shadow-glow-emerald); }
.ob-icon svg{ width:44px; height:44px; }
/* The first slide leads with the mark itself. brand-rose.svg is already a green
   disc, so wrapping it in the squircle badge only shrank it and put a second
   shape around a round one: here it is the badge, full-bleed and round, with a
   breathing halo and one slow turn (48s, Venus pace, not a spinner).

   The line is deliberately thin (brand-rose-display.svg keeps the favicon's
   stroke 8, Steven's call). Everything here exists to keep a ~1px hairline
   sharp: a fixed integer size, so the disc edge and the Earth dot land on whole
   device pixels instead of straddling two; and the rotation applied to the
   <g class="rose-spin"> INSIDE the vector rather than to the element, because
   rotating the element hands the curve to the compositor as a bitmap and a
   rotated texture resamples a hairline into mush. app.js swaps the <img> for
   the inline SVG on the first onboarding paint; until then the <img> spins as
   a whole, which is the same picture, one composite step softer. */
.ob-icon-rose{ width:132px; height:132px; margin-bottom:var(--space-6); background:none; box-shadow:none; border-radius:50%; position:relative; }
@media (max-width:22.4375rem){ .ob-icon-rose{ width:112px; height:112px; } }
.ob-icon-rose::before{ content:''; position:absolute; inset:-16%; border-radius:50%; background:radial-gradient(circle, color-mix(in srgb, var(--color-primary) 26%, transparent), transparent 70%); animation:obHalo 7s ease-in-out infinite; }
.ob-icon-rose img, .ob-icon-rose > svg{ position:relative; width:100%; height:100%; border-radius:50%; display:block; box-shadow:0 0 0 1px color-mix(in srgb, var(--color-primary) 45%, transparent), 0 18px 44px -18px color-mix(in srgb, var(--color-primary) 70%, transparent); }
.ob-icon-rose img{ animation:obSpin 48s linear infinite; }
.ob-icon-rose > svg .rose-spin{ transform-box:view-box; transform-origin:500px 500px; animation:obSpin 48s linear infinite; }
@keyframes obSpin{ from{transform:rotate(0deg);} to{transform:rotate(360deg);} }
@keyframes obHalo{ 0%,100%{opacity:.55; transform:scale(1);} 50%{opacity:.9; transform:scale(1.06);} }
@media (prefers-reduced-motion:reduce){ .ob-icon-rose img, .ob-icon-rose > svg .rose-spin, .ob-icon-rose::before{ animation:none!important; } }
.ob-slide h1{ font-size:clamp(1.8rem,1.3rem+2vw,2.6rem); margin-bottom:var(--space-3); }
.ob-slide p{ color:var(--color-text-muted); margin:0 auto; max-width:42ch; }
.ob-list{ text-align:left; max-width:360px; margin:var(--space-5) auto 0; display:flex; flex-direction:column; gap:var(--space-3); }
.ob-list li{ list-style:none; display:flex; gap:12px; align-items:flex-start; color:var(--color-text-muted); }
.ob-list li svg{ flex:none; width:22px; height:22px; color:var(--color-primary); margin-top:1px; }
.ob-list li b{ color:var(--color-text); font-weight:600; }
.ob-dots{ display:flex; gap:8px; justify-content:center; margin:var(--space-5) 0; }
.ob-dot{ width:8px; height:8px; border-radius:50%; background:var(--color-border); transition:all var(--transition); }
.ob-dot.active{ background:var(--color-primary); width:22px; border-radius:var(--radius-full); }
.ob-nav{ display:flex; gap:var(--space-3); }

/* ---- home ---- */
.greeting{ font-family:var(--font-display); font-size:clamp(2rem,1.4rem+2.5vw,3rem); margin-bottom:var(--space-1); }
.greeting-sub{ color:var(--color-text-muted); margin-bottom:var(--space-6); max-width:48ch; }
.menu{ display:grid; gap:var(--space-4); }
.menu-card{ display:flex; align-items:center; gap:var(--space-4); text-align:left; width:100%; padding:var(--space-5); border-radius:var(--radius-xl); border:1px solid var(--color-border); background:var(--color-surface); box-shadow:var(--shadow-sm); transition:transform var(--transition), box-shadow var(--transition); text-decoration:none; color:inherit; }
.menu-card:hover{ transform:translateY(-2px); box-shadow:var(--shadow-md); }
.menu-primary{ background:linear-gradient(135deg, var(--color-primary), var(--color-primary-active)); color:var(--color-text-inverse); border-color:transparent; box-shadow:var(--shadow-md); }
.menu-primary .menu-icon{ background:color-mix(in oklab,var(--color-text-inverse) 18%,transparent); color:var(--color-text-inverse); }
.menu-primary .menu-sub{ color:color-mix(in oklab,var(--color-text-inverse) 82%,transparent); }
.menu-primary .chev{ color:color-mix(in oklab,var(--color-text-inverse) 78%,transparent); }
.menu-icon{ width:52px; height:52px; flex:none; border-radius:var(--radius-lg); display:grid; place-items:center; background:var(--color-primary-light); color:var(--color-primary); }
.menu-icon svg{ width:26px; height:26px; }
.menu-text{ flex:1; min-width:0; }
.menu-title{ display:block; font-family:var(--font-display); font-size:1.3rem; font-weight:600; line-height:1.2; }
.menu-sub{ display:block; margin-top:3px; color:var(--color-text-muted); font-size:.9rem; }
.menu-row{ display:grid; grid-template-columns:1fr 1fr; gap:var(--space-4); }
.menu-row .menu-card{ flex-direction:column; align-items:flex-start; }
@media(max-width:520px){ .menu-row{ grid-template-columns:1fr; } }
.chev{ color:var(--color-text-faint); flex:none; }
.recent{ margin-top:var(--space-6); }
.recent-item{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-3) var(--space-4); border:1px solid var(--color-divider); border-radius:var(--radius-md); background:var(--color-surface); margin-top:var(--space-2); width:100%; text-align:left; text-decoration:none; color:inherit; }
.recent-item:hover{ border-color:var(--color-primary); }

/* ---- insights / data ---- */
.section-head{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--space-3); margin:var(--space-5) 0 var(--space-3); flex-wrap:wrap; }
.section-head h2{ font-size:1.6rem; }
.empty{ text-align:center; padding:var(--space-12) var(--space-5); color:var(--color-text-faint); }
.empty svg{ width:56px; height:56px; margin:0 auto var(--space-4); color:var(--color-border); }
.empty h3{ color:var(--color-text-muted); margin-bottom:var(--space-2); }
.exp{ border:1px solid var(--color-border); border-radius:var(--radius-lg); background:var(--color-surface); margin-bottom:var(--space-3); overflow:hidden; }
.exp-head{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-4); cursor:pointer; width:100%; text-align:left; }
.exp-head:hover{ background:var(--color-surface-2); }
.exp-ic{ width:40px; height:40px; flex:none; border-radius:var(--radius-md); display:grid; place-items:center; background:var(--color-primary-light); color:var(--color-primary); }
.exp-meta{ flex:1; min-width:0; }
.exp-meta .t{ font-weight:600; }
.exp-meta .d{ font-size:.82rem; color:var(--color-text-faint); }
.exp-score{ font-size:.85rem; color:var(--color-text-muted); white-space:nowrap; }
.exp-body{ padding:0 var(--space-4) var(--space-4); border-top:1px solid var(--color-divider); }
.exp-body .chat{ max-height:340px; padding:var(--space-3) 0; }
.privacy-list{ display:flex; flex-direction:column; gap:var(--space-3); margin:var(--space-4) 0; }
.privacy-list .row{ display:flex; gap:var(--space-3); align-items:flex-start; }
.privacy-list .row svg{ flex:none; width:22px; height:22px; color:var(--color-primary); margin-top:2px; }
.privacy-list .row b{ display:block; }
.btn.danger{ color:var(--color-error); border-color:color-mix(in oklab,var(--color-error) 40%,transparent); background:transparent; }
.btn.danger:hover{ background:color-mix(in oklab,var(--color-error) 10%,transparent); }
.spark{ display:flex; align-items:flex-end; gap:4px; height:44px; margin-top:8px; }
.spark .bar{ flex:1; min-width:6px; background:var(--color-primary); border-radius:3px 3px 0 0; opacity:.85; }

/* ---- engine choice ---- */
.engine-opts{ display:grid; gap:var(--space-3); text-align:left; }
.engine-opt{ display:flex; flex-direction:column; gap:6px; text-align:left; width:100%; padding:var(--space-4); border-radius:var(--radius-lg); border:1.5px solid var(--color-border); background:var(--color-surface); transition:border-color var(--transition), background var(--transition); }
.engine-opt:hover:not([disabled]){ border-color:var(--color-primary); }
.engine-opt.selected{ border-color:var(--color-primary); background:var(--color-primary-light); }
.engine-opt b{ font-size:1.05rem; }
.engine-opt[disabled]{ opacity:.55; cursor:not-allowed; }
.engine-top{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-2); }
.engine-ic{ width:34px; height:34px; flex:none; border-radius:var(--radius-md); display:grid; place-items:center; background:var(--color-surface-2); color:var(--color-primary); }
.engine-opt.selected .engine-ic{ background:var(--color-primary); color:var(--color-text-inverse); }

/* ============================================================= *
 *  OPSY Studio — study builder                                  *
 * ============================================================= */
.app.studio{ max-width:940px; }
#sview-editor, #sview-preview, #sview-hybrid{ overflow:hidden; }

.editor-bar{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-3) var(--space-5); border-bottom:1px solid var(--color-divider); position:sticky; top:0; background:color-mix(in oklab,var(--color-bg) 92%,transparent); backdrop-filter:blur(8px); z-index:5; }
.title-input{ font-family:var(--font-display); font-size:1.3rem; font-weight:600; background:transparent; border:none; flex:1; min-width:60px; color:var(--color-text); }
.title-input:focus{ outline:none; }
.editor-body{ flex:1; min-height:0; overflow-y:auto; padding:var(--space-5); }
.ring{ width:46px; height:46px; flex:none; }

/* form controls */
.studio .input{ width:100%; }
.fld{ display:flex; flex-direction:column; gap:5px; margin-bottom:var(--space-3); }
.fld > span{ font-size:.8rem; color:var(--color-text-faint); font-weight:600; }
.fld input, .fld textarea, .fld select{ width:100%; padding:10px 12px; border-radius:var(--radius-md); background:var(--color-surface); border:1px solid var(--color-border); color:var(--color-text); }
.fld textarea{ resize:vertical; min-height:64px; line-height:1.5; }
.fld input:focus, .fld textarea:focus, .fld select:focus{ outline:none; border-color:var(--color-primary); box-shadow:0 0 0 3px var(--color-primary-light); }
.grid2{ display:grid; grid-template-columns:1fr 1fr; gap:var(--space-3); }
.grid3{ display:grid; grid-template-columns:1fr 1fr 1fr; gap:var(--space-3); }
@media(max-width:560px){ .grid2,.grid3{ grid-template-columns:1fr; } }
.check{ display:flex; gap:8px; align-items:center; font-size:.92rem; color:var(--color-text-muted); cursor:pointer; }
.tab-intro{ color:var(--color-text-muted); margin-bottom:var(--space-4); max-width:62ch; }
.sealed{ border:1px solid var(--color-border); border-radius:var(--radius-md); background:var(--color-surface-2); padding:var(--space-3); margin-top:var(--space-3); }
.sealed summary{ cursor:pointer; font-weight:600; font-size:.9rem; color:var(--color-text-muted); }
.sealed pre{ font-family:var(--font-mono); font-size:.78rem; white-space:pre-wrap; margin-top:var(--space-2); color:var(--color-text-muted); }

/* block cards (flow) */
.block-card{ border:1px solid var(--color-border); border-radius:var(--radius-lg); background:var(--color-surface); margin-bottom:var(--space-3); }
.block-head{ display:flex; align-items:center; gap:var(--space-2); padding:var(--space-3) var(--space-4); }
.block-type{ font-size:.68rem; font-weight:700; text-transform:uppercase; letter-spacing:.04em; color:var(--color-primary); }
.block-id{ font-size:.78rem; color:var(--color-text-faint); }
.block-actions{ display:flex; gap:2px; margin-left:auto; }
.mini{ width:30px; height:30px; border-radius:var(--radius-md); display:grid; place-items:center; color:var(--color-text-faint); }
.mini:hover{ background:var(--color-surface-2); color:var(--color-text); }
.mini[disabled]{ opacity:.3; }
.block-body{ padding:0 var(--space-4) var(--space-4); }
.add-bar{ display:flex; flex-wrap:wrap; gap:6px; margin-top:var(--space-4); }
.add-bar button{ font-size:.82rem; padding:7px 12px; border-radius:var(--radius-full); border:1px dashed var(--color-border); color:var(--color-text-muted); }
.add-bar button:hover{ border-color:var(--color-primary); color:var(--color-primary); }

/* findings */
.findings-toggle{ width:100%; text-align:left; padding:9px var(--space-5); border-top:1px solid var(--color-divider); font-size:.85rem; color:var(--color-text-muted); display:flex; gap:10px; align-items:center; background:var(--color-surface); }
.findings{ max-height:0; overflow-y:auto; background:var(--color-surface); transition:max-height var(--transition); }
.findings.open{ max-height:240px; padding:var(--space-2) var(--space-5) var(--space-4); }
.finding{ display:flex; gap:8px; padding:7px 0; border-bottom:1px solid var(--color-divider); cursor:pointer; font-size:.88rem; align-items:flex-start; }
.finding .dot{ width:8px; height:8px; border-radius:50%; margin-top:6px; flex:none; }
.finding.error .dot{ background:var(--color-error); } .finding.warning .dot{ background:var(--color-gold); } .finding.info .dot{ background:var(--color-text-faint); }
.finding b{ font-weight:600; }
.count-chip{ display:inline-flex; gap:4px; align-items:center; padding:2px 8px; border-radius:var(--radius-full); font-weight:600; font-size:.78rem; }
.count-chip.error{ background:color-mix(in oklab,var(--color-error) 14%,transparent); color:var(--color-error); }
.count-chip.warning{ background:var(--color-gold-light); color:var(--color-gold); }
.count-chip.ok{ background:var(--color-primary-light); color:var(--color-primary); }
.optlist{ display:flex; flex-wrap:wrap; gap:6px; margin-top:6px; }
.optchip{ display:inline-flex; align-items:center; gap:6px; padding:4px 10px; border-radius:var(--radius-full); background:var(--color-surface-2); font-size:.85rem; }
.optchip button{ color:var(--color-text-faint); }
.item-row{ display:grid; grid-template-columns:62px 1fr 84px auto; gap:8px; align-items:center; margin-bottom:6px; }
.item-row input{ padding:7px 10px; border-radius:var(--radius-sm); background:var(--color-surface); border:1px solid var(--color-border); color:var(--color-text); width:100%; }

/* ---- Share / provenance tab (data-integrity subsystem) ---- */
.mono{ font-family:var(--font-mono); font-size:.86rem; word-break:break-all; }
.notice.ok{ border-color:var(--color-success); background:color-mix(in oklab,var(--color-success) 8%,var(--color-bg)); }
.notice.ok b{ color:var(--color-success); }
.btn.small{ padding:6px 12px; font-size:.82rem; }
.iv-id{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); }
.iv-entry{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); padding:var(--space-3) var(--space-4); border:1px solid var(--color-border); border-radius:var(--radius-md); background:var(--color-surface); margin-bottom:8px; }
.iv-study,.iv-notif{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); margin-bottom:8px; }
.iv-kv{ width:100%; }
.iv-kv th{ text-align:left; padding:4px 12px 4px 0; white-space:nowrap; }
.iv-kv td{ padding:4px 0; }
.iv-field{ display:flex; align-items:flex-start; justify-content:space-between; gap:var(--space-3); padding:8px 0; border-bottom:1px solid var(--color-divider); }
.iv-field > span{ font-size:.82rem; color:var(--color-text-faint); font-weight:600; padding-top:6px; min-width:120px; }
.iv-field select{ padding:8px 10px; border-radius:var(--radius-md); background:var(--color-surface); border:1px solid var(--color-border); color:var(--color-text); }
.iv-field > div{ display:flex; flex-wrap:wrap; gap:10px; justify-content:flex-end; }
.chk{ display:inline-flex; align-items:center; gap:6px; font-size:.88rem; color:var(--color-text-muted); }

/* ---- first-run feedback fixes ---- */
.dock-q{ font-size:.92rem; font-weight:600; color:var(--color-text); padding:0 2px var(--space-2); }
.choices.scroll{ max-height:44vh; overflow-y:auto; padding-right:4px; }
.end-actions{ display:flex; flex-direction:column; gap:var(--space-3); align-items:center; margin-top:var(--space-4); }
.end-actions .btn{ width:100%; max-width:280px; }
.data-actions{ display:flex; flex-direction:column; gap:var(--space-3); margin:var(--space-4) 0; }
.exp-section{ margin-top:var(--space-3); border-top:1px solid var(--color-divider); padding-top:var(--space-2); }
.exp-section > summary{ cursor:pointer; font-weight:600; font-size:.95rem; color:var(--color-text-muted); list-style:none; padding:4px 0; }
.exp-section > summary::-webkit-details-marker{ display:none; }
.exp-section > summary::before{ content:"\25B8"; display:inline-block; margin-right:8px; transition:transform var(--transition); color:var(--color-text-faint); }
.exp-section[open] > summary::before{ transform:rotate(90deg); }
.exp-actions{ display:flex; gap:var(--space-4); flex-wrap:wrap; margin-top:var(--space-3); }
.btn.btn-sm{ padding:7px 14px; font-size:.85rem; }
.meta-table{ width:100%; }
.meta-table th{ text-align:left; padding:4px 12px 4px 0; white-space:nowrap; vertical-align:top; font-size:.72rem; }
.meta-table td{ padding:4px 0; font-size:.86rem; word-break:break-word; }
.model-picker select{ width:100%; max-width:360px; padding:9px 12px; border-radius:var(--radius-md); background:var(--color-surface); border:1px solid var(--color-border); color:var(--color-text); margin-top:4px; font-family:inherit; }

/* ---- vision: image widget, reading card, toast ---- */
.pill.info{ background:var(--color-secondary-light); color:var(--color-secondary); }
.img-thumb{ display:block; max-width:200px; max-height:200px; border-radius:var(--radius-md); border:1px solid var(--color-border); }
.msg .bubble .img-thumb{ border-color:transparent; }
.reading-card{ border-left:3px solid var(--color-secondary); }
.reading-card .rc-title{ display:flex; align-items:center; gap:8px; font-size:.74rem; font-weight:700; text-transform:uppercase; letter-spacing:.04em; color:var(--color-text-faint); }
.reading-card pre{ font-family:var(--font-mono); font-size:.8rem; white-space:pre-wrap; background:var(--color-surface-2); padding:var(--space-2) var(--space-3); border-radius:var(--radius-md); margin-top:6px; }
.toast{ position:fixed; left:50%; bottom:calc(78px + env(safe-area-inset-bottom)); transform:translateX(-50%); background:var(--color-text); color:var(--color-bg); padding:10px 18px; border-radius:var(--radius-full); font-size:.88rem; box-shadow:var(--shadow-md); z-index:60; max-width:min(92vw,480px); text-align:center; animation:rise .3s ease both; }

/* Phones: backdrop blur re-composites everything under the bar on every chat
   repaint (each streamed token) and every keyboard animation frame — a real
   jank source on mobile GPUs. Solid backgrounds there; blur stays on desktop. */
@media (pointer: coarse) {
  .topbar, .tabbar, .editor-bar { backdrop-filter: none; background: var(--color-bg); }
}

/* ============================================================= *
 *  UX refinement pass — touch feedback + new surfaces           *
 * ============================================================= */

/* Instant press response, and stop :hover latching after a tap on touch. */
button,.choice,.scale .pt,.menu-card,.engine-opt,.recent-item,.tabbar a,.iconbtn{ -webkit-tap-highlight-color:transparent; }
.scale .pt:active,.choice:active,.engine-opt:active:not([disabled]),.menu-card:active,.btn:active{ transform:scale(.985); }
@media (hover: none){
  .scale .pt:hover:not(.sel){ background:var(--color-surface-2); border-color:color-mix(in oklab, var(--color-border) 60%, var(--color-text-faint)); }
  .choice:hover:not(.sel){ background:var(--color-surface); border-color:var(--color-border); }
  .engine-opt:hover:not([disabled]):not(.selected){ border-color:var(--color-border); }
  .menu-card:hover{ transform:none; box-shadow:var(--shadow-sm); }
  .recent-item:hover{ border-color:var(--color-divider); }
  .iconbtn:hover{ background:none; color:var(--color-text-muted); }
  .tabbar a:hover:not(.active){ color:var(--color-text-faint); }
  .btn-ghost:hover{ background:transparent; }
}

/* Inline banner above the chat (demo-mode notice). */
.banner{ margin:var(--space-4) var(--space-5) 0; padding:12px 14px; border-radius:var(--radius-md); font-size:.86rem; display:flex; gap:10px; align-items:flex-start; }
.banner svg{ flex:none; width:20px; height:20px; margin-top:1px; }
.banner.demo{ background:var(--color-gold-light); color:var(--color-warning); border:1px solid color-mix(in oklab,var(--color-warning) 30%,transparent); }
.banner.demo a{ color:var(--color-warning); font-weight:600; text-decoration:underline; }

/* Home: continue-an-interview / follow-up-is-due cards. */
.home-alert{ display:flex; align-items:center; gap:var(--space-3); padding:var(--space-4); border-radius:var(--radius-lg); margin-bottom:var(--space-5); text-decoration:none; color:inherit; border:1px solid transparent; }
.home-alert .ha-ic{ width:44px; height:44px; flex:none; border-radius:var(--radius-md); display:grid; place-items:center; }
.home-alert .ha-ic svg{ width:22px; height:22px; }
.home-alert .ha-text{ flex:1; min-width:0; }
.home-alert .ha-title{ font-weight:600; display:block; }
.home-alert .ha-sub{ font-size:.85rem; color:var(--color-text-muted); display:block; margin-top:2px; }
.home-alert .chev{ color:var(--color-text-faint); }
.home-alert.resume{ background:var(--color-primary-light); border-color:color-mix(in oklab,var(--color-primary) 30%,transparent); }
.home-alert.resume .ha-ic{ background:var(--color-primary); color:var(--color-text-inverse); }
.home-alert.due{ background:var(--color-secondary-light); border-color:color-mix(in oklab,var(--color-secondary) 30%,transparent); }
.home-alert.due .ha-ic{ background:var(--color-secondary); color:var(--color-text-inverse); }

/* End screen: the payoff card (what emerged), reminder presets. */
.payoff{ text-align:left; background:var(--color-surface); border:1px solid var(--color-border); border-radius:var(--radius-lg); padding:var(--space-4) var(--space-5); margin:0 auto var(--space-4); max-width:440px; box-shadow:var(--shadow-sm); }
.payoff .p-lead{ font-family:var(--font-display); font-size:1.15rem; line-height:1.35; margin-bottom:var(--space-3); }
.payoff .factor{ display:flex; align-items:center; gap:10px; margin:8px 0; font-size:.85rem; }
.payoff .factor .fl{ flex:none; width:104px; color:var(--color-text-muted); text-transform:capitalize; }
.payoff .factor .fb{ flex:1; height:8px; border-radius:var(--radius-full); background:var(--color-surface-2); overflow:hidden; }
.payoff .factor .fb>i{ display:block; height:100%; background:var(--color-primary); }
.payoff .factor .fv{ flex:none; width:48px; text-align:right; font-variant-numeric:tabular-nums; color:var(--color-text); }
.payoff .p-badge{ display:inline-block; margin-bottom:var(--space-2); }
.remind{ background:var(--color-secondary-light); border:1px solid color-mix(in oklab,var(--color-secondary) 24%,transparent); border-radius:var(--radius-lg); padding:var(--space-4); margin:0 auto var(--space-4); max-width:440px; text-align:left; }
.remind h3{ font-size:1.05rem; margin-bottom:6px; }
.remind .presets{ display:flex; gap:8px; flex-wrap:wrap; margin-top:10px; }
.remind .presets button{ padding:8px 14px; border-radius:var(--radius-full); border:1px solid var(--color-border); background:var(--color-surface); font-size:.85rem; font-weight:600; }
.remind .presets button.sel{ background:var(--color-secondary); color:var(--color-text-inverse); border-color:transparent; }
.remind .rmsg{ font-size:.85rem; color:var(--color-success); margin-top:10px; }

/* Study cards: scannable duration / what-you-get / recommended. */
.card .studymeta{ display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin-bottom:var(--space-3); }
.card .whatyouget{ font-size:.9rem; color:var(--color-text-muted); margin-bottom:var(--space-4); }
.pill.dur{ background:var(--color-secondary-light); color:var(--color-secondary); }
.pill.first{ background:var(--color-primary-light); color:var(--color-primary); }

/* Instrument matrix: a page of tappable Likert rows on one screen. */
.ibatch{ display:flex; flex-direction:column; gap:16px; max-height:54vh; overflow-y:auto; padding-right:2px; }
.ibatch-legend{ display:flex; justify-content:space-between; }
.ibatch-item{ display:flex; flex-direction:column; gap:8px; }
.ibatch-item .ibatch-q{ font-size:.92rem; color:var(--color-text); }
.ibatch .scale{ display:flex; gap:6px; flex-wrap:wrap; }

/* ============================================================ LAB-01 (personal science)
   The participant's research lab: Observatory, Ask your data, Inquiries.
   Reuses the design tokens; only adds surfaces the Lab needs. */
.lab-tabs{ display:flex; gap:6px; margin:var(--space-3) 0 var(--space-4); border-bottom:1px solid var(--color-divider); overflow-x:auto; }
.lab-tab{ padding:10px 14px; font-weight:600; font-size:.92rem; color:var(--color-text-muted); border-bottom:2px solid transparent; margin-bottom:-1px; background:none; white-space:nowrap; cursor:pointer; }
.lab-tab.active{ color:var(--color-primary); border-bottom-color:var(--color-primary); }
.lab-card{ margin-bottom:var(--space-4); }
.lab-spark{ color:var(--color-primary); margin-top:10px; }
.spark-svg{ display:block; }
.lab-big{ font-family:var(--font-display); font-size:1.6rem; font-weight:600; line-height:1; }

.chip-row{ display:flex; flex-wrap:wrap; gap:8px; }
.chip{ border:1px solid var(--color-border); background:var(--color-surface); color:var(--color-text); border-radius:var(--radius-full); padding:8px 14px; font-size:.86rem; text-align:left; cursor:pointer; transition:background var(--transition),border-color var(--transition); }
.chip:hover{ background:var(--color-surface-2); }
.chip.sel{ background:var(--color-primary-light); border-color:var(--color-primary); color:var(--color-primary); }

.ask-log{ display:flex; flex-direction:column; gap:var(--space-3); margin:var(--space-4) 0; }
.ask-msg{ display:flex; }
.ask-msg.you{ justify-content:flex-end; }
.ask-msg .bubble{ max-width:88%; padding:12px 14px; border-radius:var(--radius-lg); background:var(--color-surface); border:1px solid var(--color-border); font-size:.95rem; }
.ask-msg.you .bubble{ background:var(--color-primary); color:var(--color-text-inverse); border-color:transparent; }
.ask-msg .bubble p{ margin:0 0 6px; } .ask-msg .bubble p:last-child{ margin-bottom:0; }
.ask-form{ display:flex; gap:8px; margin-top:var(--space-2); }
.ask-form input{ flex:1; min-width:0; padding:12px 14px; border:1px solid var(--color-border); border-radius:var(--radius-full); background:var(--color-surface); color:var(--color-text); font-size:.95rem; }

.lab-wizard{ margin-bottom:var(--space-4); border-color:var(--color-primary); }
.wz-label{ display:block; font-weight:600; font-size:.85rem; margin:var(--space-3) 0 6px; }
.wz-input,.model-select{ width:100%; padding:11px 14px; border:1px solid var(--color-border); border-radius:var(--radius-md); background:var(--color-surface); color:var(--color-text); font-size:.95rem; }
.wz-plan{ margin-top:var(--space-3); padding:8px 12px; background:var(--color-surface-2); border-radius:var(--radius-md); }

.pack-row{ display:flex; gap:10px; align-items:flex-start; padding:10px 0; border-top:1px solid var(--color-divider); cursor:pointer; }
.pack-row input{ margin-top:3px; flex:none; }
.finding{ padding:12px 14px; background:var(--color-primary-light); border-radius:var(--radius-md); }
.ai-toggle{ display:flex; gap:8px; align-items:flex-start; margin-bottom:var(--space-3); cursor:pointer; }
.ai-toggle input{ margin-top:2px; flex:none; }

/* ---- N1-01 "My study" panel (n1-panel.js) -------------------------------- */
.n1-card{ margin-bottom:var(--space-4); }
.n1-card .section-head h2{ font-size:1.25rem; }
.n1-card-body > p{ margin:var(--space-2) 0; }
.n1-due{ margin:var(--space-2) 0; text-align:left; }
.n1-assessment{ padding:var(--space-3); background:var(--color-primary-light); border-radius:var(--radius-md); }
.n1-proposal{ padding:var(--space-3) 0; border-top:1px solid var(--color-divider); }
.n1-proposal-actions{ display:flex; gap:var(--space-3); margin-top:var(--space-2); }
.n1-proposal-actions .btn{ min-height:44px; }
.n1-ask-row{ display:flex; gap:var(--space-2); margin:var(--space-3) 0; }
.n1-ask-row .input{ flex:1; min-height:44px; padding:0 var(--space-3); border:1px solid var(--color-border); border-radius:var(--radius-md); background:var(--color-surface); color:inherit; }
.n1-insight{ display:grid; gap:var(--space-3); margin-top:var(--space-3); }
@media (min-width: 40rem){ .n1-insight{ grid-template-columns: 1fr 1.6fr; } }
.n1-insight-you, .n1-insight-lit{ padding:var(--space-3); border:1px solid var(--color-border); border-radius:var(--radius-md); background:var(--color-surface); }
.n1-insight-you h3, .n1-insight-lit h3{ margin-bottom:var(--space-1); text-transform:uppercase; letter-spacing:.04em; }
.n1-sources{ margin-top:var(--space-2); padding-left:1.1rem; }
.n1-provenance{ padding-left:1.1rem; }
.n1-provenance li{ margin:var(--space-2) 0; }

/* One answer, two routes: the typed alternative under a choice set mutes when
   an option is selected (typing re-activates it and clears the selection). */
.conversation-typed-alt{ transition:opacity .18s ease; }
.conversation-typed-alt.muted{ opacity:.45; }
.conversation-typed-alt.muted .btn{ filter:saturate(.4); }

/* ---- Opsy companion (chat-first home, N1-01) ------------------------------ */
.companion{ display:flex; flex-direction:column; gap:var(--space-3); margin-bottom:var(--space-5); padding:var(--space-4); border:1px solid var(--color-border); border-radius:var(--radius-xl); background:var(--color-surface); box-shadow:var(--shadow-sm); }
.companion-log{ display:flex; flex-direction:column; gap:var(--space-2); max-height:16rem; overflow-y:auto; }
.companion-msg{ max-width:88%; padding:var(--space-2) var(--space-3); border-radius:var(--radius-md); font-size:.95rem; }
.companion-msg p{ margin:0; }
.companion-msg p + p{ margin-top:var(--space-2); }
.companion-msg.opsy{ align-self:flex-start; background:var(--color-surface-2); }
.companion-msg.me{ align-self:flex-end; background:var(--color-primary-light); }
.companion-msg.thinking{ opacity:.6; }
.companion-form{ display:flex; gap:var(--space-2); }
.companion-form .input{ flex:1; min-height:44px; padding:0 var(--space-3); border:1px solid var(--color-border); border-radius:var(--radius-md); background:var(--color-bg); color:inherit; }
.companion-form .btn{ min-height:44px; }

/* ---- Talk tab: the chat IS the surface ------------------------------------ */
.talk-wrap{ flex:1; min-height:0; display:flex; flex-direction:column; padding:var(--space-4) var(--space-5) calc(var(--space-4) + env(safe-area-inset-bottom)); }
.companion-full{ flex:1; min-height:0; margin-bottom:0; }
.companion-full .companion-log{ flex:1; max-height:none; }
.companion-chips{ display:flex; flex-wrap:wrap; gap:var(--space-2); }
.companion-chip{ min-height:44px; padding:0 var(--space-4); border:1px solid var(--color-border); border-radius:var(--radius-full); background:var(--color-surface-2); font-size:.9rem; transition:background var(--transition); }
.companion-chip:hover{ background:var(--color-primary-light); border-color:var(--color-primary); }

/* ---- Interview context strip (layout cues from the Codex chat.html) ------- */
.interview-meta{ display:flex; min-height:42px; align-items:center; justify-content:space-between; gap:var(--space-3); padding:var(--space-1) var(--space-5); border-bottom:1px solid var(--color-divider); }
.interview-meta .study-context{ min-width:0; display:grid; gap:.05rem; }
.interview-meta .eyebrow{ color:var(--color-text-faint); font-size:.72rem; text-transform:uppercase; letter-spacing:.08em; line-height:1.2; }
.interview-meta #interviewTitle{ overflow:hidden; margin:0; font-size:.95rem; font-weight:700; line-height:1.35; text-overflow:ellipsis; white-space:nowrap; }
.privacy-details{ position:relative; }
.privacy-details summary{ display:inline-flex; min-width:44px; min-height:44px; align-items:center; justify-content:center; gap:.35rem; color:var(--color-text-muted); cursor:pointer; font-weight:700; font-size:.85rem; list-style:none; }
.privacy-details summary::-webkit-details-marker{ display:none; }
.privacy-popover{ position:absolute; z-index:30; top:calc(100% + .5rem); right:0; width:min(21rem, calc(100vw - 2rem)); padding:var(--space-4); border:1px solid var(--color-border); border-radius:var(--radius-md); background:var(--color-surface); box-shadow:var(--shadow-md); color:var(--color-text-muted); font-size:.85rem; }
.privacy-popover strong{ display:block; margin-bottom:var(--space-1); color:var(--color-text); }
.progress{ height:5px; background:var(--color-surface-2); }
.progress > div{ height:100%; background:var(--color-primary); transition:width .4s ease; }

/* ---- Lab: join-a-study strip ---------------------------------------------- */
.lab-studies-strip{ margin-bottom:var(--space-4); }
.lab-studies-row{ display:flex; flex-wrap:wrap; gap:var(--space-2); margin-top:var(--space-2); }
.lab-study-chip{ min-height:44px; padding:0 var(--space-4); border:1px solid var(--color-border); border-radius:var(--radius-full); background:var(--color-surface-2); font-size:.9rem; }
.lab-study-chip:hover{ background:var(--color-primary-light); border-color:var(--color-primary); }

/* ============================================================================
   TALK — the one agentic surface (layout per the Codex prototype,
   prototypes/opsy-interview-chat: living-case-study chat, emerald interviewer
   voice, action pills, hold-to-speak). Scoped to #view-talk.
   ========================================================================== */
#view-talk{ display:none; flex-direction:column; min-height:0; }
#view-talk.active{ display:flex; }
#view-talk .chat{ padding-bottom:var(--space-6); }

/* One conversational voice, the Quiet way: the base chat rules already give
   the interviewer full-width prose under the dot+label sender row and the
   person a right-aligned surface bubble. The talk surface only renames the
   sender during an interview. (The neon left-edge bubble and the localhost
   ?pills=a|b intensity variants died with the avatars.) */
/* One name in and out of an interview. "OPSY · N=1 RESEARCHER" was the
   project's vocabulary, not the participant's. */
body.interview-active #view-talk .msg.interviewer .bubble:not(.status):not(.typing-dots)::before{ content:"OPSY"; }
.conversation-screen .msg.thinking .bubble{ opacity:.65; }

/* System dividers: quiet stitches between conversation phases. */
.talk-divider{ display:flex; align-items:center; gap:var(--space-3); color:var(--color-text-faint); font-size:.78rem; text-transform:uppercase; letter-spacing:.08em; margin:var(--space-2) 0; }
.talk-divider::before, .talk-divider::after{ content:""; flex:1; border-top:1px solid var(--color-divider); }

/* --------------------------------------------------------------------------
   Chat pills and chips — the neon emerald treatment.

   One shared recipe, because a triage chip, a study card, and a bottom action
   pill are the same gesture at different sizes and should read as one family:

     a barely-there emerald wash, a hairline edge, a top sheen, and — in dark
     mode only — a soft bloom that tightens on hover.

   The sheen is what stops this looking like a flat tinted rectangle: a 1px
   inset highlight along the top edge is the cue the eye reads as machined
   rather than drawn.

   Interaction states change colour and light only, never geometry, so a hover
   never nudges its neighbours. Press is a 1px settle, which transform handles
   without touching layout.
   -------------------------------------------------------------------------- */
.talk-choices{ display:flex; flex-direction:column; gap:var(--space-2); margin-left:0; }
.talk-choice{
  text-align:left; padding:var(--space-3) var(--space-4);
  border:1px solid var(--neon-edge); border-radius:var(--radius-lg);
  background:transparent;
  color:var(--color-text);
  display:grid; gap:2px;
  box-shadow:none;
  transition:border-color var(--transition), background var(--transition), box-shadow var(--transition), transform var(--transition);
}
.talk-choice:hover{ border-color:var(--neon-edge-hover); background:var(--neon-fill); box-shadow:var(--neon-glow); }
.talk-choice:active{ transform:translateY(1px); box-shadow:none; }
.talk-choice b{ font-size:.95rem; color:var(--color-text); }
.talk-choice span{ font-size:.82rem; color:var(--color-text-muted); }
.talk-choice:hover b{ color:var(--neon-text); }

/* Quick replies — the answers to a question Opsy just asked.
   These are the PERSON's words, not Opsy's, so they sit on the person's side
   and hug the right edge, sized to their content. Left-aligned full-width rows
   read as a form; a right-aligned cluster reads as "things you could say".
   The 44px floor is the touch-target minimum, not a visual choice. */
.talk-choices.compact{
  flex-direction:row; flex-wrap:wrap;
  justify-content:flex-end;
  margin-left:auto; margin-right:0;
  max-width:min(100%, 32rem);
}
.talk-choices.compact .talk-choice{
  display:inline-flex; align-items:center; justify-content:center;
  width:auto; max-width:100%;
  padding:0 var(--space-4); min-height:44px;
  border-radius:var(--radius-full);
  text-align:center;
  /* The thread is pre-wrap; without this a long answer would keep its literal
     spacing and push past the edge instead of wrapping inside the chip. */
  white-space:normal;
}
.talk-choices.compact .talk-choice b{ font-weight:600; }

/* A suggestion says what it is, why it is being suggested, and how long it
   takes — then offers to explain itself before it starts anything. The card
   itself stays quiet so its two buttons carry the emphasis. */
.talk-suggestion{ padding:var(--space-3) var(--space-4); border:1px solid var(--color-border); border-radius:var(--radius-lg); background:var(--color-surface); display:grid; gap:4px; }
.talk-suggestion b{ font-size:.95rem; }
.talk-suggestion span{ font-size:.82rem; color:var(--color-text-muted); }
.talk-suggestion .talk-meta{ font-size:.78rem; color:var(--neon-text); opacity:.9; letter-spacing:.01em; }
.talk-suggestion-row{ display:flex; gap:var(--space-2); margin-top:var(--space-2); }
.talk-suggestion-row .talk-choice{ padding:0 var(--space-5); min-height:44px; align-items:center; justify-content:center; border-radius:var(--radius-full); text-align:center; }

/* One primary action per group: Begin is filled, Tell me more is a hairline. */
.talk-choice.primary{
  border-color:var(--neon); color:var(--neon-text); font-weight:600;
  background:transparent;
  box-shadow:none;
}
.talk-choice.primary:hover{ border-color:var(--neon-strong); background:var(--neon-fill); box-shadow:var(--neon-glow); }
.talk-choice.ghost{ background:transparent; box-shadow:none; border-color:var(--color-border); }
.talk-choice.ghost:hover{ background:var(--neon-fill); border-color:var(--neon-edge-hover); box-shadow:none; }

/* The help button. Fixed bottom-right, above the composer and clear of the
   bottom nav and the home-indicator safe area.

   It is a floating control rather than a pill because pills are contextual —
   they are hidden entirely while a question is pending — and the moment someone
   most needs a person is exactly the moment those are gone. 48px so it is
   comfortably over the 44px touch-target floor. */
/* Reaching a person, in the top bar. The predecessor was a fixed 48px disc
   floating over the composer with a measured clearance variable; it still
   overlapped the send button on a real phone (2026-08-06). This one is a
   normal laid-out bar control, so overlap is structurally impossible, and it
   is smaller: the shared 44px .iconbtn box with a 20px glyph, tinted with the
   accent so it reads as an offer rather than another settings icon. */
.help-btn{ color:var(--neon-text); }
.help-btn:hover{ background:var(--neon-fill); color:var(--neon-text); }
.help-btn:focus-visible{ outline:2px solid var(--neon); outline-offset:2px; }
/* On every screen. It used to be scoped to the talk view because the composer
   writes into that thread, which left Home, Research and Data with no way to
   reach a person (simplicity audit, 2026-09-03). app.js now opens Talk first
   and then the composer, so the button can live everywhere except the
   first-run tour, where there is no conversation yet. */
body.view-onboarding .help-btn{ display:none; }

/* Reaching a person: a composer inline in the thread, not a modal, because
   somebody who is upset should not have to navigate anywhere to ask for help. */
.contact-composer{ flex-direction:column; align-items:stretch; max-width:min(100%, 34rem); }
.contact-composer .contact-note{ font-size:.8rem; color:var(--color-text-muted); line-height:1.45; }
.contact-composer textarea{
  width:100%; padding:var(--space-3) var(--space-4);
  border:1px solid var(--neon-edge); border-radius:var(--radius-lg);
  background:var(--color-surface); color:var(--color-text);
  font:inherit; font-size:.95rem; resize:vertical; min-height:88px;
}
.contact-composer textarea:focus{ outline:none; border-color:var(--neon-edge-hover); box-shadow:var(--neon-glow); }
.contact-composer .contact-attach{ display:flex; align-items:center; gap:var(--space-2); font-size:.85rem; color:var(--color-text-muted); }
.contact-composer .talk-suggestion-row{ margin-top:0; }

/* Action pills (Rephrase · Take a moment · Prefer not to answer · Ask Opsy).
   ONE row that scrolls sideways — never a wrapped wall. Four wrapped pills cost
   ~160px of a phone screen the thread needed (2026-08-11); a chip rail costs 60.
   The auto margins on the end pills center the row while it fits and collapse
   to zero the moment it overflows, so scrolling always starts at the left. */
.talk-pills{ display:flex; flex-wrap:nowrap; gap:var(--space-2); padding:var(--space-2) var(--space-4); overflow-x:auto; scrollbar-width:none; -webkit-overflow-scrolling:touch; }
.talk-pills::-webkit-scrollbar{ display:none; }
.talk-pills .talk-pill:first-child{ margin-left:auto; }
.talk-pills .talk-pill:last-child{ margin-right:auto; }
/* The right-edge fade is the "there is more" cue; talk.js adds .scrollable
   only when the row actually overflows, so a short row never fades. */
.talk-pills.scrollable{
  -webkit-mask-image:linear-gradient(to right, #000 0, #000 calc(100% - 28px), transparent);
  mask-image:linear-gradient(to right, #000 0, #000 calc(100% - 28px), transparent);
}
/* RET-01 account/reminders state. A quiet footnote, not a badge: it reports a
   fact about the account and should never compete with the conversation. */
.talk-account-status{
  padding:0 var(--space-5) var(--space-2); text-align:center;
  font-size:.82rem; color:var(--color-text-muted);
}
.talk-account-status[hidden]{ display:none; }
.talk-pill{
  flex:none; white-space:nowrap;
  display:inline-flex; align-items:center; gap:6px;
  min-height:44px; padding:0 var(--space-4);
  border:1px solid var(--neon-edge); border-radius:var(--radius-full);
  background:transparent;
  color:var(--color-text); font-size:.88rem; letter-spacing:.01em;
  box-shadow:none;
  transition:border-color var(--transition), background var(--transition), box-shadow var(--transition), color var(--transition), transform var(--transition);
}
.talk-pill:hover{ border-color:var(--neon-edge-hover); color:var(--neon-text); background:var(--neon-fill); box-shadow:var(--neon-glow); }
.talk-pill:active{ transform:translateY(1px); box-shadow:none; }
.talk-pill.accent{
  border-color:var(--neon); color:var(--neon-text); font-weight:600;
  background:transparent;
  box-shadow:none;
}
.talk-pill.accent:hover{ border-color:var(--neon-strong); background:var(--neon-fill); box-shadow:var(--neon-glow); }

/* Focus follows the accent, so keyboard users get the same emerald language. */
.conversation-screen :focus-visible{ outline-color:var(--neon); }

/* The composer: ONE ~64px bar. Field on the left, one round primary button on
   the right — mic while the field is empty (hold to speak), send once there is
   text, send always when the device has no reliable voice (.no-voice).

   Golden-section contract (2026-08-11): on a phone, the thread keeps the major
   share (~62%) and EVERYTHING below it — pill rail + this bar + tab bar —
   stays inside the minor (~38%). Each fixture is one fixed-height row by
   construction; do not add a second row to any of them. */
.talk-composer-host{ position:sticky; bottom:0; background:var(--color-bg); border-top:1px solid var(--color-divider); }
.talk-composer{ padding:var(--space-2) var(--space-4) calc(var(--space-2) + env(safe-area-inset-bottom)); display:flex; flex-direction:column; align-items:center; gap:var(--space-2); }
/* The transcript strip exists only WHILE holding the mic (plus a 2.2s notice
   for "didn't catch"), so its height never counts against the resting layout. */
.talk-live{ width:100%; max-width:34rem; text-align:center; font-size:.95rem; color:var(--color-text-muted); font-style:italic; padding:var(--space-2) var(--space-3); border:1px dashed var(--color-border); border-radius:var(--radius-lg); background:var(--color-surface); }
.talk-live-hint{ display:block; margin-top:2px; font-size:.75rem; font-style:normal; color:var(--color-text-faint); }
.talk-composer.notice .talk-live-hint{ display:none; }
/* A notice is an instruction, not a transcript: upright, and readable enough
   that "allow the mic in site settings" can actually be followed. */
.talk-composer.notice .talk-live{ font-style:normal; color:var(--color-text); border-style:solid; line-height:1.4; }
.talk-form{ display:flex; width:100%; max-width:34rem; gap:var(--space-2); align-items:center; }
.talk-form input{ flex:1; min-width:0; min-height:48px; padding:0 var(--space-4); border:1px solid var(--color-border); border-radius:var(--radius-full); background:var(--color-surface); color:inherit; font-size:1rem; }
.talk-form input:focus{ outline:2px solid var(--color-primary); outline-offset:1px; }
/* flex:none is load-bearing, not decoration. Without it these buttons are flex
   items free to shrink: on a real phone one collapsed to its 20px icon while
   height:48px held, and border-radius:50% turned a 20x48 box into an ELLIPSE
   (measured in production, 2026-08-06). Any fixed-size round control inside a
   flex row needs it. */
.talk-send{ flex:none; width:48px; height:48px; border-radius:50%; border:none; background:var(--color-primary); color:var(--color-text-inverse); display:grid; place-items:center; }
/* user-select/touch-callout off: a hold long enough to say a sentence is also
   long enough for Android to offer text selection or a context menu over the
   button, which steals the pointer and cuts the recording short. */
.talk-mic{ flex:none; width:48px; height:48px; border-radius:50%; border:none; background:var(--color-primary); color:var(--color-text-inverse); display:grid; place-items:center; transition:transform .12s ease; touch-action:none; user-select:none; -webkit-user-select:none; -webkit-touch-callout:none; }
.talk-mic:active{ transform:scale(.94); }
.talk-mic.recording{ animation:micPulse 1.4s infinite; }
@keyframes micPulse{ 0%,100%{ box-shadow:0 0 0 0 color-mix(in srgb, var(--color-primary) 38%, transparent); } 50%{ box-shadow:0 0 0 14px transparent; } }
/* The mic/send swap. talk.js toggles .has-text on input. */
.talk-composer .talk-send{ display:none; }
.talk-composer.has-text .talk-send{ display:grid; }
.talk-composer.has-text .talk-mic{ display:none; }
.talk-composer.no-voice .talk-send{ display:grid; }
.talk-composer.no-voice .talk-mic{ display:none; }

/* ---- voice-only mode (VOICE-01) -------------------------------------------
   A SWAP inside the same slot, never a second row. .talk-voice-only is laid out
   exactly like .talk-form: one row, max-width 34rem, 48px tall, so the composer
   measures the same before and after the swap. The build plan drew a 56px
   button; 48 is what keeps the bar the height it already was, and the bar's
   height is the constraint that outranks the drawing (gate G6, and Steven on
   2026-08-11: the thread has to keep the screen).

   The countdown ring is an SVG overlay INSIDE the button's own 48px box, so it
   costs no height at all. */
.talk-composer.voice-only .talk-form{ display:none; }
.talk-voice-only{ display:none; width:100%; max-width:34rem; gap:var(--space-3); align-items:center; }
.talk-composer.voice-only .talk-voice-only{ display:flex; }
.talk-voice-btn{ position:relative; flex:none; width:48px; height:48px; border-radius:50%; border:none; background:var(--color-primary); color:var(--color-text-inverse); display:grid; place-items:center; touch-action:manipulation; user-select:none; -webkit-user-select:none; }
.talk-voice-btn:disabled{ opacity:.6; }
.talk-voice-btn > *{ grid-area:1 / 1; }
.talk-voice-ring{ position:absolute; inset:0; width:48px; height:48px; transform:rotate(-90deg); pointer-events:none; }
/* stroke-width 4, not 3: at 48 px the ring is the only sight of the minute
   going, and 3 was legible but too quiet to notice (review round 1, 7.3). */
.talk-voice-ring circle{ fill:none; stroke-width:4; }
.talk-voice-ring .ring-track{ stroke:color-mix(in srgb, var(--color-text-inverse) 26%, transparent); }
/* The drain is the one-minute countdown made visible. It is redrawn from two
   timestamps on every tick, never decremented, so a backgrounded tab comes back
   to the truth rather than to where it left off. */
.talk-voice-ring .ring-drain{ stroke:var(--color-text-inverse); stroke-linecap:round; transition:stroke-dashoffset .25s linear; }
.talk-voice-only:not(.recording) .talk-voice-ring .ring-drain{ opacity:0; }
.talk-voice-only .talk-voice-stop{ display:none; }
.talk-voice-only.recording .talk-voice-glyph{ display:none; }
.talk-voice-only.recording .talk-voice-stop{ display:block; }
/* Armed and waiting for a first word: a gentle pulse, so it is obvious the
   microphone is open before the countdown has started. */
.talk-voice-only.armed .talk-voice-btn{ animation:micPulse 1.4s infinite; }
.talk-voice-meta{ flex:1; min-width:0; display:flex; flex-direction:column; justify-content:center; gap:1px; overflow:hidden; }
.talk-voice-time{ font-variant-numeric:tabular-nums; font-weight:600; font-size:.95rem; line-height:1.1; }
.talk-voice-hint{ font-size:.75rem; color:var(--color-text-muted); line-height:1.15; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.talk-voice-actions{ display:flex; align-items:center; gap:var(--space-2); flex:none; }
.talk-voice-actions .talk-send{ display:grid; }
.talk-voice-again{ border:1px solid var(--color-border); background:var(--color-surface); color:inherit; border-radius:var(--radius-full); min-height:44px; padding:0 var(--space-3); font-size:.85rem; white-space:nowrap; }
/* "Type instead" is the escape hatch, so it is a real 44px target in the bar
   rather than a link in small print, and it is present in EVERY state: while
   recording, and after a blocked microphone. Somebody who cannot speak right
   now must still be able to answer. */
.talk-voice-type{ flex:none; border:none; background:none; color:var(--color-text-muted); text-decoration:underline; min-height:44px; padding:0 var(--space-1); font-size:.8rem; white-space:nowrap; }
.talk-voice-type:hover{ color:var(--color-text); }
@media (prefers-reduced-motion:reduce){
  /* No pulse. The ring still drains: it is information, not decoration, and
     removing it would take away the only sight of the minute going. */
  .talk-voice-only.armed .talk-voice-btn{ animation:none; }
  .talk-voice-ring .ring-drain{ transition:none; }
}

/* Interview dock lives inside the talk column now. */
#view-talk .dock{ position:static; border-top:1px solid var(--color-divider); }

/* Context bar additions: scene counter + pause control (prototype layout). */
.interview-meta .scene-label{ font-size:.78rem; color:var(--color-primary); font-weight:600; }
.interview-meta .meta-side{ display:flex; align-items:center; gap:var(--space-2); }
.pause-leave{ min-height:40px; padding:0 var(--space-3); border:none; background:none; color:var(--color-primary); font-weight:600; font-size:.88rem; }
.pause-leave:hover{ text-decoration:underline; }

/* Inline end-of-interview card: the close joins the thread. */
.talk-end-card{ margin:var(--space-2) var(--space-2) var(--space-4) 0; text-align:left; }
.talk-end-card .end-row{ gap:var(--space-2); margin-top:var(--space-3); flex-wrap:wrap; }

/* First-tester controls live inside the real completion card. Feedback and
   transcript sharing stay visibly separate so consent cannot be bundled. */
.tester-panel{ margin-top:var(--space-5); padding-top:var(--space-5); border-top:1px solid var(--color-divider); }
.tester-panel-head{ display:flex; align-items:flex-start; justify-content:space-between; gap:var(--space-3); }
.tester-panel-head .eyebrow{ display:block; margin-bottom:3px; color:var(--color-primary); font-size:.7rem; font-weight:700; letter-spacing:.09em; text-transform:uppercase; }
.tester-panel-head h3{ margin:0; font-family:var(--font-display); font-size:1.2rem; }
.tester-panel .muted{ color:var(--color-text-muted); }
.tester-privacy{ margin:var(--space-2) 0 var(--space-4); }
.tester-feedback-form{ display:grid; gap:var(--space-3); }
.tester-feedback-form[hidden]{ display:none; }
.tester-rating{ min-width:0; margin:0; padding:0; border:0; }
.tester-rating legend,.tester-note-label{ margin-bottom:6px; color:var(--color-text); font-size:.88rem; font-weight:700; }
.tester-stars{ display:flex; width:max-content; gap:3px; }
.tester-stars label{ position:relative; display:grid; width:44px; height:44px; place-items:center; border-radius:var(--radius-full); color:var(--color-border); cursor:pointer; }
.tester-stars label:hover,.tester-stars label.selected{ color:#D59A2D; background:color-mix(in srgb,#D59A2D 10%,transparent); }
.tester-stars input{ position:absolute; width:1px; height:1px; opacity:0; }
.tester-stars input:focus-visible + span{ outline:2px solid var(--color-primary); outline-offset:4px; border-radius:2px; }
.tester-stars label > span[aria-hidden="true"]{ font-size:1.65rem; line-height:1; }
.tester-note{ width:100%; min-height:88px; resize:vertical; padding:12px 14px; border:1px solid var(--color-border); border-radius:var(--radius-md); background:var(--color-surface); color:var(--color-text); font:inherit; line-height:1.45; }
.tester-note:focus{ outline:2px solid var(--color-primary); outline-offset:1px; }
.tester-actions{ flex-wrap:wrap; gap:var(--space-2); }
.tester-status{ min-height:1.25em; margin:var(--space-2) 0 0; }
.tester-status[data-kind="success"]{ color:var(--color-success); }
.tester-status[data-kind="error"]{ color:var(--color-error); }
.tester-status[data-kind="muted"]{ color:var(--color-text-muted); }
.tester-share{ display:grid; gap:var(--space-2); margin-top:var(--space-4); padding:var(--space-4); border:1px solid var(--color-border); border-radius:var(--radius-lg); background:var(--color-surface-2); }
.tester-share p{ margin:4px 0 0; }
.tester-share .btn{ width:fit-content; }
@media (max-width:34rem){
  .tester-stars{ width:100%; justify-content:space-between; }
  .tester-stars label{ width:42px; }
  .tester-actions .btn,.tester-share .btn{ width:100%; }
}

/* ---- language switch (ENG-lang) — the global preference on the home menu -- */
.lang-switch{ display:flex; align-items:center; flex-wrap:wrap; gap:var(--space-2); margin-top:var(--space-5); }
.lang-switch .lang-label{ font-size:.85rem; color:var(--color-text-muted); margin-right:var(--space-1); }
.lang-switch .lang-opt{
  border:1px solid var(--color-border); background:var(--color-surface);
  color:var(--color-text); border-radius:999px; padding:6px 14px;
  font:inherit; font-size:.9rem; cursor:pointer;
}
.lang-switch .lang-opt:hover{ background:var(--color-surface-2); }
.lang-switch .lang-opt.active{
  border-color:var(--color-primary); background:var(--color-primary);
  color:var(--color-text-inverse); font-weight:600;
}

/* ---- Research chat (LAB-02, web/research-chat.js) ------------------------
   This is the same full-height conversation grammar as Record: one scrolling
   thread, Opsy's neon edge, transient replies, and a fixed composer. */
#view-lab{ overflow:hidden; }
#view-lab > .pad{ flex:1; min-height:0; display:flex; padding:0; }
.research-chat{ width:100%; flex:1; min-height:0; display:flex; flex-direction:column; }
.rc-meta{ flex:none; gap:var(--space-3); }
.rc-meta .study-context{ min-width:0; }
.rc-meta .rc-title{ margin:0; overflow:hidden; font-size:.95rem; font-weight:700; line-height:1.35; text-overflow:ellipsis; white-space:nowrap; }

/* One visible privacy control replaces the old explanatory popover. The status
   is part of the button, so a person can see the active grant without opening
   anything. */
.rc-data-trigger{ min-height:44px; flex:none; display:flex; align-items:center; gap:9px; padding:5px 10px;
  border:1px solid var(--color-border); border-radius:var(--radius-full); background:var(--color-surface);
  color:var(--color-text); text-align:left; transition:border-color var(--transition), background var(--transition), box-shadow var(--transition); }
.rc-data-trigger > svg:first-child{ color:var(--neon-text); flex:none; }
.rc-data-trigger-copy{ min-width:0; display:grid; gap:1px; line-height:1.15; }
.rc-data-trigger-copy b{ font-size:.8rem; white-space:nowrap; }
.rc-data-trigger-copy > span{ color:var(--color-text-muted); font-size:.75rem; }
.rc-data-trigger-arrow{ color:var(--color-text-faint); flex:none; }
.rc-data-trigger:hover{ border-color:var(--neon-edge-hover); background:var(--neon-fill); box-shadow:var(--neon-glow); }
.rc-data-trigger:focus-visible{ outline:2px solid var(--neon); outline-offset:2px; }

.rc-data-dialog{ width:min(39rem, calc(100vw - 2rem)); max-height:min(88dvh, 48rem); margin:auto; padding:0;
  border:0; border-radius:calc(var(--radius-lg) + 4px); background:transparent; color:var(--color-text); overflow:visible; }
.rc-data-dialog::backdrop{ background:color-mix(in srgb, #000 58%, transparent); backdrop-filter:blur(3px); }
.rc-data-sheet{ max-height:min(88dvh, 48rem); overflow-y:auto; padding:var(--space-6); border:1px solid var(--color-border);
  border-radius:calc(var(--radius-lg) + 4px); background:var(--color-surface); box-shadow:var(--shadow-lg); }
.rc-data-sheet-head{ display:flex; justify-content:space-between; align-items:flex-start; gap:var(--space-4); }
.rc-data-sheet-head h2{ margin:3px 0 0; font-family:var(--font-display); font-size:1.45rem; line-height:1.2; }
.rc-data-close{ width:44px; height:44px; flex:none; display:grid; place-items:center; border:1px solid var(--color-border);
  border-radius:var(--radius-full); background:transparent; color:var(--color-text-muted); }
.rc-data-close:hover{ color:var(--color-text); background:var(--color-surface-2); }
.rc-data-intro{ max-width:55ch; margin:var(--space-3) 0 var(--space-5); color:var(--color-text-muted); line-height:1.55; }
.rc-access-options{ display:grid; gap:var(--space-3); }
.rc-access-option{ width:100%; min-height:92px; display:grid; gap:9px; padding:var(--space-4); text-align:left;
  border:1px solid var(--color-border); border-radius:var(--radius-lg); background:transparent; color:var(--color-text);
  transition:border-color var(--transition), background var(--transition), box-shadow var(--transition); }
.rc-access-option:hover:not(:disabled){ border-color:var(--neon-edge-hover); background:var(--neon-fill); }
.rc-access-option[aria-pressed="true"]{ border-color:var(--neon); background:var(--neon-fill); box-shadow:inset 3px 0 0 var(--neon); }
.rc-access-option:disabled{ opacity:.45; cursor:not-allowed; }
.rc-access-option-top{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); }
.rc-access-option-title{ min-width:0; display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; }
.rc-access-option-title b{ font-size:.98rem; }
.rc-access-option-title > span{ color:var(--color-text-muted); font-size:.72rem; text-transform:uppercase; letter-spacing:.05em; }
.rc-access-option-copy{ max-width:58ch; color:var(--color-text-muted); font-size:.84rem; line-height:1.45; }
.rc-access-meter{ height:24px; flex:none; display:flex; align-items:flex-end; gap:3px; }
.rc-access-meter i{ width:4px; border-radius:3px; background:var(--color-divider); }
.rc-access-meter i:nth-child(1){ height:8px; }.rc-access-meter i:nth-child(2){ height:14px; }.rc-access-meter i:nth-child(3){ height:20px; }
.rc-access-meter.bars-1 i:first-child,.rc-access-meter.bars-3 i{ background:var(--neon); }
.rc-data-boundary{ display:flex; align-items:flex-start; gap:var(--space-3); margin-top:var(--space-5); padding:var(--space-4);
  border-radius:var(--radius-md); background:var(--color-surface-2); color:var(--color-text-muted); }
.rc-data-boundary svg{ flex:none; margin-top:2px; color:var(--neon-text); }
.rc-data-boundary p{ margin:0; font-size:.8rem; line-height:1.5; }.rc-data-boundary strong{ color:var(--color-text); }
.rc-data-sheet-actions{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); margin-top:var(--space-5); }
.rc-data-sheet-actions .linkbtn{ min-height:44px; }.rc-data-sheet-actions .btn{ min-width:96px; }

/* The notebook (N1-02) is a one-line fold above the thread. Open, it is capped
   and scrolls inside itself, so the thread and composer always keep their room.
   Uncapped, a notebook with real items pushed the thread off a phone screen.
   The cap is a share of the Research column, not of the window, so it holds
   with the keyboard up too. */
.rc-notebook{ flex:0 1 auto; min-height:0; max-height:45%; display:flex; flex-direction:column; padding:var(--space-2) var(--space-5) 0; }
.rc-notebook:empty{ display:none; }
.nb-fold{ min-height:0; display:flex; flex-direction:column; overflow:hidden;
  border:1px solid var(--color-border); border-radius:var(--radius-md); background:var(--color-surface); }
.nb-fold-summary{ flex:none; width:100%; min-height:44px; display:flex; align-items:center; gap:var(--space-3); padding:0 var(--space-4);
  border:0; background:none; color:var(--color-text); font:inherit; text-align:left; cursor:pointer; }
.nb-fold-summary:focus-visible{ outline:2px solid var(--neon); outline-offset:2px; border-radius:var(--radius-md); }
.nb-fold-title{ font-size:.9rem; font-weight:700; }
.nb-fold-count{ margin-left:auto; color:var(--color-text-muted); font-size:.78rem; white-space:nowrap; }
.nb-fold-count.has-open{ color:var(--neon-text); font-weight:600; }
.nb-fold-arrow{ flex:none; color:var(--color-text-faint); transition:transform var(--transition); }
.nb-fold.is-open .nb-fold-arrow{ transform:rotate(180deg); }
.nb-fold-body{ min-height:0; overflow-y:auto; overscroll-behavior:contain;
  padding:0 var(--space-4) var(--space-3); border-top:1px solid var(--color-divider); }

/* Notebook items, in the fold and in the Talk thread's inline card. */
.nb-section h3{ margin:var(--space-3) 0 var(--space-2); color:var(--color-text-faint); font-size:.72rem;
  text-transform:uppercase; letter-spacing:.08em; }
.nb-list{ list-style:none; margin:0; padding:0; display:grid; gap:var(--space-2); }
.nb-item{ padding:var(--space-3); border-radius:var(--radius-md); background:var(--color-surface-2); }
.nb-item-text{ line-height:1.45; }
.nb-item-text q{ color:var(--color-text); }
.nb-item-actions{ display:flex; flex-wrap:wrap; gap:var(--space-1) var(--space-4); margin-top:var(--space-2); }
.nb-item-actions .linkbtn{ min-height:32px; font-size:.82rem; }

.rc-log{ flex:1; min-height:0; overflow-y:auto; margin:0; }
/* Assistant prose runs the full column (no measure cap — spec); the
   participant keeps the base 76%/88% compact bubble. */
.rc-msg .bubble{ font-size:.95rem; white-space:normal; }
.rc-msg.participant .bubble{ white-space:pre-wrap; }
.rc-msg.interviewer .bubble:not(.status):not(.typing-dots)::before{ content:"OPSY RESEARCH"; }
/* A pending answer is only the status row — no sender row above it, so there
   is exactly one dot on screen. The :not()s are for specificity: this must
   outweigh both nameplate content rules, not describe the bubble. rc-pending
   is the primary mechanism (set by the renderer); :has() stays as a backstop
   for markup that predates the class. */
.rc-msg.interviewer .bubble.rc-pending:not(.status):not(.typing-dots)::before,
.rc-msg.interviewer .bubble:not(.status):not(.typing-dots):has(.rc-status)::before{ content:none; }

.rc-status{ margin:0; display:flex; align-items:center; gap:8px; font-size:12px; font-style:normal; color:var(--color-text-faint); }
.rc-status::before{ content:""; width:7px; height:7px; flex:none; border-radius:50%; background:var(--color-primary); animation:statusPulse 1.5s ease-in-out infinite; }
/* The stream caret: a cursor that says "still writing", not a spinner that
   says "nothing is happening". */
.rc-caret{ display:inline-block; width:2px; height:1em; vertical-align:-2px; margin-left:2px;
  background:var(--color-primary); animation:rcBlink 1s steps(2) infinite; }
@keyframes rcBlink{ 0%,50%{opacity:1;} 50.01%,100%{opacity:0;} }

.bubble-md p{ margin:0 0 .6em; } .bubble-md p:last-child{ margin-bottom:0; }
.bubble-md h2,.bubble-md h3{ font-size:1.02rem; margin:.6em 0 .3em; }
.bubble-md ul{ padding-left:1.1em; margin:.4em 0; } .bubble-md li{ margin:.2em 0; }
.bubble-md strong{ font-weight:650; }

/* Citation markers are native links when their source includes a safe URL. */
.rc-cite{ font-size:.75em; font-weight:700; color:var(--color-primary);
  background:var(--color-primary-light); border-radius:4px; padding:1px 4px; margin:0 1px;
  text-decoration:none; white-space:nowrap; }
.rc-cite:hover{ color:var(--neon-text); text-decoration:underline; }
.rc-cite-static{ cursor:default; }

/* Personal summaries are composed beside the research answer on-device. The
   first three stay visible; Full access folds any remainder so the thread does
   not become another data dashboard. */
.rc-personal-context{ display:grid; gap:var(--space-3); margin-top:var(--space-4); padding:var(--space-4);
  border:1px solid var(--neon-edge); border-radius:var(--radius-lg); background:var(--neon-fill); }
.rc-context-head{ display:flex; align-items:center; gap:9px; }
.rc-context-head > span:last-child{ display:grid; gap:1px; }.rc-context-head b{ font-size:.88rem; }
.rc-context-head small{ color:var(--color-text-muted); font-size:.7rem; text-transform:uppercase; letter-spacing:.05em; }
.rc-context-icon{ width:32px; height:32px; flex:none; display:grid; place-items:center; border-radius:var(--radius-full);
  background:var(--color-surface); color:var(--neon-text); border:1px solid var(--neon-edge); }
.rc-personal-context > ul,.rc-context-more ul{ margin:0; padding-left:1.15rem; display:grid; gap:6px; }
.rc-personal-context li{ color:var(--color-text); font-size:.84rem; line-height:1.5; }
.rc-context-more{ padding-top:var(--space-2); border-top:1px solid var(--neon-edge); }
.rc-context-more summary{ min-height:44px; display:flex; align-items:center; color:var(--neon-text); font-size:.8rem; font-weight:600; cursor:pointer; }
.rc-context-more ul{ padding-top:var(--space-2); }
.rc-context-local{ margin:0; color:var(--color-text-muted); font-size:.72rem; line-height:1.45; }

/* Evidence is one quiet row until someone asks to inspect it. Inline citation
   markers remain direct links, while the complete bibliography uses native
   details/summary keyboard behavior. */
.rc-references{ margin-top:16px; }  /* spec: 16px between answer and sources */
.rc-references > summary{ min-height:48px; display:flex; align-items:center; gap:10px; list-style:none; cursor:pointer;
  color:var(--color-text-muted); border-radius:var(--radius-sm); }
.rc-references > summary::-webkit-details-marker{ display:none; }
.rc-references > summary:hover{ color:var(--color-text); }
.rc-reference-icon{ width:30px; height:30px; flex:none; display:grid; place-items:center; border-radius:var(--radius-full);
  border:1px solid var(--color-border); background:var(--color-surface-2); color:var(--neon-text); }
.rc-reference-copy{ min-width:0; flex:1; display:grid; gap:1px; }
.rc-reference-copy b{ color:var(--color-text); font-size:.82rem; }.rc-reference-copy span{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:.7rem; }
.rc-reference-chevron{ flex:none; transition:transform var(--transition); }.rc-references[open] .rc-reference-chevron{ transform:rotate(180deg); }
.rc-reference-panel{ padding:var(--space-2) 0 var(--space-1); }
.rc-sources{ list-style:none; display:flex; flex-wrap:wrap; gap:8px; margin:0; padding:0; }
.rc-sources li{ min-width:0; max-width:100%; }
/* Quiet source pills: a 5px emerald dot instead of icons; light sits on the
   page background, dark on the surface token so the pill never vanishes into
   the panel (spec decision 2). */
.rc-pill{ max-width:280px; min-width:0; display:inline-flex; align-items:center; gap:5px;
  border:1px solid var(--color-border); background:var(--color-bg); color:var(--color-text-muted);
  border-radius:var(--radius-full); padding:5px 12px; font-size:11px; font-weight:500; cursor:pointer; text-align:left;
  white-space:nowrap; text-decoration:none; transition:border-color var(--transition), color var(--transition); }
.rc-pill::before{ content:""; width:5px; height:5px; flex:none; border-radius:50%; background:var(--color-primary); }
[data-theme="dark"] .rc-pill{ background:var(--color-surface); }
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]) .rc-pill{ background:var(--color-surface); } }
.rc-pill:hover{ border-color:var(--color-primary); color:var(--color-primary); }
.rc-pill-static{ cursor:default; opacity:.72; }
.rc-pill-tag{ font-weight:700; color:var(--color-primary); flex:none; }
.rc-pill-title{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.rc-pill-year{ color:var(--color-text-muted); flex:none; }
.rc-source-details{ margin-top:var(--space-3); }
.rc-source-detail-list{ display:grid; gap:var(--space-3); padding-top:var(--space-2); }
.rc-source-detail{ display:grid; gap:2px; font-size:.78rem; color:var(--color-text-muted); }
.rc-source-detail b{ color:var(--color-text); font-size:.82rem; }
.rc-source-detail b span{ color:var(--neon-text); margin-right:4px; }
.rc-source-detail p{ margin:0; }

.rc-footer{ display:flex; align-items:center; gap:10px; margin-top:8px; flex-wrap:wrap; }
.rc-lane{ font-size:.7rem; text-transform:uppercase; letter-spacing:.05em; font-weight:700;
  color:var(--color-text-faint); border:1px solid var(--color-divider); border-radius:var(--radius-full); padding:2px 8px; }
.linkbtn{ background:none; border:none; padding:0; font:inherit; color:var(--color-primary);
  text-decoration:underline; cursor:pointer; }

.rc-chips{ display:flex; flex-direction:column; align-items:flex-start; gap:var(--space-3);
  max-width:min(100%, 42rem); margin:0 0 var(--space-5) 0; padding:0; }
.rc-chips[hidden]{ display:none; }
.rc-chipgroup{ width:100%; display:flex; flex-direction:column; align-items:flex-start; }
.rc-choice-row{ margin:0; max-width:100%; justify-content:flex-start; }
.rc-choice-row.talk-choices.compact{ margin:0; justify-content:flex-start; max-width:100%; }
.rc-choice-row.talk-choices.compact .talk-choice{ max-width:min(100%, 34rem); justify-content:flex-start; padding:9px 14px;
  border-radius:var(--radius-md); border-color:var(--color-border); background:var(--color-surface); text-align:left; line-height:1.35; }
.rc-choice-row.talk-choices.compact .talk-choice:hover{ border-color:var(--neon-edge-hover); background:var(--neon-fill); }
.rc-chiplabel{ display:block; font-size:.72rem; text-transform:uppercase; letter-spacing:.05em;
  font-weight:700; color:var(--color-text-faint); margin:0 0 7px; text-align:left; }

.rc-composer-host{ flex:none; }
.rc-composer{ display:flex; margin:0 auto; padding:var(--space-3) var(--space-5) calc(var(--space-4) + env(safe-area-inset-bottom)); }
.rc-composer input{ font-size:1rem; }
.rc-composer input[readonly]{ opacity:.68; cursor:wait; }
.rc-composer .talk-send:disabled{ opacity:.5; cursor:wait; }

@media (max-width:34rem){
  .rc-meta{ align-items:center; }
  .rc-meta .rc-title{ max-width:12rem; font-size:.84rem; }
  .rc-data-trigger{ padding:4px 8px; }
  .rc-data-trigger-copy b{ font-size:.74rem; }.rc-data-trigger-copy > span{ font-size:.72rem; }
  .rc-data-trigger-arrow{ display:none; }
  .rc-data-dialog{ width:100%; max-width:none; max-height:92dvh; margin:auto 0 0; border-radius:var(--radius-lg) var(--radius-lg) 0 0; }
  .rc-data-sheet{ max-height:92dvh; padding:var(--space-5) var(--space-4) calc(var(--space-5) + env(safe-area-inset-bottom));
    border-radius:var(--radius-lg) var(--radius-lg) 0 0; border-bottom:0; }
  .rc-data-sheet-head h2{ font-size:1.3rem; }
  .rc-data-intro{ margin-bottom:var(--space-4); }
  .rc-access-option{ min-height:88px; padding:var(--space-3); }
  .rc-data-sheet-actions{ align-items:stretch; }.rc-data-sheet-actions .btn{ min-width:112px; }
  .rc-choice-row.talk-choices.compact .talk-choice{ width:100%; max-width:100%; }
  .rc-personal-context{ padding:var(--space-3); }
  .rc-pill-title{ white-space:normal; overflow:visible; text-overflow:clip; line-height:1.3; }
}

@media (prefers-reduced-motion:reduce){
  .rc-caret{ animation:none; opacity:1; }
  .rc-msg{ animation:none; }
}

/* ===== Body & Mind (WEAR-02, wear-panel.js) ================================
   Shares .card/.btn/.notice/.small/.muted with the rest of the app; only the
   layout details below are its own. Everything reads from the tokens, so light
   and dark mode both just work and a palette change never needs this block. */
.wear-title{ font-family:var(--font-display); font-size:clamp(1.6rem,1.1rem+1.6vw,2.2rem); margin-bottom:var(--space-1); }
.wear-section{ margin-top:var(--space-6); }
.wear-h2{ font-size:1.15rem; margin-bottom:var(--space-3); }
.wear-section .card + .card{ margin-top:var(--space-3); }
.wear-actions{ display:flex; gap:var(--space-3); flex-wrap:wrap; margin-top:var(--space-4); }
.wear-note{ margin-bottom:var(--space-3); }
.wear-danger{ color:var(--color-error); }
.wear-sample-banner{ border-color:var(--color-error); border-left:3px solid var(--color-error); background:color-mix(in oklab,var(--color-error) 7%,var(--color-bg)); margin-bottom:var(--space-4); }

/* connect: the metric list and the history choice */
.wear-metrics{ list-style:none; padding:0; margin:0 0 var(--space-4); display:grid; gap:var(--space-2); }
.wear-metrics li{ display:flex; gap:var(--space-3); align-items:baseline; font-size:.95rem; }
.wear-dot{ width:6px; height:6px; border-radius:var(--radius-full); background:var(--color-primary); flex:none; transform:translateY(-2px); }
.wear-choices{ display:grid; gap:var(--space-2); }
.wear-choice{ display:flex; min-height:44px; align-items:center; padding:0 var(--space-4); border:1px solid var(--color-border); border-radius:var(--radius-md); background:transparent; color:var(--color-text); font-weight:600; text-align:left; cursor:pointer; transition:background var(--transition),border-color var(--transition); }
.wear-choice:hover{ background:var(--color-surface-2); }
.wear-choice[aria-pressed="true"]{ border-color:var(--color-primary); background:var(--color-primary-light); color:var(--color-primary); }

/* tables: the ledger and the diary. Wide content scrolls inside the card. */
.wear-scroll{ overflow-x:auto; }
.wear-table{ width:100%; border-collapse:collapse; font-size:.88rem; }
.wear-table th, .wear-table td{ text-align:left; padding:var(--space-2) var(--space-2); border-bottom:1px solid var(--color-divider); }
.wear-table th{ color:var(--color-text-muted); font-weight:600; font-size:.8rem; letter-spacing:.02em; }
.wear-table tr:last-child td{ border-bottom:none; }
.wear-num{ text-align:right; font-variant-numeric:tabular-nums; }
.wear-month td{ font-weight:700; color:var(--color-text-muted); padding-top:var(--space-4); }
.wear-x{ min-width:34px; min-height:34px; border-radius:var(--radius-full); background:transparent; color:var(--color-text-faint); font-size:.9rem; cursor:pointer; transition:background var(--transition),color var(--transition); }
.wear-x:hover{ background:var(--color-surface-2); color:var(--color-error); }

/* the diary form */
.wear-form-row{ display:flex; gap:var(--space-2); flex-wrap:wrap; align-items:center; }
.wear-form-row + .wear-form-row{ margin-top:var(--space-3); }
.wear-input{ min-height:44px; padding:0 var(--space-3); border:1px solid var(--color-border); border-radius:var(--radius-md); background:var(--color-bg); color:var(--color-text); font-size:.95rem; flex:1 1 12rem; }
.wear-input:focus{ outline:2px solid var(--color-primary); outline-offset:1px; border-color:var(--color-primary); }
.wear-input-s{ flex:0 1 8.5rem; }
.wear-input-grow{ flex:2 1 16rem; }

/* routines and results */
.wear-routine-head{ display:flex; align-items:center; gap:var(--space-2); flex-wrap:wrap; margin-bottom:var(--space-2); }
.wear-routine-head h3{ font-size:1.05rem; margin:0; }
.wear-badge{ display:inline-block; font-size:.7rem; letter-spacing:.05em; text-transform:uppercase; padding:2px 8px; border-radius:var(--radius-full); border:1px solid var(--color-border); color:var(--color-text-muted); }
.wear-badge.ok{ color:var(--color-primary); border-color:var(--color-primary); background:var(--color-primary-light); }
.wear-result{ margin-top:var(--space-4); padding-top:var(--space-4); border-top:1px solid var(--color-divider); }
.wear-narrative{ line-height:1.6; margin-bottom:var(--space-3); }
.wear-caveats{ margin:0 0 var(--space-3); padding-left:var(--space-5); }
.wear-caveats li{ font-size:.85rem; color:var(--color-text-muted); line-height:1.55; margin-bottom:var(--space-1); }
.wear-work summary{ cursor:pointer; font-size:.85rem; color:var(--color-text-muted); min-height:34px; display:flex; align-items:center; }
.wear-work pre{ font-family:var(--font-mono); font-size:.72rem; overflow-x:auto; background:var(--color-surface-2); padding:var(--space-3); border-radius:var(--radius-md); }

/* Connect data: the pathway grid (core/wear/providers.js). Three states —
   available, unconfigured (real pathway, off on this deployment), planned.
   Planned tiles are inert in the MARKUP (a div, aria-disabled, no handler);
   these rules only make that legible, they are not what enforces it. */
.wear-providers{ display:grid; grid-template-columns:repeat(auto-fill,minmax(15rem,1fr)); gap:var(--space-3); margin-bottom:var(--space-4); }
.wear-provider{ border:1px solid var(--color-border); border-radius:var(--radius-lg); padding:var(--space-4); background:var(--color-surface); display:flex; flex-direction:column; gap:var(--space-1); }
.wear-provider.is-available{ border-color:var(--color-primary); background:color-mix(in oklab,var(--color-primary) 5%,var(--color-surface)); }
.wear-provider.is-planned{ opacity:.55; background:transparent; border-style:dashed; }
.wear-provider.is-unconfigured{ border-style:dashed; }
.wear-provider-head{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-2); }
.wear-provider-head b{ font-size:1rem; }
.wear-provider-kind{ font-size:.78rem; text-transform:uppercase; letter-spacing:.04em; color:var(--color-text-faint); }
.wear-provider-unlocks{ font-size:.9rem; line-height:1.5; color:var(--color-text-muted); margin:var(--space-1) 0 0; }
.wear-provider .wear-actions{ margin-top:auto; padding-top:var(--space-3); }

/* ---- Show me around (TOUR-01) -------------------------------------------------
   A guided walk on the real screens. The veil blocks the page while a card is
   up, except on the practice step, where the person is meant to use the
   composer. The spotlight is one element with a very large box-shadow: what it
   does not cover is what the step is about. */
.tour-veil{ position:fixed; inset:0; z-index:60; background:transparent; }
.tour-veil.interactive{ pointer-events:none; }
.tour-spot{ position:fixed; z-index:61; border-radius:var(--radius-lg); pointer-events:none; box-shadow:0 0 0 200vmax rgba(5,10,7,.62), 0 0 0 2px var(--color-primary); transition:top .3s ease,left .3s ease,width .3s ease,height .3s ease; }
.tour-spot.none{ left:50%; top:-20px; width:0; height:0; box-shadow:0 0 0 200vmax rgba(5,10,7,.62); }
.tour-card{ position:fixed; left:var(--space-4); right:var(--space-4); z-index:62; max-width:440px; margin:0 auto; background:var(--color-bg); color:var(--color-text); border:1px solid var(--color-border); border-radius:var(--radius-xl); padding:var(--space-4) var(--space-5); box-shadow:var(--shadow-lg); }
.tour-card:focus{ outline:none; }
.tour-card:focus-visible{ outline:2px solid var(--color-primary); outline-offset:2px; }
.tour-step{ font-size:.72rem; letter-spacing:.1em; text-transform:uppercase; color:var(--color-primary); font-weight:700; }
.tour-card h3{ margin:4px 0; font-size:1.1rem; line-height:1.25; }
.tour-card p{ margin:0; color:var(--color-text-muted); font-size:.95rem; }
.tour-example{ margin-top:var(--space-2); padding:var(--space-2) var(--space-3); border-radius:var(--radius-md); background:var(--color-primary-light); color:var(--color-text); font-size:.9rem; }
.tour-nav{ display:flex; gap:var(--space-2); align-items:center; margin-top:var(--space-3); }
.tour-nav .tour-skip{ margin-right:auto; background:none; border:none; color:var(--color-text-faint); font-weight:600; font-size:.9rem; padding:8px 2px; min-height:44px; cursor:pointer; }
.tour-nav .tour-skip:focus-visible{ outline:2px solid var(--color-primary); outline-offset:2px; }
.tour-dots{ display:flex; gap:4px; margin-top:var(--space-3); }
.tour-dots i{ height:4px; width:12px; border-radius:2px; background:var(--color-border); }
.tour-dots i.on{ background:var(--color-primary); width:22px; }
.tour-demo-label{ display:block; font-size:.7rem; letter-spacing:.1em; text-transform:uppercase; color:var(--color-text-faint); font-weight:700; margin-bottom:2px; }
.tour-consent{ margin:var(--space-3) 0; }
.tour-consent b{ display:block; }
@media(prefers-reduced-motion:reduce){ .tour-spot{ transition:none; } }
/* One welcome slide needs no dots. */
.ob-dots:has(.ob-dot:only-child){ display:none; }
/* The lifebuoy menu: two choices, big enough to hit. */
.help-menu .panel{ max-width:420px; }
.help-menu .menu-btns{ display:flex; flex-direction:column; gap:var(--space-3); padding:var(--space-5); }

/* ---- FORUM-01: focus chrome -------------------------------------------------
   A package that declares ui.chrome "focus" runs the whole visit as the
   conversation and nothing else. Hidden in CSS as well as in JS (setView), so
   no step between the link and the end card can bring a piece of the app back:
   the tab bar, the pill rail, the engine badge, the leave chevron, the info
   button, the meta strip and the progress strip. One slim header row stays. */
body.chrome-focus #tabbar,
body.chrome-focus #talkPills,
body.chrome-focus #engineBadge,
body.chrome-focus #leaveBtn,
body.chrome-focus #transparencyBtn,
body.chrome-focus #interviewMeta,
body.chrome-focus #progress,
body.chrome-focus .brand .bar-topic{ display:none !important; }
body.chrome-focus .topbar{
  flex-wrap:nowrap;
  gap:var(--space-2);
  padding:calc(var(--space-2) + env(safe-area-inset-top)) var(--space-3) var(--space-2);
}
body.chrome-focus .brand{ flex:1 1 auto; min-width:0; cursor:default; gap:var(--space-2); }
body.chrome-focus .brand.has-topic .brand-word{ display:inline; }
body.chrome-focus .brand-word{ white-space:nowrap; }
.brand-suffix{ font-family:var(--font-body); font-size:.95rem; font-weight:500; color:var(--color-text-muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; }
.brand-suffix[hidden]{ display:none; }
.focus-privacy{ flex:none; display:inline-flex; align-items:center; min-height:44px; padding:0 var(--space-2); font-size:.85rem; font-weight:600; color:var(--color-text-muted); text-decoration:underline; text-underline-offset:3px; }
.focus-privacy[hidden]{ display:none; }

/* The skip next to an optional question lives IN the thread, under the
   question, so the bottom of the screen stays one 64px bar. */
.focus-skip{ display:flex; justify-content:flex-start; margin:calc(-1 * var(--space-1)) 0 0 var(--space-2); }
.focus-skip .talk-pill{ font-size:.85rem; }

/* The proposal card (WP3): a bordered summary with labelled rows. */
.proposal-card{ align-self:stretch; max-width:34rem; margin:0 0 0 var(--space-1); padding:var(--space-4); border:1px solid var(--color-border); border-left:3px solid var(--color-primary); border-radius:var(--radius-lg); background:var(--color-surface); }
.proposal-card .pc-eyebrow{ font-size:.72rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--color-primary); }
.proposal-card .pc-title{ font-size:1.15rem; margin:var(--space-1) 0 var(--space-2); overflow-wrap:anywhere; }
.proposal-card .pc-desc{ margin:0 0 var(--space-3); color:var(--color-text); overflow-wrap:anywhere; }
.proposal-card .pc-rows{ display:grid; grid-template-columns:minmax(6.5rem,36%) minmax(0,1fr); gap:6px var(--space-3); margin:0; font-size:.9rem; }
.proposal-card .pc-rows dt{ color:var(--color-text-faint); }
.proposal-card .pc-rows dd{ margin:0; color:var(--color-text); overflow-wrap:anywhere; }
.proposal-card .pc-ask{ margin:var(--space-3) 0 0; font-weight:600; }

/* The end of a focus study. */
.forum-end-card{ text-align:left; }
.forum-end-card .forum-record{ display:flex; flex-direction:column; align-items:flex-start; margin:var(--space-3) 0 var(--space-2); }
.forum-end-card .end-actions{ display:flex; flex-wrap:wrap; gap:var(--space-2); margin:var(--space-2) 0; }
.forum-end-card .forum-close-line{ margin-top:var(--space-4); font-weight:600; }

/* The privacy and lifebuoy sheets. */
.forum-sheet .panel{ display:flex; flex-direction:column; max-width:640px; }
.forum-sheet-frame{ width:100%; height:min(70dvh,40rem); border:0; background:var(--color-bg); }
.forum-sheet-body{ padding:var(--space-4) var(--space-5) var(--space-5); display:flex; flex-direction:column; gap:var(--space-2); }

/* Focus chrome, choices: wrapped chips instead of a two-column grid, so ten
   contribution types fit in about four rows and the thread keeps its share of
   a phone screen. Selection still shows as the selected chip's colour, and the
   deliberate Continue stays, because the type decides the branch. */
body.chrome-focus #view-talk .conversation-options,
body.chrome-focus #view-talk .conversation-options-compact,
body.chrome-focus #view-talk .conversation-options-list{
  display:flex; flex-wrap:wrap; gap:6px; max-height:min(34dvh,16rem); overflow-y:auto; padding:1px;
}
body.chrome-focus #view-talk .conversation-option{
  flex:0 0 auto; width:auto; min-height:36px; padding:4px 13px; border-radius:var(--radius-full); align-items:center; font-size:.92rem; line-height:1.25;
}
body.chrome-focus #view-talk .conversation-option-mark{ display:none; }
body.chrome-focus #view-talk .dock{ padding-top:var(--space-2); gap:var(--space-2); }
body.chrome-focus #view-talk .conversation-response-actions > .conversation-continue{ min-height:44px; }

/* Focus chrome, chips, round 2 (Steven, 2026-10-07: "the menu is buggy").
   The first chip LOOKED picked: the widget focuses it, and its focus ring sat
   outside the chip where the scroll box clipped it, while hover on a desktop
   was styled exactly like "selected". Now: the ring fits inside the box, hover
   is a quiet border, and a picked chip is filled with a tick. */
body.chrome-focus #view-talk .conversation-options,
body.chrome-focus #view-talk .conversation-options-compact,
body.chrome-focus #view-talk .conversation-options-list{ padding:4px; }
body.chrome-focus #view-talk .conversation-option:focus-visible{ outline:2px solid var(--color-primary); outline-offset:1px; }
body.chrome-focus #view-talk .conversation-option:hover{ background:var(--color-surface); border-color:var(--color-text-faint); }
body.chrome-focus #view-talk .conversation-option.selected,
body.chrome-focus #view-talk .conversation-option.selected:hover{
  background:var(--color-primary); border-color:var(--color-primary); box-shadow:none; color:var(--color-text-inverse);
}
body.chrome-focus #view-talk .conversation-option.selected .conversation-option-label::before{ content:"\2713\00a0"; }
body.chrome-focus #view-talk .conversation-continue{ width:100%; min-height:44px; }
body.chrome-focus #view-talk .conversation-self-describe{ margin-top:var(--space-1); }
