/* src/dashboard/public/dashboard.css — shared design system.
 *
 * THE TOKEN LAYER IS AUTHORITATIVE. Every color, size, weight, space and radius
 * in the dashboard comes from a custom property defined in :root below. Do not
 * introduce a raw hex literal in a page's <style> block or an inline style=
 * attribute — that is how this file drifted into 72 hardcoded colors and 19 font
 * sizes (see docs/design/ux-review-2026-08-06.md). If a value is missing, add a
 * token here rather than a literal there.
 *
 * Contrast: every text token below is verified against its intended surface at
 * WCAG AA or better. --text-4 is the faintest permitted and carries a 12px floor.
 * Nothing renders below --fs-micro (11px).
 *
 * Served from GET /dashboard.css (no secrets, no auth dependency — it is just CSS).
 */

:root {
  /* ---------- Surfaces (each step perceptibly above the last) ---------- */
  --bg: #0c0e12;              /* page */
  --surface-1: #14171e;       /* panels, nav, inputs on page */
  --surface-2: #161b24;       /* cards */
  --surface-3: #1c2130;       /* popovers, hovered rows, raised chips */
  --surface-sunken: #0a0c10;  /* code blocks, wells */

  /* ---------- Text ramp (ratio vs --bg / --surface-1) ---------- */
  --text-0: #f2f4f8;   /* 17.5 / 16.3 — headings */
  --text-1: #e2e6ee;   /* 15.4 / 14.3 — body */
  --text-2: #c3cad8;   /* 11.7 / 10.9 — secondary */
  --text-3: #a4adbf;   /*  8.6 /  8.0 — metadata, muted labels */
  --text-4: #8f99ac;   /*  6.7 /  6.3 — faintest permitted; 12px floor */

  /* ---------- Borders ---------- */
  --border-subtle: #232833;  /* row dividers */
  --border: #333a49;         /* cards, panels */
  --border-strong: #606980;  /* inputs & interactive edges — 3.52:1, meets 1.4.11 */

  /* ---------- Accent ---------- */
  --accent: #4f46e5;         /* fills; #fff on it = 6.29:1 */
  --accent-hover: #4338ca;   /* 7.90:1 */
  --accent-text: #a5b4fc;    /* accent as text on dark = 9.69:1 */
  --accent-ring: rgba(99, 102, 241, 0.45);
  --on-accent: #ffffff;

  /* ---------- Semantic status: text-on-dark / fill ---------- */
  --ok-text: #4ade80;       --ok-fill: #14331f;      /* 11.08:1 on bg */
  --warn-text: #fbbf24;     --warn-fill: #3a2f10;    /* 11.57:1 */
  --danger-text: #f87171;   --danger-fill: #3a1a1a;  /*  6.98:1 */
  --info-text: #60a5fa;     --info-fill: #17324f;    /*  7.60:1 */
  --neutral-text: #94a3b8;  --neutral-fill: #242935; /*  7.53:1 */

  /* Semantic borders — a tinted edge needs more value than a fill but less than
   * the text, so status panels read as status without shouting. */
  --ok-border: #2f6b46;
  --warn-border: #7a6520;
  --danger-border: #7f3a3a;
  --info-border: #3a5580;

  /* Chart series — separated in BOTH hue and value so they survive at 11px and
   * under color-vision deficiency (the old pair was two adjacent blues). */
  --chart-in: #38bdf8;
  --chart-out: #c4b5fd;

  --btn-approve-bg: #15803d;  /* #fff = 5.02:1 */
  --btn-danger-bg: #b91c1c;

  /* ---------- Disabled — never opacity on a dark theme ---------- */
  --disabled-bg: #1b1f2b;
  --disabled-border: #333a49;
  --disabled-text: #7f8798;

  /* ---------- Type: 7 steps replacing 19. Nothing below 11px. ---------- */
  --fs-micro: 11px;  --lh-micro: 1.45;  /* badges, chips — needs --track-caps if uppercase */
  --fs-xs: 12px;     --lh-xs: 1.45;     /* metadata, timestamps, captions */
  --fs-sm: 13px;     --lh-sm: 1.45;     /* dense table/list rows */
  --fs-base: 14px;   --lh-base: 1.55;   /* body, controls */
  --fs-lg: 16px;     --lh-lg: 1.4;      /* card titles */
  --fs-xl: 20px;     --lh-xl: 1.3;      /* section titles */
  --fs-2xl: 26px;    --lh-2xl: 1.25;    /* page titles, stat values */

  --fw-normal: 400; --fw-medium: 500; --fw-semibold: 600;  /* 650 deleted — not a real system-ui weight */
  --track-caps: 0.06em;

  --font-sans: system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ---------- Spacing (4px scale) & radius ---------- */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px;
  --r-sm: 4px;    /* controls, chips, inputs */
  --r-md: 8px;    /* cards, panels, popovers */
  --r-full: 999px;
}

