/* VocaGateway WebUI.
 *
 * Aligns with VocaHQ marketing design language ("quiet paper desk"):
 * https://github.com/VocaHQ/vocahq/blob/main/web/DESIGN.md
 *
 * Light is the brand default; dark is a graphite pair of the same teal accent.
 * html[data-theme="light"|"dark"] is set by a FOUC-safe boot script (system
 * preference unless the operator overrides with the header toggle).
 */

:root {
  /* Shared tokens (theme-independent) */
  --r: 8px;
  --r-lg: 16px;
  --r-pill: 999px;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  /* System stack only: the gateway is self-hosted and often offline, so it must
     not reach out to a font CDN to render. */
  --font-display: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;

  /* One gutter for the header, the nav and the content, so their left edges line
     up at every width. It tracks `main`'s own max-width and padding. */
  /* 100%, not 100vw: 100vw includes the scrollbar, which would push the header a
     few pixels left of the content it is supposed to line up with. */
  --gutter: max(24px, calc((100% - 1048px) / 2 + 24px));
}

/* Light: VocaHQ DESIGN.md paper desk */
html, html[data-theme="light"] {
  color-scheme: light;
  --bg: #f7f6f3;
  --surface: #ffffff;
  --raised: #efeeea;
  --sunken: #efeeea;
  --line: #e2e0da;
  --line-soft: #eceae4;
  --line-strong: #cdcbc3;
  --text: #1a1c1b;
  --muted: #5c6360;
  /* Contrast floor, not taste: #8a918d scored 2.85-3.22 against bg, surface and
     accent-wash, so every hint, caption and metric label failed WCAG AA. */
  --faint: #666d69;
  /* The brand teal the logo, favicons and tower mark all use. The lighter
     #1f8f75 gave white button ink 4.00 — under AA for the primary action. */
  --accent: #0f6b57;
  --accent-hover: #0b5344;
  --accent-ink: #ffffff;
  --accent-line: #0f6b57;
  --accent-wash: #e6f4ef;
  /* Darkened to clear AA on its own wash (was 3.84): this carries the
     "listening on every interface" warning, which has to be the most legible
     thing on the page, not the least. */
  --warn: #8a5f13;
  --warn-ink: #ffffff;
  --warn-wash: rgba(154, 107, 22, 0.1);
  --error: #b42318;
  --error-wash: rgba(180, 35, 24, 0.08);
  --shadow-soft: 0 10px 30px rgba(26, 28, 27, 0.08);
  --shadow-modal: 0 18px 48px rgba(26, 28, 27, 0.12);
  --overlay-scrim: rgba(26, 28, 27, 0.36);
  --tabs-bg: rgba(247, 246, 243, 0.92);
  --theme-color: #f7f6f3;
  --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1.75 6 6.25l5-4.5' fill='none' stroke='%235c6360' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* Dark: graphite desk, same teal family (readable on near-black) */
html[data-theme="dark"] {
  color-scheme: dark;
  --bg: #141614;
  --surface: #1b1c1b;
  --raised: #232522;
  --sunken: #101210;
  --line: #2e312e;
  --line-soft: #262924;
  --line-strong: #3d423e;
  --text: #e3e4e1;
  --muted: #a2aba5;
  --faint: #79827c;
  --accent: #77d0b2;
  --accent-hover: #8fdcc1;
  --accent-ink: #00382a;
  --accent-line: #2f6e5c;
  --accent-wash: rgba(119, 208, 178, 0.12);
  --warn: #e3b778;
  --warn-ink: #1a1c1b;
  --warn-wash: rgba(227, 183, 120, 0.12);
  --error: #ffb4ab;
  --error-wash: rgba(255, 180, 171, 0.12);
  --shadow-soft: 0 12px 32px rgba(0, 0, 0, 0.35);
  --shadow-modal: 0 20px 50px rgba(0, 0, 0, 0.5);
  --overlay-scrim: rgba(10, 12, 10, 0.72);
  --tabs-bg: rgba(20, 22, 20, 0.92);
  --theme-color: #141614;
  --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1.75 6 6.25l5-4.5' fill='none' stroke='%23a2aba5' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
}
h1 { font-size: 1rem; letter-spacing: -0.02em; margin: 0; }
h2 { font-size: 1.05rem; margin: 0 0 2px; }
h3 { font-size: 0.92rem; margin: 0; letter-spacing: -0.01em; }
h4 { font-size: 0.95rem; margin: 0; letter-spacing: -0.01em; }

p { margin: 0 0 10px; }
p:last-child { margin-bottom: 0; }

a {
  color: var(--accent);
  text-underline-offset: 0.15em;
}
a:hover { color: var(--accent-hover); }

code {
  font-family: var(--mono);
  font-size: 0.85rem;
  color: var(--text);
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 1px 6px;
}
code.block {
  display: block;
  padding: 10px 12px;
  margin: 12px 0;
  color: var(--muted);
  user-select: all;
}

.muted { color: var(--muted); }
.small { font-size: 0.88rem; display: block; }
.error { color: var(--error); }
.hidden { display: none !important; }
.loading { padding: 40px 0; color: var(--muted); }
.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-link {
  position: absolute;
  left: 12px;
  top: 12px;
  z-index: 300;
  padding: 8px 14px;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-display);
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-160%);
  transition: transform 0.15s ease;
}
.skip-link:focus {
  transform: translateY(0);
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

/* A status dot reads at a glance and costs no layout, which is why nothing here
   is a coloured pill any more. */
.dot {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--faint);
}
.dot.ok { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash); }
.dot.warn { background: var(--warn); box-shadow: 0 0 0 3px var(--warn-wash); }
.dot.bad { background: var(--error); box-shadow: 0 0 0 3px var(--error-wash); }

/* -------------------------------------------------------------- header / nav */

header {
  /* Above sticky .tabs (z-index 20) so .engine-popover is not clipped under the bar. */
  position: relative;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px var(--gutter) 14px;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.theme-toggle {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}
.theme-toggle:hover {
  border-color: var(--accent);
  color: var(--text);
  background: var(--accent-wash);
}
.theme-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.theme-toggle svg {
  display: block;
  width: 18px;
  height: 18px;
}
/* Show the icon for the action (moon in light → go dark; sun in dark → go light). */
html[data-theme="light"] .theme-icon-sun,
html[data-theme="dark"] .theme-icon-moon { display: none; }
html[data-theme="light"] .theme-icon-moon,
html[data-theme="dark"] .theme-icon-sun { display: block; }
/* System preference: small badge on the control. */
.theme-toggle[data-preference="system"] {
  box-shadow: inset 0 0 0 1px var(--accent-wash);
}
.theme-toggle[data-preference="system"]::after {
  content: "";
  position: absolute;
  /* decorative only; preference is in aria-label */
  display: none;
}

.brand { display: flex; align-items: center; gap: 12px; }
.logo-stack {
  position: relative;
  display: block;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
}
.logo {
  display: block;
  width: 28px;
  height: 28px;
  /* Square app mark (vocahq.com); favicons stay circular. */
  border-radius: 6px;
}
/* Brand kit: *-dark assets are dark-glyph marks for light surfaces. */
html[data-theme="light"] .logo-for-dark { display: none; }
html[data-theme="dark"] .logo-for-light { display: none; }
.brand h1 {
  line-height: 1.2;
  font-size: 1.05rem;
  font-weight: 600;
}
.brand .muted { font-size: 0.88rem; }

#engine-pill, .engine-status {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  max-width: min(52vw, 420px);
  margin: 0;
  padding: 6px 10px 6px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--muted);
  font-family: var(--font-body);
  font-size: 0.88rem;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}
.engine-status > span:nth-child(2) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.engine-status.ready { color: var(--text); }
.engine-status:hover,
.engine-status:focus-visible {
  border-color: var(--accent);
  color: var(--text);
  background: var(--accent-wash);
}
.engine-status:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.engine-status-hint {
  flex: 0 0 auto;
  margin-left: 2px;
  padding: 1px 8px;
  border-radius: var(--r-pill);
  background: var(--raised);
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.01em;
}
.engine-status:hover .engine-status-hint,
.engine-status:focus-visible .engine-status-hint {
  background: var(--accent);
  color: var(--accent-ink);
}

/* Network bind details: hover/focus card on the model pill (not on Overview).
   Padding-top bridges the visual gap so the pointer can move into the card. */
.engine-popover {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 40;
  display: grid;
  gap: 8px;
  min-width: min(300px, 80vw);
  padding: 10px 0 0;
  border: none;
  background: transparent;
  box-shadow: none;
  color: var(--text);
  font-size: 0.85rem;
  line-height: 1.4;
  text-align: left;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(-4px);
  transition: opacity 0.12s ease, transform 0.12s ease, visibility 0.12s ease;
}
.engine-popover-card {
  display: grid;
  gap: 8px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-soft);
}
.engine-status:hover .engine-popover,
.engine-status:focus-visible .engine-popover,
.engine-status:focus-within .engine-popover {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0);
}
.engine-popover-title {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
}
.engine-popover-row {
  display: grid;
  grid-template-columns: 4.5rem 1fr;
  gap: 10px;
  align-items: baseline;
}
.engine-popover-row > span {
  color: var(--faint);
  font-size: 0.8rem;
}
.engine-popover-row code {
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: 0.8rem;
  color: var(--text);
  background: var(--raised);
  border-color: var(--line);
}
.engine-popover-hint {
  color: var(--muted);
  font-size: 0.8rem;
}

/* Status hero is a one-line readiness signal; model/network detail is in the header. */
.status-hero {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 32px;
  margin: 0 0 26px;
}
.status-hero .headline { display: flex; gap: 11px; }
.status-hero .dot { margin-top: 8px; }
.status-hero h3 { font-size: 1.35rem; margin: 0; line-height: 1.25; }
.status-hero p { color: var(--muted); margin: 2px 0 0; font-size: 0.95rem; }

