/* Console token contract. ONE look, two themes (dark default, light via
   html[data-theme="light"]). Neutral grays with no blue tint, one saturated blue
   accent, hairline borders, no shadows. Legacy property names are aliases so the
   rest of this file keeps working. See DESIGN.md. */

/* Self-hosted typefaces (offline-first: NO Google Fonts link / @import anywhere).
   Public Sans for the UI, JetBrains Mono for data. One variable woff2 per family
   (latin subset covers en/es/fr/de). See app/static/fonts/README.md. */
@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/public-sans-latin-wght-normal.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("/static/fonts/jetbrains-mono-latin-wght-normal.woff2") format("woff2");
}

:root {
  --bg: var(--color-canvas);
  --bg-2: var(--color-sidebar);
  --panel: var(--color-surface);
  --panel-2: var(--color-subtle);
  --elev: var(--color-raised);
  --border: var(--color-border);
  --input-border: var(--color-control-border);
  --border-soft: var(--color-border-soft);
  --text: var(--color-text);
  --text-dim: var(--color-text-secondary);
  --muted: var(--color-text-muted);
  --primary: var(--color-accent);
  --primary-2: var(--color-accent-text);
  --primary-soft: var(--color-selected);
  --primary-ink: var(--color-on-accent);
  --nav-on: var(--color-nav-on);
  --ring: var(--color-focus);
  --ring-glow: color-mix(in srgb, var(--ring) 18%, transparent); /* progress flash only; focus is an outline, no glow */
  --primary-border: var(--color-accent-border);
  --radius: var(--radius-card);
  --radius-sm: var(--radius-control);
  --radius-xs: var(--radius-small);
  --shadow: var(--elevation-overlay);
  --shadow-sm: var(--elevation-flat);
  --scrollbar-hover: var(--input-border);
  --term-tag: var(--success);
  --row-dirty: var(--primary-soft);
  --row-dirty-hover: var(--primary-soft);
  --row-failed: var(--danger-soft);
  --row-failed-hover: var(--danger-soft);
  --font-body: "Public Sans", "JetBrains Mono", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-display: var(--font-body);
  --mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
  --data-font: var(--mono);
  --display-tracking: -.01em;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-8: 48px;
  --readable: 68ch;
  --content-max: 1240px;
  --radius-card: 8px;
  --radius-control: 6px;
  --radius-small: 4px;
  --radius-pill: 999px;
  --font-size-caption: 0.75rem;
  --font-size-body: 0.875rem;
  --font-size-section: 1rem;
  --font-size-title: 1.5rem;
  --line-height-body: 1.5;
  --line-height-title: 1.25;
  --line-height-editor: 1.6;
  --font-weight-heading: 600;
  --border-width: 1px;
  --control-height: 34px;
  --control-height-mobile: 40px;
  --row-height: 48px;
  --focus-width: 2px;
  --focus-offset: 2px;
  --motion-fast: 120ms;
  --motion-normal: 180ms;
  --motion-easing: ease;
  --elevation-flat: none;
  --elevation-overlay: none;
  --elevation-popover: none;
  --color-disabled-text: var(--muted);
  --color-disabled-surface: var(--panel-2);

  /* Dark theme (default). */
  --color-canvas: #1b1b1d;
  --color-sidebar: #1b1b1d;
  --color-surface: #1b1b1d;
  --color-subtle: #242427;
  --color-raised: #202023;
  --color-border: #2e2e32;
  --color-border-soft: #262629;
  --color-control-border: #77777f;
  --color-text: #ececee;
  --color-text-secondary: #a5a5ad;
  --color-text-muted: #9a9aa3;
  --color-accent: #3b6adb;
  --color-accent-text: #86a5f7;
  --color-on-accent: #ffffff;
  --color-accent-border: rgba(134, 165, 247, .35);
  --color-selected: #1f2638;
  --color-nav-on: #2a2a2e;
  --color-focus: #86a5f7;
  --success: #3fb27f;
  --success-soft: #1d3329;
  --success-border: rgba(63, 178, 127, .35);
  --warn: #e0a33a;
  --warn-soft: #3a301b;
  --warn-border: rgba(224, 163, 58, .35);
  --danger: #f2837c;
  --danger-soft: #3b2325;
  --danger-border: rgba(242, 131, 124, .4);
  --danger-border-solid: #7a4a4d;
  --hover-border: #4a4a52;
  color-scheme: dark;
}

/* Light theme: the same token names as :root (verify_ui_theme pins the parity); only the colors differ. */
html[data-theme="light"] {
  --bg: var(--color-canvas);
  --bg-2: var(--color-sidebar);
  --panel: var(--color-surface);
  --panel-2: var(--color-subtle);
  --elev: var(--color-raised);
  --border: var(--color-border);
  --input-border: var(--color-control-border);
  --border-soft: var(--color-border-soft);
  --text: var(--color-text);
  --text-dim: var(--color-text-secondary);
  --muted: var(--color-text-muted);
  --primary: var(--color-accent);
  --primary-2: var(--color-accent-text);
  --primary-soft: var(--color-selected);
  --primary-ink: var(--color-on-accent);
  --nav-on: var(--color-nav-on);
  --ring: var(--color-focus);
  --ring-glow: color-mix(in srgb, var(--ring) 18%, transparent);
  --primary-border: var(--color-accent-border);
  --radius: var(--radius-card);
  --radius-sm: var(--radius-control);
  --radius-xs: var(--radius-small);
  --shadow: var(--elevation-overlay);
  --shadow-sm: var(--elevation-flat);
  --scrollbar-hover: var(--input-border);
  --term-tag: var(--success);
  --row-dirty: var(--primary-soft);
  --row-dirty-hover: var(--primary-soft);
  --row-failed: var(--danger-soft);
  --row-failed-hover: var(--danger-soft);
  --font-body: "Public Sans", "JetBrains Mono", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-display: var(--font-body);
  --mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
  --data-font: var(--mono);
  --display-tracking: -.01em;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-8: 48px;
  --readable: 68ch;
  --content-max: 1240px;
  --radius-card: 8px;
  --radius-control: 6px;
  --radius-small: 4px;
  --radius-pill: 999px;
  --font-size-caption: 0.75rem;
  --font-size-body: 0.875rem;
  --font-size-section: 1rem;
  --font-size-title: 1.5rem;
  --line-height-body: 1.5;
  --line-height-title: 1.25;
  --line-height-editor: 1.6;
  --font-weight-heading: 600;
  --border-width: 1px;
  --control-height: 34px;
  --control-height-mobile: 40px;
  --row-height: 48px;
  --focus-width: 2px;
  --focus-offset: 2px;
  --motion-fast: 120ms;
  --motion-normal: 180ms;
  --motion-easing: ease;
  --elevation-flat: none;
  --elevation-overlay: none;
  --elevation-popover: none;
  --color-disabled-text: var(--muted);
  --color-disabled-surface: var(--panel-2);

  /* Light theme. */
  --color-canvas: #ffffff;
  --color-sidebar: #ffffff;
  --color-surface: #ffffff;
  --color-subtle: #f1f1f3;
  --color-raised: #ffffff;
  --color-border: #e4e4e7;
  --color-border-soft: #efeff1;
  --color-control-border: #7b7b84;
  --color-text: #18181b;
  --color-text-secondary: #55555c;
  --color-text-muted: #5f5f67;
  --color-accent: #2f5fd0;
  --color-accent-text: #2f5fd0;
  --color-on-accent: #ffffff;
  --color-accent-border: rgba(47, 95, 208, .35);
  --color-selected: #eaf0fc;
  --color-nav-on: #f1f1f3;
  --color-focus: #2f5fd0;
  --success: #17734a;
  --success-soft: #e6f3ec;
  --success-border: rgba(23, 115, 74, .35);
  --warn: #8f5600;
  --warn-soft: #fff3d8;
  --warn-border: rgba(143, 86, 0, .35);
  --danger: #b42318;
  --danger-soft: #fdecea;
  --danger-border: rgba(180, 35, 24, .4);
  --danger-border-solid: #e3b3ae;
  --hover-border: #c2c2c9;
  color-scheme: light;
}

* { box-sizing: border-box; }

html { scrollbar-color: var(--border) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 999px; border: 2px solid transparent; background-clip: padding-box; }
::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-hover); background-clip: padding-box; }
::-webkit-scrollbar-track { background: transparent; }

body {
  margin: 0;
  font-family: var(--font-body);
  /* Body role (DESIGN.md §3): 14px / 0.875rem Public Sans. The app previously left
     body at the 16px UA default and hand-sized every element; this establishes
     the spec baseline so unstyled reading text and table cells land at 14px.
     rem-sized elements are unaffected (rem is root-relative), so the tuned
     per-element sizes keep their values. */
  font-size: var(--font-size-body);
  /* Backgrounds are flat — the old radial-gradient wash is retired. */
  background: var(--bg);
  color: var(--text);
  line-height: var(--line-height-body);
  -webkit-font-smoothing: antialiased;
}
a { color: var(--primary-2); }
/* Long prose reads with fewer orphans/ragged lines (typeset guidance). Headings
   below opt into `balance` instead; short UI strings are unaffected either way. */
p, .help { text-wrap: pretty; }

/* Display wiring: page titles (h1 / Display), section &
   card headings (h2 / Headline), the topbar page title, and the wordmark. It is
   deliberately NOT applied to labels, buttons, table cells, or data — those stay
   Public Sans / JetBrains Mono (DESIGN.md §3). */
h1, h2, .crumbs, .brand, .brand-big, .errcode { font-family: var(--font-display); }

/* ---------------- app shell ---------------- */
.app { display: flex; min-height: 100vh; }

.sidebar {
  width: 252px; flex-shrink: 0; background: var(--bg-2);
  border-right: 1px solid var(--border-soft); padding: 14px 12px;
  /* `position: sticky` pins the sidebar on long pages AND establishes the
     absolute-positioning containing block for the .sb-resize handle (feat/
     sidebar-ux) — so the handle needs no separate `position: relative`, which
     would silently un-pin the sidebar (same specificity, later in source). */
  display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh;
}
.sidebar .brand {
  display: flex; align-items: center; gap: 10px; font-size: 1.08rem; font-weight: 700;
  color: var(--text); text-decoration: none; padding: 6px 8px 16px; letter-spacing: var(--display-tracking);
}
.sidebar .brand .mark {
  width: 30px; height: 30px; border-radius: 9px; flex-shrink: 0;
  background: var(--primary); color: var(--primary-ink);
  display: grid; place-items: center; font-size: 1rem;
}
.newbtn {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  background: var(--primary); color: var(--primary-ink); font-weight: 600;
  text-decoration: none; border-radius: var(--radius-sm); padding: 10px; margin-bottom: 12px;
  font-size: .9rem; box-shadow: none; transition: filter var(--motion-fast), transform var(--motion-fast);
}
.newbtn:hover { filter: brightness(.92); }
.newbtn:active { transform: translateY(1px); }

.side-nav { display: flex; flex-direction: column; gap: 1px; overflow-y: auto; flex: 1; }
/* Label role (DESIGN.md §3): 12px / 0.75rem, +0.05em tracking, uppercase — the
   ONE sanctioned uppercase role. Nav-group headings live here (was 10.5px/.1em). */
.nav-group { color: var(--muted); font-size: .75rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; padding: 16px 10px 5px; }
.side-nav a {
  position: relative; color: var(--text-dim); text-decoration: none; padding: 8px 11px;
  border-radius: var(--radius-sm); font-size: .9rem; display: flex; align-items: center; gap: 9px;
  transition: background var(--motion-fast), color var(--motion-fast);
}
.side-nav a:hover { background: var(--panel); color: var(--text); }
/* Active nav: subtle gray fill, 600 weight — no
   side-stripe / left border (the old ::before accent stripe is retired, per
   DESIGN.md Components + the side-stripe absolute ban). */
.side-nav a.on { background: var(--primary-soft); color: var(--primary-2); font-weight: 600; }

.side-foot { border-top: 1px solid var(--border-soft); padding-top: 12px; margin-top: 8px; display: flex; align-items: center; gap: 8px; }
.prov-dots { display: flex; gap: 5px; }
.dot { width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; font-size: .66rem; font-weight: 700; }
.dot.live { background: var(--success-soft); color: var(--success); border: 1px solid var(--success-border); }
.dot.mock { background: var(--panel-2); color: var(--muted); border: 1px solid var(--border); }
/* a local engine known only by its server URL (nothing probes it): not claimed live (#561) */
.dot.configured { background: var(--panel-2); color: var(--success); border: 1px dashed var(--success-border); }

/* ---------------- Customizable sidebar (feat/sidebar-ux) ----------------
   Collapse/resize/reorder. Desktop-facing: the width var, collapsed rail AND the
   .sb-controls toggles apply only >900px (mobile keeps the off-canvas overlay
   from mobilenav.js untouched — the controls are dead/harmful on touch, see
   sidebar_ux.js viewport gate). The glyph/label split lets a collapsed rail show
   icons only. RTL note (SB-11, not built): the resize handle lives on the right
   edge and the collapse chevrons assume LTR; an RTL layout would need mirroring. */
.nav-glyph { flex-shrink: 0; width: 18px; text-align: center; display: inline-block; }
.nav-section { display: flex; flex-direction: column; gap: 1px; }

/* Top controls: collapse + edit-layout toggles. Desktop-only: hidden by default
   and revealed in the min-width:901px block below (SB-04) so a touch viewport
   never shows the dead collapse toggle or the (undraggable) edit mode. */
.sb-controls { display: none; gap: 6px; justify-content: flex-end; padding: 0 2px 8px; }
.sb-controls button {
  background: transparent; border: 1px solid var(--input-border); color: var(--text-dim);
  border-radius: var(--radius-sm); width: 28px; height: 28px; line-height: 1;
  /* padding: 0 is load-bearing: the global `button` rule (later in this sheet) sets
     padding 10px 17px, which crushes the 28px border-box grid area and shoves the
     glyph into the bottom-right corner. Keep the glyph grid-centered. */
  padding: 0; align-self: auto;
  cursor: pointer; font-size: .95rem; display: grid; place-items: center;
  transition: background var(--motion-fast), color var(--motion-fast), border-color var(--motion-fast);
}
.sb-controls button:hover { border-color: var(--primary); color: var(--text); }
.sb-edit[aria-pressed="true"] { background: var(--primary-soft); color: var(--primary-2); border-color: var(--primary); }

/* Edit-mode toolbar (Done / Reset layout). */
.sb-edit-bar { display: none; gap: 6px; padding: 8px 2px 0; margin-top: 6px; border-top: 1px solid var(--border-soft); }
.sidebar.editing .sb-edit-bar { display: flex; }
.sb-edit-bar button {
  flex: 1; background: transparent; border: 1px solid var(--input-border); color: var(--text-dim);
  border-radius: var(--radius-sm); padding: 7px 6px; font-size: .8rem; cursor: pointer;
  transition: filter var(--motion-fast), border-color var(--motion-fast);
}
.sb-edit-bar .sb-done { background: var(--primary); color: var(--primary-ink); border-color: var(--primary); font-weight: 600; }
.sb-edit-bar button:hover { filter: brightness(.92); border-color: var(--primary); }

/* Edit-mode affordances. */
.sidebar.editing .nav-section { outline: 1px dashed var(--border); outline-offset: -2px; border-radius: var(--radius-sm); padding: 2px 0; }
.sidebar.editing .nav-group { cursor: grab; }
.sidebar.editing .side-nav a { cursor: grab; }
.sidebar.editing .nav-group::before,
.sidebar.editing .side-nav a::before { content: "⋮⋮"; content: "⋮⋮" / ""; margin-right: 6px; opacity: .45; letter-spacing: -2px; }
.sidebar.editing .side-nav a::before { font-size: .8rem; margin-right: 4px; }
.sb-dragging { opacity: .45; }

/* Resize handle. Sits fully INSIDE the sidebar's right edge (right:0, not the old
   right:-2px which spilled 1px over the content and showed a col-resize cursor
   there — SB-09). The 1px right border keeps it visually flush with the seam. */
.sb-resize { position: absolute; top: 0; right: 0; width: 6px; height: 100%; cursor: col-resize; z-index: 2; display: none; }
.sb-resize:hover { background: var(--primary-soft); }
body.sb-resizing { cursor: col-resize; user-select: none; }
body.sb-resizing .sidebar { transition: none; }
/* a11y T8 (2.5.7 / 2.1.1): no drag-only control. In edit mode every group and item
   gets 24 px up/down buttons in a second grid column (the span follows its link or
   heading, so auto-placement keeps each pair on one row); the edit bar gets a width
   stepper line; the handle is a focusable separator while editing and takes touch. */
.sb-resize { touch-action: none; }
.sb-resize:focus-visible { background: var(--primary); outline: 2px solid var(--primary); outline-offset: 0; }
.sidebar.editing .nav-section { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 4px; align-items: center; }
.sidebar.editing .nav-section > .nav-group,
.sidebar.editing .nav-section > a { grid-column: 1; }
.sidebar.editing .nav-section > .sb-move { grid-column: 2; }
.sb-move { display: inline-flex; gap: 2px; padding-right: 2px; }
.sb-move button {
  width: 24px; height: 24px; min-width: 24px; min-height: 24px; padding: 0; display: grid; place-items: center;
  background: transparent; border: 1px solid var(--input-border); color: var(--text-dim);
  border-radius: var(--radius-xs); font-size: .8rem; line-height: 1; cursor: pointer;
}
.sb-move button:hover:not(:disabled) { border-color: var(--primary); color: var(--text); }
.sb-move button:disabled { opacity: .35; cursor: default; }
.side-nav:has(.sb-dragging) .sb-move { visibility: hidden; }
.sb-edit-bar { flex-wrap: wrap; }
.sb-width-row { flex: 1 1 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; }
.sb-width-label { font-size: .8rem; color: var(--text-dim); }
.sb-width-now { flex: 1; font-size: .8rem; color: var(--text); font-variant-numeric: tabular-nums; }
.sb-width-row button { min-height: 24px; }
/* a11y T8 (1.1.1): the workflow diagram as text (_macros.html wf_graph_text). It
   inherits the shared details / summary box; the list carries its own step numbers. */
.wfg-text-list { list-style: none; margin: 8px 0 0; font-size: .88rem; }
.wfg-text-list li { margin: 3px 0; }

/* Desktop-only: custom width, the .sb-controls toggles + the collapsed rail.
   The collapsed presentation is gated on `html.sb-js` — the class sidebar_ux.js
   adds once it has actually built the expand control + label spans (SB-05). The
   <head> no-flash bootstrap pre-sets sb-js on every JS-capable load, and an
   end-of-<body> guard strips sb-js if sidebar_ux.js never initialised, so a
   failed/absent script can NEVER trap the user in a control-less 56px rail. */
@media (min-width: 901px) {
  .sidebar { width: var(--sidebar-w, 232px); transition: width var(--motion-fast) ease; }
  .sb-controls { display: flex; }
  .sb-resize { display: block; }
  html.sb-js.sb-collapsed .sidebar { width: 56px; padding-left: 6px; padding-right: 6px; }
  html.sb-js.sb-collapsed .sb-resize { display: none; }
  html.sb-js.sb-collapsed .sb-controls { justify-content: center; }
  html.sb-js.sb-collapsed .sb-edit { display: none; }
  html.sb-js.sb-collapsed .sb-edit-bar { display: none !important; }
  html.sb-js.sb-collapsed .nav-label { display: none; }
  html.sb-js.sb-collapsed .side-nav a,
  html.sb-js.sb-collapsed .newbtn,
  html.sb-js.sb-collapsed .sidebar .brand { justify-content: center; gap: 0; }
  html.sb-js.sb-collapsed .side-nav a { padding-left: 0; padding-right: 0; }
  html.sb-js.sb-collapsed .side-nav a .badge { display: none; }
  html.sb-js.sb-collapsed .nav-group { font-size: 0; padding: 10px 4px 6px; }
  html.sb-js.sb-collapsed .nav-group::after { content: ""; display: block; border-top: 1px solid var(--border-soft); }
  html.sb-js.sb-collapsed .side-foot { justify-content: center; }
  html.sb-js.sb-collapsed .side-foot .muted { display: none; }
  html.sb-js.sb-collapsed .prov-dots { flex-wrap: wrap; justify-content: center; }
}

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* Flat opaque top surface, 1px Soft Seam bottom rule — NO backdrop blur
   (glassmorphism is banned; the bar is solid). DESIGN.md §5 Navigation. */
.topbar2 {
  display: flex; align-items: center; gap: 16px;
  padding: 13px 28px; background: var(--bg-2);
  border-bottom: 1px solid var(--border-soft); position: sticky; top: 0; z-index: 5;
}
.crumbs { font-size: 1.125rem; line-height: 1.3; font-weight: 600; letter-spacing: -.01em; }
.topsearch-form { margin-left: 8px; flex: 1; max-width: 360px; }
.topsearch {
  width: 100%; background: var(--panel); border: 1px solid var(--input-border);
  border-radius: 999px; color: var(--text); padding: 8px 14px; font-size: .85rem;
}
.topsearch::placeholder { color: var(--muted); }
.topsearch:focus { outline: var(--focus-width) solid var(--ring); outline-offset: var(--focus-offset); border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring-glow); }
.topactions { display: flex; gap: 10px; align-items: center; margin-left: auto; }
.avatar { width: 32px; height: 32px; border-radius: 50%; background: var(--elev); color: var(--primary-2); display: grid; place-items: center; font-size: .8rem; font-weight: 700; border: 1px solid var(--border); }
.btn-primary {
  background: var(--primary); color: var(--primary-ink); text-decoration: none;
  font-weight: 600; border-radius: var(--radius-sm); padding: 8px 15px; font-size: .88rem;
  box-shadow: none; transition: filter var(--motion-fast), transform var(--motion-fast);
}
.btn-primary:hover { filter: brightness(.92); }
.btn-primary:active { transform: translateY(1px); }
/* Full-width, viewport-responsive: content fills .main (viewport minus the fixed
   sidebar) at any width, so an ultrawide screen is used edge-to-edge instead of
   capping at a fixed column. (Per-page reading widths like .docs-body / .narrow keep
   their own intentional caps.) */
.content { padding: 26px 28px 48px; width: 100%; }

/* Type scale (DESIGN.md §3, ratio ~1.2). Display=h1 (25px), Headline=h2 (18px),
   h3 a sub-heading (16px) in the same Public Sans face. text-wrap: balance evens heading line lengths. */
h1 { font-size: var(--font-size-title); font-weight: var(--font-weight-heading); line-height: var(--line-height-title); letter-spacing: var(--display-tracking); text-wrap: balance; }
h2 { font-size: var(--font-size-section); font-weight: var(--font-weight-heading); line-height: 1.3; letter-spacing: -.01em; text-wrap: balance; }
h3 { font-size: 1rem; line-height: 1.35; font-weight: 600; letter-spacing: -.005em; text-wrap: balance; }
.muted { color: var(--muted); }
.small { font-size: .82rem; }
.mt { margin-top: var(--space-5); }

/* Standard helper-paragraph style (copy/density audit, wave 1): replaces the old
   `.muted.small` convention for standalone helper text under a heading/control.
   One step higher-contrast than .muted (--text-dim vs --muted) since tiny+low-
   contrast text was the readability complaint; capped at --readable so it never
   runs the full width of the ultrawide .content column. */
.help { color: var(--text-dim); font-size: .85rem; max-width: var(--readable); margin: var(--space-1) 0 0; text-wrap: pretty; }
/* A few former `.muted.small.mt` sites wanted the LARGER .mt gap (separation from
   a preceding block), not .help's default tight 4px caption spacing. Two-class
   selector beats source order so this wins regardless of declaration position. */
.help.mt { margin-top: var(--space-5); }

/* Visually-hidden but screen-reader-accessible label (a11y). */
.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;
}

/* Skip-to-content link (a11y, SB-08): the first focusable element on the page,
   off-screen until focused, so a keyboard user can jump the sidebar (incl. the
   injected collapse/edit controls) instead of tabbing through every nav item. */
.skip-link {
  position: absolute; left: 64px; top: -48px; z-index: 100;
  background: var(--primary); color: var(--primary-ink); text-decoration: none;
  padding: 8px 14px; border-radius: var(--radius-sm); font-size: .88rem; font-weight: 600;
  transition: top var(--motion-fast) ease;
}
.skip-link:focus { top: 8px; outline: var(--focus-width) solid var(--ring); outline-offset: var(--focus-offset); box-shadow: 0 0 0 3px var(--ring-glow); }

/* ---------------- KPI cards ---------------- */
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; margin-bottom: 20px; }
.kpi {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px 18px; box-shadow: var(--shadow-sm);
  transition: transform var(--motion-fast), border-color var(--motion-fast);
}
.kpi:hover { transform: translateY(-2px); border-color: var(--hover-border); }
.kpi-num { display: block; font-size: 1.95rem; font-weight: 750; color: var(--text); line-height: 1.1; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.kpi-lbl { color: var(--muted); font-size: .75rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; }

/* progress + activity */
.prog { background: var(--panel-2); border-radius: 999px; height: 7px; overflow: hidden; width: 130px; }
.prog-bar { background: var(--primary); height: 100%; border-radius: 999px; }
.activity { list-style: none; padding: 0; margin: 0; }
.activity li { padding: 9px 0; border-bottom: 1px solid var(--border-soft); display: flex; align-items: center; gap: 8px; }
.activity li:last-child { border-bottom: none; }
.activity a { color: var(--primary-2); text-decoration: none; font-weight: 500; }
.quick { display: flex; flex-wrap: wrap; gap: 8px; }

/* ---------------- tiles ---------------- */
.tilegrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(210px, 100%), 1fr)); gap: 14px; }
.tile { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 15px; display: flex; flex-direction: column; gap: 5px; transition: transform var(--motion-fast), border-color var(--motion-fast); }
.tile:hover { transform: translateY(-2px); border-color: var(--hover-border); }
.tile-top { display: flex; align-items: center; justify-content: space-between; }
.tile-icon { font-size: 1.5rem; }
.tile-link { text-decoration: none; color: inherit; }
.tile-link:hover { border-color: var(--primary); }
.tile-foot { display: flex; align-items: center; justify-content: space-between; margin-top: auto; padding-top: 8px; }
.danger-btn, button.danger, .btn-danger { color: var(--danger) !important; border-color: var(--danger-border-solid) !important; }
.danger-btn:hover, button.danger:hover, .btn-danger:hover { background: rgba(240, 121, 107, .10) !important; }
.sync-cta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.expand { cursor: pointer; }
.muted-badge { background: var(--panel-2); color: var(--muted); border: 1px solid var(--border); }
.testout { min-height: 1em; }
.preset-card { margin: 10px 0; }

/* ---------------- reports bars ---------------- */
.barrow { display: flex; align-items: center; gap: 10px; margin: 7px 0; }
/* Label role (DESIGN.md §3: "table headers, control-module names, ... stat
   labels" — same treatment as .kpi-lbl / .table thead th): uppercase, tracked,
   Ash on the surface. Wave-3 polish; .barlbl only appears in the reports.html
   bars macro, so this is not a new selector, just filling in the existing one. */
.barlbl { width: 150px; font-size: .75rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); text-align: right; }
.bar { flex: 1; background: var(--panel-2); border-radius: 6px; height: 18px; overflow: hidden; }
.bar-fill { background: var(--primary); height: 100%; border-radius: 6px; }
.barval { width: 40px; font-size: .85rem; }

/* ---------------- layout helpers ---------------- */
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin: 0 0 18px; }

/* ---------------- mobile nav (hamburger) ---------------- */
/* Bug #14: below 900px the sidebar used to just vanish (display:none) with no
   way back in. The hamburger toggle (topbar2, JS-driven via mobilenav.js) slides
   the sidebar in as an overlay instead of hiding it outright; a backdrop click or
   Escape closes it. Without JS the button is inert but the page still renders -
   the sidebar just stays off-canvas until the viewport widens past 900px. */
.hamburger {
  display: none; background: transparent; border: 1px solid var(--input-border);
  border-radius: var(--radius-sm); color: var(--text); width: 36px; height: 36px;
  align-items: center; justify-content: center; font-size: 1.1rem; padding: 0;
  box-shadow: none; cursor: pointer;
}
.hamburger:hover { border-color: var(--primary); filter: none; }
.nav-backdrop {
  display: none; position: fixed; inset: 0; background: rgba(5, 8, 14, .6);
  z-index: 20;
}
@media (max-width: 900px) {
  .grid2 { grid-template-columns: 1fr; }
  .hamburger { display: flex; }
  .sidebar {
    position: fixed; left: 0; top: 0; z-index: 30; transform: translateX(-100%);
    transition: transform var(--motion-fast) ease; box-shadow: var(--shadow);
  }
  .app.nav-open .sidebar { transform: translateX(0); }
  .app.nav-open .nav-backdrop { display: block; }
}

