/*
 * Phase 6a baseline styles for the Synesis P_success frontend.
 *
 * Card-based layout with progressive disclosure. Sections start hidden
 * (via the `hidden` HTML attribute) and main.js reveals them as the user
 * advances through the flow: setup → research → parameters / scenarios /
 * benchmarks / Monte Carlo. The full theme port from
 * `../SynesisLabs Platform/psuccess-platform.html` is still deferred —
 * this pass is the structural rework, with the visual upgrade staying
 * intentionally light so the markup is what we're committing to, not
 * the colours.
 */

/* === Tokens =============================================================== */
/*  Single source of truth for the colour palette. Tweak here, not at        */
/*  per-component sites. The hex values stay close to the legacy brand       */
/*  so a future dark-theme port stays a colour swap, not a layout fight.    */

:root,
[data-theme="light"] {
  --bg: #f5f7fa;
  --surface: #ffffff;
  --surface-muted: #f7fafc;
  --border: #e2e8f0;
  --border-strong: #cbd5e0;
  --text: #1a202c;
  --text-muted: #4a5568;
  --text-subtle: #718096;
  --accent: #2b6cb0;
  --accent-strong: #1e3a5f;
  --accent-soft: rgba(43, 108, 176, 0.08);
  --good: #2f855a;
  --good-soft: #d1fae5;
  --warn: #b7791f;
  --warn-soft: #fef3c7;
  --bad: #c53030;
  --bad-soft: #fee2e2;
  --orange: #f97316;
  --modal-backdrop: rgba(15, 23, 42, 0.45);
  --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04), 0 1px 3px rgba(15, 23, 42, 0.06);
  --shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08);
}

/*  Dark-theme token overrides. Same variable names, different values —
 *  every component reads via `var(--*)` so swapping the attribute on
 *  `<html>` swaps the whole UI without touching any per-component CSS.
 *  Soft variants use rgba alpha so coloured backgrounds blend onto the
 *  dark surfaces correctly; light mode's pastel solids would look
 *  garish on a dark card.
 */
[data-theme="dark"] {
  --bg: #0b1120;
  --surface: #131c30;
  --surface-muted: #1c2740;
  --border: #2a3656;
  --border-strong: #475569;
  --text: #e7ecf3;
  --text-muted: #cbd5e0;
  --text-subtle: #8c9ab2;
  --accent: #38bdf8;
  --accent-strong: #7dd3fc;
  --accent-soft: rgba(56, 189, 248, 0.14);
  --good: #34d399;
  --good-soft: rgba(52, 211, 153, 0.18);
  --warn: #fbbf24;
  --warn-soft: rgba(251, 191, 36, 0.18);
  --bad: #f87171;
  --bad-soft: rgba(248, 113, 113, 0.18);
  --orange: #fb923c;
  --modal-backdrop: rgba(0, 0, 0, 0.6);
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 4px 14px rgba(0, 0, 0, 0.5);
}

/*  Smooth-but-not-distracting transition when the user toggles. Picks
 *  the properties that actually change so the page doesn't animate
 *  unrelated layout shifts.
 */
html {
  transition: background-color 0.15s linear;
}

body {
  transition: background-color 0.15s linear, color 0.15s linear;
}

/* === Page-level ========================================================== */
/*  Constrained reading column, neutral background, consistent type ramp.    */

* {
  box-sizing: border-box;
}

/* The `hidden` attribute must always win over a component's own `display`
   rule. Several shell elements set `display` (`.login-wrap` → flex, `.app-body`
   → grid, `.app-menu` / `.menu-item` → flex) which would otherwise override the
   UA `[hidden] { display: none }` and leave "hidden" elements rendered. This
   guard (the HTML5-boilerplate idiom) keeps `el.hidden = true` authoritative
   everywhere it's used. */
[hidden] {
  display: none !important;
}

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.5;
}

main {
  max-width: 880px;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 4rem;
}

/* === App header ========================================================== */
/*  Sticks to the top of the page (no scroll detach). Title on the left,    */
/*  account chip on the right; the chip is hidden when no session exists.   */

.app-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.85rem 1.5rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.app-title {
  font-size: 1.2rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}

.app-title .title-accent {
  color: var(--accent);
}

.app-header-actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

/* NOTE: `.app-header`, `.app-title`, `.app-header-actions` and `.title-accent`
 * above are retained because the admin SPA (`admin.html`) still uses that
 * header layout. The main app's header was replaced by the shell top bar in
 * UI-overhaul phase U0 (see the "Shell" sections near the end of this file). */

.theme-icon {
  display: block;
  pointer-events: none;
}

/*  Default (light theme): show moon glyph, hide sun. Dark theme flips. */
.theme-icon-sun { display: none; }
[data-theme="dark"] .theme-icon-moon { display: none; }
[data-theme="dark"] .theme-icon-sun  { display: block; }

/* === Card system ========================================================= */
/*  Every major section sits inside a .card. The visible heading is in
    .card-header > .card-title, with optional .card-subtitle copy. Actions
    live in .card-footer at the bottom. Cards stack vertically with
    consistent spacing so the page reads as a top-down checklist. */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow-sm);
  padding: 1.25rem 1.5rem;
  margin-bottom: 1.25rem;
}

.card-header {
  margin-bottom: 1rem;
}

.card-title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--accent-strong);
}

.card-subtitle {
  margin: 0.35rem 0 0;
  font-size: 0.9rem;
  color: var(--text-muted);
  line-height: 1.45;
}

.card-footer {
  margin-top: 1rem;
}

.section-subtitle {
  margin: 1rem 0 0.5rem;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-subtle);
  font-weight: 700;
}

.prose {
  line-height: 1.55;
  color: var(--text);
}

.prose strong {
  color: var(--accent-strong);
}

/* === Form controls ======================================================= */
/*  Inputs span the card width by default. Labels stack above inputs.       */

label {
  display: block;
  margin: 0.85rem 0 0.3rem;
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--text);
}

label .label-hint {
  font-weight: 400;
  color: var(--text-subtle);
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
select,
textarea {
  width: 100%;
  padding: 0.55rem 0.7rem;
  font-size: 0.95rem;
  font-family: inherit;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  box-sizing: border-box;
}

input:focus,
select:focus,
textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
  border-color: var(--accent);
}

textarea {
  resize: vertical;
  min-height: 5rem;
  font-family: inherit;
}

.checkbox-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.6rem;
  font-weight: 500;
  font-size: 0.9rem;
}

.checkbox-label input {
  width: auto;
  margin: 0;
}

.settings-grid label {
  margin-top: 0.7rem;
}

/* === Buttons ============================================================= */
/*  Three visual roles: primary (the headline action — RUN MODEL, Log in),  */
/*  secondary (RUN scenarios / RUN benchmarks / Run MC), ghost (Log out,    */
/*  modal close, anything that shouldn't compete for attention).            */

button {
  font-family: inherit;
  cursor: pointer;
  border: 0;
  border-radius: 6px;
  font-weight: 600;
  padding: 0.5rem 1rem;
  font-size: 0.95rem;
  transition: filter 0.12s, background-color 0.12s;
}

button:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.btn-primary {
  background: var(--accent);
  color: #fff;
}

.btn-primary:hover:not(:disabled) {
  background: var(--accent-strong);
}

.btn-secondary {
  background: var(--surface-muted);
  color: var(--text);
  border: 1px solid var(--border-strong);
}

.btn-secondary:hover:not(:disabled) {
  background: #edf2f7;
  border-color: var(--accent);
  color: var(--accent);
}

.btn-ghost {
  background: transparent;
  color: var(--text-muted);
  padding: 0.35rem 0.65rem;
  font-weight: 500;
}

.btn-ghost:hover:not(:disabled) {
  color: var(--text);
  background: var(--surface-muted);
}

.btn-large {
  padding: 0.75rem 1.5rem;
  font-size: 1rem;
}

.btn-small {
  padding: 0.3rem 0.55rem;
  font-size: 0.85rem;
}

/* Horizontal group of buttons inside a card-footer (e.g. Export + Generate
   report). Wraps on narrow screens; the gap keeps them from touching. */
.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
}

/* === Status + error lines ================================================ */
/*  Status line uses the spinner cycle from app/status-cycle.js — monospace */
/*  keeps the leading braille glyph at fixed width.                          */

.status-line {
  margin: 0.6rem 0 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.92rem;
  color: var(--accent);
}

.error {
  color: var(--bad);
  margin: 0.5rem 0 0;
}

.meta-status {
  text-align: center;
  color: var(--text-muted);
  font-style: italic;
  margin: 1rem 0;
}

/* === Sign-in (Clerk) ===================================================== */

/*
 * The sign-in screen is just a centering wrapper around Clerk's own card — we
 * intentionally don't wrap it in our .card (that produced a redundant outer box
 * the Clerk card overflowed). Clerk is themed to our palette via clerkAppearance()
 * in app/clerk.js.
 */
.login-wrap {
  display: flex;
  /* Column so the admin-only notice (when revealed) stacks above the Clerk
     card rather than sitting beside it; align-items keeps the card centred
     exactly as the old row layout did. */
  flex-direction: column;
  align-items: center;
  margin: 3rem auto;
  padding: 0 1rem;
}

/* Warning banner shown on admin-only deployments (branch trial stacks) after
   a non-admin's sign-in is refused server-side — see auth:admin-only in
   app/main.js. Uses the theme's "bad" colour at low opacity so it reads as a
   warning in both light and dark. */
