/* ============================================================
   TOKENS — every colour used anywhere below is defined here,
   once, for light and dark. Nothing outside this block may use
   a raw hex value.

   The polish pass (2026-09-24, visual-review.md §2): one cool
   neutral ramp (no beige), one accent — teal — reserved for things
   you can click (primary button, link, focus ring, active nav, the
   brand mark), and one colour per state shared by every surface:
   amber needs a person, blue working, green done, red failed, grey
   idle. The --color-attention/-positive/-negative names are the
   same colours as --state-needs/-done/-failing, kept as names so
   no rule below had to change. Coloured text is only ever a state
   word or a callout's title — a paragraph is always --color-text.
   Every text/background pair is AA (4.5:1) in both themes; the
   table is in docs/visual-theme-contrast.md (written in the theme's
   last task).
   ============================================================ */
:root {
  color-scheme: light dark;

  /* surfaces */
  --color-bg: #f5f6f8;
  --color-surface: #ffffff;
  --color-surface-sunken: #eff1f4;
  --color-sidebar: #f5f6f8;

  /* borders: dividers and panel edges; things you operate */
  --color-border: #dfe2e8;
  --color-border-strong: #c9cdd6;
  /* the edge of a control you operate (inputs, selects, secondary buttons):
     the lightest value at 3:1 on every surface a control sits on (WCAG
     1.4.11) — surface, bg, sunken and each state's soft fill */
  --color-border-control: #858b99;
  --color-border-control-hover: #656b7a;

  /* text */
  --color-text: #0e1015;
  --color-text-secondary: #4a5160;
  --color-text-tertiary: #5f6675;
  --color-on-accent: #ffffff;

  /* accent — teal, interaction only */
  --color-accent: #2f4bf5;
  --color-accent-hover: #2338b8;
  --color-accent-soft: #eef1fe;

  /* state — one colour each, with a soft twin for a tinted surface */
  --state-needs: #a14f05;
  --state-needs-soft: #fdf3e4;
  --state-working: #1d5fc4;
  --state-working-soft: #e8f0fc;
  --state-done: #17753c;
  --state-done-soft: #e8f5ec;
  --state-failing: #b42318;
  --state-failing-soft: #fdeceb;
  --state-idle: #5f6675;
  --state-idle-soft: #eff1f4;

  /* Area hues (visual theme §1): where am I. Only on sidebar icons, icon
     chips, a card's accent details and the map's selected path — never on
     buttons, chips, badges, prose, links or anything carrying state. No
     area-hued text on a -soft fill (people-on-soft is 4.47:1). */
  --area-runs: #4f46e5;          --area-runs-soft: #eef2ff;
  --area-agents: #7c3aed;        --area-agents-soft: #f5f1ff;
  --area-reviews: #0f766e;       --area-reviews-soft: #e6f3f0;
  /* lime, 58deg from --state-needs's amber (design-fixes:38/39) */
  --area-commits: #4d7c0f;       --area-commits-soft: #f0f7e4;
  --area-integrations: #be185d;  --area-integrations-soft: #fdeef5;
  --area-models: #0369a1;        --area-models-soft: #e9f4fb;
  --area-people: #15803d;        --area-people-soft: #e8f5ec;
  --area-settings: #5a6070;      --area-settings-soft: #e2e5eb;
  /* Person hues (§2, plan R7): an avatar's fill under white initials,
     picked by hashing the account id; the same in both themes. */
  /* person-4 (plum) and person-7 (olive) moved out of the needs/failing
     hue families (design-fixes:38/39) */
  --person-1: #0e7490; --person-2: #7c3aed; --person-3: #be185d; --person-4: #86198f;
  --person-5: #15803d; --person-6: #4338ca; --person-7: #3f6212; --person-8: #475569;
  --on-person: #ffffff;
  /* Severity (§3, plan R8): the findings bar, Critical to Minor. Moderate
     was #eab308 (R8): 1.92:1 on the surface, under spec §6's 3:1 floor for
     a graphic (TR-7, the whole-branch review) — darkened here. Retuned
     again (design-fixes:69, reviews:140/152) into a monotonic ramp: each
     step's contrast on the surface is strictly below the more severe
     step's, so Moderate is never darker than Major. */
  --sev-critical: var(--state-failing);
  --sev-major: #c2410c;
  --sev-moderate: #b7791f;
  /* its own grey, not --state-idle (design-fixes:69, reviews:140/152) */
  --sev-minor: #8a90a0;
  /* the older names, as the same colours */
  --color-neutral-bg: #eff1f4;
  --color-neutral-fg: #4a5160;
  --color-neutral-border: #dfe2e8;

  --color-attention-bg: var(--state-needs-soft);
  --color-attention-fg: var(--state-needs);
  --color-attention-border: #f1cf9a;

  --color-positive-bg: var(--state-done-soft);
  --color-positive-fg: var(--state-done);
  --color-positive-border: #b3dcc0;
  --color-positive-solid: #17753c;
  --color-on-positive: #ffffff;

  --color-negative-bg: var(--state-failing-soft);
  --color-negative-fg: var(--state-failing);
  --color-negative-border: #f4c1bc;

  /* diff */
  --color-diff-add-bg: #e8f5ec;
  --color-diff-add-fg: #17753c;
  --color-diff-del-bg: #fdeceb;
  --color-diff-del-fg: #b42318;

  /* elevation: panels are flat — the border does the work */
  --shadow-panel: none;
  --shadow-control: 0 1px 0 rgba(14, 16, 21, 0.04);
  --shadow-pop: 0 4px 16px rgba(14, 16, 21, 0.08), 0 1px 2px rgba(14, 16, 21, 0.06);
  /* the chat popup and its launcher: a card that floats over the page */
  --shadow-float: 0 12px 32px rgba(14, 16, 21, 0.14), 0 2px 6px rgba(14, 16, 21, 0.08);

  /* type — the website's three faces (2026-10-06, portal-site-theme):
     IBM Plex Sans for everything, Space Grotesk for page titles, section
     headings and the brand, IBM Plex Mono for code and small labels; all
     self-hosted below, each with a real system fallback */
  --font-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-display: "Space Grotesk", "IBM Plex Sans", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  /* the PRD page's headings and lead (PRD V1 §8, the canvas): a system
     serif, nothing to download */
  --font-serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;

  /* short aliases (the 2026-09-15 design doc's names) */
  --surface: var(--color-surface);
  --ink: var(--color-text);
  --muted: var(--color-text-secondary);
  --line: var(--color-border);

  /* type scale (base 14px): size / line-height */
  --text-2xs: 11px;   /* 16 — the sidebar group label only */
  --text-xs: 12px;    /* 16 — table heads, badges, hints, crumbs */
  --text-sm: 13px;    /* 20 — secondary text, labels, buttons */
  --text-md: 14px;    /* 20 (body 22) — body, cells, inputs */
  --text-lg: 16px;    /* 24 — panel title, markdown h2 */
  --text-xl: 20px;    /* 28 — section heading */
  --text-2xl: 24px;   /* 32 — page title */
  --text-prose: 15px; /* 26 — long-form reading */

  /* spacing — a 4px scale */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;

  --control-height: 36px;
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 14px;
  --radius-xl: 20px;    /* the chat popup only */
  --radius-pill: 999px; /* buttons, chips, the chat launcher and composer */
  --sidebar-width: 224px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #0e1015;
    --color-surface: #171a21;
    --color-surface-sunken: #1d212a;
    --color-sidebar: #0e1015;

    --color-border: #2a2f3a;
    --color-border-strong: #3a3f4b;
    --color-border-control: #6b7182;
    --color-border-control-hover: #8a90a0;

    --color-text: #e7e9ee;
    --color-text-secondary: #a3a9b6;
    --color-text-tertiary: #8d94a3;
    --color-on-accent: #0e1015;

    --color-accent: #8fa2ff;
    --color-accent-hover: #b3c0ff;
    --color-accent-soft: #1c2440;

    --state-needs: #f2b04e;
    --state-needs-soft: #2e2310;
    --state-working: #8db8f5;
    --state-working-soft: #16263b;
    --state-done: #7ccf98;
    --state-done-soft: #14301e;
    --state-failing: #f39189;
    --state-failing-soft: #3a1c1a;
    --state-idle: #98a0ae;
    --state-idle-soft: #1f232c;

    --area-runs: #8d8bf5;          --area-runs-soft: #20233f;
    --area-agents: #a78bfa;        --area-agents-soft: #2a2145;
    --area-reviews: #3dbfa9;       --area-reviews-soft: #0f2b27;
    --area-commits: #a3c75a;       --area-commits-soft: #1e2a12;
    --area-integrations: #ef6aa5;  --area-integrations-soft: #3a1527;
    --area-models: #67b3e0;        --area-models-soft: #12283a;
    --area-people: #6fbe8c;        --area-people-soft: #14301e;
    --area-settings: #98a0ae;      --area-settings-soft: #262a34;
    --sev-critical: var(--state-failing);
    /* retuned into the monotonic ramp (design-fixes:69, reviews:140/152):
       each step reads with less contrast on the surface than the one
       before it, so Moderate never darker (more visible) than Major. */
    --sev-major: #e8843a;
    --sev-moderate: #a88a2a;
    /* its own grey, not --state-idle (design-fixes:69, reviews:140/152) */
    --sev-minor: #6b7182;

    --color-neutral-bg: #1d212a;
    --color-neutral-fg: #c9cdd6;
    --color-neutral-border: #2a2f3a;

    --color-attention-border: #5a4418;
    --color-positive-border: #22492f;
    --color-positive-solid: #1f7a44;
    --color-on-positive: #ffffff;
    --color-negative-border: #5e2629;

    --color-diff-add-bg: #14301e;
    --color-diff-add-fg: #7ccf98;
    --color-diff-del-bg: #3a1c1a;
    --color-diff-del-fg: #f39189;

    --shadow-panel: none;
    --shadow-control: 0 1px 0 rgba(0, 0, 0, 0.3);
    --shadow-pop: 0 8px 24px rgba(0, 0, 0, 0.5);
    --shadow-float: 0 16px 40px rgba(0, 0, 0, 0.6), 0 2px 8px rgba(0, 0, 0, 0.4);
  }

  /* a hair-thin edge so an avatar's hue doesn't melt into a dark card
     (shell:55) — a rule, not a token, since it is dark-only. */
  .avatar { box-shadow: 0 0 0 1px var(--color-border-strong); }
}

/* ============================================================
   TYPE — self-hosted, no CDN, no external fetch at runtime. Every woff2
   below lives in assets/fonts/ (embedded, served hashed like portal.css
   itself); each family's licence sits beside it (OFL-ibm-plex-sans.txt,
   OFL-space-grotesk.txt, OFL-ibm-plex-mono.txt, all SIL OFL 1.1). The hashed filename
   in each url() is computed offline the same way assets.go computes it at
   startup (sha256 of the file's bytes, first 10 hex chars); a case in
   assets_test.go checks every url() still names the embedded file's hash.
   Shipped weights: Plex Sans 400/500/600, Space Grotesk 600, Plex Mono
   400/500 — no rule below asks for any other.
   ============================================================ */
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/ibm-plex-sans-400.3b646991d3.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/ibm-plex-sans-500.0717336fb3.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/ibm-plex-sans-600.8960851d69.woff2") format("woff2");
}
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/space-grotesk-600.685bbbf69f.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/ibm-plex-mono-400.08949f728d.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/ibm-plex-mono-500.01d2854474.woff2") format("woff2");
}

/* ============================================================
   RESET + BASE
   ============================================================ */
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font: 400 var(--text-md)/22px var(--font-sans);
  font-feature-settings: "cv11" 0, "ss01" 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  /* A long unbroken token — a URL, a git ref, a file path — must never be
     the thing that pushes the page body wider than the viewport. Anything
     that wants its own horizontal scroll (tables, the diff, code blocks)
     opts into it explicitly via its own overflow-x: auto. */
  overflow-wrap: break-word;
}
a { color: var(--color-accent); text-decoration: none; }
a:hover { text-decoration: underline; }
code, pre, kbd, samp { font-family: var(--font-mono); font-size: 0.9em; }
pre code { font-size: 1em; }
h1, h2, h3, h4, h5 { margin: 0; font-weight: 600; line-height: 1.3; letter-spacing: -0.005em; }
h1 { font-family: var(--font-display); font-size: var(--text-2xl); line-height: 32px; letter-spacing: -0.025em; overflow-wrap: anywhere; }
h2 { font-size: var(--text-lg); line-height: 24px; }
h3, h4, h5 { font-size: var(--text-md); line-height: 20px; }
p { margin: 0 0 var(--space-3); }
strong, b { font-weight: 600; }
p:last-child { margin-bottom: 0; }

.num, .date, time { font-variant-numeric: tabular-nums; }

/* ============================================================
   SHELL — persistent sidebar + main column
   ============================================================ */
.shell {
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  min-height: 100vh;
}
.sidebar {
  min-width: 0;
  background: var(--color-sidebar);
  border-right: 1px solid var(--color-border);
  padding: var(--space-4) var(--space-3);
  padding-left: calc(var(--space-3) + env(safe-area-inset-left, 0px));
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}
.brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 32px;
  padding: var(--space-1) var(--space-2);
  color: var(--color-text);
}
.brand:hover { text-decoration: none; }
.brand svg { flex: none; }
/* The website's mark (2026-10-06): an ink tile with three bars, the middle
   one in the accent. */
.brand-mark-bg { fill: var(--color-text); }
.brand-mark { fill: var(--color-bg); }
.brand-mark-accent { fill: var(--color-accent); }
.brand-name { font-family: var(--font-display); font-weight: 600; font-size: 18px; letter-spacing: -0.02em; }
/* J2.7: on a page taller than the viewport the sidebar keeps its account
   block in view: sticky with its own scroll. Desktop only — the ≤860px rule
   below makes the sidebar a sticky top bar of its own. */
@media (min-width: 861px) {
  .sidebar { position: sticky; top: 0; height: 100vh; overflow-y: auto; }
}
.nav-group { display: flex; flex-direction: column; gap: 2px; }

/* Small keys and labels: sentence case, small and quiet. */
.meta-label,
dl.kv dt {
  font-size: var(--text-xs);
  line-height: 16px;
  font-weight: 500;
  color: var(--color-text-tertiary);
}
.nav-link {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 30px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  font-size: var(--text-md);
  line-height: 20px;
  font-weight: 500;
}
.nav-icon { flex: none; width: 16px; height: 16px; color: var(--color-text-tertiary); }
/* Sidebar icons in their area hue (visual theme §1, plan R10) — an item
   with no area (account, sign out) keeps the plain tertiary color above. */
.nav-icon.area-runs { color: var(--area-runs); }
.nav-icon.area-agents { color: var(--area-agents); }
.nav-icon.area-reviews { color: var(--area-reviews); }
.nav-icon.area-commits { color: var(--area-commits); }
.nav-icon.area-integrations { color: var(--area-integrations); }
.nav-icon.area-models { color: var(--area-models); }
.nav-icon.area-people { color: var(--area-people); }
.nav-icon.area-settings { color: var(--area-settings); }
.nav-link:hover { background: color-mix(in srgb, var(--color-text) 5%, transparent); text-decoration: none; color: var(--color-text); }
/* TR-12 (whole-branch review): scoped like the active rule below, or this
   (two classes plus :hover, specificity 0,3,0) outranks .nav-icon.area-*
   (0,2,0) and turns any hovered area icon the plain secondary grey. */
.nav-link:hover .nav-icon:not([class*="area-"]) { color: var(--color-text-secondary); }
/* The section a page belongs to is set server-side (<body data-nav="...">):
   a neutral fill and a 2px teal tick, not a teal block. */
body[data-nav="home"] .nav-home,
body[data-nav="agents"] .nav-agents,
body[data-nav="reviews"] .nav-reviews,
body[data-nav="account"] .nav-account-link,
body[data-nav="prds"] .nav-prds,
body[data-nav="integrations"] .nav-settings,
body[data-nav="models"] .nav-settings,
body[data-nav="people"] .nav-settings,
body[data-nav="settings"] .nav-settings {
  background: color-mix(in srgb, var(--color-text) 7%, transparent);
  color: var(--color-text);
}
/* An active item's icon keeps its area hue (plan R10) — only an icon with
   no area (account, sign out) turns accent teal when active. */
body[data-nav="home"] .nav-home .nav-icon:not([class*="area-"]),
body[data-nav="agents"] .nav-agents .nav-icon:not([class*="area-"]),
body[data-nav="reviews"] .nav-reviews .nav-icon:not([class*="area-"]),
body[data-nav="account"] .nav-account-link .nav-icon:not([class*="area-"]),
body[data-nav="prds"] .nav-prds .nav-icon:not([class*="area-"]),
body[data-nav="integrations"] .nav-settings .nav-icon:not([class*="area-"]),
body[data-nav="models"] .nav-settings .nav-icon:not([class*="area-"]),
body[data-nav="people"] .nav-settings .nav-icon:not([class*="area-"]),
body[data-nav="settings"] .nav-settings .nav-icon:not([class*="area-"]) { color: var(--color-accent); }
body[data-nav="home"] .nav-home::before,
body[data-nav="agents"] .nav-agents::before,
body[data-nav="reviews"] .nav-reviews::before,
body[data-nav="account"] .nav-account-link::before,
body[data-nav="prds"] .nav-prds::before,
body[data-nav="integrations"] .nav-settings::before,
body[data-nav="models"] .nav-settings::before,
body[data-nav="people"] .nav-settings::before,
body[data-nav="settings"] .nav-settings::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  bottom: 8px;
  width: 2px;
  border-radius: 1px;
  background: var(--color-accent);
}
.nav-account { margin-top: auto; border-top: 1px solid var(--color-border); padding-top: var(--space-3); }
.nav-person { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-2) var(--space-2); min-width: 0; }
/* The sidebar's own avatar size (the shared .avatar rule, Task 3, is 24px). */
.nav-person .avatar { width: 28px; height: 28px; font-size: var(--text-2xs); }
.nav-person-text { display: flex; flex-direction: column; min-width: 0; line-height: 16px; }
.nav-account-name { font-size: var(--text-sm); font-weight: 500; color: var(--color-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-org-name { font-size: var(--text-xs); color: var(--color-text-tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-account .nav-link { font-size: var(--text-sm); min-height: 28px; }
.nav-signout { width: 100%; text-align: left; border: none; background: none; cursor: pointer; font: inherit; font-size: var(--text-sm); font-weight: 500; }
.narrow-form, form.stack { max-width: 40rem; }
.inline-form { display: flex; gap: var(--space-3); align-items: flex-start; flex-wrap: wrap; }
.inline-form .field { margin-bottom: 0; flex: 1; min-width: 12rem; }
/* A field with a hint below it must not push its neighbour button down:
   the row aligns to flex-start and the button takes the label's height
   (20px line + 4px gap) as its top margin, lining up with the input. */
.inline-form .btn { margin-top: 24px; }
/* Three short fields on one row (a model's caps). */
.field-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
.field-row .field { margin-bottom: 0; }
/* Two paired fields on one row (a connection's header name and value). */
.field-row-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* The New agent page's three type cards. */
.type-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--space-4); }
.type-card { display: block; text-decoration: none; color: inherit; transition: border-color 0.15s ease; }
.type-card:hover, .type-card:focus-visible { border-color: var(--color-border-strong, var(--color-text-tertiary)); }
dl.kv { display: grid; grid-template-columns: max-content 1fr; gap: var(--space-2) var(--space-5); margin: 0 0 var(--space-3); font-size: var(--text-md); }
dl.kv dt { align-self: baseline; font-size: var(--text-sm); color: var(--color-text-secondary); font-weight: 400; }
dl.kv dd { margin: 0; }
.kv dd code { overflow-wrap: anywhere; }
.token-reveal { border-color: var(--color-accent); }
/* J1.6: a freshly minted token and its connect command must never wrap
   mid-token — pre-wrap (the general output-block default) breaks a
   47-character token across lines. */
.token-reveal pre, .token-reveal .output-block { white-space: pre; overflow-x: auto; }
/* The People page nests each person's tokens in their row. */
table.data table.data { margin: 0; font-size: var(--text-sm); }
table.data table.data td, table.data table.data th { padding: 0.3rem 0.5rem; }
/* Room for the floating chat launcher at every width (design-fixes:23) — it
   is 44px tall wherever it sits (bottom-right on a page, bottom-right as
   a 44px circle on a phone), not only below 767px. */
.main { min-width: 0; padding-bottom: calc(44px + 2 * var(--space-4) + env(safe-area-inset-bottom, 0px)); padding-right: env(safe-area-inset-right, 0px); }
/* Two widths: 72rem for tables and dashboards; .content-narrow (48rem) for
   settings, forms and documents (layout.html sets it from the page's
   Narrow flag). */
.content {
  max-width: 72rem;
  margin: 0 auto;
  padding: var(--space-8) var(--space-10) var(--space-12);
}
.content.content-narrow { max-width: 48rem; }
/* The run and review pages carry a map and a rail side by side: they take
   the wider frame. */
.content:has(.run-top) { max-width: 90rem; }

/* ============================================================
   PAGE HEADER — breadcrumbs, title, primary action
   ============================================================ */
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--space-6);
  margin-bottom: var(--space-8);
  padding-bottom: var(--space-6);
  border-bottom: 1px solid var(--color-border);
}
.page-header-main { min-width: 0; }
/* Crumbs, table heads, badges and chips set in Plex Mono, like the
   website's labels. */