.card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-5); margin-bottom: var(--space-5); box-shadow: var(--shadow-sm); }
.card h2 { margin-top: 0; }

/* Reading cap (copy/density audit, wave 1): prose and labels inside a card stay
   legible on the ultrawide .content column. Deliberately narrow to elements, not
   the card itself — tables, .prog, pre/code, the SVG graph, fact-strips, and chip
   rows all need their natural width and are untouched by this selector. Short
   single-line inputs nested in a capped label inherit the cap (fine — they're
   short-answer controls). */
.card p, .card .help, .card label { max-width: var(--readable); }
/* ...but a label wrapping a MULTI-LINE control (prompt editors, project/workflow/
   step "describe" briefs, the Jira JSON box, engine custom-instructions) must keep
   the card's full width — the reading cap is for prose you read, not text you
   author. :has() is Baseline-2023 (all evergreen browsers); this tool ships to
   modern browsers only. Replaces the earlier .wide opt-out, which was never applied
   to any template so every authoring textarea shipped wrongly capped at 68ch. */
.card label:has(textarea) { max-width: none; }

/* ---------------- forms ---------------- */
.form { display: flex; flex-direction: column; gap: var(--space-4); }
.form label { display: flex; flex-direction: column; gap: 5px; font-size: var(--font-size-body); color: var(--muted); font-weight: 500; }
/* Text-control theming is GLOBAL (not scoped to .form): any input/select/textarea
   anywhere renders dark-themed instead of browser-default white. Control types that
   must keep native rendering (checkbox/radio/file/range/color) are excluded via :not
   so padding/background don't distort them. Declarations are unchanged from the old
   `.form input, .form select, textarea` rule — only the scope widened, so existing
   .form controls look identical. See the "specificity guard" block at end of file
   for the two pre-existing controls (.topsearch, .report-fields) that carried their
   own look and need to win against this now-wider selector. */
input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):not([type="color"]), select, textarea {
  background: var(--bg-2); border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  color: var(--text); min-height: var(--control-height); padding: 7px 12px; font-size: var(--font-size-body); font-family: inherit; transition: border-color var(--motion-fast), box-shadow var(--motion-fast);
}
input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):not([type="color"]):focus, select:focus, textarea:focus { outline: var(--focus-width) solid var(--ring); outline-offset: var(--focus-offset); border-color: var(--primary); box-shadow: 0 0 0 3px var(--ring-glow); }
textarea { resize: vertical; }

button {
  align-self: flex-start; background: var(--primary); color: var(--primary-ink);
  border: none; border-radius: var(--radius-sm); min-height: var(--control-height); padding: 7px 12px; font-weight: 600; font-size: .9rem;
  cursor: pointer; box-shadow: none; transition: filter var(--motion-fast), transform var(--motion-fast);
}
button:hover { filter: brightness(.92); }
button:active { transform: translateY(1px); }
button.small, .btn-small { padding: 6px 12px; font-size: .82rem; box-shadow: none; }
button.ghost, button.btn-secondary { background: var(--panel-2); color: var(--text); border: 1px solid var(--input-border); box-shadow: none; }
button.ghost:hover, button.btn-secondary:hover { border-color: var(--primary); filter: none; }
/* Token disabled state wins over primary/danger variants, including their
   legacy important hover declarations. Busy aria-disabled behavior is separate. */
button:disabled, button.primary:disabled, button.btn-primary:disabled,
button.danger-btn:disabled, button.danger:disabled, button.btn-danger:disabled {
  opacity: 1; color: var(--color-disabled-text) !important;
  background: var(--color-disabled-surface) !important;
  border-color: var(--border) !important;
  cursor: default; filter: none; transform: none;
}

.list { padding-left: 18px; }
.list li { margin: 4px 0; }

/* ---------------- tables (Tabular Ledger, DESIGN.md §3 + §5) ----------------
   Ruled + dense + calm: uppercase Label headers on a Seam header rule, 1px Soft
   Seam row rules, neutral-step row hover, no zebra. tabular-nums on the whole
   table so every numeric column aligns in fixed-width figures (proportional
   figures in a data column are a defect). `.grid` (tm/glossary/templates/segment
   lists) now shares the SAME Tabular-Ledger chrome as `.table` — header rule,
   Soft-Seam row rules, neutral-step hover, 550-weight links — via the shared
   selectors below (wave 2). `.grid`'s per-column width semantics (`.grid td.tm/
   .terms/.num/.src/.tgt`, `.editor td.*`) and the editor's `--row-*` state tints
   are declared later in source at >= specificity, so they still win intact; the
   `table grid` combos (document/editor/lqa) already inherited this from `.table`
   in wave 1, so only standalone `.grid` lists change. */
.table, .grid { font-variant-numeric: tabular-nums; }
.table, .grid { width: 100%; border-collapse: collapse; }
.table th, .table td, .grid th, .grid td { text-align: left; padding: 11px 12px; border-bottom: 1px solid var(--border-soft); }
/* Label role (12px/0.75rem) + a stronger Seam header rule above the softer row rules. */
.table thead th, .grid thead th { color: var(--muted); font-weight: 600; font-size: .75rem; text-transform: uppercase; letter-spacing: .05em; border-bottom: 1px solid var(--border); }
.table tbody tr, .grid tbody tr { height: var(--row-height); transition: background var(--motion-fast); }
.table tbody tr:hover, .grid tbody tr:hover { background: var(--bg-2); }
.table a, .grid a { color: var(--primary-2); text-decoration: none; font-weight: 550; }
.table a:hover, .grid a:hover { text-decoration: underline; }
/* Data role: mono + tabular figures for data-shaped cells (locale codes like
   en-US → de-DE, counts, costs, hashes, IDs, timestamps). Applied surgically in
   templates. Deliberately unsized so it inherits the cell's size and stays
   vertically aligned with its neighbours (JetBrains Mono runs a touch large). */
.data-mono { font-family: var(--data-font); font-variant-numeric: tabular-nums; }
.nowrap { white-space: nowrap; }

.chips { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.chip { background: var(--panel-2); border: 1px solid var(--border); border-radius: 999px; padding: 3px 11px; font-size: .8rem; color: var(--text-dim); }
.chip-x { margin-left: 6px; color: var(--muted); text-decoration: none; font-weight: 700; }
.chip-x:hover { color: var(--danger, #c0392b); }

.back { color: var(--primary-2); text-decoration: none; font-size: .88rem; }
.next .roadmap { color: var(--muted); }
.next .roadmap li { margin: 4px 0; }
/* .88em scales with context but must never dip under the 12px floor (inside
   .small/.help it would land ~11.5px) — max() pins it at .75rem. */
code { background: var(--panel-2); padding: 1px 6px; border-radius: var(--radius-xs); font-size: max(.88em, .75rem); font-family: var(--mono, monospace); }

.error, .err { color: var(--danger); background: rgba(240,121,107,.08); border: 1px solid var(--danger-border-solid); border-radius: var(--radius-sm); padding: 9px 12px; }
span.err { display: inline-block; padding: 2px 8px; }
/* a11y T6: the page error summary (_form_errors.html), first child of <main>. */
.error-summary { margin: 0 0 16px; border-width: 2px; }
.error-summary:focus { outline: 3px solid var(--danger); outline-offset: 2px; }
.error-summary-title { margin: 0 0 4px; font-size: 1rem; color: var(--danger); }
.error-summary p { margin: 4px 0 0; }
.error-summary-link a { color: inherit; text-decoration: underline; }
.error-inline { color: var(--danger); }
.ok { color: var(--success); }

/* upload modes */
.modes { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 11px 13px; }
.modes legend { color: var(--muted); padding: 0 6px; font-size: .82rem; }
.radio { flex-direction: row !important; align-items: flex-start; gap: 8px; color: var(--text) !important; }
.radio input { width: auto; margin-top: 3px; }
.radio span { font-size: .9rem; }

/* tabs */
.tabs { display: flex; gap: 6px; margin-top: 12px; flex-wrap: wrap; }
.tab { padding: 6px 13px; border: 1px solid var(--input-border); border-radius: 999px; color: var(--muted); text-decoration: none; font-size: .84rem; transition: all var(--motion-fast); }
.tab:hover { color: var(--text); border-color: var(--primary); }
.tab.active { background: var(--primary-soft); color: var(--primary-2); border-color: var(--primary); font-weight: 600; }
/* UX pass 1 p7: filter chips are links; the active one is filled and bold (not colour alone). */
a.chip { text-decoration: none; display: inline-flex; align-items: center; min-height: 24px; }
a.chip:hover { border-color: var(--primary); color: var(--text); }
a.chip.active { background: var(--primary-soft); color: var(--primary-2); border-color: var(--primary); font-weight: 600; }
/* UX pass 1 p4: rename and delete sit on their own row, apart from the locale tabs. */
.doc-admin-actions { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-3) var(--space-5); margin-top: var(--space-4); padding-top: var(--space-3); border-top: 1px solid var(--border); }
.doc-admin-actions > * { margin: 0; }

/* Status badges — "Signal dots" (Option A,
   the chosen design). The dot + uppercase-micro-label treatment is STATUS-ONLY
   (the plate's vocabulary is explicitly status): it applies to a `.badge` that
   carries a state modifier (.run/.ok/.warn/.danger/.exact/.fuzzy/.semantic/
   .reauth/.paused/.beta) OR the explicit neutral-status marker `.status`. A BARE
   `.badge` with none of those stays the quiet neutral quiet neutral pill below —
   because `.badge` is also used for config / data / name / type TAGS (assignee
   email, customer name, "adaptive feedback (n=3)", cache tokens, run/batch type)
   that must NOT be case-folded into a status dot. Each state maps to an EXISTING
   token via two component-scoped custom props, --sig (dot) and --sig-ink (label);
   the shared dot/pulse/hollow/squared geometry, the narrow collapse, and the
   reduced-motion degrade live in the "Signal-dot machinery" section further down.
   Live states (.run = machine working, .reauth = reconnect) pulse; Beta+Semantic
   use a HOLLOW dot; Paused a SQUARED dot — non-colour redundancy. Every state
   selector sits at (0,2,0), above this (0,1,0) base, so it wins regardless of
   source order (the base pill only paints when no state/`.status` is present). */
.badge { font-size: .75rem; font-weight: 600; padding: 3px 9px; border-radius: 999px; display: inline-block; background: var(--panel-2); color: var(--muted); border: 1px solid var(--border); }
.badge.status { --sig: var(--muted); --sig-ink: var(--muted); }
.badge.warn { --sig: var(--warn); --sig-ink: var(--warn); }
.badge.reauth { --sig: var(--danger); --sig-ink: var(--danger); }
.badge.exact { --sig: var(--success); --sig-ink: var(--success); }
.badge.fuzzy { --sig: var(--warn); --sig-ink: var(--warn); }
.badge.semantic { --sig: var(--primary-2); --sig-ink: var(--primary-2); }
.badge.beta { --sig: var(--primary-2); --sig-ink: var(--primary-2); }
.badge.paused { --sig: var(--warn); --sig-ink: var(--warn); }

.inline-form { flex-direction: row !important; flex-wrap: wrap; align-items: flex-end; gap: 13px; }
.inline-form label { flex-direction: column; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.row-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.justfilled { animation: flashfill 1s ease-out; }
@keyframes flashfill { from { background: var(--primary-soft); } to { background: transparent; } }
.suggest { color: var(--success); font-style: italic; opacity: .85; }

.btnlink { display: inline-block; background: var(--panel-2); border: 1px solid var(--input-border); color: var(--text); text-decoration: none; border-radius: var(--radius-sm); padding: 7px 13px; font-size: .85rem; transition: border-color var(--motion-fast); }
.btnlink:hover { border-color: var(--primary); }

/* segment / term grid */
.grid td.tm { width: 60px; }
.grid td.terms { width: 22%; }
.grid td.num { color: var(--muted); width: 38px; text-align: right; font-family: var(--data-font); font-variant-numeric: tabular-nums; }
.grid td.src { width: 44%; }
.grid td.tgt { width: 36%; }
.grid td { vertical-align: top; }
.grid .tag { background: var(--primary-soft); color: var(--primary-2); margin-right: 3px; white-space: nowrap; }
.grid td.tags { white-space: nowrap; width: 1%; }
.grid code.tag { white-space: nowrap; overflow-wrap: normal; word-break: keep-all; }
.term { display: inline-block; background: var(--success-soft); border: 1px solid var(--success-border); color: var(--term-tag); border-radius: var(--radius-xs); padding: 1px 7px; margin: 1px 2px; font-size: .8rem; }
.term.dnt { background: var(--warn-soft); border-color: var(--warn-border); color: var(--warn); }
.term em { color: var(--muted); font-style: normal; }

/* inline-fidelity tag chips (read-only surfaces) — de-noised stand-in for the
   ⟦r1⟧/⟦g1⟧/⟦x1⟧ reconstruction tokens stored in segment text. */
.tag-chip { display: inline-block; min-width: 1.1em; white-space: nowrap; text-align: center; padding: 0 5px;
  margin: 0 1px; border-radius: var(--radius-xs); font-size: .72rem; font-weight: 600; line-height: 1.5;
  vertical-align: baseline; user-select: none; cursor: default;
  background: var(--primary-soft); color: var(--primary-2); border: 1px solid var(--primary-border); }
.tag-chip.point { background: var(--panel-2); color: var(--text-dim); border-color: var(--border); }
.tag-hint { display: inline-block; margin: 0 0 4px; padding: 1px 7px; border-radius: var(--radius-xs);
  background: var(--warn-soft); border: 1px solid var(--warn-border); color: var(--warn); }
.tag-hint code { background: transparent; color: inherit; padding: 0; }

/* workflow builder + runs */
.steplist { padding-left: 18px; }
.step, .runstep { background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 11px 13px; margin: 8px 0; }
.step-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.inline-del { margin-left: auto; }
.step-output { max-height: 24rem; overflow: auto; }
.prompt { background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius-xs); padding: 9px 11px; white-space: pre-wrap; font-size: .82rem; color: var(--text-dim); overflow-x: auto; margin: 8px 0 0; }
.preset { color: var(--primary-2); text-decoration: none; }

/* CAT editor */
.editor td { vertical-align: top; }
.tgt-edit { width: 100%; min-width: 220px; }
.mini { padding: 3px 9px; font-size: .75rem; border-radius: var(--radius-xs); background: var(--panel-2); color: var(--text); border: 1px solid var(--input-border); cursor: pointer; margin: 1px; box-shadow: none; }
.mini:hover { border-color: var(--primary); filter: none; }
.mini.ok-btn { color: var(--success); }
.acts { white-space: nowrap; position: relative; }
/* T4: the alternates popover is position:fixed (editor.html places it from its
   trigger's rect), so the Editor table's horizontal scroller (.tablewrap) never clips
   it; its choices are real buttons in a list. */
.altmenu { position: fixed; z-index: 10; background: var(--elev); border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: var(--shadow); min-width: 240px; max-width: min(440px, calc(100vw - 16px)); max-height: min(60vh, calc(100vh - 16px)); overflow-y: auto; white-space: normal; }
.altlist { list-style: none; margin: 0; padding: 0; }
button.altitem { display: block; width: 100%; text-align: start; white-space: normal; background: none; color: var(--text); border: 0; border-bottom: 1px solid var(--border-soft); border-radius: 0; padding: 9px 11px; font-weight: 400; font-size: .85rem; cursor: pointer; }
button.altitem:hover { background: var(--panel-2); filter: none; }
button.altitem:focus-visible { outline-offset: -2px; background: var(--panel-2); }
.concrow { padding: 5px 0; border-bottom: 1px solid var(--border-soft); font-size: .85rem; }
kbd { background: var(--panel-2); border: 1px solid var(--border); border-bottom-width: 2px; border-radius: var(--radius-xs); padding: 0 5px; font-size: .8rem; font-family: var(--mono, monospace); }

/* LQA */
.bigscore { font-size: 1.7rem; font-weight: 750; }
.bigscore.good { color: var(--success); }
.bigscore.bad { color: var(--danger); }
.lqaerr { margin: 3px 0; font-size: .85rem; }

/* --- Help / docs --- */
.docs { display: grid; grid-template-columns: 190px 1fr; gap: 32px; align-items: start; }
@media (max-width: 900px) { .docs { grid-template-columns: 1fr; } .docs-toc { position: static; } }
.docs-toc { position: sticky; top: 80px; }
.toc-title { color: var(--muted); font-size: .75rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; margin-bottom: 8px; }
/* Punch-list: the old 2px transparent border-left that filled to a colored
   stripe on hover was a side-stripe affordance (the ban). Replaced with a
   neutral-step background tint + rounded corners — the nav hover pattern. */
.docs-toc a { display: block; color: var(--muted); text-decoration: none; padding: 4px 8px; border-radius: var(--radius-xs); font-size: .85rem; transition: background var(--motion-fast), color var(--motion-fast); }
.docs-toc a:hover { color: var(--text); background: var(--panel-2); }
/* Scroll-spy active state (tocspy.js): the current section's link takes the
   side-nav active look — subtle gray fill, 600
   weight, NO side-stripe. Purely a JS-driven `.on` toggle; with JS off the TOC
   is hover-only exactly as before. */
.docs-toc a.on { background: var(--primary-soft); color: var(--primary-2); font-weight: 600; }
.docs-body { max-width: 760px; }
.doc-section { margin-bottom: 34px; scroll-margin-top: 80px; }
.doc-section h1 { margin-top: 0; }
.doc-section h2 { border-bottom: 1px solid var(--border-soft); padding-bottom: 8px; margin-bottom: 12px; }
.doc-list { padding-left: 20px; }
.doc-list li { margin: 7px 0; }
pre.code { background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 13px 15px; overflow-x: auto; font-size: .84rem; line-height: 1.5; color: var(--text-dim); white-space: pre; font-family: var(--mono, monospace); }
.table.api td { vertical-align: middle; }
/* Literal-caps chips (GET/POST) join the Label/badge scale: 12px floor, +0.05em
   caps tracking (was 10.9px — literal capitals dodge a text-transform grep). */
.method { display: inline-block; white-space: nowrap; font-size: .75rem; font-weight: 700; padding: 2px 7px; border-radius: var(--radius-xs); letter-spacing: .05em; }
.method.get { background: var(--success-soft); color: var(--success); border: 1px solid var(--success-border); }
.method.post { background: var(--primary-soft); color: var(--primary-2); border: 1px solid var(--primary-border); }
/* The keys/Delta group is the first to use verbs beyond GET/POST. PUT and PATCH
   read as writes (the POST treatment); DELETE gets the danger palette, because a
   reader scanning a table of forty rows should be able to spot the destructive one
   without reading it. */
.method.put, .method.patch { background: var(--primary-soft); color: var(--primary-2); border: 1px solid var(--primary-border); }
.method.delete { background: var(--bg-2); color: var(--danger); border: 1px solid var(--danger-border); }

/* --- key grid: two-column shell (rail + keys) ---
   Follows the .docs / .docs-toc rail+content pattern: a narrow left rail
   (Filter/Translate/Add-a-key/Export/Import/Key-settings/Ask) sticky beside
   a scrolling right column of key cards. Collapses to one column, rail on
   top, at the same 900px breakpoint as .grid2/.docs. The rail itself can run
   taller than the viewport (many cards, most gated open) — overflow-y:auto +
   a max-height keep it from being clipped or pushing the sticky position off
   past the viewport bottom. */
.keys-layout { display: grid; grid-template-columns: 280px 1fr; gap: var(--space-5); align-items: start; }
.keys-rail {
  position: sticky; top: 80px; max-height: calc(100vh - 100px);
  overflow-y: auto; overflow-x: hidden; padding-right: 2px;
}
.keys-main { min-width: 0; }
@media (max-width: 900px) {
  .keys-layout { grid-template-columns: 1fr; }
  .keys-rail { position: static; max-height: none; overflow: visible; top: auto; }
}

/* Per-key cards replacing the old matrix table (issue #48). Each key is one
   compact unit: name/badges, the source line, one block per target locale,
   then actions — dense stacking for the 100+-key pages these pages usually
   are. `.plural-line` is the #48 fix: every plural category (base AND each
   target locale) gets its own block-level line instead of flowing as inline
   `.rowform` siblings in one cell — `.rowform` itself stays display:inline
   (style.css above), unchanged for the 7 other templates that rely on it. */
.keycards { display: flex; flex-direction: column; gap: 10px; margin: 10px 0 22px; }
.keycard { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel-2); padding: var(--space-3) var(--space-4); }
.keycard-head { margin-bottom: 6px; }
.keycard-head .muted.small { margin-top: 2px; }
.keycard-locale { padding: 7px 0; border-top: 1px solid var(--border-soft); }
.keycard-source { border-top: none; padding-top: 2px; }
.loc-label { font-weight: 600; letter-spacing: .04em; font-size: .72rem; margin-bottom: 4px; display: flex; align-items: center; gap: 6px; }
.plural-values { display: flex; flex-direction: column; gap: 6px; }
.plural-line { display: block; overflow-wrap: anywhere; }
.plural-line .rowform { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.plural-line .rowform input[type="text"] { flex: 1 1 auto; min-width: 120px; width: auto; }
.keycard-actions { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border-soft); }
.keycard-actions .form label { max-width: none; }

/* Live progress for the runs a Translate just launched (issue #47), at the top
   of the keys column. One row per run; a FAILED run's step error is rendered
   below its row (issue #44's silent-failure surface) and stays put -- the
   widget deliberately does NOT auto-reload past a failure. */
.runstrip {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel-2); padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
}
.runstrip-title { margin: 0 0 8px; font-size: .9rem; }
.runstrip-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 5px 0; border-top: 1px solid var(--border-soft);
}
.runstrip-row:first-of-type { border-top: none; }
.runstrip-locale { font-weight: 600; min-width: 68px; }
.runstrip-state { font-size: .78rem; letter-spacing: .03em; }
.runstrip-row[data-run-status="failed"] .runstrip-state { color: var(--danger); font-weight: 600; }
.runstrip-error { flex: 1 1 100%; font-size: .8rem; overflow-wrap: anywhere; }
.runstrip-error[hidden] { display: none; }
.runstrip-foot { margin: 8px 0 0; }

/* F9: Export / Import / Key settings collapse behind a default-closed
   <details> in the rail (project_keys.html) -- without it the rail can run
   ~2500-3000px tall with everything open, burying Export/Import/Key settings/
   Ask past a max-height scroll container. Filter/Translate/Add a Key stay
   always-open. These already sit inside a `.card` box, so the chrome is just
   the summary + a disclosure arrow, not another nested box; the summary
   wraps the untouched `<h2>` so it keeps the exact card-heading look. */
.rail-collapse { margin: 0; }
.rail-collapse > summary { list-style: none; cursor: pointer; }
.rail-collapse > summary::-webkit-details-marker { display: none; }
.rail-collapse > summary::before { content: "▸"; content: "▸" / ""; display: inline-block; width: 1em; color: var(--muted); font-size: .8em; transition: transform var(--motion-fast); }
.rail-collapse[open] > summary::before { transform: rotate(90deg); }
.rail-collapse > summary h2 { display: inline; }
.rail-collapse > *:not(summary) { margin-top: var(--space-3); }

/* --- auth pages --- */
.authbody { display: grid; place-items: center; min-height: 100vh; padding: 24px;
  background: var(--bg); }
.auth-title { font-size: 1.125rem; margin: 8px 0 4px; }
.rowform .btn-small { min-height: 24px; min-width: 24px; }
.authcard { width: 100%; max-width: 380px; background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 28px; box-shadow: var(--shadow); }
/* Auth-screen wordmark = Display role (DESIGN.md §3: "page titles and the
   auth-screen wordmark"): 24px/600 Public Sans. */
.brand-big { display: flex; align-items: center; gap: 10px; font-size: var(--font-size-title); font-weight: 600; letter-spacing: var(--display-tracking); line-height: 1.2; margin-bottom: 6px; }
.brand-big .mark { width: 32px; height: 32px; border-radius: 9px; background: var(--primary); color: var(--primary-ink); display: grid; place-items: center; }

/* toggles */
.toggle { display: flex !important; flex-direction: row !important; align-items: flex-start; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--border-soft); color: var(--text) !important; }
.toggle:last-child { border-bottom: none; }
.toggle input { width: 18px; height: 18px; margin-top: 2px; accent-color: var(--primary); flex-shrink: 0; }
.toggle span { font-size: .9rem; font-weight: 400; }
.rowform { display: inline; }
.rowform select { background: var(--panel-2); border: 1px solid var(--input-border); border-radius: var(--radius-xs); color: var(--text); padding: 3px 6px; font-size: .82rem; }
.linkbox { width: 100%; min-width: 280px; background: var(--bg-2); border: 1px solid var(--input-border); border-radius: var(--radius-xs); color: var(--primary-2); padding: 5px 8px; font-size: .78rem; font-family: var(--mono, monospace); }
.sep { border: none; border-top: 1px solid var(--border-soft); margin: 16px 0; }

