/* evalkit dashboard design system.
   One committed stylesheet, no build step. Tokens first, then base elements
   (all form controls are styled globally so nothing ever renders unstyled),
   then components. Dark mode overrides only the tokens. */

:root {
  --bg: #f7f6f2;          /* warm paper */
  --bg-elev: #ffffff;
  --fg: #1c1917;          /* warm ink */
  --muted: #6b675f;
  --faint: #a29d92;
  --border: #e7e4dc;
  --border-strong: #d3cec3;
  --surface: #f1efe9;
  --surface-2: #e9e6de;
  --media-bg: #14120e;
  --accent: #0f766e;      /* deep teal */
  --accent-soft: #e3f1ef;
  --accent-fg: #0b5d57;   /* darker teal for links/text on light bg */
  --on-accent: #ffffff;
  --focus-ring: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
  --shadow: 0 1px 2px rgba(16, 24, 40, .06), 0 1px 3px rgba(16, 24, 40, .10);
  --shadow-lg: 0 4px 6px -1px rgba(16, 24, 40, .08), 0 12px 24px -8px rgba(16, 24, 40, .14);
  --ok-bg: #e7f6ec;   --ok-fg: #157347;
  --err-bg: #fdecec;  --err-fg: #c2331f;
  --warn-bg: #fef4e2; --warn-fg: #b25f0a;
  --radius: 12px;
  --radius-sm: 8px;
  /* Height of the sticky top bar, so a second sticky element can park directly
     beneath it instead of under it. `.topbar nav` is held to this below, which
     is what keeps the number honest. */
  --topbar-h: 3.4rem;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #131311;
    --bg-elev: #1b1b18;
    --fg: #e9e7e2;
    --muted: #a3a094;
    --faint: #737065;
    --border: #2b2a25;
    --border-strong: #45433b;
    --surface: #21201c;
    --surface-2: #292823;
    --media-bg: #0a0908;
    --accent: #2fb3a6;
    --accent-soft: #16302c;
    --accent-fg: #7fd6cc;   /* links on dark bg */
    --on-accent: #0c1513;   /* dark text on the brighter dark-mode accent */
    --shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 1px 3px rgba(0, 0, 0, .4);
    --shadow-lg: 0 4px 6px -1px rgba(0, 0, 0, .4), 0 12px 28px -8px rgba(0, 0, 0, .55);
    --ok-bg: #102c1c;   --ok-fg: #5fd693;
    --err-bg: #331417;  --err-fg: #f2868c;
    --warn-bg: #2e2410; --warn-fg: #f0be6a;
  }
}

/* ---------- base ---------- */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font: 14.5px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, sans-serif;
  color: var(--fg);
  background: var(--bg);
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}
main { max-width: 1200px; margin: 0 auto; padding: 1.75rem 1.5rem 4rem; }
h1 { font-size: 1.45rem; font-weight: 700; letter-spacing: -.02em; margin: .25rem 0 .35rem; }
h2 { font-size: 1rem; font-weight: 650; letter-spacing: -.01em; margin: 2rem 0 .75rem; }
p { margin: .5rem 0; }
a { color: var(--accent-fg); text-decoration: none; }
a:hover { text-decoration: underline; }
code { font: .84em var(--mono); }
pre {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: .85rem 1rem; overflow-x: auto; font-size: .8rem;
}

/* ---------- form controls (global — every control is styled, everywhere) ----------
   "every control" is load-bearing and was not true until BE-17361: the selector
   below listed four text-ish `type=` values and omitted `password`, so every
   credential field in the dashboard — the whole of /profile — fell back to UA
   default rendering. That is three visible defects at once, and they read as
   three unrelated ones: square corners against 8px everywhere else, an unthemed
   background that inverts wrongly in dark mode, and no focus ring. It also left
   the bullet glyphs at whatever colour the UA chose instead of `--fg`, which is
   what made them look washed out next to the label. Type-gated inputs are easy
   to forget: a text-entry type used on a `.form-card` surface must be added
   here rather than styled at its own call site. NOT yet covered: untyped
   `<input>`s and `datetime-local` on the investigation pages, which lay their
   own inline label rows out and would change shape under `width: 100%`. */
input, select, textarea, button { font: inherit; color: inherit; }
input[type="text"], input[type="number"], input[type="search"], input[type="url"],
input[type="password"], input[type="email"], input[type="tel"], input[type="date"],
select, textarea {
  appearance: none;
  width: 100%;
  padding: .5rem .7rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-elev);
  color: var(--fg);
  transition: border-color .12s, box-shadow .12s;
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: var(--focus-ring);
}
input::placeholder, textarea::placeholder { color: var(--faint); }
input[readonly] { background: var(--surface); color: var(--muted); }
/* Author-styled inputs lose the UA's disabled look, so restore one — the
   Comfy-key field on /profile renders disabled when BYOK is unavailable and
   must not read as an editable credential input. */
input:disabled, select:disabled, textarea:disabled {
  background: var(--surface); color: var(--muted); cursor: not-allowed; opacity: .7;
}
textarea { resize: vertical; min-height: 2.6rem; }
select {
  padding-right: 2.1rem;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 17px) 50%, calc(100% - 12px) 50%;
  background-size: 5px 5px;
  background-repeat: no-repeat;
  cursor: pointer;
}
input[type="checkbox"] {
  width: 1rem; height: 1rem; margin: 0;
  accent-color: var(--accent); cursor: pointer; flex: none;
}
label { cursor: pointer; }

/* ---------- buttons ---------- */
.btn, .btn-primary {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  padding: .5rem 1rem; border-radius: var(--radius-sm);
  font: inherit; font-weight: 600; font-size: .9rem; line-height: 1.4;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: filter .12s, transform .04s, border-color .12s, color .12s;
}
.btn { border: 1px solid var(--border-strong); background: var(--bg-elev); color: var(--fg); }
.btn:hover { border-color: var(--accent); color: var(--accent-fg); text-decoration: none; }
.btn-primary { border: 0; background: var(--accent); color: var(--on-accent); box-shadow: var(--shadow); }
.btn-primary:hover { filter: brightness(1.07); text-decoration: none; }
.btn-primary:active { transform: translateY(1px); }
.btn:disabled,
.btn-primary:disabled { opacity: .5; cursor: not-allowed; filter: none; }