.tabs {
  display: flex;
  gap: 2px;
  position: sticky;
  top: 0;
  z-index: 20;
  padding: 0 var(--gutter);
  border-bottom: 1px solid var(--line);
  background: var(--tabs-bg);
  backdrop-filter: saturate(140%) blur(10px);
}
.tab {
  background: none;
  border: none;
  border-radius: 0;
  color: var(--muted);
  font-family: var(--font-body);
  font-size: 0.95rem;
  padding: 11px 14px 12px;
  cursor: pointer;
  box-shadow: inset 0 -1px 0 transparent;
}
.tab:hover { color: var(--text); }
.tab.active {
  color: var(--text);
  font-weight: 700;
  box-shadow: inset 0 -2px 0 var(--accent);
}
.tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }

main {
  flex: 1 0 auto;
  width: 100%;
  max-width: 1048px;
  margin: 0 auto;
  padding: 28px 24px 48px;
}

/* ------------------------------------------------------------------- footer */

.site-footer {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: auto;
  padding: 18px var(--gutter) 22px;
  border-top: 1px solid var(--line-soft);
  color: var(--faint);
  font-size: 0.84rem;
  text-align: center;
}
.footer-copy {
  margin: 0;
  color: var(--faint);
}
.footer-copy a {
  color: inherit;
  text-decoration: none;
}
.footer-copy a:hover { color: var(--accent); }

/* ------------------------------------------------------------------ sections */

/* `.card` is a section, not a box. Sections are told apart by a hairline and
   space; only data and controls get a surface of their own. */
.card {
  border-top: 1px solid var(--line-soft);
  padding-top: 22px;
  margin: 0 0 26px;
}
main > .card:first-child, #panel > .card:first-child { border-top: none; padding-top: 2px; }
.section-heading h3 .muted { font-weight: 400; }
#models-list { margin-bottom: 26px; }
#engine-result { margin-top: 14px; }
.card > h3 + p.muted { margin-bottom: 14px; }
.card > p.muted { max-width: none; }
.card > .table-scroll, .card > form, .card > .checklist, .card > .facts { margin-top: 14px; }

.grid.two { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; }

.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 18px;
}
.page-head p { color: var(--muted); margin: 2px 0 0; }

.section-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 12px;
}
.probe-age { color: var(--faint); font-size: 12px; white-space: nowrap; }

/* A note, not a box: one coloured rule carries the whole signal. */
.callout {
  border-left: 2px solid var(--line);
  padding: 1px 0 1px 14px;
  margin: 0 0 22px;
}
.callout strong { color: var(--text); }
.callout span { color: var(--muted); display: block; }
.callout.warning { border-left-color: var(--warn); }
.callout.warning strong { color: var(--warn); }
.callout.success { border-left-color: var(--accent); }
.callout.success strong { color: var(--accent); }
.callout.compact { margin: 16px 0 20px; }
.callout .row { margin-top: 8px; }

/* Full-width one-line notification strip (wildcard bind). × dismisses for 24h. */
.exposure-banner {
  width: 100%;
  border-bottom: 1px solid color-mix(in srgb, var(--warn) 35%, var(--line));
  /* Opaque: --warn-wash is ~12% alpha, and the shell paints the sidebar rail
     across the full height of <body>, so the rail and its divider showed
     through the banner as a stray block and a vertical line near the left. */
  background: linear-gradient(0deg, var(--warn-wash), var(--warn-wash)), var(--bg);
  color: var(--text);
}
.exposure-banner[hidden] { display: none !important; }
/* Clears the rail and repeats main's gutter, so the row inside starts exactly
   where the page content starts. */
.exposure-banner-inner {
  box-sizing: border-box;
  margin-left: var(--rail-w);
  padding: 12px clamp(24px, 4vw, 64px);
}
/* Same measure and centring as #panel, so the warning and the page beneath it
   share a left and a right edge at every width — which is what keeps the
   dismiss button next to the text it dismisses. */
.exposure-banner-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: 4px 12px;
  max-width: 1240px;
  margin: 0 auto;
}
/* A drawn warning triangle rather than an "!" in a filled circle: it matches
   the stroked icon set the rest of the UI uses, and reads at a glance. */
.exposure-banner-mark {
  flex: 0 0 auto;
  display: inline-flex;
  color: var(--warn);
  margin-top: 1px;
}
/* Wraps rather than truncating. It used to be one nowrap line with an
   ellipsis, so on a narrower window the security advice was cut off mid
   sentence — the one piece of copy that must never be. */
.exposure-banner-copy {
  margin: 0;
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.86rem;
  line-height: 1.5;
}
/* Title on its own line, detail under it: one 1300px-long sentence was a
   band of text rather than something with a shape you can scan. */
.exposure-banner-copy strong { display: block; color: var(--warn); font-weight: 700; }
.exposure-banner-copy > span { color: var(--muted); }
.exposure-banner-dismiss {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  min-height: 32px;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 1.35rem;
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
}
.exposure-banner-dismiss:hover {
  background: color-mix(in srgb, var(--warn) 18%, transparent);
  color: var(--text);
}
.exposure-banner-dismiss:focus-visible {
  outline: 2px solid var(--warn);
  outline-offset: 2px;
}

/* Persistent network panel on Pair & test. */
.exposure-panel {
  margin: 0 0 22px;
  padding: 16px 18px;
  border: 1px solid color-mix(in srgb, var(--warn) 40%, var(--line));
  border-radius: var(--r-lg);
  background: var(--warn-wash);
}
.exposure-panel-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}
.exposure-panel-head strong {
  color: var(--warn);
  font-size: 1rem;
  font-weight: 700;
}
.exposure-panel > p {
  margin: 0 0 10px;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.45;
}
.exposure-panel-list {
  margin: 0 0 12px;
  padding-left: 1.2rem;
  color: var(--text);
  font-size: 0.9rem;
  line-height: 1.5;
}
.exposure-panel-list li { margin: 3px 0; }
.exposure-panel .muted.small { margin: 0; }

@media (max-width: 760px) {
  .exposure-banner-inner { margin-left: 0; padding: 12px 16px; }
}

/* ------------------------------------------------------- overview: operations */

.operations { border-top: 1px solid var(--line-soft); padding-top: 22px; margin: 0 0 26px; }

/* KPI strip: restored main counters; charts cover trends / stages. */
.operations-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--line-soft);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.operations-grid-kpis { margin-bottom: 0; }
.metric-card { min-width: 0; padding: 13px 15px 15px; background: var(--surface); }
.metric-card.success .metric-value { color: var(--accent-hover); }
html[data-theme="dark"] .metric-card.success .metric-value { color: var(--accent); }
.ops-bar-ok { fill: var(--accent); }
.ops-bar-bad { fill: var(--error); }
.ops-bars rect { shape-rendering: crispEdges; }
.metric-label, .metric-detail { display: block; }
.metric-label { color: var(--muted); font-size: 12px; }
.metric-value {
  display: block;
  overflow: hidden;
  margin: 5px 0 3px;
  color: var(--text);
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.metric-card.failure .metric-value, .metric-card.failed .metric-value { color: var(--error); }
.metric-detail { min-height: 1.4em; color: var(--faint); font-size: 12px; line-height: 1.45; }

/* Capacity sits between KPIs and charts — one place for queue/active. */
.ops-capacity {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin: 12px 0 0;
  padding: 10px 14px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.ops-capacity-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--muted);
}
.ops-capacity-meta .muted { font-weight: 500; }
.ops-bar-thin { height: 8px; }