.login-notice {
  max-width: 25rem;
  margin: 0 0 1.25rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--bad);
  border-radius: 6px;
  color: var(--bad);
  background: color-mix(in srgb, var(--bad) 8%, transparent);
  font-size: 0.92rem;
}

.card-title {
  margin: 0;
}

/* === Upload zone ========================================================= */

.upload-zone {
  border: 1.5px dashed var(--border-strong);
  border-radius: 6px;
  padding: 1rem 0.75rem;
  text-align: center;
  font-size: 0.9rem;
  color: var(--text-muted);
  cursor: pointer;
  background: var(--surface-muted);
  transition: border-color 0.12s, color 0.12s, background-color 0.12s;
}

.upload-zone:hover,
.upload-zone:focus {
  border-color: var(--accent);
  color: var(--accent);
  outline: none;
}

.upload-zone.dragging {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
}

.upload-hint {
  margin-top: 0.25rem;
  font-size: 0.75rem;
  color: var(--text-subtle);
}

.file-list {
  margin-top: 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.file-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.55rem;
  background: var(--surface-muted);
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 0.85rem;
}

.file-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-badge {
  font-size: 0.75rem;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  background: var(--border);
  color: var(--text-muted);
}

.file-badge-parsing { background: var(--border); color: var(--text-muted); }
.file-badge-ok      { background: var(--good-soft); color: var(--good); }
.file-badge-unsupported { background: var(--warn-soft); color: var(--warn); }
.file-badge-error   { background: var(--bad-soft); color: var(--bad); }

.file-remove {
  background: transparent;
  color: var(--text-subtle);
  border: 0;
  padding: 0 0.4rem;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  font-weight: 700;
}

.file-remove:hover {
  color: var(--bad);
}

/* === Parameter group headings ============================================ */
/*  Group headings ("INTERNAL FACTORS (PROJECT STRENGTHS)", "ELASTICITIES     */
/*  (PROJECT RISKS)", "CALIBRATION CONSTANTS") are injected by sliders.js      */
/*  between the three groups of the Parameters tab.                           */

#slidersSection h2 {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin: 1.5rem 0 0.4rem;
  color: var(--text-subtle);
  font-weight: 700;
}
/* First group heading sits directly under the tab-head; trim its top gap. */
#slidersSection h2:first-child {
  margin-top: 0.25rem;
}

/* The old six-cell diagnostic result strip (.result-strip / .result-cell /
   .result-label / .result-value) was replaced by the headline metrics strip in
   UI-overhaul phase U0 — see the "Shell: headline strip" section below. */

/* ========================================================================= *
 *  UI-overhaul phase U2 — Parameters tab rows
 *
 *  Each row (internal factor, elasticity, or calibration delta) is a three-
 *  column grid: label + arrow + ⓘ | track | numeric readout. The track carries
 *  a slim line, two muted endpoint captions, and a value BUBBLE positioned at
 *  the value's spot along the track and colour-coded by favourability. The real
 *  control is a transparent `<input type="range">` (`.param-input`) laid over
 *  the whole track — see the sliders.js header for the accessibility rationale.
 *  All colours use existing tokens so both themes work unchanged.
 * ========================================================================= */

.param-row {
  display: grid;
  grid-template-columns: 11rem 1fr 4rem;
  align-items: center;
  gap: 0.6rem;
  padding: 0.35rem 0;
}

/* Left cell: label text + optional direction arrow + ⓘ info trigger. */
.param-label {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  margin: 0;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text);
}
.param-arrow {
  color: var(--text-subtle);
  font-weight: 700;
}

/* Middle cell: the track. Tall enough for the bubble (above the line) and the
   endpoint captions (below it). `position: relative` anchors all the absolutely
   positioned children. */
.param-track {
  position: relative;
  height: 1.9rem;
}
.param-track-line {
  position: absolute;
  left: 0;
  right: 0;
  top: 0.5rem;
  height: 2px;
  background: var(--border-strong);
  border-radius: 1px;
}
.param-cap {
  position: absolute;
  top: 0.95rem;
  font-size: 0.6rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-subtle);
  pointer-events: none;
  white-space: nowrap;
}
.param-cap-lo { left: 0; }
.param-cap-hi { right: 0; text-align: right; }

/* The value bubble. Positioned by JS (`left: NN%`) and centred on the line; it
   never intercepts pointer events so a drag over it still reaches the input. */
.param-bubble {
  position: absolute;
  top: 0.5rem;
  transform: translate(-50%, -50%);
  padding: 1px 6px;
  border-radius: 5px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  font: 700 0.68rem ui-monospace, SFMono-Regular, monospace;
  white-space: nowrap;
  pointer-events: none;
  z-index: 2;
}
/* Favourability variants — soft fill + strong text, mirroring the file-badge
   pattern so contrast holds in both light and dark themes. */
.param-bubble.fav-good { background: var(--good-soft); color: var(--good); border-color: var(--good); }
.param-bubble.fav-bad { background: var(--bad-soft); color: var(--bad); border-color: var(--bad); }
.param-bubble.fav-warn { background: var(--warn-soft); color: var(--warn); border-color: var(--warn); }
.param-bubble.fav-neutral { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }

/* The real range control: transparent but fully interactive, laid over the
   whole track (above the bubble so a drag anywhere reaches it). Its thumb is
   invisible; the bubble is the visible handle. */
.param-input {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
  z-index: 3;
}
/* Keyboard focus indicator: since the input itself is invisible, draw the ring
   on the track's line + bubble when anything inside the track is focused. */
.param-track:focus-within .param-track-line {
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.param-track:focus-within .param-bubble {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Right cell: the monospace numeric readout (absolute value for params, signed
   delta for calibration rows). */
.param-readout {
  font-variant-numeric: tabular-nums;
  text-align: right;
  font-size: 0.82rem;
  color: var(--text-muted);
  font-family: ui-monospace, SFMono-Regular, monospace;
}

/* === Source badges ======================================================= */
/*  Inline chips above the research summary: "web research used" /          */
/*  "N documents".                                                          */

.source-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

.source-badge {
  display: inline-block;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  font-size: 0.78rem;
  background: var(--border);
  color: var(--text-muted);
}

.source-badge.active {
  background: var(--good-soft);
  color: var(--good);
}

.source-badge.inactive {
  background: var(--surface-muted);
  color: var(--text-subtle);
}

/* === Scenario cards (U3) ================================================= */
/*  Collapsible ScenarioCards: header row (id chip, direction arrow, title,
    Δpp badge, ✎/× icon buttons) + an expandable drawer (description, the
    shifted-params table, outcome line, Adopt, FOLD UP). Rendered by
    app/scenarios.js. All colours come from the existing tokens so both themes
    work unchanged.                                                            */

.scenarios-list {
  margin-top: 0.85rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

/* Toolbar above the scenario list: Summary heading left, Reset right. */
.scenarios-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.scenarios-toolbar .section-subtitle {
  margin: 1rem 0 0.5rem;
}

.scenarios-hint {
  margin: 0 0 0.25rem;
  font-size: 0.8rem;
  color: var(--text-subtle);
  font-style: italic;
}

.scenario-card {
  background: var(--surface-muted);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong);
  border-radius: 6px;
  overflow: hidden;
}
.scenario-card.scenario-up   { border-left-color: var(--good); }
.scenario-card.scenario-down { border-left-color: var(--bad); }

/* Adopted card: accent-tinted ring so the user can see which entry currently
   drives the sliders (shared visual language with .benchmark-card.adopted). */
.scenario-card.adopted {
  background: var(--accent-soft);
  box-shadow: 0 0 0 1px var(--accent) inset;
}

/* Header row — the expand/collapse affordance. */
.scenario-card-head {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.6rem 0.85rem;
  cursor: pointer;
  font-weight: 600;
  font-size: 0.95rem;
}
.scenario-card-head:hover {
  background: var(--surface);
}
.scenario-card-head:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.scenario-id-chip {
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--accent);
}

.scenario-up .scenario-arrow   { color: var(--good); }
.scenario-down .scenario-arrow { color: var(--bad); }

/* Title takes the slack so the Δpp badge + icons sit flush right. */
.scenario-card-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Projected Δpp vs baseline (filled async from /score/batch). */
.scenario-pp {
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--text-muted);
  white-space: nowrap;
}
.scenario-pp.pos { color: var(--good); }
.scenario-pp.neg { color: var(--bad); }

/* ✎ / × icon buttons (shared style with the benchmark cards). */
.scenario-card-actions,
.benchmark-card-actions {
  display: inline-flex;
  gap: 0.15rem;
}
.scenario-icon-btn,
.benchmark-icon-btn {
  background: none;
  border: 0;
  padding: 0.1rem 0.3rem;
  font-size: 0.9rem;
  line-height: 1;
  color: var(--text-subtle);
  cursor: pointer;
  border-radius: 4px;
}
.scenario-icon-btn:hover,
.benchmark-icon-btn:hover {
  color: var(--accent);
  background: var(--accent-soft);
}

/* --- Drawer ------------------------------------------------------------- */

.scenario-drawer,
.benchmark-drawer {
  padding: 0.75rem 0.85rem 0.85rem;
  border-top: 1px solid var(--border);
  cursor: auto;
}

.scenario-desc,
.benchmark-desc {
  font-size: 0.88rem;
  color: var(--text-muted);
  line-height: 1.5;
}
.scenario-desc p,
.benchmark-desc p {
  margin: 0 0 0.4rem;
}

/* Shared shifted-params / comparison table pattern. Column widths are fixed
   for the numeric cells; the wrapper scrolls horizontally on narrow screens
   (min-width on the rows) so the cards never overflow the panel. */