/* ---------- top bar ---------- */
.topbar {
  position: sticky; top: 0; z-index: 20;
  background: color-mix(in srgb, var(--bg-elev) 85%, transparent);
  backdrop-filter: saturate(1.4) blur(12px);
  border-bottom: 1px solid var(--border);
}
.topbar nav {
  max-width: 1200px; margin: 0 auto; padding: .6rem 1.5rem;
  display: flex; gap: 1.25rem; align-items: center;
  /* Minus the bar's 1px bottom border, so the bar as a whole measures exactly
     `--topbar-h` and anything offset by that token lines up with its edge. */
  min-height: calc(var(--topbar-h) - 1px);
}
.brand {
  display: inline-flex; align-items: center; gap: .5rem;
  font-weight: 720; letter-spacing: -.02em; color: var(--fg); font-size: 1rem;
}
.brand:hover { text-decoration: none; }
/* The mark is now `static/icon.svg` (the same file the tab icon uses), so the
   gradient lives in the asset rather than being restated here — one identity,
   one definition. The ring stays: it is header chrome, not part of the mark,
   and the favicon must not carry it at 16px. */
.brand-mark {
  width: 18px; height: 18px;
  /* Matches the SVG's OWN corner (rx=14 on a 64 viewBox = 21.875%) rather than
     the old fixed 6px. The art already carries its corners; this only shapes
     the ring below, and a mismatched radius leaves it visibly off the tile.
     A percentage keeps that true if the mark is ever rendered at another size. */
  border-radius: 22%;
  display: block; flex: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.nav-links { display: flex; gap: .3rem; flex-wrap: wrap; margin-left: auto; align-items: center; }
.nav-links a {
  color: var(--muted); font-weight: 550; font-size: .9rem;
  padding: .35rem .7rem; border-radius: var(--radius-sm); transition: background .12s, color .12s;
}
.nav-links a:hover { color: var(--fg); background: var(--surface); text-decoration: none; }
.nav-links a.active { color: var(--accent-fg); background: var(--accent-soft); }
/* Beats `.nav-links a`'s specificity so the CTA keeps button colors. */
.nav-links a.nav-cta { margin-left: .5rem; padding: .35rem .8rem; font-size: .85rem; color: var(--on-accent); background: var(--accent); }
.nav-links a.nav-cta:hover { color: var(--on-accent); background: var(--accent); filter: brightness(1.07); }
.nav-user { color: var(--muted); font-size: .85rem; margin-left: .5rem; }

footer {
  max-width: 1200px; margin: 0 auto; padding: 1.25rem 1.5rem;
  color: var(--faint); font-size: .8rem; border-top: 1px solid var(--border);
}

/* ---------- login ---------- */
.login-wrap { display: flex; justify-content: center; padding-top: 3.5rem; }
.login-card {
  width: 100%; max-width: 22rem; text-align: center;
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); padding: 2.25rem 2rem;
}
.login-mark { margin-bottom: .75rem; }
.login-card h1 { font-size: 1.15rem; margin-bottom: .35rem; }
.login-card .lede { margin: 0 0 1.5rem; }
.login-btn { width: 100%; }
/* The MCP mint page shows one very long unbroken token; `pre`'s default
   horizontal scroll would hide most of it (and most of the copy-paste line it
   sits in), so this one wraps instead. */
.token-block { white-space: pre-wrap; overflow-wrap: anywhere; }

/* ---------- page & section headers ---------- */
.page-head { margin-bottom: 1.25rem; }
.page-head .lede { color: var(--muted); margin: 0; max-width: 60ch; }
.detail-head, .section-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
}
.detail-head { margin-bottom: .5rem; }
.detail-head h1 { margin: 0; overflow-wrap: anywhere; }
.detail-head .actions { display: flex; gap: .5rem; align-items: center; }
.section-head { margin: 2rem 0 .75rem; }
.section-head h2 { margin: 0; }
.section-head .more { font-size: .88rem; font-weight: 550; }
.backlink { font-size: .88rem; }
/* Row of navigation links under a detail heading (back-link + Temporal
   deep-links). Wraps rather than overflowing on a narrow viewport. */
.detail-links { display: flex; gap: 1rem; flex-wrap: wrap; font-size: .88rem; }

/* ---------- panels ---------- */
.panel {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 1.1rem 1.2rem; margin: 1rem 0;
}

/* ---------- stat tiles ---------- */
.stat-grid {
  display: grid; gap: .9rem; margin: 1.25rem 0 .5rem;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.stat {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .95rem 1.05rem; box-shadow: var(--shadow);
}
.stat .label {
  font-size: .7rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted);
}
.stat .value {
  font-size: 1.8rem; font-weight: 700; letter-spacing: -.03em; line-height: 1.15;
  margin: .3rem 0 .1rem; font-variant-numeric: tabular-nums;
}
.stat .value.accent { color: var(--accent-fg); }
.stat .value.ok { color: var(--ok-fg); }
.stat .sub { font-size: .78rem; color: var(--faint); }
.stat .value .unit { font-size: .95rem; font-weight: 600; color: var(--muted); margin-left: .15rem; }

/* ---------- run shortcuts (empty /runs/new state) ---------- */
.shortcut-grid {
  display: grid; gap: .9rem; margin: 1.1rem 0;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.shortcut {
  display: block; background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow); padding: 1rem 1.1rem;
  transition: border-color .12s, transform .12s; text-decoration: none;
}
.shortcut:hover { border-color: var(--accent); transform: translateY(-1px); text-decoration: none; }
.shortcut-title { display: block; font-weight: 650; color: var(--fg); }
.shortcut-sub { display: block; margin-top: .25rem; font-size: .82rem; color: var(--muted); }
.shortcut-accent { border-color: var(--accent); background: var(--accent-soft); }
.shortcut-accent .shortcut-title { color: var(--accent-fg); }

/* ---------- tables ---------- */
.table-wrap, main > table, .content-table {
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
}
/* Wide tables (test cases, run items) scroll inside their own bordered card
   rather than pushing the page sideways on a phone. `-webkit-overflow-scrolling`
   is a no-op from iOS 13 on but is what gives older iOS Safari momentum
   scrolling; `overscroll-behavior-x` keeps a swipe that reaches the end of the
   table from triggering Safari's back-navigation gesture. */
