/* yetter·chat -- the chat and load-test console for the yetter LLM gateway.
 *
 * Same family as the rbln-bench dashboard, different room. The tokens are
 * carried over unchanged: Rebellions fluorescent green (#52f756) only ever on a
 * dark field -- the sidebar, the login page, dark-theme accents -- because at
 * 1.4:1 on white it is unreadable as text; light panels use the darker green
 * step (#02ab6c). Mono for numbers, identifiers and labels; the system sans for
 * prose; tabular numerals wherever digits line up.
 *
 * What is deliberately different: this is a conversation surface, not an
 * instrument panel. One column, a longer measure, more air, and nothing
 * competing with the transcript. The recurring motif is the caret -- it blinks
 * after the wordmark on the sign-in page and again at the end of a streaming
 * answer, because "text is still arriving" is the moment this console exists
 * to measure. */

:root {
  color-scheme: light;
  --surface-1: #ffffff;
  --surface-0: #f4f5f4;
  --surface-2: #ebedeb;
  --surface-3: #dfe2df;
  --text-primary: #14171a;
  --text-secondary: #3b434a;
  --text-muted: #6f7780;
  --grid: #e3e6e3;
  --axis: #c4c9c4;
  --border: rgba(20, 23, 26, 0.13);

  /* Validated series slots, in the dashboard's fixed order. Only the first two
     are used here: RPS rides slot 1, in-flight slot 2, each on its own chart. */
  --series-1: #2a78d6;
  --series-2: #eb6834;

  --good: #02ab6c;
  --warning: #fab219;
  --critical: #d03b3b;
  --accent: #02ab6c;
  --brand: #52f756;
  --navy: #14171a;
  --on-accent: #ffffff;

  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", sans-serif;
  --radius: 6px;
  --sidebar: 248px;
  /* The transcript measure. Prose, not a data grid, so it reads at a book-ish
     width rather than the dashboard's full 1560px. */
  --column: 52rem;
}

@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --surface-1: #1b1e1b;
    --surface-0: #121412;
    --surface-2: #262a26;
    --surface-3: #323632;
    --text-primary: #f0f2f0;
    --text-secondary: #bcc2bc;
    --text-muted: #8b928b;
    --grid: #262a26;
    --axis: #323632;
    --border: rgba(240, 242, 240, 0.14);
    --series-1: #3987e5;
    --series-2: #d95926;
    --accent: #52f756;
    --good: #52f756;
    --on-accent: #0d1a0e;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --surface-1: #1b1e1b; --surface-0: #121412; --surface-2: #262a26; --surface-3: #323632;
  --text-primary: #f0f2f0; --text-secondary: #bcc2bc; --text-muted: #8b928b;
  --grid: #262a26; --axis: #323632; --border: rgba(240, 242, 240, 0.14);
  --series-1: #3987e5; --series-2: #d95926;
  --accent: #52f756; --good: #52f756; --on-accent: #0d1a0e;
}

* { box-sizing: border-box; }