.crumbs {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 16px;
  color: var(--color-text-tertiary);
  margin-bottom: var(--space-2);
}
.crumbs a { color: var(--color-text-tertiary); }
.crumbs a:hover { color: var(--color-accent); text-decoration: none; }
.crumbs [aria-current="page"] { color: var(--color-text-secondary); }
.crumb-sep { margin: 0 var(--space-2); color: var(--color-border-strong); }
.page-title-row { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2) var(--space-3); }
.page-header h1 { font-family: var(--font-display); font-size: 30px; line-height: 36px; font-weight: 600; letter-spacing: -0.03em; }
.header-actions { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; flex: none; }
.header-actions form { display: inline-flex; margin: 0; }
.page-subtitle { font-size: var(--text-md); line-height: 22px; color: var(--color-text-secondary); margin: var(--space-1) 0 0; max-width: 65ch; }
.page-tags { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-top: var(--space-2); }
/* A status line under the header — never a row of buttons. */
.page-header-meta { color: var(--color-text-secondary); font-size: var(--text-md); margin: 0 0 var(--space-6); }

/* ============================================================
   PANEL — the bordered container everything lives in. A panel's
   title lives in its header; .section-label groups several panels
   and is a real 16px heading, not an uppercase eyebrow.
   ============================================================ */
.panel {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  margin-bottom: var(--space-6);
}
.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  min-height: 48px;
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--color-border);
}
.panel-header h2, .panel-header h3 { font-size: var(--text-md); line-height: 20px; font-weight: 600; letter-spacing: 0; }
.panel-header .header-actions { gap: var(--space-2); }
.panel-title { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2) var(--space-3); min-width: 0; }
.panel-subtitle { display: block; margin-top: 2px; font-size: var(--text-sm); line-height: 20px; font-weight: 400; color: var(--color-text-tertiary); }
.panel-body { padding: var(--space-5); }
.panel-body > *:last-child { margin-bottom: 0; }
/* Sub-sections inside one panel: a rule between, an h3 at 14/600. */
.panel-body + .panel-body { border-top: 1px solid var(--color-border); }
.panel-body > h3:first-child { margin-bottom: var(--space-3); }
.section-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  font-family: var(--font-display);
  font-size: 19px;
  line-height: 24px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--color-text);
  margin: var(--space-10) 0 var(--space-4);
}
.section-label:first-child { margin-top: 0; }
.section-label .meta-aside { font-size: var(--text-sm); }
/* The danger zone: destructive controls at the foot of a page, one row
   each — what it does on the left, the button on the right. */
.danger-zone { border-color: var(--color-negative-border); }
.danger-zone .panel-header h2, .danger-zone .panel-header h3 { color: var(--state-failing); }
.danger-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; }
.danger-row + .danger-row { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--color-border); }
.danger-row p { margin: 0; color: var(--color-text-secondary); font-size: var(--text-sm); max-width: 56ch; }
.danger-row .danger-title { color: var(--color-text); font-size: var(--text-md); font-weight: 500; }
.danger-row form { margin: 0; }

/* ============================================================
   METADATA LABEL — small uppercase letter-spaced key
   ============================================================ */
.muted { color: var(--color-text-secondary); }
.text-tertiary { color: var(--color-text-tertiary); }
.mt-section { margin-top: 1.25rem; }
.meta-aside { text-transform: none; font-weight: 400; color: var(--color-text-tertiary); }
.m-0 { margin: 0; }
/* A block of plain text that should wrap, not scroll (job agents J7's
   fixed contract text). */
.pre-wrap { white-space: pre-wrap; word-wrap: break-word; }

/* Spacing and small utilities — the classes the templates use instead of
   inline style attributes (the CSP forbids those). */
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 0.75rem; }
.mt-4 { margin-top: 1rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
.label-plain { text-transform: none; letter-spacing: 0; font-weight: 400; }
.field-10 { max-width: 10rem; }
.field-12 { max-width: 12rem; }
.border-top { border-top: 1px solid var(--color-border); }

/* Screen-reader-only text — visually hidden, still announced. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ============================================================
   BADGES — tags: a fact about a thing ("default", "requires
   approval", a severity). Pills in Plex Mono, sentence case. A status
   enum (statusBadge: pending, awaiting approval, approved…) is a
   state, so it reads as a dot and a word like .chip below.
   ============================================================ */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 20px;
  padding: 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 20px;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  vertical-align: 1px;
}
a.badge:hover { text-decoration: none; filter: brightness(0.97); }
.badge-row { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
.badge-outline, .badge-neutral, .badge-policy, .badge-pending {
  background: var(--color-neutral-bg);
  border-color: transparent;
  color: var(--color-text-secondary);
}
/* tones — coloured word on its soft surface (each AA, see the tokens) */
.badge-attention { background: var(--state-needs-soft); color: var(--state-needs); }
.badge-positive { background: var(--state-done-soft); color: var(--state-done); }
.badge-negative { background: var(--state-failing-soft); color: var(--state-failing); }
.badge-active { background: var(--state-working-soft); color: var(--state-working); }
/* status enums: a dot and a word on the page, no fill */
.badge-awaiting_approval, .badge-running, .badge-pending,
.badge-approved, .badge-approve, .badge-completed,
.badge-rejected, .badge-reject, .badge-abandoned, .badge-request_changes, .badge-active {
  height: 22px;
  padding: 0 var(--space-2);
  border-radius: 999px;
  background: var(--color-surface);
  border-color: var(--color-border);
  color: var(--color-text-secondary);
}
.badge-awaiting_approval::before, .badge-running::before, .badge-pending::before,
.badge-approved::before, .badge-approve::before, .badge-completed::before,
.badge-rejected::before, .badge-reject::before, .badge-abandoned::before, .badge-request_changes::before, .badge-active::before {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--state-idle);
}
.badge-awaiting_approval, .badge-running { color: var(--state-needs); }
.badge-awaiting_approval::before, .badge-running::before { background: var(--state-needs); }
.badge-approved, .badge-approve, .badge-completed { color: var(--state-done); }
.badge-approved::before, .badge-approve::before, .badge-completed::before { background: var(--state-done); }
.badge-rejected, .badge-reject, .badge-request_changes { color: var(--state-failing); }
.badge-rejected::before, .badge-reject::before, .badge-request_changes::before { background: var(--state-failing); }
.badge-active { color: var(--state-working); }
.badge-active::before { background: var(--state-working); }

/* a finding's severity as a legend pill (design-fixes:49, Task 1): a 1px
   border and 8px dot carry the severity hue, the word stays plain text —
   so a severity's own contrast is never text's, only the dot's. */
.badge-sev { display: inline-flex; align-items: center; gap: 0.3rem; border: 1px solid var(--sev-c, var(--color-border)); background: var(--color-surface); color: var(--color-text); border-radius: 999px; padding: 0 0.5rem; font-size: var(--text-xs); font-weight: 500; }
.badge-sev::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--sev-c, var(--color-border-control)); }
.badge-sev.sev-critical { --sev-c: var(--sev-critical); }
.badge-sev.sev-major { --sev-c: var(--sev-major); }
.badge-sev.sev-moderate { --sev-c: var(--sev-moderate); }
.badge-sev.sev-minor { --sev-c: var(--sev-minor); }
.badge-sev.sev-pre-existing { --sev-c: var(--color-border-control); }

/* ============================================================
   STATUS — a dot and a word. One vocabulary (statusChip, status.html):
   ✓ done · ◐ working · ● needs you · ? blocked · ✕ error · ◌ stale,
   and the generic "state" partial for any tone and word. The dot
   carries the colour; the word carries the meaning (never colour
   alone). The glyph stays in the markup for the mini stage strip,
   where it is the visible mark. Colours: amber needs a person, blue
   working, green done, red failed, grey otherwise — teal is never a
   state.
   ============================================================ */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 22px;
  padding: 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  line-height: 16px;
  font-weight: 500;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text-secondary);
  white-space: nowrap;
  vertical-align: 1px;
}
/* the glyph drawn as a 6px dot in the state's colour */
.chip .chip-glyph {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  font-size: 0;
  line-height: 0;
  overflow: hidden;
}
.chip-done, .chip-tone-done { color: var(--state-done); }
.chip-working, .chip-tone-working { color: var(--state-working); }
.chip-you, .chip-blocked, .chip-tone-needs { color: var(--state-needs); }
.chip-error, .chip-tone-failing { color: var(--state-failing); }
.chip-stale, .chip-tone-idle { color: var(--state-idle); }
.chip-stale { border-style: dashed; }
/* waiting (design-fixes:13): a viewer's own Home row, neutral — shares
   --state-idle with stale, no new hue. */
.chip-waiting { color: var(--state-idle); }
/* notyet: the absence of status (statusPlaceholder) — an empty ring. */
.chip-notyet { color: var(--color-text-tertiary); }
.chip-notyet .chip-glyph { background: transparent; box-shadow: inset 0 0 0 1.5px currentColor; }

/* The Activity tab (agent memory Task 13): one row per recorded activity,
   with a delivery mark per memory it is given, coloured by the state
   tokens above. */
.activity-filters { display: flex; flex-wrap: wrap; gap: 0.35rem 1rem; margin: 0.75rem 0 1rem; font-size: var(--text-md); }
.activity-filters a[aria-current="true"] { font-weight: 600; color: var(--color-text); text-decoration: underline; text-underline-offset: 0.25em; }
.activity-row { list-style: none; border-bottom: 1px solid var(--color-border); padding: 0.75rem 0; }
.activity-row:last-child { border-bottom: none; }
.activity-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }

/* A status sentence (home, integrations, the run and review heads): the
   sentence in the text colour, the state as the dot before it. */
.status-line {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  background: none;
  color: var(--color-text);
  font-size: var(--text-md);
  line-height: 20px;
  font-weight: 500;
}
.status-line::before {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--state-idle);
  transform: translateY(-1px);
}
.status-line.tone-needs::before { background: var(--state-needs); }
.status-line.tone-working::before { background: var(--state-working); }
.status-line.tone-done::before { background: var(--state-done); }
.status-line.tone-failing::before { background: var(--state-failing); }
.status-line.tone-idle::before { background: var(--state-idle); }
.status-line + .status-line, .status-line + p { margin-top: var(--space-2); }
/* beats the generic .tone-* fill below: the sentence is never coloured */
.status-line[class*="tone-"] { color: var(--color-text); background: none; }

/* ============================================================
   BUTTONS
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: var(--control-height);
  padding: 0 var(--space-4);
  font: inherit;
  font-size: var(--text-sm);
  line-height: 20px;
  font-weight: 600;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border-control);
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-control);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  vertical-align: middle;
}
.btn:hover { background: var(--color-surface-sunken); text-decoration: none; }
.btn:active { transform: translateY(0.5px); }
/* One primary per page, in the header, right. */
.btn-primary {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12), 0 1px 0 rgba(14, 16, 21, 0.08);
}
.btn-primary:hover { background: var(--color-accent-hover); border-color: var(--color-accent-hover); }
/* Secondary: a real button, not a disabled-looking ghost. */
.btn-secondary { background: var(--color-surface); border-color: var(--color-border-control); color: var(--color-text); }
.btn-secondary:hover { background: var(--color-surface-sunken); }
/* Ghost: toolbars, panel headers, row actions. */
.btn-ghost { background: transparent; border-color: transparent; color: var(--color-text-secondary); box-shadow: none; }
.btn-ghost:hover { background: var(--color-surface-sunken); color: var(--color-text); }
/* Danger: quiet until hovered; lives in the danger zone. */
.btn-danger { background: var(--color-surface); border-color: var(--color-border-control); color: var(--color-negative-fg); }
.btn-danger:hover { background: var(--color-negative-bg); border-color: var(--color-negative-fg); }
.btn-approve { border-color: var(--color-positive-solid); background: var(--color-positive-solid); color: var(--color-on-positive); }
.btn-approve:hover { filter: brightness(1.08); background: var(--color-positive-solid); }
/* Request changes reads needs-amber (visual theme Task 11): a person must
   act on it, which is not the same signal as a failure (--state-failing,
   .btn-danger's own color). */
.btn-reject { background: var(--color-surface); border-color: var(--state-needs); color: var(--state-needs); }
.btn-reject:hover { background: var(--state-needs-soft); border-color: var(--state-needs); }
.btn-sm { height: 30px; padding: 0 var(--space-3); font-size: var(--text-xs); }
/* J3.4: a disabled button reads as inactive, not as a faded colour. */
.btn[disabled], .btn[disabled]:hover {
  opacity: 1;
  cursor: not-allowed;
  background: var(--color-surface-sunken);
  color: var(--color-text-tertiary);
  border-color: var(--color-border);
  box-shadow: none;
  filter: none;
}
.refusal-note { margin: var(--space-2) 0 0; color: var(--color-text-secondary); font-size: var(--text-sm); }
/* Unused since portal jobs were replaced by agents (task A5) — kept until a
   template guard's dead-CSS sweep removes it (Task A6's own guard checks
   wording, not unused selectors). */
.job-instructions { white-space: pre-wrap; font-family: var(--font-mono); }
/* J3.4: the reason a disabled control can't be used, inline beside it
   rather than in a paragraph below the button row. */
.refusal-inline { color: var(--color-text-secondary); font-size: var(--text-sm); }
.btn-block-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }
.btn-block-actions form { display: inline-flex; gap: var(--space-2); align-items: center; margin: 0; }
.btn:focus-visible, a:focus-visible, summary:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

/* ============================================================
   FORMS
   ============================================================ */
label { font-size: var(--text-sm); line-height: 20px; font-weight: 500; color: var(--color-text); display: block; margin-bottom: var(--space-1); }
/* "(optional)" inside a label: quiet. */
.label-note { text-transform: none; font-weight: 400; color: var(--color-text-tertiary); }
.field { margin-bottom: var(--space-4); }
.field:last-child { margin-bottom: 0; }
.field-hint { font-size: var(--text-xs); line-height: 16px; color: var(--color-text-tertiary); margin: var(--space-1) 0 0; }
/* A hint standing between blocks (not closing a field) keeps its distance
   from what follows. */
.field-hint:not(:last-child) { margin-bottom: var(--space-3); }
/* A group of radios or checkboxes: no box of its own, its legend a label. */
fieldset.field { border: 0; padding: 0; margin-inline: 0; min-width: 0; }
fieldset.field legend { font-size: var(--text-sm); line-height: 20px; font-weight: 500; color: var(--color-text); padding: 0; margin-bottom: var(--space-1); }
/* A list of links with no bullets. */
.plain-list { list-style: none; margin: 0; padding: 0; }
.plain-list li + li { margin-top: var(--space-2); }
/* upload-preview: files chosen in a skill upload's file/folder inputs,
   filled by portal.js; empty and invisible until then. */
.upload-preview { list-style: none; margin: var(--space-2) 0 0; padding: 0; font-size: var(--text-xs); color: var(--color-text-tertiary); }
.upload-preview:empty { display: none; }
.upload-preview li + li { margin-top: var(--space-1); }
.check-row { display: flex; gap: var(--space-2); align-items: baseline; margin-bottom: var(--space-2); }
.check-row label, label.check-row { font-weight: 400; margin: 0; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--color-accent); width: 14px; height: 14px; margin: 0; transform: translateY(2px); }
input[type="text"], input[type="password"], input[type="number"], input[type="email"], input[type="url"], input[type="search"], input:not([type]), textarea, select {
  font: inherit;
  font-size: var(--text-md);
  line-height: 20px;
  width: 100%;
  height: var(--control-height);
  padding: 0 var(--space-3);
  border: 1px solid var(--color-border-control);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: 0 1px 0 rgba(14, 16, 21, 0.03);
}
input:hover, select:hover, textarea:hover { border-color: var(--color-border-control-hover); }
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 22%, transparent);
}
input[disabled], select[disabled], textarea[disabled] { background: var(--color-surface-sunken); color: var(--color-text-tertiary); cursor: not-allowed; }
/* A chevron drawn with two gradients — no data: URI, no CSP change. */
select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 28px;
  background-image: linear-gradient(45deg, transparent 50%, var(--color-text-secondary) 50%),
                    linear-gradient(135deg, var(--color-text-secondary) 50%, transparent 50%);
  background-position: calc(100% - 15px) 13px, calc(100% - 11px) 13px;
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
}
select[multiple], select[size] { height: auto; padding: var(--space-1); background-image: none; }
textarea { height: auto; min-height: 80px; padding: var(--space-2) var(--space-3); resize: vertical; font-family: var(--font-sans); }
textarea.mono { font-family: var(--font-mono); font-size: var(--text-sm); line-height: 20px; }
::placeholder { color: var(--color-text-tertiary); opacity: 1; }
/* The native file picker's button drawn as a secondary pill. */
input[type="file"] { font: inherit; font-size: var(--text-sm); color: var(--color-text-secondary); }
input[type="file"]::file-selector-button { height: var(--control-height); margin-right: var(--space-3); padding: 0 var(--space-4); border: 1px solid var(--color-border-control); border-radius: var(--radius-pill); background: var(--color-surface); color: var(--color-text); font: inherit; font-size: var(--text-sm); font-weight: 600; cursor: pointer; }
input[type="file"]::file-selector-button:hover { background: var(--color-surface-sunken); }
/* A one-control form in a table row: the control and its button side by
   side (Settings' background jobs). */
.job-form { display: flex; gap: var(--space-2); align-items: center; margin: 0; }
.job-form select { flex: 1; min-width: 0; }

/* ============================================================
   TABLES
   ============================================================ */