.scenario-table,
.benchmark-table {
  margin-top: 0.75rem;
  overflow-x: auto;
}
.scenario-table-head,
.scenario-tr,
.benchmark-table-head,
.benchmark-tr {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 30rem; /* horizontal scroll instead of squashing at ~900px */
  border-bottom: 1px solid var(--border);
  padding: 0.3rem 0;
}
.scenario-table-head,
.benchmark-table-head {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--text-subtle);
}
.scenario-th.col-name, .scenario-td.col-name,
.benchmark-th.col-name, .benchmark-td.col-name {
  flex: 1;
  min-width: 0;
}
.scenario-th.col-num, .scenario-td.col-num {
  width: 6.5rem;
  flex: none;
}
.benchmark-th.col-num, .benchmark-td.col-num {
  width: 6.5rem;
  flex: none;
}
.scenario-tr, .benchmark-tr {
  font-size: 0.85rem;
}
.scenario-td-code, .benchmark-td-code {
  color: var(--text-subtle);
  font-size: 0.72rem;
}
.scenario-td.mono, .benchmark-td.mono {
  font-family: ui-monospace, SFMono-Regular, monospace;
}
.scenario-td.strong, .benchmark-td.strong { font-weight: 700; }
.scenario-td.muted, .benchmark-td.muted { color: var(--text-muted); }
/* Favourability colouring: pos = favourable move, neg = unfavourable (an
   elasticity DECREASE is favourable — computed in JS per param direction). */
.scenario-td.pos, .benchmark-td.pos { color: var(--good); }
.scenario-td.neg, .benchmark-td.neg { color: var(--bad); }

/* Outcome line: Baseline P_success X% → Scenario P_success Y%. */
.scenario-outcome {
  margin-top: 0.75rem;
  font-size: 0.88rem;
}
.scenario-outcome strong {
  font-family: ui-monospace, SFMono-Regular, monospace;
}
.scenario-outcome strong.pos { color: var(--good); }
.scenario-outcome strong.neg { color: var(--bad); }

/* Adopt button row + FOLD UP footer (shared with benchmark cards). */
.scenario-adopt-row,
.benchmark-adopt-row {
  margin-top: 0.6rem;
  display: flex;
  justify-content: flex-end;
}
.scenario-adopt-btn,
.benchmark-adopt-btn {
  font-size: 0.78rem;
  font-weight: 700;
  padding: 0.45rem 1.1rem;
  border-radius: 6px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
}
.scenario-adopt-btn:hover,
.benchmark-adopt-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
}
/* Adopted state: filled accent + revert affordance in the label. */
.scenario-adopt-btn.active,
.benchmark-adopt-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.scenario-foldup,
.benchmark-foldup {
  margin-top: 0.75rem;
  width: 100%;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  background: none;
  padding: 0.45rem;
  text-align: center;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  cursor: pointer;
}
.scenario-foldup:hover,
.benchmark-foldup:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* === Benchmark cards (U4) ================================================ */
/*  Collapsible BenchmarkCards: #rank / title / p_success% header + drawer
    (rationale, Sources line, comparison line, Adopt, full 14-param table).
    Rendered by app/peers.js; most drawer styles are shared with the scenario
    selectors above.                                                          */

.peers-list {
  margin-top: 0.85rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.benchmark-card {
  background: var(--surface-muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.benchmark-card.adopted {
  background: var(--accent-soft);
  box-shadow: 0 0 0 1px var(--accent) inset;
}

.benchmark-card-head {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.6rem 0.85rem;
  cursor: pointer;
  font-weight: 600;
  font-size: 0.95rem;
}
.benchmark-card-head:hover {
  background: var(--surface);
}
.benchmark-card-head:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.benchmark-rank {
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-size: 0.78rem;
  color: var(--text-subtle);
}

.benchmark-card-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.benchmark-prob {
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--accent);
  white-space: nowrap;
}

/* Muted "Sources: …" line under the description; only http(s) entries are
   links (escaping + URL vetting happens in peers.js#buildSourcesLine). */
.benchmark-sources {
  margin-top: 0.4rem;
  font-size: 0.78rem;
  color: var(--text-subtle);
  overflow-wrap: anywhere; /* long URLs must not overflow the card */
}
.benchmark-sources a {
  color: var(--text-subtle);
  text-decoration: underline;
}
.benchmark-sources a:hover {
  color: var(--accent);
}

/* "<Name> X% vs Your baseline Y% (you trail/lead by N pp)" line. */
.benchmark-compare {
  margin-top: 0.75rem;
  font-size: 0.88rem;
}
.benchmark-compare .cmp-name { font-weight: 700; }
.benchmark-compare strong {
  font-family: ui-monospace, SFMono-Regular, monospace;
}
.benchmark-compare .cmp-pos { color: var(--good); }
.benchmark-compare .cmp-neg { color: var(--bad); }
.benchmark-compare .cmp-neutral { color: var(--text-muted); }

/* === Monte Carlo trigger + modal ========================================= */
/*  Trigger button gets the brand-accent treatment to read as the lone      */
/*  flagship action inside the Parameters card.                              */

.mc-trigger {
  background: linear-gradient(135deg, #f97316, #dc2626) !important;
  color: #fff !important;
  border: 0 !important;
  font-weight: 700;
}

.mc-trigger:hover:not(:disabled) {
  filter: brightness(1.05);
}

#monteCarloModal {
  max-width: 760px;
  width: 92%;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 1.25rem 1.5rem;
  background: var(--surface);
  color: var(--text);
  margin: auto;
}

#monteCarloModal::backdrop {
  background: var(--modal-backdrop);
}

#monteCarloModal h3 {
  margin: 0 0 0.25rem;
  font-size: 1.15rem;
  color: var(--accent-strong);
}

.modal-subtitle {
  margin: 0 0 1rem;
  font-size: 0.9rem;
  color: var(--text-muted);
}

#monteCarloModal label span {
  font-family: ui-monospace, SFMono-Regular, monospace;
  color: var(--accent);
  font-weight: 700;
  margin-left: 0.25rem;
}

#monteCarloModal input[type="range"] {
  width: 100%;
}

.mc-status {
  margin: 0.6rem 0 0;
  font-style: italic;
  color: var(--accent);
}

.modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 1rem;
}

.mc-results {
  margin-top: 1rem;
}

.mc-chart {
  width: 100%;
  max-height: 220px;
  background: var(--surface-muted);
  border: 1px solid var(--border);
  border-radius: 6px;
}

/* SVG marker styling — extracted from inline SVG attributes into CSS
 * classes so the chart adapts to the theme. Bars keep their semantic
 * red/amber/green inline-fills (those convey meaning, not theming);
 * markers and axis labels follow the theme's text colour.
 */
.mc-marker {
  stroke: var(--text);
  stroke-width: 1.2;
  stroke-dasharray: 3 3;
}

.mc-marker-label {
  fill: var(--text);
  font-size: 10px;
  font-family: ui-monospace, SFMono-Regular, monospace;
}

.mc-axis-label {
  fill: var(--text-muted);
  font-size: 10px;
  font-family: ui-monospace, SFMono-Regular, monospace;
}

.mc-stats {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0.75rem;
  margin: 0.85rem 0 0.5rem;
}

.mc-stat-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-subtle);
  font-weight: 700;
}

.mc-stat-value {
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text);
}

.mc-stat-value.mc-stat-low  { color: var(--bad); }
.mc-stat-value.mc-stat-high { color: var(--good); }
.mc-stat-value.mc-stat-mean { color: var(--accent); }

.mc-band {
  margin: 0.25rem 0 0;
  font-size: 0.9rem;
  color: var(--text-muted);
}

.mc-verdict {
  margin-top: 0.85rem;
  padding: 0.75rem 0.9rem;
  background: rgba(249, 115, 22, 0.08);
  border-left: 3px solid var(--orange);
  border-radius: 4px;
}

.mc-verdict-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #c2410c;
  font-weight: 700;
}

.mc-verdict-text {
  margin-top: 0.25rem;
  font-size: 0.95rem;
  color: var(--text);
  line-height: 1.45;
}

/* === Model clarification modal ========================================== */

#modelClarificationModal {
  max-width: 560px;
  width: 90%;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 1.25rem 1.5rem;
  color: var(--text);
  background: var(--surface);
  margin: auto;
}

#modelClarificationModal::backdrop {
  background: var(--modal-backdrop);
}

#modelClarificationModal h3 {
  margin: 0 0 0.25rem;
  font-size: 1.1rem;
  color: var(--accent-strong);
}

#modelClarificationModal .modal-body {
  white-space: pre-wrap;
  line-height: 1.5;
  font-size: 0.95rem;
  color: var(--text);
  max-height: 60vh;
  overflow-y: auto;
  padding: 0.75rem 0.85rem;
  background: var(--surface-muted);
  border: 1px solid var(--border);
  border-radius: 4px;
}

/* === Responsive tweaks =================================================== */

@media (max-width: 600px) {
  .param-row {
    grid-template-columns: 8rem 1fr 3.5rem;
  }
  .mc-stats {
    grid-template-columns: repeat(2, 1fr);
  }
  main {
    padding: 1rem 0.85rem 3rem;
  }
}

/* === Phase 6d: API-settings + Terms modals ============================== */

/* Prompt-template selector (now a labelled row inside the hamburger menu).
   The run-mode radio group and web-search checkbox that used to sit alongside
   it in the Setup card were replaced in UI-overhaul phase U0 by the sidebar
   WEB SEARCH ON/OFF toggle chip. */
.prompt-template-select {
  margin-bottom: 0.25rem;
}