html, body { height: 100%; }
body {
  margin: 0;
  background: var(--surface-0);
  color: var(--text-primary);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  /* Korean line-breaking: confine breaks to existing spaces rather than between
     any two syllables. Latin is unaffected. */
  word-break: keep-all;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------------------------------------------------------------- shell --- */

.shell { display: flex; height: 100vh; height: 100dvh; overflow: hidden; }

aside.sidebar {
  width: var(--sidebar);
  flex: 0 0 var(--sidebar);
  /* The one place the brand navy is a field, so the console reads as
     Rebellions without tinting the surface the conversation sits on. */
  background: #14171a;
  color: #bcc2bc;
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.brand {
  font-family: var(--mono);
  font-weight: 600;
  font-size: 17px;
  letter-spacing: -0.02em;
  color: #f0f2f0;
  padding: 18px 18px 14px;
}
.brand span { color: #52f756; font-weight: 400; }
.brand a { color: inherit; }
.brand a:hover { text-decoration: none; }

.nav-section {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.13em;
  text-transform: uppercase; color: #7d857d; padding: 14px 18px 6px;
}
.nav-item {
  display: flex; align-items: baseline; gap: 8px;
  padding: 7px 18px; font-size: 14px; color: #bcc2bc;
  border-left: 2px solid transparent;
}
.nav-item:hover { background: rgba(255, 255, 255, 0.07); text-decoration: none; color: #ffffff; }
.nav-item.active {
  background: rgba(82, 247, 86, 0.14);
  border-left-color: #52f756;
  color: #ffffff; font-weight: 500;
}
.nav-cta {
  display: block; margin: 12px 18px 2px; padding: 9px 12px;
  background: var(--brand); color: #0d1a0e; border: none; width: calc(100% - 36px);
  border-radius: var(--radius); font-family: var(--sans);
  font-size: 14px; font-weight: 600; text-align: center; cursor: pointer;
}
.nav-cta:hover { text-decoration: none; filter: brightness(1.07); }

/* The scrolling middle of the sidebar: conversations on /, recent runs on
   /load. The footer stays pinned below it. */
.sidebar-list { flex: 1 1 auto; overflow-y: auto; min-height: 0; padding-bottom: 8px; }
.sidebar-empty { padding: 10px 18px; font-size: 12.5px; color: #7d857d; }

ul.convs { list-style: none; margin: 4px 0 0; padding: 0; }
ul.convs > li { position: relative; }
.conv-link {
  display: flex; align-items: baseline; gap: 8px;
  padding: 7px 18px; color: #bcc2bc; font-size: 13.5px;
  border-left: 2px solid transparent;
}
.conv-link:hover { background: rgba(255, 255, 255, 0.07); color: #fff; text-decoration: none; }
li.on > .conv-link {
  background: rgba(82, 247, 86, 0.14);
  border-left-color: #52f756; color: #ffffff;
}
.conv-title {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.conv-time { font-family: var(--mono); font-size: 11px; color: #7d857d; flex: none; }
/* Rename/delete live on the row but only surface on hover or keyboard focus;
   on a touch screen (no hover at all) they are simply always there. */
.conv-tools {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  display: none; gap: 2px; background: #14171a; padding-left: 4px;
}
ul.convs > li:hover .conv-tools,
ul.convs > li:focus-within .conv-tools { display: inline-flex; }
@media (hover: none) { .conv-tools { display: inline-flex; } }
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0;
  background: transparent; border: none; border-radius: 4px;
  color: #8b928b; cursor: pointer;
}
.icon-btn:hover { background: rgba(255, 255, 255, 0.12); color: #fff; }
.icon-btn svg { width: 13px; height: 13px; }
.conv-rename-input {
  width: calc(100% - 26px); margin: 3px 8px 3px 18px;
  font-size: 13px; padding: 4px 7px;
  background: #262a26; color: #f0f2f0; border-color: #3b433b;
}

/* Recent load runs, same list slot. A button, not a link: it opens the result
   panel in place. */
ul.runs { list-style: none; margin: 4px 0 0; padding: 0; }
.run-link {
  display: flex; flex-direction: column; gap: 1px; width: 100%;
  text-align: left; padding: 7px 18px; cursor: pointer;
  background: transparent; border: none; border-left: 2px solid transparent;
  color: #bcc2bc; font-family: var(--mono); font-size: 12px;
}
.run-link:hover { background: rgba(255, 255, 255, 0.07); color: #fff; }
li.on > .run-link { background: rgba(82, 247, 86, 0.14); border-left-color: #52f756; color: #fff; }
.run-top { display: flex; align-items: center; gap: 7px; min-width: 0; }
.run-model { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.run-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: #7d857d; }
.run-dot.done { background: var(--brand); }
.run-dot.failed { background: #e66767; }
.run-dot.running { background: var(--brand); animation: live-pulse 1.8s ease-in-out infinite; }
.run-sub { color: #7d857d; font-size: 11px; font-variant-numeric: tabular-nums; padding-left: 14px; }
@keyframes live-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .25 } }

.sidebar-foot {
  flex: none; padding: 12px 18px 14px;
  border-top: 1px solid rgba(240, 242, 240, 0.1);
  font-family: var(--mono); font-size: 12px; color: #8a949a;
}
.sidebar-foot .who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-bottom: 5px; }
.sidebar-foot .foot-row { display: flex; align-items: center; gap: 12px; }
.sidebar-foot a { color: var(--brand); }
.theme-btn {
  margin-left: auto; padding: 2px 8px; font-family: var(--mono); font-size: 11px;
  background: transparent; color: #8a949a; border: 1px solid rgba(240, 242, 240, 0.18);
  border-radius: 4px; cursor: pointer;
}
.theme-btn:hover { color: #fff; border-color: #8a949a; }

/* The narrow-width top bar and its scrim. Hidden on desktop; at ≤900px the bar
   carries the brand and the menu button, and the sidebar becomes an overlay. */
.topbar { display: none; }
.scrim { display: none; }

main.surface {
  flex: 1 1 auto; min-width: 0; min-height: 0;
  display: flex; flex-direction: column;
}

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

button, .button {
  font-family: var(--mono); font-size: 13.5px; padding: 8px 16px;
  border-radius: var(--radius); border: 1px solid var(--border);
  background: var(--surface-2); color: var(--text-primary); cursor: pointer;
}
button:hover { filter: brightness(1.05); }
button.primary { background: var(--accent); border-color: transparent; color: var(--on-accent); font-weight: 600; }
button:disabled { opacity: 0.45; cursor: not-allowed; filter: none; }
button.danger { background: transparent; border-color: var(--critical); color: var(--critical); }
button.danger:hover { background: color-mix(in srgb, var(--critical) 10%, transparent); }

input[type="number"], input[type="text"], select, textarea {
  font-family: var(--mono); font-size: 13.5px; padding: 7px 9px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface-1); color: var(--text-primary);
}
::placeholder { color: var(--text-muted); opacity: 1; }

label.stack { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
label.stack > .lbl {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--text-muted);
}
label.stack > input, label.stack > select { width: 100%; }
.hint { font-size: 11.5px; color: var(--text-muted); font-family: var(--mono); }

label.check {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 13.5px; color: var(--text-secondary); cursor: pointer;
}
label.check > input { accent-color: var(--accent); margin: 0; }
label.check code { color: var(--text-primary); }

.note { color: var(--text-muted); font-size: 12.5px; line-height: 1.5; }

.pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--mono); font-size: 12px;
  padding: 2px 9px; border-radius: 11px;
  border: 1px solid var(--border); color: var(--text-secondary); white-space: nowrap;
}
.pill::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--text-muted); }
.pill.done::before { background: var(--good); }
.pill.running::before { background: var(--accent); animation: live-pulse 1.8s ease-in-out infinite; }
.pill.failed::before { background: var(--critical); }
.pill.cancelled::before { background: var(--text-muted); }

/* A banner that states a fault and what to do about it. */
.banner {
  border-left: 3px solid var(--critical);
  background: color-mix(in srgb, var(--critical) 9%, var(--surface-1));
  padding: 10px 14px; font-size: 13.5px; border-radius: 0 var(--radius) var(--radius) 0;
}
.banner.warn { border-left-color: var(--warning); background: color-mix(in srgb, var(--warning) 10%, var(--surface-1)); }

code { font-family: var(--mono); background: var(--surface-2); border-radius: 4px; padding: 2px 6px; font-size: 13px; }

/* ------------------------------------------------------------ chat page --- */

.chat-head {
  flex: none; display: flex; align-items: center; gap: 16px;
  padding: 12px 24px; border-bottom: 1px solid var(--border);
  background: var(--surface-0); min-height: 58px;
}
.chat-title {
  flex: 1 1 auto; min-width: 0; margin: 0;
  font-size: 15px; font-weight: 600; letter-spacing: -0.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.model-pick { display: flex; align-items: center; gap: 8px; flex: none; min-width: 0; }
.model-pick > .lbl {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--text-muted);
}
.model-pick select { max-width: 300px; }
.model-ctx { font-family: var(--mono); font-size: 11.5px; color: var(--text-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }

.chat-banner { flex: none; padding: 10px 24px 0; }
.chat-banner .banner { max-width: var(--column); margin: 0 auto; }

.transcript {
  flex: 1 1 auto; overflow-y: auto; min-height: 0; scroll-padding-bottom: 24px;
  /* A column so the empty state can center itself with margin:auto. */
  display: flex; flex-direction: column;
}
.turns { flex: none; width: 100%; }
.turns {
  max-width: var(--column); margin: 0 auto; padding: 28px 24px 32px;
  display: flex; flex-direction: column; gap: 22px;
}

.turn { min-width: 0; }
/* The asker's turn is a compact bubble against the field; the answer is plain
   text on the field itself. Alignment and treatment separate the two roles
   without a label shouting on every message. */
.turn.user { align-self: flex-end; max-width: min(85%, 40rem); }
.turn.user .msg-body {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 12px 12px 3px 12px;
  padding: 9px 14px;
}
.turn.assistant { align-self: stretch; }

.msg-body { white-space: pre-wrap; overflow-wrap: anywhere; }
.msg-body .prose { white-space: pre-wrap; }
.msg-body pre.code {
  position: relative; white-space: pre; overflow: auto; max-height: 420px;
  margin: 10px 0; padding: 12px 14px;
  background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius);
  font-family: var(--mono); font-size: 12.5px; line-height: 1.6; tab-size: 2;
}
.msg-body pre.code > code { background: none; padding: 0; font-size: inherit; }
.code-lang {
  position: sticky; top: 0; float: right; margin: -4px -6px 0 10px;
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-muted);
}

/* --- rendered Markdown (assistant turns) ----------------------------------
   The HTML comes from the server's markdown-it with raw HTML off; this styles
   it at chat scale. Headings one step below the page's own, so a model's ##
   never competes with the UI. While streaming the turn is still plain
   pre-wrapped text; these rules take over when the rendered HTML lands. */

.msg-body.md { white-space: normal; }
.md > :first-child { margin-top: 0; }
.md > :last-child { margin-bottom: 0; }
.md p { margin: 0 0 12px; }
.md h1, .md h2, .md h3, .md h4, .md h5, .md h6 {
  letter-spacing: -0.01em; font-weight: 600; line-height: 1.35;
  color: var(--text-primary); margin: 20px 0 8px;
}
.md h1 { font-size: 19px; }
.md h2 { font-size: 17px; border-bottom: 1px solid var(--grid); padding-bottom: 4px; }
.md h3 { font-size: 15.5px; }
.md h4, .md h5, .md h6 {
  font-size: 12.5px; font-family: var(--mono);
  letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-muted);
}
.md ul, .md ol { margin: 0 0 12px; padding-left: 22px; }
.md li { margin: 0 0 4px; }
.md li > ul, .md li > ol { margin: 4px 0 0; }
.md a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.md strong { font-weight: 600; }
.md s, .md del { color: var(--text-muted); }
.md hr { border: 0; border-top: 1px solid var(--border); margin: 18px 0; }
.md blockquote {
  margin: 0 0 12px; padding: 2px 0 2px 13px;
  border-left: 3px solid var(--accent); color: var(--text-secondary);
}
.md blockquote > :last-child { margin-bottom: 0; }
.md code { overflow-wrap: anywhere; }
.md pre {
  margin: 10px 0; padding: 12px 14px; max-width: 100%; max-height: 420px;
  overflow: auto; white-space: pre;
  background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius);
  font-family: var(--mono); font-size: 12.5px; line-height: 1.6; tab-size: 2;
}
.md pre > code { background: none; padding: 0; font-size: inherit; overflow-wrap: normal; white-space: pre; }
/* A model asked for a recipe reaches for a table immediately; a wide one
   scrolls in its own box rather than pushing the transcript sideways. */
.md table {
  display: block; max-width: 100%; overflow-x: auto;
  border-collapse: collapse; font-family: var(--mono); font-size: 12.5px;
  margin: 0 0 12px;
}
.md th {
  text-align: left; font-weight: 600; color: var(--text-secondary);
  border-bottom: 1px solid var(--border); padding: 6px 14px 6px 0; white-space: nowrap;
}
.md td {
  padding: 6px 14px 6px 0; border-bottom: 1px solid var(--grid);
  font-variant-numeric: tabular-nums; vertical-align: top;
}

/* The answer stopped at the output cap: not an error -- the text above is
   real -- but not a complete reply either, and it must not be quoted as one.
   Metrics-line register, warning edge. */
.msg-cut {
  margin-top: 8px; padding: 6px 11px; display: inline-block;
  border-left: 3px solid var(--warning);
  background: color-mix(in srgb, var(--warning) 10%, var(--surface-1));
  border-radius: 0 var(--radius) var(--radius) 0;
  font-family: var(--mono); font-size: 11.5px; color: var(--text-secondary);
  font-variant-numeric: tabular-nums; overflow-wrap: anywhere;
}

/* TTFT and total, per answer. The reason the console exists, said at caption
   size in the muted ink so it never competes with the text above it. */
.msg-meta {
  margin-top: 7px; font-family: var(--mono); font-size: 11.5px;
  color: var(--text-muted); font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.msg-error {
  margin-top: 8px; padding: 8px 12px;
  border-left: 3px solid var(--critical);
  background: color-mix(in srgb, var(--critical) 9%, var(--surface-1));
  border-radius: 0 var(--radius) var(--radius) 0;
  font-family: var(--mono); font-size: 12.5px; color: var(--text-secondary);
  white-space: pre-wrap; overflow-wrap: anywhere;
}

/* The streaming caret: a steady block that blinks while text is arriving.
   With reduced motion it stays lit instead of blinking. */
.caret {
  display: inline-block; width: 0.55em; height: 1.05em;
  margin-left: 2px; vertical-align: -0.16em;
  background: var(--accent);
  animation: caret-blink 1s steps(2, jump-none) infinite;
}
@keyframes caret-blink { 50% { opacity: 0; } }

.transcript-empty {
  margin: auto; text-align: center; color: var(--text-muted);
  padding: 40px 24px; max-width: 34rem;
}
.transcript-empty .mark { font-family: var(--mono); font-size: 21px; color: var(--text-secondary); margin-bottom: 10px; }
.transcript-empty .mark span { color: var(--accent); }
.transcript-empty p { margin: 0; font-size: 14px; }

/* --------------------------------------------------------------composer --- */

.composer-wrap { flex: none; padding: 0 24px 14px; background: var(--surface-0); }
.composer {
  max-width: var(--column); margin: 0 auto;
  display: flex; align-items: flex-end; gap: 10px;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: 12px; padding: 8px 8px 8px 14px;
  box-shadow: 0 2px 10px rgba(10, 12, 10, 0.05);
}
.composer:focus-within { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }
.composer textarea {
  flex: 1 1 auto; min-width: 0; border: none; background: none; padding: 6px 0;
  font-family: var(--sans); font-size: 15px; line-height: 1.5;
  resize: none; max-height: 200px; overflow-y: auto;
}
.composer textarea:focus-visible { outline: none; }
.send-btn { flex: none; padding: 8px 18px; }
.composer-hint {
  max-width: var(--column); margin: 6px auto 0; text-align: right;
  font-family: var(--mono); font-size: 11px; color: var(--text-muted);
}

/* ------------------------------------------------------------ load page --- */

main.surface.scroll { overflow-y: auto; display: block; }

.page-head { padding: 26px 28px 0; max-width: 1280px; }
.eyebrow {
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--text-muted); margin: 0 0 6px;
}
h1 { font-size: 24px; margin: 0 0 4px; letter-spacing: -0.02em; font-weight: 600; }
.subtitle { color: var(--text-secondary); margin: 0; font-size: 13.5px; max-width: 62ch; }

.load-layout {
  display: grid; grid-template-columns: 400px minmax(0, 1fr);
  gap: 20px; align-items: start;
  padding: 20px 28px 56px; max-width: 1280px;
}
@media (max-width: 1080px) {
  .load-layout { grid-template-columns: minmax(0, 1fr); }
}

.card {
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 18px 20px;
}
.card h2 { margin: 0 0 14px; font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
.card h3 {
  margin: 22px 0 8px; font-family: var(--mono); font-size: 11px; font-weight: 500;
  letter-spacing: 0.11em; text-transform: uppercase; color: var(--text-muted);
  border-bottom: 1px solid var(--grid); padding-bottom: 5px;
}

.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 14px; }
.field-grid .full { grid-column: 1 / -1; }
.form-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 12px; }
.form-note { margin: 10px 0 0; }
.form-actions { margin-top: 16px; display: flex; gap: 10px; align-items: center; }
.form-said { font-family: var(--mono); font-size: 12px; color: var(--critical); overflow-wrap: anywhere; }

/* Prompt source: one prompt typed here, or an uploaded dataset. A segmented
   pair, because it is one choice with two states. */
.seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: var(--surface-1); }
.segbtn {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px;
  font-family: var(--mono); font-size: 12.5px; color: var(--text-secondary);
  cursor: pointer; user-select: none;
}
.segbtn + .segbtn { border-left: 1px solid var(--border); }
.segbtn > input { accent-color: var(--accent); margin: 0; }
.segbtn:has(input:checked) { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--text-primary); }
.segbtn:focus-within { outline: 2px solid var(--accent); outline-offset: -2px; }
[hidden] { display: none !important; }