/* ---------- Layout container ---------- */
/* One container for every page. The nav's inner row uses this too, so the
 * masthead and the content it heads share a left edge. */
.page { max-width: 1280px; margin: 0 auto; padding: 0 var(--sp-5); }

/* ---------- Section titles ---------- */
.sec-title {
  font-size: var(--fs-xl); line-height: var(--lh-xl); font-weight: var(--fw-semibold);
  color: var(--text-0); margin: 0 0 var(--sp-2); padding-bottom: var(--sp-1);
  border-bottom: 1px solid var(--border-subtle);
}
.sec-title .sec-sub {
  color: var(--text-3); font-size: var(--fs-xs); font-weight: var(--fw-normal);
  margin-left: var(--sp-2);
}

/* ---------- Canonical card + in-card titles ---------- */
.card {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: var(--sp-4);
}
.card h3 {
  font-size: var(--fs-lg); line-height: var(--lh-lg); font-weight: var(--fw-semibold);
  color: var(--text-0); margin: 0 0 var(--sp-3); padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--border-subtle);
}
/* Project panel titles sit at the same rank as section titles — a card title
 * must never outweigh the section that contains it. */
.card h3.proj-title { font-size: var(--fs-xl); line-height: var(--lh-xl); }
.proj-no { color: var(--text-3); font-weight: var(--fw-medium); margin-right: 0.15em; }
.sub-title {
  font-size: var(--fs-micro); font-weight: var(--fw-semibold); text-transform: uppercase;
  letter-spacing: var(--track-caps); color: var(--text-3); margin: var(--sp-3) 0 var(--sp-1);
}

/* ---------- Unified button system (.btn) ---------- */
/* One system for all pages: solid (primary action), approve (solid green),
 * reject (ghost red), ghost. Sizes: default md (min 32px tall), .btn-sm. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1);
  background: var(--surface-3); color: var(--text-1); border: 1px solid var(--border);
  border-radius: var(--r-sm); cursor: pointer;
  font-family: inherit; font-size: var(--fs-sm); font-weight: var(--fw-medium);
  padding: var(--sp-2) var(--sp-4);
  min-height: 32px; text-decoration: none; white-space: nowrap;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.btn:hover { background: var(--border); }
.btn:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
/* Disabled must stay READABLE while looking unavailable. Opacity on a dark theme
 * drove the primary CTA to 2.54:1 — never reintroduce it here. */