.table-scroll { overflow-x: auto; }
table.data { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--text-md); line-height: 20px; }
table.data th {
  text-align: left;
  height: 36px;
  padding: var(--space-2) var(--space-5);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  line-height: 16px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-secondary);
  background: var(--color-surface-sunken);
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
}
table.data td {
  height: 48px;
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}
table.data tr:last-child td { border-bottom: none; }
table.data tbody tr:hover { background: color-mix(in srgb, var(--color-surface-sunken) 60%, transparent); }
table.data td.num, table.data th.num { text-align: right; font-variant-numeric: tabular-nums; }
table.data .row-title { font-weight: 500; color: var(--color-text); }
table.data .row-title a { color: var(--color-text); }
table.data .row-title a:hover { color: var(--color-accent); text-decoration: none; }
table.data .row-sub { color: var(--color-text-tertiary); font-size: var(--text-sm); line-height: 20px; margin-top: 2px; font-weight: 400; }
/* A table that opens a panel keeps the panel's rounded corners. */
.panel > .table-scroll:first-child table.data th:first-child,
.panel > table.data:first-child th:first-child { border-top-left-radius: var(--radius-lg); }
.panel > .table-scroll:first-child table.data th:last-child,
.panel > table.data:first-child th:last-child { border-top-right-radius: var(--radius-lg); }
/* A cell that holds only a row's action(s) — shrink it to its content. */
.cell-action { width: 1%; white-space: nowrap; text-align: right; }
/* A date/timestamp never wraps ("2026- / 09-12 / 09:45"). */
.date { white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ============================================================
   EMPTY STATE — in a panel: title and one line on the left, the
   action on the right, one row. A whole-page empty is centred.
   ============================================================ */
.empty-state {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: var(--space-4);
  row-gap: 2px;
  padding: var(--space-5);
  text-align: left;
  color: var(--color-text-secondary);
  font-size: var(--text-sm);
  line-height: 20px;
}
.empty-state > * { grid-column: 1; margin: 0; }
.empty-state > .btn, .empty-state > form { grid-column: 2; grid-row: 1 / span 4; justify-self: end; }
.empty-state p { margin: 0; }
.empty-state .empty-title { color: var(--color-text); font-weight: 500; font-size: var(--text-md); }
.empty-state .btn { flex: none; }
.empty-state-page { grid-template-columns: minmax(0, 1fr); justify-items: center; text-align: center; padding: var(--space-12) var(--space-6); row-gap: var(--space-1); }
.empty-state-page > .btn, .empty-state-page > form { grid-column: 1; grid-row: auto; justify-self: center; margin-top: var(--space-3); }
.empty-state-page p { max-width: 32rem; }

/* ============================================================
   SETTINGS FRAME (settings.go, settings.html, people.html; design-fixes
   Task 3): the index's whole-row links, one shared frame (crumb, width)
   for every page under it, and People's inline role control.
   ============================================================ */
.settings-list { list-style: none; margin: 0; padding: 0; }
.settings-row { display: grid; grid-template-columns: auto minmax(0,1fr) auto auto; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-5); border-top: 1px solid var(--color-border); color: var(--color-text); text-decoration: none; min-height: 44px; }
.panel > .settings-list:first-child li:first-child .settings-row { border-top: 0; }
.settings-row:hover { background: var(--color-surface-sunken); text-decoration: none; }
.settings-row-main { display: flex; flex-direction: column; min-width: 0; }
.settings-row-title { font-weight: 500; }
.settings-row-status { color: var(--color-text-secondary); font-size: var(--text-sm); }
.settings-row-chevron { color: var(--color-text-tertiary); }
.row-menu > summary { list-style: none; cursor: pointer; min-width: 32px; text-align: center; }
.row-menu > summary::-webkit-details-marker { display: none; }
.inline-list { display: flex; flex-wrap: wrap; gap: 0.3rem 1rem; list-style: none; margin: 0; padding: 0; }
@media (max-width: 767px) {
  .people-table thead { display: none; }
  .people-table tr { display: grid; gap: 0.4rem; padding: var(--space-3) 0; border-top: 1px solid var(--color-border); }
  .people-table td { border: 0; padding: 0; }
}

/* ============================================================
   HOME (home.go, home.html; visual theme Tasks 6-7) — the greeting and KPI
   band, the two-column grid with its side column (this month's runs, the
   activity heatmap, the decisions feed), and the three run lists (Needs
   you, Awaiting someone else's decision, Being worked) plus Knowledge to
   review, each a list of cards rather than a table row; the illustrated
   "No runs yet" panel is EMPTY STATE above with an area's emptyArt as its
   first child, centred by empty-state-page's own layout.
   ============================================================ */
.home-head { margin-bottom: var(--space-5); }
/* 600, not the plan's 700: the fonts ship 600 at most
   (TestStylesheetAsksOnlyForShippedWeights) — 700 fakes a bold from the
   600 file, so 600 is the heaviest real weight on this page. */
.home-greeting { margin: 0; font-family: var(--font-display); font-size: 30px; line-height: 1.2; font-weight: 600; letter-spacing: -0.03em; }
.home-summary { margin: var(--space-1) 0 0; color: var(--color-text-secondary); }

/* BP-4 (browser check, a theme bug already on main): the empty "No
   decisions yet." line carries none of the row's 3 columns (avatar | text
   | time), so it landed alone in the 24px avatar column and wrapped a few
   letters per line. home.html has only this one .home-feed, and only this
   one empty line. */

.home-cards { display: flex; flex-direction: column; gap: var(--space-3); list-style: none; margin: 0; padding: 0; }
.home-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto auto auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
/* A tone class also colors text/background wherever it appears (chips,
   bands, activity-actor) — pinned back here so a card's own surface and text
   stay plain regardless of tone; only its border picks up tone-needs. */
.home-card.tone-needs { border-color: var(--state-needs-soft); background: var(--color-surface); color: var(--color-text); }
.home-card.tone-working { background: var(--color-surface); color: var(--color-text); }
.home-card-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* BP-2/BP-5 (browser check): overflow-wrap: anywhere lets the browser treat
   every character as a break opportunity for min-content sizing, so a
   title's grid track could collapse well below its longest word's width —
   one letter per line on the Features board (BP-2) and an occasional
   mid-word break here at medium widths ("Maintenanc e windows", BP-5).
   break-word keeps the same safety net (a single token longer than the
   line still breaks) but only after ordinary word-boundary wrapping no
   longer fits. */
.home-card-title { font-weight: 500; color: var(--color-text); overflow-wrap: break-word; }
.home-card-title:hover { color: var(--color-accent); text-decoration: none; }
.home-card-sub { margin: 0; font-size: var(--text-sm); color: var(--color-text-secondary); overflow-wrap: anywhere; }
/* BP-5 (browser check, ~800px viewport): above 640px the row's trailing
   action cluster (an avatar or the portal's glyph, the age chip, the
   status chip, Decide/Open) still shared one row with the title, with no
   room at medium widths. Below 900px they wrap under the text exactly as
   they already did on a phone. */
@media (max-width: 900px) {
  .home-card { grid-template-columns: auto minmax(0, 1fr); }
  .home-card > .btn { grid-column: 1 / -1; }
  /* A chip keeps its own width in the narrow grid, never the column's. */
  .home-card > .badge, .home-card > .chip { justify-self: start; }
}

/* ============================================================
   DECISIONS — the record's highest-value part
   ============================================================ */
ul.decisions { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }

/* ============================================================
   STAGE ATTEMPT PANEL
   ============================================================ */

pre.output-block {
  white-space: pre-wrap; word-wrap: break-word;
  background: var(--color-surface-sunken);
  border: 1px solid var(--color-border);
  padding: 0.75rem 0.9rem;
  border-radius: var(--radius-sm);
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-md);
  line-height: 1.55;
}

details.disclosure { margin-top: 0.9rem; border-top: 1px solid var(--color-border); padding-top: 0.7rem; }
details.disclosure summary {
  cursor: pointer; color: var(--color-text-secondary); font-size: var(--text-sm); font-weight: 600;
  list-style: none;
}
details.disclosure summary::-webkit-details-marker { display: none; }
details.disclosure summary::before { content: "▸ "; color: var(--color-text-tertiary); }
details.disclosure[open] summary::before { content: "▾ "; }
details.disclosure > pre { margin-top: 0.6rem; }

/* ============================================================
   SLACK STEP — Settings › Slack's four-step checklist
   (agent memory task 16). Each step is its own <details>, styled
   like a panel; only the current one opens by default.
   ============================================================ */
.slack-step {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  margin-bottom: var(--space-4);
  padding: var(--space-4) var(--space-5);
}
.slack-step > summary {
  cursor: pointer;
  font-weight: 600;
  font-size: var(--text-md);
  list-style: none;
}
.slack-step > summary::-webkit-details-marker { display: none; }
.slack-step > :not(summary) { margin-top: var(--space-4); }
.slack-step.state-done > summary { color: var(--state-done); }
.slack-step.state-later > summary { color: var(--color-text-tertiary); }

/* ============================================================
   CALLOUT — at most one per place, only when there is something to
   do or know. The body is always --color-text; only the title
   carries the tone. Never two in a row (merge them).
   ============================================================ */
/* R16: a callout stacked right after the tab bar, or right after another
   callout (a job agent's page can carry several), used to touch it —
   give it its own top margin. */
.tabs + .callout, .callout + .callout { margin-top: var(--space-3); }
.callout, .callout-neutral, .critique-block, .worker-error-block, .import-warning {
  display: block;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
  color: var(--color-text);
  font-size: var(--text-md);
  line-height: 22px;
}
.callout > :last-child { margin-bottom: 0; }
.callout-title { display: block; font-weight: 600; margin: 0 0 2px; color: var(--color-text); }
.callout-body { margin: 0; }
.callout-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-top: var(--space-3); }
.callout-actions form { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0; }
.callout-actions select { width: 16rem; max-width: 100%; }
.callout-neutral { color: var(--color-text); }
/* a person is needed */
.callout-needs, .critique-block, .import-warning { background: var(--state-needs-soft); border-color: var(--color-attention-border); }
.callout-needs .callout-title, .critique-block .callout-title { color: var(--state-needs); }
/* something failed that you must fix */
.callout-failing, .worker-error-block { background: var(--state-failing-soft); border-color: var(--color-negative-border); }
.callout-failing .callout-title, .worker-error-block .callout-title { color: var(--state-failing); }
/* in progress / for information */
.callout-working { background: var(--state-working-soft); border-color: color-mix(in srgb, var(--state-working) 30%, transparent); }
.callout-working .callout-title { color: var(--state-working); }
.callout-done { background: var(--state-done-soft); border-color: var(--color-positive-border); }
.callout-done .callout-title { color: var(--state-done); }
.critique-block, .worker-error-block { margin-top: var(--space-3); }
.critique-block pre, .worker-error-block pre { background: transparent; border: none; padding: var(--space-1) 0 0; color: var(--color-text); white-space: pre-wrap; }
.worker-error-block pre { font-family: var(--font-mono); font-size: var(--text-sm); }
.worker-error { margin-top: 0; }
.worker-error p { margin: 0; }
.worker-error-sentence { font-weight: 600; }
.worker-error-fix, .worker-error-hint { margin-top: var(--space-1); color: var(--color-text-secondary); }
.worker-error-details { margin-top: var(--space-2); }
.worker-error-details summary { cursor: pointer; font-size: var(--text-sm); color: var(--color-text-secondary); }
.worker-error-details pre { margin: 0; max-height: 14rem; overflow: auto; overflow-wrap: anywhere; }
/* Critique is agent-generated prose, rendered through the markdown
   pipeline (markdown.go). */
.critique-block .markdown-body { color: var(--color-text); margin-top: var(--space-1); font-size: var(--text-md); line-height: 22px; max-width: none; }
.critique-block .markdown-body > *:last-child { margin-bottom: 0; }
.import-warning { margin: var(--space-2) 0; padding: var(--space-2) var(--space-3); }

/* A severity label the judge opens a point with (severity.go): an inline
   badge that sits on the text's baseline instead of the panel header's. */
.markdown-body .badge.severity { vertical-align: 0.1em; margin-right: 0.3rem; }

/* Portal authoring: a portal-executed stage still being drafted. */
.drafting-box { border-color: var(--color-border); background: var(--color-surface-sunken); }
.drafting-box .approval-heading { color: var(--color-text); }

/* A form sent back with what was wrong with it. */
.form-error {
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-negative-border);
  border-radius: var(--radius-md);
  background: var(--state-failing-soft);
  color: var(--color-text);
  margin-bottom: var(--space-4);
}

ul.files { list-style: none; margin: 0.4rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.2rem; }
ul.files li code { font-size: var(--text-sm); background: var(--color-surface-sunken); padding: 0.1rem 0.4rem; border-radius: 3px; }

/* diff — no syntax highlighting, no library: coloured by leading +/- */
.diff-panel { margin-top: 0.6rem; }
.diff {
  overflow-x: auto;
  background: var(--color-surface-sunken);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 0.5rem 0;
  font: var(--text-xs)/1.5 var(--font-mono);
}
.diff .line { padding: 0 1rem; white-space: pre; }
.diff .add { background: var(--color-diff-add-bg); color: var(--color-diff-add-fg); }
.diff .del { background: var(--color-diff-del-bg); color: var(--color-diff-del-fg); }
.diff .hunk { color: var(--color-text-tertiary); }
.diff .meta { color: var(--color-text-tertiary); font-weight: 500; }
.diff .ln { display: inline-block; position: relative; min-width: 2.6rem; padding-right: 0.6rem; text-align: right; color: var(--color-text-tertiary); user-select: none; }
.diff .ln a { color: inherit; text-decoration: none; }
.diff .ln a:hover { text-decoration: underline; }
.diff .line:target { outline: 2px solid var(--color-accent); outline-offset: -2px; }
.diff-file { margin-top: 0.8rem; font-size: var(--text-sm); }
/* J5.10-style navigation: one file's diff behind a <details>, its summary
   showing the path and a +N/−M tally so a reviewer can scan the file list
   without opening every diff. */
.diff-file-box { margin-top: 0.8rem; border: 1px solid var(--color-border); border-radius: var(--radius-sm); }
.diff-file-box summary {
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.8rem;
  font-size: var(--text-sm);
  font-family: var(--font-mono);
  list-style: none;
}
.diff-file-box summary::-webkit-details-marker { display: none; }
.diff-file-box[open] summary { border-bottom: 1px solid var(--color-border); }
.diff-file-box .diff-add-count { color: var(--color-diff-add-fg); }
.diff-file-box .diff-del-count { color: var(--color-diff-del-fg); }
.diff-file-box .diff { border: none; border-radius: 0; margin: 0; }
/* The raw `diff --git`/`index`/`---`/`+++` lines — closed by default, small,
   folded away from the +/- lines a reviewer actually reads. */
.diff-meta { margin-top: 0.4rem; font-size: var(--text-2xs); }
.diff-meta summary { cursor: pointer; color: var(--color-text-tertiary); list-style: none; }
.diff-meta summary::-webkit-details-marker { display: none; }
.diff-meta pre { margin-top: 0.3rem; }

/* linediff: a rescanned reference or skill against the approved text (the
   agent page's review panel). Colours are the run page's diff tokens. */
.diff-heading { margin: 0.8rem 0 0.3rem; font-size: var(--text-sm); }
.diff-counts { margin: 0 0 0.25rem; font-size: var(--text-sm); }
.diff-table { width: 100%; border-collapse: collapse; font-family: var(--font-mono); font-size: var(--text-sm); border: 1px solid var(--color-border); border-radius: var(--radius-sm); }
.diff-table td { padding: 0 0.5rem; vertical-align: top; }
.diff-num { width: 3.5rem; text-align: right; color: var(--color-text-tertiary); user-select: none; }
.diff-text { white-space: pre-wrap; overflow-wrap: anywhere; }
.diff-hunk td { color: var(--color-text-tertiary); padding-top: 0.4rem; }
.diff-line.diff-add { background: var(--color-diff-add-bg); color: var(--color-diff-add-fg); }
.diff-line.diff-del { background: var(--color-diff-del-bg); color: var(--color-diff-del-fg); }

/* review — numbered output, comment forms and threads; no script */
.review-lines { border: 1px solid var(--color-border); border-radius: var(--radius-sm); background: var(--color-surface-sunken); padding: 0.4rem 0; font: var(--text-sm)/1.55 var(--font-mono); }
.review-line { display: flex; padding: 0 0.8rem; white-space: pre-wrap; }
.review-line:target { outline: 2px solid var(--color-accent); outline-offset: -2px; }
.review-line .ln { position: relative; flex: 0 0 2.6rem; padding-right: 0.8rem; text-align: right; color: var(--color-text-tertiary); text-decoration: none; user-select: none; }
.review-line .text { flex: 1; min-width: 0; }
/* J3.7: nothing said a line number was clickable — a "+" appears on hover,
   absolutely positioned inside the number cell so it never shifts the
   digits or the line it sits on. */
.review-line .ln:hover::before, .diff .ln a:hover::before {
  content: "+";
  position: absolute;
  left: 0;
  color: var(--color-accent);
}
/* The rendered review: a stage output as blocks (blocks.go), the "At a
   glance" ones in a card at the top. Each block carries a Comment link at its
   right — shown on hover, when the block is the one linked to, and whenever
   it has keyboard focus (it is only ever transparent, never removed, so it
   stays reachable). */

.output-block { position: relative; padding: 0.1rem 4.5rem 0.1rem 0.4rem; border-radius: var(--radius-sm); }
.output-block:target { outline: 2px solid var(--color-accent); outline-offset: -2px; }

.output-blocks .comment,
.output-blocks .comment-form { margin-left: 0.4rem; }
.comment-link { margin-left: 0.6rem; font-size: var(--text-sm); font-weight: 400; text-transform: none; }
.diff .diff-aside { white-space: normal; font: var(--text-md)/1.5 var(--font-sans); padding: 0.2rem 0; }
.comment, .comment-form { margin: 0.4rem 0.8rem 0.6rem 3.4rem; padding: 0.6rem 0.8rem; border: 1px solid var(--color-border); border-radius: var(--radius-sm); font: var(--text-md)/1.5 var(--font-sans); white-space: normal; }
.comment.is-resolved > summary { cursor: pointer; color: var(--color-text-secondary); }
/* A comment's colour is its state — open is attention (amber), resolved is
   positive (green) — as its left edge and a pill. An accent (blue) pill marks
   one raised on an earlier attempt; a neutral pill says why a carried comment
   is not on this attempt's lines. */
.comment.is-resolved { border-left: 3px solid var(--color-positive-fg); background: var(--color-surface-sunken); }
.comment-state, .comment-from, .comment-unplaced {
  display: inline-block; margin-right: 0.35rem; padding: 0.05rem 0.5rem;
  border: 1px solid transparent; border-radius: 999px;
  font-size: var(--text-xs); font-weight: 500;
  line-height: 1.5; white-space: nowrap; vertical-align: 0.05rem;
}
.comment.is-resolved .comment-state { background: var(--color-positive-bg); color: var(--color-positive-fg); border-color: var(--color-positive-border); }
.comment-from { background: var(--color-neutral-bg); color: var(--color-text-secondary); border-color: var(--color-neutral-border); }
.comment-unplaced { background: var(--color-neutral-bg); color: var(--color-neutral-fg); border-color: var(--color-neutral-border); }

.comment-body { margin: 0.3rem 0; white-space: pre-wrap; }
.comment-quote { margin: 0.3rem 0; padding: 0.3rem 0.6rem; background: var(--color-surface-sunken); border-radius: var(--radius-sm); font: var(--text-sm)/1.5 var(--font-mono); white-space: pre-wrap; }
/* J3.3: a suggestion diff inside a comment holds prose, not code — it must
   wrap at the panel edge instead of forcing a horizontal scrollbar. The
   plain .diff (code, in the Changes panel) keeps white-space: pre above. */