textarea.prompt-box { width: 100%; min-height: 84px; resize: vertical; font-size: 13px; line-height: 1.55; }

.dataset-row { display: flex; gap: 8px; align-items: center; }
.dataset-row select { flex: 1 1 auto; min-width: 0; }
.upload-label {
  display: inline-flex; align-items: center; font-family: var(--mono); font-size: 12px;
  padding: 6px 11px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface-2); color: var(--text-primary); cursor: pointer; white-space: nowrap;
}
.upload-label:hover { filter: brightness(1.05); }
.upload-label input[type="file"] { width: 1px; height: 1px; opacity: 0; position: absolute; overflow: hidden; }
.upload-label:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.upload-said { display: block; margin-top: 6px; font-family: var(--mono); font-size: 11.5px; color: var(--accent); overflow-wrap: anywhere; }
.upload-said.bad { color: var(--critical); }

ul.dataset-list { list-style: none; margin: 8px 0 0; padding: 0; }
ul.dataset-list > li {
  display: flex; align-items: center; gap: 10px; padding: 5px 0;
  border-bottom: 1px solid var(--grid); font-family: var(--mono); font-size: 12px;
}
ul.dataset-list .ds-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-secondary); }
ul.dataset-list .ds-meta { color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ds-del { background: transparent; border: none; color: var(--text-muted); cursor: pointer; padding: 2px 4px; border-radius: 4px; }
.ds-del:hover { color: var(--critical); background: color-mix(in srgb, var(--critical) 10%, transparent); }

/* -------------------------------------------------------- load results --- */

.result-empty {
  border: 1px dashed var(--border); border-radius: var(--radius);
  padding: 44px 24px; text-align: center; color: var(--text-muted); font-size: 14px;
}

.result-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.result-title { font-family: var(--mono); font-size: 13px; color: var(--text-secondary); overflow-wrap: anywhere; }
.result-head .spacer { flex: 1 1 auto; }

/* The headline pair. Achieved leads at display size; target sits beside it at
   half the size in the muted ink, always visible, never the headline. The gap
   between the two is the finding, so the share is printed with them. */
.achieved-row { display: flex; align-items: flex-end; gap: 26px; flex-wrap: wrap; margin: 4px 0 2px; }
.achieved .value {
  font-family: var(--mono); font-size: 38px; line-height: 1.1;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
}
.achieved .label, .target-of .label {
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-muted); margin-top: 3px;
}
.target-of .value {
  font-family: var(--mono); font-size: 19px; line-height: 1.4;
  color: var(--text-secondary); font-variant-numeric: tabular-nums;
}
.target-share { font-size: 13px; color: var(--text-muted); font-family: var(--mono); }