.table-wrap.scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}
/* The run page's results table: its own scroll pane, so the header row can
   stick to the top of it while a 160-item run scrolls underneath.
   `position: sticky` resolves against the nearest SCROLL CONTAINER, and
   `.table-wrap.scroll` is already one (an `overflow-x: auto` box computes
   `overflow-y` to `auto` too) — so a viewport-anchored `thead` here is a no-op
   no matter what `top` says. Bounding the height is what turns that container
   into one that actually scrolls, which is what makes the sticky header work.
   `overflow` (not `overflow-x`) because the pane now scrolls both ways. */
.table-wrap.scroll.results-pane { max-height: min(70vh, 44rem); overflow: auto; }
.results-pane thead th {
  position: sticky; top: 0; z-index: 2;
  /* `thead th`'s background is already opaque, but its bottom border scrolls
     with the cell rather than staying pinned under a sticky one — the shadow
     redraws the separator the rows would otherwise slide over. */
  box-shadow: inset 0 -1px 0 var(--border);
}
table { border-collapse: collapse; width: 100%; font-size: .88rem; font-variant-numeric: tabular-nums; }
thead th {
  text-align: left; padding: .55rem .85rem; background: var(--surface);
  color: var(--muted); font-weight: 600; font-size: .74rem; letter-spacing: .04em;
  text-transform: uppercase; white-space: nowrap; border-bottom: 1px solid var(--border);
}
tbody td { padding: .6rem .85rem; border-bottom: 1px solid var(--border); vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background .1s; }
tbody tr:hover { background: color-mix(in srgb, var(--accent) 5%, transparent); }
tbody tr[data-href] { cursor: pointer; }
td.reason, td.uri { max-width: 28rem; overflow-wrap: anywhere; }
th.num, td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---------- prompt cell (test cases list, BE-5903) ----------
   The list carries each case's whole prompt so a suite can be read at a glance
   instead of one click-through per case. Whole means whole: nothing is cut
   server-side, so the page always holds the full text. What keeps 100 rows
   scannable is a visual clamp to the first few lines, and the <details> around
   it un-clamps one prompt in place on click — no JavaScript, and with CSS off
   the summary just renders in full. Rows go top-aligned because a four-line
   prompt next to a middle-aligned checkbox reads as a different row. */
.case-row td { vertical-align: top; }
.prompt-cell { min-width: 16rem; max-width: 34rem; }
/* The prompt takes the width it needs from its neighbours, and `cloud-mcp`
   broken across two lines is worse than a slightly wider table — the row lives
   in `.table-wrap.scroll`, which is what absorbs the extra width. */
.case-row .nowrap-cell { white-space: nowrap; }
.prompt-disclosure { margin: 0; }
/* Not the muted, small global `details summary`: this one is the content. The
   marker goes because the prompt itself is the affordance. */
.prompt-disclosure > summary {
  cursor: pointer; color: var(--fg); font-size: .88rem; padding: 0; list-style: none;
}
.prompt-disclosure > summary::-webkit-details-marker { display: none; }
.prompt-disclosure > summary:hover .prompt-text { color: var(--accent-fg); }
.prompt-text {
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 4; line-clamp: 4; overflow: hidden;
  white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.45;
}
/* Lifting the clamp resets all three properties on purpose. `display: block`
   alone is enough for the legacy `-webkit-line-clamp`, which only applies
   inside a `-webkit-box` — but the standard `line-clamp` (CSS Overflow 4,
   shipping in newer Chrome) clamps a plain block too, so a browser that
   implements it would leave an "expanded" prompt still cut at four lines. */
.prompt-disclosure[open] > summary .prompt-text {
  display: block; -webkit-line-clamp: none; line-clamp: none;
}

/* ---------- badges & chips ---------- */
.badge {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .12rem .55rem; border-radius: 999px;
  font-size: .73rem; font-weight: 600; white-space: nowrap; line-height: 1.5;
}
.badge.ok   { background: var(--ok-bg);   color: var(--ok-fg); }
.badge.err  { background: var(--err-bg);  color: var(--err-fg); }
.badge.warn { background: var(--warn-bg); color: var(--warn-fg); }
.badge.muted { background: var(--surface); color: var(--muted); border: 1px solid var(--border); }
.badge.mcp { background: var(--accent-soft); color: var(--accent-fg); }
.badge.cli { background: var(--warn-bg); color: var(--warn-fg); }

/* Recent-runs history strip: one mark per run, newest first, reusing the badge
   colour families above rather than a palette of its own. Roughly 8px per mark
   including its gap, so ten marks sit in about the width of the score column
   beside it, and the strip never wraps — a wrapped strip breaks the vertical
   band that makes a whole-run outage visible down the table. */
.history-strip { display: inline-flex; align-items: center; gap: 1px; white-space: nowrap; line-height: 1; }
/* A SQUARE box, sized in px rather than left to the glyph's line height. The
   glyph is a circle and the current-run ring is drawn on the box, so a box as
   tall as a line of text rings it as an ellipse overlapping its neighbour. */
.history-strip .hist {
  display: inline-flex; align-items: center; justify-content: center;
  position: relative; width: 7px; height: 7px; border-radius: 50%;
  font-size: .56rem; line-height: 1; text-decoration: none; color: var(--muted);
}
.history-strip .hist.ok   { color: var(--ok-fg); }
.history-strip .hist.err  { color: var(--err-fg); }
.history-strip .hist.warn { color: var(--warn-fg); }
.history-strip .hist.muted { color: var(--border-strong); }
/* Past the case-version boundary: still readable, visibly not current. */
.history-strip .hist.is-superseded { opacity: .45; }
/* box-shadow, not outline: it follows the border radius, so the ring is a
   circle around the dot and takes no layout width. Two rings — a page-coloured
   gap under the accent one — because an accent ring laid straight on a green
   `accepted` dot is teal on green and all but invisible. */
.history-strip .hist.is-current {
  box-shadow: 0 0 0 1.5px var(--bg-elev), 0 0 0 3px var(--accent-fg);
}
.history-strip .hist.is-rerun::after {
  content: ""; position: absolute; top: -1px; right: -2px;
  width: 3px; height: 3px; border-radius: 50%; background: var(--accent-fg);
}
.history-strip .hist-divider {
  display: inline-block; width: 1px; height: 9px;
  margin: 0 2px; background: var(--border-strong);
}