/* Shared form layout used inside the API-settings modal: stacked
   label/control pairs with comfortable spacing. */
.settings-grid {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin: 0.5rem 0 0;
}
.settings-grid label:not(.checkbox-label) {
  font-weight: 600;
  font-size: 0.9rem;
}
.settings-grid input[type="password"],
.settings-grid select {
  width: 100%;
}
.checkbox-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.25rem;
  font-weight: 500;
  cursor: pointer;
}

/* Status line tones for the "Test connection" result. Base `.status-line`
   already sets layout/colour; these override the colour by outcome. */
.status-ok {
  color: var(--good);
}
.status-bad {
  color: var(--bad);
}

#apiSettingsModal {
  max-width: 520px;
  width: 92%;
  /* The tutorial panel makes this dialog tall when expanded — without a cap it
     pushes the footer buttons off-screen on a laptop viewport. Same treatment
     as #termsModal. */
  max-height: 85vh;
  overflow-y: auto;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 1.25rem 1.5rem;
  background: var(--surface);
  color: var(--text);
  margin: auto;
}
#apiSettingsModal::backdrop {
  background: var(--modal-backdrop);
}
#apiSettingsModal h3 {
  margin: 0 0 0.25rem;
  font-size: 1.15rem;
  color: var(--accent-strong);
}

/* Terms modal: tall, scrollable body with a pinned header banner + footer.
   Flex layout is scoped to [open] so the dialog's closed (display:none)
   state from the UA stylesheet still applies. */
#termsModal {
  max-width: 760px;
  width: 92%;
  max-height: 85vh;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 1.25rem 1.5rem;
  background: var(--surface);
  color: var(--text);
  margin: auto;
}
#termsModal[open] {
  display: flex;
  flex-direction: column;
}
#termsModal::backdrop {
  background: var(--modal-backdrop);
}
#termsModal h3 {
  margin: 0 0 0.5rem;
  font-size: 1.2rem;
  font-weight: 800;
  letter-spacing: -0.4px;
}
.terms-tag {
  font-size: 0.75rem;
  color: var(--text-muted);
  font-weight: 500;
  margin-left: 0.6rem;
  padding-left: 0.6rem;
  border-left: 1px solid var(--border-strong);
}

/* Amber "out of date" notice — pinned above the scrollable body. */
.terms-stale-banner {
  background: var(--warn-soft);
  border-left: 3px solid var(--warn);
  border-radius: 5px;
  padding: 0.7rem 0.85rem;
  margin: 0 0 0.6rem;
  font-size: 0.85rem;
  line-height: 1.5;
}
.terms-stale-banner strong {
  color: var(--warn);
}

/* Blue consent banner — shown only in gate mode. */
.terms-consent-banner {
  background: var(--accent-soft);
  border-left: 3px solid var(--accent);
  border-radius: 5px;
  padding: 0.7rem 0.85rem;
  margin: 0 0 0.6rem;
  font-size: 0.85rem;
  line-height: 1.5;
}

/* The scrollable legal text. flex:1 lets it take the remaining height
   between the banners and the footer; overflow-y makes it scroll. */
.terms-body {
  flex: 1;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.75rem 1rem;
  font-size: 0.82rem;
  line-height: 1.55;
}
.terms-body h4 {
  color: var(--accent-strong);
  font-size: 0.9rem;
  margin: 1rem 0 0.35rem;
}
.terms-body h4:first-of-type {
  margin-top: 0.25rem;
}
.terms-body ul {
  padding-left: 1.3rem;
  margin: 0.35rem 0;
}
.terms-body a {
  color: var(--accent);
}
.terms-effective,
.terms-footnote {
  color: var(--text-muted);
  font-size: 0.75rem;
}
.terms-footnote {
  margin-top: 1.2rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--border);
}

/* === Per-parameter ⓘ rationale popover (slider rows) =================== */

/* The ⓘ trigger sits inline after each slider's label text. position:relative
   makes it the anchor for the absolutely-positioned popover. */
.slider-info {
  position: relative;
  display: inline-block;
  margin-left: 0.35rem;
  color: var(--accent);
  cursor: help;
  font-size: 0.85rem;
  line-height: 1;
  font-weight: 700;
}
.slider-info:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

/* The popover. Hidden until the trigger is hovered or focused. Wider than the
   label column, so it overlays the slider area (high z-index). Scrolls
   internally if a rationale + long source list overflow the max height. */
.rationale-tip {
  display: none;
  position: absolute;
  top: 1.5em;
  left: 0;
  z-index: 50;
  width: 300px;
  max-width: 78vw;
  max-height: 320px;
  overflow-y: auto;
  padding: 0.7rem 0.85rem;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
  font-size: 0.8rem;
  font-weight: 400;
  line-height: 1.5;
  text-align: left;
  white-space: normal;
  cursor: auto;
}
.slider-info:hover .rationale-tip,
.slider-info:focus .rationale-tip,
.slider-info:focus-within .rationale-tip {
  display: block;
}
.rationale-tip > strong {
  display: block;
  margin-bottom: 0.25rem;
  color: var(--accent-strong);
}
.rationale-tip .rt-info {
  color: var(--text-muted);
}
.rationale-tip .rt-model {
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--border);
}
.rationale-tip .rt-proposed {
  margin-bottom: 0.25rem;
}
.rationale-tip .rt-text {
  margin-bottom: 0.4rem;
}
.rationale-tip .src {
  font-size: 0.74rem;
  color: var(--text-muted);
}
.rationale-tip .src-heading {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 0.2rem;
}
.rationale-tip .src ul {
  margin: 0;
  padding-left: 1.1rem;
}
.rationale-tip .src li {
  margin: 0.15rem 0;
  word-break: break-word;
}
.rationale-tip .src a {
  color: var(--accent);
}

/* ========================================================================= *
 *  UI-overhaul phase U0 — app-shell layout
 *
 *  The main app moved from a single centred column of progressively-revealed
 *  cards to an app shell: a fixed top bar, a left sidebar (~380px), and a right
 *  content panel with a four-tab bar. Everything below is expressed in the
 *  existing token palette (`--surface`, `--border`, `--accent`, `--good`, …) so
 *  both the light and dark themes work without any new colour values.
 * ========================================================================= */

/* === Shell: grid ========================================================= */
/*  The shell is a vertical stack: the top bar, then a body that fills the rest
    of the viewport. The body is a two-column grid (sidebar + content), each
    column scrolling independently so the headline strip / tab bar stay put
    while long content scrolls. Below ~900px the grid collapses to a single
    column and the whole page scrolls normally (a fuller responsive pass is
    deferred to U5). */

.app-shell {
  display: flex;
  flex-direction: column;
  /* Exactly viewport height — NOT min-height. With min-height the shell
     grows past the viewport when sidebar/content get tall, the page itself
     scrolls, and the sidebar's pinned RUN PROJECT footer scrolls away with
     it. A fixed height forces all overflow into the sidebar's and content
     panel's own scroll containers (2026-08-03). */
  height: 100vh;
}

.shell-main {
  /* Override the global `main` rule (max-width 880px, centred, padded) — the
     shell body must span the full width so the sidebar/content grid fills it. */
  max-width: none;
  margin: 0;
  padding: 0;
  width: 100%;
  flex: 1;
  min-height: 0;
  display: flex;
}

/* The signed-in shell body: sidebar | content. `hidden` (display:none from the
   UA stylesheet) still wins when #appSection is hidden pre-login. */
.app-body {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 380px minmax(0, 1fr);
}

/* Flex column: the scrollable body (.sidebar-scroll) takes the free height
   and the RUN PROJECT footer (.sidebar-run, styled with the other sidebar
   pieces further down) stays pinned at the bottom, always on screen. */
.sidebar {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Grid items refuse to shrink below their content by default
     (min-height:auto); without this the tall sidebar stretches the grid row
     past the viewport and the pinned footer overflows off screen. */
  min-height: 0;
  background: var(--surface);
  border-right: 1px solid var(--border);
}

.sidebar-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 1rem 1.1rem 1.25rem;
}

.content {
  overflow-y: auto;
  min-width: 0;
  /* Same grid-item shrink allowance as .sidebar: tall tab content must
     scroll in here, not stretch the shared grid row past the viewport. */
  min-height: 0;
  padding: 1.25rem 1.5rem 3rem;
}

/* The login view fills the shell body and centres Clerk's card.
   .login-wrap is a COLUMN flex (so the admin-only notice stacks above the
   Clerk card — see the base rule), which rotates the axes here: top-pinning
   the card is justify-content, not align-items. An align-items override
   would instead LEFT-shift the card (that exact bug shipped once —
   left-justified sign-in form). Horizontal centring comes from the base
   rule's align-items: center. */
.shell-main > .login-wrap {
  flex: 1;
  justify-content: flex-start;
  /* The shell is hard-capped at 100vh now (see .app-shell); on a short
     window the Clerk card must scroll within the login view rather than
     clip. Vertical only — horizontal centring still comes from the base
     rule's align-items and is not affected. */
  overflow-y: auto;
}

/* === Shell: top bar ====================================================== */
/*  Two clusters — brand (left), API chip + hamburger (right; New Project
    lives in the hamburger menu). Fixed height, own surface, bottom border. */

.topbar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 1rem;
  height: 60px;
  padding: 0 1.25rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.topbar-brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: none;
}

.brand-wordmark {
  font-weight: 800;
  font-size: 1.56rem;
  letter-spacing: -0.01em;
  color: var(--text);
  white-space: nowrap;
}