/* per-project mini stats */
.stats { display: flex; flex-wrap: wrap; gap: 14px; }
.stat { background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 12px 18px; min-width: 92px; text-align: center; }
.statnum { display: block; font-size: 1.5rem; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.statlbl { color: var(--muted); font-size: .75rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; }

/* ---- profile dropdown (topbar) ---- */
.usermenu { position: relative; }
.usermenu > summary {
  display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none;
  padding: 4px 8px 4px 4px; border: 1px solid var(--input-border); border-radius: 999px;
  background: var(--panel-2); transition: border-color var(--motion-fast), background var(--motion-fast); user-select: none;
}
.usermenu > summary::-webkit-details-marker { display: none; }
.usermenu > summary:hover { border-color: var(--primary); }
.usermenu[open] > summary { border-color: var(--primary); background: var(--panel); }
.um-id { display: flex; flex-direction: column; line-height: 1.15; max-width: 160px; }
.um-name { font-size: .82rem; color: var(--text); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Role is a VALUE, not a label — a tracked-uppercase micro-caption on a value is
   the eyebrow tell, and DESIGN.md §3 sanctions uppercase only for the Label role
   (headers / group headings / stat labels). Demoted to normal-case Ash. */
.um-role { font-size: .72rem; color: var(--muted); }
.um-caret { color: var(--muted); font-size: .7rem; transition: transform var(--motion-fast); }
.usermenu[open] .um-caret { transform: rotate(180deg); }
.um-panel {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 50; min-width: 230px;
  background: var(--elev); border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: var(--shadow); padding: 6px; animation: um-pop .12s ease;
}
@keyframes um-pop { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.um-head { padding: 8px 10px 10px; border-bottom: 1px solid var(--border-soft); margin-bottom: 6px; }
.um-head .um-name { white-space: normal; }
.um-panel a {
  display: block; padding: 8px 10px; border-radius: var(--radius-xs); color: var(--text-dim);
  text-decoration: none; font-size: .86rem; transition: background var(--motion-fast), color var(--motion-fast);
}
.um-panel a:hover { background: var(--panel-2); color: var(--text); }
/* #251: Sign out is a POST form; its menu item is a button styled like the links. */
.um-form { margin: 0; }
.um-panel button.um-item {
  display: block; width: 100%; text-align: left; padding: 8px 10px; margin: 0; border: 0;
  border-radius: var(--radius-xs); background: none; box-shadow: none; font: inherit;
  font-size: .86rem; cursor: pointer; transition: background var(--motion-fast), color var(--motion-fast);
}
.um-panel button.um-item:hover { background: var(--panel-2); }
.um-sep { border-top: 1px solid var(--border-soft); margin: 6px 0; }
.um-danger { color: var(--danger) !important; }
.um-danger:hover { background: rgba(240, 121, 107, .10) !important; }

/* ---- dashboard ---- */
/* Dashboard summary is the landing screen's glanceable status line (Principle 1:
   control made visible) — body size (not an off-scale .92rem) and readable dim
   ink, not muted-tiny; the .accent/.warn-text spans inside still carry the
   in-progress / awaiting-review emphasis. */
.dash-summary { font-size: var(--font-size-body); color: var(--text-dim); }
.accent { color: var(--primary-2); }
.warn-text { color: var(--warn); }
.projects-table .proj-name { font-weight: 600; color: var(--text); text-decoration: none; }
.projects-table .proj-name:hover { color: var(--primary-2); }
.projects-table td { vertical-align: middle; }
.prog-cell { min-width: 150px; }

/* ---- app-wide warning banner (e.g. mock MT fallback) ---- */
.warnbar {
  margin: 12px 24px 0; padding: 10px 14px; border-radius: var(--radius-sm);
  background: rgba(230, 180, 85, .10); border: 1px solid rgba(230, 180, 85, .45);
  color: var(--warn, #e6b455); font-size: .9rem;
}
.warnbar a { color: inherit; font-weight: 600; }
/* spend-cap kill switch tripped: vermilion, not amber (harder stop than the mock
   warning) — the warning hue stays apart from the accent. */
.warnbar.spendbar {
  background: rgba(240, 121, 107, .12); border-color: rgba(240, 121, 107, .55);
  color: var(--danger, #f0796b);
}

/* ---- error page ---- */
.errcode { font-size: 2.4rem; margin: 10px 0 6px; color: var(--text); }

/* ---- pagination ---- */
.pager { display: flex; gap: 10px; align-items: center; margin-top: 14px; font-size: .9rem; }
.pager a { color: var(--primary-2); text-decoration: none; }
.pager a:hover { text-decoration: underline; }
.pager .muted { margin: 0 4px; }

/* ---- background-job progress ---- */
.job-watch { margin: 10px 0 4px; max-width: 480px; }
.job-watch .prog { margin-bottom: 4px; }
.job-watch.job-failed .job-label { color: var(--danger); }

/* green status badge (Security page: OIDC ready) */
.badge.ok { --sig: var(--success); --sig-ink: var(--success); }

/* --- language picker (P2, item 1) --- */
.lp-fieldset { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 12px 14px; margin: 10px 0; }
.lp-fieldset legend { padding: 0 6px; font-weight: 600; font-size: .9rem; }
.langpicker { margin-top: 6px; }
.lp-controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 8px; }
.lp-search { flex: 1 1 200px; min-width: 160px; }
.lp-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.lp-chip { display: inline-flex; align-items: center; gap: 2px; }
.lp-chip-x { background: none; border: none; color: var(--text-dim); cursor: pointer; font-size: 1rem; line-height: 1; padding: 0 2px; box-shadow: none; }
.lp-chip-x:hover { color: var(--danger); }
.lp-list { max-height: 260px; overflow-y: auto; border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 6px; background: var(--panel-2);
  /* Compact multi-column checklist (Phrase/Lokalise convention): each option is
     one short row, flowing into as many columns as fit. */
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 2px 10px; align-content: start; }
.lp-list.lp-grid { grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr)); gap: 2px 12px; max-height: 420px; }
/* `.form label` (column layout for regular form fields) outspecifies `.lp-opt`
   and was stacking each checkbox ABOVE its centered name — the exact anti-
   pattern. Re-assert the row layout at equal-or-higher specificity. */
.lp-opt, .form label.lp-opt, .inline-form label.lp-opt {
  display: flex; flex-direction: row; align-items: center; gap: 8px;
  padding: 3px 6px; border-radius: var(--radius-sm); cursor: pointer;
  font-weight: 400; text-align: left; min-width: 0; }
.lp-opt:hover { background: var(--panel); }
.lp-opt input { margin: 0; flex: 0 0 auto; }
.lp-name { flex: 1; color: var(--text); font-size: .85rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lp-code { color: var(--text-dim); font-size: .78rem; font-family: var(--data-font, monospace); }

/* --- New-project popout (R2 item 2) --------------------------------------- */
.card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.card-head h2 { margin: 0; }
dialog.modal { border: none; border-radius: var(--radius, 10px); padding: 0; background: transparent;
  width: min(760px, calc(100vw - 32px)); max-height: calc(100vh - 48px); }
dialog.modal::backdrop { background: rgba(0, 0, 0, .55); }
dialog.modal .modal-card { margin: 0; max-height: calc(100vh - 48px); overflow-y: auto; }
.modal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.modal-head h2 { margin: 0; }

/* --- Report builder (R2-1) --- */
.report-form { margin-top: 12px; }
.report-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.report-fields label { display: flex; flex-direction: column; gap: 4px; font-size: .82rem; color: var(--text-dim); }
.report-fields select, .report-fields input { padding: 6px 8px; }
.report-actions { margin-top: 14px; }
.report-results { margin-top: 18px; }
.report-results-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.tablewrap { overflow-x: auto; }
.report-table { width: 100%; border-collapse: collapse; font-size: .85rem; font-variant-numeric: tabular-nums; }
.report-table th, .report-table td { border: 1px solid var(--border); padding: 6px 9px; text-align: right; white-space: nowrap; }
.report-table th:first-child, .report-table td:first-child { text-align: left; }
.report-table thead th { background: var(--bg-2); }
.report-table tfoot td { background: var(--bg-2); font-weight: 600; }

/* --- per-pair provider matrix (Phase 2) --- */
label.inline { display: block; margin: 4px 0; font-weight: normal; }
.pp-matrix { border-collapse: collapse; margin: 8px 0; }
.pp-matrix td { padding: 3px 10px 3px 0; vertical-align: middle; }
.pp-matrix td:first-child { font-family: var(--data-font, monospace); white-space: nowrap; }

/* ============================================================
   Design-system foundation additions (visual-refresh wave 1)
   All rules below use existing tokens only; append-only.
   ============================================================ */

/* --- specificity guard -----------------------------------------------------
   The now-global `input:not(...)` text-control rule computes to a high
   specificity (five :not([attr]) components). These two pre-existing controls
   carried their own look BEFORE the rule went global and must keep winning, so
   they are re-asserted with !important (idiomatic in this file). Without this,
   the top-bar search would lose its pill shape and the report builder its
   compact field padding. */
.topsearch { background: var(--panel) !important; border-radius: 999px !important; padding: 8px 14px !important; font-size: .85rem !important; }
.report-fields select, .report-fields input { padding: 6px 8px !important; }
/* Token/key/URL readouts (API keys, invite links): keep the compact monospace
   blue look. Outside a .form the global rule would otherwise flip them to
   proportional near-white (review finding); this also makes in-form linkboxes
   match the design intent everywhere. */
input.linkbox { font-family: var(--mono, monospace) !important; color: var(--primary-2) !important; padding: 5px 8px !important; font-size: .78rem !important; border-radius: var(--radius-xs) !important; }

/* --- keyboard focus ring ---------------------------------------------------
   Unstyled controls previously lost any focus affordance. A single :focus-visible
   ring covers links, buttons, summaries and every form control (including the
   native ones excluded from the text-control rule). */
a:focus-visible, button:focus-visible, summary:focus-visible,
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: var(--focus-width) solid var(--ring); outline-offset: var(--focus-offset); border-radius: var(--radius-sm);
}

/* --- checkbox row ----------------------------------------------------------
   Real `.checkbox` class: box first, label text after, on one row. Mirrors the
   `.toggle` row-override mechanics (re-asserting row layout with !important so it
   beats `.form label`'s flex-column), but without the settings-row border.
   Muted small hints may follow the text. */
.checkbox { display: flex !important; flex-direction: row !important; align-items: center; gap: 8px; color: var(--text) !important; font-weight: 400; cursor: pointer; }
.checkbox input { width: auto; height: auto; flex: 0 0 auto; margin: 0; accent-color: var(--primary); }
.checkbox small, .checkbox .hint { color: var(--muted); font-weight: 400; }

/* --- disclosure (details / summary) ----------------------------------------
   Quiet default styling for advanced/secondary disclosures (pages that want a
   button-like summary keep using `.btnlink` on the <summary>). */
/* Scoped past the two <details> with bespoke chrome: `.usermenu` (profile
   dropdown, base.html) and `.inline-edit` (languages.html table-cell popover
   whose summary is already a `.btnlink`). Everything else gets the quiet box. */
details:not(.usermenu):not(.inline-edit):not(.rail-collapse) { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel-2); padding: 0; margin: 8px 0; }
details:not(.usermenu):not(.inline-edit):not(.rail-collapse) > summary { list-style: none; cursor: pointer; padding: 9px 13px; font-size: .88rem; color: var(--text); display: flex; align-items: center; gap: 8px; transition: background var(--motion-fast); }
details:not(.usermenu):not(.inline-edit):not(.rail-collapse) > summary::-webkit-details-marker { display: none; }
details:not(.usermenu):not(.inline-edit):not(.rail-collapse) > summary::before { content: "▸"; content: "▸" / ""; color: var(--muted); font-size: .8em; transition: transform var(--motion-fast); }
details:not(.usermenu):not(.inline-edit):not(.rail-collapse)[open] > summary::before { transform: rotate(90deg); }
details:not(.usermenu):not(.inline-edit):not(.rail-collapse) > summary:hover { background: var(--panel); }
details:not(.usermenu):not(.inline-edit):not(.rail-collapse)[open] > summary { border-bottom: 1px solid var(--border-soft); }
details:not(.usermenu):not(.inline-edit):not(.rail-collapse) > *:not(summary) { padding: 0 13px 12px; }
/* Progressive-disclosure fieldsets (workflow-builder Add-a-step, Wave 3): the
   <details class="adv"> box is the frame, so a wrapped .lp-fieldset drops its own
   border/margin/padding to avoid a double box (the details rule above pads it). */
details.adv > .lp-fieldset { border: none; margin: 0; padding: 0; }

/* --- file input ------------------------------------------------------------ */
input[type="file"] { background: var(--bg-2); border: 1px solid var(--input-border); border-radius: var(--radius-sm); color: var(--text-dim); padding: 7px 10px; font-size: .88rem; }
input[type="file"]::file-selector-button {
  background: var(--panel-2); color: var(--text); border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  padding: 5px 12px; margin-right: 10px; font-size: .82rem; font-weight: 600; cursor: pointer; transition: border-color var(--motion-fast);
}
input[type="file"]::file-selector-button:hover { border-color: var(--primary); }

/* --- select[multiple] (fallback styling; wave 2 replaces the main offender) --- */
select[multiple] { padding: 4px; background: var(--bg-2); border: 1px solid var(--input-border); border-radius: var(--radius-sm); }
select[multiple] option { padding: 4px 8px; border-radius: 4px; }

/* --- truncation / long-name containment ------------------------------------ */
select { max-width: 100%; }
.trunc { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
.inline-form select { max-width: 34ch; text-overflow: ellipsis; }

/* --- anchors that render as buttons ----------------------------------------
   `.btnlink` already covers many; these aliases let a plain <a class="btn|ghost|
   btn-secondary"> match the ghost-button look (the compound rules above are
   `button.`-prefixed and don't reach anchors). */
a.btn, a.ghost, a.btn-secondary {
  display: inline-flex; align-items: center; justify-content: center; text-decoration: none;
  background: var(--panel-2); color: var(--text); border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  padding: 10px 17px; font-weight: 600; font-size: .9rem; cursor: pointer; box-shadow: none; transition: border-color var(--motion-fast), filter var(--motion-fast);
}
a.btn:hover, a.ghost:hover, a.btn-secondary:hover { border-color: var(--primary); filter: none; }
a.btn.small, a.ghost.small, a.btn-secondary.small { padding: 6px 12px; font-size: .82rem; }

/* --- key/value description list (customer overview) ------------------------- */
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; margin: 0; font-size: .88rem; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; color: var(--text); }

/* ============================================================================
   Wave 2B — long-name overflow guard, empty states, narrow utility
   (append-only; existing tokens only; do not edit rules above this point)
   ============================================================================ */

/* --- destructive buttons: a STANDALONE <button class="danger-btn"> (not
   combined with .ghost/.btnlink) was still rendering with the primary
   gradient + no border. The legacy `.danger-btn` rule above only sets
   color/border-color (it was built for ghost/btnlink combos); base <button>
   supplies a gradient background and border:none, so give plain
   button/a.danger-btn the ghost-danger look. Hover is already handled by the
   existing rule above (it uses !important). --- */
button.danger-btn, a.danger-btn {
  background: transparent;
  box-shadow: none;
  border: 1px solid var(--danger-border-solid);
}

/* --- long-name overflow guard: table-row selects. `.rowform` (users.html's
   per-row customer/org pickers) is a different, non-flex inline layout than
   `.inline-form`, so it isn't covered by `.inline-form select`'s 34ch cap
   above. Native HTML table auto-layout grows a column to fit an unclipped
   <select>, so give `.rowform select` the same kind of explicit cap. 26ch (not
   tighter) because `.rowform` also hosts the connector resolve-link pickers
   (connection.html Notion/Help Scout), whose selected page titles should stay
   mostly readable at rest; the open dropdown is always full-width. --- */
.rowform select { max-width: 26ch; text-overflow: ellipsis; }

/* --- narrow utility: replaces repeated `style="max-width:640px"` wrapper
   divs (connection.html webhook/API-endpoint panels). --- */
.narrow { max-width: 640px; }

/* --- empty-state component: quiet centered placeholder for empty lists
   (projects/workflows/glossaries/tm/customers/jobs/users/audit). A subtle
   glyph + one-line message + optional CTA link — no new colors, reuses
   --muted/--border already defined above. */
.empty { text-align: center; padding: 30px 16px; color: var(--muted); }
.empty::before { content: "\22EF"; content: "\22EF" / ""; display: block; font-size: 1.4rem; line-height: 1; margin-bottom: 6px; color: var(--border); letter-spacing: .2em; }
.empty p { margin: 0; font-size: .9rem; }

/* ============================================================================
   Wave 3A — light-theme support: literal-color overrides.
   The token block near the top does the heavy lifting (every var() flips per
   theme). Only rules whose colors bypass tokens need explicit light overrides.
   ============================================================================ */

/* Onboarding "Get started" setup checklist card (SPEC-onboarding-tour Phase 1).
   All colours are existing tokens; every selector is scoped under
   .onboarding. Static markup — the only motion is the native <details> toggle, so
   there is nothing extra to gate behind prefers-reduced-motion. */
.onboarding { border-color: var(--primary-border); }
.onboarding .ob-details > summary { list-style: none; cursor: pointer; }
.onboarding .ob-details > summary::-webkit-details-marker { display: none; }
.onboarding .ob-summary {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px; flex-wrap: wrap;
}
.onboarding .ob-title { margin: 0; }
.onboarding .ob-sub { margin: 3px 0 0; }
.onboarding .ob-progress { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; min-width: 130px; }
.onboarding .ob-meter { width: 150px; }
.onboarding .prog-bar.done { background: var(--success); }
.onboarding .ob-steps { list-style: none; margin: 16px 0 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.onboarding .ob-step {
  display: flex; align-items: center; gap: 12px;
  background: var(--panel-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 11px 13px;
}
.onboarding .ob-step.is-done { border-color: var(--success-border); background: var(--success-soft); }
.onboarding .ob-mark { flex: 0 0 auto; }
.onboarding .ob-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.onboarding .ob-step-title { font-weight: 600; }
.onboarding .ob-step-desc { margin: 0; }
.onboarding .ob-cta { flex: 0 0 auto; margin-left: auto; white-space: nowrap; }
.onboarding .ob-foot { margin-top: 14px; }
.onboarding .ob-dismiss-form { margin: 0; }
.onboarding .ob-open:focus-visible { outline: var(--focus-width) solid var(--ring); outline-offset: var(--focus-offset); }

/* --- Onboarding spotlight (SPEC-onboarding-tour Phase 2) --------------------
   The quiet per-step "Show me" affordance in the checklist. It sits after the
   plain-link CTA and is rendered [hidden] in the template — onboarding.js unhides
   it only when driver.js is present, so with JS disabled it stays invisible/inert
   and the Phase-1 plain links are the whole feature (progressive enhancement). */
.onboarding .ob-showme {
  flex: 0 0 auto; margin-left: 8px; padding: 2px 8px;
  font: inherit; font-size: .82rem; line-height: 1.4;
  color: var(--muted); background: transparent;
  border: 1px solid var(--input-border); border-radius: var(--radius-xs);
  cursor: pointer; white-space: nowrap;
}
.onboarding .ob-showme:hover { color: var(--text); border-color: var(--primary); }
.onboarding .ob-showme:focus-visible { outline: var(--focus-width) solid var(--ring); outline-offset: var(--focus-offset); }

/* --- driver.js popover: Console token overrides ----------------
   driver.css (loaded BEFORE style.css so these win the cascade) ships hardcoded
   light-only colors. We re-theme its popover/buttons/arrow with EXISTING
   tokens only, so the spotlight reads correctly in both themes.
   The overlay scrim itself is an <svg fill> set from JS config (dark, both
   themes). No new tokens introduced. */
.driver-popover {
  color: var(--text);
  background-color: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  font-family: var(--font-body, inherit);
}
.driver-popover-title { color: var(--text); }
.driver-popover-description { color: var(--text-dim); }
.driver-popover-progress-text { color: var(--muted); }
.driver-popover-close-btn { color: var(--muted); }
.driver-popover-close-btn:hover,
.driver-popover-close-btn:focus { color: var(--text); }
/* The action ("Take me there" / "Got it") button reads as the primary control. */
.driver-popover-footer-btn {
  color: var(--primary-ink);
  background-color: var(--primary);
  border: 1px solid var(--primary);
  border-radius: var(--radius-xs);
}
.driver-popover-footer-btn:hover,
.driver-popover-footer-btn:focus {
  background-color: var(--primary-2);
  border-color: var(--primary-2);
}
.driver-popover-footer-btn:focus-visible { outline: var(--focus-width) solid var(--ring); outline-offset: var(--focus-offset); }
/* Arrow: recolor only the VISIBLE pointing side per placement to match --panel;
   the three transparent sides stay as driver.css set them. */
.driver-popover-arrow-side-left { border-left-color: var(--panel); }
.driver-popover-arrow-side-right { border-right-color: var(--panel); }
.driver-popover-arrow-side-top { border-top-color: var(--panel); }
.driver-popover-arrow-side-bottom { border-bottom-color: var(--panel); }
/* prefers-reduced-motion: kill driver's fade even if JS animate slips through
   (driver keys its overlay/popover fade off --driver-animation-duration). */
@media (prefers-reduced-motion: reduce) {
  :root { --driver-animation-duration: 0s; }
  html:root { --motion-fast: 0s; --motion-normal: 0s; }
  .driver-fade .driver-overlay,
  .driver-fade .driver-popover { animation: none !important; }
}

/* Topbar surface. The base `.topbar2` rule now uses var(--bg-2) (a token that
   already flips per theme), so this override is a redundant, explicit reassertion
   kept for clarity + the theme-parity smoke's marker. No blur, no translucency —
   The bar is flat and solid in both themes. Every other literal in this file
   is a low-alpha state tint (danger/warn/success washes, neutral scrims) that
   reads fine on light once the state *text* tokens above are darkened. */
html[data-theme="light"] .topbar2 { background: var(--bg-2); }

/* ============================================================================
   Busy indicators (feat/busy-indicators): the app-wide "working" state for any
   click that does real work. busy.js drives it (auto on every form submit, plus
   LocBusy.start/wrap for in-page fetch buttons); _busy.html renders the bar. Two
   pieces, deliberately small: a thin top progress bar and a spinner on the busy
   button. No full-page overlay. APPEND-ONLY, existing tokens only (the accent is
   var(--primary), so both themes are covered with no overrides).
   ============================================================================ */

/* The top bar: fixed to the very top of the viewport, full width, 4px, above the
   topbar (5), the mobile nav drawer (30) and the driver.js scrim (10000), below the
   driver.js popover (1e9). pointer-events: none, so it never eats a click. */
.busybar {
  position: fixed; top: 0; left: 0; right: 0; height: 4px; z-index: 10001;
  overflow: hidden; pointer-events: none; background: var(--primary-soft);
  opacity: 0; visibility: hidden; transition: opacity var(--motion-fast) ease, visibility 0s linear var(--motion-fast);
}
.busybar.is-active { opacity: 1; visibility: visible; transition: opacity var(--motion-fast) ease; }
.busybar::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 40%;
  background: var(--primary); border-radius: 0 2px 2px 0;
  animation: busybar-slide 1.25s ease-in-out infinite;
}
/* translateX is relative to the 40%-wide slug: -100% starts it just off the left
   edge, 250% (= 100% of the bar) carries it just off the right edge. */
@keyframes busybar-slide { from { transform: translateX(-100%); } to { transform: translateX(250%); } }

/* The busy button: a CSS-only ring left of the label, in the button's own text
   colour (currentColor), so it reads on primary, ghost, small, link-style and
   danger buttons alike. The submit path also adds .busy-locked (below). */
.btn-busy { cursor: progress; opacity: .8; white-space: nowrap; }
/* One line, never wider than its container: the ring and the counter keep their
   size and a long translated label ("Conception du flux de travail...") is
   ellipsized on a phone-width screen instead of spilling out of the button. */
button.btn-busy, a.btn-busy {
  display: inline-flex; align-items: center; justify-content: center; max-width: 100%;
}
.btn-busy .busy-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.btn-busy::before, .btn-busy .busy-elapsed { flex: none; }
.btn-busy::before {
  content: ""; display: inline-block; box-sizing: border-box;
  width: .9em; height: .9em; margin-right: .5em; vertical-align: -.12em;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
  animation: busy-spin .7s linear infinite;
}
/* A real `disabled` (the JS API path) must not fade it to the 50% disabled look:
   busy is "working", not "unavailable". */
button.btn-busy:disabled { opacity: .8; cursor: progress; }
/* The elapsed counter ("12s") has its own span with a reserved width that fits
   "999 s", in tabular figures, so the ticking number never changes the button's
   width; busy.js pins the width once, right after the label swap. */
.btn-busy .busy-elapsed {
  display: inline-block; min-width: 5ch; margin-left: .35em; text-align: left;
  font-variant-numeric: tabular-nums;
}
@keyframes busy-spin { to { transform: rotate(360deg); } }

/* Submit path: every submit button of a busy form looks inert. They are NOT
   `disabled` (a disabled submitter drops its name/value from the POST) and they
   stay clickable on purpose: busy.js drops a quick second submit and, after a few
   seconds, asks whether to send it again (the way out of a stopped navigation).
   Hover/press feedback is switched off so they do not look ready. */
.busy-locked { cursor: progress; }
.busy-locked:hover { filter: none; }
.busy-locked:active { transform: none; }
.busy-locked:not(.btn-busy) { opacity: .55; }
form[aria-busy="true"] { cursor: progress; }

/* Reduced motion: no sliding and no spinning. The bar becomes a static accent
   rule and the ring a static open circle, still clearly "working". !important
   because the global reduced-motion block above forces animation timings with it. */
@media (prefers-reduced-motion: reduce) {
  .busybar::before { animation: none !important; width: 100%; opacity: .75; }
  .btn-busy::before { animation: none !important; }
}

/* ============================================================================
   editor UX (SPEC-editor-ux) — APPEND-ONLY, tokens only. All rules here are
   ADDITIVE (new classes / new state variants) and never modify a rule above.
   Theme-agnostic: every color is a token, so both dark (:root) and
   html[data-theme="light"] are covered automatically.
   ============================================================================ */

/* 1. Status chips, color-by-value (class sits alongside the base `.chip`). These
   join the "Signal dots" family: the machinery section resets the inherited
   `.chip` pill to a borderless dot + uppercase label for exactly these st-*
   selectors, so here we only assign each state's token. st-edited reads as the
   accent "machine touched this" state; st-mt/st-new are the neutral reading. */
.chip.st-approved { --sig: var(--success); --sig-ink: var(--success); }
.chip.st-needs_review { --sig: var(--warn); --sig-ink: var(--warn); }
.chip.st-edited { --sig: var(--primary-2); --sig-ink: var(--primary-2); }
.chip.st-mt, .chip.st-new { --sig: var(--muted); --sig-ink: var(--muted); }

/* 2 + 6. Sticky editor toolbar carrying the confirmed-progress bar + shortcut
   hint. z-index 4 sits just below the topbar (z-index 5); top offset clears it. */
.editor-toolbar {
  position: sticky; top: var(--topbar-h, 69px); z-index: 4;
  display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 8px 14px; margin: 0 0 14px;
}
.prog-wrap { display: flex; align-items: center; gap: 10px; }
.prog-count { font-size: .82rem; color: var(--text-dim); white-space: nowrap; }
.prog-count b { color: var(--success); font-weight: 700; }
.editor-toolbar .kbd-hint { margin: 0; font-size: .78rem; }
/* flash the bar when confirm-and-advance finds no further unconfirmed row (item 3) */
.prog.flash { animation: prog-flash .6s ease; }
@keyframes prog-flash { 0%, 100% { box-shadow: none; } 50% { box-shadow: 0 0 0 3px var(--ring-glow); } }

/* 4. Unsaved-edit (dirty) + save-failed row accents. The editor uses a background
   TINT on the whole row, not an inset side-stripe on the first cell (the old
   `box-shadow: inset 3px 0 0` accents are retired per DESIGN.md Components +
   the side-stripe ban). The :hover variants are mandatory: `.table tbody
   tr:hover` (0,2,2) would otherwise replace the status tint with the neutral
   row-hover — hiding the status exactly when the user points at the row. The
   hover tint is a STRONGER step of the same hue, so hover feedback survives
   and dirty/failed stay distinct from each other and from plain hover.
   save-failed follows dirty in source so its danger tint wins a shared row. */
.editor tbody tr.dirty { background: var(--row-dirty); }
.editor tbody tr.dirty:hover { background: var(--row-dirty-hover); }
.editor tbody tr.save-failed { background: var(--row-failed); }
.editor tbody tr.save-failed:hover { background: var(--row-failed-hover); }

/* 5. Autosized targets keep a sensible minimum; JS grows them from scrollHeight. */
.editor .tgt-edit { min-height: 2.6em; overflow: hidden; }

/* 8. Column widths for the editor grid now that a Terms column is present.
   Scoped to `.editor` so document.html's grid (unchanged) is unaffected — equal
   specificity to the base `.grid td.*` rules but declared later, so these win. */
.editor td.src { width: 32%; }
.editor td.tgt { width: 30%; }
.editor td.terms { width: 16%; }

/* ============================================================================
   Project-page header reorg — compact horizontal fact strip + a row of small
   <details> edit disclosures, replacing the old fully-stacked header (name,
   meta, then every edit form full-width, one under another). APPEND-ONLY,
   tokens only, theme-agnostic. Full-width-aware: wraps at any viewport instead
   of overflowing (the .content cap was removed project-wide already).
   ============================================================================ */
.project-header h1 { margin-bottom: 4px; }

/* Facts: a row of labeled key/value tiles (status, due date, content type,
   segmentation, provider(s), target locales) — flows to multiple lines on a
   narrow window instead of ever causing horizontal overflow. */
.fact-strip {
  display: flex; flex-wrap: wrap; gap: 14px 28px;
  margin: 14px 0 4px; padding-top: 14px; border-top: 1px solid var(--border-soft);
}
.fact { display: flex; flex-direction: column; gap: 3px; min-width: 100px; max-width: 100%; }
.fact-lbl { color: var(--muted); font-size: .75rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; }
.fact-val { font-size: .88rem; color: var(--text-dim); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* Target locales gets the full row width so its chip list can wrap freely
   instead of squeezing into a fixed-width column. */
.fact-wide { flex: 1 1 260px; }
.fact-wide .chips { margin-top: 1px; }

/* Edit affordances: every form lives behind a small collapsed <details>: a
   horizontal row of `.btnlink small` summaries (project.html's existing
   `.inline-edit` details already render chrome-free — only the summary is
   styled — so stacking them in a flex row reads as one compact toolbar). Each
   details' body still opens as a normal block below the row when expanded. */
.edit-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; align-items: flex-start; }
.edit-row > details.inline-edit { margin: 0; }
.edit-row > details.inline-edit > summary { margin: 0; }

/* Conditional-workflow builder graph (SPEC-conditional-workflows Phase 4). A
   read-only, server-rendered SVG aid next to the step list; theme-aware via the
   same CSS variables. The SVG scales down on narrow viewports (max-width:100%)
   and never forces the page to scroll horizontally. */
.wf-graph { margin: 6px 0 2px; }
.wf-graph figcaption { margin-bottom: 6px; }
.wf-graph-svg { max-width: 100%; height: auto; display: block; }
.wfg-title { font-size: 12px; font-weight: 600; fill: var(--text); }
.wfg-sub { font-size: 10.5px; fill: var(--muted); }
/* Phase 3.1 per-node META line (packaged assets/gates). Muted, one notch smaller
   than the role/model sub-line; same semantic token so it reads calm in both themes. */
.wfg-meta { font-size: 10px; fill: var(--muted); }
.wfg-elabel { font-size: 10px; font-weight: 600; }
/* Run-view overlay (SPEC-conditional-workflows Phase 4 deferred item): a per-node
   status label (drawn right-aligned inside the node) + an inline figcaption note. Colour
   comes from the status token set inline on the <text> fill (success/danger/muted/primary/
   warn), so no new colour pair and the contrast checker is untouched; the TEXT label makes
   it colourblind-safe. */
.wfg-state { font-size: 10px; font-weight: 600; }
.wfg-run-note { display: inline-block; margin-left: 8px; font-size: .72rem; }
/* Phase 3 shape line: primary + additive chips and a one-line headline that introduces
   the workflow diagram. LAYOUT ONLY — colours come from the existing .chip / .muted
   tokens the markup already carries (the review headline inherits body --text for a
   prominent line; the builder headline carries .muted). No new colour pair. */
.wf-shape-line { margin: 12px 0 4px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.wf-shape-headline { flex-basis: 100%; margin: 4px 0 0; font-size: .92rem; }
/* legend keys in the figcaption */
.wfg-k { display: inline-block; margin-left: 8px; font-weight: 600; font-size: .72rem; }
.wfg-pass { color: var(--success); }
.wfg-fail { color: var(--danger); }
.wfg-loop { color: var(--danger); }

/* SPEC-workflow-canvas Phase 1 — drag-to-reorder affordances on the BUILDER graph
   only (the macro emits them solely under editable=true; every other call site's SVG
   is byte-identical to before). LAYOUT + CURSOR ONLY: no new colour pair — the grip
   reuses --border/--muted like the node outlines, the insertion marker and the "run
   together" band highlight reuse --success, exactly the token the pass edge already
   uses. The slot/band rects themselves are fill:none and therefore never hit-tested by
   the browser; wfcanvas.js hit-tests their client rects itself. */
.wf-canvas { position: relative; }
/* touch-action MUST be static on the grip, not toggled by a class at pointerdown.
   Per Pointer Events L2 the browser decides whether a gesture is a scroll or a drag from
   the touch-action value in force when the gesture STARTS; a class added in the
   pointerdown handler is already one gesture too late, so the first touch-drag on a
   phone scrolled the page and fired pointercancel instead of moving the step. */
.wf-canvas .wfg-grip { cursor: grab; touch-action: none; }
/* Belt and braces: touch-action on an SVG CHILD element (the grip is a <g>) has less
   certain browser support than on the svg root, which is a replaced/box element. Setting
   it on the root too guarantees the browser has the answer before the gesture starts,
   whichever element it consults. The graph does not scroll internally, so suppressing
   touch panning over it costs nothing. */
.wf-canvas .wf-graph-svg { touch-action: none; }
.wf-canvas .wfg-node.wfg-dragging { cursor: grabbing; opacity: .65; }
.wf-canvas .wfg-node.wfg-dragging .wfg-grip { cursor: grabbing; }
/* During a drag the non-dragged nodes stop swallowing text selection so the pointer
   stream stays clean. */
.wf-graph-svg.wfg-drag-active { user-select: none; }
.wf-canvas-notice { margin: 6px 0 0; font-size: .85rem; color: var(--muted); }
.wf-canvas-notice.error { color: var(--danger); font-weight: 600; }

/* Run View v2 #1 — per-step-visit Gantt on /runs/{id}, under the run graph. LAYOUT
   ONLY over the existing semantic tokens (no new colour pair): row/name text on
   --text/--muted, the bar TRACK on --bg-2, and each bar FILL is a status hue token
   (var(--success)/--warn/--danger/--primary/--muted) that is redundant with the
   status_badge + label the row also carries (colourblind-safe — colour is never the
   sole signal). All selectors are scoped under .rv-gantt so nothing leaks page-wide. */
.rv-gantt { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 10px;
  overflow-x: auto; }
.rv-gantt-h { font-size: .95rem; margin: 0 0 2px; }
.rv-gantt-rows { min-width: 460px; margin-top: 8px; }
.rv-gantt-row, .rv-gantt-foot {
  display: grid; grid-template-columns: minmax(120px, 1.6fr) auto minmax(90px, 3fr) 62px 92px;
  align-items: center; gap: 10px; padding: 4px 0; }
.rv-gantt-foot { border-top: 1px solid var(--border); margin-top: 4px; padding-top: 8px; }
.rv-gantt-name { font-size: .84rem; color: var(--text); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.rv-gantt-orphan .rv-gantt-name { color: var(--muted); font-style: italic; }
.rv-gantt-visit { margin-left: 3px; font-size: .74rem; color: var(--muted); }
.rv-gantt-badge { justify-self: start; }
.rv-gantt-track { width: 100%; min-width: 60px; display: flex; align-items: center; }
.rv-gantt-svg { width: 100%; height: 12px; display: block; }
.rv-gantt-bg { fill: var(--bg-2); }
.rv-gantt-dur { font-size: .78rem; color: var(--text); text-align: right; }
.rv-gantt-cost { font-size: .78rem; color: var(--muted); text-align: right; }
.rv-gantt-foot-wall { justify-content: flex-start; }

/* Group page coverage: a calm table; the locale is monospace, the fallback note muted.
   No new colours — "no coverage" leans on the existing .muted token, not a red. */
.wf-cov { margin: 6px 0 2px; }
.wf-cov-note { margin: 2px 0 10px; }
.wf-cov td.wf-cov-fallback { color: var(--muted); }

/* B0 per-step PACKAGE / VERIFY panel (SPEC-workflows-redesign Track B). A calm,
   read-only two-column readout in a collapsed <details> under each step. LAYOUT +
   the existing semantic tokens only — no new colour pair (the scope tag reuses
   --muted, the divider --border), so it reads correct in both themes and passes the
   contrast checker (all text is --text/--muted on --panel/--card, unchanged pairs). */
.wf-pkg { margin: 6px 0 2px; }
.wf-pkg > summary { font-size: .86rem; color: var(--muted); }
.wf-pkg-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 8px; }
.wf-pkg-col { min-width: 0; }
.wf-pkg-h { margin: 0 0 6px; font-size: .82rem; font-weight: 600; color: var(--text); }
.wf-pkg-list { margin: 0; }
.wf-pkg-row { display: grid; grid-template-columns: minmax(6.5em, 9em) 1fr; gap: 8px;
              padding: 3px 0; border-top: 1px solid var(--border); }
.wf-pkg-row:first-child { border-top: 0; }
.wf-pkg-k { margin: 0; font-size: .8rem; font-weight: 600; color: var(--muted); }
.wf-pkg-v { margin: 0; font-size: .8rem; color: var(--text); word-break: break-word; }
.wf-pkg-scope { color: var(--muted); font-style: italic; white-space: nowrap; }
/* Narrow viewports: stack the two columns so neither overflows horizontally. */
@media (max-width: 680px) {
  .wf-pkg-cols { grid-template-columns: 1fr; gap: 10px; }
}

/* LLM Ledger viewer (/prompt-tap). Layout only — reuses existing tokens (no new
   color pairs), scoped under .ptap-* so nothing leaks app-wide. */
.filterbar { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; margin: 8px 0 12px; }
.ptap-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.ptap-rec { padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; }
.ptap-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ptap-ts { margin-left: auto; }
.ptap-meta { margin: 6px 0 2px; }
/* Correlation facts are separate spans; the separator is inserted BETWEEN them so a
   capture with only some of them never leads with a stray dot. */
.ptap-meta > span + span::before { content: " \00b7  "; }
.ptap-rec > details.adv { margin-top: 6px; }
.ptap-rec pre.code { max-height: 22rem; overflow: auto; }
/* Download row: the format links sit inline with their explanatory note. */
.ptap-dl { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; }

/* ============================================================================
   Run-envelope viewer (proposal §9): translate ACTION -> language-pair RUN ->
   nested CALLS, as native <details> disclosures. Two levels of nesting, each a
   quiet surface step so the hierarchy reads at a glance without cards-in-cards.
   Density with hierarchy (PRODUCT.md §3); state carried by the shared badges.
   ============================================================================ */
.tap-actions { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.tap-action { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel); overflow: hidden; }
.tap-action > details > summary {
  list-style: none; cursor: pointer; padding: 11px 14px;
  display: grid; grid-template-columns: 1fr auto; grid-auto-rows: min-content;
  gap: 4px 14px; align-items: center;
}
.tap-action > details > summary::-webkit-details-marker { display: none; }
.tap-action > details > summary::before {
  content: "\25B8"; content: "\25B8" / ""; color: var(--muted); font-size: .8rem;
  transition: transform var(--motion-fast) ease; grid-row: 1 / span 2; align-self: center;
  margin-right: 2px;
}
.tap-action > details[open] > summary::before { transform: rotate(90deg); }
.tap-action-sum { display: contents; }
.tap-action-lead { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
.tap-action-title { font-weight: 600; color: var(--text); }
.tap-action-title .muted { font-weight: 400; }
.tap-action-locales { grid-column: 1; color: var(--text-dim); font-size: .8rem; }
.tap-metrics { grid-column: 2; grid-row: 1 / span 2; display: flex; align-items: center; gap: 12px; justify-self: end; text-align: right; flex-wrap: wrap; }
.tap-metrics .danger-text, .tap-run-metrics .danger-text { color: var(--danger); font-weight: 600; }

.tap-runs { padding: 4px 12px 12px 30px; background: var(--bg-2); border-top: 1px solid var(--border-soft); }
.tap-wf { margin: 8px 2px; }
.tap-run { border: 1px solid var(--border-soft); border-radius: var(--radius-xs); margin-top: 8px; background: var(--panel); }
.tap-run > summary {
  list-style: none; cursor: pointer; padding: 8px 12px;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.tap-run > summary::-webkit-details-marker { display: none; }
.tap-run > summary::before { content: "\25B8"; content: "\25B8" / ""; color: var(--muted); font-size: .75rem; transition: transform var(--motion-fast) ease; }
.tap-run[open] > summary::before { transform: rotate(90deg); }
.tap-run-locale { font-weight: 600; color: var(--text); }
.tap-run-metrics { display: flex; align-items: center; gap: 12px; margin-left: auto; flex-wrap: wrap; }

.tap-calls { list-style: none; margin: 0; padding: 4px 12px 12px; display: flex; flex-direction: column; gap: 8px; }
.tap-call { padding: 9px 11px; border: 1px solid var(--border-soft); border-radius: var(--radius-xs); background: var(--bg-2); }
.tap-call-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tap-call-ts { margin-left: auto; }
.tap-call-meta { margin: 6px 0 2px; }
.tap-call-meta > span + span::before { content: " \00b7  "; }
.tap-call > details.adv { margin-top: 6px; }
.tap-call pre.code { max-height: 20rem; overflow: auto; }
.tap-more, .tap-empty-run { margin: 6px 12px 10px; }

/* A quieter chip variant for the descriptor tags (kind / batch) so they don't
   compete with the value chips inside a call. */
.chip.soft { background: transparent; color: var(--muted); }
.mono, .chip.mono { font-family: var(--data-font, var(--mono, ui-monospace, monospace)); font-variant-numeric: tabular-nums; }

/* The flat "All calls" fallback is a secondary disclosure; its summary carries an
   inline heading rather than the default marker. */
.tap-flat > summary { cursor: pointer; list-style: none; }
.tap-flat > summary::-webkit-details-marker { display: none; }
.tap-flat > summary::before { content: "\25B8 "; content: "\25B8 " / ""; color: var(--muted); transition: transform var(--motion-fast) ease; display: inline-block; }
.tap-flat[open] > summary::before { transform: rotate(90deg); }
.inline-h2 { display: inline; font-size: 1.05rem; }

@media (max-width: 640px) {
  .tap-action > details > summary { grid-template-columns: 1fr; }
  .tap-metrics { grid-column: 1; grid-row: auto; justify-self: start; text-align: left; }
  .tap-runs { padding-left: 14px; }
}

/* ============================================================================
   Reduced motion (accessibility floor, DESIGN.md §5 Focus + PRODUCT.md a11y).
   Motion is state-conveying only (150–250ms); for users who ask for
   reduced motion, collapse every transition/animation to a near-instant change
   and disable smooth scrolling. Kept last so it wins the cascade. The tiny
   non-zero duration preserves transitionend/animationend callbacks that JS may
   rely on (e.g. the confirm-and-advance flash) instead of dropping them.
   ============================================================================ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================================
   Account -> Appearance: Light / Dark / Match my computer, as a radio choice
   list. Tokens only. The same .choice-option look is used by the Get started card.
   ============================================================================ */
.choice-options { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr)); gap: 10px; margin: var(--space-2) 0 var(--space-4); }
/* Re-assert row layout at >= specificity so `.form label`'s flex-column (which
   would stack the radio above the text) does not win. */
.form label.choice-option, .choice-option {
  display: flex; flex-direction: row; align-items: center; gap: 11px;
  border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  padding: 10px 12px; cursor: pointer; font-weight: 400; text-align: left;
  transition: border-color var(--motion-fast), background var(--motion-fast);
}
.choice-option:hover { border-color: var(--primary); }
.choice-option.on { border-color: var(--primary); background: var(--primary-soft); }
.choice-option input { flex: 0 0 auto; margin: 0; accent-color: var(--primary); }
.choice-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.choice-name { font-weight: 600; color: var(--text); font-size: .9rem; }
.choice-desc { color: var(--muted); font-size: .8rem; }

/* ============================================================================
   Per-page polish — Wave A (dashboard / projects / project / document).
   Hierarchy, primary-action clarity, genuine empty states, spacing rhythm.
   APPEND-ONLY, existing tokens only (token layer untouched), no class/id
   renames (JS hooks preserved), theme-agnostic.
   ============================================================================ */

/* Genuine empty state: quiet, left-aligned, and always ending in a clear next
   action — not decorative filler. Lighter than the full-card `.empty` treatment;
   used for a section that has no rows yet but lives inside a populated screen. */
.emptystate { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); padding: var(--space-3) 0 var(--space-1); color: var(--muted); font-size: var(--font-size-body); }
.emptystate p { margin: 0; }

/* Action-bar grouping: a hairline divider separating the primary/run group from
   the secondary/navigate group inside a `.row-actions` flow, so the Translate
   primary is not lost in a wall of equal-weight controls (document Segments). */
.row-actions .act-sep { align-self: stretch; width: 1px; margin: 3px 3px; background: var(--border-soft); }

/* Project Overview: lead the metric readout with a real progress bar (a non-tile
   element that breaks the identical-tile grid) so approved-% reads as the primary
   metric and the remaining resource counts become a secondary detail row. */
.overview-progress { display: flex; align-items: center; gap: var(--space-3); margin: 0 0 var(--space-4); flex-wrap: wrap; }
.overview-progress .prog { width: clamp(160px, 40%, 320px); height: 8px; }

/* ============================================================================
   Visualization quick-wins (VIZ-RESEARCH-2026-07-21) — TM-leverage stacked bar.
   Signature loc-analytics visual (Lokalise match-distribution / Crowdin Savings
   Summary analog): a single horizontal part-to-whole bar, band colors REUSE the
   existing reserved-status palette (never a new hue) so it stays consistent with
   the segment-status chips / TM-match badges elsewhere. Scoped to .card .tmlev
   (its page ancestor, per the tokens-only / scoped-selectors rule) — it only ever
   renders inside a card on project.html Overview or reports.html.
   ============================================================================ */
.card .tmlev { margin-top: var(--space-3); }
.card .tmlev-heading {
  font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); margin: 0 0 6px;
}
.card .tmlev-bar {
  display: flex; height: 14px; border-radius: 4px; overflow: hidden;
  background: var(--panel-2); border: 1px solid var(--border-soft);
}
.card .tmlev-seg { height: 100%; }
.card .tmlev-seg.tmlev-exact { background: var(--success); }
.card .tmlev-seg.tmlev-fuzzy { background: var(--warn); }
.card .tmlev-seg.tmlev-new { background: var(--muted); }
.card .tmlev-seg.tmlev-untranslated { background: var(--border); }
.card .tmlev-legend {
  list-style: none; display: flex; flex-wrap: wrap; gap: 6px 16px;
  margin: 8px 0 0; padding: 0; font-size: .82rem; color: var(--text-dim);
}
.card .tmlev-key { display: inline-flex; align-items: center; gap: 6px; }
.card .tmlev-key b { margin-left: 2px; color: var(--text); font-weight: 700; }
.card .tmlev-swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; flex: none; }
.card .tmlev-key.tmlev-exact .tmlev-swatch { background: var(--success); }
.card .tmlev-key.tmlev-fuzzy .tmlev-swatch { background: var(--warn); }
.card .tmlev-key.tmlev-new .tmlev-swatch { background: var(--muted); }
.card .tmlev-key.tmlev-untranslated .tmlev-swatch { background: var(--border); }