/* Charts continue the same section (no nested “Trends” card). */
.ops-charts {
  margin-top: 12px;
  padding: 0;
  border: none;
  border-radius: 0;
  background: transparent;
}
.ops-charts-inline { margin-top: 12px; }
.ops-charts-grid,
.ops-charts-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}
.ops-chart {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
  padding: 12px 13px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.ops-chart-head {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.ops-chart-title {
  color: var(--text);
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.ops-chart-sub {
  color: var(--faint);
  font-size: 0.75rem;
  line-height: 1.35;
}
.ops-chart-body {
  min-height: 48px;
  display: flex;
  align-items: center;
}
.ops-chart-body-bar { min-height: 28px; }
.ops-chart-footer {
  margin: 0;
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.35;
}
.sparkline {
  display: block;
  width: 100%;
  height: 48px;
  overflow: visible;
}
.sparkline-line {
  stroke: var(--accent);
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.sparkline-fill {
  fill: var(--accent-wash);
  opacity: 0.9;
}
.sparkline-baseline {
  stroke: var(--line);
  stroke-width: 1;
  stroke-dasharray: 4 4;
  vector-effect: non-scaling-stroke;
}
.ops-bar {
  display: flex;
  width: 100%;
  height: 14px;
  overflow: hidden;
  border-radius: var(--r-pill);
  background: var(--raised);
  border: 1px solid var(--line-soft);
}
.ops-bar-empty {
  background: repeating-linear-gradient(
    -45deg,
    var(--raised),
    var(--raised) 6px,
    var(--line-soft) 6px,
    var(--line-soft) 12px
  );
}
.ops-bar-seg { display: block; height: 100%; min-width: 0; }
.ops-bar-ok { background: var(--accent); }
.ops-bar-bad { background: var(--error); }
.ops-bar-normalize { background: color-mix(in srgb, var(--accent) 45%, var(--raised)); }
.ops-bar-load { background: color-mix(in srgb, var(--accent) 70%, var(--text)); }
.ops-bar-infer { background: var(--accent); }
.ops-bar-active { background: var(--accent); }
.ops-bar-queue { background: var(--warn); }
.ops-bar-free { background: transparent; }

/* ------------------------------------------------------- facts and checklists */

.facts { display: grid; grid-template-columns: auto 1fr; gap: 7px 18px; margin: 0; }
.facts dt { color: var(--muted); }
.facts dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 12.5px;
  overflow-wrap: anywhere;
}
.facts dd code { background: none; border: none; padding: 0; }

/* Overview host panel: capability hero + expandable inventory. */
.system-card {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  padding: 0;
  border-top: 1px solid var(--line);
  overflow: hidden;
}
/* `.card` usually uses a top hairline only; this block is a real surface. */
#panel > .card.system-card {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 0;
  background: var(--surface);
}
.sys-hero {
  padding: 18px 18px 16px;
  background:
    linear-gradient(135deg, var(--accent-wash) 0%, transparent 55%),
    var(--surface);
}
.sys-hero-kicker {
  margin: 0 0 4px;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
}
.sys-hero-headline {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--text);
}
.sys-hero-specs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 4px;
  margin: 0 0 6px;
  color: var(--text);
  font-family: var(--font-display);
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.4;
}
.sys-spec {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  max-width: 100%;
}
.sys-spec-text {
  min-width: 0;
  overflow-wrap: anywhere;
}
.sys-spec-sep {
  color: var(--faint);
  font-weight: 500;
  margin: 0 2px;
  user-select: none;
}
/* Bare brand marks — no circular badge chrome. */
.sys-vendor {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: auto;
  height: auto;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 0;
  background: transparent;
  color: var(--muted);
  line-height: 0;
}
.sys-vendor svg {
  display: block;
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
}
/* Brand colors only — icon sits directly next to the text. */
.sys-vendor-nvidia { color: #76b900; }
.sys-vendor-amd { color: #ed1c24; }
.sys-vendor-intel { color: #0071c5; }
.sys-vendor-apple { color: var(--text); }
html[data-theme="dark"] .sys-vendor-nvidia { color: #a3e635; }
html[data-theme="dark"] .sys-vendor-amd { color: #fb7185; }
html[data-theme="dark"] .sys-vendor-intel { color: #38bdf8; }
.sys-hero-meta {
  margin: 0;
  color: var(--faint);
  font-size: 0.88rem;
  line-height: 1.4;
}
.sys-details {
  border-top: 1px solid var(--line-soft);
  margin: 0;
  padding: 14px 18px;
}
/* `.sys-details > summary` picks up the shared disclosure row further down. */
.sys-details-facts {
  margin: 0;
  padding: 4px 0 0;
  grid-template-columns: minmax(7rem, auto) 1fr;
  gap: 8px 16px;
}
.sys-details-facts dt {
  color: var(--faint);
  font-size: 0.82rem;
  font-weight: 600;
}
.sys-details-facts dd {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  font-family: var(--font-body);
  font-size: 0.9rem;
  color: var(--text);
  overflow-wrap: anywhere;
}
.sys-detail-text { min-width: 0; }
.sys-detail-extra {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  width: 100%;
  margin-top: 3px;
  color: var(--muted);
  font-size: 0.86rem;
}
.sys-detail-extra .sys-vendor svg {
  width: 15px;
  height: 15px;
}

/* Libraries & tools — same card language as system / ops charts. */
#panel > .card.libraries-card {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 0;
  background: var(--surface);
  overflow: hidden;
}
.lib-hero {
  padding: 18px 18px 14px;
  background:
    linear-gradient(135deg, var(--accent-wash) 0%, transparent 55%),
    var(--surface);
  border-bottom: 1px solid var(--line-soft);
}
.lib-kicker {
  margin: 0 0 4px;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
}
.lib-headline {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--text);
}
.lib-meta {
  margin: 0;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.45;
}
.lib-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: var(--line-soft);
}
.lib-tile {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  padding: 14px 15px 15px;
  background: var(--surface);
}
.lib-tile-top {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.lib-tile-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text);
}
.lib-tile-status {
  flex: 0 0 auto;
  color: var(--faint);
  font-size: 0.78rem;
  font-weight: 500;
}
.lib-tile-detail {
  margin: 0;
  overflow: hidden;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.4;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.checklist { list-style: none; margin: 0; padding: 0; }
.checklist li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 4px 0;
  flex-wrap: wrap;
}
.check { flex: 0 0 12px; font-size: 12px; line-height: inherit; }
.check.ok { color: var(--accent); }
.check.missing { color: var(--error); }
.checklist .hint { margin-left: auto; }

.steps { margin: 0; padding-left: 18px; color: var(--muted); }
.steps li { padding: 3px 0; }

/* --------------------------------------------------------------- pairing card */

.pairing-lead { margin-bottom: 16px; max-width: 52rem; }
.pairing-lead:has(+ .pairing-token-notice) { margin-bottom: 12px; }

/* Stale / unknown token notice sits between the lead and the QR task. */
.pairing-token-notice {
  display: grid;
  gap: 16px;
  margin: 0 0 28px;
  padding: 12px 8px 8px 16px;
  max-width: 52rem;
  border-left-color: color-mix(in srgb, var(--accent) 65%, var(--line));
}
.pairing-token-notice.warning {
  border-left-color: var(--warn);
}
.pairing-token-notice-body {
  margin: 0;
  color: var(--muted);
  line-height: 1.5;
}
.pairing-token-notice-body strong { color: var(--text); }
.pairing-token-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}
.pairing-token-actions form { margin: 0; }
.pairing-token-rotate-hint {
  margin: 0;
  max-width: 36rem;
  line-height: 1.45;
}

.pairing-layout {
  display: flex;
  flex-wrap: wrap;
  gap: 24px 28px;
  align-items: flex-start;
}
.pairing-qr-wrap {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
button.pairing-qr,
.pairing-qr {
  position: relative;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 10px;
  width: 178px;
  height: 178px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-soft);
  font: inherit;
  color: inherit;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
button.pairing-qr:hover,
button.pairing-qr:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash), var(--shadow-soft);
  outline: none;
}
button.pairing-qr:active {
  transform: scale(0.98);
}
.pairing-qr svg { width: 160px; height: 160px; display: block; pointer-events: none; }
.pairing-qr-copy { width: 100%; justify-content: center; }

/* ------------------------------------------------------------- QR enlarged */
/* Sized off the viewport's short edge so it fills a phone screen and stays
   square on a laptop; white plate and quiet zone kept, since a scanner needs
   the contrast and the margin. */
.qr-zoom {
  width: max-content;
  max-width: calc(100vw - 32px);
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--surface);
  color: var(--text);
  overflow: visible;
}
.qr-zoom::backdrop { background: rgb(0 0 0 / 68%); }
.qr-zoom-figure {
  display: grid;
  place-items: center;
  margin: 28px 28px 0;
  padding: 18px;
  border-radius: 14px;
  background: #fff;
}
.qr-zoom-figure svg {
  display: block;
  width: min(58vmin, 380px);
  height: min(58vmin, 380px);
}
.qr-zoom-meta {
  display: grid;
  justify-items: center;
  gap: 6px;
  margin: 0;
  padding: 18px 28px 26px;
  text-align: center;
}
.qr-zoom-meta > span:first-child { font-weight: 600; }
.qr-zoom-meta code { font-size: 0.9rem; overflow-wrap: anywhere; }
@media (max-width: 560px) {
  .qr-zoom-figure { margin: 22px 16px 0; padding: 12px; }
  .qr-zoom-meta { padding: 14px 16px 20px; }
}
.pairing-qr-hint {
  position: absolute;
  inset: auto 8px 8px 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 5px 8px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--text) 78%, transparent);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.15s ease, transform 0.15s ease;
  pointer-events: none;
}
button.pairing-qr:hover .pairing-qr-hint,
button.pairing-qr:focus-visible .pairing-qr-hint,
button.pairing-qr.is-copied .pairing-qr-hint {
  opacity: 1;
  transform: translateY(0);
}
button.pairing-qr.is-copied .pairing-qr-hint {
  background: var(--accent);
  color: var(--accent-ink);
}
html[data-theme="dark"] .pairing-qr-hint {
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  color: var(--text);
  border: 1px solid var(--line);
}
html[data-theme="dark"] button.pairing-qr.is-copied .pairing-qr-hint {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: transparent;
}
.pairing-meta {
  flex: 1 1 280px;
  min-width: 0;
  max-width: 480px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.pairing-fields { display: grid; gap: 12px; }
.pairing-fields label,
.pairing-panel label {
  display: grid;
  gap: 5px;
}
.pairing-fields label > span:first-child,
.pairing-panel label > span:first-child {
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
}
.field-hint {
  color: var(--faint);
  font-size: 0.78rem;
  font-weight: 400;
  line-height: 1.35;
}
.pairing-fields select,
.pairing-fields input,
.pairing-panel input { width: 100%; }
.pairing-encode-summary {
  margin: 0;
  line-height: 1.45;
}
.pairing-code {
  font-size: 0.84em;
  overflow-wrap: anywhere;
}
.pairing-token-hint { color: var(--faint); }
.pairing-advanced {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line-soft);
}
.pairing-advanced-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px 20px;
  align-items: start;
}
.pairing-panel {
  margin: 0;
  min-width: 0;
  padding: 14px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  background: var(--bg);
}
.pairing-panel .row {
  margin-top: 0;
  max-width: none;
}
.pairing-include-port {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 4px;
  cursor: pointer;
}
.pairing-include-port input {
  width: 18px;
  height: 18px;
  margin: 2px 0 0;
  flex: none;
  accent-color: var(--accent);
}
.pairing-include-port span {
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 400;
  line-height: 1.35;
}
.pairing-saved { margin-top: 16px; }
.pairing-subhead {
  margin: 0 0 8px;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
}
.pairing-saved ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.pairing-saved li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-width: 0;
  padding: 8px 10px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r);
  background: var(--bg);
}
.pairing-saved li code { overflow-wrap: anywhere; font-size: 0.85rem; }
/* A card-level footnote. Sitting flush under the "Custom address" disclosure
   row, it read as that section's body rather than as advice about the address
   picker above, so it gets a rule and real space of its own. */
.pairing-note {
  color: var(--faint);
  font-size: 0.8rem;
  line-height: 1.45;
  margin: 22px 0 0;
  padding-top: 16px;
  border-top: 1px solid var(--line-soft);
  max-width: 52rem;
}

/* Test dictation controls (aligned field labels like pairing). */
.test-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px 14px;
  margin-top: 14px;
}
.test-field {
  display: grid;
  gap: 5px;
  min-width: 0;
}
.test-field > span {
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
}
.test-field select {
  min-width: 11rem;
}
.test-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
  padding-bottom: 1px;
}
.test-status { min-height: 1.4em; margin-top: 10px; }

/* ----------------------------------------------------------- onboarding CTAs */

.onboarding-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 16px;
}
.onboarding-steps { max-width: none; }
button.text-link {
  display: inline;
  padding: 0;
  border: none;
  border-radius: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 0.15em;
  cursor: pointer;
}
button.text-link:hover { color: var(--accent-hover); }

/* ----------------------------------------------------------------- model list */

/* Family tile + models panel are grid *siblings*. Opening a family only
   unhides the models sibling with grid-column: 1 / -1, so the tile stays in
   its cell and models fill the full next row. */