.chips { display: flex; flex-wrap: wrap; gap: .35rem; margin: .75rem 0; }
.chips-cell { max-width: 26rem; }
.chips-cell .chip { margin: 0 .25rem .25rem 0; }
.chip {
  display: inline-block; padding: .08rem .6rem; border: 1px solid var(--border-strong);
  border-radius: 999px; background: var(--surface); color: var(--fg); font-size: .75rem; white-space: nowrap;
}
.chip:hover { border-color: var(--accent); color: var(--accent-fg); text-decoration: none; }
.chip.is-on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-fg); font-weight: 600; }
/* Section jump links, in the ok-green so they read as navigation rather than as
   another row of the status badges they sit above. The clearance keeps a
   jumped-to anchor from landing under the sticky top bar. */
.toc-head { margin: 1.4rem 0 .5rem; }
.toc { gap: .5rem; margin: 0 0 1.6rem; }
.toc .chip {
  padding: .3rem .85rem; font-size: .95rem; font-weight: 600;
  color: var(--ok-fg); background: var(--ok-bg); border-color: currentColor;
}
.toc .chip:hover { background: var(--ok-fg); color: var(--ok-bg); border-color: var(--ok-fg); }
[id] { scroll-margin-top: calc(var(--topbar-h) + .6rem); }
/* The suite filter's always-visible option row — every real suite one tap away. */
.suite-chips { align-items: center; margin: -.5rem 0 1.1rem; }
.suite-chips .subtle { margin-right: .15rem; }

/* ---------- filter bars ---------- */
.filters {
  display: flex; gap: .8rem; align-items: end; flex-wrap: wrap;
  margin: 1rem 0 1.1rem; padding: .8rem .9rem;
  background: var(--bg-elev); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  font-size: .88rem;
}
.filters label {
  display: flex; flex-direction: column; gap: .25rem; color: var(--muted);
  font-size: .78rem; font-weight: 600; letter-spacing: .02em;
  /* Flex items default to min-width:auto, which pins a label to its content's
     min width — a select sized by its longest option then pushes the whole page
     wider than a phone viewport instead of wrapping. */
  min-width: 0; max-width: 100%;
}
/* `min(11rem, 100%)`, not a flat `11rem`: min-width beats max-width when the two
   disagree, so a flat floor would still push a control past its container once
   11rem stops fitting (narrow phone, or any viewport with the root font size
   turned up). */
.filters input, .filters select {
  min-width: min(11rem, 100%); width: auto; font-size: .88rem; max-width: 100%;
}
.filters select.run-select { max-width: min(26rem, 100%); }
/* One desktop row for the cases page's five controls. A select's intrinsic
   width is its longest OPTION — `expect-tool-attempted` in the family lens,
   whole sentences under `text-any:` in the tag picker — so floors alone still
   overflow a non-maximized window and wrap the milestone onto its own line.
   Caps bound what each control can insist on (~57rem all told, one row from
   ~1000px up); the closed control clips a long selected value, but the open
   dropdown always renders full-width, and phones still wrap — the bar keeps
   flex-wrap. */
.filters select.tag-picker { max-width: min(14rem, 100%); }
.filters label.compact select { min-width: min(8.5rem, 100%); max-width: min(11rem, 100%); }
.filters input[name="suite"] { min-width: 0; width: min(10rem, 100%); }
.filter-chips { margin: -.4rem 0 .8rem; }
.filters .btn, .filters .btn-primary { padding: .48rem .9rem; font-size: .85rem; }
.filters .clear { align-self: center; font-size: .84rem; margin-left: .1rem; }
/* A filter bar that stays reachable while you scroll a long results table. It
   parks under the sticky top bar rather than at `top: 0`, which would slide it
   behind that bar's blur; `--topbar-h` is only accurate while the nav is one
   row, which is why this is gated to a viewport wide enough to guarantee that.
   Narrow screens keep the plain, non-sticky bar — vertical space is the scarce
   thing there anyway. */
@media (min-width: 820px) {
  .filters.sticky { position: sticky; top: var(--topbar-h); z-index: 15; }
}

/* ---------- pagination ---------- */
.pagination { display: flex; gap: 1rem; align-items: center; margin: 1rem 0; font-size: .88rem; color: var(--muted); }
.pagination .disabled { color: var(--faint); }

/* ---------- meta lists ---------- */
dl.meta {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: .55rem 1.5rem; margin: 1rem 0; padding: .9rem 1.1rem;
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow);
}
dl.meta div { display: flex; gap: .5rem; }
dl.meta dt { color: var(--muted); min-width: 5.5rem; }
dl.meta dd { margin: 0; overflow-wrap: anywhere; }

.summary { color: var(--muted); font-weight: 400; }
.empty {
  color: var(--muted); padding: 2rem 1rem; text-align: center;
  background: var(--bg-elev); border: 1px dashed var(--border-strong);
  border-radius: var(--radius); margin: 1rem 0;
}
.error { color: var(--err-fg); }
.subtle { color: var(--muted); font-size: .78rem; }
.muted { color: var(--muted); }

/* ---------- run scoreboard (overall + per-tier bars) ---------- */
/* Mirrors the milestone report's critbar/progress structure, but in this
   dashboard's semantic tokens rather than that report's brand palette. Each bar
   is a flex row of percentage-width segments over a grey track, so whatever is
   left untinted is what the run hasn't decided yet (still pending/running). */
.scoreboard {
  background: var(--bg-elev); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 1rem 1.1rem; margin: .75rem 0 1.5rem;
}
.scoreboard .totalpct {
  font-size: 1.5rem; font-weight: 700; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
}
.scoreboard .note { font-size: .82rem; font-weight: 400; color: var(--muted); letter-spacing: 0; }
.progress {
  display: flex; height: 14px; border-radius: var(--radius-sm); overflow: hidden;
  background: var(--surface-2); margin-top: .5rem;
}
.progress .seg { height: 100%; }
.progress .p-pass { background: var(--ok-fg); }
.progress .p-fail { background: var(--err-fg); }
.progress .p-skip { background: var(--warn-fg); }
.legend { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: .45rem; font-size: .78rem; color: var(--muted); }
.legend i { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: .35rem; vertical-align: middle; }
.legend i.p-pass { background: var(--ok-fg); }
.legend i.p-fail { background: var(--err-fg); }
.legend i.p-skip { background: var(--warn-fg); }
.critbars { margin-top: 1rem; }
/* One line per tier. The label and the percentage are fixed-width so the bars
   line up; the label wraps rather than widening the row on a phone. */