.btn:disabled {
  background: var(--disabled-bg); border-color: var(--disabled-border);
  color: var(--disabled-text); cursor: not-allowed;
}
.btn-sm { min-height: 26px; padding: var(--sp-1) var(--sp-3); font-size: var(--fs-xs); }
.btn-solid { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn-solid:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn-solid:disabled { background: #2c2f57; border-color: #3a3e6b; color: #9ba2c4; }
.btn-approve { background: var(--btn-approve-bg); border-color: var(--btn-approve-bg); color: var(--on-accent); }
.btn-approve:hover { background: #166534; border-color: #166534; }
.btn-reject { background: transparent; border-color: #5c2a2a; color: var(--danger-text); }
.btn-reject:hover { background: var(--danger-fill); border-color: var(--danger-text); }
.btn-ghost { background: transparent; }

/* ---------- Unified chip system (.chip + tones) ---------- */
/* Light semantics: yellow = needs a human ONLY; blue = links/info ONLY.
 * This is the ONLY definition of .chip — page-level overrides are forbidden
 * (work.html once redefined it at 3.78:1 and silently lost 1.4 points). */
.chip {
  display: inline-block; border-radius: var(--r-md); padding: 1px var(--sp-2);
  font-size: var(--fs-micro); background: var(--neutral-fill); color: var(--text-3);
  white-space: nowrap;
}
.chip-warn { background: var(--warn-fill); color: var(--warn-text); }
.chip-info { background: var(--info-fill); color: var(--info-text); }
.chip-ok { background: var(--ok-fill); color: var(--ok-text); }
.chip-bad { background: var(--danger-fill); color: var(--danger-text); }
.chip-na { background: var(--neutral-fill); color: var(--text-4); }

/* Numeric/tabular data lines up. */
.meta { font-variant-numeric: tabular-nums; }

/* ---------- Status badges (shared: role badges, state badges) ---------- */
/* Uppercase at small sizes REQUIRES tracking, and a fixed min-width so the
 * column after the badge has a straight left edge. */
.state-badge {
  display: inline-block; border-radius: var(--r-sm); padding: 1px var(--sp-2);
  font-size: var(--fs-micro); font-weight: var(--fw-semibold);
  letter-spacing: var(--track-caps); text-transform: uppercase;
  background: var(--neutral-fill); color: var(--text-3); white-space: nowrap;
}

/* ---------- Work lights (shared: Overview board + /work ticket list) ---------- */
/* One idiom, one place — the per-ticket live status dot. Every state also
 * carries a non-color cue (shape or glyph) so it survives color-blindness. */
.wglight {
  display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  margin-right: 6px; vertical-align: middle; flex: 0 0 auto;
}
.l-grey { background: var(--neutral-text); }
.l-working { background: var(--info-text); box-shadow: 0 0 7px var(--info-text); }
.l-waiting { background: var(--warn-text); box-shadow: 0 0 7px var(--warn-text); }
.l-green { background: var(--ok-text); box-shadow: 0 0 7px var(--ok-text); }
.l-red { background: var(--danger-text); box-shadow: 0 0 7px var(--danger-text); }
.l-decision { background: var(--accent-text); box-shadow: 0 0 7px var(--accent); }
.l-blocked { background: transparent; border: 2px solid var(--danger-text); }

/* Outcome chips/panels — the verdict + diff-proof signal (L2 observability).
   Grounded in the host-computed diff proof, never the agent's self-report. */
.wi-outcome {
  font-size: var(--fs-micro); padding: 1px var(--sp-1); border-radius: var(--r-md);
  margin-left: var(--sp-1); white-space: nowrap;
}
.wi-outcome.o-fixed { background: var(--ok-fill); color: var(--ok-text); }
.wi-outcome.o-blocked { background: var(--danger-fill); color: var(--danger-text); }
.wi-outcome.o-partial { background: var(--warn-fill); color: var(--warn-text); }
.wi-outcome.o-planning_only { background: var(--neutral-fill); color: var(--neutral-text); }
.wi-role.blocked { color: var(--danger-text); }
.wi-role.plan { color: var(--neutral-text); }
.wi-role.partial { color: var(--warn-text); }
.pop-outcome { margin: var(--sp-1) 0; font-size: var(--fs-xs); }
.pop-outcome.o-blocked { color: var(--danger-text); }
.pop-outcome.o-fixed { color: var(--ok-text); }
.pop-outcome.o-partial { color: var(--warn-text); }
.pop-outcome.o-planning_only { color: var(--neutral-text); }
.wi-outcome-panel {
  margin: var(--sp-1) 0 var(--sp-2); padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm); border-left: 3px solid var(--neutral-text);
  background: rgba(148, 163, 184, 0.08);
}
.wi-outcome-panel.o-fixed { border-left-color: var(--ok-text); }
.wi-outcome-panel.o-blocked { border-left-color: var(--danger-text); background: rgba(248, 113, 113, 0.08); }
.wi-outcome-panel.o-partial { border-left-color: var(--warn-text); }

/* Run-detail panel — ended-cascade review on /work (verdict + steps + actions). */
.rd-outcome {
  font-size: var(--fs-sm); padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm); margin-bottom: var(--sp-1);
}
.rd-outcome.o-blocked { background: var(--danger-fill); color: var(--danger-text); }
.rd-outcome.o-fixed { background: var(--ok-fill); color: var(--ok-text); }
.rd-outcome.o-partial { background: var(--warn-fill); color: var(--warn-text); }
.rd-outcome.o-planning_only { background: var(--neutral-fill); color: var(--neutral-text); }
.rd-actions {
  font-size: var(--fs-xs); color: var(--text-2); margin: var(--sp-1) 0 var(--sp-2);
  display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap;
}
.rd-actions span { flex: 1; min-width: 0; }
.rd-step {
  font-size: var(--fs-xs); padding: var(--sp-1) 0; color: var(--text-3);
  line-height: var(--lh-xs); word-break: break-word;
}
.rd-step .rd-mark { display: inline-block; width: 1.2em; }
.rd-step.s-blocked, .rd-step.s-failed { color: var(--danger-text); }
.rd-step.s-passed { color: var(--accent-text); }
@media (prefers-reduced-motion: no-preference) {
  .l-working { animation: wg-pulse 1.6s ease-in-out infinite; }
  @keyframes wg-pulse {
    0%, 100% { box-shadow: 0 0 3px var(--info-text); }
    50% { box-shadow: 0 0 10px var(--info-text); }
  }
}

/* ---------- Cascade stage lights (the MRT line) ---------- */
/* One station per playbook step, joined by track. This is the product's
 * namesake surface — "boxes light up as the cascade advances across roles".
 * Every state carries a SHAPE as well as a colour (filled / ringed / hollow /
 * crossed), so the line survives colour-vision deficiency and greyscale.
 * Markup: stageLineHtml() in ob-shared.js — one renderer for the Overview row
 * and the workspace panel. */
.mrt-line { display: inline-flex; align-items: center; vertical-align: middle; }
.mrt-line.mrt-labelled { align-items: flex-start; }

.mrt-stop { display: inline-flex; flex-direction: column; align-items: center; gap: 2px; flex: 0 0 auto; }
.mrt-dot {
  width: 10px; height: 10px; border-radius: 50%;
  border: 2px solid var(--border-strong); background: transparent;
  box-sizing: border-box;
}
.mrt-name {
  font-size: var(--fs-micro); line-height: 1.2; color: var(--text-4);
  letter-spacing: 0.02em; white-space: nowrap;
}
/* Track between stations: lit only when the PRECEDING station completed, so an
 * unreached tail reads as unlit line rather than as a gap. */
.mrt-track { width: 14px; height: 2px; background: var(--border-subtle); flex: 0 0 auto; }
.mrt-line.mrt-labelled .mrt-track { margin-top: 4px; }
.mrt-track.lit { background: var(--ok-text); }

.mrt-passed  .mrt-dot { background: var(--ok-text);     border-color: var(--ok-text); }
.mrt-passed  .mrt-name { color: var(--ok-text); }
.mrt-running .mrt-dot { background: var(--info-text);   border-color: var(--info-text);
                        box-shadow: 0 0 0 3px var(--info-fill); }
.mrt-running .mrt-name { color: var(--info-text); }
.mrt-failed  .mrt-dot { background: var(--danger-text); border-color: var(--danger-text); }
.mrt-failed  .mrt-name { color: var(--danger-text); }
/* Blocked is a RING, never a filled dot — it must not read as "done". */
.mrt-blocked .mrt-dot { background: transparent;        border-color: var(--danger-text); border-width: 3px; }
.mrt-blocked .mrt-name { color: var(--danger-text); }
.mrt-skipped .mrt-dot { background: var(--border-strong); border-color: var(--border-strong); }
.mrt-pending .mrt-dot { background: transparent;        border-color: var(--border-subtle); }

@media (prefers-reduced-motion: no-preference) {
  .mrt-running .mrt-dot { animation: mrt-pulse 1.6s ease-in-out infinite; }
  @keyframes mrt-pulse {
    0%, 100% { box-shadow: 0 0 0 2px var(--info-fill); }
    50%      { box-shadow: 0 0 0 5px var(--info-fill); }
  }
}

/* Caption naming the current station, plus the live tool summary. */
.mrt-cap { font-size: var(--fs-micro); color: var(--text-3); white-space: nowrap; }
.mrt-cap .cur { color: var(--text-2); font-weight: var(--fw-medium); }

/* ---------- Live activity (in-flight rows only) ---------- */
/* Fed by the `runevent` WebSocket stream the dashboard already broadcasts to
 * every client — no polling, and no cache to go stale. */
.live-tools {
  font-size: var(--fs-micro); color: var(--text-3);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.live-tools .lt-err { color: var(--danger-text); }
.live-say {
  display: block; margin-left: 18px; margin-top: 2px;
  font-size: var(--fs-xs); color: var(--text-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  border-left: 2px solid var(--info-border); padding-left: var(--sp-2);
}
.live-say:empty { display: none; }
.live-stale { color: var(--text-4); }

/* ---------- Workspace panel ---------- */
/* The read-only view of a work item's cascade worktree: what ran, what it was
 * asked to do, what changed, and the files themselves. Sections are separated
 * by a rule and a label rather than by cards — this is a dense reference
 * surface inside a side panel, not a dashboard. */
.ws-sec { margin: var(--sp-4) 0 var(--sp-2); }
.ws-h {
  font-size: var(--fs-micro); font-weight: var(--fw-semibold); text-transform: uppercase;
  letter-spacing: var(--track-caps); color: var(--text-3);
  padding-bottom: var(--sp-1); margin-bottom: var(--sp-2);
  border-bottom: 1px solid var(--border-subtle);
}
.ws-sub { font-size: var(--fs-xs); color: var(--text-3); margin-top: var(--sp-1); }
.ws-path { font-family: var(--font-mono); font-size: var(--fs-micro); color: var(--text-4); margin-bottom: var(--sp-2); word-break: break-all; }
.ws-task { font-size: var(--fs-sm); color: var(--text-1); background: var(--surface-sunken); border-left: 3px solid var(--border-strong); padding: var(--sp-2) var(--sp-3); border-radius: 0 var(--r-sm) var(--r-sm) 0; }
.ws-paths { font-family: var(--font-mono); font-size: var(--fs-micro); color: var(--text-2); margin-top: var(--sp-2); max-height: 180px; overflow: auto; }

.ws-det { margin: var(--sp-2) 0; }
.ws-det > summary { font-size: var(--fs-xs); color: var(--text-3); cursor: pointer; padding: var(--sp-1) 0; }
.ws-det > summary:hover { color: var(--text-1); }
.ws-det > summary:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
.ws-pre {
  background: var(--surface-sunken); border: 1px solid var(--border-subtle); border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3); font-family: var(--font-mono); font-size: var(--fs-micro);
  line-height: 1.5; color: var(--text-2); max-height: 320px; overflow: auto;
  white-space: pre-wrap; word-break: break-word;
}

/* File tree — one level at a time; a worktree holds whole repos. */
.ws-tree { font-size: var(--fs-xs); }
.ws-row { display: flex; align-items: baseline; gap: var(--sp-2); padding: 1px 0; min-width: 0; }
.ws-tw, .ws-tw-sp {
  flex: 0 0 auto; width: 1.2em; text-align: center;
  background: none; border: 0; padding: 0; color: var(--text-3);
  font: inherit; cursor: pointer;
}
.ws-tw:hover { color: var(--text-1); }
.ws-tw:focus-visible, .ws-link:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
.ws-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ws-dir { color: var(--text-1); font-weight: var(--fw-medium); }
.ws-file { color: var(--text-2); font-family: var(--font-mono); font-size: var(--fs-micro); }
.ws-link { background: none; border: 0; padding: 0; cursor: pointer; color: var(--accent-text); text-align: left; }
.ws-link:hover { text-decoration: underline; }
.ws-meta { margin-left: auto; flex: 0 0 auto; color: var(--text-4); font-size: var(--fs-micro); font-variant-numeric: tabular-nums; }
.ws-kids { margin-left: var(--sp-4); border-left: 1px solid var(--border-subtle); padding-left: var(--sp-2); }

#ws-file-view { border-top: 1px solid var(--border); padding: var(--sp-3); background: var(--surface-1); }
.ws-file-head { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-2); font-size: var(--fs-xs); }
.ws-file-head b { font-family: var(--font-mono); color: var(--text-1); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.ws-file-head .btn { margin-left: auto; }

/* ---------- Connection / staleness bar ---------- */
/* A live monitoring board MUST be able to say it is not live. Shown whenever the
 * dashboard WebSocket is not open; while it is up, controls that send over the
 * socket are disabled (see setConnState() in the page scripts). */
.conn-bar {
  display: none; align-items: center; gap: var(--sp-2);
  margin: 0 0 var(--sp-2); padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--warn-text); background: var(--warn-fill);
  border-radius: var(--r-sm); color: var(--warn-text);
  font-size: var(--fs-sm); line-height: var(--lh-sm);
}
.conn-bar.show { display: flex; }
.conn-bar .conn-detail { color: var(--text-2); font-variant-numeric: tabular-nums; }
.conn-bar .btn { margin-left: auto; }
/* The board is dimmed — but never hidden — while the data is known-stale. */
.is-stale { opacity: 0.72; }

/* Freshness stamp for polled regions ("Updated 12s ago"). */
.fresh { font-size: var(--fs-xs); color: var(--text-4); font-variant-numeric: tabular-nums; }
.fresh.stale { color: var(--warn-text); }

/* ---------- Empty states ---------- */
/* Produced by emptyHtml() in ob-shared.js — the only sanctioned way to render an
 * empty region (so every empty surface is guiding, never just "No X yet."). */
.ob-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-1);
  padding: var(--sp-5) var(--sp-4);
  text-align: center;
  color: var(--text-3);
}
.ob-empty .ob-ico {
  font-size: 22px;
  line-height: 1;
  filter: grayscale(0.2);
}
.ob-empty .ob-h {
  color: var(--text-2);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  margin: 0;
}
.ob-empty .ob-p {
  font-size: var(--fs-xs);
  line-height: var(--lh-xs);
  max-width: 42ch;
  margin: 0;
}
.ob-empty .ob-actions {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
  justify-content: center;
  margin-top: var(--sp-2);
}
/* Degradation levels: when a dependency is missing (e.g. Plane not connected),
 * surface it as a warning rather than hiding the empty state. */