.comment .diff .line { white-space: pre-wrap; overflow-wrap: anywhere; }
.comment-event { margin-top: 0.25rem; font-size: var(--text-sm); }
.comment-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.5rem; }
.comment-actions form { display: flex; gap: 0.3rem; margin: 0; }
.comment-actions input { min-width: 0; }
.comment-tally { margin: 0 0 0.8rem; font-size: var(--text-md); }

/* ============================================================
   APPROVAL FORM — the most important control in the product
   ============================================================ */
.approval-box {
  border: 1px solid var(--color-attention-border);
  background: var(--state-needs-soft);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-5);
  margin-top: var(--space-4);
  color: var(--color-text);
}
.approval-box .approval-heading {
  display: flex; align-items: center; gap: var(--space-2);
  font-weight: 600; font-size: var(--text-md); color: var(--state-needs);
  margin-bottom: var(--space-3);
}
.approval-box textarea {
  background: var(--color-surface);
}
.approval-box .btn-block-actions { margin-top: 0.75rem; }

/* An outline button, no fill (visual theme Task 11): sending back is a
   routing choice, not a danger — .btn's own base already gives it
   --color-surface. */

kbd { display: inline-block; padding: 0 0.3rem; border: 1px solid var(--color-border-strong); border-bottom-width: 2px; border-radius: var(--radius-sm); background: var(--color-surface); color: var(--color-text-secondary); font-size: 0.7rem; line-height: 1.4; }

/* The marks on a finding card: three segmented radios (the input hidden,
   its label the control) and a note, both on the decision form. The note is
   a textarea: Enter there is a new line, never a submit of Send back. */
.marks-seg { display: inline-flex; margin: 0.7rem 0 0; padding: 0; border: 1px solid var(--color-border-control); border-radius: var(--radius-md); overflow: hidden; }
.marks-seg input { position: absolute; opacity: 0; pointer-events: none; }
.marks-seg label { margin: 0; padding: 0.25rem 0.75rem; border-right: 1px solid var(--color-border); color: var(--color-text-secondary); font-size: var(--text-sm); font-weight: 500; cursor: pointer; }
.marks-seg label:last-of-type { border-right: 0; }
.marks-seg input:checked + label { color: var(--color-surface); }
.marks-seg input[value="fix"]:checked + label { background: var(--color-negative-fg); }
.marks-seg input[value="accept"]:checked + label { background: var(--color-positive-fg); }
.marks-seg input[value="ignore"]:checked + label { background: var(--color-neutral-fg); }
.marks-seg input:focus-visible + label { outline: 2px solid var(--color-accent); outline-offset: -2px; }
/* reviews:157 — a mark segment is a real touch target on a phone. */
@media (max-width: 767px) { .marks-seg label { min-height: 40px; display: inline-flex; align-items: center; } }
.finding-note { display: block; width: 100%; margin-top: 0.5rem; font-size: var(--text-sm); }

/* run:15 — a scroll cue for the map's own box: two edge fades that scroll
   with the content (so they only show where there is more to see) over two
   shadows fixed to the viewport (so they only peek through once something
   is actually scrolled past) — the standard background-attachment:
   local/scroll pair, tokens only, no script. */
.chain-scroll {
  overflow-x: auto;
  margin: 0 0 1rem;
  background:
    linear-gradient(to right, var(--color-surface) 30%, transparent) left / 24px 100% no-repeat local,
    linear-gradient(to left, var(--color-surface) 30%, transparent) right / 24px 100% no-repeat local,
    linear-gradient(to right, color-mix(in srgb, var(--color-text) 16%, transparent), transparent) left / 8px 100% no-repeat scroll,
    linear-gradient(to left, color-mix(in srgb, var(--color-text) 16%, transparent), transparent) right / 8px 100% no-repeat scroll;
}

/* The map is the chain's accessible form: a box reached with Tab shows a
   ring of its own, thicker than a selected box's, and dashed so it reads as
   focus, not selection, on a box that is both. */

.detail header { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 0.5rem; }
.detail header h4 { margin: 0; font-size: var(--text-lg); }
.detail .kind { font-size: var(--text-xs); font-weight: 500; color: var(--color-text-tertiary); }
.detail .src { margin-left: auto; font-size: var(--text-sm); }
.detail .lead { font-size: var(--text-md); margin: 0 0 0.75rem; line-height: 1.5; }
.detail .facts { display: grid; grid-template-columns: max-content 1fr; gap: 0.35rem 1rem; margin: 0 0 0.75rem; font-size: var(--text-sm); }
.detail .facts dt { color: var(--color-text-tertiary); }
.detail .facts dd { margin: 0; }
.detail .arrow { color: var(--color-text-tertiary); }
.why { border: 1px solid; border-left-width: 4px; border-radius: var(--radius-sm); padding: 0.75rem 1rem; font-size: var(--text-sm); }

.why > p { margin: 0.6rem 0 0; }
.why .deploy { margin-top: 0.25rem; color: var(--color-text-secondary); }

.finding { border: 1px solid var(--color-border); border-radius: var(--radius-sm); padding: 0.6rem 0.8rem; background: var(--color-surface); }
.finding p { margin: 0.35rem 0 0; }
.finding .fix { color: var(--color-text-secondary); }

/* The stage bands over the column heads (chainbands.go): the stage's label
   alone, centred in its band — its state shows once, on the band row above
   the map, never here too. */

.mark { margin-top: 0.5rem; font-size: var(--text-sm); }
.mark blockquote { margin: 0.3rem 0 0; padding: 0.3rem 0.7rem; border-left: 3px solid var(--color-border-strong); color: var(--color-text-secondary); }
/* reviews:145 — a bare <blockquote> (a rule's quote, a suggestion's source
   words, an ignore note) takes the same quiet left accent as everywhere
   else a quote sits, rather than the browser default. */
.panel-body blockquote { border-left: 3px solid var(--color-border-strong); margin: 0.4rem 0 0; padding: 0 0 0 var(--space-3); color: var(--color-text-secondary); }
.why .overall { margin: 0.75rem 0 0; }

/* ============================================================
   SKILL MARKDOWN BODY
   ============================================================ */
/* A long document (a main skill, stage instructions): 15/26, at most 72
   characters a line. Inside a rail, a card or a critique it takes the
   container's width (the rules at those places set max-width: none). */
.markdown-body { font-size: var(--text-prose); line-height: 26px; max-width: 72ch; color: var(--color-text); }
.markdown-body h1 { font-size: var(--text-xl); line-height: 28px; letter-spacing: -0.01em; border-bottom: 0; padding-bottom: 0; margin: 0 0 var(--space-3); }
.markdown-body h2 { font-size: var(--text-lg); line-height: 24px; font-weight: 600; margin: var(--space-8) 0 var(--space-2); }
.markdown-body h3 { font-size: var(--text-prose); line-height: 24px; font-weight: 600; margin: var(--space-6) 0 var(--space-2); }
.markdown-body h4, .markdown-body h5 { font-size: var(--text-md); font-weight: 600; margin: var(--space-4) 0 var(--space-1); }
.markdown-body > :first-child { margin-top: 0; }
.markdown-body p { margin: 0 0 var(--space-4); }
.markdown-body ul, .markdown-body ol { margin: 0 0 var(--space-4); padding-left: 1.4rem; }
.markdown-body li { margin-bottom: var(--space-1); }
.markdown-body li::marker { color: var(--color-text-tertiary); }
.markdown-body strong { font-weight: 600; }
.markdown-body hr { border: 0; border-top: 1px solid var(--color-border); margin: var(--space-6) 0; }
.markdown-body code {
  font-family: var(--font-mono); font-size: 0.88em;
  background: var(--color-surface-sunken); padding: 1px 4px; border-radius: 4px; border: 0;
}
.markdown-body pre {
  background: var(--color-surface-sunken); border: 1px solid var(--color-border);
  border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); overflow-x: auto; margin: 0 0 var(--space-4);
  font-size: var(--text-sm); line-height: 20px;
}
.markdown-body pre code { background: none; padding: 0; font-size: var(--text-sm); }
.markdown-body blockquote {
  margin: 0 0 var(--space-4); padding: var(--space-1) var(--space-4); border-left: 2px solid var(--color-border-strong);
  color: var(--color-text-secondary);
}
.markdown-body a { color: var(--color-accent); }
.run-rail .markdown-body,
.output-blocks .markdown-body,
.output-blocks,
.comment .markdown-body { max-width: none; }
.run-rail .markdown-body,
.comment .markdown-body { font-size: var(--text-md); line-height: 22px; }
/* display:block turns the table itself into the scrollable container — its
   rows/cells keep their normal table layout, but a table wider than the
   panel scrolls horizontally in place instead of widening the page. */
.markdown-body table { display: block; overflow-x: auto; border-collapse: collapse; margin: 0 0 0.9rem; font-size: 0.85em; }
.markdown-body table th, .markdown-body table td { border: 1px solid var(--color-border); padding: 0.4rem 0.6rem; }

/* ============================================================
   MISC
   ============================================================ */
.stack { display: flex; flex-direction: column; gap: var(--space-4); }
.stack > .field, .stack > .field-row, .stack > fieldset.field { margin-bottom: 0; }
kbd { font-family: var(--font-mono); }

/* ============================================================
   RESPONSIVE — sidebar becomes a top bar; nothing scrolls the
   page body sideways; wide content scrolls in its own box.
   ============================================================ */
@media (max-width: 860px) {
  .shell { grid-template-columns: 1fr; }
  .sidebar {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    border-right: none;
    border-bottom: 1px solid var(--color-border);
    padding: 0.75rem 1rem;
    padding-left: calc(1rem + env(safe-area-inset-left, 0px));
    padding-right: calc(1rem + env(safe-area-inset-right, 0px));
    gap: 0.75rem;
    position: sticky;
    top: 0;
    z-index: 5;
  }
  .brand { padding: 0; }
  .nav-group { flex-direction: row; flex-wrap: wrap; }
  /* shell:58: a tap target of at least 44px on the phone menu's own
     links and its disclosure. */
  .nav-link { min-height: 44px; }
  .nav-menu > summary { min-height: 44px; display: inline-flex; align-items: center; gap: var(--space-2); }

  /* Compact "Signed in" row: keep the person's name and Sign out, drop the
     "Signed in" label (above) and the org name — one row instead of three. */
  .nav-account { margin-top: 0; border-top: 0; padding-top: 0; flex-direction: row; align-items: center; flex-wrap: wrap; }
  .nav-person { padding: 0 var(--space-2); }
  .nav-org-name { display: none; }
  .content { padding: var(--space-5) var(--space-4) var(--space-12); }
  .page-header { flex-direction: column; align-items: flex-start; }
  .field-row { grid-template-columns: minmax(0, 1fr); }
  .danger-row { align-items: flex-start; }
  /* J3.3/responsiveness: a suggestion diff's margin-left leaves little room
     at this width for the comment/suggestion text itself. */
  .comment-form, .comment { margin-left: 1.2rem; }
}

/* portal.js: screen-reader-only live region and the top bar */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.poll-bar { position: sticky; top: 0; z-index: 30; display: flex; gap: 0.75rem; align-items: center; padding: 0.45rem 1.25rem; background: var(--color-accent); color: var(--color-on-accent); font-size: var(--text-md); }
.poll-bar.is-error { background: var(--color-negative-fg); }
.poll-bar button { margin-left: auto; background: var(--color-surface); color: var(--color-accent); border: 0; border-radius: 6px; padding: 0.2rem 0.75rem; font-weight: 600; cursor: pointer; }
.poll-bar .poll-bar-close { margin-left: 0; background: transparent; color: inherit; }

/* tag-chip: the run page's own branch/repository tags (⎇ main, a
   repository's short name) — a plain label, not a status, so it is its own
   class and never collides with statusChip's ".chip" base (final review I1:
   the two used to share one class name, and this one, defined later in the
   file, silently overrode every ".chip-*" tone). */
.tag-chip { display: inline-flex; align-items: center; gap: 0.35rem; border: 1px solid var(--color-border); background: var(--color-surface); border-radius: 999px; padding: 0.05rem 0.6rem; font-size: var(--text-xs); color: var(--color-text-secondary); }
.chip-icon { flex: none; color: var(--color-text-tertiary); }

/* tag / tag-builtin: the Agents list's built-in tag (task 14, spec
   2026-09-27) — a small label beside an agent's name, distinct from
   statusChip's tone-driven ".chip" base and from tag-chip's own run-page
   role. */
.tag { display: inline-flex; align-items: center; border-radius: 999px; padding: 0.05rem 0.6rem; font-size: var(--text-xs); font-weight: 600; }

/* The Agents list as a card grid (visual theme Task 12): every card the
   same width band, wrapping to fit; buttons inside stay teal — an agent
   is not an area of its own, so the card carries the agents area chip,
   never a hue on anything that carries state. */
/* TR-13 (whole-branch review): padded on three sides so a card's own border
   does not abut the panel's — the top keeps the margin above instead, for
   the gap under .agent-list-filters. */
.agent-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr)); gap: var(--space-3); margin: 0.7rem 0 0; padding: 0 var(--space-5) var(--space-5); }
.agent-card { display: flex; flex-direction: column; gap: 0.5rem; padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface); }
.agent-card-head { display: flex; align-items: flex-start; gap: 0.6rem; }
.agent-card-name { min-width: 0; overflow-wrap: anywhere; font-weight: 500; }
.agent-card-name a { color: var(--color-text); }
.agent-card-desc { margin: 0; font-size: var(--text-sm); }
.agent-card .row-sub { margin: -0.3rem 0 0; }
.agent-card-stats { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.25rem 0.9rem; font-size: var(--text-xs); color: var(--color-text-secondary); }
.agent-card-review { margin: 0; font-size: var(--text-sm); }
.agent-card > form { align-self: flex-start; }
/* The add card: a dashed placeholder, the same illustration and words the
   empty state uses elsewhere — never a table row, so it sits at the grid's
   own end. */

.tabs { display: flex; gap: 2px; margin: 1.1rem 0 0; border-bottom: 1px solid var(--color-border); overflow-x: auto; }
.tab { padding: 0.5rem 0.75rem; margin-bottom: -1px; border-bottom: 2px solid transparent; color: var(--color-text-secondary); white-space: nowrap; font-size: var(--text-md); text-decoration: none; }
.tab:hover { color: var(--color-text); text-decoration: none; }
.tab[aria-current="page"] { color: var(--color-text); border-bottom-color: var(--color-text); font-weight: 600; }
/* Under 768px the tabs wrap onto a second line: no tab is ever hidden off
   the edge, and nothing needs a script to reach it. */
@media (max-width: 767px) {
  .tabs, .agent-tabs { flex-wrap: wrap; overflow-x: visible; }
}
.tab-count { display: inline-block; min-width: 1.1rem; padding: 0 0.3rem; margin-left: 0.25rem; border-radius: 9px; background: var(--color-surface-sunken); color: var(--color-text-secondary); font-size: var(--text-xs); text-align: center; }

.finding-card { margin-bottom: 0.75rem; padding: 0.9rem 1rem; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); outline: none; }
.finding-card.is-current, .finding-card:focus-visible { box-shadow: 0 0 0 2px var(--color-accent); }
/* A row already marked away from "to fix" (settled — accepted or ignored)
   reads decided, not dimmed to under-contrast (reviews:134/151: opacity
   0.6 read 2.36:1) — a left accent and a filled mark glyph (reviews:146)
   say "dealt with" without losing the text. */

.finding-top { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.finding-where { color: var(--color-text-tertiary); font-size: var(--text-sm); }
.finding-where a { color: var(--color-text-secondary); }
.finding-issue { margin: 0.5rem 0 0; }
.finding-fix { margin: 0.6rem 0 0; color: var(--color-text-secondary); }
.finding-disposition { margin: 0.35rem 0 0; color: var(--color-text-secondary); font-size: var(--text-sm); }

/* ============================================================
   REVIEW PAGE (review.html, reviews.go) — design-fixes Task 6
   ============================================================ */
/* reviews:115 — the trust line: quiet by default, attention tone only for
   a thin (diff-only) review. */
.review-trust { margin: 0.2rem 0 0; color: var(--color-text-secondary); font-size: var(--text-sm); }
.review-trust.tone-attention { color: var(--state-needs); }
.finding-evidence { margin-top: 0.5rem; }
.finding-evidence summary { color: var(--color-text-secondary); font-size: var(--text-sm); cursor: pointer; }
.finding-place { margin: 0.4rem 0; color: var(--color-text-secondary); font-size: var(--text-sm); }
.snippet { margin-top: 0.6rem; overflow-x: auto; border: 1px solid var(--color-border); border-radius: var(--radius-sm); font-family: var(--font-mono); font-size: var(--text-xs); }
.snippet-row { display: grid; grid-template-columns: 2.4rem 2.4rem 1fr; white-space: pre; line-height: 1.55; }
.snippet-row span { padding: 0 0.4rem; }
.snippet-row .ln { color: var(--color-text-tertiary); text-align: right; user-select: none; }
.snippet-row.add { background: var(--color-diff-add-bg); }
.snippet-row.del { background: var(--color-diff-del-bg); }
.snippet-row.is-hit { box-shadow: inset 3px 0 0 var(--color-attention-fg); }
/* reviews:161 — a long line in a finding's snippet wraps rather than
   forcing the whole card to scroll sideways. */
.snippet-row .text { white-space: pre-wrap; overflow-wrap: anywhere; }

/* The review page rebuilt around the mockup (wave 1b, task 9, and its
   2026-09-29 controller layout fix): its own page grid, the verdict head,
   the trust line, the seven-angle strip, the criteria table and the angle
   sections — new classes only, every color still a token. */

/* .review-page is scoped to this page, not the shared .run-top/.run-rail
   grid (380px, run/agent/prd pages): minmax(0,1fr) 320px, matching the
   mockup exactly; .review-span crosses both columns (the head and the
   strip); the rail sticks at the same 16px every sticky rail in the app
   uses, and is never its own scroll container. */
.review-page { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); align-items: start; }
@media (min-width: 1000px) {
  .review-page { grid-template-columns: minmax(0, 1fr) 320px; }
  .review-page > .review-span { grid-column: 1 / -1; }
  .review-page > .review-rail-col { position: sticky; top: 16px; }
}
.review-head-block { display: grid; gap: var(--space-3); }
.review-main { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
.review-rail-col { display: grid; gap: var(--space-4); min-width: 0; }
.review-rail-col > * { min-width: 0; max-width: 100%; }

.review-verdict-head .panel-body { display: grid; gap: var(--space-3); }
.review-head-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3); }
.review-head-verdict { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
/* The verdict badge is the largest thing in the head (controller fix):
   15px/600, a padded pill with its own icon — bigger than the ordinary
   .badge it still is one (same tone classes, same tokens). */
.review-verdict-badge { font-size: 15px; font-weight: 600; padding: 6px 14px; gap: var(--space-2); }
.review-verdict-icon { flex: none; }
.review-one-line { margin: 0; max-width: 78ch; font-size: 15px; }
.review-chips { margin: 0; }

/* .status-row (mockup gap e): "Marked" and "N of M" are two sibling
   elements with no text between them — with no rule at all they rendered
   glued together ("Marked0 of 4"); space-between is the mockup's own fix,
   not a text-node space, since the row also carries a link and a pill
   side by side elsewhere (the PRD-changes list). */
.status-row { display: flex; justify-content: space-between; gap: var(--space-2); font-size: var(--text-sm); align-items: center; }

/* One row of seven compact tiles (controller fix): display:grid was
   missing, which let them fall back to block stacking. */
.angle-strip { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--space-2); }
@media (max-width: 1000px) { .angle-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.angle-tile { display: grid; gap: 2px; padding: var(--space-2) var(--space-3); color: inherit; min-height: 60px; align-content: center; }
.angle-tile:hover { text-decoration: none; border-color: var(--color-border-strong); }
.angle-tile-label { font-size: var(--text-xs); color: var(--color-text-secondary); font-weight: 500; }
.angle-tile-val { font-size: var(--text-sm); font-weight: 600; display: flex; align-items: center; gap: var(--space-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.angle-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.angle-dot.sev-critical { background: var(--sev-critical); }
.angle-dot.sev-major { background: var(--sev-major); }
.angle-dot.sev-moderate { background: var(--sev-moderate); }
.angle-dot.sev-minor { background: var(--sev-minor); }
.angle-dot.sev-done { background: var(--state-done); }
.angle-dot.sev-needs { background: var(--state-needs); }
.angle-dot.sev-idle { background: var(--state-idle); }

.criteria-table td.id, .criteria-table a.id { white-space: nowrap; }
/* The criterion's own words (from the PRD version the review used) under
   its id, and each evidence pointer's code under the verdict's note. */
.criteria-table .criterion-text { margin-top: 0.2rem; color: var(--color-text-secondary); font-size: var(--text-sm); font-weight: normal; max-width: 32ch; }
table.data.criteria-table td { vertical-align: top; }
.code-snippet { margin-top: 0.6rem; }
.code-snippet summary { cursor: pointer; }
.code-snippet .snippet { margin-top: 0.25rem; }
.snippet-head, .finding-where { font-size: var(--text-sm); color: var(--color-text-secondary); overflow-wrap: anywhere; }
.facts-list li, .finding-issue, .finding-fix { overflow-wrap: anywhere; }
.snippet-head a { color: var(--color-text-secondary); }
/* A running review's last step, under "Reviewing…". */
.review-progress { margin-top: 0.35rem; color: var(--color-text-secondary); font-size: var(--text-sm); }
.angle-section + .angle-section { margin-top: var(--space-4); }

.rail-checklist { margin: 0; padding: 0; display: grid; gap: var(--space-2); list-style: none; }
.rail-checklist li { display: grid; grid-template-columns: 16px 1fr; gap: var(--space-2); font-size: var(--text-sm); align-items: start; }
.rail-checklist .box { width: 13px; height: 13px; margin-top: 3px; border: 1.5px solid var(--color-border-control); border-radius: 4px; }

.commit { margin-bottom: 0.5rem; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-sm); }
.commit > summary { padding: 0.55rem 0.8rem; cursor: pointer; font-size: var(--text-md); }
.commit[open] > summary { border-bottom: 1px solid var(--color-border); }

.context-fold { margin-bottom: 0.75rem; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); font-size: var(--text-sm); }
.context-fold > summary { padding: 0.6rem 0.9rem; cursor: pointer; font-weight: 600; }
.context-body { padding: 0 0.9rem 0.9rem; }

/* The re-review callout (task 6): why an attempt came back — a send-back or
   changes requested — accent-soft, matching .badge-active's accent family
   rather than the negative tone .worker-error-block uses (this is context,
   not a fault). */
.callout-back {
  border: 1px solid var(--color-attention-border);
  background: var(--state-needs-soft);
  color: var(--color-text);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-3);
  font-size: var(--text-md);
}
.callout-back p { margin: 0.4rem 0 0; }
.callout-back p:first-child { margin-top: 0; }
.callout-back .meta-label { margin-top: 0.6rem; }
.callout-findings { margin: 0.3rem 0 0; padding-left: 1.2rem; }
.callout-findings li { margin-bottom: 0.2rem; }