/* CV-1: the 7-band WORD-based leverage bar. Three hue FAMILIES (covered=leverage
   green, fuzzy=amber, new=muted), the covered/fuzzy tiers separated by opacity so
   seven labelled bands stay legible without inventing seven distinct hues. Reuses
   the tmlev-bar geometry. */
.card .lev7-bar {
  display: flex; height: 14px; border-radius: 4px; overflow: hidden;
  background: var(--panel-2); border: 1px solid var(--border-soft);
}
.card .lev7-seg { height: 100%; }
.card .lev7-seg.lev7-repetitions { background: var(--success); opacity: .6; }
.card .lev7-seg.lev7-context     { background: var(--success); opacity: .8; }
.card .lev7-seg.lev7-exact       { background: var(--success); }
.card .lev7-seg.lev7-fuzzy_high  { background: var(--warn); opacity: .55; }
.card .lev7-seg.lev7-fuzzy_mid   { background: var(--warn); opacity: .75; }
.card .lev7-seg.lev7-fuzzy_low   { background: var(--warn); }
.card .lev7-seg.lev7-new         { background: var(--muted); }
.card .lev7-legend {
  list-style: none; display: flex; flex-wrap: wrap; gap: 6px 16px;
  margin: 8px 0 0; padding: 0; font-size: .82rem; color: var(--text-dim);
}
.card .lev7-key { display: inline-flex; align-items: center; gap: 6px; }
.card .lev7-key b { margin-left: 2px; color: var(--text); font-weight: 700; }
.card .lev7-swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; flex: none; }
.card .lev7-key.lev7-repetitions .lev7-swatch { background: var(--success); opacity: .6; }
.card .lev7-key.lev7-context .lev7-swatch     { background: var(--success); opacity: .8; }
.card .lev7-key.lev7-exact .lev7-swatch       { background: var(--success); }
.card .lev7-key.lev7-fuzzy_high .lev7-swatch  { background: var(--warn); opacity: .55; }
.card .lev7-key.lev7-fuzzy_mid .lev7-swatch   { background: var(--warn); opacity: .75; }
.card .lev7-key.lev7-fuzzy_low .lev7-swatch   { background: var(--warn); }
.card .lev7-key.lev7-new .lev7-swatch         { background: var(--muted); }

/* ============================================================================
   Visualization quick-wins — the spend-cap meter (DESIGN.md §5 "Control Surfaces"
   specifies this exact module: Label-role title, large Data-role spend figure, a
   9px meter with accent fill + an Amber Hold cap tick at the auto-pause
   threshold). It never existed as a graphic before this wave — the spend banner
   was text-only. Scoped to .card .spend-meter (setup_ai.html's Spend cap card)
   and .warnbar.spendbar (base.html's app-shell trip banner).
   ============================================================================ */
.card .spend-meter { margin: 6px 0 12px; }
.card .spend-meter-title {
  font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin: 0 0 4px;
}
.card .spend-meter-figure { font-size: 1.55rem; font-weight: 750; color: var(--text); margin: 0 0 8px; }
.card .spend-meter-of { font-size: 1rem; font-weight: 600; color: var(--text-dim); }
.card .spend-meter-track { position: relative; max-width: 420px; margin-bottom: 6px; }
.card .spend-meter-track .prog { width: 100%; height: 9px; }
.card .spend-meter-track .prog-bar.spend-danger { background: var(--danger); }
.card .spend-meter-tick {
  position: absolute; top: -2px; width: 2px; height: 13px;
  background: var(--warn); border-radius: 1px; transform: translateX(-1px); pointer-events: none;
}
.card .spend-meter-legend { margin: 0; display: flex; align-items: center; gap: 8px; }
/* CV-5: the compact per-department token-usage meter in the budgets table cell —
   a small .prog track + a percent readout, reusing the danger fill for over-cap. */
.usage-bar { display: flex; align-items: center; gap: 8px; }
.usage-bar .prog { width: 90px; height: 6px; }
.usage-bar-pct { color: var(--text-dim); white-space: nowrap; }
/* the app-shell trip banner: a compact always-danger meter beneath the existing
   text (the banner only renders when the cap IS tripped, so this is always the
   at/over-cap state — no tick needed, the numbers are in the text right above). */
.warnbar.spendbar .prog { margin-top: 8px; max-width: 300px; height: 6px; }
.warnbar.spendbar .prog-bar { background: var(--danger); }

/* ============================================================================
   Per-page polish — Wave B (automation surfaces: editor, workflow builder,
   author-review, connectors, connector_type, connection). Hierarchy, calm
   sectioning of long forms, primary-action clarity, and a genuine alert
   affordance. APPEND-ONLY, existing tokens only (token layer untouched),
   no class/id renames — the editor --row-* state tints, every JS hook, and the
   read-only SVG step graph are preserved. Theme-agnostic.
   ============================================================================ */

/* Notice / callout: a real alert affordance for the "could not be created",
   "review these before creating", and "some steps were LLM-adapted" messages
   that shipped as a stretched pill (display:block on a .badge — a badge is a
   reading, not an alert). The neutral base is dim ink on the second neutral
   layer; -warn reuses the contrast-proven warn combo (identical values to
   .badge.warn) so no new color pair is introduced. */
.notice { padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--panel-2); color: var(--text-dim);
  font-size: var(--font-size-body); margin: var(--space-3) 0; max-width: var(--readable); }
.notice > :first-child { margin-top: 0; }
.notice > :last-child { margin-bottom: 0; }
.notice strong { color: var(--text); }
.notice ul { margin: var(--space-1) 0 0; padding-left: 1.2em; }
.notice ul li { margin: 2px 0; }
.notice-warn { background: var(--warn-soft); border-color: var(--warn-border); color: var(--warn); }
.notice-warn strong { color: inherit; }

/* Connector catalog (connectors.html only): on that page the Integrations tiles
   are .tile-link and lift via the base .tile:hover rule (kept). The MT-engine +
   roadmap tiles are non-navigational, so a hover lift there is a false
   affordance — suppress it, but scope the reset to the connectors static grids
   (.tilegrid-static, a marker added to just those two grids) so setup_ai.html's
   non-link .tile hover is byte-unaffected (it shares the app-wide .tilegrid). */
.tilegrid-static .tile:hover { transform: none; }
/* Roadmap (planned, not-yet-available) tiles read as clearly secondary. The
   .tile-inert class is used only on connectors.html's roadmap tiles. */
.tile-inert { opacity: .74; }
.tile-inert:hover { border-color: var(--border); }

/* Workflow builder step cards: lift the step NAME to primary within a dense
   chip row and give each block a hover + calmer vertical rhythm, so the list
   reads as an ordered sequence rather than a wall. The chips/badges are emitted
   unchanged (branch, key, QE gate, guard … are load-bearing state readouts).
   ALL scoped under .steplist — the workflow_builder step list's stable ancestor
   — so run_view.html (which uses .runstep + a bare .step-head, no .steplist) is
   byte-unaffected. */
.steplist .step { transition: border-color var(--motion-fast); }
.steplist .step:hover { border-color: var(--hover-border); }
.steplist > li.step { margin: 10px 0; }
.steplist .step-head > strong:first-child { font-size: .95rem; color: var(--text); margin-right: 2px; }

/* Long connector forms (connector_type / connection): the Settings and
   Credentials groups now sit inside fieldset legends for calm sectioning. Keep
   the "· stored encrypted" / "· leave blank to keep" legend notes quiet so they
   don't inherit the legend's 600 weight and shout. */
.form fieldset.lp-fieldset > legend .muted { font-weight: 400; }

/* ============================================================================
   Per-page polish — Wave C (data & operations surfaces: tm / tm_detail /
   glossaries / glossary_detail / jobs / settings / reports).
   Glanceable status, ledger-grade number columns, genuine actionable empty
   states. APPEND-ONLY, existing tokens only (token layer untouched), no
   class/id renames (JS hooks preserved), theme-agnostic.
   ============================================================================ */

/* Status badges as GLANCEABLE signal dots (jobs.html; DESIGN §5 "the machine is
   working" = accent, failure = Vermilion Stop, held = Amber Hold). .run is the
   in-progress/running "the machine is working" state — accent dot that PULSES
   (one expanding ring, see the machinery section). .danger is the failed state —
   Vermilion Stop dot + label. Both map to existing tokens, so the state-colour-
   on-surface contrast the six-combo gate now enforces holds in all six combos. */
.badge.run { --sig: var(--primary-2); --sig-ink: var(--primary-2); }
.badge.danger { --sig: var(--danger); --sig-ink: var(--danger); }

/* Progress at 100% turns Growth Green (DESIGN §5 progress spec) so a finished
   job's bar is unmistakable from an in-flight accent one. Scoped to the
   `.done` modifier — only jobs.html sets it; every other .prog-bar is untouched. */
.prog-bar.done { background: var(--success); }

/* Report bars: give the VALUE column the Tabular Ledger treatment (DESIGN
   "The Tabular Ledger Rule" — any column of numbers is mono/tabular, right-
   aligned where compared). The label already right-aligns toward its bar; the
   value now sits in a fixed mono column so counts line up down the stack. */
.barval { font-family: var(--data-font); font-variant-numeric: tabular-nums; text-align: right; color: var(--text-dim); }

/* Genuine, actionable empty state for a filtered table that returned nothing:
   reuse the Wave-A `.emptystate` row (quiet, ends in a clear next action) so a
   no-match result offers a way back instead of a dead end. */
.emptystate a:not(.btnlink) { color: var(--primary-2); font-weight: 550; }

/* ============================================================================
   Batch language-matrix run monitor (SPEC-workflows-redesign Phase 5b).
   Page-scoped to batch_view.html. TOKENS ONLY — every colour is an existing
   :root token (defined for dark and light), so the status chips
   below inherit both themes automatically.
   The status chips reuse the SAME success/warn/primary/danger families the
   segment-status chips (.chip.st-*) already use; failed reuses the danger row
   wash (--row-failed) that has a value in both themes.
   ============================================================================ */
.bm-agg { margin: 6px 0; }
.bm-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 10px; }
.bm-inline { display: inline; margin: 0; }
.bm-refresh { color: var(--primary-2); font-weight: 550; text-decoration: none; }
.bm-refresh:hover { text-decoration: underline; }

/* The matrix: compact, tabular-ledger cells. Keeps .table chrome; only tightens
   the cell padding so a wide step axis stays scannable, and centres the chips. */
.bm-matrix th, .bm-matrix td { padding: 8px 10px; white-space: nowrap; }
.bm-matrix td.bm-cell { text-align: center; }
.bm-matrix thead th { vertical-align: bottom; }
.bm-legend { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; }

/* Per-cell / per-row status chips, color-by-status. Same "Signal dots" family as
   the .chip.st-* chips (machinery section resets these bm-* selectors to a
   borderless dot + uppercase label). bm-run is the live "running" state and
   PULSES; bm-fail is Vermilion Stop; bm-rev/bm-part are Amber Hold; the rest are
   the neutral reading. Values are the same success/warn/primary-2/danger tokens
   the six-combo contrast gate now covers on --panel/--panel-2. */
.chip.bm-ok      { --sig: var(--success); --sig-ink: var(--success); }
.chip.bm-run     { --sig: var(--primary-2); --sig-ink: var(--primary-2); }
.chip.bm-rev     { --sig: var(--warn); --sig-ink: var(--warn); }
.chip.bm-fail    { --sig: var(--danger); --sig-ink: var(--danger); }
.chip.bm-part    { --sig: var(--warn); --sig-ink: var(--warn); }
.chip.bm-skip    { --sig: var(--muted); --sig-ink: var(--muted); }
.chip.bm-na      { --sig: var(--muted); --sig-ink: var(--muted); }
.chip.bm-pending { --sig: var(--muted); --sig-ink: var(--text-dim); }

/* Heatmap tint (VIZ-RESEARCH-2026-07-21 quick-win): a light alpha wash of the
   SAME status-family color the chip already carries, painted on the cell behind
   it, so the eye finds hot cells at a glance across a wide locale x step matrix.
   REDUNDANT reinforcement only — the chip + label remain the actual signal, this
   never stands alone (a cell with no chip, e.g. an em-dash "not in this variant",
   gets no tint class and stays untinted). color-mix() keeps this to EXISTING
   tokens (--success/--primary-2/--warn/--danger/--muted, already six-combo
   contrast-checked as text/border colors) rather than inventing six new -soft
   background tokens; percentages are kept low (8-14%) to match the existing
   *-soft token family's alpha range. */