.family-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
  margin-bottom: 26px;
  align-items: start;
}
/* Every model action (download/select/cancel/delete) re-renders #models-list
   from scratch, then JS re-opens whichever family panels were open before the
   swap (see restoreOpenFamilies in app.js). That collapse-then-reopen dance
   happens within one tick, but Chrome's scroll-anchoring heuristic still
   picks an anchor node inside the subtree being torn down and rebuilt, and
   overcompensates once the panel reappears — the page jumps hundreds of
   pixels for no visible reason. Anchoring is opt-out per element; since we
   already manage this panel's open/closed state ourselves, let it opt out. */
#models-list,
.family-models {
  overflow-anchor: none;
}
.family-tile {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  min-width: 0;
  align-self: start;
}
.family-tile[data-active="1"] {
  background: var(--accent-wash);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--line));
}
/* Open = accent border + chevron; wash stays reserved for Active only. */
.family-tile.is-open {
  border-color: var(--accent);
  box-shadow: var(--shadow-soft);
  z-index: 1;
}
.family-tile[data-active="1"].is-open {
  border-color: var(--accent);
}
/* Card body is a full-bleed button so the whole tile is the hit target. */
button.family-summary {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  margin: 0;
  padding: 14px 14px 14px 16px;
  border: none;
  border-radius: inherit;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  min-width: 0;
  box-sizing: border-box;
}
button.family-summary:hover {
  background: var(--raised);
}
.family-tile.is-open > button.family-summary:hover {
  background: transparent;
}
button.family-summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.family-summary-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  flex: 1 1 auto;
}
.family-summary-main {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px 10px;
  flex-wrap: nowrap;
  min-width: 0;
}
.family-name {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.family-tags {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
}

/* Vendor badge — whose model this family is.

   One shape for every vendor, whether it holds a glyph or a monogram: the
   marks come from different sources and would otherwise sit at different
   weights and sizes, and a row of badges that do not line up reads as broken
   rather than as brand-accurate. The colour is the vendor's, at a tint that
   stays legible on both themes rather than at full brand saturation, which is
   too loud beside the accent green and unreadable in light mode. */
.vendor-badge {
  --vendor-mark: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  margin-top: 1px;
  border: 1px solid color-mix(in srgb, var(--vendor-mark) 35%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--vendor-mark) 12%, transparent);
  color: var(--vendor-mark);
}
.vendor-glyph {
  display: block;
}
/* Monograms carry the vendors with no redistributable mark, so they have to
   hold their own beside a real glyph: tight tracking, and a step down in size
   for the three-letter ones rather than overflowing the badge. */