/* The Synesis ⓘ tooltip trigger. position:relative anchors the popover. */
.brand-info {
  position: relative;
  display: inline-block;
  margin-left: 0.3rem;
  color: var(--accent);
  cursor: help;
  font-size: 0.8rem;
  font-weight: 700;
}
.brand-info:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

/* The Synesis blurb popover — two paragraphs, revealed on hover/focus. */
.brand-tip {
  display: none;
  position: absolute;
  top: 1.6em;
  left: 0;
  z-index: 300;
  width: 320px;
  max-width: 80vw;
  padding: 0.85rem 1rem;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: var(--shadow-md);
  font-size: 0.8rem;
  font-weight: 400;
  line-height: 1.55;
  text-align: left;
  white-space: normal;
  cursor: auto;
}
.brand-info:hover .brand-tip,
.brand-info:focus .brand-tip,
.brand-info:focus-within .brand-tip {
  display: block;
}
.brand-tip-p {
  display: block;
}
.brand-tip-p + .brand-tip-p {
  margin-top: 0.6rem;
}

/* Thin vertical divider between the wordmark and the product name. */
.brand-divider {
  width: 1px;
  height: 31px;
  background: var(--border-strong);
}

.brand-product {
  font-weight: 700;
  /* Match the wordmark's enlarged size (was the inherited 1rem body default). */
  font-size: 1.56rem;
  color: var(--accent);
  white-space: nowrap;
}

/* Small "BETA" chip. */
.brand-beta {
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
}

/* Right cluster: API chip + hamburger. margin-left:auto pushes it to the far
   edge now that the centre cluster is gone (New Project moved into the
   hamburger menu, 2026-08-03 styling pass). */
.topbar-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex: none;
  margin-left: auto;
}

/* API chip: a pill button with a status dot. */
.api-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.35rem 0.85rem;
  border-radius: 999px;
  background: var(--surface-muted);
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.03em;
}
.api-chip:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* Status dot: green when a key is stored, red when not (set by main.js). The
   neutral default covers the brief moment before the first paint. */
.api-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-subtle);
  flex: none;
}
.api-dot-good { background: var(--good); }
.api-dot-bad { background: var(--bad); }

/* === Shell: hamburger menu =============================================== */

.menu-wrap {
  position: relative;
}

.menu-toggle {
  background: transparent;
  border: 0;
  padding: 0.3rem 0.55rem;
  border-radius: 6px;
  font-size: 1.2rem;
  line-height: 1;
  color: var(--text-muted);
  cursor: pointer;
}
.menu-toggle:hover {
  background: var(--surface-muted);
  color: var(--text);
}

/* Dropdown panel, anchored to the toggle. `hidden` closes it (main.js). */
.app-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 200;
  min-width: 250px;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.4rem;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: var(--shadow-md);
}

/* One menu row — used for links, buttons, and the theme toggle. Applies to both
   <button> and <a> so they read as one list. */
.menu-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0;
  padding: 0.5rem 0.6rem;
  border-radius: 6px;
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
}
.menu-item:hover {
  background: var(--surface-muted);
}

/* The signed-in email chip: quieter, and allowed to wrap a long address. */
.menu-email {
  color: var(--text-muted);
  font-size: 0.82rem;
  word-break: break-all;
}

/* The prompt-template selector row inside the menu: a small label above the
   full-width select. */
.menu-row {
  padding: 0.4rem 0.6rem 0.5rem;
}
.menu-row label {
  display: block;
  margin: 0 0 0.25rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-subtle);
}

/* Theme toggle as a menu item: the icon stack sits before the "Theme" label. */
.theme-toggle-icons {
  display: inline-flex;
  align-items: center;
  color: var(--text-muted);
}

/* === Shell: sidebar ====================================================== */

/* (The "PROJECT MENU" heading and its .sidebar-title rule were dropped on
   2026-07-28 — the sidebar's contents are self-describing and the label was
   just consuming vertical space at the top of the column.) */

.sidebar-block {
  margin-bottom: 1rem;
}

/* ======================================================================== */
/* Question panel (U1) — the clarify-chat authoring surface.                */
/*                                                                          */
/* Rendered by `app/clarify-chat.js`. Two states share the bordered panel:  */
/* the chat (message log + input row) and the collapsed ACCEPTED QUESTION   */
/* card. All colours come from the existing theme tokens (no new palette),  */
/* so both light and dark themes work unchanged. The accepted brief lands   */
/* in the hidden #projectDescription field; these rules only style the UI.  */
/* ======================================================================== */

.question-panel {
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  overflow: hidden;
  margin-bottom: 1rem;
}

/* Panel header row ("Question"). */
.question-head {
  display: flex;
  align-items: center;
  padding: 0.65rem 0.8rem;
  border-bottom: 1px solid var(--border);
  background: var(--surface-muted);
}

.question-title {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text);
}

/* --- Chat state --------------------------------------------------------- */

.clarify-chat-log {
  /* min-height gives the panel presence on first load: the welcome bubble sits
     at the top with breathing room below it, instead of the panel collapsing
     to a cramped strip (user feedback, 2026-07-22). max-height still caps the
     log once a conversation grows; between the two it scrolls. */
  min-height: 200px;
  max-height: 320px;
  overflow-y: auto;
  padding: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* One message bubble. Bot/system align left; the user's own turns align
   right. Bot/user carry one square corner to match the wireframe's shape. */
.clarify-msg {
  max-width: 88%;
  padding: 0.5rem 0.7rem;
  border-radius: 10px;
  font-size: 0.9rem;
  line-height: 1.45;
  white-space: pre-wrap;
  word-break: break-word;
}

.clarify-msg-bot {
  align-self: flex-start;
  background: var(--surface-muted);
  border: 1px solid var(--border);
  color: var(--text);
  border-bottom-left-radius: 2px;
}

/* User bubble: accent fill. `color: var(--surface)` is the theme-safe inverse
   of the accent (accent-on-surface is used for links, so surface-on-accent is
   equally legible) — readable in both light and dark modes. */
.clarify-msg-user {
  align-self: flex-end;
  background: var(--accent);
  color: var(--surface);
  border-bottom-right-radius: 2px;
}

.clarify-msg-system {
  align-self: center;
  background: transparent;
  color: var(--text-muted);
  font-style: italic;
  font-size: 0.83rem;
  max-width: 100%;
  text-align: center;
}

.clarify-msg-thinking {
  align-self: flex-start;
  color: var(--text-muted);
  letter-spacing: 0.15em;
}

/* The closed brief, shown inside the bot's "ready" message as a quoted block. */
.clarify-brief-block {
  margin-top: 0.4rem;
  padding: 0.5rem 0.6rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 4px;
  font-size: 0.88rem;
  color: var(--text);
}

/* Accept button, inside the "ready" bubble (extends .btn-primary). */
.clarify-accept-btn {
  display: block;
  width: 100%;
  margin-top: 0.6rem;
}

.clarify-chat-input-row {
  display: flex;
  gap: 0.5rem;
  padding: 0.5rem;
  border-top: 1px solid var(--border);
}

.clarify-chat-input {
  flex: 1;
  resize: vertical;
  /* Roomy default: fits the wrapped placeholder without a native scrollbar and
     gives the user space to type a real description (user feedback). */
  min-height: 5.75rem;
}

/* Send stretches to match the input's height so the row reads as one control
   (align-self: stretch, not flex-end — the button previously sat short at the
   bottom of the row). */
.clarify-chat-input-row .btn-primary {
  align-self: stretch;
  white-space: nowrap;
}

/* --- Accepted state ----------------------------------------------------- */

.clarify-accepted {
  padding: 0.8rem;
}

/* The ACCEPTED QUESTION content. Deliberately *unstyled* as a card: the
   surrounding .question-panel already provides the border, radius and
   background, so giving this a second border/fill nested it inside its own
   well (user feedback, 2026-07-28). It keeps only `position: relative`, which
   anchors the ✎ edit button to the content area's top-right corner. */
.clarify-accepted-card {
  position: relative;
}

.clarify-accepted-label {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--text-subtle);
  margin-bottom: 0.5rem;
  padding-right: 1.8rem; /* clear the edit button */
}

.clarify-accepted-brief {
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--text);
  white-space: pre-wrap;
  word-break: break-word;
}

/* ✎ Edit affordance, top-right of the accepted content. Zero offsets align it
   with the content box (which is already inset by .clarify-accepted's padding)
   so it sits flush with the label's baseline row rather than floating inward. */
.clarify-edit-btn {
  position: absolute;
  top: 0;
  right: 0;
  width: 1.6rem;
  height: 1.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-size: 0.9rem;
  color: var(--text-subtle);
  background: transparent;
  border: 0;
  border-radius: 4px;
  cursor: pointer;
}

.clarify-edit-btn:hover {
  background: var(--accent-soft);
  color: var(--text);
}

/* Accordion: a bordered group with a clickable header (a real <button> for
   keyboard access) and a body toggled via the `hidden` attribute. */
.accordion {
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-bottom: 0.6rem;
  background: var(--surface);
  overflow: hidden;
}

.accordion-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  width: 100%;
  padding: 0.65rem 0.8rem;
  background: var(--surface-muted);
  border: 0;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text);
  cursor: pointer;
  text-align: left;
}

/* Chevron indicator: a rotated corner that points down when collapsed and up
   when the accordion is expanded (`aria-expanded="true"`). */
.chev {
  width: 0.5rem;
  height: 0.5rem;
  border-right: 2px solid var(--text-subtle);
  border-bottom: 2px solid var(--text-subtle);
  transform: rotate(45deg);
  transition: transform 0.15s ease;
  flex: none;
}
.accordion-head[aria-expanded="true"] .chev {
  transform: rotate(-135deg);
}