/* Settings: an SSH public key, one long word that must wrap. */
code.public-key { display: block; overflow-wrap: anywhere; font-size: var(--text-xs); max-width: 28rem; }

/* The agent page (agent.html) and a skill's pages: the main skill and a
   reference file shown as the file itself, and "Repositories as a list". */
.main-skill-file, .skill-file, .repo-list-text { white-space: pre-wrap; overflow-wrap: anywhere; font-family: var(--font-mono); font-size: var(--text-sm); }
.repo-list-text { width: 100%; }
.panel-tabs { margin: 0; padding: 0 1.1rem; }
.row-form { display: inline; margin-left: 0.5rem; }
.typed-decision { padding: 0.75rem 1rem; border: 1px dashed var(--color-border); border-radius: var(--radius-md); }
.typed-text { white-space: pre-wrap; overflow-wrap: anywhere; font-family: var(--font-mono); font-size: var(--text-sm); }
.approve-ready { margin-bottom: 1rem; }

/* Import from Git: the proposal page's items (agent_import.html). */
.import-change { border-top: 1px solid var(--color-border); padding: 0.75rem 0; }
.import-change-head { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: baseline; }
.import-file { margin: 0.5rem 0 0 1.5rem; }
.import-notes ul { margin: 0.25rem 0 0; }
.import-warning {
  border-left: 3px solid var(--color-attention-fg);
  background: var(--color-attention-bg);
  color: var(--color-attention-fg);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: 0.4rem 0.9rem;
  margin: 0.4rem 0;
}

/* ============================================================
   RUN PAGE — THE TO-DO LIST (2026-09-18 design, "The look"): the
   run-page rebuild's own regions — a right-hand rail holding the to-do
   list beside the map (§1, §4), the to-do list and stage notes, the
   requirement story, the stage band row, and the activity feed. §5:
   fewer boxes — only the map and the decision box are cards; everything
   below is a plain list on the page background.
   ============================================================ */

/* State tone: colour and background from the state tokens above, nothing
   else — the to-do tag, a story row's highlight, a band's progress fill
   and the feed all read one of these five. */
.tone-needs { color: var(--state-needs); background: var(--state-needs-soft); }
.tone-working { color: var(--state-working); background: var(--state-working-soft); }
.tone-done { color: var(--state-done); background: var(--state-done-soft); }
.tone-failing { color: var(--state-failing); background: var(--state-failing-soft); }
.tone-idle { color: var(--state-idle); background: var(--state-idle-soft); }

/* U1 — the action must be on the first screen: a right-hand rail on wide
   screens keeps the to-do list beside the map and the decision pinned to
   the rail's bottom, so a long-lived run's Approve button never falls
   below the fold; below 1100px there is one column with the action before
   the picture. The head, the map and the rail sit in .run-top; the
   selected stage's document (#doc) comes after it — never in the rail. */
.run-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; }

/* The document: below 1100px it follows the map (order 0, after the map in
   the markup, before the feed's 1); from 1100px it is the row under
   .run-top, the page's full width, so a diff or the numbered output reads
   at that width and the rail — sticky only within .run-top — never slides
   over it. With nothing to show it takes no room at all. */
.run-layout > .run-doc { min-width: 0; }
.run-doc:empty { display: none; }

/* No decision to make (a finished run, a stage no one has submitted): the
   empty aside draws no box, no rule and no gap. */

@media (max-width: 1099.98px) {
  /* One column: the head, the to-do list — its Needs-you group, then the
     decision, then the rest — the map, the document, then the feed.
     .run-top's and the rail's children are the grid's own here, so the map
     can sit between them; nothing is sticky. */
  .run-top, .run-rail { display: contents; }
  
  #activity { order: 1; }

}
@media (min-width: 1100px) {
  /* Two columns: the head and the map on the left; the rail beside both,
     from the top of the page, so the head never pushes it down. The rail
     scrolls on its own and keeps the decision pinned to its bottom (U1):
     the to-do list and the notes scroll under it. */
  .run-top { display: grid; grid-template-columns: minmax(0, 1fr) 380px; grid-template-rows: auto 1fr; gap: 24px; min-width: 0; }

  .run-rail { grid-column: 2; grid-row: 1 / span 2; display: flex; flex-direction: column; gap: 24px; min-width: 0;
    position: sticky; top: 16px; align-self: start; max-height: calc(100vh - 32px); overflow: auto; }
  
}
/* run:15 — between 1100 and 1439px the two-column layout above already
   applies, but the map's own readable floor (760/900/1140px below) can
   still clip against a narrower rail column: a narrower 320px rail buys
   the map room, and the readable floor is dropped so nothing is forced
   past the viewport — the map keeps scrolling sideways in its own box
   instead (.chain-scroll), never the page. At 1440px and up the rail
   widens back to 380px and the readable floors return. */
@media (min-width: 1100px) and (max-width: 1439.98px) {
  .run-top { grid-template-columns: minmax(0, 1fr) 320px; }
  
}

/* The to-do list: a plain list (§5), grouped by who acts (U6) — open items
   lead at full weight with a clear box; done items fade (§2). */

.todo-group h3 { font-size: var(--text-sm); line-height: 20px; font-weight: 600; color: var(--color-text-secondary); margin: 0 0 var(--space-2); }
/* "Questions for you" (task G1): a sub-heading inside the Needs you group,
   between its two lists. */
.todo-group h4 { font-size: var(--text-sm); color: var(--color-text-tertiary); margin: 0.75rem 0 0.5rem; }
.todo-group ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }

/* Nothing in the rail is wider than the rail: its words wrap anywhere, its
   lists indent inside it, and a preformatted block or a table scrolls in
   its own box. (Inherited through the rail's display: contents below
   1100px as well.) */
.run-rail { overflow-wrap: anywhere; }
.run-rail > * { min-width: 0; max-width: 100%; }
.run-rail .markdown-body ul, .run-rail .markdown-body ol { padding-left: 1.25rem; }
.run-rail pre { max-width: 100%; overflow-x: auto; white-space: pre-wrap; }
.run-rail table { display: block; max-width: 100%; overflow-x: auto; }
.run-rail img, .run-rail svg { max-width: 100%; }
.todo-item {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr) auto;
  align-items: start;
  gap: 0.6rem;
  padding: 0.4rem 0;
  font-size: var(--text-md);
}
.todo-item::before {
  content: "";
  box-sizing: border-box;
  width: 16px;
  height: 16px;
  margin-top: 0.15rem;
  border: 1.5px solid currentColor;
  border-radius: var(--radius-sm);
}
/* run:21 — a done item reads quiet, not struck out: struck text under-
   contrasts and reads like something removed, not something finished. */
.todo-item.is-done { color: var(--color-text-secondary); }
.todo-item.is-done::before { background: var(--state-done); border-color: var(--state-done); }

/* U3, U10: the stage band row replaces the stage tabs on a run with no
   chain too — drawn as a pipeline stepper (visual theme Task 9): a
   connected node per band (stepArt, viz.go's stepperOf), the same link,
   title and word every band always carried, centred underneath it. */

/* U12: the activity feed — grouped by attempt, newest 15, "Show all". */
.activity { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.6rem; }

/* A band's state in its own tone (the .band rule above sets the base). */

.status-line a { color: inherit; text-decoration: none; }
.status-line a:hover { text-decoration: underline; }

.todo-progress { color: var(--color-text-secondary); font-size: var(--text-sm); margin-left: 0.4rem; }

.todo-text { min-width: 0; color: var(--color-text); }
/* A Get started row's note and link travel together in the third column
   (portal-site-theme review: four grid items in three columns pushed the
   link into the 20px checkbox column, one letter per line). */
.todo-aside { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: baseline; gap: var(--space-1) var(--space-3); text-align: right; }
.todo-aside .field-hint { margin: 0; }
@media (max-width: 640px) {
  .todo-item:has(.todo-aside) { grid-template-columns: 20px minmax(0, 1fr); }
  .todo-item .todo-aside { grid-column: 2; justify-content: flex-start; text-align: left; }
}

/* run:21/reviews:134/151 — a marked to-do row reads decided (a left accent),
   never dimmed under-contrast. */

.todo-item[data-finding]:focus, .todo-item.is-current { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.todo-item .marks-seg { margin-top: 0.4rem; }
.todo-item .finding-note { width: 100%; margin-top: 0.4rem; }

/* The decisions in the notes: the title and the person's answer, the
   rejected alternatives folded. */

.doc-fold > summary { cursor: pointer; font-weight: 600; padding: 0.5rem 0; }
.doc-fold[open] > summary { margin-bottom: 0.5rem; }
.activity h2 { font-size: var(--text-lg); line-height: 24px; margin: 0 0 var(--space-3); }
.activity-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.6rem; }

/* run page rules */
/* A finding's rule chip (spec §9): the rule number only, linked to its own
   page — the same chip on the to-do list and the Findings pane. */
.rule-chip {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 500;
  padding: 0 6px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border-strong);
  color: var(--color-text-secondary);
  text-decoration: none;
  white-space: nowrap;
}
.rule-chip:hover, .rule-chip:focus-visible { color: var(--color-accent); border-color: var(--color-accent); }
.finding-top .rule-chip { margin-left: 0.4rem; }
/* Retired: still linked to its record, marked so, in a quieter tone. */
.rule-chip-retired { border-style: dashed; color: var(--color-text-tertiary); }
.rule-chip-retired:hover, .rule-chip-retired:focus-visible { color: var(--color-accent); border-color: var(--color-accent); }
/* Unknown: a citation naming a number the organisation has never used —
   never a link, and toned like the negative badges so it reads as a
   problem with the citation, not an ordinary chip. */
.rule-chip-unknown { cursor: help; background: var(--color-negative-bg); color: var(--color-negative-fg); border-color: var(--color-negative-border); }

/* A branch review's finding marks (/reviews/{id}, learning-reviewer slice 2):
   who marked a finding how, their note, the no-code notice above the
   findings, and the one Save marks form under them. */
.review-no-code { margin: 0 0 0.6rem; }
.review-mark { margin: 0.6rem 0 0; font-size: var(--text-sm); font-weight: 500; color: var(--color-text-secondary); }
.review-mark-note { margin: 0.35rem 0 0; padding-left: 0.75rem; border-left: 2px solid var(--color-border-strong); color: var(--color-text-secondary); font-size: var(--text-sm); white-space: pre-wrap; }
/* The review page's one decision block (p2 D10, review.html): the checklist,
   the comment and the verdict buttons in one rail card, whose form the
   finding cards' marks join. The buttons wrap, never overflow the rail. */
.review-decision .panel-body { display: grid; gap: var(--space-3); }
.review-decision-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.review-decision .verdict-history { padding-left: 0; list-style: none; display: grid; gap: var(--space-2); font-size: var(--text-sm); }
.review-details-h { margin: var(--space-4) 0 var(--space-2); font-size: var(--text-sm); }
/* "Review a branch" on Reviews: a link, not a primary button (p2 D12). */
/* Drawn as a secondary pill button (portal-site-theme review): it opens a
   form, so it reads as an action, not a stray link under the lists. */
.review-new > summary.review-new-link { display: inline-flex; align-items: center; height: var(--control-height); padding: 0 var(--space-4); list-style: none; cursor: pointer; border: 1px solid var(--color-border-control); border-radius: var(--radius-pill); background: var(--color-surface); color: var(--color-text); font-size: var(--text-sm); font-weight: 600; }
.review-new > summary.review-new-link::-webkit-details-marker { display: none; }
.review-new > summary.review-new-link:hover { background: var(--color-surface-sunken); }

/* "Make this a rule" (learning-reviewer slice 2, task E7): a receipt of a
   rule already suggested from a finding's mark note or a comment, or the
   link that drafts one — shown after the item's note, on the to-do list, a
   comment and a branch review's finding card alike. */
.rule-receipt, .make-rule { display: inline-block; margin-top: 0.2rem; font-size: var(--text-xs); }
.rule-receipt { color: var(--color-text-tertiary); }
.make-rule { color: var(--color-accent); }
.make-rule:hover, .make-rule:focus-visible { text-decoration: underline; }
.make-rule-heading { font-weight: 600; margin: 0 0 0.3rem; }

/* An agent's and a skill's history (learning-reviewer slice 3): each
   version's line with its revert form at the end of it, and what changed
   besides the text on its own lines above the diff. */
.history-version .history-revert { margin-left: auto; }
.history-change { margin: 0.35rem 0 0; color: var(--color-text-secondary); }

/* Impact preview (F3): a rule's or a suggestion's one line, how often its
   globs applied to the owner's recent changes. */
.impact { margin: 0.2rem 0 0; color: var(--color-text-tertiary); font-size: var(--text-sm); }

/* Settings › Integrations (task A4): the watch and mode forms are compact
   inline rows, not the stacked .field layout the rest of Settings uses —
   several sit one after another per installation. */
.watch-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.6rem;
  margin: 0 0 0.75rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: 6px;
}
.watch-form .field { margin: 0; }
.watch-form .row-title { font-weight: 600; margin-right: 0.4rem; align-self: center; }
.mode-form { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.mode-form select { width: auto; }
/* First-run model onboarding (task A8): a model endpoint's test result, and the
   provider preset list on the add form. */
.check-ok { color: var(--color-positive-fg); }
.check-warn { color: var(--color-attention-fg); }
.check-fail { color: var(--color-negative-fg); }
.preset-link {
  display: inline-block; padding: 0.2rem 0.6rem; border: 1px solid var(--color-border);
  border-radius: var(--radius-md); font-size: var(--text-sm); color: var(--color-text-secondary);
  text-decoration: none;
}
.preset-link:hover, .preset-link:focus-visible { background: var(--color-surface-sunken); color: var(--color-text); }

/* A Critical finding's badge (task R1, finding 2): Critical and Major both
   take severityTone's "negative" tone, so without this they render pixel
   for pixel identical apart from the word. This adds weight, not a new
   colour — no new badge tone. */
.badge-critical { font-weight: 600; box-shadow: inset 0 0 0 1px currentcolor; }

/* The review page (the 2026-09-23 design bar, §1.7) borrows the run page's
   .run-top / .run-rail grid so the facts, the way back to the change and
   Save marks sit on the first screen. Its left column is the findings, not
   a map: one class, the same two placements .run-map takes. */
.run-top > .review-main { min-width: 0; }
@media (min-width: 1100px) {
  .run-top > .review-main { grid-column: 1; grid-row: 2; }
}

/* A bare <h3> in a .panel-body used to render larger and heavier than the
   .panel-header <h2> above it, because the size rule only applies inside
   .panel-header — the hierarchy was inverted on the most-visited screen of
   the reviewer (the 2026-09-23 design bar, §1.3). This makes the mistake
   unrepeatable; a section inside a panel is a .section-label. */
.panel-body h3 { font-size: var(--text-md); }

/* A panel's own inset block: what a step has just found, set apart from the
   form that found it without being an error or a critique. Settings ›
   Integrations uses it for the checked GitLab token's group, handle, scopes
   and expiry — shown before anything is stored. */
.panel-inset {
  background: var(--color-surface-sunken);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 0.75rem 0.9rem;
  margin: 0.9rem 0;
}
.panel-inset > :first-child { margin-top: 0; }
.panel-inset > :last-child { margin-bottom: 0; }

/* MCP connections — Settings › Connections and the agent page's Tools. */
.conn-meta { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; margin: 0 0 var(--space-6); }
.conn-meta form { margin: 0; }
.meta-sep { color: var(--color-text-tertiary); margin: 0 var(--space-1); }
.test-result { display: flex; align-items: center; gap: var(--space-2); font-weight: 500; }
.tool-mark { color: var(--color-text-secondary); white-space: nowrap; }
.tool-desc { max-width: 60ch; }
.tool-names { color: var(--color-text-secondary); font-size: var(--text-sm); }
.tool-builtin { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; padding: 0 0 var(--space-3); margin-bottom: var(--space-3); border-bottom: 1px solid var(--color-border); }
.tool-builtin .tool-names { margin-left: auto; font-family: var(--font-mono); font-size: var(--text-xs); }
.tool-tick { margin-bottom: var(--space-2); }
.tool-tick .field-hint { margin: 0 0 0 1.5rem; }
.tool-ticks { padding: var(--space-3) 0 0; }
.nowrap { white-space: nowrap; }

/* ============================================================
   CHAT (portal.js's chatPanel; spec 2026-09-26-chat-design, redrawn as
   a popup in chat T8): a pill launcher bottom-right on every page, a
   380px floating card that overlays the page (never pushes it), and
   /chat's own two-column page drawing the same fragment. An 8px grid:
   12px panel padding, 12px between messages. Every colour is a shared
   token above, defined for light and redefined for dark.
   ============================================================ */

/* ---- the launcher ---- */
.chat-launcher {
  position: fixed;
  right: var(--space-4);
  bottom: var(--space-4);
  z-index: 40;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 44px;
  padding: 0 var(--space-5) 0 var(--space-4);
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-size: var(--text-md);
  font-weight: 500;
  box-shadow: var(--shadow-float);
}
.chat-launcher:hover { background: var(--color-accent-hover); text-decoration: none; }
.chat-launcher:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; }
.chat-launcher .chat-icon { width: 18px; height: 18px; }
.chat-launcher-dot {
  position: absolute;
  top: 2px;
  right: 4px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--state-needs);
  border: 2px solid var(--color-surface);
}
.chat-launcher-dot[hidden] { display: none; }
body.chat-open .chat-launcher { display: none; }
/* /chat is the chat already: no launcher over its own composer. */
body:has(.chat-page) .chat-launcher { display: none; }