.vendor-monogram {
  font-family: var(--font-display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
}
.vendor-ibm .vendor-monogram,
.vendor-openai .vendor-monogram {
  font-size: 9px;
}
/* Brand colours, following the hardware panel's .sys-vendor-* convention: the
   published colour on light, a lifted one on dark. NVIDIA green and Sber green
   are the reason — both go muddy against the dark surface. */
.vendor-nvidia { --vendor-mark: #76b900; }
.vendor-openai { --vendor-mark: #0f9d76; }
.vendor-alibaba { --vendor-mark: #ff6a00; }
.vendor-qwen { --vendor-mark: #6950ef; }
.vendor-mediatek { --vendor-mark: #d97f14; }
.vendor-ibm { --vendor-mark: #0f62fe; }
.vendor-cohere { --vendor-mark: #9333ea; }
.vendor-useful-sensors { --vendor-mark: #64748b; }
.vendor-dataocean { --vendor-mark: #0891b2; }
.vendor-salute { --vendor-mark: #21a038; }
.vendor-k2fsa { --vendor-mark: #7c3aed; }
html[data-theme="dark"] .vendor-nvidia { --vendor-mark: #a3e635; }
html[data-theme="dark"] .vendor-openai { --vendor-mark: #34d399; }
html[data-theme="dark"] .vendor-alibaba { --vendor-mark: #fb923c; }
html[data-theme="dark"] .vendor-qwen { --vendor-mark: #a78bfa; }
html[data-theme="dark"] .vendor-mediatek { --vendor-mark: #fbbf24; }
html[data-theme="dark"] .vendor-ibm { --vendor-mark: #60a5fa; }
html[data-theme="dark"] .vendor-cohere { --vendor-mark: #d18ee2; }
html[data-theme="dark"] .vendor-useful-sensors { --vendor-mark: #94a3b8; }
html[data-theme="dark"] .vendor-dataocean { --vendor-mark: #38bdf8; }
html[data-theme="dark"] .vendor-salute { --vendor-mark: #4ade80; }
html[data-theme="dark"] .vendor-k2fsa { --vendor-mark: #a78bfa; }
.family-vendor {
  color: var(--muted);
}
.family-meta {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 6px;
  color: var(--faint);
  font-size: 0.85rem;
  min-width: 0;
  overflow: hidden;
}
/* Flex children shrink below their content width by default, and plain text
   wraps when it runs out of room — "20 models" was breaking into "20" over
   "models" on narrower tiles. Only .family-engines should ever give up space
   (it already ellipsizes); the count and installed-label stay one line. */
.family-meta > span {
  flex: 0 0 auto;
  white-space: nowrap;
}
.family-engines {
  flex: 1 1 auto;
  overflow: hidden;
  max-width: 100%;
  min-width: 0;
  color: var(--muted);
  text-overflow: ellipsis;
  white-space: nowrap;
}
.family-engines::before {
  content: "· ";
  color: var(--faint);
}
/* Chevron: points down when closed, up when open. */
.family-expand-icon {
  flex: 0 0 auto;
  display: block;
  margin-top: 2px;
  color: var(--faint);
  transition: transform 0.15s ease, color 0.12s ease;
}
.family-tile.is-open .family-expand-icon {
  transform: rotate(180deg);
  color: var(--accent-hover);
}
html[data-theme="dark"] .family-tile.is-open .family-expand-icon {
  color: var(--accent);
}

/* Sibling of .family-tile in .family-grid — full row when open. */
.family-models {
  display: none;
  grid-column: 1 / -1;
  flex-direction: column;
  gap: 12px;
  margin: 0;
  padding: 12px 16px 16px;
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line));
  border-radius: var(--r-lg);
  background: var(--sunken);
  box-shadow: var(--shadow-soft);
  min-width: 0;
}
.family-models.is-open,
.family-models:not([hidden]) {
  display: flex;
}
.family-models[hidden] {
  display: none !important;
}
.family-models-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  padding-bottom: 2px;
  border-bottom: 1px solid var(--line-soft);
}
.family-models-label {
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--accent-hover);
}
html[data-theme="dark"] .family-models-label {
  color: var(--accent);
}
.family-models-count {
  font-size: 0.82rem;
}
.family-models-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
  gap: 12px;
  align-items: start;
  min-width: 0;
}
.family-models .model-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
  padding: 14px 15px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.family-models .model-card:hover {
  border-color: var(--line-strong);
}
/* Installed = quiet surface; only the *active* model gets teal wash. */
.family-models .model-card[data-state="installed"] {
  border-color: color-mix(in srgb, var(--accent) 18%, var(--line));
}
.family-models .model-card[data-active="1"] {
  background: var(--accent-wash);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent);
}
.family-models .model-main {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.family-models .model-title h4 {
  font-size: 0.98rem;
  line-height: 1.3;
}
.family-models /* Card chrome: title → toolbar (info left, download right). */
.model-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
  padding: 14px 15px;
  overflow: visible;
}
.model-main { min-width: 0; flex: 1 1 auto; }
.model-title { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.model-title h4 { letter-spacing: -0.01em; font-size: 0.95rem; margin: 0; }
.model-tags { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
/* Three comparable specs, not one wrapping sentence. Size leads because it is
   what the operator is actually deciding about. */
.model-specs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0 0;
}
.model-spec {
  padding: 2px 8px;
  border-radius: var(--r-pill);
  background: var(--sunken);
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.4;
}
.model-spec-size { color: var(--text); }
.model-blurb {
  margin: 8px 0 0;
  color: var(--muted);
  font-size: 0.84rem;
  font-weight: 400;
  line-height: 1.45;
  /* Still clamped so a grid row stays scannable; "Details" carries the rest. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  overflow-wrap: anywhere;
}
.model-details-btn { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.model-details-btn svg { width: 15px; height: 15px; }
/* A model whose engine has no runtime on this host. Warn-coloured rather than
   error-coloured: the weights are fine, only the CLI or package is absent. */
.model-toolbar {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-width: 0;
  margin-top: auto;
}
.model-toolbar-start {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  min-width: 0;
  flex: 1 1 auto;
}
.model-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  min-width: 0;
  margin-left: auto;
  flex: 0 0 auto;
}
.model-actions .progress { flex: 1 1 100%; }
.model-actions .progress-copy { flex: 1 1 auto; }
.model-action-error { flex: 1 1 100%; margin: 0; }

/* Download progress spans the card so bar + Cancel don't fight the toolbar. */
.model-card[data-state="downloading"] .model-toolbar {
  flex-wrap: wrap;
  align-items: flex-start;
}
.model-card[data-state="downloading"] .model-actions {
  flex: 1 1 100%;
  width: 100%;
  margin-left: 0;
  justify-content: stretch;
}
.model-download-status {
  display: grid;
  gap: 8px;
  width: 100%;
  min-width: 0;
}
.model-download-status .progress { width: 100%; margin: 0; }
.model-download-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-width: 0;
}
.model-download-meta .progress-copy {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* Language chips: a plain wrapping list in the detail dialog. They used to sit
   in a 7.5rem scroll box inside a hover popover, so a 25-language model showed
   about six of them behind a nested scrollbar. */
.model-language-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 10px 0 0;
  padding: 0;
  min-width: 0;
}
.model-language-chip {
  display: inline-block;
  padding: 2px 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1.3;
  white-space: nowrap;
}
.family-models {
  overflow: visible;
}
.family-models .model-card {
  overflow: visible;
}

.models-toolbar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  position: relative;
  flex-wrap: wrap;
  justify-content: flex-end;
}
/* Icon-only toolbar controls (expand / refresh); label is title + aria-label. */
button.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  min-width: 38px;
  min-height: 38px;
  padding: 0;
  border-radius: var(--r-pill);
}
button.icon-btn .toolbar-icon {
  display: block;
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
}
/* Must beat button.icon-btn .toolbar-icon (0,2,1) so only one chevron shows. */
button.families-expand-toggle .icon-collapse { display: none; }
button.families-expand-toggle[data-expanded="true"] .icon-expand { display: none; }
button.families-expand-toggle[data-expanded="true"] .icon-collapse { display: block; }
/* Models page: filter rail on the left, list + BYO on the right. */
.models-layout {
  display: grid;
  grid-template-columns: minmax(220px, 260px) minmax(0, 1fr);
  gap: 18px 20px;
  align-items: start;
  margin-bottom: 8px;
}
.models-layout.is-filter-collapsed {
  grid-template-columns: minmax(0, 1fr);
}
.models-main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
}
.models-layout #models-list { margin-bottom: 26px; }

.models-filter {
  position: sticky;
  top: 12px;
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: calc(100vh - 24px);
  overflow-y: auto;
  overscroll-behavior: contain;
  min-width: 0;
  padding: 12px 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: none;
}
.models-layout.is-filter-collapsed .models-filter {
  display: none;
}
.models-filter.has-active {
  border-color: color-mix(in srgb, var(--accent) 35%, var(--line));
}
.filter-side-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex: 0 0 auto;
}
.filter-side-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}
button.filter-side-collapse {
  width: 32px;
  height: 32px;
  min-width: 32px;
  min-height: 32px;
}
button.filter-trigger {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  padding: 7px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  user-select: none;
}
button.filter-trigger:hover {
  border-color: var(--accent);
  background: var(--accent-wash);
}
button.filter-trigger[aria-expanded="true"],
button.filter-trigger.has-active {
  border-color: var(--accent);
  color: var(--accent-hover);
  background: var(--accent-wash);
}
html[data-theme="dark"] button.filter-trigger[aria-expanded="true"],
html[data-theme="dark"] button.filter-trigger.has-active {
  color: var(--accent);
}
.filter-icon { flex: 0 0 auto; display: block; }
.filter-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  padding: 0 5px;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1;
}
.filter-panel {
  display: grid;
  gap: 12px;
  flex: 1 1 auto;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
  margin: 0;
  padding: 0 2px 2px 0;
  border: none;
  background: transparent;
  box-shadow: none;
  width: auto;
  max-height: none;
  position: static;
}
.filter-field {
  display: grid;
  gap: 6px;
  margin: 0;
  padding: 0;
  border: none;
  color: var(--muted);
  font-size: 0.82rem;
  min-width: 0;
}
.filter-field > span,
.filter-field > legend {
  padding: 0;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
}
.filter-field select {
  width: 100%;
  color: var(--text);
}
.filter-field select:not(:has(option[value=""]:checked)) {
  border-color: var(--accent);
  color: var(--text);
}
.filter-search {
  width: 100%;
  margin: 0;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-size: 0.85rem;
}
.filter-search:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
/* One scroll region, not four. The rail used to be a fixed-height panel that
   did not scroll, so Family, Language and Engine each got their own 8.5rem
   well — Family showed two and a half checkboxes above its own scrollbar. The
   rail scrolls now and the lists run at their natural length. */
.filter-check-list {
  display: grid;
  gap: 2px;
  padding: 0;
  border: 0;
  background: none;
}
/* The one list that cannot run inline: ~100 languages. The search box above it
   is the real filter; this cap just stops it swallowing the rail. */
.filter-check-list-tall {
  max-height: 17rem;
  overflow-x: hidden;
  overflow-y: auto;
  padding: 2px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r);
  background: var(--bg);
}
.filter-check {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 5px 8px;
  border-radius: 6px;
  color: var(--text);
  font-size: 0.86rem;
  cursor: pointer;
  user-select: none;
}
/* Author `display: flex` would otherwise beat the UA `[hidden]` rule. */
.filter-check[hidden] { display: none !important; }
.filter-check:hover { background: var(--raised); }
.filter-check:has(input:checked) {
  background: var(--accent-wash);
  color: var(--accent-hover);
  font-weight: 600;
}
html[data-theme="dark"] .filter-check:has(input:checked) { color: var(--accent); }
.filter-check input {
  flex: 0 0 auto;
  width: 0.95rem;
  height: 0.95rem;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}
.filter-check span {
  min-width: 0;
  overflow-wrap: anywhere;
}
.filter-clear { width: 100%; justify-self: stretch; }
.max-size-filter { max-width: none; }

@media (max-width: 900px) {
  .models-layout {
    grid-template-columns: 1fr;
  }
  .models-filter {
    position: static;
    max-height: none;
  }
  .models-layout.is-filter-collapsed .models-filter {
    display: none;
  }
}

.model-custom-links {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.55rem;
  margin: 12px 0 0;
}
.model-custom-links a { font-weight: 600; }
.empty-state { color: var(--muted); padding: 26px 0; }
.models-language-hint { margin-bottom: 18px; }
.models-language-hint em { font-style: normal; color: var(--text); }

/* --------------------------------------------------------------------- tables */

.table { width: 100%; border-collapse: collapse; }
.table-scroll { width: 100%; overflow-x: auto; }
.table th {
  text-align: left;
  color: var(--muted);
  font-weight: 500;
  font-size: 12px;
  padding: 0 10px 7px;
  border-bottom: 1px solid var(--line);
}
.table td {
  padding: 9px 10px;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
}
.table tr:last-child td { border-bottom: none; }
.table th:first-child, .table td:first-child { padding-left: 0; }
.table th:last-child, .table td:last-child { padding-right: 0; }
.table .align-right { text-align: right; }
.table td code { background: none; border: none; padding: 0; color: var(--muted); }
.table td button + button { margin-left: 6px; }
.state { display: inline-flex; align-items: center; gap: 7px; color: var(--muted); }

.badge {
  display: inline-block;
  font-size: 0.78rem;
  line-height: 1.5;
  padding: 1px 8px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  color: var(--muted);
  background: var(--surface);
}
.badge.ok, .badge.recommended {
  color: var(--accent-hover);
  border-color: var(--accent);
  background: var(--accent-wash);
}
/* Active is not a badge: check + solid label so it can't be confused with
   "recommended". Card/family also get a teal wash via [data-active="1"]. */
.model-active-label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.4;
  padding: 2px 9px 2px 7px;
  border-radius: var(--r-pill);
  border: none;
  color: var(--accent-ink);
  background: var(--accent);
  letter-spacing: 0.01em;
}
.model-active-label svg {
  display: block;
  flex: 0 0 auto;
}
.badge.missing {
  color: var(--error);
  border-color: #f0b4ae;
  background: var(--error-wash);
}
.badge.retired {
  color: var(--error);
  border-color: #f0b4ae;
  background: var(--error-wash);
}
.badge.offloaded {
  color: var(--muted);
  border-color: var(--line-strong);
}

/* ------------------------------------------------------------------- controls */

button {
  font-family: var(--font-body);
  font-size: 0.92rem;
  cursor: pointer;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
}
button.primary, a.primary {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
  padding: 9px 18px;
  border: 1px solid transparent;
}
button.primary:hover, a.primary:hover { background: var(--accent-hover); color: var(--accent-ink); }
button.ghost, a.ghost {
  background: transparent;
  border: 1px solid var(--line-strong);
  color: var(--text);
  padding: 8px 16px;
}
button.ghost:hover, a.ghost:hover {
  color: var(--accent);
  border-color: var(--accent);
}
a.primary, a.ghost {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  border-radius: var(--r-pill);
  font-family: var(--font-body);
  font-size: 0.92rem;
  cursor: pointer;
}
button.small { padding: 6px 12px; font-size: 0.88rem; }
button.danger:hover { color: var(--error); border-color: var(--error); }
button.recording {
  background: var(--error);
  color: #fff;
  border-radius: var(--r-pill);
}
button:disabled { opacity: 0.45; cursor: default; }
button:focus-visible, input:focus-visible, select:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

input, select {
  font-family: var(--font-body);
  font-size: 0.95rem;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  color: var(--text);
  border-radius: var(--r);
  padding: 9px 12px;
}
input::placeholder { color: var(--faint); }
input:focus, select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}
select {
  appearance: none;
  padding-right: 32px;
  background-color: var(--surface);
  background-image: var(--select-chevron);
  background-repeat: no-repeat;
  background-position: right 12px center;
  cursor: pointer;
}

.settings-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
  margin: 0 0 10px;
  max-width: 640px;
}
.settings-field {
  display: grid;
  /* Not stretch: a field with a hint under it is taller than its neighbour, and
     stretching would grow that neighbour's control instead of leaving a gap. */
  align-content: start;
  gap: 5px;
  min-width: 0;
}
.settings-field small { color: var(--muted); font-size: 0.82rem; line-height: 1.45; }
.settings-field > span:first-child {
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
}
.settings-grid input,
.settings-grid select,
.settings-field input,
.settings-field select {
  width: 100%;
  color: var(--text);
}
.settings-hint { margin: 0 0 4px; max-width: 40rem; }
.memory-policy {
  display: flex;
  align-items: end;
  flex-wrap: wrap;
  gap: 14px 24px;
  margin: 18px 0 10px;
  padding-top: 18px;
  max-width: 640px;
  border-top: 1px solid var(--line-soft);
}
.memory-policy-toggle {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  flex: 1 1 360px;
  cursor: pointer;
}
.memory-policy-toggle input {
  width: 18px;
  height: 18px;
  margin: 2px 0 0;
  accent-color: var(--accent);
}
.memory-policy-toggle span { display: grid; gap: 3px; }
.memory-policy-toggle strong { font-size: 0.94rem; }
.memory-policy-toggle small { color: var(--muted); line-height: 1.45; }
.memory-policy-timeout { flex: 0 1 180px; }
.settings-engine-result { margin-top: 12px; }
/* Empty until a save responds, so it must not reserve a blank line. */
.settings-engine-result:empty { display: none; }

.settings-actions { margin-top: 14px; }
.settings-facts { margin-top: 12px; }
.settings-facts code {
  font-size: 0.88em;
  overflow-wrap: anywhere;
}
.tokens-create {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--line-soft);
  /* No max-width here: it capped the section rule at 560px under a table that
     runs the full card, so the divider stopped mid-row. The input inside is
     what should be narrow, and `.tokens-create .row` already caps it. */
}
.tokens-empty { padding: 12px 0; }
/* Not `display: flex`: that takes a <td> out of table layout entirely, so the
   Actions cell stopped lining up under its own header and the row divider ran
   only as far as the first column. `.align-right` plus the existing
   `button + button` margin does the same job inside a real cell. */
.token-actions { white-space: nowrap; }
button.ghost.danger,
button.danger.ghost {
  color: var(--error);
}
button.ghost.danger:hover,
button.danger.ghost:hover {
  border-color: var(--error);
  background: var(--error-wash);
  color: var(--error);
}
.row { display: flex; gap: 8px; align-items: center; margin: 12px 0 0; }
/* A name or a URL does not need a thousand pixels of input. */
.card > form .row,
.tokens-create .row { max-width: 560px; }
.row input { flex: 1; min-width: 0; }
.row:first-child { margin-top: 0; }

.progress {
  width: 100%;
  height: 5px;
  background: var(--raised);
  border-radius: var(--r-pill);
  overflow: hidden;
  margin: 3px 0;
}
.progress .bar {
  height: 100%;
  background: var(--accent);
  /* Match ~poll interval so each sample eases into the next instead of jumping. */
  transition: width 0.4s linear;
}

/* ---------------------------------------------------------------- test result */

.result { margin-top: 18px; border-top: 1px solid var(--line-soft); padding-top: 16px; }
.result-header { display: flex; align-items: center; justify-content: space-between; }
#test-transcript { font-size: 16px; margin: 10px 0 4px; }
.benchmark-grid {
  display: grid;
  /* Auto-fit rather than a fixed six: the cleanup tile is present only where a
     cleanup model is, so the row has to hold six or seven without orphaning. */
  grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
  gap: 1px;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  overflow: hidden;
  margin-top: 16px;
}
.benchmark-grid div { display: grid; gap: 2px; padding: 11px 13px; background: var(--surface); }
.benchmark-grid span { color: var(--muted); font-size: 12px; }
.benchmark-grid strong {
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
/* Transcript cleanup: the settings card's model list and the mic-test
   before-and-after comparison. */
.settings-subheading {
  margin: 22px 0 6px;
  padding-top: 18px;
  border-top: 1px solid var(--line-soft);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.cleanup-models { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 10px; }
.cleanup-model {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  padding: 14px 16px;
  display: grid;
  gap: 8px;
  min-width: 0;
}
.cleanup-model-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 4px 12px;
}
.cleanup-model p { margin: 0; }
.cleanup-model .settings-actions { margin-top: 4px; }
/* ------------------------------------------------------- transcript cleanup */

.cleanup-status-card { display: grid; gap: 14px; }
.cleanup-status-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.cleanup-status-line { margin: 0; flex: 1 1 240px; font-size: 0.95rem; }
.cleanup-status-actions { margin-left: auto; }

/* The four preconditions, together. Each one fails the same way — the
   transcript comes back unchanged — so seeing them side by side is what turns
   "it does nothing" into a specific missing step. */
.cleanup-steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 1px;
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.cleanup-step {
  display: flex;
  gap: 9px;
  padding: 12px 14px;
  background: var(--surface);
  align-items: start;
}
.cleanup-step strong { display: block; font-size: 0.86rem; font-weight: 600; }
.cleanup-step small { display: block; margin-top: 2px; color: var(--muted); line-height: 1.45; }
.cleanup-step-mark {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 11px;
  line-height: 1;
}
.cleanup-step.done .cleanup-step-mark { background: var(--accent); color: var(--accent-ink); }
.cleanup-step.todo .cleanup-step-mark { background: var(--warn-wash); color: var(--warn); }
.cleanup-step.todo strong { color: var(--warn); }

.cleanup-try-controls { display: flex; align-items: end; gap: 14px; flex-wrap: wrap; margin: 12px 0 10px; }
.cleanup-try-language { max-width: 180px; }
.cleanup-try-input {
  width: 100%;
  resize: vertical;
  font-family: inherit;
  font-size: 15px;
  line-height: 1.55;
  padding: 11px 13px;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.cleanup-try-result { margin-top: 18px; border-top: 1px solid var(--line-soft); padding-top: 14px; }
.cleanup-try-text { margin: 0 0 4px; font-size: 15px; line-height: 1.6; overflow-wrap: anywhere; }

/* A cleanup card carries provenance a speech card has no field for: which
   weights, quantized by whom, and which languages were actually measured. */
.cleanup-model-list { display: grid; gap: 10px; margin-top: 14px; }
.cleanup-facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 14px;
  margin: 8px 0 0;
  font-size: 12.5px;
}
.cleanup-facts dt { color: var(--muted); }
.cleanup-facts dd { margin: 0; overflow-wrap: anywhere; }

.diff-legend { display: inline-flex; gap: 8px; margin-left: 10px; font-weight: 400; font-size: 11px; }
.diff-legend span { opacity: 0.9; }

/* What cleanup changed. Both marks lean on the existing theme tokens, so they
   stay legible in light and dark without a second palette to keep in step. */
.diff-add {
  background: var(--accent-wash);
  border-bottom: 2px solid var(--accent-line);
  border-radius: 3px;
  padding: 0 1px;
}
.diff-remove {
  background: var(--warn-wash);
  border-radius: 3px;
  padding: 0 1px;
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  opacity: 0.85;
}
/* A colour-blind reader gets the underline and the strike-through; the wash is
   the redundant cue, not the only one. */
@media (prefers-contrast: more) {
  .diff-add { outline: 1px solid var(--accent-line); }
  .diff-remove { outline: 1px solid var(--warn); }
}

.result-subheading {
  margin: 14px 0 4px;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--muted);
}
#test-original { margin: 0; overflow-wrap: anywhere; }

.record-timer {
  min-width: 46px;
  color: var(--error);
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

/* --------------------------------------------------------------- modal, toast */

.overlay {
  position: fixed;
  inset: 0;
  background: var(--overlay-scrim);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 24px;
}
.modal {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 28px;
  width: min(420px, 100%);
  box-shadow: var(--shadow-modal);
}
/* Not an input: the field below it is. */
.modal code.block { background: var(--raised); border-color: var(--line); }
.modal h2 {
  margin-bottom: 6px;
  font-size: 1.2rem;
}
.modal .muted { margin-bottom: 0; }
.modal input { width: 100%; margin: 16px 0 12px; }
.modal button { width: 100%; }

.toast {
  position: fixed;
  bottom: 22px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--r);
  padding: 12px 18px;
  max-width: 90vw;
  box-shadow: var(--shadow-soft);
  z-index: 200;
  font-size: 0.95rem;
}
.toast.error { border-left-color: var(--error); }

.htmx-request .actions button, .actions button.htmx-request { opacity: 0.45; pointer-events: none; }

/* About reuses Overview / Settings chrome. Official tower mark is 52px in the
   system-card hero. Family and talk-to-us icons stay 16px currentColor. */
#about-host .sys-hero {
  display: flex;
  align-items: center;
  gap: 16px;
  background: var(--surface);
}
.about-host-mark {
  display: block;
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  border-radius: var(--r);
}
.about-info {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 16px 0 20px;
  padding: 14px 16px;
  border: 1px solid color-mix(in srgb, var(--accent) 28%, var(--line));
  border-radius: var(--r-lg);
  background: var(--accent-wash);
}
.about-info-mark {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin-top: 2px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-display);
  font-size: 0.78rem;
  font-weight: 700;
  font-style: italic;
  line-height: 1;
}
.about-info strong {
  display: block;
  color: var(--text);
  margin-bottom: 2px;
}
.about-info div span {
  display: block;
  color: var(--muted);
}
.about-icon {
  display: flex;
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  color: inherit;
}
.about-icon svg,
.about-icon img {
  display: block;
  width: 16px;
  height: 16px;
}
#about-this-build .facts {
  align-items: baseline;
  column-gap: 12px;
  row-gap: 6px;
}
#about-this-build .facts dt,
#about-this-build .facts dd {
  font-size: 0.92rem;
  line-height: 1.45;
}
#about-this-build .facts dd a {
  text-underline-offset: 0.18em;
}

/* ---------------------------------------------------------------- responsive */

@media (max-width: 1000px) {
  .operations-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ops-charts-grid,
  .ops-charts-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .lib-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .benchmark-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 760px) {
  :root { --gutter: 16px; }
  main { padding: 20px 16px 40px; }
  .site-footer {
    padding-block: 16px 20px;
  }
  .brand .muted { display: none; }
  .tabs { overflow-x: auto; }
  .tab { padding-inline: 10px; white-space: nowrap; }
  .grid.two { grid-template-columns: 1fr; gap: 0; }
  .status-hero { flex-direction: column; gap: 16px; }
  /* Stay right-aligned with the pill and inside the viewport: anchoring left
     pushed the card past the right edge and gave the page a horizontal scroll. */
  .engine-popover { right: 0; left: auto; min-width: 0; width: max-content; max-width: calc(100vw - 24px); }
  .page-head { flex-direction: column; align-items: stretch; gap: 12px; }
  .models-toolbar-actions { flex-wrap: wrap; }
  .operations-grid, .benchmark-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ops-charts-grid,
  .ops-charts-grid-3 { grid-template-columns: 1fr; }
  .lib-grid { grid-template-columns: 1fr; }
  .section-heading { flex-direction: column; align-items: flex-start; gap: 3px; }
  .family-grid { grid-template-columns: 1fr; }
  .family-models.is-open,
  .family-models:not([hidden]) {
    grid-template-columns: 1fr;
    padding: 12px;
    gap: 10px;
  }
  .pairing-layout { gap: 16px; }
  .pairing-advanced-grid { grid-template-columns: 1fr; }
  .test-controls { flex-direction: column; align-items: stretch; }
  .test-field select { width: 100%; min-width: 0; }
  .test-actions { width: 100%; }
  .test-actions .primary { flex: 1; }
  .row { flex-direction: column; align-items: stretch; }
  .row button, .row select, .row input { width: 100%; }
  .settings-grid { grid-template-columns: 1fr; }
  .table .align-right { text-align: left; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; transition: none !important; }
}

/* Workspace shell: persistent navigation and a quieter, task-first hierarchy. */
html[data-theme="dark"] {
  --bg: #212121;
  --surface: #262626;
  --raised: #303030;
  --sunken: #1b1b1b;
  --line: #3a3a3a;
  --line-soft: #333333;
  --text: #eeeeee;
  --muted: #b0b0b0;
  /* #999999 came to 4.40 over the active tile's accent wash, just under AA. */
  --faint: #a8a8a8;
}
/* Shell grid. The banner and header span both columns, so the rail always
   begins below the header no matter how tall the (dismissible, wrapping)
   exposure banner is — a fixed `top` offset would tuck it under the header. */
body {
  font-size: 15px;
  line-height: 1.6;
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
    "banner banner"
    "header header"
    "nav    main"
    "nav    footer";
  /* Paints the rail and its divider full height. `.tabs` itself is content-sized
     so that it can stick; a stretched grid item never sticks. */
  background-image: linear-gradient(to right,
    var(--sunken) 0 calc(var(--rail-w) - 1px),
    var(--line-soft) calc(var(--rail-w) - 1px) var(--rail-w),
    var(--bg) var(--rail-w));
}
:root { --rail-w: 220px; --header-h: 84px; }
#exposure-banner { grid-area: banner; }
.app-header {
  grid-area: header;
  position: sticky;
  top: 0;
  height: var(--header-h);
  padding: 16px 28px;
  background: var(--bg);
  border-bottom: 1px solid var(--line-soft);
}
.brand h1 { font-size: 18px; letter-spacing: -.5px; }
.brand .muted { font-size: 11px; }
.tabs {
  grid-area: nav;
  position: sticky;
  top: var(--header-h);
  align-self: start;
  max-height: calc(100vh - var(--header-h));
  overflow-y: auto;
  padding: 28px 14px;
  flex-direction: column;
  gap: 6px;
  background: none;
  border-bottom: 0;
  border-right: 0;
  backdrop-filter: none;
}
.tab {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 46px;
  padding: 12px 16px;
  border-radius: 8px;
  text-align: left;
  font-size: 14px;
  box-shadow: none;
}
.tab.active { background: var(--raised); box-shadow: none; }
.tab:hover { background: var(--raised); }
.nav-icon { width: 19px; height: 19px; flex-shrink: 0; }
main { grid-area: main; margin: 0; width: auto; max-width: none; padding: 40px clamp(24px, 4vw, 64px) 60px; }
#panel { max-width: 1240px; margin: 0 auto; outline: none; }
/* Form- and prose-heavy pages read badly at the full 1240px: a 640px settings
   form inside a 1090px card left a third of every card empty. Models and
   Overview keep the width — their grids and metric rows use it. */
#panel:has(> .page-head[data-page="settings"]),
#panel:has(> .page-head[data-page="pair"]),
#panel:has(> .page-head[data-page="about"]) { max-width: 940px; }
.site-footer { grid-area: footer; margin-left: 0; padding: 18px; }
.page-head { margin-bottom: 30px; align-items: center; }
.page-head h2 { font-size: clamp(26px, 3vw, 34px); letter-spacing: -1px; line-height: 1.25; margin: 4px 0 10px; }
.page-head p { max-width: 660px; }
p.eyebrow { font-size: 10px; font-weight: 700; letter-spacing: 1.8px; color: var(--muted); margin: 0 0 8px; }
h2, h3 { letter-spacing: -.4px; }
.card { padding: 24px; border: 1px solid var(--line-soft); border-radius: 12px; background: var(--surface); margin-bottom: 20px; }
#panel > .card:first-child { border: 1px solid var(--line-soft); padding: 24px; }
.status-hero { padding: 20px 24px; border: 1px solid var(--accent-line); border-radius: 12px; background: var(--accent-wash); margin-bottom: 20px; }
.status-hero h3 { font-size: 19px; }
.onboarding h3 { font-size: 22px; }
.onboarding-steps { display: grid; gap: 12px; padding-left: 22px; margin: 20px 0 24px; }
.onboarding-steps li { padding-left: 8px; }
.onboarding-steps li::marker { color: var(--accent); font-weight: 700; }
.onboarding-actions { flex-wrap: wrap; }
.hero-actions { display: flex; gap: 10px; flex-shrink: 0; }
.operations { margin: 32px 0; }
.section-heading { flex-wrap: wrap; }
.section-heading h3 { font-size: 17px; }
.metric-card { background: var(--surface); border-radius: 10px; }
.metric-value { letter-spacing: -1px; }
/* Collapsed sections read as controls, not headings. A bare bold label with a
   small triangle sat next to real headings ("Pair phone") and people missed it,
   so every summary is a filled, full-width row with a boxed chevron and an
   explicit Show / Hide word. Every collapsible in the admin UI uses this,
   `.sys-details` included — a nested one styled more faintly was the easiest
   of the lot to walk straight past. */
.disclosure > summary,
.activity-details > summary,
.sys-details > summary,
.pairing-advanced > summary {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--raised);
  color: var(--text);
  cursor: pointer;
  font-weight: 600;
  user-select: none;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.disclosure > summary::-webkit-details-marker,
.activity-details > summary::-webkit-details-marker,
.sys-details > summary::-webkit-details-marker,
.pairing-advanced > summary::-webkit-details-marker { display: none; }
.disclosure > summary::marker,
.activity-details > summary::marker,
.sys-details > summary::marker,
.pairing-advanced > summary::marker { content: ""; }
/* Boxed chevron: a lone glyph was too faint to register as a control. */
.disclosure > summary::before,
.activity-details > summary::before,
.sys-details > summary::before,
.pairing-advanced > summary::before {
  content: "\203A";
  order: -1;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  border-radius: 6px;
  background: var(--surface);
  color: var(--muted);
  font-size: 0.95rem;
  line-height: 1;
  transition: transform 0.15s ease;
}
.disclosure[open] > summary::before,
.activity-details[open] > summary::before,
.sys-details[open] > summary::before,
.pairing-advanced[open] > summary::before { transform: rotate(90deg); }
/* The word carries the affordance for anyone who reads past the icon. */
.disclosure > summary::after,
.activity-details > summary::after,
.sys-details > summary::after,
.pairing-advanced > summary::after {
  content: "Show";
  order: 2;
  margin-left: auto;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 500;
}
.disclosure[open] > summary::after,
.activity-details[open] > summary::after,
.sys-details[open] > summary::after,
.pairing-advanced[open] > summary::after { content: "Hide"; }
.disclosure > summary:hover,
.activity-details > summary:hover,
.sys-details > summary:hover,
.pairing-advanced > summary:hover { background: var(--surface); border-color: var(--accent-line); }
.disclosure > summary:hover::after,
.activity-details > summary:hover::after,
.sys-details > summary:hover::after,
.pairing-advanced > summary:hover::after { color: var(--accent); }
/* Subtitle drops to its own line, past the chevron. */
.disclosure > summary .muted { order: 3; flex-basis: 100%; margin: 2px 0 0 32px; font-weight: 400; }
.disclosure[open] > summary,
.activity-details[open] > summary,
.sys-details[open] > summary,
.pairing-advanced[open] > summary { margin-bottom: 20px; }
/* A details carrying `.card` drops the card chrome: the summary row already
   is the surface, and a filled row inside a bordered card gave the same control
   two different weights depending on where it happened to sit. */
.card.disclosure { padding: 0; background: none; border: 0; }
.disclosure .card { background: none; border: 0; border-top: 1px solid var(--line-soft); border-radius: 0; padding: 24px 0 0; }
.disclosure .card:last-child { margin-bottom: 0; }
.activity-details { margin-top: 22px; color: var(--muted); }
.models-toolbar-actions { flex-shrink: 0; }
/* Auto-fill rather than a hard 2 columns: 19 families in two very wide tiles
   wastes most of the row on a large display. */
.family-grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr)); gap: 12px; }
.family-tile { background: var(--surface); border-radius: 10px; }
.family-name { font-size: 16px; }
.family-summary { min-height: 88px; }
/* Narrower tiles (3 up) clipped the engine list mid-word; let it wrap instead. */
.family-meta { flex-wrap: wrap; overflow: visible; row-gap: 2px; }
.models-filter { background: var(--surface); border-radius: 12px; }
.flow-step { display: flex; gap: 14px; align-items: flex-start; margin: 28px 0 18px; }
.flow-step h3, .flow-step p { margin: 0; }
.step-number { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%; background: var(--raised); font-weight: 700; flex-shrink: 0; }
.modal { padding: 36px; border-radius: 16px; }
.modal h2 { font-size: 28px; line-height: 1.25; }
.modal label { display: block; margin: 22px 0 6px; font-size: 13px; font-weight: 600; }
.modal #token-save { width: 100%; margin-top: 16px; }
/* Comfortable hit targets for text fields and buttons — but not for checkboxes
   and radios, where a 40px box turned every filter row into a 72px stripe. */