.accordion-body {
  padding: 0.75rem 0.8rem 1rem;
}

.accordion-desc {
  margin: 0 0 0.6rem;
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.45;
}

/* Stacked, full-width action buttons inside an accordion. */
.sidebar-actions {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.sidebar-actions button {
  width: 100%;
}

/* Small uppercase sub-labels inside the Information Source accordion
   ("Online Information", "Private Documents"). */
.sub-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-subtle);
  font-weight: 700;
  margin: 0.85rem 0 0.4rem;
}
.sub-label:first-child {
  margin-top: 0;
}

/* Info tooltip anchor + popover for a sub-label (same idiom as .brand-info). */
.sub-info {
  position: relative;
  display: inline-block;
  margin-left: 0.25rem;
  color: var(--accent);
  cursor: help;
  font-size: 0.75rem;
}
.sub-info:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}
.sub-tip {
  display: none;
  position: absolute;
  top: 1.5em;
  left: 0;
  z-index: 50;
  width: 260px;
  max-width: 70vw;
  padding: 0.6rem 0.75rem;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  box-shadow: var(--shadow-md);
  font-size: 0.78rem;
  font-weight: 400;
  line-height: 1.5;
  text-transform: none;
  letter-spacing: normal;
  text-align: left;
}
.sub-info:hover .sub-tip,
.sub-info:focus .sub-tip,
.sub-info:focus-within .sub-tip {
  display: block;
}

/* WEB SEARCH ON/OFF toggle chip: a labelled switch. The track + thumb move on
   the `aria-checked` state that main.js maintains. */
.toggle-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  background: transparent;
  border: 0;
  padding: 0.2rem 0;
  cursor: pointer;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text);
}
.toggle-track {
  position: relative;
  width: 34px;
  height: 18px;
  border-radius: 999px;
  background: var(--border-strong);
  transition: background-color 0.15s ease;
  flex: none;
}
.toggle-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  transition: transform 0.15s ease;
}
.toggle-chip[aria-checked="true"] .toggle-track {
  background: var(--good);
}
.toggle-chip[aria-checked="true"] .toggle-thumb {
  transform: translateX(16px);
}

/* Document caps note under the file list. */
.docs-caps {
  margin: 0.55rem 0 0;
  font-size: 0.72rem;
  color: var(--text-subtle);
  line-height: 1.4;
}

/* === Shell: headline strip =============================================== */
/*  Three large metrics (P_success / P_failure / Δ vs baseline). Metrics-only
    since the RUN PROJECT button moved to the sidebar (2026-07-22 feedback). */

.headline {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
  padding: 0.25rem 0 1rem;
}

.headline-metrics {
  display: flex;
  gap: 2.5rem;
  flex-wrap: wrap;
  align-items: flex-start;
}
.headline-metrics.calculating {
  opacity: 0.55;
}

.headline-metric {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.headline-label {
  display: flex;
  align-items: center;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--text-subtle);
}

.headline-value {
  font-size: 2.4rem;
  font-weight: 800;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  line-height: 1.1;
}

/* Semantic tones for the headline figures (styling spec 2026-08-03):
   P_success is always --good, P_failure always --bad, and Δ vs baseline is
   toned by its magnitude with a ±0.05 pp dead-band (> +0.05 pp → good,
   < −0.05 pp → bad, otherwise the neutral --text inherited from
   .headline-value above — so a displayed "0.0 pp" is never coloured).
   Applied by result-strip.js via buildMetric's `tone` argument. --accent is
   reserved for a raw-score figure should one return to the strip. */
.headline-value.tone-good   { color: var(--good); }
.headline-value.tone-bad    { color: var(--bad); }
.headline-value.tone-accent { color: var(--accent); }

/* Inline error rendered into the headline strip when a /score call fails. */
.headline-error {
  color: var(--bad);
  font-size: 0.95rem;
}

/* P_success / P_failure ⓘ tooltip (same idiom as .brand-info). */
.headline-info {
  position: relative;
  display: inline-block;
  margin-left: 0.3rem;
  color: var(--accent);
  cursor: help;
  font-size: 0.75rem;
  font-weight: 700;
}
.headline-info:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}
.headline-tip {
  display: none;
  position: absolute;
  top: 1.5em;
  left: 0;
  z-index: 300;
  width: 300px;
  max-width: 80vw;
  padding: 0.75rem 0.9rem;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  box-shadow: var(--shadow-md);
  font-size: 0.78rem;
  font-weight: 400;
  line-height: 1.55;
  text-align: left;
  white-space: normal;
  cursor: auto;
}
.headline-info:hover .headline-tip,
.headline-info:focus .headline-tip,
.headline-info:focus-within .headline-tip {
  display: block;
}

/* The primary RUN PROJECT button — the flagship action of the panel. */
.btn-run {
  background: var(--accent);
  color: #fff;
  font-weight: 800;
  letter-spacing: 0.5px;
  padding: 0.85rem 2.5rem;
  border-radius: 8px;
  font-size: 1rem;
}
.btn-run:hover:not(:disabled) {
  background: var(--accent-strong);
}

/* === Shell: tab bar + panels ============================================= */

.tab-bar {
  display: flex;
  border: 1.5px solid var(--border-strong);
  border-radius: 6px;
  overflow: hidden;
}

.tab-btn {
  flex: 1;
  text-align: center;
  padding: 0.7rem 0.5rem;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--text-muted);
  background: var(--surface-muted);
  border: none;
  border-right: 1px solid var(--border);
  border-radius: 0;
  cursor: pointer;
}
.tab-btn:last-child {
  border-right: 0;
}
.tab-btn:not(.active):hover {
  background: var(--surface);
  color: var(--text);
}
.tab-btn.active {
  background: var(--accent);
  color: #fff;
}

.tab-panels {
  margin-top: 1rem;
}

/* Pre-run empty-state line shown before a research run. The RUN PROJECT
   reference inside is bolded but not italic. */
.tab-empty {
  margin: 0.5rem 0;
  font-size: 0.9rem;
  font-style: italic;
  color: var(--text-muted);
}
.tab-empty strong {
  font-style: normal;
}

/* Scenarios / Benchmarks tab header row: a title, a RUN button, and a
   (currently disabled) + add button. */
.tab-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}
.tab-head-title {
  flex: 1;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-subtle);
}
.tab-run-btn {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 0.35rem 0.9rem;
  border-radius: 6px;
  background: var(--surface-muted);
  border: 1px solid var(--border-strong);
  color: var(--text);
  cursor: pointer;
}
.tab-run-btn:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent);
}
.tab-add-btn {
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
  padding: 0.2rem 0.65rem;
  border-radius: 6px;
  background: var(--surface-muted);
  border: 1px solid var(--border-strong);
  color: var(--text-muted);
  cursor: pointer;
}

/* === Shell: responsive =================================================== */
/*  Below ~900px the two-pane grid collapses: the sidebar stacks above the
    content and the whole page scrolls as one document (a fuller polish pass is
    deferred to U5). */

@media (max-width: 900px) {
  .shell-main {
    display: block;
    overflow-y: auto;
  }
  .app-body {
    display: block;
  }
  .sidebar {
    display: block;
    overflow: visible;
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }
  .sidebar-scroll {
    /* Single-column mode: the page scrolls as one, so the inner scroll
       container (and the RUN footer pin) stand down. */
    overflow: visible;
  }
  .content {
    overflow: visible;
  }
}

/* === Add/Edit entry modal (U3/U4) ======================================== */
/*  Shared native <dialog> for adding/editing custom scenarios + benchmarks
    (app/modals/entry-modal.js — the markup is built in JS from /meta). Sticky
    footer per the wireframe so Cancel/Save stay reachable while the 14 rows
    scroll.                                                                   */

#entryModal {
  max-width: 640px;
  width: 92%;
  max-height: 85vh;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 1.25rem 1.5rem 0;
  background: var(--surface);
  color: var(--text);
  margin: auto;
}
#entryModal::backdrop {
  background: var(--modal-backdrop);
}
#entryModal .entry-modal-title {
  margin: 0 0 0.25rem;
  font-size: 1.15rem;
  color: var(--accent-strong);
}

.entry-field-label {
  margin-top: 0.9rem;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--text-subtle);
}

.entry-input {
  width: 100%;
  margin-top: 0.35rem;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  box-sizing: border-box;
}
.entry-input:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}
.entry-textarea {
  resize: vertical;
}

.entry-param-rows {
  margin-top: 0.4rem;
}
.entry-param-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.25rem 0;
}
.entry-param-label {
  flex: 1;
  font-size: 0.88rem;
}
.entry-param-code {
  color: var(--text-subtle);
  font-size: 0.72rem;
}
.entry-num {
  width: 7.5rem;
  flex: none;
  margin-top: 0;
  font-family: ui-monospace, SFMono-Regular, monospace;
}

/* Sticky footer: stays pinned while the param rows scroll. */
.entry-modal-footer {
  position: sticky;
  bottom: 0;
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: 0.9rem 0;
  margin-top: 1rem;
}

/* === API-key tutorial panel ==============================================
   In-modal "Learn how to obtain your API key here" disclosure. Replaces the
   former one-line .api-signup-hint pointer, which this panel supersedes.

   The <summary> is deliberately styled as a text link rather than a button:
   it is help, offered quietly, and should not compete with Save / Test
   connection for attention. Markup in index.html, behaviour in
   app/modals/api-key-help.js. */

.api-key-help {
  margin: 0.15rem 0 0.4rem;
  font-size: 0.78rem;
}