/* The limiter verdict: whose fault the gap was, in a sentence. Warning edge
   when the client was the constraint -- the number says nothing about the
   gateway and the card must make that misreading impossible. Accent edge when
   the gateway was actually the thing being measured. */
.verdict-box {
  margin: 14px 0 0; padding: 12px 15px;
  border-left: 3px solid var(--warning);
  background: color-mix(in srgb, var(--warning) 10%, var(--surface-1));
  border-radius: 0 var(--radius) var(--radius) 0;
}
.verdict-box.server { border-left-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--surface-1)); }
.verdict-box.met { border-left-color: var(--good); background: color-mix(in srgb, var(--good) 8%, var(--surface-1)); }
.verdict-box .v-head { margin: 0 0 4px; font-size: 14px; font-weight: 600; }
.verdict-box .v-body { margin: 0; font-size: 13px; color: var(--text-secondary); max-width: 66ch; }

.stats { display: flex; gap: 14px 28px; flex-wrap: wrap; margin-top: 18px; }
.stat .value {
  font-family: var(--mono); font-size: 20px; letter-spacing: -0.01em;
  line-height: 1.2; font-variant-numeric: tabular-nums;
}
.stat .label {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--text-muted); margin-top: 2px; white-space: nowrap;
}
.stat .value.bad { color: var(--critical); }
.stat-note { margin-top: 10px; }