button, input, select { min-height: 40px; }
input[type="checkbox"], input[type="radio"] { min-height: 0; }
button.text-link,
button.icon-btn,
button.filter-side-collapse,
.exposure-banner-dismiss { min-height: auto; }
summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 10px; }
@media (max-width: 1050px) {
  :root { --rail-w: 180px; }
  main { padding: 28px 24px 48px; }
  /* Track main's gutter here too, or the banner drifts out of alignment with
     the content the moment the clamp and this padding disagree. */
  .exposure-banner-inner { padding-inline: 24px; }
  .family-grid { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  /* Single column: the rail becomes a sticky bar under the header. */
  body {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "banner" "header" "nav" "main" "footer";
    grid-template-rows: auto auto auto 1fr auto;
    background-image: none;
  }
  .app-header { position: relative; height: auto; min-height: 76px; padding: 16px; gap: 10px; }
  .brand .muted { display: none; }
  .brand h1 { font-size: 16px; }
  .header-actions { gap: 5px; }
  .tabs { position: sticky; top: 0; align-self: stretch; max-height: none; overflow: visible; padding: 8px; flex-direction: row; gap: 3px; background: var(--sunken); border-right: 0; border-bottom: 1px solid var(--line); }
  .tab { flex: 1; flex-direction: column; gap: 4px; padding: 8px 3px; min-width: 0; font-size: 12px; text-align: center; justify-content: center; white-space: nowrap; }
  .nav-icon { width: 18px; height: 18px; }
  main { padding: 24px 16px 36px; }
  /* Declared here, not in the earlier ≤760 block: the ≤1050 rule above comes
     later in the file and would otherwise win on a phone. */
  .exposure-banner-inner { margin-left: 0; padding-inline: 16px; }
  .page-head { flex-wrap: wrap; gap: 16px; margin-bottom: 22px; }
  .card, #panel > .card:first-child { padding: 20px; }
  .status-hero { padding: 18px; }
  .family-grid { grid-template-columns: 1fr; }
  .probe-age { white-space: normal; }
  .modal { padding: 24px; }
}
/* One joined segmented control rather than three loose pills, so it reads as a
   single "which models am I looking at" switch. */
.catalog-views {
  display: inline-flex;
  margin: -8px 0 24px;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--sunken);
  max-width: 100%;
  overflow-x: auto;
}
.catalog-views button {
  flex: 0 0 auto;
  padding: 7px 14px;
  border: 0;
  border-radius: 7px;
  background: none;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.catalog-views button:hover { color: var(--text); }
.catalog-views button[aria-pressed="true"] {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-soft);
}
.catalog-views button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ------------------------------------------------------ model detail dialog */
.model-detail-dialog {
  width: min(680px, calc(100vw - 32px));
  max-height: min(84vh, 900px);
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  color: var(--text);
  overflow: visible;
}
.model-detail-dialog::backdrop { background: rgb(0 0 0 / 55%); }
.dialog-close {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 1;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--raised);
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.dialog-close:hover { color: var(--text); border-color: var(--muted); }
#model-detail-body { max-height: min(84vh, 900px); overflow-y: auto; }
.model-detail { padding: 26px 28px 24px; }
.model-detail-head { display: flex; flex-wrap: wrap; gap: 10px 16px; padding-right: 34px; }
.model-detail-head h3 { margin: 2px 0 0; font-size: 22px; letter-spacing: -0.4px; }
.model-detail-head .eyebrow { margin-bottom: 2px; }
.model-detail-blurb { margin: 16px 0 0; color: var(--muted); line-height: 1.6; }
.model-detail-note { margin: 16px 0 0; }
.model-detail-facts {
  display: grid;
  grid-template-columns: minmax(8rem, auto) 1fr;
  gap: 8px 20px;
  margin: 22px 0 0;
  padding: 18px 0 0;
  border-top: 1px solid var(--line-soft);
}
.model-detail-facts dt { color: var(--muted); font-size: 0.86rem; }
/* Prose values, not machine output: `.facts dd` is monospace so that paths and
   ports line up, but "Fastest · cached streaming" is a sentence. */