.ob-empty.ob-warn .ob-ico { filter: none; }
.ob-empty.ob-warn .ob-h { color: var(--warn-text); }
.ob-empty.ob-block .ob-h { color: var(--danger-text); }
/* Inline variant: nested inside a card rather than filling a whole panel. */
.ob-empty.ob-inline { padding: var(--sp-3) var(--sp-2); }
.ob-empty.ob-inline .ob-ico { font-size: var(--fs-lg); }
.ob-empty.ob-inline .ob-h { font-size: var(--fs-xs); }
.ob-empty.ob-inline .ob-p { font-size: var(--fs-xs); }
/* Quiet variant: for regions that are EXPECTED to be empty in the steady state
 * (the decision queue, agent rules). A single line — an empty queue is good news
 * and must not outweigh the live board. */
.ob-empty.ob-quiet { padding: var(--sp-2) var(--sp-2); flex-direction: row; gap: var(--sp-2); }
.ob-empty.ob-quiet .ob-ico { font-size: var(--fs-sm); }
.ob-empty.ob-quiet .ob-h { font-size: var(--fs-xs); font-weight: var(--fw-medium); }
.ob-empty.ob-quiet .ob-p, .ob-empty.ob-quiet .ob-actions { display: none; }

/* ---------- Buttons (onboarding surfaces) ---------- */
/* .ob-btn is an alias of .btn — kept so existing markup keeps working, but it no
 * longer carries an independent size/radius system. */