/* ---- the popup: a floating card over the page ---- */
.chat-dock {
  position: fixed;
  right: var(--space-4);
  bottom: var(--space-4);
  z-index: 40;
  width: 380px;
  height: min(560px, calc(100vh - 96px));
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-float);
}
.chat-dock[hidden] { display: none; }
/* Under 768px the popup is a full-screen sheet. */
@media (max-width: 767px) {
  .chat-dock { inset: 0; width: auto; height: auto; border: 0; border-radius: 0; padding-bottom: env(safe-area-inset-bottom, 0px); }
  /* On a phone the launcher is a 44px icon-only circle (its words stay for
     a screen reader), lifted clear of the home bar. */
  .chat-launcher { right: var(--space-3); bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px)); width: 44px; height: 44px; padding: 0; justify-content: center; }
  .chat-launcher .chat-icon { width: 22px; height: 22px; }
  .chat-launcher-text { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
}
/* The open sheet holds the page still behind it (portal.js makes the page
   inert too). */
html.chat-sheet-open, html.chat-sheet-open body { overflow: hidden; }

.chat-icon { width: 16px; height: 16px; flex: none; }

/* ---- the fragment ---- */
.chat {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  background: var(--color-surface);
}
.chat:focus { outline: none; }
/* The header is a grid so the cluster chip, on its own row under the
   agent's name, can run under the actions too — the popup is only 380px:
   [back] [avatar] [name ............] [ring … – ×]
                   [chip ...................................] */
.chat-head {
  position: relative;
  z-index: 2;
  flex: none;
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto;
  align-items: center;
  min-height: 60px;
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-border);
}
.chat-head > .chat-back { grid-column: 1; grid-row: 1 / span 2; margin: 0 2px 0 calc(-1 * var(--space-1)); }
.chat-head > .chat-avatar { grid-column: 2; grid-row: 1 / span 2; margin-right: var(--space-2); }
.chat-head-main { display: contents; }
.chat-head-main > .chat-title { grid-column: 3; grid-row: 1; }
.chat-head-main > .chat-subtitle, .chat-head-main > .chat-cluster { grid-column: 3 / 5; grid-row: 2; justify-self: start; min-width: 0; max-width: 100%; }
.chat-head-main > .chat-cluster { margin-left: -6px; }
.chat-head-actions { grid-column: 4; grid-row: 1; }
/* A header with no second row (the start screen, the history list): the
   title and the actions centre on the avatar. */
.chat-head:not(:has(.chat-cluster, .chat-subtitle)) .chat-head-main > .chat-title,
.chat-head:not(:has(.chat-cluster, .chat-subtitle)) > .chat-head-actions { grid-row: 1 / span 2; align-self: center; }
.chat-title {
  max-width: 100%;
  font-size: var(--text-md);
  line-height: 20px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chat-subtitle {
  max-width: 100%;
  font-size: var(--text-xs);
  line-height: 16px;
  color: var(--color-text-tertiary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chat-head-actions { display: flex; align-items: center; gap: 0; margin-left: var(--space-2); }

.chat-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-text-secondary);
  cursor: pointer;
  list-style: none;
}
.chat-icon-btn::-webkit-details-marker { display: none; }
.chat-icon-btn:hover { background: var(--color-surface-sunken); color: var(--color-text); text-decoration: none; }
.chat-icon-btn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }
.chat-icon-btn .chat-icon { width: 18px; height: 18px; }

.chat-avatar {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--color-accent-soft);
  color: var(--color-accent);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
}
.chat-avatar .chat-icon { width: 18px; height: 18px; }
.chat-avatar-lg { width: 48px; height: 48px; font-size: var(--text-lg); }

/* ---- popovers: the "…" menu and the cluster chip's menu ---- */
.chat-menu, .chat-cluster { position: relative; }
.chat-menu > summary, .chat-cluster > summary { list-style: none; }
.chat-menu > summary::-webkit-details-marker, .chat-cluster > summary::-webkit-details-marker { display: none; }
.chat-menu[open] > summary { background: var(--color-surface-sunken); color: var(--color-text); }
.chat-pop {
  position: absolute;
  top: calc(100% + 6px);
  z-index: 5;
  min-width: 220px;
  max-width: 300px;
  padding: 6px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-pop);
}
.chat-menu-pop { right: 0; }
.chat-cluster-pop { left: -4px; }
.chat-pop form { margin: 0; }
.chat-pop-head { margin: 0; padding: 6px 8px 4px; font-size: var(--text-xs); line-height: 16px; font-weight: 500; color: var(--color-text-tertiary); }
.chat-pop-list { list-style: none; margin: 0; padding: 0; }
.chat-pop-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2);
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-text);
  font: inherit;
  font-size: var(--text-sm);
  line-height: 20px;
  text-align: left;
  cursor: pointer;
}
.chat-pop-item:hover { background: var(--color-surface-sunken); text-decoration: none; }
.chat-pop-item:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }
.chat-pop-item .chat-icon { color: var(--color-text-secondary); }
.chat-pop-item[aria-current] { font-weight: 500; }
.chat-pop-item[aria-current] .chat-icon { color: var(--color-accent); margin-left: auto; }
.chat-pop-item[disabled] { color: var(--color-text-tertiary); cursor: default; }
.chat-pop-item[disabled]:hover { background: transparent; }
.chat-pop-text { display: flex; flex-direction: column; min-width: 0; }
.chat-pop-sub { font-size: var(--text-xs); line-height: 16px; font-weight: 400; color: var(--color-text-tertiary); }

/* The cluster chip: the chat's one cluster, under the agent's name. */
.chat-cluster-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: 100%;
  min-height: 24px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  font-size: var(--text-xs);
  line-height: 16px;
  cursor: pointer;
}
.chat-cluster-chip:hover, .chat-cluster[open] > .chat-cluster-chip { background: var(--color-surface-sunken); color: var(--color-text); }
.chat-cluster-chip:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 0; }
.chat-cluster-chip .chat-icon { width: 13px; height: 13px; flex: none; }
/* The chip's dot: Production red, as its badge, another environment
   grey, Any cluster an empty ring. */
.chat-cluster-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--state-idle); }
.chat-cluster-dot[data-env="production"] { background: var(--state-failing); }
.chat-cluster-dot[data-env=""] { background: transparent; box-shadow: inset 0 0 0 1.5px var(--state-idle); }
.chat-cluster-label { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-cluster-env { color: var(--color-text-tertiary); white-space: nowrap; }
.chat-cluster-static .chat-cluster-chip { cursor: default; }
.chat-cluster-static .chat-cluster-chip:hover { background: transparent; color: var(--color-text-secondary); }
/* Production, said in words and the danger colour wherever a cluster is
   named: the chip, its menu and the change card's target. */
.env-badge {
  display: inline-flex;
  align-items: center;
  flex: none;
  margin-left: var(--space-1);
  padding: 0 var(--space-1);
  border: 1px solid color-mix(in srgb, var(--state-failing) 40%, transparent);
  border-radius: var(--radius-sm);
  background: var(--state-failing-soft);
  color: var(--state-failing);
  font-size: var(--text-2xs);
  line-height: 16px;
  font-weight: 600;
  white-space: nowrap;
}
.chat-pop-item .chat-cluster-dot { margin: 0 2px; }
.chat-pop-item .chat-pop-text .env-badge { align-self: flex-start; margin: 2px 0 0; }

/* ---- the context ring (R4): tenths by class, tone by data-tone ---- */
/* The ring's hover hangs from the header's own bottom edge (the ring is
   not positioned), so it never covers the cluster chip under the name. */
.ctx-ring {
  position: static;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 32px;
  padding: 0 3px;
  border-radius: var(--radius-md);
  color: var(--color-text-secondary);
  font-size: var(--text-xs);
}
.ctx-ring:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }
.ctx-ring-svg { display: block; width: 26px; height: 26px; flex: none; }
.ctx-ring-track, .ctx-ring-fill { fill: none; stroke-width: 3; }
.ctx-ring-track { stroke: var(--color-border); }
.ctx-ring-fill { stroke: var(--color-accent); stroke-dasharray: 0 100; }
.ctx-ring-num { fill: var(--color-text-secondary); font-size: var(--text-xs); font-weight: 600; font-variant-numeric: tabular-nums; }
.ctx-ring[data-tone="amber"] .ctx-ring-num { fill: var(--state-needs); }
.ctx-ring[data-tone="red"] .ctx-ring-num { fill: var(--state-failing); }
.ctx-ring[data-tone="amber"] .ctx-ring-fill { stroke: var(--state-needs); }
.ctx-ring[data-tone="red"] .ctx-ring-fill { stroke: var(--state-failing); }
.ctx-ring-fill.ctx-p0 { stroke-dasharray: 0 100; }
.ctx-ring-fill.ctx-p1 { stroke-dasharray: 10 100; }
.ctx-ring-fill.ctx-p2 { stroke-dasharray: 20 100; }
.ctx-ring-fill.ctx-p3 { stroke-dasharray: 30 100; }
.ctx-ring-fill.ctx-p4 { stroke-dasharray: 40 100; }
.ctx-ring-fill.ctx-p5 { stroke-dasharray: 50 100; }
.ctx-ring-fill.ctx-p6 { stroke-dasharray: 60 100; }
.ctx-ring-fill.ctx-p7 { stroke-dasharray: 70 100; }
.ctx-ring-fill.ctx-p8 { stroke-dasharray: 80 100; }
.ctx-ring-fill.ctx-p9 { stroke-dasharray: 90 100; }
.ctx-ring-fill.ctx-p10 { stroke-dasharray: 100 100; }
.ctx-hover {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  right: var(--space-2);
  z-index: 5;
  width: 260px;
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  color: var(--color-text-secondary);
  box-shadow: var(--shadow-pop);
  font-size: var(--text-xs);
  line-height: 18px;
  white-space: normal;
  pointer-events: none;
}
.ctx-ring:hover .ctx-hover, .ctx-ring:focus-within .ctx-hover { display: block; }
/* Escape dismisses the breakdown and keeps focus on the ring. */
.ctx-ring[data-dismissed] .ctx-hover { display: none; }
.ctx-hover-title { margin: 0 0 var(--space-1); font-size: var(--text-sm); font-weight: 500; color: var(--color-text); }
.ctx-hover-line { margin: 0; }

/* ---- a body that scrolls: start screen, empty chat, history ---- */
.chat-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--space-4) var(--space-3);
}
.chat-start-title { margin: 0 var(--space-1) var(--space-3); font-size: var(--text-lg); line-height: 24px; font-weight: 600; }
.chat-agents { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.chat-agent-card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  color: var(--color-text);
}
.chat-agent-card:hover { border-color: var(--color-border-strong); background: var(--color-surface-sunken); text-decoration: none; }
.chat-agent-card:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }
.chat-agent-card > .chat-icon { color: var(--color-text-tertiary); }
.chat-agent-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.chat-agent-name { font-size: var(--text-md); line-height: 20px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-agent-line { font-size: var(--text-xs); line-height: 16px; color: var(--color-text-secondary); }
.chat-start-sub { margin: var(--space-5) var(--space-1) var(--space-1); font-size: var(--text-xs); line-height: 16px; font-weight: 600; color: var(--color-text-secondary); }
.chat-recent { list-style: none; margin: 0; padding: 0; }
.chat-recent-item {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  padding: var(--space-2);
  border-radius: var(--radius-md);
  color: var(--color-text);
  font-size: var(--text-sm);
  line-height: 20px;
}
.chat-recent-item:hover { background: var(--color-surface-sunken); text-decoration: none; }
.chat-recent-title { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-recent-age { flex: none; font-size: var(--text-xs); color: var(--color-text-tertiary); }
.chat-see-all { margin: var(--space-2) var(--space-2) 0; font-size: var(--text-sm); }

.chat-empty { display: flex; flex-direction: column; align-items: center; text-align: center; padding-top: var(--space-8); }
.chat-empty-name { margin: var(--space-3) 0 var(--space-1); font-size: var(--text-lg); line-height: 24px; font-weight: 600; }
.chat-empty-hint { max-width: 32ch; margin: 0 0 var(--space-5); font-size: var(--text-sm); line-height: 20px; color: var(--color-text-secondary); }
.chat-suggestions { list-style: none; margin: 0; padding: 0; width: 100%; display: flex; flex-direction: column; gap: var(--space-2); }
.chat-suggestion {
  display: block;
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  color: var(--color-text);
  font-size: var(--text-sm);
  line-height: 20px;
  text-align: left;
}
.chat-suggestion:hover { border-color: var(--color-accent); background: var(--color-accent-soft); text-decoration: none; }

/* ---- the thread ---- */
.chat-thread {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-4) var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  overscroll-behavior: contain;
}
/* A short thread sits at the bottom, by the composer, as a chat does. */
.chat-thread > :first-child { margin-top: auto; }
.chat-group { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.chat-group-person { align-items: flex-end; }
.chat-group-agent { align-items: flex-start; }
.chat-msg {
  max-width: 88%;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-md);
  line-height: 1.5;
  color: var(--color-text);
  overflow-wrap: anywhere;
}
.chat-person { max-width: 80%; background: var(--color-accent-soft); border-radius: var(--radius-lg) var(--radius-lg) var(--radius-sm) var(--radius-lg); }
.chat-agent { background: var(--color-surface-sunken); border-radius: var(--radius-lg) var(--radius-lg) var(--radius-lg) var(--radius-sm); }
.chat-failed { background: var(--state-failing-soft); }
.chat-text { margin: 0; white-space: pre-wrap; }
.chat-meta { margin: 0; padding: 0 var(--space-1); font-size: var(--text-2xs); line-height: 16px; color: var(--color-text-tertiary); }
.chat-retry { margin: 0; }
.chat-stopped { margin: 0; padding: 0 var(--space-1); font-size: var(--text-xs); color: var(--state-needs); }

/* ---- a goal's card in a chat (agent sessions): head, plan, budget, the
   question it waits on; its live parts sit in .chat-live ---- */
.goal-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}
.chat-live .goal-card { margin: var(--space-2) 0 0; }
.goal-card[data-state="waiting"] { border-color: var(--color-attention-border); }
.goal-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0; }
.goal-title { overflow-wrap: anywhere; }
.goal-now { margin: 0; font-size: var(--text-sm); color: var(--color-text); }
.goal-plan { margin: 0; padding-left: var(--space-5); font-size: var(--text-sm); color: var(--color-text-secondary); }
.goal-plan li[data-status="doing"] { color: var(--color-text); font-weight: 600; }
.goal-plan li[data-status="done"] { color: var(--color-text-tertiary); }
.goal-plan li[data-status="dropped"] { color: var(--color-text-tertiary); text-decoration: line-through; }
.goal-helpers { margin: 0; padding-left: var(--space-5); font-size: var(--text-sm); color: var(--color-text-secondary); }
.goal-helpers .goal-helpers { margin-top: var(--space-1); }
.goal-helpers details { margin-top: var(--space-1); }
.goal-helper-n { color: var(--color-text-tertiary); }
.goal-budget { margin: 0; font-size: var(--text-xs); line-height: 16px; color: var(--color-text-tertiary); }
.goal-question { margin: 0; padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); background: var(--color-attention-bg); color: var(--color-text); overflow-wrap: anywhere; }
/* the approval card inside a goal's card, and the decided actions' lines */
.action-card { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-3); border: 1px solid var(--color-attention-border); border-radius: var(--radius-sm); background: var(--color-surface); min-width: 0; }
.action-args { margin: 0; max-height: 240px; overflow: auto; padding: var(--space-2); border-radius: var(--radius-sm); background: var(--color-bg); font-size: var(--text-xs); white-space: pre; }
.action-meta { margin: 0; font-size: var(--text-xs); line-height: 16px; color: var(--color-text-tertiary); }
.action-form { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; margin: 0; }
.action-form input[type="text"] { flex: 1 1 160px; min-width: 0; }
.action-line { font-size: var(--text-sm); color: var(--color-text-secondary); overflow-wrap: anywhere; }
.goal-actions { display: flex; gap: var(--space-2); }
.goal-actions form { margin: 0; }
.chat-compose .chat-goal-start { margin-top: var(--space-2); }
.chat-unchecked {
  margin: 0 0 var(--space-2);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--color-attention-bg);
  color: var(--color-attention-fg);
  font-size: var(--text-xs);
}