.model-detail-facts dd {
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.9rem;
  overflow-wrap: anywhere;
}
.model-detail-facts dd code { font-family: var(--mono); font-size: 0.85rem; }
.model-detail-languages { margin: 22px 0 0; padding: 18px 0 0; border-top: 1px solid var(--line-soft); }
.model-detail-languages h4 { display: flex; align-items: baseline; gap: 8px; margin: 0; font-size: 0.95rem; }
.model-detail-count {
  padding: 1px 8px;
  border-radius: var(--r-pill);
  background: var(--raised);
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 600;
}
.model-detail-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: 24px 0 0;
  padding: 18px 0 0;
  border-top: 1px solid var(--line-soft);
}
@media (max-width: 560px) {
  .model-detail { padding: 22px 18px 20px; }
  .model-detail-facts { grid-template-columns: 1fr; gap: 2px; }
  .model-detail-facts dd { margin-bottom: 10px; }
  .model-detail-actions > button { flex: 1 1 auto; }
}
.activity-details .operations-grid { margin-bottom: 18px; }
.pairing-advanced[open] > summary { margin-bottom: 16px; }
.family-models-grid { grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr)); }
@media (max-width: 760px) {
  .models-toolbar-actions { justify-content: flex-start; width: 100%; }
}

/* ------------------------------------------------- speed / accuracy ratings */
/* Five fixed-width pips per row so two models line up down a column: the emoji
   is the filled state, a dot the empty one. */