.ob-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1);
  background: var(--surface-3);
  color: var(--text-1);
  border: 1px solid var(--border);
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--r-sm);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  min-height: 26px;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
  white-space: nowrap;
  text-decoration: none;
}
.ob-btn:hover { background: var(--border); }
.ob-btn:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
.ob-btn:disabled {
  background: var(--disabled-bg); border-color: var(--disabled-border);
  color: var(--disabled-text); cursor: not-allowed;
}
.ob-btn.ob-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.ob-btn.ob-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.ob-btn.ob-ghost { background: transparent; }
.ob-btn.ob-ok { border-color: var(--ok-text); color: var(--ok-text); }
.ob-btn.ob-warn { border-color: var(--warn-text); color: var(--warn-text); }
.ob-btn.ob-sm { padding: var(--sp-1) var(--sp-2); font-size: var(--fs-micro); min-height: 24px; }

/* ---------- Onboarding banner (3-state) ---------- */
.ob-banner {
  margin: 0 0 var(--sp-3);
  border: 1px solid var(--border);
  background: linear-gradient(180deg, #1a1f33 0%, var(--surface-1) 100%);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
}
.ob-banner .ob-ico { font-size: var(--fs-lg); flex-shrink: 0; margin-top: 1px; }
.ob-banner .ob-body { flex: 1; min-width: 0; }
.ob-banner .ob-h {
  font-size: var(--fs-base); font-weight: var(--fw-semibold);
  color: var(--text-1); margin: 0 0 var(--sp-1);
}
.ob-banner .ob-p {
  font-size: var(--fs-sm); color: var(--text-3); margin: 0; line-height: var(--lh-sm);
}
.ob-banner .ob-p a { color: var(--accent-text); text-decoration: none; }
.ob-banner .ob-p a:hover { text-decoration: underline; }
.ob-banner .ob-actions { display: flex; gap: var(--sp-2); margin-top: var(--sp-2); flex-wrap: wrap; }
.ob-banner.ob-compact { padding: var(--sp-2) var(--sp-3); align-items: center; }
.ob-banner.ob-compact .ob-p { margin: 0; }
.ob-banner.ob-compact .ob-actions { margin-top: 0; margin-left: auto; }
.ob-banner.ob-hidden { display: none; }
.ob-banner.ob-done { border-color: var(--ok-fill); background: linear-gradient(180deg, #16261c 0%, var(--surface-1) 100%); }
.ob-banner.ob-done .ob-ico { color: var(--ok-text); }

/* ---------- Setup deck sidebar ---------- */
.ob-deck {
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  margin-bottom: var(--sp-3);
  overflow: hidden;
}
.ob-deck-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  cursor: pointer;
  user-select: none;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border-subtle);
}
.ob-deck-head .ob-h { font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--text-2); }
.ob-deck-head .ob-chev { margin-left: auto; color: var(--text-4); font-size: var(--fs-micro); transition: transform 0.15s; }
.ob-deck.collapsed .ob-chev { transform: rotate(-90deg); }
.ob-deck.collapsed .ob-deck-body { display: none; }
.ob-deck-progress { height: 4px; background: var(--surface-sunken); overflow: hidden; }
.ob-deck-progress > span { display: block; height: 100%; background: var(--accent); transition: width 0.3s; }
.ob-deck-body { padding: var(--sp-1) 0; }
.ob-step {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border-subtle);
}
.ob-step:last-child { border-bottom: none; }
.ob-step .ob-light {
  width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0;
  background: var(--neutral-text);
}
.ob-step.s-verified .ob-light, .ob-step.s-complete .ob-light { background: var(--ok-text); box-shadow: 0 0 6px var(--ok-text); }
.ob-step.s-skipped .ob-light { background: var(--border); }
.ob-step.s-unknown .ob-light { background: var(--neutral-text); }
.ob-step .ob-label { flex: 1; min-width: 0; }
.ob-step .ob-label .ob-name { font-size: var(--fs-sm); color: var(--text-1); }
.ob-step .ob-label .ob-meta { font-size: var(--fs-xs); color: var(--text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ob-step .ob-act { flex-shrink: 0; }

/* ---------- Toast (verify result feedback) ---------- */
.ob-toast {
  position: fixed;
  bottom: var(--sp-3);
  right: var(--sp-3);
  z-index: 9999;
  background: var(--surface-3);
  border: 1px solid var(--accent);
  color: var(--text-1);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  line-height: var(--lh-sm);
  max-width: 360px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s, transform 0.2s;
}
.ob-toast.ob-show { opacity: 1; transform: translateY(0); }
.ob-toast.ob-ok { border-color: var(--ok-text); }
.ob-toast.ob-err { border-color: var(--danger-text); }

/* ---------- Shared form controls ---------- */
/* An input's fill is only 1.02:1 from the page, so the BORDER is the only thing
 * marking the field. It must clear 3:1 (WCAG 1.4.11) — hence --border-strong. */
.field, input[type="text"], input[type="email"], input[type="password"],
input[type="number"], input[type="search"], select, textarea {
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  color: var(--text-1);
  font-family: inherit;
  font-size: var(--fs-sm);
  padding: var(--sp-1) var(--sp-2);
}
input::placeholder, textarea::placeholder { color: var(--text-4); }
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--accent-text); outline-offset: 1px; border-color: var(--accent);
}

@media (prefers-reduced-motion: reduce) {
  .ob-banner, .ob-btn, .btn, .ob-deck-head .ob-chev, .ob-deck-progress > span, .ob-toast { transition: none; }
}