.critbar { display: flex; align-items: center; gap: .8rem; margin: .45rem 0; }
.critlabel {
  width: 12rem; flex: none; font-size: .82rem; font-weight: 600;
  overflow-wrap: anywhere; min-width: 0;
}
.critbar .progress { flex: 1; margin: 0; height: 9px; min-width: 0; }
.critpct {
  width: 7.5rem; flex: none; text-align: right; font-size: .82rem;
  font-variant-numeric: tabular-nums;
}
/* Click-to-filter (release-gate report only). `app.js` adds role=button and the
   tabindex, so these only ever style a row the script has already upgraded —
   a no-JS page keeps the plain, non-interactive bars. */
.critbar[role="button"] { cursor: pointer; border-radius: var(--radius-sm); padding: .1rem .35rem; margin-left: -.35rem; margin-right: -.35rem; }
.critbar[role="button"]:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.critbar[role="button"].is-active { background: color-mix(in srgb, var(--accent) 13%, transparent); }
.critbar[role="button"].is-active .critlabel { color: var(--accent-fg); }
/* The UA's own `[hidden]` rule is what actually hides a filtered-out row, and it
   loses to any author rule that sets `display` on a class. Restate it here so a
   later `.transcript-row { display: ... }` cannot silently un-hide the filter. */
.rg-results tr[hidden] { display: none; }
/* Column-header filter menus. The <summary> is the caret in the header; the
   menu is absolutely positioned so opening it cannot widen the column or push
   the table's other headers around. */
.col-filter { display: inline-block; position: relative; margin: 0 0 0 .2rem; vertical-align: middle; }
/* Same trap as `.rg-results tr[hidden]` above, and this rule is the one that
   springs it: `display: inline-block` on the line above is an author rule, so
   it beats the UA's `[hidden]` and would leave the menu on screen with no
   script to make its buttons do anything. */
.col-filter[hidden] { display: none; }
.col-filter > summary { list-style: none; padding: 0 .15rem; font-size: .7rem; color: var(--faint); }
.col-filter > summary::-webkit-details-marker { display: none; }
.col-filter > summary:hover, .col-filter[open] > summary { color: var(--accent-fg); }
.col-filter.is-active > summary { color: var(--accent-fg); }
.col-menu {
  position: absolute; z-index: 20; left: 0; top: 100%; min-width: 11rem; max-width: 22rem;
  max-height: 18rem; overflow-y: auto; display: flex; flex-direction: column;
  padding: .3rem; background: var(--bg-elev); border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lg);
}
.col-menu button {
  background: none; border: 0; border-radius: var(--radius-sm); cursor: pointer;
  padding: .3rem .5rem; text-align: left; font: inherit; font-size: .8rem;
  font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--fg);
  overflow-wrap: anywhere;
}
.col-menu button:hover { background: var(--surface); }
.col-menu button.is-active { background: var(--accent-soft); color: var(--accent-fg); font-weight: 650; }
/* Names a control for a screen reader without taking space in the header. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
/* The active-filter readout + its "Show all" escape, inserted under the Results
   heading. The readout is a permanent `role="status"` live region, so it is
   never `hidden` — a live region announces nothing in the tick it re-enters the
   accessibility tree. It empties out instead, and collapses to zero height (no
   content, no margin) whenever no filter is active. */
.tier-filter { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; font-size: .85rem; }
.tier-filter.is-on { margin-top: .7rem; }
.tier-filter .btn { padding: .3rem .7rem; font-size: .8rem; }
/* `.btn` sets `display: inline-flex`, an author rule that beats the UA's
   `[hidden] { display: none }` outright — the same cascade trap as
   `.rg-results tr[hidden]` above, so restate it for the "Show all" button too.
   Without this the escape hatch renders on every report with no filter set. */
.tier-filter .btn[hidden] { display: none; }

.scoreboard h3 { font-size: .82rem; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); margin: 1.2rem 0 .5rem; }
.failure-reasons, .failure-classes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .35rem; }
.failure-reasons li, .failure-classes li { display: flex; gap: .5rem; align-items: baseline; font-size: .85rem; overflow-wrap: anywhere; }
/* The runs a converged scoreboard was rolled up from, above the second board. */
.rerun-chain { list-style: none; margin: .2rem 0 .8rem; padding: 0; display: flex; flex-direction: column; gap: .25rem; }
.rerun-chain li { display: flex; gap: .5rem; align-items: baseline; font-size: .82rem; overflow-wrap: anywhere; }
/* Chain navigation (parent breadcrumb + this run's direct reruns) and the
   rerun launcher. `wrap` because a run id is long and there may be several. */
.rerun-nav { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; margin: .2rem 0 1rem; }
.rerun-nav .chip { gap: .35rem; }
.rerun-chip { margin-left: .4rem; }
.rerun-launch, .stop-launch { margin: 0 0 1.4rem; }
/* The `summary` IS the button, so it must not also render a disclosure marker. */
.rerun-confirm > summary { display: inline-block; list-style: none; cursor: pointer; }
.rerun-confirm > summary::-webkit-details-marker { display: none; }
.rerun-confirm > form, .stop-confirm > form {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-elev); box-shadow: var(--shadow);
  padding: .9rem 1.1rem 1.1rem; margin: .7rem 0 0; max-width: 44rem;
}
.rerun-confirm label, .stop-confirm label { display: block; margin: .6rem 0; }
.rerun-confirm label.checkbox, .stop-confirm label.checkbox { display: flex; align-items: center; gap: .45rem; }
.rerun-confirm input[type="text"] { max-width: 18rem; }
@media (max-width: 640px) {
  /* Stack the row on a phone: a 12rem label + 7.5rem percentage leaves the bar
     nothing to occupy inside a 390px viewport. */
  .critbar { flex-wrap: wrap; gap: .25rem .8rem; }
  .critlabel { width: auto; flex: 1 1 auto; }
  .critpct { width: auto; flex: none; }
  .critbar .progress { flex: 1 0 100%; }
}

/* ---------- artifact galleries ---------- */
/* Outputs shown INSIDE a results row: the same `artifact_media` markup as a
   gallery card, sized down to a strip so a case with several renders does not
   set the row height. The gallery below is still where they are viewed. */