.bm-matrix td.bm-cell.bm-ok      { background: color-mix(in srgb, var(--success) 12%, transparent); }
.bm-matrix td.bm-cell.bm-run     { background: color-mix(in srgb, var(--primary-2) 12%, transparent); }
.bm-matrix td.bm-cell.bm-rev     { background: color-mix(in srgb, var(--warn) 14%, transparent); }
.bm-matrix td.bm-cell.bm-fail    { background: color-mix(in srgb, var(--danger) 12%, transparent); }
.bm-matrix td.bm-cell.bm-part    { background: color-mix(in srgb, var(--warn) 14%, transparent); }
.bm-matrix td.bm-cell.bm-skip,
.bm-matrix td.bm-cell.bm-na,
.bm-matrix td.bm-cell.bm-pending { background: color-mix(in srgb, var(--muted) 8%, transparent); }

/* Cell adornments: the LQA/QE score + the loop visit-count ride next to the
   chip in the data font; the em-dash marks a step a diverged variant lacks. */
.bm-score { color: var(--text-dim); font-size: .78rem; margin-left: 2px; }
.bm-visits { color: var(--muted); font-size: .74rem; margin-left: 2px; }
.bm-dash { color: var(--muted); }
.bm-div { color: var(--warn); font-weight: 700; margin-left: 3px; }
.bm-rowaction { text-align: right; }

/* ============================================================================
   Workflows create surface: "Design a workflow" info affordance (copy-density
   cleanup -- replaces the old stacked heading + two paragraphs with one h2 and
   a hover/focus tooltip). Page-scoped under .wf-create (the create-surface
   .card's own marker, added alongside it) so this circular-badge treatment
   doesn't leak onto any other title=-carrying element app-wide (past waves
   shipped unscoped selectors that bled onto neighbour pages -- see the
   .steplist / .tilegrid-static scoping fix). TOKENS ONLY: --panel-2/--border/
   --muted mirror the existing .badge resting state, so both themes
   inherit with no new token. */
.wf-create h2 { display: flex; align-items: center; gap: 8px; }
.wf-create .wf-info-btn {
  width: 18px; height: 18px; padding: 0; display: inline-grid; place-items: center;
  border-radius: 50%; background: var(--panel-2); color: var(--muted);
  border: 1px solid var(--input-border); font-size: .72rem; font-weight: 700;
  font-style: italic; line-height: 1; cursor: help; box-shadow: none;
}
.wf-create .wf-info-btn:hover { border-color: var(--primary); color: var(--primary-2); filter: none; }
/* No .wf-info-btn:focus-visible rule needed -- it's a <button>, so the
   app-wide button:focus-visible ring (defined earlier in this file) already
   applies without any scoped override. */

/* ============================================================================
   Signal-dot machinery (Option A status indicators). Shared geometry for the badge + status-chip families: the
   coloured DOT (::before), the live PULSE ring (::after on the working/live
   states), the HOLLOW dot (Beta / Semantic), the SQUARED dot (Paused), the
   narrow-width label collapse, and the reduced-motion degrade. Colours arrive
   through the per-variant --sig / --sig-ink custom props assigned above; every
   value resolves to an EXISTING token, so both themes inherit with
   NO new :root token (token layer untouched). This also RESETS the base
   `.chip` pill for the st- and bm- status subsets only (base `.chip` — role /
   coverage / removable chips — keeps its pill). APPEND-ONLY, tokens only.
   ============================================================================ */
/* STATUS-scoped: a `.badge` gets the dot treatment only with a state modifier or
   the explicit `.status` marker — never bare (see the base-badge note above). */
.badge.status, .badge.run, .badge.ok, .badge.warn, .badge.danger, .badge.exact,
.badge.fuzzy, .badge.semantic, .badge.reauth, .badge.paused, .badge.beta,
.chip.st-approved, .chip.st-needs_review, .chip.st-edited, .chip.st-mt, .chip.st-new,
.chip.bm-ok, .chip.bm-run, .chip.bm-rev, .chip.bm-fail, .chip.bm-part,
.chip.bm-skip, .chip.bm-na, .chip.bm-pending {
  display: inline-flex; align-items: center; gap: 6px; position: relative;
  background: transparent; border: 0; padding: 0; border-radius: 0;
  font-size: .8125rem; font-weight: 500; line-height: 1.4; letter-spacing: 0;
  text-transform: none; white-space: nowrap; vertical-align: middle;
  color: var(--sig-ink, var(--muted));
}
/* the dot itself */
.badge.status::before, .badge.run::before, .badge.ok::before, .badge.warn::before,
.badge.danger::before, .badge.exact::before, .badge.fuzzy::before, .badge.semantic::before,
.badge.reauth::before, .badge.paused::before, .badge.beta::before,
.chip.st-approved::before, .chip.st-needs_review::before, .chip.st-edited::before,
.chip.st-mt::before, .chip.st-new::before,
.chip.bm-ok::before, .chip.bm-run::before, .chip.bm-rev::before, .chip.bm-fail::before,
.chip.bm-part::before, .chip.bm-skip::before, .chip.bm-na::before, .chip.bm-pending::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto;
  background: var(--sig, var(--muted));
}
/* HOLLOW dot (non-colour redundancy): Beta + Semantic-match read as a ring. */
.badge.beta::before, .badge.semantic::before {
  background: transparent; box-shadow: inset 0 0 0 1.6px var(--sig);
}
/* SQUARED dot: Paused differs from Review (both --warn) by SHAPE, not colour. */
.badge.paused::before { border-radius: 2px; }
/* LIVE PULSE — one expanding ring over the dot, on the "the machine is working"
   / "reconnect needed" states. Absolutely positioned so it stays concentric with
   the left-edge dot (the first flex item) instead of trailing the label. */
.badge.run::after, .badge.reauth::after, .chip.bm-run::after {
  content: ""; position: absolute; left: 0; top: 50%; margin-top: -4px;
  width: 8px; height: 8px; border-radius: 50%; box-sizing: border-box;
  border: 1.5px solid var(--sig); opacity: .6; pointer-events: none;
  animation: sig-pulse 1.6s ease-out 3;
}
@keyframes sig-pulse {
  0%   { transform: scale(.6);  opacity: .6; }
  100% { transform: scale(2.3); opacity: 0; }
}
/* Reduced-motion degrade (belt-and-suspenders over the app-wide reduce block at
   the `prefers-reduced-motion` rule earlier in this file, which already forces
   animation-iteration-count:1 on *::after). Here we make the intent explicit and
   testable: the pulse ring is removed entirely and the static dot alone carries
   the state, so no information depends on motion. */
@media (prefers-reduced-motion: reduce) {
  .badge.run::after, .badge.reauth::after, .chip.bm-run::after {
    animation: none; opacity: 0;
  }
}

/* ============================================================================
   Workflow Studio — Wave B AIDS RAIL (/workflows/new). A second pane of
   click-to-insert intelligence beside the create flow. EVERYTHING is scoped
   under `.wf-studio` (the two-pane wrapper) so none of this leaks onto other
   pages (the .steplist / .wf-create scoping precedent). TOKENS ONLY — every
   colour/space/radius reads an existing custom prop, so both themes
   inherit with no new :root token. APPEND-ONLY.

   2026-09-03 visual cleanup: the rail is a TABBED toolbox (one panel visible at
   a time once JS adds `.wf-tabbed`; stacked without JS), sticky beside the
   description box, and the create column is compacted (provider + model on one
   row, the two secondary start paths side by side in one card).
   ============================================================================ */
.wf-studio {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(min(380px, 100%), 45%);
  gap: var(--space-5);
  align-items: start;
}
.wf-studio-main { min-width: 0; }
/* Stack the rail under the create flow on narrower viewports. */
@media (max-width: 980px) {
  .wf-studio { grid-template-columns: 1fr; }
  .wf-studio-rail { position: static; max-height: none; }
}

/* --- Create column ------------------------------------------------------- */
.wf-hero { margin-bottom: var(--space-4); }
.wf-hero p { margin: 0; }
.wf-create textarea { min-height: 150px; resize: vertical; font-size: .95rem; line-height: 1.5; }
.wf-describe-hint { font-weight: 400; }
.wf-create-row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  gap: var(--space-4); align-items: start;
}
@media (max-width: 1180px) { .wf-create-row { grid-template-columns: 1fr; } }
.wf-create-row .wfmodel .row-actions { margin-top: 2px; }
.wf-create-actions { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

.wf-other { margin-top: var(--space-4); }
.wf-other-h { font-size: .95rem; margin: 0 0 var(--space-2); }
.wf-other-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
@media (max-width: 1180px) { .wf-other-grid { grid-template-columns: 1fr; } }
.wf-other-grid > details { margin: 0; align-self: start; }
.wf-other-grid > details p { margin: var(--space-2) 0 0; }
.wf-other-grid > details .inline-form { margin-top: var(--space-2); }

/* --- Rail ---------------------------------------------------------------- */
.wf-studio-rail {
  min-width: 0; display: flex; flex-direction: column; gap: var(--space-3);
  position: sticky; top: 80px;
  max-height: calc(100vh - 100px); overflow-y: auto; overflow-x: hidden;
  padding-right: 2px;
}
.wf-rail-head { display: flex; flex-direction: column; gap: 4px; }
.wf-rail-title { font-family: var(--font-display, inherit); font-size: 1.1rem; margin: 0; }
.wf-studio-rail .wf-rail-lead { margin: 0; }

/* Tab strip: a segmented row of pill buttons with a count badge. Hidden until JS
   runs (the [hidden] attribute is removed by the scoped script). */
.wf-tabs {
  display: flex; flex-wrap: wrap; gap: 2px;
  padding: 4px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--panel);
}
.wf-tabs[hidden] { display: none; }
.wf-tab {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 7px; font-size: .76rem; font-weight: 600; cursor: pointer;
  flex: 1 1 auto; justify-content: center; white-space: nowrap;
  background: transparent; color: var(--muted);
  border: 1px solid transparent; border-radius: var(--radius-pill, 999px);
  box-shadow: none; width: auto;
}
.wf-tab:hover { color: var(--text); border-color: var(--border); filter: none; }
.wf-tab.is-active { background: var(--panel-2); color: var(--text); border-color: var(--primary); }
.wf-tab-n {
  font-family: var(--data-font, var(--mono, monospace)); font-weight: 500;
  font-size: .66rem; line-height: 1; padding: 2px 4px; border-radius: 999px;
  background: var(--bg-2, var(--panel-2)); color: var(--text-dim);
  border: 1px solid var(--border-soft);
}
.wf-tab.is-active .wf-tab-n { border-color: var(--primary); color: var(--text); }
/* Narrow desktops: keep the five tabs on ONE row by dropping the static counts
   (the Customer count stays: it is the one that changes with the selection). */
@media (max-width: 1560px) {
  .wf-tab:not([data-tab="know"]) .wf-tab-n { display: none; }
}

/* With JS: only the active panel shows. Without JS: everything stacks (unchanged). */
.wf-studio.wf-tabbed .wf-rail-card[data-panel]:not(.is-active) { display: none; }
/* The 'Insert lines (copy)' blocks exist for the zero-JS reader; with JS the chips
   themselves insert, so the duplicate copy block is hidden. */
.wf-studio.wf-tabbed .wf-nojs-copy { display: none; }

.wf-rail-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
}
.wf-rail-h {
  font-family: var(--font-display, inherit);
  font-size: 1rem; margin: 0 0 var(--space-2);
  display: flex; align-items: center; gap: var(--space-2);
}
.wf-rail-sub { margin: 0 0 var(--space-3); }

.wf-rail-list { list-style: none; margin: 0; padding: 0; display: flex;
  flex-direction: column; gap: var(--space-2); }

/* A click-to-insert control: full-width, left-aligned, ghost, hover -> accent
   border (the DESIGN.md secondary-button hover rule). */
.wf-ins {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: var(--panel-2); color: var(--text);
  border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  padding: 8px 10px; box-shadow: none;
  transition: border-color var(--motion-fast), background var(--motion-fast);
}
.wf-ins:hover { border-color: var(--primary); filter: none; }
.wf-ins.wf-ins-done { border-color: var(--primary); background: var(--panel); }
.wf-ins .wf-ins-name { display: block; font-weight: 600; color: var(--text); }
.wf-ins .wf-ins-preview {
  display: block; margin-top: 2px; color: var(--muted);
  font-size: .78rem; line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wf-shape .wf-ins-preview { white-space: normal; }

.wf-ins-item { min-width: 0; }
/* The "full text" / "Insert lines (copy)" disclosures are quiet inline links, not
   the app-wide bordered <details> card (which made every preset look like two rows). */
.wf-studio-rail .wf-rail-card details.wf-ins-full-wrap {
  margin: 0; border: none; background: transparent; padding: 0;
}
.wf-studio-rail .wf-rail-card details.wf-ins-full-wrap > summary {
  cursor: pointer; padding: 2px 4px; font-size: .74rem; color: var(--muted);
  display: inline-flex; gap: 4px;
}
.wf-studio-rail .wf-rail-card details.wf-ins-full-wrap > summary:hover { background: transparent; color: var(--text); }
.wf-studio-rail .wf-rail-card details.wf-ins-full-wrap[open] > summary { border-bottom: none; }
.wf-studio-rail .wf-rail-card details.wf-ins-full-wrap > *:not(summary) { padding: 0; }
.wf-ins-full {
  margin: 4px 0 0; padding: var(--space-2);
  background: var(--bg-2, var(--panel-2)); border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  font-family: var(--mono, monospace); font-size: .72rem; line-height: 1.4;
  color: var(--text-dim); white-space: pre-wrap; word-break: break-word;
}

/* Zero-JS "Insert lines (copy)" block: the exact insert sentences as selectable
   plain text (pairs / glossary terms / style guides whose buttons show a shorthand). */
.wf-ins-lines { list-style: none; margin: 4px 0 0; padding: var(--space-2);
  background: var(--bg-2, var(--panel-2)); border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm); }
.wf-ins-lines li { font-size: .74rem; line-height: 1.5; color: var(--text-dim);
  word-break: break-word; }

/* Placeholder + pair chips: compact pill buttons that wrap. */
.wf-chip-row { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.wf-chip {
  display: inline-block; width: auto; cursor: pointer;
  background: var(--panel-2); color: var(--text);
  border: 1px solid var(--input-border); border-radius: var(--radius-pill, 999px);
  padding: 3px 10px; font-family: var(--data-font, var(--mono, monospace));
  font-size: .76rem; box-shadow: none;
}
.wf-chip:hover { border-color: var(--primary); filter: none; }

/* "What we already know" panel. */
.wf-know-picker { display: flex; align-items: flex-end; gap: var(--space-2);
  margin-bottom: var(--space-3); }
.wf-know-picker label { flex: 1 1 auto; min-width: 0; }
.wf-know-empty { margin: 0; }
.wf-know-sec { margin-top: var(--space-3); }
.wf-know-h {
  font-size: .72rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 var(--space-2);
}
.wf-know-name { font-weight: 600; color: var(--text); margin-right: 6px; }
.wf-know-tm { margin: 0; color: var(--text); }

.wf-studio-rail .wf-rail-card details.wf-know-gloss { border: 1px solid var(--border-soft); border-radius: var(--radius-sm);
  padding: 6px 8px; margin: 0 0 var(--space-2); background: transparent; }
.wf-studio-rail .wf-rail-card details.wf-know-gloss > summary { cursor: pointer; display: flex; flex-wrap: wrap;
  align-items: baseline; gap: 6px; padding: 2px 0; }
.wf-studio-rail .wf-rail-card details.wf-know-gloss[open] > summary { border-bottom: none; }
.wf-studio-rail .wf-rail-card details.wf-know-gloss > *:not(summary) { padding: 0; }
.wf-know-terms { list-style: none; margin: var(--space-2) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(175px, 1fr)); gap: 4px; }
.wf-know-terms .wf-term { font-size: .78rem; padding: 5px 8px; height: 100%; }
.wf-term-src { font-weight: 600; }
.wf-term-tgt { color: var(--primary-2, var(--primary)); }
.wf-term-dnt {
  font-family: var(--data-font, var(--mono, monospace)); font-size: .66rem; font-weight: 600;
  letter-spacing: .04em; padding: 1px 5px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--muted); margin-right: 2px;
}

.wf-know-ctx { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 4px; }
.wf-know-ctx li { line-height: 1.4; }

/* ============================================================================
   Workflow step-header hierarchy (the maintainer's P1 pain: scrolling a LONG custom
   workflow, he loses track of where one step ends and the next begins). Makes
   each step's header unmistakable while scrolling -- a bigger/bolder Headline-
   role step number, a quiet step-type chip beside it, and a clear rule/spacing
   break separating one step from the next. The rest of each step's form is
   untouched.
   Scoped two ways, both following the existing scoping precedents in this
   file: (a) `.steplist .step-head` -- workflow_builder.html's read-view step
   list; `.step-head` is a bare class ALSO used unscoped by run_view.html (see
   the "leak lesson" note above the ARC/gantt rules), so everything here is
   qualified under `.steplist`, run_view's stable per-step ancestor is absent
   there, so it is untouched. (b) `.wf-step-fieldset` / `.wf-step-legend` --
   brand-new class names added only to workflow_author_review.html's per-step
   fieldset/legend, so no ancestor qualifier is needed (nothing else in the app
   can ever carry them). `.wf-step-num` is shared verbatim by both surfaces (one
   visual language for "this is a step boundary"). TOKENS ONLY -- no new colour
   pairs; reuses --text/--border/--border-soft/--font-display/--space-*, so
   both themes inherit with zero new contrast-check entries.
   APPEND-ONLY.

   Sticky positioning was tried for the per-step header and DELIBERATELY
   DROPPED. A <legend> that receives a non-static `position` stops being laid
   out as a fieldset legend (it loses the border-straddling notch and is
   treated as a plain block by the fieldset layout algorithm), which is an
   unreliable, engine-inconsistent trade purely to gain stickiness. Worse, the
   author-review page's per-step fieldsets vary hugely in height (data-types
   field-hiding means an lqa/human_review step is a fraction of an llm_agent
   step's height), so a pinned header for a short step would still be sitting
   over the START of the NEXT step's content on a fast scroll -- the classic
   stacked-sticky-header overlap glitch, and exactly the kind of thing the spec
   said to skip rather than ship janky. A strong static header (size, weight,
   rule, spacing) resolves the "lose my place" complaint without that risk.
   ============================================================================ */
.wf-step-num {
  font-family: var(--font-display);
  font-size: 1.125rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
  color: var(--text);
}

/* -- workflow_builder.html: the read-view step list -- */
.steplist > li.step {
  border-top: 2px solid var(--border);
  margin: var(--space-4) 0;
  padding-top: var(--space-3);
}
.steplist > li.step:first-child { margin-top: var(--space-2); }
.steplist .step-head {
  border-bottom: 1px solid var(--border-soft);
  padding-bottom: var(--space-2);
  margin-bottom: var(--space-2);
}
.steplist .step-head .wf-step-num { margin-right: 2px; }

/* -- workflow_author_review.html: the full per-step edit form -- */
.wf-step-fieldset {
  border-top: 3px solid var(--border);
  margin-top: var(--space-5);
  padding-top: var(--space-4);
}
.wf-step-fieldset:first-of-type { margin-top: 10px; }
.wf-step-legend {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: 0 8px;
}
.wf-step-type-chip { font-family: var(--data-font, var(--mono, monospace)); }

/* ============================================================
   ASK + DRAFT (SPEC-ask-draft) -- the "?" module-guide link, the grounded
   "Ask about this page" panel, and the pre-filled Draft forms. All selectors
   are namespaced (.help-*, .assist-*) so nothing here can leak app-wide.
   ============================================================ */
.help-hint {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.35em;
  height: 1.35em;
  margin-left: var(--space-2);
  border: 1px solid var(--border);
  border-radius: 50%;
  font-size: 0.6em;
  font-weight: 700;
  line-height: 1;
  color: var(--muted);
  text-decoration: none;
  vertical-align: middle;
}
.help-hint:hover { color: var(--fg); border-color: var(--muted); }

.assist-panel .assist-starters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-3) 0;
}
.assist-panel .assist-starters .chip {
  cursor: pointer;
  border: 1px solid var(--input-border);
  background: transparent;
  color: inherit;
}
.assist-panel .assist-answer:empty { display: none; }
.assist-panel .assist-answer {
  margin-top: var(--space-3);
  padding: var(--space-3);
  border-left: 3px solid var(--border);
}
.assist-panel .assist-answer p { margin: 0 0 var(--space-2); }
.assist-panel .assist-answer p:last-child { margin-bottom: 0; }
.assist-panel .assist-answer .assist-list { margin: 0 0 var(--space-2) 1.2em; }
.assist-panel.is-busy .assist-answer { opacity: 0.6; }
.assist-panel .grow { flex: 1 1 240px; }
/* F8: the keys-grid rail's content box is ~230px, narrower than the assist
   panel's usual 240px flex-basis for `.grow` -- scoped to the rail only so
   every other page keeps the wider default. */
.keys-rail .assist-panel .grow { flex: 1 1 auto; min-width: 0; }

.assist-draft-note {
  border-top: 2px solid var(--border);
  margin-top: var(--space-4);
  padding-top: var(--space-3);
}
.assist-degraded {
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 0.85em;
  opacity: 0.8;
}
.warn-note { color: var(--muted); margin: var(--space-2) 0; }
.checkline { display: block; }

.help-card-dl dt { font-weight: 600; margin-top: var(--space-3); }
.help-card-dl dd { margin: 0 0 0 0; }
.help-card-index { margin: 0; padding-left: 1.2em; }
.help-card-index li { margin-bottom: var(--space-2); }
.help-card-body h2:first-child { margin-top: 0; }

/* ============================================================================
   2026-09-03 VISUAL TIDY-UP (pre-demo sweep of every sidebar page). Six
   cross-cutting defects explained nearly everything the maintainer saw; each fix below
   names the page it was found on. TOKENS ONLY, APPEND-ONLY.
   ============================================================================ */

/* 1. Checkbox / radio labels (Data export, Vendors, Settings, Evaluations…):
      `.form label` is a COLUMN flex, so a leading <input type=checkbox|radio>
      landed on the line ABOVE its text. A label that starts with a tick/radio
      is a row, in text colour, with the control top-aligned to the first line. */
.form label:has(> input[type="checkbox"]):not(.choice-option):not(.toggle),
.form label:has(> input[type="radio"]):not(.choice-option):not(.toggle) {
  flex-direction: row; align-items: flex-start; gap: 9px;
  color: var(--text); font-weight: 400; cursor: pointer; line-height: 1.4;
}
.form label:has(> input[type="checkbox"]):not(.choice-option):not(.toggle) > input,
.form label:has(> input[type="radio"]):not(.choice-option):not(.toggle) > input {
  flex: 0 0 auto; margin: 3px 0 0; accent-color: var(--primary);
}

/* 2. Bare <fieldset> inside a form (Data export "Scope" / "History depth",
      Vendors "Add a vendor"): the browser default is the dated grey box with
      the legend cutting the border and no spacing between the rows. */
.form > fieldset:not(.lp-fieldset):not(.wf-step-fieldset),
.form fieldset:not(.lp-fieldset):not(.wf-step-fieldset):not([class]) {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel-2); padding: 12px 14px 14px; margin: 0; min-width: 0;
  display: flex; flex-direction: column; gap: 10px;
}
.form fieldset:not(.lp-fieldset):not(.wf-step-fieldset) > legend {
  font-size: .72rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); padding: 0 6px; margin-left: -6px;
}

/* 3. Inline forms (Customers, Users, Routing "Add rule", Content types, Audit,
      Glossaries…): `.form button` is align-self:flex-start, so the submit
      button floated ABOVE the input row it belongs to. Also the wrapper
      variant (<div class="inline-form"> inside a .form, e.g. Style guides
      source/target locale) was never display:flex, so its fields stacked. */
.inline-form { display: flex; }
.inline-form > button, .inline-form > .btnlink { align-self: flex-end; }
.inline-form label { min-width: 0; }
.inline-form label > .muted.small { max-width: 30ch; line-height: 1.3; }
/* A grow field takes the remaining row width (Evaluations "Describe what you want"). */
.inline-form > .grow { flex: 1 1 260px; }
.inline-form > .grow > input, .inline-form > .grow > select { width: 100%; max-width: none; }

/* 4. Empty states (Projects "Create from template", Workflows, Jobs, TM,
      Images, Users, Evaluations): `.card p` is capped at the readable width,
      so a centred empty-state paragraph sat left of the ⋯ glyph. Centre the
      block itself. */
.empty p { margin-left: auto; margin-right: auto; }
.empty::before { color: var(--muted); opacity: .55; }

/* 5. `.filters` (Jobs, My work, Queries) had NO rule: bare labels ran the
      "Type All Status All" filter row together. Same visual grammar as
      .filterbar: bottom-aligned row, small muted labels above their control. */
.filters {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 14px;
  margin: 0 0 var(--space-4);
}
.filters label {
  display: flex; flex-direction: column; gap: 5px;
  font-size: .8rem; color: var(--muted); font-weight: 500; min-width: 0;
}
.filters label:has(> input[type="checkbox"]) {
  flex-direction: row; align-items: center; gap: 7px;
  color: var(--text); font-weight: 400; padding-bottom: 8px;
}
.filters label:has(> input[type="checkbox"]) > input { margin: 0; accent-color: var(--primary); }
.filters > button, .filters > .btnlink { align-self: flex-end; }

/* 6. `.form.filterbar` (LLM Ledger "Runs" filter): .form forces a COLUMN with
      the filterbar's align-items:flex-end, which produced a right-aligned
      staircase of fields. A filter bar is a row. */
/* Top-aligned so a label that carries a hint under its control (Status) hangs
   the hint below the row instead of lifting every neighbour. */
.form.filterbar { flex-direction: row; align-items: flex-start; gap: 10px 14px; }
.form.filterbar > label { min-width: 0; }
.form.filterbar > label.check { flex-direction: row; align-items: center; gap: 7px; color: var(--text); font-weight: 400; margin-top: 21px; padding: 9px 0; }
.form.filterbar > label > .small.muted { max-width: 34ch; line-height: 1.3; margin-top: 2px; }
.form.filterbar > button, .form.filterbar > .btnlink { align-self: flex-start; margin-top: 21px; }

/* 7. Two-column setting rows (Security SSO / LDAP): the label text and its
      input share a row, but the input never grew, so placeholders truncated
      at ~20ch in a 600px column. */
.grid2 > label > input:not([type="checkbox"]):not([type="radio"]),
.grid2 > label > select { flex: 1 1 auto; min-width: 0; }

/* 8. Connector tiles: the "0 connections" chip and "Manage →" link wrapped
      into two-line pills at the tile foot. */
.tilegrid { grid-template-columns: repeat(auto-fill, minmax(min(232px, 100%), 1fr)); }
.tile-foot { gap: 8px; flex-wrap: wrap; }
.tile-foot .chip { white-space: nowrap; }
.tile-foot .btnlink { white-space: nowrap; padding: 5px 10px; margin-top: 0; }
/* 9. `.lp-fieldset` used as a plain form group (Vendors "Add a vendor"): its
      labels had no vertical rhythm. Only adjacent labels/buttons gain space, so
      the Languages page's list-bearing fieldsets are untouched. */
.form > fieldset.lp-fieldset > label + label,
.form > fieldset.lp-fieldset > label + button { margin-top: 10px; }
.form > fieldset.lp-fieldset > legend { font-weight: 600; color: var(--text); padding: 0 6px; }
/* 10. Security page (the one form NOT built on .form): "Protocol / Issuer /
       Client ID / Server URI…" are inline labels with a 200px default input
       inside a 530px grid column. Make each a row whose control fills it. */
form:not(.form) .grid2 > label { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px; }
form:not(.form) .grid2 > label > input:not([type="checkbox"]):not([type="radio"]),
form:not(.form) .grid2 > label > select { flex: 1 1 160px; min-width: 0; }
form:not(.form) .grid2 > label > .muted.small, form:not(.form) .grid2 > label > .small.muted { flex-basis: 100%; }

/* ============================================================================
   Settings > Features card (2026-09-03): a grid of feature cards with a real
   switch each, instead of one long stack of "title — On … [Disable]" rows.
   Scoped under #features. TOKENS ONLY, APPEND-ONLY.
   ============================================================================ */
#features .feat-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
#features .feat-head h2 { margin: 0; }
#features .feat-count { font-size: .85rem; }
#features .feat-count strong { color: var(--text); font-family: var(--data-font, var(--mono, monospace)); }