.ratings { display: grid; gap: 3px; margin: 10px 0 0; }
.rating { display: flex; align-items: center; gap: 8px; font-size: 0.78rem; }
.rating-label { min-width: 4.6rem; color: var(--muted); font-weight: 600; }
.rating-pips { display: inline-flex; gap: 2px; }
.rating-pip { font-size: 0.86rem; line-height: 1.4; }
.rating-pip.is-off { filter: grayscale(1); opacity: 0.25; }
.model-detail-ratings {
  margin: 20px 0 0;
  padding: 16px 18px;
  border-radius: 10px;
  background: var(--raised);
}
.model-detail-ratings .ratings { margin: 0 0 8px; }
.model-detail-ratings .rating { font-size: 0.86rem; }
.model-detail-ratings p { margin: 0; }

/* ---------------------------------------------------------- model picker */
.picker {
  margin: 0 0 22px;
  padding: 20px 22px 22px;
  border: 1px solid var(--accent-line);
  border-radius: 12px;
  background: var(--accent-wash);
}
.picker-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px 20px;
}
.picker-head h3 { margin: 0; font-size: 1.05rem; }
.picker-head p { margin: 4px 0 0; font-size: 0.88rem; }
.picker-language { display: grid; gap: 4px; }
.picker-language > span { color: var(--muted); font-size: 0.78rem; font-weight: 600; }
.picker-language select { min-width: 11rem; }
.picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
  gap: 12px;
  margin-top: 18px;
}
.picker-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
}
/* The suggested starting point reads first, so give it the accent border. */
.picker-card[data-lead] { border-color: var(--accent); }
.picker-intent { margin: 0; font-size: 0.86rem; font-weight: 700; }
.picker-why { margin: 2px 0 0; }
.picker-model { margin: 12px 0 0; font-size: 0.98rem; line-height: 1.3; }
.picker-card .ratings { margin: 8px 0 0; }
.picker-size { margin: 8px 0 0; }
/* Pushed down so the buttons line up across cards of unequal text length. */
.picker-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: auto; padding-top: 14px; }
@media (max-width: 760px) {
  .picker { padding: 16px; }
  .picker-language, .picker-language select { width: 100%; }
}

/* Cleanup cards live outside speech-model families and own their card layout. */
.cleanup-model-list {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: 16px;
}
.cleanup-model-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 22px;
  gap: 20px;
  border: 1px solid var(--line-soft);
  border-radius: 14px;
  background: var(--sunken);
}
.cleanup-model-card[data-active="1"] { border-color: var(--accent); }
.cleanup-model-card .model-title { flex-direction: column; align-items: flex-start; gap: 12px; }
.cleanup-model-card .model-title h4 { font-size: 1.12rem; line-height: 1.4; }
.cleanup-model-card .model-specs { margin-top: 16px; }
.cleanup-model-card .model-spec { background: var(--surface); }
.cleanup-model-card .model-blurb { display: block; margin-top: 14px; line-height: 1.6; }
.cleanup-languages { font-size: 0.8rem; line-height: 1.7; margin: 14px 0; }
.cleanup-languages > span { display: block; }
.cleanup-model-details { font-size: 0.8rem; }
.cleanup-model-details summary { cursor: pointer; color: var(--muted); padding: 6px 0; }
.cleanup-model-details summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.cleanup-model-card .model-toolbar { border-top: 1px solid var(--line-soft); padding-top: 16px; flex-wrap: wrap; }
.cleanup-model-card .cleanup-facts { grid-template-columns: minmax(0, 1fr); gap: 3px; }
.cleanup-model-card .cleanup-facts dd { margin-bottom: 8px; }
@media (max-width: 480px) {
  .cleanup-model-card { padding: 16px; }
  .cleanup-model-card .model-actions { flex-wrap: wrap; }
}

/* Cleanup settings use the full card, with related controls kept together. */
.cleanup-settings-form { display: grid; gap: 24px; }
.cleanup-enable {
  align-items: center;
  justify-content: space-between;
  flex: none;
  padding: 18px 20px;
  border: 1px solid var(--line-soft);
  border-radius: 10px;
  background: var(--sunken);
}
.cleanup-enable input { flex-shrink: 0; width: 22px; height: 22px; }
.cleanup-preferences { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px; }
.cleanup-preference-group { display: grid; align-content: start; gap: 22px; min-width: 0; }
.cleanup-preference-group h4 { margin: 0; font-size: 0.85rem; color: var(--muted); }
.cleanup-preference-group .settings-field { gap: 9px; }
.cleanup-preference-group .settings-field > span:first-child { font-size: 0.9rem; color: var(--text); }
.cleanup-memory-toggle { flex: none; }
.cleanup-settings-footer { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px; border-top: 1px solid var(--line-soft); padding-top: 20px; }
.cleanup-settings-footer p, .cleanup-library-note { margin: 0; color: var(--muted); font-size: 0.8rem; line-height: 1.5; }
.cleanup-library-note { margin-top: 18px; }
.cleanup-model-card .model-title { min-height: 64px; }
.cleanup-model-card .model-blurb { min-height: 44px; }
.cleanup-model-details summary { padding: 10px 12px; background: var(--surface); border-radius: 8px; }
.cleanup-model-details[open] .cleanup-facts { padding: 10px 12px; }
@media (max-width: 700px) {
  .cleanup-preferences { grid-template-columns: 1fr; gap: 28px; }
  .cleanup-enable { padding: 14px; }
  .cleanup-settings-footer { align-items: stretch; flex-direction: column; }
  .cleanup-settings-footer button { align-self: flex-start; }
}
.cleanup-settings-card { container: cleanup-settings / inline-size; }
@container cleanup-settings (max-width: 620px) {
  .cleanup-preferences { grid-template-columns: 1fr; gap: 28px; }
  .cleanup-settings-footer { align-items: stretch; flex-direction: column; }
  .cleanup-settings-footer button { align-self: flex-start; }
}

.cleanup-header-state { color: var(--muted); margin-left: 6px; }
.cleanup-header-status .dot.muted { background: var(--muted); }
.cleanup-header-status .dot.error { background: var(--error); }
.cleanup-activity { border-top: 1px solid var(--line-soft); margin-top: 24px; padding-top: 20px; }
.header-actions { flex-wrap: wrap; }