/* A reply's markdown, sized for a bubble. */
.markdown-body.chat-md { max-width: none; font-size: var(--text-md); line-height: 1.5; }
.markdown-body.chat-md > :first-child { margin-top: 0; }
.markdown-body.chat-md > :last-child { margin-bottom: 0; }
.markdown-body.chat-md p { margin: 0 0 var(--space-2); }
.markdown-body.chat-md h1, .markdown-body.chat-md h2, .markdown-body.chat-md h3, .markdown-body.chat-md h4 {
  margin: var(--space-3) 0 var(--space-1);
  font-size: var(--text-md);
  line-height: 20px;
  font-weight: 600;
}
.markdown-body.chat-md ul, .markdown-body.chat-md ol { margin: 0 0 var(--space-2); padding-left: 20px; }
.markdown-body.chat-md li { margin: 2px 0; }
.markdown-body.chat-md li > p { margin: 0; }
.markdown-body.chat-md code {
  padding: 1px 5px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  font-size: 0.86em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.markdown-body.chat-md pre {
  margin: 0 0 var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  overflow-x: auto;
  font-size: var(--text-xs);
  line-height: 1.5;
}
.markdown-body.chat-md pre code { padding: 0; border: 0; background: none; font-size: inherit; }
.chat-section { margin: var(--space-2) 0 var(--space-1); font-size: var(--text-xs); line-height: 16px; font-weight: 600; color: var(--color-text-secondary); }
.chat-checked { margin-top: var(--space-2); padding-top: var(--space-1); border-top: 1px solid var(--color-border); }
.chat-checked .markdown-body.chat-md { font-size: var(--text-sm); color: var(--color-text-secondary); }

/* The change card: the target, each field before → after, the command to
   run (Copy) and the patch folded — as wide as the reply it belongs to. */
.chat-card {
  align-self: flex-start;
  width: 100%;
  max-width: 88%;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}
.chat-main-pane .chat-card { max-width: min(88%, 720px); }
.chat-card-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-sm);
  line-height: 20px;
}
.chat-card-head > .chat-icon:first-child { color: var(--color-accent); }
.chat-card-title { font-weight: 500; }
.chat-card-sub { font-size: var(--text-xs); color: var(--color-text-tertiary); }
.chat-card-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
}
.chat-card-target { margin: 0; font-size: var(--text-sm); line-height: 20px; overflow-wrap: anywhere; }
.chat-card-k { margin-right: var(--space-1); font-size: var(--text-xs); font-weight: 600; color: var(--color-text-secondary); }
.chat-card-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--text-sm); line-height: 20px; }
.chat-card-rows li { overflow-wrap: anywhere; }
.chat-card-field { font-size: var(--text-xs); }
.chat-card-before { color: var(--color-text-secondary); text-decoration: line-through; }
.chat-card-arrow { color: var(--color-text-tertiary); }
.chat-card-after { font-weight: 600; }
.chat-card-warn, .chat-card-missing {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--state-needs-soft);
  color: var(--state-needs);
  font-size: var(--text-xs);
  line-height: 18px;
}
.chat-card-cmd .chat-section { margin-top: 0; }
/* The command is read before it is run: it wraps rather than hides. */
.chat-card-cmd pre { white-space: pre-wrap; overflow-wrap: anywhere; }
.chat-card-patch > summary { display: inline-flex; align-items: center; gap: var(--space-1); cursor: pointer; list-style: none; font-size: var(--text-xs); font-weight: 600; color: var(--color-text-secondary); }
.chat-card-patch > summary::-webkit-details-marker { display: none; }
.chat-card-patch > summary .chat-icon { width: 14px; height: 14px; transition: transform 120ms ease; }
.chat-card-patch[open] > summary .chat-icon { transform: rotate(90deg); }
.chat-card-hint { margin: var(--space-1) 0 0; font-size: var(--text-xs); line-height: 16px; color: var(--color-text-tertiary); }
.chat-card-patch > .chat-code { margin-top: var(--space-2); }
.chat-risks-label { color: var(--state-needs); }
.chat-code { position: relative; }
.chat-code pre {
  margin: 0;
  padding: var(--space-3);
  padding-right: 64px;
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
  overflow-x: auto;
  font-size: var(--text-xs);
  line-height: 1.5;
}
.chat-copy {
  position: absolute;
  top: 6px;
  right: 6px;
  height: 24px;
  padding: 0 var(--space-2);
  border: 1px solid var(--color-border-control);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  font-size: var(--text-xs);
  font-weight: 500;
  cursor: pointer;
}
.chat-copy:hover { background: var(--color-surface-sunken); }
.chat-card-body .typed-text { margin: 0; font-family: var(--font-sans); font-size: var(--text-sm); line-height: 20px; }
.chat-risks .markdown-body.chat-md { font-size: var(--text-sm); }

/* What it read: one quiet folded line under a reply. */
.chat-reads { max-width: 88%; font-size: var(--text-xs); line-height: 16px; color: var(--color-text-tertiary); }
.chat-reads > summary {
  display: inline-flex;
  align-items: flex-start;
  gap: 6px;
  padding: 2px var(--space-1);
  border-radius: var(--radius-sm);
  cursor: pointer;
  list-style: none;
}
.chat-reads > summary::-webkit-details-marker { display: none; }
.chat-reads > summary:hover { color: var(--color-text-secondary); background: var(--color-surface-sunken); }
.chat-reads > summary .chat-icon { width: 14px; height: 14px; margin-top: 1px; }
.chat-reads-list {
  margin: var(--space-1) 0 0;
  padding: var(--space-2) var(--space-3) var(--space-2) 28px;
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}
.chat-reads-list code { font-size: var(--text-2xs); }

/* The quiet lines across the thread: a cluster change, a summary. */
.chat-note-line, .chat-divider > summary {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-1) 0;
  font-size: var(--text-xs);
  line-height: 16px;
  color: var(--color-text-tertiary);
  text-align: center;
}
.chat-note-line::before, .chat-note-line::after,
.chat-divider > summary::before, .chat-divider > summary::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--color-border);
}
.chat-divider { align-self: stretch; }
.chat-divider > summary { cursor: pointer; list-style: none; }
.chat-divider > summary::-webkit-details-marker { display: none; }
.chat-divider > summary:hover { color: var(--color-text-secondary); }
/* The summary is the portal's, not the agent's: a left rule on no fill,
   labelled, never a bubble. */
.chat-summary {
  margin: var(--space-2) 0 0;
  padding: var(--space-1) 0 var(--space-1) var(--space-3);
  border-left: 2px solid var(--color-border-strong);
}
.chat-summary-label { margin: 0 0 var(--space-1); font-size: var(--text-2xs); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-text-tertiary); }
.chat-summary .markdown-body { font-size: var(--text-sm); color: var(--color-text-secondary); }
.chat-divider-opened { display: none; }
.chat-divider[open] .chat-divider-closed { display: none; }
.chat-divider[open] .chat-divider-opened { display: inline; }
/* A failed summary is history here, a quiet line; its outcome is said
   by the composer when a person asked for it. */
.chat-divider-failed {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin: var(--space-1) 0;
  color: var(--color-text-tertiary);
  font-size: var(--text-xs);
  line-height: 16px;
  text-align: center;
}
.chat-divider-failed form { margin: 0; }

/* ---- live: one muted line with a spinner while it works ---- */
.chat-live { flex: none; padding: 0 var(--space-3); }
.chat-progress {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: 6px 0;
  font-size: var(--text-xs);
  line-height: 16px;
  color: var(--color-text-secondary);
}
.chat-compacting { flex: none; padding: 6px var(--space-3) 0; }
.chat-spinner {
  flex: none;
  width: 12px;
  height: 12px;
  border: 2px solid var(--color-border-strong);
  border-top-color: var(--state-working);
  border-radius: 50%;
  animation: chat-spin 0.8s linear infinite;
}
@keyframes chat-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .chat-spinner { animation-duration: 2.4s; }
}
/* A failure notice stops the spinner: nothing is being followed. */
.chat[data-stalled] .chat-live .chat-progress { display: none; }
.ctx-note { margin: 0; padding: 6px 0 0; font-size: var(--text-xs); line-height: 16px; color: var(--color-text-secondary); }
.chat-live[data-tone="amber"] .ctx-note { color: var(--state-needs); }
.chat-live[data-tone="red"] .ctx-note { color: var(--state-failing); }

/* ---- the composer: a rounded box, a growing textarea, a round send ---- */
.chat-composer { flex: none; padding: var(--space-2) var(--space-3) var(--space-3); background: var(--color-surface); }
.chat-composer .form-error { margin: 0 0 var(--space-2); }
/* A failure, said by the composer: what happened and the one way on. */
.chat-notice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--state-needs-soft);
  color: var(--state-needs);
  font-size: var(--text-xs);
  line-height: 18px;
}
.chat-notice[hidden] { display: none; }
.chat-notice form { margin: 0; }
.chat-notice-text { flex: 1 1 12rem; margin: 0; }
.chat-notice .btn[hidden] { display: none; }
.chat-visibility { margin: var(--space-4) var(--space-1) 0; font-size: var(--text-xs); line-height: 16px; color: var(--color-text-tertiary); }
.chat-composer .refusal-note { margin: 0; padding: var(--space-2) var(--space-1); }
.chat-compose { margin: 0; }
.chat-compose-box {
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-1) var(--space-1) var(--space-4);
  border: 1px solid var(--color-border-control);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
}
.chat-compose-box:hover { border-color: var(--color-border-control-hover); }
.chat-compose-box:focus-within {
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 22%, transparent);
}
.chat-compose-box textarea {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 20px;
  max-height: 128px;
  margin: 0;
  padding: 6px 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  color: var(--color-text);
  font: inherit;
  font-size: var(--text-md);
  line-height: 20px;
  resize: none;
  overflow-y: auto;
  field-sizing: content;
}
.chat-compose-box textarea:focus, .chat-compose-box textarea:hover { border: 0; box-shadow: none; outline: none; }
.chat-compose-box textarea::placeholder { color: var(--color-text-tertiary); }
.chat-compose-box textarea[disabled] { background: transparent; }
.chat-send {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--color-accent);
  color: var(--color-on-accent);
  cursor: pointer;
}
.chat-send:hover { background: var(--color-accent-hover); }
.chat-send:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
/* One resting state everywhere: dim until there is something to send. */
.chat-send[disabled],
.chat-compose-box:has(textarea:placeholder-shown) .chat-send { background: var(--color-border-strong); color: var(--color-surface); cursor: default; }
.chat-send .chat-icon { width: 18px; height: 18px; }

/* ---- the chat list: /chat's left column, and the popup's history ---- */
.chat-list { display: flex; flex-direction: column; gap: var(--space-3); }
.chat-list-top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.chat-seg { display: inline-flex; margin: 0; padding: 2px; border-radius: var(--radius-md); background: var(--color-surface-sunken); }
.chat-seg > a, .chat-seg > span { padding: 2px var(--space-3); border-radius: var(--radius-sm); font-size: var(--text-xs); line-height: 18px; font-weight: 500; color: var(--color-text-secondary); }
.chat-seg > a:hover { color: var(--color-text); text-decoration: none; }
.chat-seg-on { background: var(--color-surface); color: var(--color-text); box-shadow: var(--shadow-control); }
.chat-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.chat-row {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
}
.chat-row:hover { background: var(--color-surface-sunken); }
.chat-row-current, .chat-row-current:hover { background: var(--color-accent-soft); }
.chat-row > a { color: var(--color-text); font-size: var(--text-sm); line-height: 20px; font-weight: 500; overflow-wrap: anywhere; }
.chat-row > a::after { content: ""; position: absolute; inset: 0; border-radius: var(--radius-md); }
.chat-row > a:hover { text-decoration: none; }
.chat-row > a:focus-visible { outline: none; }
.chat-row > a:focus-visible::after { outline: 2px solid var(--color-accent); outline-offset: -2px; }
.chat-row-meta { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); font-size: var(--text-xs); line-height: 16px; color: var(--color-text-tertiary); }
.chat-row-meta > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- /chat: two columns, the list left and the chat right ---- */
.chat-page { display: grid; grid-template-columns: 280px minmax(0, 1fr); gap: var(--space-5); align-items: start; }
.chat-list-pane { position: sticky; top: var(--space-4); max-height: calc(100vh - 140px); overflow-y: auto; }
.chat-main-pane { min-width: 0; }
.chat-main-pane > .chat {
  height: calc(100vh - 150px);
  min-height: 480px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.chat-main-pane .chat-thread, .chat-main-pane .chat-live, .chat-main-pane .chat-composer, .chat-main-pane .chat-body {
  padding-left: var(--space-5);
  padding-right: var(--space-5);
}
.chat-main-pane .chat-msg { max-width: min(88%, 720px); }
.chat-main-pane .chat-person { max-width: min(80%, 640px); }
@media (max-width: 960px) {
  .chat-page { grid-template-columns: minmax(0, 1fr); }
  .chat-list-pane { position: static; max-height: none; }
  .chat-main-pane { order: -1; }
  .chat-main-pane > .chat { height: 75vh; min-height: 420px; }
}

/* Never wider than the viewport at phone width (375px). */
@media (max-width: 767px) {
  .chat, .chat-thread, .chat-composer { max-width: 100vw; }
  /* 16px stops iOS zooming on focus; every control a 44px target. */
  .chat-compose-box textarea { font-size: 16px; line-height: 22px; }
  .chat-icon-btn, .chat-send { width: 44px; height: 44px; }
  /* The chip keeps its small look; its hit area is 44px tall. */
  .chat-cluster-chip { position: relative; min-height: 28px; }
  .chat-cluster-chip::after { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 44px; transform: translateY(-50%); }
  .chat-compose-box textarea { padding: 10px 0; }
  .chat-pop-item { min-height: 44px; }
  .chat-main-pane .chat-thread, .chat-main-pane .chat-live, .chat-main-pane .chat-composer, .chat-main-pane .chat-body {
    padding-left: var(--space-3);
    padding-right: var(--space-3);
  }
}

/* ==== agent page: head and tabs (T1) ==== */
.agent-tabs { overflow-x: auto; }

/* ==== agent page: map (T5) ==== */
.agent-map-scroll { overflow-x: auto; margin: 0 0 1rem; }
svg.agent-map { display: block; width: 100%; min-width: 760px; height: auto; font-family: var(--font-sans); }
svg.agent-map rect { stroke-width: 1.2; }
svg.agent-map text { fill: var(--color-text); font-size: var(--text-xs); }
svg.agent-map .map-name { font-weight: 600; font-size: var(--text-sm); }
svg.agent-map .map-fact { fill: var(--color-text-secondary); }
svg.agent-map .map-lane-title { fill: var(--color-text-tertiary); font-size: var(--text-xs); font-weight: 500; }
svg.agent-map .map-legend { fill: var(--color-text-tertiary); font-size: var(--text-xs); font-style: italic; }
svg.agent-map .map-badge-text { fill: var(--color-text-tertiary); font-size: var(--text-xs); text-anchor: end; }
svg.agent-map .agent-node rect { fill: var(--color-surface); stroke: var(--color-border-control); stroke-width: 1.6; }
svg.agent-map .lane-block rect { fill: var(--state-idle-soft); stroke: var(--state-idle); }
svg.agent-map .lane-block.tone-needs rect { fill: var(--state-needs-soft); stroke: var(--state-needs); }
svg.agent-map .lane-block.tone-working rect { fill: var(--state-working-soft); stroke: var(--state-working); }
svg.agent-map .lane-block.tone-done rect { fill: var(--state-done-soft); stroke: var(--state-done); }
svg.agent-map .lane-block.tone-failing rect { fill: var(--state-failing-soft); stroke: var(--state-failing); }
svg.agent-map .lane-block.tone-idle rect { fill: var(--state-idle-soft); stroke: var(--state-idle); }
svg.agent-map .lane-block.is-dashed rect { stroke-dasharray: 5 4; }
svg.agent-map .lane-block.is-add rect { fill: none; }
svg.agent-map .lane-muted { opacity: 0.55; }
svg.agent-map .edge-live { fill: none; stroke: var(--color-border-control); stroke-width: 1.5; }
svg.agent-map .edge-pending { fill: none; stroke: var(--color-border-control); stroke-width: 1.2; stroke-dasharray: 5 4; }
svg.agent-map a { cursor: pointer; }
svg.agent-map a:hover .lane-block rect, svg.agent-map a:hover .agent-node rect { stroke-width: 2; }
svg.agent-map a:focus { outline: none; }
svg.agent-map a:focus-visible .lane-block rect, svg.agent-map a:focus-visible .agent-node rect {
  stroke-width: 3;
  stroke: var(--color-accent);
}
.agent-map-list { display: none; }
.agent-map-list summary { cursor: pointer; }
/* The disclosure triangle is the summary's own marker; its heading must
   stay inline content so the two sit on one line (a block h3 pushes the
   title to its own line below the marker — browser check, 375px). */
.agent-map-list h3 { display: inline; font-size: var(--text-sm); margin: 0; }
.agent-map-list ul { margin: var(--space-2) 0 var(--space-3); padding-left: var(--space-4); }
@media (max-width: 719.98px) {
  .agent-map-scroll { display: none; }
  .agent-map-list { display: block; }
}

/* ==== agent page: overview (T9) ==== */
/* Reuses .run-top / .run-rail (run.html's own grid, portal.css ~1746-1810):
   at ≥1100px .run-top is a two-column grid, minmax(0,1fr) 380px. The map
   needs the page's full width to fit the 1140-unit map with no sideways
   scroll (browser check, 1280px: the main column alone is only ~600px
   there, well under the map's 760px floor) — so unlike the run page's own
   .run-map, the overview's map is its own row spanning BOTH columns,
   first under the head (browser check, round 2: below the rail it fell off
   the first screen and left a gap beside the rail); the main column and the
   rail share the row under it. The markup keeps the rail first, so keyboard
   and screen-reader order still reach Needs you before the map (N-2). */
@media (min-width: 1100px) {
  .agent-overview-top { grid-template-rows: auto auto; }
  .run-top > .agent-overview-map { grid-column: 1 / -1; grid-row: 1; min-width: 0; }
  .run-top > .agent-overview-main { grid-column: 1; grid-row: 2; min-width: 0; }
  .run-top > .run-rail.agent-overview-rail { grid-row: 2; position: static; max-height: none; }
}
/* N-2: below 1100px the overview is one column in source order (the rail
   — Needs you first — then the main column, then the map): .run-top and
   .run-rail are already `display: contents` there (portal.css ~1765), and
   .agent-overview-main is too, so every section is the .run-layout grid's
   own child in the order the markup gives. No `order` rule: keyboard and
   screen-reader order match what is seen. */
@media (max-width: 1099.98px) {
  .agent-overview-main { display: contents; }
}
.agent-overview-rail > * + * { margin-top: 0; }
.needs-list li,
.setup-list li,
.cluster-list li { margin: 0 0 var(--space-2); }
.setup-list li.is-done { color: var(--color-text-tertiary); }
.agent-needs-waiting summary { cursor: pointer; color: var(--color-text-secondary); }
.needs-fix-form { display: inline; }
.needs-fix-form .btn { height: auto; padding: 0; border: none; background: none; box-shadow: none; color: var(--color-accent); text-decoration: underline; }
.needs-fix-form .btn:hover { background: none; color: var(--color-accent-hover); }
.agent-where-used { margin-top: var(--space-4); }

/* ==== agent page: know-how, settings (T10) ==== */
.nowrap-name { white-space: nowrap; }

/* ==== agent page: repositories (T11) ==== */
.repo-status-filters { display: flex; flex-wrap: wrap; gap: 0.3rem 1rem; margin: 0.7rem 0; font-size: var(--text-sm); }
.repo-status-filters a { color: var(--color-text-secondary); text-decoration: none; }
.repo-status-filters a:hover { color: var(--color-text); text-decoration: underline; }
.repo-status-filters a[aria-current="page"] { color: var(--color-text); font-weight: 600; }

/* ==== agent page: import from Git, its summary + action up top (T7,
   design-fixes agents:83) ==== */
.import-summary { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin: var(--space-3) 0; color: var(--color-text-secondary); }

/* ==== agent page: connectors (T12) ==== */
.conn-removed-flash { padding: var(--space-3); border-radius: var(--radius-md); background: var(--color-surface-sunken); color: var(--color-text-secondary); }
.conn-actions { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-2); }
.conn-actions form { margin: 0; }
.conn-overflow summary, .change-tools summary { cursor: pointer; }
.row-detail td { background: var(--color-surface-sunken); padding: var(--space-3); }
.row-detail form { margin-top: var(--space-2); }
.conn-add-actions { display: flex; gap: var(--space-2); align-items: center; justify-content: flex-end; flex-wrap: wrap; }
.conn-add-actions form { margin: 0; }