.row-thumbs { display: flex; flex-wrap: wrap; gap: .3rem; }
.row-thumbs a { display: block; }
.row-thumbs img, .row-thumbs video {
  display: block; width: 4.5rem; height: 3.4rem; object-fit: cover;
  border-radius: var(--radius-sm); background: var(--media-bg);
}
.row-thumbs audio { width: 11rem; }
.row-thumbs .file-chip { font-size: .75rem; }
/* Reads as part of the row above it, not as a row of its own. */
.transcript-row > td { border-top: 0; padding-top: 0; }
/* Eight columns, two of them running prose. Auto layout squeezes the prose to
   one word per line to fit; a floor on the table (and a window on the prose
   cells) makes the wrapper scroll instead, which is what it is there for. */
.rg-results { min-width: 76rem; }
.rg-results .rg-prose { min-width: 12rem; max-width: 22rem; }
.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: 1rem; margin: .75rem 0 1.5rem; }
.gallery.compact { grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); }
.artifact-card {
  margin: 0; display: flex; flex-direction: column; background: var(--bg-elev);
  border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow);
  transition: box-shadow .15s;
}
.artifact-card:hover { box-shadow: var(--shadow-lg); }
.artifact-card.is-accepted { border-color: color-mix(in srgb, var(--ok-fg) 55%, var(--border)); }
.artifact-card.is-rejected { border-color: color-mix(in srgb, var(--err-fg) 55%, var(--border)); }
.artifact-card img, .artifact-card video {
  display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: contain; background: var(--media-bg);
}
.artifact-card a:has(> img) { display: block; cursor: zoom-in; }
.artifact-card audio { display: block; width: 100%; padding: 1.25rem .5rem; }
.artifact-card .file-chip { display: block; padding: 1.5rem .75rem; text-align: center; font-size: .85rem; overflow-wrap: anywhere; }
.artifact-card figcaption {
  padding: .45rem .65rem .55rem; font-size: .78rem;
  display: flex; flex-direction: column; gap: .15rem; border-top: 1px solid var(--border);
}
.caption-link { font-weight: 600; overflow-wrap: anywhere; }
.caption-meta { color: var(--muted); }