#features .feat-legend { list-style: none; margin: var(--space-3) 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 6px 18px; max-width: none; }
#features .feat-legend li { display: inline-flex; align-items: baseline; gap: 7px; }
#features .feat-more { margin: 0 0 var(--space-4); }
#features .feat-more p { max-width: none; }

#features .feat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: var(--space-3); }
#features .feat-card {
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 12px 14px 12px; display: flex; flex-direction: column; gap: 6px; min-width: 0;
  transition: border-color var(--motion-fast), box-shadow var(--motion-fast);
}
#features .feat-card.is-on { border-color: var(--border); box-shadow: inset 3px 0 0 var(--success); }
#features .feat-card.is-off { box-shadow: inset 3px 0 0 var(--border); }
#features .feat-card.is-off .feat-title strong { color: var(--text-dim); }
#features .feat-card.is-busy { opacity: .6; pointer-events: none; }
#features .feat-card.feat-flash { animation: feat-flash .9s ease-out; }
@keyframes feat-flash { from { background: var(--primary-soft); } to { background: var(--panel-2); } }

#features .feat-top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); }
#features .feat-title { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; min-width: 0; }
#features .feat-title strong { font-size: .95rem; }
#features .feature-toggle { display: block; flex: 0 0 auto; margin: 0; }
#features .feat-desc { margin: 0; font-size: .83rem; line-height: 1.45; max-width: none; }
#features .feat-foot { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 8px; margin-top: 2px; }
#features .feat-sentence { display: none; }
#features .feat-state {
  font-size: .7rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border); color: var(--muted);
}
#features .feat-card.is-on .feat-state { color: var(--success); border-color: var(--success); }
#features .feat-moved-link { white-space: nowrap; font-size: .78rem; padding: 5px 9px; }

/* Semantics chips: the same three colours on the legend and on every card. */
.feat-sem {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  font-size: .72rem; font-weight: 600; padding: 2px 8px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--panel); color: var(--text-dim);
}
.feat-sem::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--sig, var(--muted)); }
.feat-sem[data-sem="removed"]    { --sig: var(--danger); }
.feat-sem[data-sem="writes_off"] { --sig: var(--warn); }
.feat-sem[data-sem="opt_in"]     { --sig: var(--primary-2, var(--primary)); }

/* The switch. It is a real submit <button>, so every global button rule is reset here. */
#features .feat-switch {
  position: relative; width: 42px; height: 24px; padding: 0; margin: 0; flex: 0 0 auto;
  border-radius: 999px; border: 1px solid var(--input-border); background: var(--input-border);
  box-shadow: none; cursor: pointer; font-size: 0; line-height: 0; min-height: 0;
  transition: background var(--motion-fast), border-color var(--motion-fast); filter: none;
}
#features .feat-switch:hover { filter: none; border-color: var(--muted); }
#features .feat-switch:focus-visible { outline: var(--focus-width) solid var(--ring); outline-offset: var(--focus-offset); box-shadow: 0 0 0 3px var(--ring-glow); }
#features .feat-knob {
  position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%;
  background: var(--panel); box-shadow: 0 1px 2px rgba(0, 0, 0, .3); transition: transform var(--motion-fast);
}
#features .feat-card.is-on .feat-switch { background: var(--success); border-color: var(--success); }
#features .feat-card.is-on .feat-knob { transform: translateX(18px); }

/* Inline confirm for switching OFF (replaces window.confirm). */
#features .feat-confirm {
  margin-top: 4px; padding: 9px 11px; border-radius: var(--radius-sm);
  border: 1px solid var(--warn); background: var(--panel);
}
#features .feat-confirm p { margin: 0 0 8px; font-size: .83rem; line-height: 1.45; max-width: none; }
#features .feat-confirm .row-actions { gap: 8px; }
#features .feat-error { margin: 4px 0 0; font-size: .8rem; max-width: none; }

/* ===========================================================================
   Advanced Workflows -- deferred UX train (operator help panel, mission template
   picker, per-field hints, and the per-unit transcript cards on the run page).
   APPEND-ONLY, existing tokens only (no new :root token, token layer
   untouched); nothing here edits a rule above this point. Theme-safe by
   construction: every colour is a token defined in both the dark :root and the
   light html[data-theme=light] block, so both themes get correct contrast. No em
   dashes in any content (the maintainer's copy rule) -- these are style rules only.
   =========================================================================== */
details.adv-help > summary { cursor: pointer; font-size: 1rem; }
details.adv-help[open] > summary { margin-bottom: .6rem; }
.adv-help-body section { margin: 0 0 .9rem; }
.adv-help-body h3 { margin: 0 0 .25rem; font-size: .95rem; }
.adv-help-body p { margin: .15rem 0; max-width: 60rem; }

.adv-tmpl {
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
  margin: 0 0 1rem; padding: .6rem .8rem;
  border: 1px solid var(--border-soft); border-radius: var(--radius-sm); background: var(--bg-2);
}
.adv-tmpl .adv-tmpl-desc { flex-basis: 100%; margin: .2rem 0 0; }

.field-hint { display: block; margin: .2rem 0 0; font-size: .8rem; line-height: 1.4; }

/* Per-unit transcript cards (run page). .pill / .finding / .lens-* / .tbadge / .kind / .sev /
   .blocks are already defined in the run page's own inline <style>; these add only the card. */
.adv-unit {
  border: 1px solid var(--border); border-left-width: 3px; border-radius: var(--radius-sm);
  margin: .5rem 0; padding: .55rem .75rem;
}
.adv-unit-delivered { border-left-color: var(--success); }
.adv-unit-held { border-left-color: var(--danger); }
.adv-unit-skipped { border-left-color: var(--warn); }
.adv-unit-staged { border-left-color: var(--muted); }
.adv-unit-head { display: flex; align-items: baseline; gap: .5rem; margin-bottom: .3rem; }
.adv-unit-row { margin: .2rem 0; font-size: .86rem; line-height: 1.45; }
.adv-cands { margin: .25rem 0; padding-left: 1.1rem; font-size: .86rem; }
.adv-cands li { margin: .12rem 0; }
.adv-cands li.adv-unusable { opacity: .7; }
.adv-blind {
  font-size: .72rem; text-transform: uppercase; letter-spacing: .03em;
  padding: 0 .35rem; border-radius: var(--radius-xs);
  border: 1px solid var(--border); color: var(--text-dim);
}
.adv-win { font-weight: 600; }
.adv-why { font-style: italic; color: var(--text-dim); }
.adv-held-why { color: var(--danger); }
.pill.adv-status-delivered { background: var(--success); color: var(--panel); }
.pill.adv-status-held { background: var(--danger); color: var(--panel); }
.pill.adv-status-skipped { background: var(--warn); color: var(--panel); }
.pill.adv-status-staged { background: var(--border); color: var(--text); }

/* A11Y T1: minimum pointer targets (WCAG 2.5.8). */
.mini { min-height: 24px; display: inline-flex; align-items: center; justify-content: center; gap: .3em; }
button.lp-chip-x { min-width: 24px; min-height: 24px; }
button.chip { min-height: 24px; }
.keycard-head a.small { min-width: 24px; min-height: 24px; display: inline-flex; align-items: center; justify-content: center; }

/* A11Y-1 reflow (WCAG 1.4.10) */
.grid2 > *, .docs > *, .inline-form > *, .adv-tmpl > *, .filters > *,
.row-actions > *, .card-head > * { min-width: 0; }
fieldset { min-width: 0; }
p code, li code, dd code, .help code, .muted code { overflow-wrap: break-word; }
.tablewrap { overflow-x: auto; max-width: 100%; }
#features .feat-grid { grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); }
.tilegrid { grid-template-columns: repeat(auto-fill, minmax(min(232px, 100%), 1fr)); }
.lp-list { grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr)); }
.lp-list.lp-grid { grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr)); }
.choice-options { grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr)); }
.wf-know-terms { grid-template-columns: repeat(auto-fill, minmax(min(175px, 100%), 1fr)); }
.badge { max-width: 100%; }
.badge:is(.status, .run, .ok, .warn, .danger, .exact, .fuzzy, .semantic, .reauth, .paused, .beta) {
  white-space: normal; overflow-wrap: break-word;
}
td > .badge { white-space: nowrap; }
.error-inline { color: var(--danger); overflow-wrap: break-word; max-width: 100%; }
.step-note { overflow-wrap: anywhere; }
.step-head > .error-inline, .step-head > .step-note { flex: 1 1 100%; min-width: 0; overflow-wrap: anywhere; }
.nav-close { display: none; min-width: 24px; min-height: 24px; border: 1px solid var(--input-border); }
.tablewrap:focus-visible, pre[tabindex]:focus-visible { outline: var(--focus-width) solid var(--ring); outline-offset: var(--focus-offset); }
html { scroll-padding-top: calc(var(--topbar-h, 69px) + 12px); }
html:has(.editor-toolbar) { scroll-padding-top: calc(var(--topbar-h, 69px) + 105px); }
.doc-section { scroll-margin-top: 0; }
.docs-toc-details { align-self: start; position: sticky; top: calc(var(--topbar-h, 69px) + 11px); }
.docs-toc-details .docs-toc { position: static; }
@media (min-width: 901px) {
  .docs-toc-details { max-height: calc(100vh - var(--topbar-h, 69px) - 22px); overflow-y: auto; }
  .docs-toc-details[open] > summary { display: none; }
}
@media (max-width: 900px) {
  .sidebar { visibility: hidden; transition: transform var(--motion-fast) ease, visibility 0s linear var(--motion-fast); }
  .app.nav-open .sidebar { visibility: visible; transition: transform var(--motion-fast) ease; }
  .nav-close { display: inline-flex; align-items: center; justify-content: center; align-self: flex-start; }
  .docs-toc-details { position: static; }
  .docs-toc-details > summary { cursor: pointer; padding: 8px 0; }
}
@media (max-height: 500px) {
  .sidebar { overflow-y: auto; }
}
@media (max-width: 600px) {
  .topbar2 { flex-wrap: wrap; padding: 8px 16px; gap: 8px 10px; }
  .crumbs { flex: 1 1 0; min-width: 0; overflow-wrap: anywhere; font-size: 1rem; }
  .topsearch-form { flex: 1 1 100%; max-width: none; margin-left: 0; }
  .topactions { gap: 6px; }
  .usermenu > summary { padding: 2px; }
  /* Keep name and role available to assistive technology. */
  .um-id { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
  .um-caret { display: none; }
  .content { padding: 16px 16px 32px; }
  .card { padding: 16px; }
  .warnbar { margin: 12px 16px 0; }
}
/* Let sticky chrome scroll away in a short or zoomed viewport. */
@media (max-width: 600px), (max-height: 500px) {
  .topbar2, .editor-toolbar { position: static; }
  html, html:has(.editor-toolbar) { scroll-padding-top: 12px; }
}

/* T3 follow-up: preserve pair layout and size compact targets. */
fieldset.pair-row { border: 0; padding: 0; margin: 0 0 8px; min-width: 0; }
#new-project-dialog input[name="project_type"] { min-width: 24px; min-height: 24px; }
.danger-btn { min-width: 24px; min-height: 24px; }
/* T4: Editor row state as text (after the status chip; the row tint stays), the
   visible assertive status, and compact in-cell disclosures for QA flags, TM locks and
   the toolbar badges. The disclosure rules repeat the quiet-box :not() chain so they
   win without !important. */
.rowstate { display: block; margin-top: 4px; font-size: .78rem; color: var(--muted); white-space: normal; }
.save-failed .rowstate { color: var(--danger); }
.rowstate .mini { margin-left: 4px; }
.ed-alert { color: var(--danger); font-weight: 600; }
details:is(.qa-flag, .lockinfo, .badgeinfo):not(.usermenu):not(.inline-edit):not(.rail-collapse) {
  display: inline-block; vertical-align: top; max-width: 100%; margin: 0 2px 2px 0;
  border: 0; background: none; white-space: normal;
}
/* The summary is the click target: at least 24 x 24 px (WCAG 2.2 2.5.8), the badge
   centred in it. */
details:is(.qa-flag, .lockinfo, .badgeinfo):not(.usermenu):not(.inline-edit):not(.rail-collapse) > summary { display: inline-flex; align-items: center; min-height: 24px; min-width: 24px; padding: 0; gap: 3px; font-size: inherit; }
details:is(.qa-flag, .lockinfo, .badgeinfo):not(.usermenu):not(.inline-edit):not(.rail-collapse) > summary:hover { background: none; }
details:is(.qa-flag, .lockinfo, .badgeinfo):not(.usermenu):not(.inline-edit):not(.rail-collapse)[open] > summary { border-bottom: 0; }
details:is(.qa-flag, .lockinfo, .badgeinfo):not(.usermenu):not(.inline-edit):not(.rail-collapse) > *:not(summary) { display: block; padding: 2px 0 0; margin: 0; }
/* === a11y T9: session timeout warning + draft notice (keep this block together) === */
dialog.session-dialog { width: min(480px, calc(100vw - 32px)); }
.session-dialog .modal-card h2 { margin-top: 0; }
.session-countdown { font-family: var(--data-font, monospace); font-size: 1.25rem; font-weight: 600; }
.session-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 12px; }
.session-sign-out-form { margin: 0; }
.session-sign-out-form:has(> button[hidden]) { display: none; }
/* #260: once the session has ended, nothing behind the signed-out dialog stays readable. */
body.session-ended > :not(#session-dialog) { visibility: hidden !important; }
.session-draft { margin-bottom: 16px; }
.session-draft p { margin: 0; }
/* Reduced motion: nothing here animates (no transition on open, a countdown that only
   changes its text), so there is nothing to turn off. */
/* === end a11y T9 === */

/* ================= Editable page layouts (train L1) =================
   app/static/page_layout.js moves a page's movable cards (<section data-card>)
   into .layout-row containers, one per row of 1, 2 or 3 cards (data-cols). DOM order
   is the visual order. Rows of 2 or 3 stack below 900px (like .grid2); a 3-across
   row drops to 2 across between 900 and 1200px (the TM and Glossary forms are wide).
   Edit mode (.layout-editing, desktop only) shows each card as a compact tile: the
   title, a drag handle look and the Move tools; its body is inert and hidden. */
/* Train L2: the Edit layout button sits at the top right of any page header marked
   .has-layout-edit (a card header, a page header or the customer header); inside a header
   whose h1 already shares a .row-between with a badge or a primary action, it is an
   inline flex item pushed right (.layout-edit-inline) so it never covers that item. */
.has-layout-edit { position: relative; }
/* #564: a compact, quiet secondary control (muted text, hairline border, small type), not a
   prominent button. Keeps a 24x24 px minimum target (WCAG 2.2 SC 2.5.8) and the global focus ring. */
button.layout-edit-btn { position: absolute; top: var(--space-5); right: var(--space-5); padding: 2px 8px; font-size: .75rem; font-weight: 400; line-height: 1.3; color: var(--muted); background: transparent; min-height: 24px; min-width: 24px; }
button.layout-edit-btn:hover, button.layout-edit-btn:focus-visible { color: var(--text); }
.layout-edit-btn[hidden] { display: none; }
.has-layout-edit:not(.card) > .layout-edit-btn { top: 0; right: 0; }
.layout-edit-btn.layout-edit-inline { position: static; margin-left: auto; flex: 0 0 auto; }
.has-layout-edit:has(.layout-edit-btn:not([hidden])) > h1 { padding-right: 7rem; }
.layout-row { display: grid; grid-template-columns: minmax(0, 1fr); column-gap: 18px; align-items: start; }
.layout-row[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.layout-row[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1200px) {
  .layout-row[data-cols="3"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  .layout-row[data-cols="2"], .layout-row[data-cols="3"] { grid-template-columns: minmax(0, 1fr); }
  .layout-edit-btn, .layout-editbar { display: none; }
}
.layout-editbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; margin: 0 0 var(--space-5);
  padding: 12px 16px; border: 1px dashed var(--primary); border-radius: var(--radius); background: var(--panel);
}
.layout-editbar .layout-help { margin: 0; flex: 1 1 22rem; color: var(--muted); }
.layout-editbtns { display: flex; flex-wrap: wrap; gap: 8px; }
.layout-editbar .layout-error { flex-basis: 100%; margin: 0; }
.layout-editing .layout-row { position: relative; padding-top: 4px; }
.layout-editing .layout-tile { cursor: grab; padding: var(--space-4); }
.layout-editing .layout-tile h2, .layout-editing .layout-tile-title { margin: 0 0 8px; font-size: 1rem; font-weight: 600; }
.layout-editing .layout-tile .layout-inert { display: none; }
.layout-editing .layout-dragging { opacity: .5; }
.layout-tools { display: flex; flex-wrap: wrap; gap: 6px; }
.layout-tool { display: inline-flex; align-items: center; justify-content: center; gap: 4px; min-width: 24px; min-height: 24px; padding: 4px 9px; font-size: .82rem; background: var(--panel-2); color: var(--text); border: 1px solid var(--input-border); }
.layout-tool:hover { border-color: var(--primary); filter: none; }
.layout-tool[aria-disabled="true"] { opacity: .5; cursor: default; }
.layout-rowbar {
  grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px;
  margin: 0 0 8px; padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.layout-rowbar legend { float: left; padding: 0; margin-right: 6px; font-weight: 600; font-size: .85rem; }
.layout-rowhandle { cursor: grab; color: var(--muted); padding: 0 4px; }
.layout-wopt { display: inline-flex; align-items: center; gap: 4px; font-size: .85rem; max-width: none; }
.layout-wopt input { min-width: 24px; min-height: 24px; margin: 0; }
.layout-newrow {
  min-height: 24px; margin: 0 0 var(--space-3); border: 1px dashed var(--border); border-radius: var(--radius-sm);
  color: var(--muted); font-size: .78rem; text-align: center; line-height: 24px;
}
.layout-drop-strip, .layout-drop-row { border-color: var(--primary); background: var(--panel-2); }
.layout-drop-before { box-shadow: inset 4px 0 0 var(--primary); }
.layout-drop-after { box-shadow: inset -4px 0 0 var(--primary); }
/* ================= end editable page layouts ================= */
/* ================= two-step sign-in (TOTP train) =================
   The QR code keeps a white quiet zone in every theme (scanners need dark-on-light) and
   shrinks with the card at 320 px; the setup key wraps instead of forcing a scroll. */
.mfa-qr { margin: 12px 0; }
.mfa-qr svg { display: block; max-width: 100%; height: auto; background: #fff; border-radius: 4px; }
.mfa-key { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; margin: 8px 0 16px; }
.mfa-key dt { font-weight: 600; }
.mfa-key dd { margin: 0; overflow-wrap: anywhere; }
.mfa-steps { padding-left: 1.4em; }
.mfa-codes { columns: 2; column-gap: 24px; padding-left: 1.6em; }
.mfa-codes code { font-size: 1.05em; letter-spacing: 0.04em; }
/* ================= end two-step sign-in ================= */

/* ============================================================================
   GitHub #340 — run step-diff view (run_step_diff.html).
   Insertions/deletions are marked by UNDERLINE and STRIKE-THROUGH on top of the
   native <ins>/<del> semantics: never colour-only (a11y), so a monochrome print
   or a colour-blind reader still sees exactly what changed. Tokens only.
   ============================================================================ */
.step-diff-seg { border: 1px solid var(--border); border-radius: 6px; margin: 6px 0; padding: 4px 10px; }
.step-diff-seg summary { cursor: pointer; font-weight: 550; }
.step-diff-text { margin: 6px 0 2px; white-space: pre-wrap; word-break: break-word; font-family: var(--data-font); }
.step-diff-text ins.diff-ins { text-decoration: underline; text-decoration-thickness: 2px; }
.step-diff-text del.diff-del { text-decoration: line-through; }
.step-diff-picker select { min-width: 220px; }

/* Shared motion and touch density. */
@media (max-width: 600px) {
  html:root { --control-height: var(--control-height-mobile); }
}

/* BEGIN Direction A components (branch 02). */
.page-header { margin-bottom: var(--space-5); }
.page-header h1 { margin: 0 0 6px; }
.page-header { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
.page-header-copy { min-width: 0; overflow-wrap: anywhere; }
.page-header-title, .page-header-actions, .action-bar, .action-bar-secondary { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-3); }
.page-header-meta { color: var(--color-text-secondary); }
.page-header-actions { flex-shrink: 0; }
.action-bar { margin-block: var(--space-4); }
.action-bar-destructive { margin-inline-start: auto; padding-inline-start: var(--space-4); border-inline-start: 1px solid var(--color-border); }
.action-bar-consequence { flex-basis: 100%; }
.data-table-region { max-width: 100%; overflow: auto; max-height: 70vh; border: 1px solid var(--color-border); border-radius: var(--radius-card); scroll-padding: 4rem 1rem; }
.data-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--font-size-body); }
.data-table caption { text-align: start; padding: var(--space-3); color: var(--color-text-secondary); }
.data-table th, .data-table td { padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--color-border-soft); text-align: start; vertical-align: middle; }
.data-table thead th { position: sticky; top: 0; z-index: 1; background: var(--color-subtle); color: var(--color-text-secondary); font-weight: 600; }
.data-table tbody th, .data-table td { background: var(--color-surface); height: var(--row-height); padding-block: var(--space-1); }
.data-table tbody tr:hover > *, .data-table tbody tr:focus-within > * { background: var(--color-subtle); }
.data-table .numeric { text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap; }
.data-table .row-actions { position: sticky; inset-inline-end: 0; opacity: 1; visibility: visible; white-space: nowrap; border-inline-start: 1px solid var(--color-border); }
.data-table thead .row-actions { z-index: 2; }
/* The legacy flex `.row-actions` (rule near the top) must not turn table cells into flex
   boxes: an anonymous wrapper cell breaks the sticky pin and the row border and tint. */
.data-table :is(th, td).row-actions { display: table-cell; }
.data-table td.row-actions form { display: inline-block; margin: 0; }
.data-table td.row-actions > * + * { margin-inline-start: var(--space-2); }
.data-table .row-actions a, .data-table .row-actions button { min-height: var(--control-height); display: inline-flex; align-items: center; }
.empty-state { padding: var(--space-6) var(--space-4); text-align: center; color: var(--color-text-secondary); border: 1px dashed var(--color-border); border-radius: var(--radius-card); }
.form-section { min-width: 0; margin-block: var(--space-5); padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-card); }
.form-section legend { padding-inline: var(--space-2); font-weight: 600; }
.component-disclosure { margin-block: var(--space-3); border-block-end: 1px solid var(--color-border); }
.component-disclosure > summary { cursor: pointer; padding-block: var(--space-3); }
.component-disclosure > div { padding-block: var(--space-3); }
.component-tab-list { display: flex; overflow-x: auto; gap: var(--space-2); border-bottom: 1px solid var(--color-control-border); padding: var(--space-1); scroll-padding-inline: var(--space-4); }
.component-tab-list a { flex: 0 0 auto; min-height: var(--control-height); padding: var(--space-2) var(--space-3); color: var(--color-text-secondary); text-decoration: none; border-bottom: 3px solid transparent; }
.component-tab-list [aria-selected="true"] { color: var(--color-accent-text); border-bottom-color: var(--color-accent); background: var(--color-selected); }
[data-component-panel] { padding-block: var(--space-4); scroll-margin-top: var(--space-5); }
[data-component-panel][hidden] { display: none; }
.component-dialog { width: min(36rem, calc(100% - 2rem)); max-height: calc(100dvh - 2rem); overflow: auto; padding: var(--space-5); border: 1px solid var(--color-control-border); border-radius: var(--radius-card); background: var(--color-surface); color: var(--color-text); box-shadow: var(--elevation-overlay); }
.component-dialog::backdrop { background: var(--color-canvas); opacity: .75; }
.component-dialog-heading { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.component-drawer { margin: 0 0 0 auto; min-height: 100dvh; max-height: 100dvh; border-radius: 0; }
.component-toast { padding: var(--space-3) var(--space-4); border: 1px solid var(--color-control-border); border-radius: var(--radius-control); background: var(--color-raised); color: var(--color-text); }
.step-indicator { display: flex; flex-wrap: wrap; gap: var(--space-5); padding-inline-start: var(--space-5); }
.step-indicator [aria-current="step"] { font-weight: 600; text-decoration: underline; text-underline-offset: .3em; }
/* Glossary detail (issue #565): the single next step for a draft glossary, and pairs as removable chips. */
.glossary-next-step { border-inline-start: 3px solid var(--color-accent); margin-block: var(--space-4); }
.glossary-next-step h2 { font-size: var(--font-size-section); margin-block: var(--space-3) var(--space-2); }
.glossary-next-step .step-indicator { list-style: none; padding-inline-start: 0; margin-block: var(--space-2); gap: var(--space-2) var(--space-5); }
.glossary-next-step .step-indicator li { display: inline-flex; align-items: center; gap: var(--space-2); }
.glossary-pair-chip { display: inline-flex; align-items: center; gap: var(--space-2); padding-block: 2px; }
.glossary-pair-remove { display: inline; margin: 0; }
@media (max-width: 600px) {
  .page-header { flex-direction: column; }
  .page-header-actions { flex-shrink: 1; }
  .data-table th, .data-table td { padding-inline: var(--space-3); }
  .component-tab-list { border-inline-end: 3px solid var(--color-control-border); }
}
/* END Direction A components. */

/* BEGIN branch 04 page layouts: shared list/detail and anchored-help geometry. */
.dashboard-page, .help-page, .module-help-page { min-width: 0; }
.dashboard-page .page-header { margin-bottom: var(--space-5); }
.dashboard-page .dash-summary { line-height: 1.8; }
.dashboard-page .dash-running-head { margin-bottom: var(--space-4); }
.dashboard-page .dash-attention { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.dashboard-page .dash-attention a { padding-block: var(--space-1); }
.dashboard-page .data-table { min-width: 760px; }
.dashboard-page .data-table td:first-child { min-width: 190px; }
.dashboard-page .activity li { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2); padding-block: var(--space-3); border-bottom: 1px solid var(--border-soft); }
.dashboard-page .quick { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.dashboard-page .quick a { min-height: 36px; display: inline-flex; align-items: center; }
.dashboard-page .onboarding { border-color: var(--border); }
.dashboard-page .onboarding .ob-step { background: transparent; border: 0; border-bottom: 1px solid var(--border-soft); border-radius: 0; padding: var(--space-3) 0; }
.dashboard-page .onboarding .ob-step.is-done { background: transparent; }
.dashboard-page .onboarding .ob-steps { gap: 0; }
.help-page .docs { grid-template-columns: minmax(180px, 220px) minmax(0, 1fr); gap: var(--space-6); }
/* The body takes the full column so reference tables grow with the window; only
   running prose keeps the readable measure. */
.help-page .docs-body { min-width: 0; max-width: none; overflow-wrap: anywhere; }
.help-page .doc-section > :is(p, ul, ol, dl, blockquote, h3, h4) { max-width: var(--readable); }
.help-page .docs-toc-details { padding: var(--space-3); background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); }
.help-page .docs-toc a { min-height: 28px; }
.help-page .doc-section { margin-bottom: var(--space-6); }
.help-page pre.code { max-width: 100%; color: var(--text); }
.help-page .help-scroll { max-width: 100%; overflow-x: auto; }
.help-page .help-scroll table { min-width: 580px; width: 100%; }
/* API tables: method and gate stay on one line, paths break only at word
   boundaries in their own column, and the purpose column takes the rest. */
.help-page .table.api { min-width: 760px; overflow-wrap: normal; }
.help-page .table.api :is(td, th):first-child,
.help-page .table.api :is(td, th):last-child { width: 1%; white-space: nowrap; }
.help-page .table.api td:nth-child(2) { width: 30%; min-width: 14rem; }
.help-page .table.api td:nth-child(2) code { overflow-wrap: anywhere; }
.help-page .table.api td:nth-child(3) { min-width: 18rem; }
.module-help-page { max-width: 960px; }
.module-help-page .card { padding: var(--space-5); }
.module-help-page .card p, .module-help-page .card dd, .module-help-page .card li { max-width: var(--readable); overflow-wrap: anywhere; }
.module-help-page .help-card-dl dd { margin-inline-start: 0; margin-bottom: var(--space-4); }
.setup-page .page-header { margin-block: var(--space-5); }
.setup-page .page-header h1 { font-size: var(--font-size-title); }
@media (max-width: 900px) {
  .help-page .docs { grid-template-columns: minmax(0, 1fr); gap: var(--space-5); }
}
@media (max-width: 600px) {
  /* The generic details box would nest a bordered panel inside the card: flatten it here. */
  .dashboard-page .onboarding details.ob-details { border: 0; background: transparent; border-radius: 0; margin: 0; }
  .dashboard-page .onboarding details.ob-details > summary { padding: 0; }
  .dashboard-page .onboarding details.ob-details > *:not(summary) { padding: 0; }
  .dashboard-page .onboarding details.ob-details[open] > summary { border-bottom: 0; }
  .dashboard-page .onboarding .ob-step { flex-wrap: wrap; }
  .dashboard-page .onboarding .ob-body { flex: 1 1 160px; }
  .dashboard-page .onboarding .ob-cta { margin-inline-start: 0; white-space: normal; }
  .dashboard-page .onboarding .ob-showme { margin-inline-start: 0; }
  .dashboard-page .onboarding a, .dashboard-page .onboarding button,
  .dashboard-page .quick a, .help-page .docs-toc a { min-height: 40px; }
  .module-help-page .card { padding: var(--space-4); }
}
/* END branch 04 page layouts. */

/* BEGIN branch 06 documents and delivery. */
.documents-page { min-width: 0; }
.documents-page .document-heading { margin-block-end: var(--space-5); }
.documents-page .document-heading > .page-header { margin-block-end: var(--space-3); }
.documents-page .document-heading > p { max-width: var(--readable); }
.documents-page .has-layout-edit { position: relative; }
.documents-page .page-header h1 { overflow-wrap: anywhere; }
.documents-page .card { min-width: 0; }
.documents-page .data-table { min-width: 36rem; }
.documents-page .data-table td { overflow-wrap: anywhere; }
.documents-page .data-table .src,
.documents-page .data-table .tgt { min-width: 14rem; vertical-align: top; }
.documents-page .fact-strip { margin-block: var(--space-4); }
.documents-page .row-between { flex-wrap: wrap; gap: var(--space-3); }
.documents-page .row-actions { flex-wrap: wrap; }
.documents-page .keys-layout { grid-template-columns: minmax(0, 17.5rem) minmax(0, 1fr); }
.documents-page .keycard { background: var(--color-surface); border-radius: var(--radius-card); }
.documents-page .keycard-head { overflow-wrap: anywhere; }
.documents-page .grid2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.documents-page .comparison-results .data-table td { vertical-align: top; }
@media (min-width: 901px) {
  .documents-page .has-layout-edit:has(.layout-edit-btn:not([hidden])) > .page-header { padding-inline-end: 9.5rem; }
}
@media (max-width: 900px) {
  .documents-page .keys-layout,
  .documents-page .grid2 { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 600px) {
  .documents-page .fact-strip { gap: var(--space-3); }
  .documents-page .filters { align-items: stretch; }
  .documents-page .filters label { min-width: 0; max-width: 100%; }
  .documents-page .regions-table .data-table { min-width: 0; }
  .documents-page .regions-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .documents-page .regions-table table,
  .documents-page .regions-table tbody,
  .documents-page .regions-table tr,
  .documents-page .regions-table td { display: block; width: 100%; }
  .documents-page .regions-table .data-table .src,
  .documents-page .regions-table .data-table .tgt { min-width: 0; }
  .documents-page .regions-table tr { padding-block: var(--space-3); border-block-end: 1px solid var(--color-border); }
  .documents-page .regions-table td { border: 0; padding-block: var(--space-1); }
  .documents-page .regions-table textarea { width: 100%; min-width: 0; }
  .documents-page .regions-table .form { gap: var(--space-2); align-items: flex-start; }
  .documents-page .regions-table td { position: static; height: auto; }
  .documents-page .regions-table td:last-child { padding-block-start: var(--space-3); }
}
/* Comparison tables stay real tables inside their named local scroll region. */
.documents-page .comparison-results .data-table { min-width: 36rem; }
/* Jobs: wrap Kind and Subject at word boundaries, never mid-word. */
.documents-page .data-table td.col-kind { overflow-wrap: normal; white-space: nowrap; }
.documents-page .data-table td.col-subject { overflow-wrap: break-word; min-width: 12rem; }
/* Translation actions: one tidy row, no stray blank space. */
.documents-page [data-card="workflow"] .action-bar { margin-block: var(--space-3) 0; align-items: center; }
.documents-page [data-card="workflow"] .action-bar form { margin: 0; }
/* END branch 06 documents and delivery. */
/* BEGIN ux/language-assets-tm-glossary */
.asset-customer-picker { margin-block-end: var(--space-5); }
.asset-sections { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5); margin-block: var(--space-4) var(--space-5); padding-block: var(--space-3); border-block-end: 1px solid var(--color-border); }
.asset-sections a { display: inline-flex; align-items: center; min-height: var(--control-height); }
.tm-text-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); }
.tm-text-pair > .card { min-width: 0; }
.tm-text-pair p { overflow-wrap: anywhere; line-height: var(--line-height-editor); }
@media (max-width: 700px) {
  .tm-text-pair { grid-template-columns: minmax(0, 1fr); gap: var(--space-3); }
}
/* The legacy .empty marker wraps the shared empty state; the shared state draws the box. */
.empty:has(> .empty-state) { padding: 0; }
.empty:has(> .empty-state)::before { display: none; }
/* Visible headings already name these fieldsets; keep the legend for assistive tech only. */
#tm-filter-fields > legend, #new-glossary > legend, #glossary-new-term > legend {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
/* END ux/language-assets-tm-glossary */