/* Kill the native disclosure triangle in both engines so the custom ▸ marker
   below is the only one drawn. `display: block` is required for the Blink
   default (`list-item`) to stop rendering its own marker. */
.api-key-help > summary {
  display: block;
  list-style: none;
  cursor: pointer;
  width: fit-content;
  color: var(--text-subtle);
  text-decoration: underline;
}
.api-key-help > summary::-webkit-details-marker {
  display: none;
}
.api-key-help > summary:hover {
  color: var(--accent);
}
.api-key-help > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Custom marker, rotated when the panel is open. */
.api-key-help > summary::before {
  content: '▸';
  display: inline-block;
  margin-right: 0.35rem;
  text-decoration: none;
  transition: transform 0.15s ease;
}
.api-key-help[open] > summary::before {
  transform: rotate(90deg);
}

/* Video slot. `aspect-ratio` reserves the full 16:9 box up front so swapping
   the poster for the player causes no layout shift (and no jump in the
   modal's scroll position). */
.video-mount {
  position: relative;
  aspect-ratio: 16 / 9;
  margin: 0.5rem 0 0.4rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  background: var(--surface-muted);
}
.video-mount > iframe,
.video-mount > .video-facade {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Click-to-load poster. A real <button> so it is focusable and keyboard-
   operable; the image is decorative (alt="") because the button carries the
   accessible name. */
.video-facade {
  display: block;
  padding: 0;
  cursor: pointer;
  background: none;
}
.video-facade img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Play glyph, centred over the poster. */
.video-facade .video-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3.25rem;
  height: 3.25rem;
  padding-left: 0.2rem; /* optically centre the triangle in the circle */
  border-radius: 50%;
  background: rgba(15, 23, 42, 0.72);
  color: #fff;
  font-size: 1.35rem;
  transition: background 0.15s ease, transform 0.15s ease;
}
.video-facade:hover .video-play {
  background: var(--accent);
  transform: translate(-50%, -50%) scale(1.06);
}
.video-facade:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* Console + YouTube links under the video. */
.api-key-help-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin: 0 0 0.2rem;
  color: var(--text-subtle);
}
.api-key-help-links a {
  color: var(--text-subtle);
  text-decoration: underline;
}
.api-key-help-links a:hover {
  color: var(--accent);
}

/* ======================================================================== */
/* Define Project modal + sidebar RUN (2026-07-22 feedback)                 */
/*                                                                          */
/* The clarification chat and the project-name input live in the            */
/* #projectDefineModal (auto-opened on first load / New Project); the       */
/* sidebar Question panel shows either the pre-accept placeholder or the    */
/* ACCEPTED QUESTION card. RUN PROJECT moved from the headline strip into   */
/* the sidebar, above the (pre-run greyed) Generate Reports accordion.      */
/* ======================================================================== */

/* Both dialogs are themed explicitly (like every other dialog in this file):
   a <dialog> with no background/colour of its own falls back to the UA's
   white card, which reads as broken in dark mode. */
#projectDefineModal,
#confirmReacceptModal {
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  padding: 1.25rem 1.5rem;
  background: var(--surface);
  color: var(--text);
  margin: auto;
}
#projectDefineModal::backdrop,
#confirmReacceptModal::backdrop {
  background: var(--modal-backdrop);
}
#projectDefineModal h3,
#confirmReacceptModal h3 {
  margin: 0 0 0.25rem;
  font-size: 1.15rem;
  color: var(--accent-strong);
}

/* The define modal is wider than the settings dialogs so the chat has room.
   Scaled up 25% on 2026-07-28 (was 680px) — this is the app's main authoring
   surface and the chat wants the space. The 92vw cap keeps it from overflowing
   narrow viewports. */
#projectDefineModal {
  width: min(850px, 92vw);
}

/* ---- Shared corner ✕ dismiss button ------------------------------------ */
/*
   Every dialog dismisses from a ✕ in its top-right corner rather than a
   "Close"/"Cancel" button in the footer (user feedback, 2026-07-28). Footer
   buttons that only dismissed were removed; footers now carry actions only.
   The one exception is #confirmReacceptModal, a destructive confirmation
   where an explicitly labelled Cancel beside "Clear and continue" is worth
   keeping — there the ✕ is a second route to the same cancel.

   No `position: relative` on the dialogs: the UA stylesheet already gives
   `dialog:modal` `position: fixed` (and a plain open dialog `position:
   absolute`), either of which makes it the containing block for this
   absolutely positioned child. Overriding to `relative` would defeat the
   `inset: 0` + `margin: auto` centering these dialogs rely on.

   Positioned inside each dialog's existing padding, so it costs no extra
   height or padding — it just occupies corner whitespace that was already
   there. Visual weight matches the ✎ affordance on the sidebar's accepted
   card.
*/
.dialog-close-btn {
  position: absolute;
  top: 0.55rem;
  right: 0.6rem;
  width: 1.9rem;
  height: 1.9rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-size: 0.95rem;
  line-height: 1;
  color: var(--text-subtle);
  background: transparent;
  border: 0;
  border-radius: 4px;
  cursor: pointer;
}
.dialog-close-btn:hover {
  color: var(--accent);
  background: var(--accent-soft);
}

/* Focus handling. `app/modals/dialog-focus.js` is what actually keeps the ✕
   from being the target when a dialog opens; these two rules are the backstop,
   so a stray programmatic focus can never draw a ring around the close button
   (user feedback, 2026-07-28 — "should not be highlighted, ever"). Keyboard
   users still get a clear indicator: :focus-visible only matches genuine
   keyboard navigation. */
.dialog-close-btn:focus {
  outline: none;
}
.dialog-close-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The dialog shell receives initial focus (see dialog-focus.js) and is never
   tab-reachable, so it should never render a ring of its own. */
dialog:focus {
  outline: none;
}

/* Reserve the corner so a long title can never run under the ✕. Applies to
   every dialog carrying one. */
#projectDefineModal h3,
#monteCarloModal h3,
#modelClarificationModal h3,
#confirmReacceptModal h3,
#apiSettingsModal h3,
#termsModal h3,
.entry-modal .entry-modal-title {
  padding-right: 2rem;
}
#confirmReacceptModal {
  width: min(460px, 92vw);
}

/* Name field: label on its own line above the input (feedback 2026-07-22).
   The name is mandatory — the chat can't be used until it's filled. */
.define-name-row {
  margin: 1rem 0;
}
.define-name-row label {
  display: block;
  margin-bottom: 0.35rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
}
.define-name-row input {
  width: 100%;
}
/* Required marker + the hint shown while the field is empty. */
.define-name-req {
  color: var(--bad);
  margin-left: 0.15rem;
}
.define-name-hint {
  margin: 0.35rem 0 0;
  font-size: 0.75rem;
  color: var(--text-subtle);
}
.define-name-hint.is-required {
  color: var(--bad);
}

/* The chat inside the modal: give the log real presence (the modal IS the
   authoring surface now) while keeping it scrollable on short viewports. */
.define-chat {
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--surface);
}
/* Scaled up 25% alongside the modal width (was 260px / 45vh, 2026-07-28) so the
   dialog grows in both dimensions rather than just getting wider. The vh cap
   still bounds it on short viewports; past that the log scrolls as before. */
.define-chat .clarify-chat-log {
  min-height: 325px;
  max-height: 56vh;
}

/* Sidebar pre-accept placeholder: hint + full-width "Define project". */
.question-placeholder {
  padding: 0.85rem;
}
.question-placeholder-hint {
  margin: 0 0 0.75rem;
  font-size: 0.83rem;
  line-height: 1.5;
  color: var(--text-muted);
}
.question-placeholder .btn-primary {
  display: block;
  width: 100%;
}

/* Project name on the ACCEPTED QUESTION card (above the brief). */
.clarify-accepted-name {
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 0.35rem;
  padding-right: 1.8rem; /* clear the ✎ button */
  overflow-wrap: anywhere;
}

/* Sidebar RUN PROJECT block: pinned footer of the sidebar flex column
   (2026-08-03) — the flagship action goes full-width and never scrolls
   off screen. */
.sidebar-run {
  flex: none;
  margin: 0;
  padding: 0.85rem 1.1rem 1rem;
  border-top: 1px solid var(--border);
  background: var(--surface);
}
.sidebar-run .btn-run {
  display: block;
  width: 100%;
}
.sidebar-run .error {
  margin: 0.5rem 0 0;
}

/* In-flight state (run-indicator.js): the label carries the verb spinner.
   Full opacity despite `disabled` — this is "working", not the greyed-out
   gated state — with a progress cursor signalling the interaction block. */
.btn-run.running:disabled {
  opacity: 1;
  cursor: progress;
}

/* Global input lock while a run is in flight (run-indicator.js puts
   `run-locked` on <body> and makes the input regions inert). The sidebar
   body dims to signal its controls are locked; the tab panels stay at full
   opacity so in-flight results remain readable — their controls are dead
   via `inert`, not visually greyed. */
body.run-locked .sidebar-scroll {
  opacity: 0.55;
}

/* A disabled accordion head (Generate Reports pre-run): visibly muted and
   inert — sidebar.js's toggle never fires on a disabled button. */
.accordion-head:disabled {
  color: var(--text-subtle);
  cursor: not-allowed;
}
.accordion-head:disabled .chev {
  opacity: 0.4;
}