/* ---------------------------------------------------------------- charts --- */

.chart-block { margin-top: 8px; }
.chart-title {
  display: flex; align-items: baseline; gap: 10px;
  font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-muted); margin: 14px 0 4px;
}
.chart-title .swatch { width: 10px; height: 10px; border-radius: 3px; align-self: center; }
svg.chart { width: 100%; height: auto; display: block; overflow: visible; }
.chart .grid-line { stroke: var(--grid); stroke-width: 1; }
.chart .axis-line { stroke: var(--axis); stroke-width: 1; }
.chart .tick { fill: var(--text-muted); font-family: var(--mono); font-size: 10.5px; font-variant-numeric: tabular-nums; }
.chart .line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart .area { opacity: 0.08; }
.chart .ref { stroke: var(--text-muted); stroke-width: 1.5; stroke-dasharray: 5 4; }
.chart .ref-label { fill: var(--text-secondary); font-family: var(--mono); font-size: 10.5px; }
.chart .cursor { stroke: var(--axis); stroke-width: 1; stroke-dasharray: 3 3; }

.chart-tip {
  position: fixed; pointer-events: none; z-index: 30;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 6px 9px;
  font-family: var(--mono); font-size: 11.5px; color: var(--text-secondary);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
}

details.raw-view > summary {
  cursor: pointer; font-family: var(--mono); font-size: 11.5px;
  color: var(--text-muted); letter-spacing: 0.06em; text-transform: uppercase;
  margin-top: 12px; padding: 4px 0;
}
details.raw-view > summary:hover { color: var(--text-primary); }
.table-scroll { overflow-x: auto; max-width: 100%; }