/* BEGIN ux/review-runs: run inspection and review queues. */
.review-runs-page { min-width: 0; }
.review-runs-page .page-header { margin-bottom: var(--space-4); }
.review-runs-page .page-header-meta { max-width: var(--readable); }
.review-runs-page .page-header-actions .layout-edit-btn { position: static; }
.review-runs-page .page-header-actions:has(> button[hidden]:only-child) { display: none; }
.review-runs-page button.danger { background: var(--danger-soft); color: var(--danger); border-color: var(--danger); }
.review-runs-sections { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); padding-block: var(--space-3); margin-bottom: var(--space-4); border-bottom: 1px solid var(--color-border); }
.review-runs-sections a { display: inline-flex; align-items: center; min-height: var(--control-height); }
.review-runs-page section[id] { scroll-margin-top: var(--space-5); }
.review-runs-page .form-section { min-width: 0; width: 100%; }
.review-runs-page .filters .form-section { display: flex; flex-wrap: wrap; align-items: end; gap: var(--space-3); }
.review-runs-page .form-section > label { max-width: 100%; }
.review-runs-page .post { padding: var(--space-4); margin-block: var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface); overflow-wrap: anywhere; }
.review-runs-page .post > p:last-child { white-space: pre-wrap; }
.review-runs-page .step-head, .review-runs-page .bm-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.review-runs-page .runstep { border-inline-start: 2px solid var(--color-border); padding-inline-start: var(--space-4); }
.review-runs-page .step-diff-text { overflow-wrap: anywhere; }
.review-runs-comparison { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
.review-runs-comparison > section { min-width: 0; padding: var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-control); }
.review-runs-page .tablewrap { max-width: 100%; overflow-x: auto; }
.review-runs-page .rv-gantt-row, .review-runs-page .rv-gantt-foot { grid-template-columns: minmax(0, 1fr) auto; }
.review-runs-page .rv-gantt-name, .review-runs-page .rv-gantt-track { grid-column: 1 / -1; }
.review-runs-page .rv-gantt-name { white-space: normal; overflow-wrap: anywhere; }
.review-runs-page .rv-gantt-row { padding-block: var(--space-3); border-bottom: 1px solid var(--color-border-soft); }
@media (min-width: 1000px) {
  .review-runs-page #runGraphCard { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-4); align-items: start; }
  .review-runs-page #runGraphCard > h2 { grid-column: 1 / -1; }
  .review-runs-page #runGraphCard > :is(.wf-graph, .wf-graph-region) { grid-column: 1; grid-row: 2 / span 3; }
  .review-runs-page #runGraphCard > :not(h2):not(.wf-graph):not(.wf-graph-region) { grid-column: 2; }
}
@media (max-width: 600px) {
  .review-runs-comparison { grid-template-columns: minmax(0, 1fr); }
  .review-runs-page .filters .form-section { display: grid; grid-template-columns: minmax(0, 1fr); }
  .review-runs-page .form-section input:not([type=checkbox]), .review-runs-page .form-section select { max-width: 100%; }
  .review-runs-page .bm-agg { line-height: 2; }
}
/* END ux/review-runs. */

/* BEGIN ux/language-assets-guidance: resource sections and readable editing. */
.guidance-page { min-width: 0; }
.guidance-page .page-header-meta > p { max-width: var(--readable); }
.guidance-page .page-header-actions .layout-edit-btn { position: static; }
.guidance-sections { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block: var(--space-4) var(--space-5); padding-block: var(--space-2); border-block: 1px solid var(--color-border); }
.guidance-sections:empty { display: none; }
.guidance-sections a { display: inline-flex; align-items: center; min-height: var(--control-height); padding: var(--space-2) var(--space-3); border-radius: var(--radius-control); color: var(--color-text-secondary); text-decoration: none; }
.guidance-sections a:hover { background: var(--color-selected); color: var(--color-accent-text); }
.guidance-page [id] { scroll-margin-block-start: 8rem; }
.guidance-page .card, .guidance-page .grid2 > * { min-width: 0; }
.guidance-page .form-section { margin-block: var(--space-2); width: 100%; }
.guidance-page .form-section > label { display: block; margin-block: var(--space-3); }
.guidance-page .form-section > label > :is(input:not([type=checkbox]), select, textarea) { display: block; width: 100%; }
.guidance-page .form-section textarea { max-width: var(--readable); line-height: var(--line-height-editor); }
.guidance-page pre { white-space: pre-wrap; overflow-wrap: anywhere; }
.guidance-page .data-table { min-width: 36rem; }
.guidance-page .data-table td { overflow-wrap: normal; word-break: normal; }
.guidance-page .data-table td.trunc { display: table-cell; }
.guidance-page .card > .form + .form { margin-block-start: var(--space-3); }
.guidance-page .data-table-region + .form { margin-block-start: var(--space-4); }
/* The legend repeats the section heading or label; keep it as the group's accessible name only. */
.guidance-page .form-section > legend { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
@media (max-width: 600px) {
  .guidance-page .inline-form { flex-wrap: wrap; }
  .guidance-page .inline-form > label { min-width: 0; max-width: 100%; }
  .guidance-page :is(input, select, textarea) { max-width: 100%; }
  .guidance-page .data-table-region { border-inline-end: 3px solid var(--color-control-border); }
  .guidance-page .form-section { padding: var(--space-3); }
}
/* END ux/language-assets-guidance. */

/* BEGIN ux/admin-ai: anchored setup and inspection surfaces. */
.admin-ai-layout { display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: var(--space-5); align-items: start; }
.admin-ai-page { min-width: 0; overflow-wrap: anywhere; }
.admin-ai-page .page-header-meta { max-width: var(--readable); }
.admin-ai-index { display: flex; flex-direction: column; gap: var(--space-1); position: sticky; top: 80px; }
.admin-ai-index a { display: flex; align-items: center; min-height: var(--control-height); padding: var(--space-2) var(--space-3); border-radius: var(--radius-control); color: var(--text); text-decoration: none; }
.admin-ai-index a:hover { background: var(--panel-2); color: var(--primary-2); }
.admin-ai-page :is(section, h2, h3)[id] { scroll-margin-top: 90px; }
.admin-ai-page .page-layout, .admin-ai-page .card, .admin-ai-page .tile { min-width: 0; }
.admin-ai-page .tilegrid { grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); gap: var(--space-4); }
.admin-ai-page .tile { text-align: start; padding: var(--space-4); }
.admin-ai-page .tile:hover { transform: none; }
.admin-ai-page .form-section { margin-block: var(--space-4); }
.admin-ai-page :is(input:not([type="checkbox"], [type="radio"]), select, textarea) { max-width: 100%; min-width: 0; }
.admin-ai-index-inline { position: static; flex-direction: row; flex-wrap: wrap; margin-bottom: var(--space-5); border-bottom: 1px solid var(--border); padding-bottom: var(--space-3); }
.admin-ai-inspect pre.code { max-width: 100%; overflow: auto; }
.admin-ai-inspect .data-table { min-width: 700px; }
.admin-ai-inspect .data-table th:nth-child(1) { min-width: 110px; }
.admin-ai-inspect .data-table th:nth-child(2) { min-width: 75px; }
.admin-ai-inspect .data-table th:nth-child(3) { min-width: 200px; }
.admin-ai-inspect .data-table th:nth-child(4) { min-width: 300px; }
@media (max-height: 640px) {
  /* A sticky index taller than a short viewport would clip its last links. */
  .admin-ai-index { position: static; }
}
@media (max-width: 1000px) {
  .admin-ai-layout { grid-template-columns: minmax(0, 1fr); }
  .admin-ai-index { position: static; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 600px) {
  .admin-ai-page .grid2 { grid-template-columns: minmax(0, 1fr); }
  .admin-ai-page .row-actions { flex-wrap: wrap; }
  .admin-ai-index a { min-height: 40px; }
}
/* END ux/admin-ai */

/* BEGIN ux/admin-settings: anchored administration sections. */
.settings-frame { display: grid; grid-template-columns: minmax(10rem, 13rem) minmax(0, 1fr); gap: var(--space-5); align-items: start; }
.settings-frame > *, .settings-content { min-width: 0; }
.settings-index { display: flex; flex-direction: column; gap: var(--space-1); position: sticky; top: calc(var(--topbar-h, 69px) + var(--space-4)); padding: var(--space-2); border: 1px solid var(--color-border-soft); border-radius: var(--radius-card); background: var(--color-surface); }
.settings-index a { display: flex; align-items: center; min-height: 40px; padding: var(--space-2) var(--space-3); color: var(--color-text-secondary); text-decoration: none; border-radius: var(--radius-control); overflow-wrap: anywhere; }
.settings-index a:hover, .settings-index a:focus-visible { background: var(--color-selected); color: var(--color-accent-text); }
.settings-frame section[id] { scroll-margin-block-start: var(--space-5); }
.settings-frame .card { overflow-wrap: anywhere; }
.settings-frame .data-table { min-width: 44rem; overflow-wrap: normal; }
.settings-frame .preflight { padding: var(--space-4); background: var(--color-subtle); border: 1px solid var(--color-border-soft); border-radius: var(--radius-card); margin-block: var(--space-4); }
.settings-frame > .layout-editbar { grid-column: 1 / -1; }
.settings-frame #features .feat-moved-link { white-space: normal; max-width: 100%; }
.settings-frame input:not([type="checkbox"]):not([type="radio"]), .settings-frame select, .settings-frame textarea { max-width: 100%; }
@media (max-width: 900px) {
  .settings-frame { grid-template-columns: minmax(0, 1fr); }
  .settings-index { position: static; flex-direction: row; flex-wrap: wrap; }
  .settings-index a { flex: 1 1 9rem; }
}
/* END ux/admin-settings. */

/* BEGIN ux/admin-connections: section 14 page geometry. */
.connections-page { min-width: 0; overflow-wrap: anywhere; }
.connections-page .page-header { padding-block: var(--space-2) var(--space-4); border-bottom: 1px solid var(--color-border-soft); }
.connections-page .page-header-meta { max-width: var(--readable); }
.connections-page .page-header-actions { flex-wrap: wrap; max-width: 100%; }
.connections-page .page-header-actions .toolbar { margin: 0; }
.connections-page .page-header .layout-edit-btn { position: static; }
.connections-page .tile { padding: var(--space-4); gap: var(--space-2); }
.connections-page .tile > strong { font-size: var(--font-size-section); }
.connections-page .tile-foot { margin-top: auto; }
.connections-page .tile .help { overflow-wrap: anywhere; }
/* Planned tiles: full-opacity text (the .74 fade put "Planned" and its blurb under 4.5:1); the dashed edge marks them secondary. */
.connections-page .tile-inert { opacity: 1; border-style: dashed; }
.connections-layout { display: grid; grid-template-columns: 11rem minmax(0, 1fr); gap: var(--space-5); align-items: start; }
.connections-section-nav { position: sticky; top: 5rem; display: flex; flex-direction: column; gap: var(--space-1); max-height: calc(100dvh - 6rem); overflow: auto; padding: var(--space-1); }
.connections-section-nav a { display: flex; align-items: center; min-height: var(--control-height); padding: var(--space-2) var(--space-3); border-radius: var(--radius-control); color: var(--color-text-secondary); text-decoration: none; }
.connections-section-nav a:hover { color: var(--color-accent-text); background: var(--color-selected); }
.connections-page h2[id] { scroll-margin-top: 5rem; }
.connections-page :is(.page-layout, .card, .form, .form-section, .data-table-region) { min-width: 0; }
.connections-page .form-section { margin-block: var(--space-3); }
.connections-page .data-table-region { margin-block: var(--space-3); max-height: none; }
.connections-page .data-table thead th { white-space: nowrap; }
.connections-page .data-table { min-width: 34rem; }
.connections-page .data-table :is(input, select, textarea) { max-width: 100%; }
.connections-page .data-table td { padding-block: var(--space-3); }
.connections-page .data-table :is(th, td).row-actions { display: table-cell; }
.connections-page .connection-operations { margin-bottom: var(--space-5); }
@media (max-width: 1100px) {
  .connections-layout { grid-template-columns: minmax(0, 1fr); gap: var(--space-3); }
  .connections-section-nav { position: static; flex-direction: row; flex-wrap: wrap; max-height: none; border-bottom: 1px solid var(--color-border-soft); }
  .connections-section-nav a { flex: 0 1 auto; }
}
@media (max-width: 600px) {
  .connections-page .page-header-actions { width: 100%; }
  .connections-page .form label { min-width: 0; }
  .connections-page .form-section { padding: var(--space-3); }
  .connections-page .inline-form { flex-wrap: wrap; }
}
/* END ux/admin-connections. */

/* BEGIN ux/admin-insight: evaluations, reports and Ledger. */
.insight-page { min-width: 0; }
.insight-page .page-header { margin-block-end: var(--space-5); }
.insight-page .page-header-meta { max-width: 85ch; }
.insight-page .page-header-meta > :last-child { margin-block-end: 0; }
.insight-page .card, .insight-page .grid2 > *, .insight-page .report-results { min-width: 0; }
.insight-page .data-table { font-variant-numeric: tabular-nums; }
.insight-page .data-table td { overflow-wrap: anywhere; }
.insight-page .data-table-region { margin-block: var(--space-3); }
.insight-page .badge { white-space: normal; }
.insight-page pre { max-width: 100%; overflow: auto; }
.insight-page h2[id] { scroll-margin-block-start: 6rem; }
.insight-sections { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block: var(--space-4); padding-block-end: var(--space-3); border-block-end: 1px solid var(--color-border); }
.insight-sections a { display: inline-flex; align-items: center; min-height: var(--control-height); padding: var(--space-2) var(--space-3); border-radius: var(--radius-control); color: var(--color-text-secondary); text-decoration: none; }
.insight-sections a:hover, .insight-sections a:focus-visible { color: var(--color-accent-text); background: var(--color-selected); }
.insight-page .tap-call-head, .insight-page .ptap-head, .insight-page .tap-action-sum, .insight-page .tap-run-sum { flex-wrap: wrap; overflow-wrap: anywhere; }
.insight-page .tap-action-title, .insight-page .tap-action-lead { min-width: 0; }
.insight-prompt_tap .form-section form { display: grid; gap: var(--space-3); }
.insight-prompt_tap .form-section label { display: block; }
.insight-prompt_tap .form-section input:not([type="checkbox"]) { display: block; width: 100%; }
.insight-prompt_tap .form-section button { justify-self: start; }
@media (min-width: 1200px) {
  .insight-page:has(> .insight-sections) { padding-inline-start: 12rem; position: relative; }
  .insight-page > .insight-sections { position: absolute; inset-inline-start: 0; top: 0; width: 10.5rem; flex-direction: column; border-block-end: 0; border-inline-end: 1px solid var(--color-border); padding-inline-end: var(--space-3); }
}
@media (max-width: 600px) {
  .insight-page .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .insight-page .grid2 { grid-template-columns: minmax(0, 1fr); }
  .insight-page .report-fields { grid-template-columns: minmax(0, 1fr); }
  .insight-page .report-fields > *, .insight-page label, .insight-page input, .insight-page select, .insight-page textarea { min-width: 0; max-width: 100%; }
  .insight-page .filterbar { flex-direction: column; align-items: stretch; }
  .insight-page .data-table th { min-width: 6rem; }
  .insight-page .tap-metrics { flex-wrap: wrap; }
  .insight-page .barlbl { overflow-wrap: anywhere; min-width: 0; }
  .insight-eval_compare .data-table:has(td[data-label]) thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .insight-eval_compare .data-table:has(td[data-label]) tr { display: block; padding-block: var(--space-3); border-block-end: 1px solid var(--color-border); }
  .insight-eval_compare td[data-label] { display: grid; grid-template-columns: minmax(7rem, 40%) minmax(0, 1fr); gap: var(--space-3); height: auto; border: 0; }
  .insight-eval_compare td[data-label]::before { content: attr(data-label); font-family: inherit; color: var(--color-text-secondary); }
  .insight-cell-value { min-width: 0; }
}
/* END ux/admin-insight. */

/* BEGIN ux/review-editor: review surfaces and per-segment resource rail. */
.review-intro { margin-block: var(--space-5); }
.review-intro > p { max-width: var(--readable); }
.review-section-nav { display: flex; flex-wrap: wrap; gap: var(--space-4); padding-block: var(--space-3); margin-block-end: var(--space-4); border-bottom: 1px solid var(--border); }
[id^="lqa-"] { scroll-margin-top: 6rem; }
.review-editor { display: block; width: 100%; min-width: 0 !important; }
.review-editor thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.review-editor tbody { display: block; }
.review-editor tbody tr { display: grid; height: auto; min-height: var(--row-height); grid-template-columns: 2rem minmax(0, 1fr) minmax(0, 1fr) minmax(12rem, .75fr); border-bottom: 1px solid var(--border); }
.review-editor td { min-width: 0; width: auto !important; border: 0; overflow-wrap: anywhere; }
.review-editor td.num { grid-column: 1; grid-row: 1 / 4; }
.review-editor td.src { grid-column: 2; grid-row: 1 / 4; }
.review-editor td.tgt { grid-column: 3; grid-row: 1 / 4; }
.review-editor td.tm { grid-column: 4; grid-row: 1; }
.review-editor td.terms { grid-column: 4; grid-row: 2; }
.review-editor td.qa { grid-column: 4; grid-row: 3; }
.review-editor td.review-state { grid-column: 2; grid-row: 4; }
.review-editor td.acts { grid-column: 3 / 5; grid-row: 4; display: flex; flex-wrap: wrap; gap: var(--space-2); position: static; }
.review-editor .tm, .review-editor .terms, .review-editor .qa { border-inline-start: 1px solid var(--border); background: var(--color-subtle); }
.review-cell-label { display: block; font-size: var(--font-size-caption); font-weight: 600; color: var(--muted); margin-bottom: var(--space-2); }
.review-editor .cell-empty { white-space: nowrap; }
.review-editor textarea { width: 100%; min-width: 0; line-height: var(--line-height-editor); }
.review-editor tr:focus-within { outline: 1px solid var(--color-accent); outline-offset: -1px; }
.review-editor tbody tr.dirty:hover { background: color-mix(in srgb, var(--row-dirty) 92%, var(--color-accent)); }
.review-editor tbody tr.save-failed:hover { background: color-mix(in srgb, var(--row-failed) 92%, var(--danger)); }
.review-workspace.has-resource-rail { display: grid; grid-template-columns: minmax(0, 1fr) minmax(12rem, 16rem); gap: var(--space-4); align-items: start; }
.has-resource-rail .review-editor tbody tr { grid-template-columns: 2rem minmax(0, 1fr) minmax(0, 1fr); }
.has-resource-rail .review-editor td.acts { grid-column: 3; }
/* Cells are hidden visually, never with display:none: a removed cell shifts every later cell under the wrong column header for a screen reader. */
.review-editor tbody tr { position: relative; }
.has-resource-rail .review-editor td.tm, .has-resource-rail .review-editor td.terms { position: absolute; width: 1px !important; height: 1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* The selected row keeps only its Accept TM button (next in Tab order after the target); the percentage is in the rail. */
.has-resource-rail .review-editor tr.review-selected td.tm:has(> button) { position: static; width: auto !important; height: auto; overflow: visible; clip-path: none; white-space: normal; grid-column: 3; grid-row: 4; background: none; border: 0; padding-block: 0; }
/* Every row keeps its QA flags in the row, in Tab order between Accept TM and the row actions. */
.has-resource-rail .review-editor td.qa > .review-cell-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; margin: 0; }
/* Compact desktop row (daily post-editing): number, Source, Target and the row actions share one line;
   status, QA flags and Accept TM share a second thin line. The cell labels stay in the accessibility tree. */
.has-resource-rail .review-editor tbody tr { grid-template-columns: 1.75rem minmax(0, 1fr) minmax(0, 1fr) 11rem; column-gap: var(--space-2); padding-block: var(--space-1); min-height: 0; }
.has-resource-rail .review-editor td { padding: var(--space-1) 0; line-height: 1.4; }
.has-resource-rail .review-editor td.num { grid-column: 1; grid-row: 1 / 3; padding-inline-start: var(--space-1); }
.has-resource-rail .review-editor td.src { grid-column: 2; grid-row: 1; }
.has-resource-rail .review-editor td.tgt { grid-column: 3; grid-row: 1; }
.has-resource-rail .review-editor td.acts { grid-column: 4; grid-row: 1; align-content: start; gap: var(--space-1); }
.has-resource-rail .review-editor td.review-state { grid-column: 2; grid-row: 2; padding-block: 0; }
.has-resource-rail .review-editor td.qa { grid-column: 3; grid-row: 2; background: none; border: 0; padding-block: 0; }
.has-resource-rail .review-editor td.qa > div { display: flex; flex-wrap: wrap; gap: 0 var(--space-3); }
.has-resource-rail .review-editor tr.review-selected td.tm:has(> button) { grid-column: 4; grid-row: 2; padding: 0; }
.has-resource-rail .review-editor td.src > .review-cell-label, .has-resource-rail .review-editor td.tgt > .review-cell-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; margin: 0; }
.has-resource-rail .review-editor .unit-context, .has-resource-rail .review-editor .tag-hint { margin: 0 0 var(--space-1); }
.has-resource-rail .review-editor .tgt-edit { min-height: 2.2em; padding-block: var(--space-1); line-height: 1.4; }
.review-resources { position: sticky; top: 5rem; border: 1px solid var(--border); border-radius: var(--radius-card); background: var(--color-surface); padding: var(--space-4); min-width: 0; overflow-wrap: anywhere; }
.review-resources > section { padding-block: var(--space-3); border-top: 1px solid var(--border); }
.review-resources h3 { font-size: var(--font-size-body); margin: 0 0 var(--space-3); }
.review-selected { background: var(--color-selected); }
/* The dirty tint equals the selected tint, so selection also carries a
   non-colour cue: an inset accent bar on the row's first cell. */