/* ==== agent page: connection decision (T13) ==== */
.conn-stepper { color: var(--color-text-secondary); font-size: var(--text-sm); margin: 0 0 var(--space-3); }
.conn-stepper strong { color: var(--color-text); }
.conn-step-sep { color: var(--color-text-tertiary); }
.conn-give-form { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.conn-give-form select { width: 16rem; max-width: 100%; }

/* ==== agent page: rules (T14) ==== */

/* ==== agent page: phone header (T15) ====
   The sidebar's nav renders twice (AP-R8): the desktop copy (.nav-desktop)
   generates no box of its own (display: contents), so it slots straight
   into .sidebar's own existing flex layout unchanged at every width down to
   768px, including the ≤860px top-bar rule above; the phone copy
   (.nav-menu, a native <details>) is hidden until then. display: none takes
   the hidden copy out of the accessibility tree, so each viewport keeps
   exactly one menu, and no JavaScript is needed either way. */
.nav-desktop { display: contents; }
.nav-menu { display: none; }
.nav-menu > summary { cursor: pointer; font-weight: 600; padding: var(--space-2); list-style: none; }
.nav-menu > summary::-webkit-details-marker { display: none; }
@media (max-width: 767px) {
  .nav-desktop { display: none; }
  .nav-menu { display: block; flex: 1 1 100%; }
  /* A phone's menu can grow tall when opened; sticking it to the top would
     crowd the page under it, so below 768px the sidebar scrolls with the
     page like any other header. */
  .sidebar { position: static; }
}
/* A jump link (an anchor such as #to-decide, #rules, #repositories) never
   lands its target flush under a sticky header, on any page, at any
   width. */
[id] { scroll-margin-top: var(--space-4); }

/* Add a cluster (K3-K7, K-R22): the recommended server/kubectl commands and
   the ClusterRole YAML are shown verbatim, unwrapped, for copying — a long
   line (the can-i check, the binding commands) must scroll inside its own
   box, never widen the page (browser check, 375px). */
.cmd-block { overflow-x: auto; max-width: 100%; }
.cmd-block pre { margin: 0; }

/* ============================================================
   GRAPHICS (visual theme §3) — partials_viz.html's inline SVG, built
   from viz.go's views the same way the chain map already is: no
   script, no template.HTML, every decorative
   <svg> aria-hidden with the fact stated in words beside it. Colors
   are tokens only; a tone/kind/level/hue class is the dynamic part of
   an otherwise fixed class name, chosen by the view the partial
   renders.
   ============================================================ */

/* Severity bar: Critical/Major/Moderate/Minor stacked in one 8px bar
   — Home's stat tile, a review's header, the run page's review band. */
.viz-sev { display: block; width: 100%; }
.viz-sev svg { width: 100%; height: 8px; overflow: hidden; border-radius: 4px; display: block; }
.viz-sev-track { fill: var(--color-surface-sunken); }
.viz-sev-seg.sev-critical { fill: var(--sev-critical); }
.viz-sev-seg.sev-major { fill: var(--sev-major); }
.viz-sev-seg.sev-moderate { fill: var(--sev-moderate); }
.viz-sev-seg.sev-minor { fill: var(--sev-minor); }
/* a 1px surface gap between adjacent segments (design-fixes:118): the
   ramp's own contrast is monotonic, not adjacent-distinguishable, so the
   gap plus hue (and the visible legend, badge-sev) carry that job. */
.viz-sev-seg + .viz-sev-seg { stroke: var(--color-surface); stroke-width: 1; }

/* Avatars: a person's initials, white on a hue hashed from their
   account id (plan R13) — approvals log, activity feed, decision
   cards, comments, People, Home rows, the sidebar's account. */
.avatar { display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; font-size: 11px; font-weight: 600; color: var(--on-person); flex: none; }
.avatar.hue-1 { background: var(--person-1); }
.avatar.hue-2 { background: var(--person-2); }
.avatar.hue-3 { background: var(--person-3); }
.avatar.hue-4 { background: var(--person-4); }
.avatar.hue-5 { background: var(--person-5); }
.avatar.hue-6 { background: var(--person-6); }
.avatar.hue-7 { background: var(--person-7); }
.avatar.hue-8 { background: var(--person-8); }
.avatar.is-system { background: transparent; border: 1.5px dashed var(--color-border-strong); }

/* Agent glyph: a bot icon in the agents area hue — the same slot an
   avatar takes wherever the portal itself acted. */
.agent-glyph { display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: var(--radius-sm); background: var(--area-agents-soft); color: var(--area-agents); flex: none; }
.agent-glyph svg { width: 15px; height: 15px; }
.agent-glyph-eye { fill: currentColor; stroke: none; }

/* Area icon chip: a page header's or entity card's 34–44px rounded
   square, soft fill, hue icon — one --chip-hue/--chip-soft pair per
   area, shared with the illustrated empty states below. */
.area-chip { display: inline-grid; place-items: center; width: 36px; height: 36px; border-radius: 10px; background: var(--chip-soft); color: var(--chip-hue); flex: none; }
.area-chip .nav-icon, .area-chip svg { width: 18px; height: 18px; color: inherit; }
.area-chip.area-runs, .empty-art.area-runs { --chip-hue: var(--area-runs); --chip-soft: var(--area-runs-soft); }
.area-chip.area-agents, .empty-art.area-agents { --chip-hue: var(--area-agents); --chip-soft: var(--area-agents-soft); }
.area-chip.area-reviews, .empty-art.area-reviews { --chip-hue: var(--area-reviews); --chip-soft: var(--area-reviews-soft); }
.area-chip.area-commits, .empty-art.area-commits { --chip-hue: var(--area-commits); --chip-soft: var(--area-commits-soft); }
.area-chip.area-integrations, .empty-art.area-integrations { --chip-hue: var(--area-integrations); --chip-soft: var(--area-integrations-soft); }
.area-chip.area-models, .empty-art.area-models { --chip-hue: var(--area-models); --chip-soft: var(--area-models-soft); }
.area-chip.area-people, .empty-art.area-people { --chip-hue: var(--area-people); --chip-soft: var(--area-people-soft); }
.area-chip.area-settings, .empty-art.area-settings { --chip-hue: var(--area-settings); --chip-soft: var(--area-settings-soft); }

/* Illustrated empty states: one small geometric SVG per area, tinted
   by the same --chip-hue/--chip-soft pair as its icon chip, replacing
   bare lines like "Never looked yet." */
.empty-art { width: 140px; height: 96px; }
.empty-art-back { fill: var(--chip-soft); }
.empty-art-card { fill: var(--color-surface); stroke: var(--chip-hue); stroke-width: 1.5; }
.empty-art-dot { fill: var(--chip-hue); }

/* ============================================================
   THE PRD PAGE (PRD V1, spec 2026-09-29-prd-v1-design.md §8; plan
   Task 8; prd.html, prdv1.go): one read-only document in five tabs,
   review notes in a margin beside their anchors, the PRD's own chat
   right of it. Below 1100px the chat drops under the document and
   each note under its block. The look is the canvas's: serif
   headings, margin notes, flat cards. Prose stays --color-text;
   state hues only on badges and the stopped draft's edge.
   ============================================================ */
.content:has(.prd-page) { max-width: 90rem; }
.prd-page { min-width: 0; }

/* the head: the status sentence, the draft line, the merge requests */
.prd-head > .page-header { margin-bottom: var(--space-4); }
.prd-status { margin: 0 0 var(--space-2); font-size: var(--text-md); line-height: 22px; font-weight: 500; color: var(--color-text); }
.prd-draft-note, .prd-drafted, .prd-fallback { margin: 0 0 var(--space-2); font-size: var(--text-sm); line-height: 20px; color: var(--color-text-secondary); }
.prd-stopped {
  margin: 0 0 var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--state-failing);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}
.prd-stopped .form-error { margin: 0 0 var(--space-2); }
.prd-stopped .worker-error-block { padding: 0; border: 0; background: transparent; }
.prd-draft-note .worker-error-block { padding: var(--space-2) var(--space-3); font-size: var(--text-sm); line-height: 20px; }
.prd-retry { margin: var(--space-2) 0 0; }
/* New PRD's Repositories: one list per agent, its agent named above it
   while every list shows (no script). */
.prd-repos { border: 0; margin: 0; padding: 0; min-width: 0; }
.prd-repos + .prd-repos { margin-top: var(--space-3); }
.prd-repos-agent { padding: 0; margin: 0 0 var(--space-1); font-size: var(--text-sm); font-weight: 500; color: var(--color-text-secondary); }
/* The PRD page's head: the repositories it changes, and the form that edits them. */
.prd-repos-block { margin: 0 0 var(--space-2); }
.prd-repos-form { margin: 0 0 var(--space-3); padding: var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface); }
.prd-repos-title { padding: 0; margin-bottom: var(--space-2); font-size: var(--text-sm); font-weight: 500; color: var(--color-text); }
.prd-repos-line { margin: 0 0 var(--space-2); font-size: var(--text-sm); line-height: 20px; color: var(--color-text-secondary); }
.prd-mrs { list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-3) 0 0; padding: 0; }
.prd-mr {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
  max-width: 100%;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  font-size: var(--text-sm);
  line-height: 20px;
  overflow-wrap: anywhere;
}
.prd-mr-note { font-size: var(--text-xs); color: var(--color-text-secondary); }
.prd-head { position: relative; margin-bottom: var(--space-5); }
/* the head's "⋯" menu sits at the top right of the head; opened, its
   confirm form drops below it as a small card instead of pushing the head
   lines down */
.prd-head > .row-menu { position: absolute; top: 0; right: 0; z-index: 2; }
.prd-head:has(> .row-menu) > .page-header { padding-right: 40px; }
.prd-head > .row-menu[open] > .prd-cancel-form {
  position: absolute;
  top: 100%;
  right: 0;
  width: 20rem;
  max-width: calc(100vw - 2 * var(--space-4));
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}
.prd-banner {
  margin: 0 0 var(--space-5);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
  font-size: var(--text-sm);
  line-height: 20px;
}

/* the frame: the document, and the chat right of it at 1100px and up */
.prd-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-6); align-items: start; }
.prd-doc { min-width: 0; }
.prd-tabs { margin-top: 0; }
.prd-tabs .tab-count { padding: 0 var(--space-2); }
.prd-tab { padding-top: var(--space-6); }
.prd-empty { margin: 0; padding: var(--space-6) 0; font-size: var(--text-md); color: var(--color-text-secondary); }

/* a block and its margin: a section, a part, a journey or an option on
   the left, its review notes in their own column on the right — on a tab
   that has any — or under it below 1100px */
.prd-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-3) var(--space-5); margin: 0 0 var(--space-8); }
.prd-row.prd-part { margin-bottom: var(--space-6); }
.prd-row-main { min-width: 0; }
.prd-sec-title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin: 0 0 var(--space-3);
  font-family: var(--font-serif);
  font-size: var(--text-xl);
  line-height: 28px;
  font-weight: 600;
  color: var(--color-text);
}
.prd-part-title { margin: 0 0 var(--space-2); font-family: var(--font-serif); font-size: var(--text-lg); line-height: 24px; font-weight: 600; color: var(--color-text); }
/* Summary: the serif lead */
.markdown-body.prd-lead { font-family: var(--font-serif); font-size: var(--text-xl); line-height: 30px; }
.prd-items { list-style: none; display: flex; flex-direction: column; gap: var(--space-2); margin: var(--space-3) 0 0; padding: 0; }
.prd-item { display: flex; align-items: baseline; gap: var(--space-2) var(--space-3); min-width: 0; }
.prd-item-text { flex: 1 1 auto; min-width: 0; }
.prd-item-text > :last-child { margin-bottom: 0; }
.prd-id {
  flex: none;
  padding: 0 var(--space-2);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  background: var(--color-surface-sunken);
  font: 500 var(--text-xs)/18px var(--font-mono);
  color: var(--color-text-secondary);
  white-space: nowrap;
}

/* review notes: the margin's own small print, each under its anchor */
.prd-notes { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.prd-note { padding-left: var(--space-3); border-left: 2px solid var(--color-border-strong); }
.prd-note-label { margin: 0 0 var(--space-1); font-size: var(--text-xs); line-height: 16px; font-weight: 500; font-style: italic; color: var(--color-text-secondary); overflow-wrap: anywhere; }
.prd-note.is-resolved { border-left-style: dashed; }
.prd-note.is-resolved .prd-note-text { color: var(--color-text-secondary); }
.prd-not-stated { margin: 0; font-size: var(--text-sm); line-height: 20px; font-style: italic; color: var(--color-text-secondary); }
.prd-note-text { margin: 0; font-size: var(--text-sm); line-height: 20px; color: var(--color-text); overflow-wrap: anywhere; }
.prd-notes-top {
  margin: 0 0 var(--space-6);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
}

/* cards: a journey with its requirements and criteria; an option */
.prd-card { padding: var(--space-4) var(--space-5); border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-surface); }
.prd-card-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); margin: 0 0 var(--space-3); }
.prd-card-title { margin: 0; font-family: var(--font-serif); font-size: var(--text-lg); line-height: 24px; font-weight: 600; color: var(--color-text); }
.prd-journey, .prd-option { margin-bottom: var(--space-5); }
.prd-story { margin-bottom: var(--space-3); }
.prd-reqs { list-style: none; display: flex; flex-direction: column; gap: var(--space-3); margin: 0; padding: 0; }
.prd-req { padding-top: var(--space-3); border-top: 1px solid var(--color-border); }
.prd-acs { margin: var(--space-2) 0 0 var(--space-6); }
.prd-acs .markdown-body { font-size: var(--text-md); line-height: 22px; }
.prd-option.is-chosen > .prd-card { border-color: var(--color-border-strong); }
.prd-why { margin-top: var(--space-4); padding-top: var(--space-3); border-top: 1px solid var(--color-border); }

/* Architecture's legend, the diagram's own marks in small */
.prd-legend { list-style: none; display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5); margin: 0 0 var(--space-6); padding: 0; font-size: var(--text-sm); color: var(--color-text-secondary); }
.prd-legend li { display: inline-flex; align-items: center; gap: var(--space-2); }
.prd-legend-box { width: 16px; height: 12px; border: 2px solid var(--line); border-radius: 3px; background: var(--surface); }
.prd-legend-new { border-color: var(--color-accent); }
.prd-legend-changed { border-color: var(--state-working); }
.prd-legend-removed { border-color: var(--state-failing); border-style: dashed; }

/* drafting: a quiet stand-in, no motion */
.prd-skeleton { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-2) 0; }
.prd-skel-line { display: block; height: 12px; border-radius: var(--radius-pill); background: var(--color-surface-sunken); }
.prd-skel-title { width: 40%; height: 20px; margin-top: var(--space-4); }
.prd-skel-short { width: 60%; }

/* History: the versions, the folds, Cancel */
.prd-hist { min-width: 0; }
.prd-versions { list-style: none; display: flex; flex-direction: column; gap: var(--space-2); margin: 0 0 var(--space-5); padding: 0; }
.prd-version {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}
.prd-version.is-current { border-color: var(--color-border-strong); background: var(--color-surface-sunken); }
.prd-version-line { min-width: 0; padding-top: 2px; font-size: var(--text-sm); line-height: 20px; color: var(--color-text); overflow-wrap: anywhere; }
.prd-fold { margin: 0 0 var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface); }
.prd-fold > summary { padding: var(--space-2) var(--space-3); font-size: var(--text-sm); line-height: 20px; font-weight: 500; cursor: pointer; }
.prd-fold[open] > summary { border-bottom: 1px solid var(--color-border); }
.prd-fold-note { margin: var(--space-3) var(--space-3) 0; font-size: var(--text-sm); color: var(--color-text-secondary); }
.prd-given { max-height: 24rem; overflow: auto; margin: var(--space-3); padding: var(--space-3); border-radius: var(--radius-sm); background: var(--color-surface-sunken); }
.prd-fold-title { margin: var(--space-3) var(--space-3) var(--space-1); font-size: var(--text-sm); line-height: 20px; font-weight: 600; }
.prd-read-list { margin: 0 var(--space-3) var(--space-3); padding-left: 1.2rem; font-size: var(--text-sm); line-height: 20px; overflow-wrap: anywhere; }
.prd-cancel-form { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-3); }
.prd-confirm { display: flex; align-items: flex-start; gap: var(--space-2); margin: 0; font-weight: 400; }

/* the chat panel: the chat's own fragment in a fixed frame */
.prd-chat { min-width: 0; }
.prd-chat-title { margin: 0 0 var(--space-2); font-size: var(--text-md); line-height: 20px; font-weight: 600; }
.prd-chat > .chat {
  height: 70vh;
  min-height: 420px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

@media (min-width: 1100px) {
  .prd-layout { grid-template-columns: minmax(0, 1fr) clamp(300px, 26vw, 380px); }
  .prd-chat { position: sticky; top: var(--space-4); }
  .prd-chat > .chat { height: calc(100vh - 96px); }
  .prd-tab:has(.prd-row > .prd-notes) .prd-row { grid-template-columns: minmax(0, 1fr) clamp(160px, 15vw, 220px); }
}
@media (max-width: 720px) {
  .prd-card { padding: var(--space-3) var(--space-4); }
  .prd-acs { margin-left: var(--space-3); }
}
/* end of the PRD page */

/* The PRD board (spec 2026-09-28-conversational-prd): its columns, each a
   .panel of .home-cards reused as-is. */
/* BP-2/BP-3 (browser check): min-width: 0 keeps the board itself from
   forcing the whole page to scroll sideways; at 640px and under it stacks
   into one column instead of relying on its own scrollbar (the old
   ≤1100px rule widened each column on a NARROWER screen, the wrong
   direction, worsening exactly the overflow this replaces). */
.feature-board { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-4); overflow-x: auto; min-width: 0; }
/* A card here has 4 children (ring, title, avatar, age) in a column about
   220px wide: on one row the ring, avatar and age leave the title no width
   at all, so it wrapped one letter per line. The title takes its own row
   beside the ring, and the avatar and age sit on the line below it. */
.feature-column .home-card { grid-template-columns: auto auto minmax(0, 1fr); align-items: start; }

.feature-column .home-card > .home-card-main { grid-column: 2 / -1; grid-row: 1; }
.feature-column .home-card > .avatar { grid-column: 2; grid-row: 2; }
.feature-column .home-card > .badge { grid-column: 3; grid-row: 2; justify-self: start; }
@media (max-width: 640px) {
  .feature-board { grid-template-columns: minmax(0, 1fr); }
}

/* Task 5 (spec 2026-09-29-prd-v1-design.md §8.4; plan Task 5): the PRD's
   own "Today"/"With this change" diagrams — a pure layered layout
   (prdflow.go's flowViewOf) drawn as inline SVG, .chain's and .agent-map's
   own discipline: geometry lives in the Go view, only colour lives here,
   and every colour is a token — the 2026-09-15 design doc's own short
   aliases (--surface/--ink/--muted/--line) plus --color-accent for a new
   box, --state-working for a changed one and --state-failing for a removed
   one, no new tokens added. f.TooLarge falls back to .prd-flow-lines, a
   plain list, drawn with no <svg> at all. */
.prd-flow-figure { margin: 0 0 1rem; }
svg.prd-flow { display: block; width: 100%; min-width: 480px; height: auto; font-family: var(--font-sans); }
svg.prd-flow rect { fill: var(--surface); stroke: var(--line); stroke-width: 1.2; }
svg.prd-flow rect.prd-flow-new { stroke: var(--color-accent); }
svg.prd-flow rect.prd-flow-changed { stroke: var(--state-working); }
svg.prd-flow rect.prd-flow-removed { stroke: var(--state-failing); stroke-dasharray: 5 4; }
svg.prd-flow rect.prd-flow-removed ~ .prd-flow-name { text-decoration: line-through; }
svg.prd-flow text { fill: var(--ink); font-size: var(--text-xs); }
svg.prd-flow .prd-flow-name { font-weight: 600; font-size: var(--text-sm); }
svg.prd-flow .prd-flow-mark { fill: var(--muted); font-style: italic; font-size: var(--text-2xs); }
svg.prd-flow .prd-flow-arrow { fill: none; stroke: var(--line); stroke-width: 1.4; }
svg.prd-flow .prd-flow-arrow-dashed { stroke-dasharray: 5 4; }
svg.prd-flow .prd-flow-arrow-label { fill: var(--muted); font-size: var(--text-2xs); text-anchor: middle; }
svg.prd-flow .prd-flow-arrowhead { fill: var(--line); }
.prd-flow-lines { margin: 0; padding-left: 1.2rem; font-size: var(--text-sm); color: var(--ink); }