table.data { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 12.5px; }
table.data th {
  text-align: left; font-weight: 500; color: var(--text-muted);
  font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase;
  border-bottom: 1px solid var(--border); padding: 7px 14px 7px 0; white-space: nowrap;
}
table.data td { padding: 6px 14px 6px 0; border-bottom: 1px solid var(--grid); font-variant-numeric: tabular-nums; }
table.data td.num, table.data th.num { text-align: right; }

/* Error samples: real bodies, and the label says sampled. */
.sample-item {
  margin: 8px 0 0; padding: 9px 12px;
  background: var(--surface-0); border: 1px solid var(--border); border-radius: var(--radius);
  font-family: var(--mono); font-size: 11.5px; line-height: 1.55; color: var(--text-secondary);
  white-space: pre-wrap; overflow-wrap: anywhere;
}

/* ------------------------------------------------------------ login page --- */

/* Theme-invariant, deliberately: the sign-in card is the one full-bleed brand
   moment, and the brand is fluorescent green on near-black -- as rebellions.ai
   itself is. Both themes see the same page. */
body.login {
  background: #101312;
  background-image: radial-gradient(60rem 40rem at 50% 30%, #161b17, #101312 70%);
  color: #f0f2f0;
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; min-height: 100dvh; padding: 24px;
}
.login-box { width: min(400px, 100%); text-align: center; }
.login-brand {
  font-family: var(--mono); font-weight: 600; font-size: 27px;
  letter-spacing: -0.02em; color: #f0f2f0; margin-bottom: 22px;
}
.login-brand span.g { color: #52f756; font-weight: 400; }
.login-brand .caret { background: #52f756; width: 0.5em; height: 1em; vertical-align: -0.1em; }

.login-card {
  background: #1b1e1b; border: 1px solid rgba(240, 242, 240, 0.14);
  border-radius: 10px; padding: 26px 26px 22px; text-align: left;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.4);
}
.login-desc { margin: 0 0 18px; font-size: 14px; line-height: 1.6; color: #bcc2bc; }

.google-btn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; padding: 10px 16px;
  background: #ffffff; color: #1f1f1f; border-radius: var(--radius);
  font-family: var(--sans); font-size: 14px; font-weight: 600;
}
.google-btn:hover { text-decoration: none; filter: brightness(0.96); }
.google-btn svg { flex: none; }

.login-rule {
  margin: 14px 0 0; font-family: var(--mono); font-size: 12px; color: #8b928b;
  text-align: center;
}
.login-rule code { background: rgba(240, 242, 240, 0.08); color: #bcc2bc; }

.login-error {
  margin: 0 0 14px; padding: 10px 12px;
  border-left: 3px solid #e66767; background: rgba(230, 103, 103, 0.1);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 13px; color: #f0c4c4; white-space: pre-wrap; overflow-wrap: anywhere;
}
.login-note {
  margin: 0 0 14px; padding: 10px 12px;
  border-left: 3px solid #fab219; background: rgba(250, 178, 25, 0.09);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 13px; color: #e8d7ae;
}
.login-unconfigured {
  padding: 12px 14px; border-left: 3px solid #fab219;
  background: rgba(250, 178, 25, 0.09); border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 13px; color: #d9dedb; line-height: 1.6;
}
.login-unconfigured code { display: inline-block; background: rgba(240, 242, 240, 0.08); color: #bcc2bc; font-size: 12px; margin: 1px 0; }
.login-foot { margin: 18px 0 0; font-family: var(--mono); font-size: 11.5px; color: #6f7780; text-align: center; }

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

@media (max-width: 900px) {
  .shell { display: block; height: auto; min-height: 100vh; min-height: 100dvh; }
  .topbar {
    display: flex; align-items: center; gap: 12px;
    position: sticky; top: 0; z-index: 40;
    background: #14171a; color: #f0f2f0;
    padding: 10px 16px; border-bottom: 1px solid rgba(240, 242, 240, 0.12);
  }
  .topbar .brand { padding: 0; font-size: 15px; }
  .menu-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; padding: 0; flex: none;
    background: transparent; border: 1px solid rgba(240, 242, 240, 0.2);
    border-radius: var(--radius); color: #f0f2f0; cursor: pointer;
  }
  .menu-btn svg { width: 16px; height: 16px; }

  aside.sidebar {
    position: fixed; inset: 0 auto 0 0; z-index: 50;
    width: min(300px, 84vw); height: 100dvh;
    transform: translateX(-100%); transition: transform 160ms ease;
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.4);
  }
  body.sidebar-open aside.sidebar { transform: translateX(0); }
  .scrim { display: none; position: fixed; inset: 0; z-index: 45; background: rgba(8, 10, 8, 0.5); border: none; padding: 0; }
  body.sidebar-open .scrim { display: block; }

  main.surface { height: calc(100dvh - 55px); }
  main.surface.scroll { height: auto; }

  .chat-head { flex-wrap: wrap; gap: 8px 16px; padding: 10px 16px; min-height: 0; }
  .chat-title { flex-basis: 100%; order: 2; font-size: 13.5px; }
  .model-pick { order: 1; flex: 1 1 auto; }
  .model-pick select { flex: 1 1 auto; max-width: none; min-width: 0; }
  .model-ctx { display: none; }

  .turns { padding: 20px 16px 24px; }
  .composer-wrap { padding: 0 12px 10px; }
  .composer-hint { display: none; }
  .chat-banner { padding: 10px 16px 0; }

  .page-head { padding: 20px 16px 0; }
  .load-layout { padding: 16px 16px 40px; gap: 16px; }
  .achieved .value { font-size: 30px; }
}

@media (max-width: 560px) {
  .field-grid { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  .caret { animation: none; }
}