.review-selected > :first-child { box-shadow: inset 3px 0 var(--color-accent); }
.review-state .chip:has(> .badge) { padding: 0; border: 0; background: none; }
.review-state .chip:has(> .badge)::before { display: none; }
@media (max-width: 700px) {
  .review-editor tbody tr { grid-template-columns: minmax(0, 1fr); padding-block: var(--space-3); }
  .review-editor tbody tr > td[class] { grid-column: 1; grid-row: auto; }
  .review-editor td.num { order: 0; text-align: start; }
  .review-editor td.src { order: 1; }
  .review-editor td.tgt { order: 2; }
  .review-editor td.review-state { order: 3; }
  .review-editor td.acts { order: 4; }
  .review-editor td.tm { order: 5; }
  .review-editor td.terms { order: 6; }
  .review-editor td.qa { order: 7; }
  .review-editor .tm, .review-editor .terms, .review-editor .qa { border-inline-start: 0; }
  .review-editor .mini { min-height: 40px; min-width: 40px; }
  /* A segment with no TM match, terms or QA flags shows no empty section label. */
  .review-editor td.tm:not(:has(.badge, button)), .review-editor td.terms:not(:has(.term)), .review-editor td.qa:not(:has(details)) { position: absolute; width: 1px !important; height: 1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .editor-toolbar { position: static; }
}
/* END ux/review-editor. */

/* BEGIN ux/workflows: page layout; shared tokens and saved card keys stay intact. */
.ux-workflows { min-width: 0; }
.ux-workflows .wf-intro { position: relative; margin-bottom: var(--space-5); }
.ux-workflows .wf-intro .page-header { margin-bottom: var(--space-2); }
.ux-workflows .page-header h1 { overflow-wrap: anywhere; }
.ux-workflows .wf-section-nav, .ux-workflows .wf-area-tabs {
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  padding-block: var(--space-2); margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-border);
}
.ux-workflows .wf-section-nav a, .ux-workflows .wf-area-tabs a {
  display: inline-flex; align-items: center; min-height: var(--control-height);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-control);
}
.ux-workflows .wf-area-tabs a[aria-current] {
  background: var(--color-selected); color: var(--color-accent-text);
  text-decoration: underline; text-underline-offset: .35em;
}
.ux-workflows [id] { scroll-margin-top: var(--space-5); }
.ux-workflows .form-section { min-width: 0; width: 100%; margin-block: var(--space-3); }
.ux-workflows .form-section label { min-width: 0; }
.ux-workflows .form-section label:not(.checkbox):not(.check) {
  display: flex; flex-direction: column; align-items: stretch; gap: var(--space-2);
  margin-block: var(--space-3);
}
.ux-workflows .form-section :is(input:not([type="checkbox"]):not([type="radio"]), select, textarea) { width: 100%; max-width: 100%; }
/* Specificity must beat the legacy `.form > fieldset:not(...)` flex column (rule 2 above). */
.ux-workflows .inline-form > fieldset.form-section:not(.lp-fieldset):not(.wf-step-fieldset) {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: var(--space-3); align-items: end;
}
.ux-workflows .inline-form > fieldset.form-section > button { justify-self: start; }
.ux-workflows .step { padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface); }
.ux-workflows .step + .step { margin-top: var(--space-3); }
.ux-workflows .step-head { align-items: center; gap: var(--space-2); }
.ux-workflows .step-head strong { overflow-wrap: anywhere; }
.ux-workflows .step-edit { margin-top: var(--space-3); border-top: 1px solid var(--color-border); padding-top: var(--space-3); }
.ux-workflows .step-edit > summary { min-height: var(--control-height); }
.ux-workflows .data-table-region { max-width: 100%; }
/* The legacy flex `.row-actions` (rule near the top) must not turn table cells into flex boxes. */
.ux-workflows .data-table th.row-actions, .ux-workflows .data-table td.row-actions { display: table-cell; }
.ux-workflows .data-table td.row-actions form { display: inline-block; }
.ux-workflows .data-table td.num { text-align: end; font-variant-numeric: tabular-nums; }
.ux-workflows .prompt { white-space: pre-wrap; overflow-wrap: anywhere; }
.wf-graph-region { max-width: 100%; overflow: auto; padding: var(--space-1); }
.wf-graph-region > .wf-graph { margin-inline: 0; }
.wf-graph-region .wf-graph-svg { max-width: none; }
@media (max-width: 600px) {
  .ux-workflows .wf-studio { grid-template-columns: minmax(0, 1fr); }
  .ux-workflows .wf-new-cta { white-space: normal; }
  .ux-workflows .step { padding: var(--space-3); }
  .ux-workflows .inline-form { flex-wrap: wrap; }
  .ux-workflows .inline-form > label { min-width: 0; flex-basis: 100%; }
  .ux-workflows .grid2 { grid-template-columns: minmax(0, 1fr); }
}
/* The legacy .empty marker stays on the workflows list; the shared empty state supplies the look. */
.ux-workflows .empty { padding: 0; }
.ux-workflows .empty::before { content: none; }
/* END ux/workflows. */

/* BEGIN ux/admin-auth-account: account sections and standalone sign-in geometry. */
.auth-admin-layout { display: grid; grid-template-columns: 176px minmax(0, 1fr); gap: var(--space-5); align-items: start; }
.auth-admin-layout > .page-layout { min-width: 0; }
.auth-section-nav { display: grid; gap: var(--space-1); padding-block: var(--space-2); }
.auth-section-nav a { display: flex; align-items: center; min-height: 40px; padding: var(--space-2) var(--space-3); border-inline-start: 2px solid var(--color-border); color: var(--color-text-secondary); text-decoration: none; overflow-wrap: anywhere; }
.auth-section-nav a:hover, .auth-section-nav a:focus-visible { color: var(--color-text); background: var(--color-surface); border-inline-start-color: var(--color-control-border); }
.auth-admin-layout .card { min-width: 0; scroll-margin-block-start: 90px; }
.auth-admin-layout .card:target { outline: 2px solid var(--color-control-border); outline-offset: 3px; }
.auth-admin-layout .form-section { margin: 0; padding: 0; border: 0; }
.auth-admin-layout .form-section > legend { padding: 0; max-width: 100%; }
.auth-admin-layout .form-section > legend h2 { margin-block: 0 var(--space-4); }
.auth-admin-layout .form + .form { padding-block-start: var(--space-4); border-block-start: 1px solid var(--color-border); }
.auth-admin-layout .form > button { justify-self: start; }
.auth-admin-layout .data-table-region { margin-block-start: var(--space-4); }
.auth-admin-layout .data-table td { vertical-align: top; }
.auth-admin-layout .choice-options { min-width: 0; }
.auth-admin-layout .choice-option { min-width: 0; }
.auth-admin-layout .choice-meta { min-width: 0; overflow-wrap: anywhere; }
.auth-account { padding: var(--space-5); }
.auth-account .authcard { max-width: 480px; padding: var(--space-6); border-radius: var(--radius-card); box-shadow: none; min-width: 0; overflow-wrap: anywhere; }
.auth-account .brand-big { margin-block-end: var(--space-5); }
.auth-account .page-header { margin-block-end: var(--space-3); }
.auth-account .page-header h1 { font-size: 1.5rem; line-height: 1.25; }
.auth-account .form { gap: var(--space-4); margin-block-start: var(--space-5); }
.auth-account .form > button { width: 100%; }
.auth-account .notice { margin-block: var(--space-4); }
.auth-mfa-card { max-width: 760px; }
.auth-mfa-setup { display: grid; grid-template-columns: minmax(0, 200px) minmax(0, 1fr); gap: var(--space-5); align-items: center; margin-block: var(--space-5); }
.auth-mfa-setup .mfa-qr { margin: 0; }
.auth-mfa-setup .mfa-key { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
.auth-mfa-setup .mfa-key dd { margin-block-end: var(--space-3); }
.auth-recovery-panel { padding: var(--space-4); margin-block: var(--space-5); background: var(--color-surface); border: 1px solid var(--color-control-border); border-radius: var(--radius-card); color: var(--color-text); }
.auth-recovery-panel .mfa-codes { margin-block-start: 0; }
.auth-recovery-panel textarea { width: 100%; max-width: 100%; }
.auth-account .auth-mfa-setup { grid-template-columns: minmax(0, 1fr); }
.auth-account .mfa-qr { max-width: 240px; }
@media (max-width: 1000px) {
  .auth-admin-layout { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
  .auth-section-nav { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 600px) {
  .auth-account { padding: var(--space-3); }
  .auth-account .authcard { padding: var(--space-4); }
  .auth-mfa-setup { grid-template-columns: minmax(0, 1fr); }
  .auth-mfa-setup .mfa-qr { max-width: 240px; }
  .auth-admin-layout .choice-option { flex-wrap: wrap; }
  .auth-admin-layout .inline-form > label { min-width: 0; max-width: 100%; }
  .auth-admin-layout .inline-form input, .auth-admin-layout .inline-form select { max-width: 100%; }
}
.auth-admin-layout .linkbox { box-sizing: border-box; max-width: 100%; min-width: 0; }
/* END ux/admin-auth-account */

/* BEGIN ux/advanced: anchored expert forms and token-based run transcript. */
.adv-page { min-width: 0; }
.adv-page .adv-intro { margin-bottom: var(--space-5); }
.adv-page .adv-intro > p { max-width: var(--readable); }
.adv-page .adv-section-nav { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block: var(--space-5); border-bottom: 1px solid var(--color-border); padding-bottom: var(--space-3); }
.adv-page .adv-section-nav a { display: inline-flex; align-items: center; min-height: var(--control-height); padding: var(--space-2) var(--space-3); border-radius: var(--radius-control); background: var(--color-subtle); }
.adv-page .form-section { min-width: 0; margin-block: var(--space-4); }
.adv-page .form .row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); gap: var(--space-4); align-items: start; }
.adv-page .data-table .row-actions { display: table-cell; }
.adv-page .form label, .adv-page .form input, .adv-page .form select, .adv-page .form textarea { min-width: 0; max-width: 100%; }
.adv-page .form label:not(.inline) { display: flex; flex-direction: column; gap: var(--space-2); }
.adv-page .form-section > .inline { display: inline-flex; align-items: baseline; margin: var(--space-2) var(--space-3) var(--space-2) 0; }
.adv-page .adv-tmpl label { min-width: 0; flex-wrap: wrap; }
.adv-page .adv-tmpl select { min-width: 0; max-width: 100%; }
.adv-page .card, .adv-page .form-section { scroll-margin-top: var(--space-8); }
.adv-page .adv-kv { display: flex; flex-wrap: wrap; gap: var(--space-5); margin-block: var(--space-2) var(--space-4); }
.adv-page .adv-kv > div { min-width: 7rem; }
.adv-page .adv-kv .k, .adv-page .kind { font-size: var(--font-size-caption); color: var(--color-text-muted); }
.adv-page .adv-kv .v { font-size: var(--font-size-section); font-weight: var(--font-weight-heading); }
.adv-page .tbadge { display: inline-block; padding: var(--space-1) var(--space-2); border-radius: var(--radius-small); font: var(--font-size-caption) var(--mono); background: var(--color-subtle); border: 1px solid var(--color-border); color: var(--color-text); }
.adv-page .txn { border: 1px solid var(--color-border); border-radius: var(--radius-control); margin-block: var(--space-2); padding: var(--space-3); }
.adv-page .tier-spend { border-inline-start: 3px solid var(--danger); }
.adv-page .tier-read, .adv-page .kind-delegate_result { border-inline-start: 3px solid var(--color-accent); }
.adv-page .tier-write, .adv-page .tier-write_staged { border-inline-start: 3px solid var(--success); }
.adv-page .kind-adjudication { border-inline-start: 3px solid var(--color-accent); background: var(--color-subtle); }
.adv-page .kind-quality { border-inline-start: 3px solid var(--warn); background: var(--color-subtle); }
.adv-page .txn .head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-3); align-items: baseline; }
.adv-page .txn .seq { color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
.adv-page .txn pre { white-space: pre-wrap; overflow-wrap: anywhere; background: var(--color-subtle); border: 1px solid var(--color-border); color: var(--color-text); padding: var(--space-3); border-radius: var(--radius-small); }
.adv-page .lens-blocked, .adv-page .blocks { color: var(--danger); font-weight: 600; }
.adv-page .lens-pass, .adv-page .adj .win { color: var(--success); font-weight: 600; }
.adv-page .finding { font-size: var(--font-size-body); margin-block: var(--space-1); }
.adv-page .finding .sev, .adv-page .cand { font-family: var(--mono); }
.adv-page .adj { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block: var(--space-2); }
.adv-page .adj .why { font-style: italic; }
.adv-page .txn details { margin-top: var(--space-2); }
.adv-page .txn summary { cursor: pointer; min-height: var(--control-height); color: var(--color-text-muted); }
.adv-page .manifest { padding-inline-start: var(--space-5); }
.adv-page .adv-transcript { max-height: 42rem; overflow: auto; padding: var(--space-2); }
.adv-page .adv-unit-head { flex-wrap: wrap; }
.adv-page .adv-unusable { opacity: 1; color: var(--color-text-muted); }
.adv-page .adv-unit, .adv-page .adv-kv, .adv-page .txn, .adv-page label { overflow-wrap: anywhere; }
.adv-page .btn.danger { color: var(--danger); background: var(--danger-soft); border-color: var(--danger); }
.adv-page .btn.danger:hover { color: var(--danger); background: var(--danger-soft); }
@media (max-width: 600px) {
  .adv-page .form-section > .inline { display: flex; min-height: 40px; }
  .adv-page .adv-section-nav a { min-height: 40px; }
}
.adv-page .adv-inline-form { display: inline; }
.adv-page .adv-kv .v.adv-corr { font-size: var(--font-size-caption); }
/* END ux/advanced. */

/* BEGIN ux/workflow-step-tabs (#594): one tab per workflow step. Reuses the app's .tabs/.tab
   pill look and semantic tokens only. The tab bar is hidden until wftabs.js adds .wf-tabbed,
   so with no JS every panel stays visible as its own stacked card. */
.ux-workflows .wf-tabbar { display: none; }
.ux-workflows .wf-steptabs.wf-tabbed > .wf-tabbar {
  display: flex; flex-wrap: nowrap; gap: var(--space-2); margin-top: var(--space-3);
  overflow-x: auto; overscroll-behavior-x: contain; padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
}
.ux-workflows .wf-tabbar .tab {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: var(--control-height); max-width: 16rem; background: transparent; cursor: pointer;
  font: inherit; font-size: .84rem; color: var(--muted); white-space: nowrap;
}
.ux-workflows .wf-tabbar .tab:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.ux-workflows .wf-tabbar .tab[aria-selected="true"] {
  background: var(--primary-soft); color: var(--primary-2); border-color: var(--primary); font-weight: 600;
}
.ux-workflows .wf-tab-num {
  display: inline-grid; place-items: center; min-width: 1.5em; height: 1.5em; padding: 0 .25em;
  border-radius: var(--radius-pill); border: 1px solid currentColor; font-size: .78em; font-variant-numeric: tabular-nums;
}
.ux-workflows .wf-tab-type { overflow: hidden; text-overflow: ellipsis; }
.ux-workflows .wf-tab-add { border-style: dashed; }
.ux-workflows .wf-tabbar .tab[data-invalid] { border-color: var(--danger-border-solid); color: var(--danger); }
.ux-workflows .wf-tabbar .tab[data-invalid]::after { content: "!"; font-weight: 700; }
.ux-workflows :is(.wf-tabpanel, fieldset.wf-step-fieldset[role="tabpanel"]) {
  margin-top: var(--space-3); padding: var(--space-4);
  border: 1px solid var(--color-border); border-radius: var(--radius-card); background: var(--color-surface);
}
.ux-workflows :is(.wf-tabpanel, fieldset.wf-step-fieldset[role="tabpanel"])[hidden], .ux-workflows [data-wf-panel-wrap][hidden] { display: none; }
.ux-workflows :is(.wf-tabpanel, fieldset.wf-step-fieldset[role="tabpanel"]):focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.ux-workflows fieldset.wf-step-fieldset[role="tabpanel"] { margin-inline: 0; min-width: 0; }
.ux-workflows .wf-panel-title { margin: var(--space-3) 0 var(--space-2); font-size: 1rem; }
.ux-workflows .wf-settings-panel > .wf-panel-title:first-child { margin-top: 0; }
.ux-workflows div.steplist { padding-left: 0; }
.ux-workflows .steplist, .ux-workflows ol.steplist { list-style: none; padding-left: 0; margin: 0; }
.ux-workflows .steplist > li.step-item { margin: 0; padding: 0; border: 0; list-style: none; }
.ux-workflows .steplist > li.step-item + li.step-item { margin-top: var(--space-3); }
.ux-workflows .steplist .step.wf-tabpanel { margin-top: var(--space-3); }
.ux-workflows .wf-step-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); flex: 1 1 100%;
  padding-top: var(--space-2); border-top: 1px dashed var(--color-border);
}
.ux-workflows .wf-step-actions form { margin: 0; }
@media (max-width: 600px) {
  .ux-workflows :is(.wf-tabpanel, fieldset.wf-step-fieldset[role="tabpanel"]) { padding: var(--space-3); }
  .ux-workflows .wf-tabbar .tab { min-height: var(--control-height-mobile); }
}
/* END ux/workflow-step-tabs. */

/* ============================================================================
   Console look (2026-10-08, approved by the maintainer from the mockups).
   The tokens at the top of this file carry the palette; this block carries the
   structure: sidebar same as the page with a hairline divider, subtle gray active
   nav, hairline borders and no shadows, unboxed tables with small uppercase headers,
   a centered content column on list and settings pages, and the "..." row menu.
   Work surfaces (editor, canvas) opt out of the column with content-wide.
   ============================================================================ */

/* Shell */
.sidebar { background: var(--bg); border-right: 1px solid var(--border); padding: 14px 10px; }
.sidebar .brand { font-size: .8125rem; font-weight: 600; gap: 8px; padding: 6px 8px 14px; letter-spacing: 0; }
.sidebar .brand .mark { width: 22px; height: 22px; border-radius: 6px; background: var(--text); color: var(--bg); font-size: .75rem; }
.side-nav a { padding: 7px 10px; font-size: .8125rem; border-radius: var(--radius-sm); }
.side-nav a:hover { background: color-mix(in srgb, var(--nav-on) 55%, transparent); color: var(--text); }
.side-nav a.on { background: var(--nav-on); color: var(--text); font-weight: 600; box-shadow: none; }
.nav-group { font-size: .6875rem; letter-spacing: .06em; padding: 14px 10px 4px; }
.side-foot { border-top: 1px solid var(--border); }
.topbar2 { background: var(--bg); border-bottom: 1px solid var(--border); min-height: 52px; padding: 8px 40px; }
.topbar2 .crumbs { font-size: .8125rem; color: var(--muted); }
.topsearch { background: var(--elev); border-radius: var(--radius-sm); }
.avatar { background: var(--panel-2); color: var(--text); border: 0; }
.usermenu > summary { border-radius: var(--radius-sm); background: transparent; border-color: transparent; padding: 3px 8px 3px 3px; }
.usermenu > summary:hover, .usermenu[open] > summary { border-color: var(--border); background: var(--panel-2); }
.um-panel { border-radius: var(--radius-card); min-width: 240px; }
.um-panel a, .um-panel button.um-item { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; padding: 8px 10px; color: var(--text-dim); font-weight: 400; min-height: 0; }
.um-panel button.um-item:hover, .um-panel a:hover { background: var(--panel-2); color: var(--text); filter: none; }
/* The menu glyphs are plain symbols and emoji: keep them monochrome and quiet. */
.um-panel a > [aria-hidden="true"]:first-child, .um-panel button.um-item > [aria-hidden="true"]:first-child:not(.um-check) { filter: grayscale(1); opacity: .75; }
.um-group-label { padding: 6px 10px 2px; font-size: .6875rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.um-check { display: inline-block; width: 1em; text-align: center; color: var(--primary-2); }
.um-item[aria-checked="true"] .um-check::before { content: "\2713"; content: "\2713" / ""; }
.um-item[aria-checked="true"] { color: var(--text); }
.um-danger { color: var(--danger) !important; }
.um-danger:hover { background: var(--danger-soft) !important; }

/* Content column: centered on list and settings pages, full width on work surfaces. */
.content { max-width: var(--content-max); margin-inline: auto; padding: 36px 40px 56px; }
.content.content-wide { max-width: none; margin-inline: 0; padding-inline: 28px; }

/* Page header: title, one muted sentence (optional link), one primary action on the right. */
.page-header { align-items: flex-end; margin-bottom: 20px; }
.page-header h1 { margin: 0 0 4px; font-size: var(--font-size-title); }
.page-header-meta { color: var(--text-dim); font-size: var(--font-size-body); }
.page-header-meta p { margin: 0; }
.page-header-actions { align-items: center; }
@media (max-width: 600px) {
  /* Stacked on a phone: the copy and the action start at the left edge (flex-end would push them right). */
  .page-header { align-items: flex-start; }
}

/* Hairline surfaces, no shadows. */
.card { background: transparent; border: 1px solid var(--border); box-shadow: none; padding: 20px 24px; }
.card.flat, .card[data-flat] { border: 0; padding: 0; background: none; }
/* A card that floats over the page (a dialog's panel) must stay opaque: the hairline card is
   transparent, so without this the New project dialog, the session-timeout prompt and the editor's
   confirm dialog show the page through them. */
dialog .card, .modal-card { background: var(--elev); }
.card.flat > h2:first-child, .card.flat > .card-head, .card[data-flat] > h2:first-child { margin-top: 0; }
.kpi, .tile, .stat, .step, .runstep { box-shadow: none; }
.kpi:hover, .tile:hover { transform: none; }

/* Controls: 6px radius, a field surface, a 2px focus ring. */
input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):not([type="color"]), select, textarea { background: var(--elev); }
input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):not([type="color"]):focus, select:focus, textarea:focus { box-shadow: none; }
button { font-weight: 600; font-size: .8125rem; }
button.small, .btn-small, .mini, .btnlink, td button:not(.primary):not(.btn-primary):not(.row-menu-btn):not([role="menuitem"]):not(.danger-btn):not(.danger), td .btnlink:not([role="menuitem"]) {
  background: transparent; color: var(--text); border: 1px solid var(--input-border);
}
button.small:hover, .btn-small:hover, .mini:hover, td button:not(.primary):not(.btn-primary):not(.row-menu-btn):not([role="menuitem"]):hover { border-color: var(--text-dim); filter: none; }
.btnlink { font-size: .8125rem; }
.badge { font-weight: 500; }

/* Tables: unboxed, small uppercase muted headers, hairline row dividers. */
.data-table-region { border: 0; border-radius: 0; max-height: none; overflow-y: visible; }
.data-table { border-collapse: collapse; }
.data-table thead th, .table thead th, .grid thead th {
  background: var(--bg); color: var(--muted); font-size: .6875rem; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; border-bottom: 1px solid var(--border);
  padding: 0 12px 10px;
}
.data-table td, .data-table tbody th { background: transparent; border-bottom: 1px solid var(--border-soft); vertical-align: top; padding: 14px 12px; height: auto; }
.data-table tbody tr:hover > *, .data-table tbody tr:focus-within > * { background: color-mix(in srgb, var(--panel-2) 40%, transparent); }
.table th, .table td, .grid th, .grid td { border-bottom-color: var(--border-soft); }
.table tbody tr:hover, .grid tbody tr:hover { background: color-mix(in srgb, var(--panel-2) 40%, transparent); }
/* The actions column stays pinned (sticky) so the "..." button is always reachable in a scrolled table. */
.data-table .row-actions { position: sticky; inset-inline-end: 0; border-inline-start: 0; width: 44px; text-align: end; padding-inline: 0 4px; }
.data-table td.row-actions, .data-table thead .row-actions { background: var(--bg); }
.data-table td.row-actions { z-index: 1; }
.data-table td.row-actions:has(.rm-open) { z-index: 30; }
.data-table tbody tr:hover > td.row-actions, .data-table tbody tr:focus-within > td.row-actions { background: color-mix(in srgb, var(--panel-2) 40%, var(--bg)); }
.card:is(.flat, [data-flat]) .data-table :is(th, td):first-child { padding-inline-start: 0; }
.card:is(.flat, [data-flat]) .data-table { min-width: 720px; }
/* TM entries has 7 columns: scroll inside its own focusable region (data-table-region) instead of squeezing Source/Target on a phone. */
.card:is(.flat, [data-flat]) .tm-entries .data-table { min-width: 64rem; }
.tm-entries .data-table :is(th, td):is(:nth-child(1), :nth-child(2)) { min-width: 12rem; }
.tm-entries .data-table :is(th, td):is(:nth-child(4), :nth-child(6), :nth-child(7)) { white-space: nowrap; }
.data-table a { color: var(--primary-2); }
.cell-primary { display: block; font-weight: 600; }
.cell-primary a { color: var(--text); font-weight: 600; text-decoration: none; }
.cell-primary a:hover { text-decoration: underline; }
.cell-secondary { display: block; font-size: .8125rem; color: var(--text-dim); }
.cell-tag { display: inline-block; margin-top: 6px; font-size: .6875rem; padding: 1px 6px; border-radius: var(--radius-small); background: var(--panel-2); color: var(--text-dim); }
/* A key project's type tag is the Keys link (gated on the switch, review A-F8). */
.projects-table td > a.badge.beta { margin-top: 6px; }
.prog { height: 4px; border-radius: 2px; width: 100%; max-width: 170px; }
.prog-bar { border-radius: 2px; }

/* List-page toolbar: a full-width search with Filters beside it. */
.list-toolbar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0 0 12px; }
/* The input itself carries the control chrome (border, field surface, focus ring); the magnifier is overlaid. */
.list-search { position: relative; flex: 1 1 360px; display: block; color: var(--muted); }
.list-search-form { display: contents; }
.list-search-form > fieldset.form-section { border: 0; padding: 0; margin: 0; min-width: 0; }
.list-search svg { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); pointer-events: none; }
.list-search input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):not([type="color"]) { width: 100%; padding-left: 34px; }
.list-toolbar > details.filters:not(.usermenu):not(.inline-edit):not(.rail-collapse) { position: relative; border: 0; background: none; border-radius: 0; }
.list-toolbar > details.filters:not(.usermenu):not(.inline-edit):not(.rail-collapse) > summary { display: inline-flex; align-items: center; gap: 6px; min-height: var(--control-height); padding: 0 12px; border: 1px solid var(--input-border); border-radius: var(--radius-sm); background: var(--elev); color: var(--text); font-size: .8125rem; cursor: pointer; }
.list-toolbar > details.filters:not(.usermenu):not(.inline-edit):not(.rail-collapse) > summary::before { display: none; }
.list-toolbar > details.filters:not(.usermenu):not(.inline-edit):not(.rail-collapse) > summary:hover { background: var(--panel-2); }
.list-toolbar > details.filters:not(.usermenu):not(.inline-edit):not(.rail-collapse)[open] > summary { border-color: var(--text-dim); border-bottom: 1px solid var(--text-dim); }
.list-toolbar > details.filters:not(.usermenu):not(.inline-edit):not(.rail-collapse) > .filters-panel { padding: 12px 0 0; }
.list-toolbar:has(details.filters[open]) { align-items: flex-start; }
.list-toolbar > details.filters[open] { flex: 1 1 100%; order: 3; }
.count-chip { display: inline-block; font-size: .75rem; padding: 2px 8px; border-radius: var(--radius-pill); background: var(--panel-2); color: var(--text-dim); }

/* Row actions "..." menu. The buttons and the hiding of the lists apply only once rowmenu.js
   has run (html.row-menu-ready), so without JS the actions are an inline list. */
.row-menu { position: relative; display: inline-block; }
.row-menu-btn { display: none; align-items: center; justify-content: center; width: 32px; height: 32px; min-height: 32px; padding: 0; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-dim); font-size: 1.25rem; line-height: 1; }
.row-menu-btn:hover { background: var(--panel-2); color: var(--text); filter: none; }
html.row-menu-ready .row-menu-btn { display: inline-flex; }
.row-menu-list { display: flex; flex-direction: column; gap: 2px; text-align: left; }
.row-menu-list form { margin: 0; }
html.row-menu-ready .row-menu-list { display: none; position: fixed; z-index: 60; min-width: 190px; padding: 4px; background: var(--elev); border: 1px solid var(--border); border-radius: var(--radius-card); box-shadow: var(--shadow); }
html.row-menu-ready .row-menu-list.rm-open { display: flex; }
.row-menu-list a, .row-menu-list button { display: flex; align-items: center; width: 100%; min-height: 32px; padding: 6px 10px; border: 0; border-radius: var(--radius-xs); background: none; color: var(--text); font: inherit; font-size: .8125rem; font-weight: 400; text-align: left; text-decoration: none; cursor: pointer; }
.row-menu-list a:hover, .row-menu-list button:hover { background: var(--panel-2); filter: none; }
.row-menu-list .rm-danger, .row-menu-list button.danger-btn { color: var(--danger) !important; border: 0 !important; }
.row-menu-list .rm-danger:hover, .row-menu-list button.danger-btn:hover { background: var(--danger-soft) !important; }
.row-menu-list .rm-note { display: block; padding: 4px 10px; font-size: .75rem; color: var(--muted); }

@media (max-width: 900px) {
  .content { padding-inline: 20px; }
  .content.content-wide { padding-inline: 16px; }
  .topbar2 { padding-inline: 16px; }
}
@media (max-width: 600px) {
  .content, .content.content-wide { padding: 16px 16px 32px; }
  .card { padding: 16px; }
  .card.flat, .card[data-flat] { padding: 0; }
  .list-search { flex-basis: 100%; }
  /* Projects on a phone: Progress and Due drop out (overdue and due-soon stay as badges in
     Status), so the list fits the screen instead of scrolling inside its card. */
  .card:is(.flat, [data-flat]) .projects-table .data-table { min-width: 0; }
  .projects-table .data-table :is(th, td) { padding-inline: 0 8px; }
  .projects-table .data-table :is(th, td):is(:nth-child(3), :nth-child(5)) { display: none; }
}