/* ===========================================================================
   Report preview overlay (app/report.js)
   ===========================================================================

   The generated report used to open as a same-origin `blob:` tab, which gave
   anything inside it read access to the BYOK key in localStorage
   (F-D3-REPORT-XSS). It now renders into a sandboxed iframe inside this
   near-fullscreen dialog: scripting off, opaque origin, no way back into the
   app. The dialog is built in JS on demand, so these rules are keyed off the
   `.report-preview` class rather than an id in index.html.
*/
.report-preview {
  width: min(1180px, 96vw);
  height: 92vh;
  max-width: none;
  max-height: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Toolbar: the controls that used to be inline `onclick` buttons inside the
   report document itself. They live out here, in the trusted parent, so the
   report can be rendered with scripting disabled entirely. */
.report-preview-bar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  padding: 0.65rem 3rem 0.65rem 1rem; /* right padding clears the ✕ */
  border-bottom: 1px solid var(--border);
  background: var(--surface-2, var(--surface));
}
.report-preview-title {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin-right: 0.3rem;
}
.report-preview-hint {
  font-size: 0.75rem;
  color: var(--text-subtle);
}
/* The shared corner-✕ rules position against the dialog box; nudge it to sit
   centred in the toolbar strip rather than over the report content. */
.report-preview-close {
  top: 0.5rem;
  right: 0.5rem;
}

/* The isolated frame fills whatever the toolbar leaves. White background
   because the report is a print artefact with hardcoded light colours and is
   deliberately not themed. */
.report-preview-frame {
  flex: 1 1 auto;
  width: 100%;
  border: 0;
  background: #fff;
}

/* ----- privacy controls (Terms footer + Clerk account modal) -------------- */
/* Rendered by `app/privacy.js` into both surfaces, so these rules have to hold
   inside our own Terms dialog AND inside Clerk's account modal, whose own
   styles we don't control. Everything is scoped under .privacy-controls and
   uses the app palette rather than inherited colours, so the panel reads the
   same in either host and in both themes. */
.privacy-controls {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  color: var(--text);
}

/* Page variant (`asPage`) — the account-modal surface. Sections supply their
   own spacing here, so the column gap that separates the inline control from
   its status line would only double up. */
.privacy-page {
  gap: 0;
}

/* Matched declaration-for-declaration against the computed style of Clerk's own
   page header (Profile / Security), so the Privacy tab's title is the same type
   rather than an approximation of it.

   The `--clerk-*` custom properties are read rather than their values inlined:
   the panel is a plain DOM node inside Clerk's shell, not an iframe, so those
   properties inherit down to us and the title tracks Clerk's scale if it ever
   shifts. The fallbacks are what Clerk itself falls back to, which is also what
   the dev-bypass dialog gets — there is no Clerk shell there to inherit from,
   and 0.8125rem × 17/13 resolves to the same 1.0625rem either way.

   `font-family: inherit` for the same reason: inheriting means this is literally
   Clerk's face, not a near-match. `color` resolves to rgb(231, 236, 243) in
   dark, exactly Clerk's own — `clerkAppearance()` feeds it our `--text`, so the
   mapping round-trips. */
.privacy-page-title {
  box-sizing: border-box;
  margin: 0 0 var(--clerk-spacing, 1rem);
  padding: 0;
  font-family: inherit;
  font-size: var(--clerk-font-size-lg, calc(var(--clerk-font-size, 0.8125rem) * 17 / 13));
  font-weight: var(--clerk-font-weight-bold, 700);
  line-height: 1.41176;
  letter-spacing: normal;
  color: var(--text);
}

/* Clerk's header divider: a hairline carrying the same spacing below it as the
   title carries above it. */
.privacy-page-divider {
  height: 1px;
  margin: 0 0 var(--clerk-spacing, 1rem);
  border: 0;
  background: var(--border);
}

.privacy-section-title {
  margin: 0 0 0.85rem;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--text);
}

/* The shared row shape for both surfaces: copy on the left, control hard right,
   vertically centred against however many lines the copy runs to. */
.privacy-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

/* `min-width: 0` is what actually lets the copy wrap: without it a flex item
   refuses to shrink below its longest unbreakable run, so a long subtitle would
   push the control off the right edge instead of running to a second line. */
.privacy-item-text {
  flex: 1 1 auto;
  min-width: 0;
}

/* Never shrink the control — the copy beside it absorbs the width instead. */
.privacy-item-control {
  flex: 0 0 auto;
}

.privacy-toggle-row {
  cursor: pointer;
}

/* The checkbox is repainted as a sliding switch: `appearance: none` strips the
   native tick box, the element itself becomes the track, and ::after is the
   knob. Purely presentational — it stays a real <input type="checkbox">, so
   the label wrapper, keyboard toggling and the `checked` reads in privacy.js
   all keep working untouched. Colours come from the palette vars so the switch
   inverts correctly in dark mode and inside Clerk's modal. */
.privacy-toggle-row input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  position: relative;
  width: 2.75rem;
  height: 1.5rem;
  margin: 0;
  border: 0;
  border-radius: 999px;
  background: var(--border-strong);
  transition: background-color 0.15s;
  cursor: pointer;
}

.privacy-toggle-row input[type="checkbox"]::after {
  content: '';
  position: absolute;
  top: 0.1875rem;
  left: 0.1875rem;
  width: 1.125rem;
  height: 1.125rem;
  border-radius: 50%;
  background: var(--surface);
  transition: transform 0.15s;
}

.privacy-toggle-row input[type="checkbox"]:checked {
  background: var(--accent);
}

/* Track 44px − knob 18px − 3px inset either side = 20px of travel. */
.privacy-toggle-row input[type="checkbox"]:checked::after {
  transform: translateX(1.25rem);
}

.privacy-toggle-row input[type="checkbox"]:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* `appearance: none` also drops the native focus ring, so put one back. */
.privacy-toggle-row input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Three weights, descending: section headings are the heaviest (700, set
   below), an item's own title is bold but lighter (600), and its subtitle is
   plain (400) and grey. That ordering is what keeps a settings page scannable —
   the eye finds the sections first, then the items, then the explanations. */
.privacy-item-text strong,
.privacy-item-text h3 {
  display: block;
  margin: 0;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text);
}

/* `--text-subtle`, not `--text-muted`: muted is only a shade off body text and
   barely reads as secondary against Clerk's dark card, which is what put the
   item title and its explanation on the same visual level. Subtle is the
   palette's actual grey and it flips per theme, so this stays legible in both
   rather than being one fixed grey that only suits one of them. */
.privacy-toggle-sub,
.privacy-item-text p {
  display: block;
  margin: 0.15rem 0 0;
  color: var(--text-subtle);
  font-size: 0.85rem;
  font-weight: 400;
}

/* Reserves its line height while empty so showing a status message doesn't
   shift the controls underneath it. */
.privacy-status {
  color: var(--text-muted);
  font-size: 0.8rem;
  margin: 0;
  min-height: 1.2em;
}

/* No rule between the sections: once each carries its own heading, a divider
   as well is redundant — the headings do the separating. The only divider on
   the page is the one under the "Privacy" title, matching Clerk's own pages. */
.privacy-section + .privacy-section {
  margin-top: 2rem;
}

/* Right-aligned and wrapping upward, because this sits in the item's control
   slot in place of the button it replaces. */
.privacy-confirm {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* The only destructive action in the app, so the variant lives here with its
   one caller rather than in the shared button block above. */
.btn-danger {
  background: var(--bad);
  color: #fff;
}

.btn-danger:hover:not(:disabled) {
  filter: brightness(0.92);
}

/* Terms footer: the consent row spanning the footer's width, with Accept on a
   second row beneath it. Overrides the shared .modal-footer's single
   right-aligned row — the toggle is part of the consent act, so it reads as
   something to settle immediately before pressing Accept rather than as a
   control competing with it across the footer. In read-only mode `terms.js`
   hides both and the footer collapses entirely; the ✕ is the only affordance
   there. */
.terms-footer {
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  gap: 0.85rem;
}

/* Accept keeps the footer's usual right-hand position, directly under the
   switch. */
.terms-footer > button {
  align-self: flex-end;
}

/* Full width here, unlike the account-modal surface: the row's whole point is
   that the copy sits at the left edge and the switch at the right, above the
   button. `flex: 0 0 auto` because the footer is a column now, where a grow
   factor would stretch this block vertically instead of horizontally. */
.terms-privacy {
  flex: 0 0 auto;
  max-width: none;
}

/* Copy left, switch right is now the shared `.privacy-item` shape, so there is
   nothing to override here — both surfaces want the same arrangement.

   In the Terms footer the consent copy is set in the Accept button's own type
   — same family and size — so the two sides of the footer read as one row. The
   only difference between the two lines is weight: the label carries the
   button's 600, the subtitle drops to normal. This is deliberately scoped to
   the Terms surface; the same control inside Clerk's account modal keeps its
   quieter, smaller subtitle, where it sits in a settings list rather than
   beside a button. */
.terms-privacy .privacy-toggle-row strong,
.terms-privacy .privacy-toggle-sub {
  font-family: inherit;
  font-size: 0.85rem;
}

.terms-privacy .privacy-toggle-row strong {
  font-weight: bold;
}

.terms-privacy .privacy-toggle-sub {
  font-weight: 400;
  color: grey;
}

/* The reserved status line exists to stop messages shoving the Clear History
   block around; in the footer there is no such block, so the reserved height —
   plus the column gap above it — only opens a dead band between the toggle and
   the Accept button below. Collapse both while it is empty and give the
   message its own spacing only when there is one to show. */
.terms-privacy .privacy-controls {
  gap: 0;
}

.terms-privacy .privacy-status {
  min-height: 0;
}

.terms-privacy .privacy-status:not(:empty) {
  margin-top: 0.35rem;
}