/* ---------- lightbox ---------- */
.lightbox {
  border: 0; padding: 0; margin: auto; background: transparent; max-width: min(94vw, 1400px);
}
.lightbox::backdrop { background: rgba(5, 7, 10, .82); backdrop-filter: blur(4px); }
.lightbox figure {
  margin: 0; display: flex; flex-direction: column; gap: .5rem; align-items: center;
}
.lightbox img, .lightbox video {
  max-width: min(94vw, 1400px); max-height: 82vh; border-radius: var(--radius-sm);
  background: var(--media-bg); object-fit: contain;
}
.lightbox figcaption {
  color: #e7ebf0; font-size: .85rem; display: flex; gap: .8rem; align-items: center; flex-wrap: wrap;
  justify-content: center;
}
.lightbox figcaption a { color: #9fdcd4; }
.lightbox .lightbox-close {
  position: fixed; top: 1rem; right: 1.25rem;
  border: 0; background: rgba(255, 255, 255, .12); color: #fff;
  width: 2.2rem; height: 2.2rem; border-radius: 999px; font-size: 1.1rem; cursor: pointer;
}
.lightbox .lightbox-close:hover { background: rgba(255, 255, 255, .25); }

/* ---------- readable detail ---------- */
blockquote.assistant-text {
  margin: .5rem 0 1rem; padding: .75rem 1rem; background: var(--bg-elev);
  border: 1px solid var(--border); border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0; white-space: pre-wrap; overflow-wrap: anywhere; box-shadow: var(--shadow);
}
/* The accent edge marks the system under test's own voice. A quote the HARNESS
   spoke — the follow-up turn's prompt — drops it, so the two blockquotes on a
   nudged item page are not read as one speaker. */
blockquote.assistant-text.harness-text { border-left-color: var(--muted); }

details { margin: .5rem 0; }
details summary { cursor: pointer; color: var(--muted); font-size: .88rem; padding: .3rem 0; }
details summary:hover { color: var(--accent-fg); }
details[open] summary { margin-bottom: .4rem; }
/* A page section behind a disclosure: the heading inside the summary is the
   section's own, so it keeps heading weight rather than the muted rule above. */
.section-collapse > summary { color: var(--fg); padding: 0; }
.section-collapse > summary > h2 { display: inline; }

/* ---------- interleaved transcript timeline (item detail) ---------- */
.timeline { margin: .75rem 0 1.5rem; border-left: 2px solid var(--border); padding-left: .9rem; }
.timeline .tl-ts { font: .75rem var(--mono); color: var(--muted); white-space: nowrap; }
.tl-msg { margin: .6rem 0 .9rem; }
.tl-head { margin: 0 0 .2rem; }
.tl-role { font-size: .75rem; font-weight: 650; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.tl-text { white-space: pre-wrap; overflow-wrap: anywhere; }
.timeline details.tool { margin: .35rem 0; }
.timeline details.tool > summary { color: var(--fg); }
.timeline details.tool.is-error > summary { color: var(--err-fg); }
.tl-omitted { margin: .4rem 0 .8rem; font-style: italic; }
.tl-label { margin: .5rem 0 .2rem; font-size: .72rem; font-weight: 650; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.timeline pre { white-space: pre-wrap; overflow-wrap: anywhere; max-height: 24rem; overflow-y: auto; }
.timeline pre.tl-err { border-color: color-mix(in srgb, var(--err-fg) 45%, var(--border)); color: var(--err-fg); }

/* One-off page advisory (e.g. the floating-model-alias launch notice). Sits in
   the same family as .form-errors but in the warn palette: it reports, it does
   not block. */
.notice {
  border: 1px solid color-mix(in srgb, var(--warn-fg) 45%, var(--border));
  background: color-mix(in srgb, var(--warn-fg) 7%, var(--bg-elev));
  border-radius: var(--radius-sm); padding: .7rem 1rem; margin: 1rem 0;
}

/* ---------- forms (case editor, run launcher) ---------- */
.form-errors {
  border: 1px solid color-mix(in srgb, var(--err-fg) 45%, var(--border));
  background: color-mix(in srgb, var(--err-fg) 7%, var(--bg-elev));
  border-radius: var(--radius-sm); padding: .7rem 1rem; margin: 1rem 0;
}
.form-errors ul { margin: .4rem 0 0; padding-left: 1.2rem; }
/* Four templates put this class ON the <ul> itself (run_detail's stop/rerun
   error blocks, run_new, release_gate_finding_form) rather than on a wrapper
   around one. The rule above only matches a NESTED list, so those got the
   box's own `padding: .7rem 1rem` — which overrides the UA's list indent —
   leaving the marker against the border and wrapped lines running back under
   it instead of hanging. Drop the marker when the box IS the list. */
ul.form-errors { list-style: none; }

.form-card { max-width: 780px; display: flex; flex-direction: column; gap: 1rem; margin-top: 1rem; }
.form-card .field { display: flex; flex-direction: column; gap: .3rem; }
.form-card .field > span { font-size: .8rem; font-weight: 600; color: var(--muted); }
.form-card .field em { font-style: normal; font-weight: 400; opacity: .75; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
@media (max-width: 640px) { .field-row { grid-template-columns: 1fr; } }
.form-card fieldset {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-elev); box-shadow: var(--shadow);
  padding: .9rem 1.1rem 1.1rem; margin: 0;
  display: flex; flex-direction: column; gap: .8rem;
}
.form-card legend {
  font-size: .76rem; font-weight: 650; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); padding: 0 .35rem;
}
.form-card textarea { font-family: inherit; }
.form-card .field-prompt textarea { font-size: .95rem; }
.form-actions { display: flex; align-items: center; gap: 1rem; }
/* Scoped to `.form-card form` rather than added to `.form-actions` globally:
   the other users of that class (`run_new`, `run_detail`, and
   `release_gate_finding_form`, where `.form-card` is the <form> itself so this
   descendant selector never matches) lay their own forms out and a global
   margin would shift them for no reason. This matches the fieldset's own `gap`,
   so a field and its button row are separated by the same rhythm as two
   sibling fields. A bare button child (the single-button "Clear key" forms)
   keeps its intrinsic width instead of stretching across the card. */
.form-card form { display: flex; flex-direction: column; gap: .8rem; }
.form-card form > button { align-self: flex-start; }

/* ---------- judge provider rows (/profile) ---------- */
.judge-key-row + .judge-key-row {
  margin-top: .4rem; padding-top: 1rem; border-top: 1px solid var(--border);
}
.judge-key-row h3 {
  display: flex; align-items: center; gap: .5rem;
  margin: 0 0 .35rem; font-size: .98rem;
}
/* A monogram badge, not the vendor's own artwork: no network fetch, no asset
   pipeline, and nothing that goes stale when a brand refreshes. `aria-hidden`
   at the call site because the heading already names the provider — announcing
   "A Anthropic" is noise. Swap the span for an <img>/inline <svg> here if real
   marks are ever vendored; the layout does not change. */
.provider-mark {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 1.35rem; height: 1.35rem; border-radius: 6px;
  font-size: .72rem; font-weight: 700; line-height: 1;
  /* A fixed dark fallback, not `--muted`: an unmapped provider takes this path,
     and white on dark-mode `--muted` is ~2.6:1 — this one clears 7:1 in both
     themes. The badge carries white text on a mid-tone fill, so it needs a
     hairline to stay separated from a light card rather than floating. */
  color: #fff; background: #57534e;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / .08);
}
.provider-mark--anthropic { background: #d97757; }
.provider-mark--openai    { background: #0f9d7d; }
.provider-mark--google    { background: #4285f4; }

.checks { display: flex; flex-wrap: wrap; gap: .4rem .6rem; }
.check {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .35rem .7rem; font-size: .86rem;
  border: 1px solid var(--border-strong); border-radius: 999px;
  background: var(--bg-elev); transition: border-color .12s, background .12s;
}
.check:hover { border-color: var(--accent); }
.check:has(input:checked) {
  border-color: var(--accent); background: var(--accent-soft); color: var(--accent-fg); font-weight: 600;
}
.check code { font-size: .8rem; }

/* ---------- run launcher ---------- */
.selection-bar {
  position: sticky; bottom: 0; z-index: 10;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  flex-wrap: wrap;
  margin-top: .75rem; padding: .7rem 1rem;
  background: color-mix(in srgb, var(--bg-elev) 92%, transparent);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-lg);
}
.selection-actions { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; min-width: 0; }
/* "Run all N tagged “<tag>”" interpolates a user-supplied tag into a `.btn`,
   which is `white-space: nowrap` — so on a phone the button cannot shrink and
   drags the whole page wider than the viewport, which in turn swallows the
   table's own horizontal scroll. Let these two buttons wrap instead; on a
   desktop-width bar they still lay out on one line. */
.selection-bar .btn, .selection-bar .btn-primary {
  white-space: normal; overflow-wrap: anywhere; min-width: 0; max-width: 100%;
}
.run-form { max-width: 780px; display: flex; flex-direction: column; gap: 1rem; }
.run-form fieldset {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-elev); box-shadow: var(--shadow);
  padding: .9rem 1.1rem 1.1rem; margin: 0;
}
.run-form legend {
  font-size: .76rem; font-weight: 650; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); padding: 0 .35rem;
}
.run-form label { display: block; margin: .6rem 0; }
.run-form label.checkbox { display: flex; align-items: center; gap: .45rem; }
.run-form input[type="number"] { max-width: 14rem; }
.selected-cases { margin: 0; padding-left: 1.1rem; }
.selected-cases li { margin: .15rem 0; }
.driver-tools { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1rem; }
.driver-tool-name { margin: 0 0 .1rem; font-weight: 600; }
.driver-tool-desc { margin: 0 0 .4rem; }
/* A flex item by virtue of the label; `min-width: 0` is what lets it wrap
   instead of stretching the row past the fieldset on a narrow screen. */
.driver-meta { min-width: 0; overflow-wrap: anywhere; font-size: .8rem; }
.driver-choices { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.driver-choices label {
  display: flex; align-items: center; gap: .6rem; margin: 0;
  padding: .6rem .8rem; border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  transition: border-color .12s, background .12s;
}
.driver-choices label:hover { border-color: var(--accent); }
.driver-choices label:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
/* The judge panel's members, votes and cost copy are dead weight until someone
   opts into a panel, and on a form this long they read as questions that have to
   be answered. Same `:has()` shape `.check:has(input:checked)` uses, so the state
   follows the checkbox with no script and survives a redisplay, where the box is
   re-checked server-side. `display: none` rather than the `hidden` attribute
   because these fields stay submitted-but-ignored (`parse_judge_panel` reads them
   only when the box is ticked) rather than removed. A browser without `:has()`
   shows them, which is the behaviour this replaces. */
.judge-panel:not(:has(input[name="judge_panel"]:checked)) .judge-panel-details {
  display: none;
}
.driver-live { margin-left: auto; }
/* Follows `.driver-live` in the row, so it takes no `margin-left: auto` of its
   own — two auto margins would spread the pair to opposite ends of the label.
   A profile can only be BYOK-eligible if it is live (`byok_applies` requires
   it), so this badge never has to push itself over on its own. */
.driver-byok { background: var(--accent-soft); color: var(--accent-fg); }

/* ---------- by-case matrix ---------- */
.case-card {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 1rem 1.1rem; margin: 1rem 0;
  background: var(--bg-elev); box-shadow: var(--shadow);
}
.case-head { display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap; }
/* Title leads, slug follows demoted — the `case_label` shape, in the one place
   that lays its two halves out itself rather than calling the macro. */
.case-title { font-weight: 600; }
.case-id { font-size: .78rem; color: var(--muted); }
.case-prompt { margin: .35rem 0 .8rem; font-size: 1rem; line-height: 1.45; }
.case-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 14px; }
.combo {
  border: 1px solid var(--border); border-radius: var(--radius-sm); padding: .7rem;
  display: flex; flex-direction: column; gap: .55rem; border-top-width: 3px; background: var(--bg);
}
.combo.is-accepted { border-top-color: var(--ok-fg); }
.combo.is-rejected { border-top-color: var(--err-fg); }
.combo-head { display: flex; justify-content: space-between; align-items: center; gap: .4rem; }
.combo-media {
  width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: var(--radius-sm);
  background: var(--media-bg); display: block;
}
audio.combo-media { aspect-ratio: auto; background: transparent; }
a:has(> img.combo-media) { cursor: zoom-in; }
.combo-empty {
  display: flex; align-items: center; justify-content: center;
  color: var(--muted); font-size: .85rem; background: var(--surface);
}
.combo-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: .3rem; margin: 0; }
.combo-stats div { display: flex; flex-direction: column; }
.combo-stats dt { font-size: .62rem; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.combo-stats dd { margin: 0; font-family: var(--mono); font-size: .88rem; font-variant-numeric: tabular-nums; }
.combo-run { font-size: .78rem; }
/* The "other attempt" note (BE-13665) is a DIRECT child of `.combo`, unlike
   every other badge on this page — those sit inside `.combo-head`, a row flex.
   `.combo` is a COLUMN flex, so its default `align-items: stretch` would pull
   the pill across the whole cell and render a full-width rounded bar. Shrink it
   back to its content; nothing else is a direct `.badge` child of `.combo`. */
.combo > .badge { align-self: start; }

/* ---------- compare ---------- */
/* The cap sits on the scroll container, not the table: the container is what
   draws the card, so capping the table alone would leave a full-width card
   around a 640px table. Keep it a *soft* cap — the container also scrolls, and a
   flat `max-width` would hide a column behind a scrollbar on a wide desktop
   whenever the nowrap header row (it embeds both driver names) needs more than
   640px. `fit-content` lets the card grow past the cap for content that can't
   fit, `min-width` holds the 640px shape for content that can, and `max-width`
   keeps it inside the viewport on a phone. */
.metrics-compare-wrap { width: fit-content; min-width: min(640px, 100%); max-width: 100%; }
table.metrics-compare td:first-child { color: var(--muted); }

/* ---------- MCP token page: scope toggle + copy buttons ----------
   Both controls are `hidden` in the markup until app.js reveals them, so these
   rules only ever apply to a page whose script ran. */
.cmd-actions { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.copy-btn { padding: .35rem .7rem; font-size: .85rem; min-width: 5.5rem; }
.scope-toggle {
  display: inline-flex;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  overflow: hidden;
}
/* Same cascade trap as `.col-filter[hidden]` above, and the comment at the top
   of this block is only true because of this line: `display: inline-flex` here
   and on `.btn` are author-origin rules, so both beat the UA's
   `[hidden] { display: none }` and would leave the toggle and the copy buttons
   on screen — inert — on exactly the no-script page they are meant to stay off. */
.scope-toggle[hidden], .copy-btn[hidden] { display: none; }
.scope-toggle button {
  padding: .35rem .7rem; border: 0; cursor: pointer;
  font: inherit; font-size: .85rem; font-weight: 600; line-height: 1.4;
  background: var(--bg-elev); color: var(--muted);
  transition: background .12s, color .12s;
}
.scope-toggle button + button { border-left: 1px solid var(--border-strong); }
.scope-toggle button:hover { color: var(--accent-fg); }
.scope-toggle button.is-active { background: var(--accent-soft); color: var(--accent-fg); }
/* `--focus-ring` is an OUTSET box-shadow and these buttons sit flush against
   the toggle's padding box, so `overflow: hidden` above would clip it away and
   leave a keyboard user with no focus indicator at all. A negative
   `outline-offset` paints the ring inside the border box instead, where
   nothing clips it. */
.scope-toggle button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.evidence-search { display: flex; gap: 1rem; flex-wrap: wrap; align-items: end; margin: 1.5rem 0; }
.evidence-search label { display: grid; gap: .4rem; }
.evidence-query { flex: 1; min-width: 16rem; }
.evidence-search input, .evidence-search select { padding: .65rem; width: 100%; }
.evidence-card { border: 1px solid #34404c; border-radius: 10px; padding: 1.4rem; margin: 1.2rem 0; }
.evidence-heading { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.evidence-heading h2 { margin-top: 0; }
.evidence-columns { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1.4rem; }
.evidence-payload { white-space: pre-wrap; overflow-wrap: anywhere; max-height: 32rem; overflow: auto; font-size: .85rem; }
.evidence-pagination { display: flex; gap: 1rem; margin: 1.5rem 0; }
@media (max-width: 800px) { .evidence-columns { grid-template-columns: 1fr; } }
