/* Tier colours follow the medal metaphor: gold, silver, bronze. Each mode has
   its own steps, validated against its own surface — see apps/web/README.md for
   what passes and what the metaphor costs. */

:root {
  color-scheme: light;

  --surface-1: #fcfcfb;
  --page-plane: #f9f9f7;
  --text-primary: #0b0b0b;
  --text-secondary: #52514e;
  --text-muted: #898781;
  --gridline: #e1e0d9;
  --baseline: #c3c2b7;
  --hairline: rgba(11, 11, 11, 0.1);
  --danger: #d03b3b;

  --tier-0: #cfa81c; /* gold */
  --tier-1: #9aa3b0; /* silver */
  --tier-2: #a05f0e; /* bronze */

  /* Deliberately lighter and more transparent than silver, so the dominated
     cloud never reads as a tier. */
  --rest-mark: #d9d8d0;
  --rest-opacity: 0.55;
}

@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme='light'])) {
    color-scheme: dark;

    --surface-1: #1a1a19;
    --page-plane: #0d0d0d;
    --text-primary: #ffffff;
    --text-secondary: #c3c2b7;
    --text-muted: #898781;
    --gridline: #2c2c2a;
    --baseline: #383835;
    --hairline: rgba(255, 255, 255, 0.1);
    --danger: #e66767;

    --tier-0: #e8c33a;
    --tier-1: #c3cbd6;
    --tier-2: #cf8720;

    /* On a dark surface "recessive" means darker — silver is the light one here. */
    --rest-mark: #4c4c46;
    --rest-opacity: 0.75;
  }
}

:root[data-theme='dark'] {
  color-scheme: dark;

  --surface-1: #1a1a19;
  --page-plane: #0d0d0d;
  --text-primary: #ffffff;
  --text-secondary: #c3c2b7;
  --text-muted: #898781;
  --gridline: #2c2c2a;
  --baseline: #383835;
  --hairline: rgba(255, 255, 255, 0.1);
  --danger: #e66767;

  --tier-0: #e8c33a;
  --tier-1: #c3cbd6;
  --tier-2: #cf8720;

  --rest-mark: #4c4c46;
  --rest-opacity: 0.75;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--page-plane);
  color: var(--text-primary);
  font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 15px;
  line-height: 1.5;
}

.page {
  max-width: min(1760px, 96vw);
  margin: 0 auto;
  padding: 32px 24px 64px;
}

.page-header h1 {
  margin: 0 0 8px;
  font-size: 1.6rem;
  letter-spacing: -0.01em;
}

.subtitle {
  margin: 0;
  max-width: 68ch;
  color: var(--text-secondary);
}

/* ── Header links ────────────────────────────────────────────────────────── */

.header-links {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  margin: 14px 0 0;
  font-size: 0.92rem;
}

/* The bot and its caption share one line, and the wrapper is what keeps them
   together: loose in the column, the caption sat next to the source link and
   read as if it explained GitHub. It wraps rather than overflowing, since the
   two together are wider than a narrow column. */
.header-bot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
}

/* A link, drawn as one. It carries the weight and the only primary-text colour
   up here, which is what marks it as the call to action — a pill made it look
   like a button that would do something on the page rather than leave it. */
.header-link {
  color: var(--text-primary);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.header-link:hover {
  text-decoration-thickness: 2px;
}

.header-links-note {
  color: var(--text-secondary);
}

.header-source {
  color: var(--text-secondary);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.header-source:hover {
  color: var(--text-primary);
}

.meta {
  margin: 12px 0 0;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.meta.is-error,
.quota.is-error {
  color: var(--danger);
}

.quota {
  margin: 0 0 16px;
  padding: 10px 14px;
  font-size: 0.85rem;
  color: var(--text-secondary);
  background: var(--surface-1);
  border: 1px solid var(--hairline);
  border-radius: 8px;
}

/* ── Toolbar: what stays visible when the filters are folded away ────────── */

.toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 24px 0 0;
}

/* Wide layouts lay the filters out anyway, so the button has nothing to do. */
.filters-toggle {
  display: none;
}

/* ── Controls: a labelled rail of rows above everything they scope ───────── */

/* One row per purpose, its name in a left rail: "Filter models" changes which
   models are plotted, "Overlays" only what is drawn on top. The rail and the
   inline control labels keep every row to a single tier of text — group
   captions floating above a second deck of labels read as clutter. */
.controls {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  align-items: center;
  gap: 10px 20px;
  margin: 12px 0 16px;
  padding: 12px 16px;
  background: var(--surface-1);
  border: 1px solid var(--hairline);
  border-radius: 10px;
}

.control {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.8rem;
  color: var(--text-secondary);
}

.control select,
.control input[type='search'] {
  min-width: 168px;
  padding: 7px 10px;
  font: inherit;
  font-size: 0.9rem;
  color: var(--text-primary);
  background: var(--page-plane);
  border: 1px solid var(--hairline);
  border-radius: 7px;
}

/* The rail is this row's label, so the box takes the width instead. */
.control-search {
  flex: 1 1 240px;
  max-width: 420px;
}

.control-search input {
  width: 100%;
  min-width: 0;
}

.control-check {
  gap: 8px;
  min-height: 36px;
}

.control-view {
  gap: 4px;
  padding: 3px;
  background: var(--page-plane);
  border: 1px solid var(--hairline);
  border-radius: 8px;
}

.control-actions {
  gap: 8px;
  margin-left: auto;
}

.control-group-label {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.control-group-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 18px;
  min-width: 0;
}

.toggle,
.filters-toggle,
#usage,
#refresh,
.token-form button,
.picker-actions button {
  padding: 7px 14px;
  font: inherit;
  font-size: 0.85rem;
  color: var(--text-secondary);
  background: transparent;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}

.toggle.is-active {
  color: var(--text-primary);
  background: var(--surface-1);
  box-shadow: 0 1px 2px rgb(0 0 0 / 12%);
}

.filters-toggle,
#usage,
#refresh,
.token-form button {
  color: var(--text-primary);
  border: 1px solid var(--hairline);
}

#usage:disabled,
#refresh:disabled,
.token-form button:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Local development only: pasting the token the server printed. */

/* `display: flex` below beats the `hidden` attribute's UA rule, so the form
   would sit open on every page load without this — the same trap as
   `.picker-row`. */
.token-form[hidden] {
  display: none;
}

.token-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 0 16px;
  padding: 10px 14px;
  font-size: 0.85rem;
  color: var(--text-secondary);
  background: var(--surface-1);
  border: 1px solid var(--hairline);
  border-radius: 8px;
}

.token-form input {
  flex: 1;
  min-width: 180px;
  padding: 7px 10px;
  font: inherit;
  font-size: 0.85rem;
  color: var(--text-primary);
  background: var(--page-plane);
  border: 1px solid var(--hairline);
  border-radius: 6px;
}

/* ── Tier, creator and model pickers ─────────────────────────────────────── */

.picker {
  position: relative;
}

.picker summary {
  min-width: 200px;
  padding: 7px 10px;
  font-size: 0.9rem;
  color: var(--text-primary);
  background: var(--page-plane);
  border: 1px solid var(--hairline);
  border-radius: 7px;
  cursor: pointer;
  list-style: none;
}

.picker summary::-webkit-details-marker {
  display: none;
}

.picker summary::after {
  content: '▾';
  float: right;
  color: var(--text-muted);
}

.picker-panel {
  position: absolute;
  z-index: 20;
  top: calc(100% + 6px);
  left: 0;
  width: 280px;
  background: var(--surface-1);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgb(0 0 0 / 18%);
}

/* Sits above the buttons because it decides what they act on. Not auto-focused
   on open: on a phone that raises the keyboard over the list it is filtering. */
.control .picker-filter {
  display: block;
  width: calc(100% - 16px);
  min-width: 0;
  margin: 8px 8px 0;
}

.picker-actions {
  display: flex;
  gap: 4px;
  padding: 8px;
  border-bottom: 1px solid var(--hairline);
}

.picker-empty {
  margin: 0;
  padding: 4px 14px 14px;
  font-size: 0.85rem;
  color: var(--text-muted);
}

/* One line under the list saying what checking a row actually does — the tier
   and front-line pickers look identical and do two different things. */
.picker-hint {
  margin: 0;
  padding: 2px 14px 10px;
  font-size: 0.76rem;
  color: var(--text-muted);
}

.picker-list {
  /* Model names wrap to two or three lines, so a flat 320px showed four of them.
     Half the viewport is as much as a dropdown can take without covering what it
     is filtering. */
  max-height: min(50vh, 360px);
  overflow-y: auto;
  padding: 6px;
}

.picker-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  font-size: 0.87rem;
  color: var(--text-primary);
  border-radius: 6px;
  cursor: pointer;
}

/* `display: flex` above beats the hidden attribute's UA rule, so filtered-out
   rows need saying twice. */
.picker-row[hidden] {
  display: none;
}

.picker-row:hover {
  background: var(--page-plane);
}

.picker-row.is-disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

/* Model names are long and the maker is what tells two "mini"s apart, so the
   creator goes on a second line rather than competing for the first. */
.picker-text {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  overflow-wrap: anywhere;
}

.picker-note {
  font-size: 0.76rem;
  color: var(--text-muted);
}

.picker-row .count {
  flex: none;
  margin-left: auto;
  padding-left: 8px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.picker-row .swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: none;
}

/* A bar where the tier rows carry a dot: the row toggles a line, not models. */
.picker-row .swatch-line {
  width: 14px;
  height: 3px;
  border-radius: 2px;
  flex: none;
}

/* ── Cards ───────────────────────────────────────────────────────────────── */

.card {
  position: relative;
  padding: 20px;
  background: var(--surface-1);
  border: 1px solid var(--hairline);
  border-radius: 10px;
}

.card.is-loading {
  opacity: 0.55;
}

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  margin: 0 0 12px;
  padding: 0;
  list-style: none;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.legend li {
  display: flex;
  align-items: center;
  gap: 8px;
}

.legend .swatch {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  flex: none;
}

.legend .count {
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* A tier filtered out of the plot still shows its count — it exists, it is just
   not drawn. */
.legend li.is-hidden {
  opacity: 0.4;
  text-decoration: line-through;
  text-decoration-color: var(--text-muted);
}

/* The zoom buttons close the legend row: outside the plot, because any corner
   of the plot is data on some pair of axes. */
.legend-zoom {
  margin-left: auto;
  gap: 4px;
}

.legend-zoom button {
  min-width: 28px;
  padding: 2px 8px;
  font: inherit;
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--text-primary);
  background: transparent;
  border: 1px solid var(--hairline);
  border-radius: 6px;
  cursor: pointer;
}

.legend-zoom button:hover {
  border-color: var(--baseline);
}

.legend-zoom button:disabled {
  opacity: 0.45;
  cursor: default;
}

.legend-match::before {
  content: '';
  width: 12px;
  height: 12px;
  border: 2px solid var(--text-primary);
  border-radius: 50%;
  flex: none;
}

/* The chart is sized in CSS pixels and re-rendered on resize, so it fills wide
   displays instead of topping out at a fixed width. */
.chart {
  height: clamp(560px, 72vh, 960px);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) auto;
  gap: 4px;
}

.chart-axis-y {
  grid-column: 1;
  grid-row: 1;
  align-self: center;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
}

.chart-plot {
  grid-column: 2;
  grid-row: 1;
  min-width: 0;
  min-height: 0;
  overflow-x: auto;
  overflow-y: hidden;
}

.chart-axis-x {
  grid-column: 2;
  grid-row: 2;
}

.chart svg {
  display: block;
  /* One finger still scrolls the page; pinches are the chart's. `pan-y` also
     bars the browser from pinch-zooming the page from on top of the plot. */
  touch-action: pan-y;
}

/* Zoomed, dragging the plot pans the window. */
.chart svg.is-zoomed .hover-surface {
  cursor: grab;
}

.chart svg.is-panning .hover-surface {
  cursor: grabbing;
}

/* A live gesture only transforms the layer as a preview: names would stretch
   with it, so they sit the moment out. The re-render brings them back. */
.chart svg.is-zooming .mark-label {
  display: none;
}

.chart-empty {
  margin: 48px 0;
  text-align: center;
  color: var(--text-muted);
}

/* Only rendered when a shortened name is actually on the plot, so it stays a
   footnote about what the reader can see rather than a standing disclaimer. */
.chart-note {
  margin: 6px 0 0;
  color: var(--text-muted);
  font-size: 0.75rem;
  line-height: 1.35;
}

/* ── Chart internals ─────────────────────────────────────────────────────── */

.grid line {
  stroke: var(--gridline);
  stroke-width: 1;
}

.axis line {
  stroke: var(--baseline);
  stroke-width: 1;
}

.tick-label text {
  fill: var(--text-muted);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.chart-axis-label {
  overflow: hidden;
  color: var(--text-secondary);
  font-size: 0.78rem;
  line-height: 1.3;
  text-align: center;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mark-rest circle {
  fill: var(--rest-mark);
  opacity: var(--rest-opacity);
}

.mark-tier circle {
  stroke: var(--surface-1);
  stroke-width: 2;
}

.front-line {
  fill: none;
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.tier-0 circle { fill: var(--tier-0); }
.tier-1 circle { fill: var(--tier-1); }
.tier-2 circle { fill: var(--tier-2); }

.front-line.tier-0 { stroke: var(--tier-0); }
.front-line.tier-1 { stroke: var(--tier-1); }
.front-line.tier-2 { stroke: var(--tier-2); }

/* Search emphasis: matches keep their tier colour and gain an ink ring;
   everything else recedes. It recedes rather than disappears — a match is only
   informative against the field it sits in, so the fronts stay readable behind
   it (2026-08-15: 0.12 erased them). The two figures differ so the tiers still
   read as ranked above the dominated cloud while dimmed. Identity is never
   carried by the ring alone — the legend and the table stay in sync. */
.is-searching .mark-rest circle {
  opacity: 0.22;
}

.is-searching .mark-tier circle {
  opacity: 0.38;
}

.is-searching .front-line {
  opacity: 0.42;
}

.is-searching circle.is-match {
  opacity: 1;
  stroke: var(--text-primary);
  stroke-width: 2;
}

.is-searching .mark-rest circle.is-match {
  r: 5;
}

/* Names sit clear of their mark with a hairline pointing back at it, so the
   label never covers the datum it describes. The halo is a stroke painted
   under the fill: labels cross the dominated cloud and would otherwise be
   unreadable over it. */
.mark-label {
  pointer-events: none;
}

.mark-label text {
  fill: var(--text-primary);
  font-size: 11px;
  paint-order: stroke;
  stroke: var(--surface-1);
  stroke-width: 3.5;
  stroke-linejoin: round;
}

/* Clearly darker than the grid it crosses. At --baseline it read as one more
   gridline, which is exactly what a pointer must not look like. */
.mark-label line {
  stroke: var(--text-muted);
  stroke-width: 1;
  stroke-linecap: round;
}

/* Names that were already on the plot when the search started stay on it and
   recede, like the marks and the front lines they annotate: the match reads
   against a labelled field instead of an empty one. Kept a little above the
   tiers' 0.38 because text has far less ink than a dot to be read by, and its
   halo fades with it. */
.mark-label .is-dimmed {
  opacity: 0.45;
}

.hover-ring {
  fill: none;
  stroke: var(--text-primary);
  stroke-width: 1.5;
  pointer-events: none;
}

/* ── Tooltip ─────────────────────────────────────────────────────────────── */

.tooltip {
  position: fixed;
  z-index: 30;
  min-width: 200px;
  max-width: min(280px, calc(100vw - 24px));
  padding: 10px 12px;
  pointer-events: none;
  background: var(--surface-1);
  border: 1px solid var(--hairline);
  border-radius: 8px;
  box-shadow: 0 6px 20px rgb(0 0 0 / 18%);
  font-size: 0.82rem;
}

/* The plot may only have room for `Claude Opus 5 (e)`; this is where the whole
   configuration is spelled out, so it wraps rather than stretching the card. */
.tooltip .tip-name {
  font-weight: 600;
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

.tooltip .tip-creator {
  color: var(--text-muted);
  margin-bottom: 6px;
}

.tooltip .tip-tier {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
  color: var(--text-secondary);
}

.tooltip .swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: none;
}

.tooltip dl {
  display: grid;
  grid-template-columns: auto auto;
  gap: 2px 14px;
  margin: 0;
}

.tooltip dt {
  color: var(--text-secondary);
}

.tooltip dd {
  margin: 0;
  text-align: right;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

/* ── Table view ──────────────────────────────────────────────────────────── */

#table-card {
  overflow-x: auto;
}

.data-table {
  width: 100%;
  min-width: 640px;
  border-collapse: collapse;
  font-size: 0.88rem;
}

.data-table caption {
  margin-bottom: 12px;
  text-align: left;
  color: var(--text-secondary);
}

.data-table th,
.data-table td {
  padding: 8px 10px;
  text-align: left;
  border-bottom: 1px solid var(--hairline);
}

.data-table th {
  color: var(--text-secondary);
  font-weight: 600;
}

/* Only the headings resist wrapping — a wrapped heading costs two rows of
   height for every row of the table. Model names must stay wrappable, or the
   column's minimum width is the longest name and it pushes the numbers off a
   narrow screen. */
.data-table thead th {
  white-space: nowrap;
}

/* Abbreviated headings should not look like a spelling mistake. */
.data-table abbr {
  text-decoration: none;
  cursor: help;
}

.data-table tbody th {
  color: var(--text-primary);
  font-weight: 500;
}

.data-table tr.is-match {
  background: var(--page-plane);
  box-shadow: inset 3px 0 0 var(--text-primary);
}

.data-table .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.data-table .swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: none;
}

/* Three characters wide and no wider: `width: 1%` makes the column give every
   pixel it does not need back to the name beside it. Muted, because the letter
   points at the plot rather than saying anything about the model. */
.data-table .variant {
  width: 1%;
  white-space: nowrap;
  color: var(--text-muted);
}

.tier-cell {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.page-footer {
  margin-top: 24px;
  font-size: 0.82rem;
  color: var(--text-muted);
}

.page-footer a {
  color: inherit;
}

.page-footer .meta {
  margin-bottom: 0;
}

@media (max-width: 720px) {
  .page {
    max-width: 100%;
    padding: 14px 6px 32px;
  }

  .page-header h1 {
    margin-bottom: 4px;
    font-size: 1.35rem;
  }

  .subtitle {
    font-size: 0.9rem;
    line-height: 1.4;
  }

  .meta {
    margin-top: 6px;
  }

  /* The links keep their weight and their explanatory line: it is what tells a
     first-time reader what the bot is, and it wraps to its own row here. */
  .header-links {
    gap: 8px;
    margin-top: 10px;
    font-size: 0.85rem;
  }

  .toolbar {
    margin-top: 12px;
    gap: 6px;
  }

  .toolbar .control-view {
    flex: 1;
  }

  .toolbar .control-view .toggle {
    flex: 1;
  }

  .filters-toggle {
    display: block;
    flex: 1;
  }

  /* Folded away by default: a phone should open on the data, not on the
     filters. `is-open` is set by the toggle above. The rail cannot afford a
     column of its own here, so its labels become section lines and each row
     goes back to the two-column rhythm with labels above the widgets. */
  .controls {
    display: none;
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
    gap: 8px;
    margin: 8px 0 10px;
    padding: 10px;
    border-radius: 8px;
  }

  .controls.is-open {
    display: grid;
  }

  .controls .control {
    flex-direction: column;
    align-items: stretch;
    min-width: 0;
    gap: 4px;
  }

  .controls .control-check {
    flex-direction: row;
    align-items: center;
  }

  .control-group-label {
    margin-top: 4px;
  }

  .control-group-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: end;
    gap: 10px 8px;
  }

  .control select,
  .control input[type='search'],
  .picker summary {
    width: 100%;
    min-width: 0;
  }

  .control-search,
  .control-actions {
    grid-column: 1 / -1;
    max-width: none;
  }

  .control-view,
  .control-actions {
    display: flex;
  }

  .control-view .toggle,
  .control-actions button {
    flex: 1;
  }

  .controls .control-actions {
    flex-direction: row;
    margin-left: 0;
  }

  .picker-panel {
    width: min(280px, calc(100vw - 34px));
  }

  /* Every pixel here is a pixel the plot does not get. */
  .card {
    padding: 6px 4px;
    border-radius: 8px;
  }

  .legend {
    gap: 4px 12px;
    margin-bottom: 6px;
    font-size: 0.76rem;
    line-height: 1.35;
  }

  .legend li {
    gap: 5px;
  }

  .legend .swatch,
  .legend-match::before {
    width: 9px;
    height: 9px;
  }

  .chart {
    height: clamp(300px, 58svh, 420px);
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
    gap: 2px;
  }

  .chart-axis-y {
    grid-column: 1;
    grid-row: 1;
    writing-mode: horizontal-tb;
    transform: none;
  }

  .chart-plot {
    grid-column: 1;
    grid-row: 2;
  }

  .chart-axis-x {
    grid-column: 1;
    grid-row: 3;
  }

  .chart-axis-label {
    font-size: 0.7rem;
  }

  /* The table earns its width back from the gaps between fields. No minimum
     width either — the leading columns are the ones worth reading, and they
     land on screen instead of behind a scroll. */
  .data-table {
    min-width: 0;
    font-size: 0.78rem;
  }

  .data-table caption {
    margin-bottom: 8px;
    font-size: 0.74rem;
  }

  .data-table th,
  .data-table td {
    padding: 5px 3px;
  }

  .data-table th:first-child,
  .data-table td:first-child {
    padding-left: 1px;
  }

  .data-table th:last-child,
  .data-table td:last-child {
    padding-right: 1px;
  }

  /* Left to itself the model column collapses to its narrowest word and every
     row grows six lines tall. A floor keeps rows short; the columns past
     cost/task are the ones that pay for it with a little sideways scroll. */
  .data-table tbody th {
    min-width: 10em;
  }

  .tier-cell {
    gap: 4px;
  }

  .data-table .creator {
    color: var(--text-secondary);
  }

  .page-footer {
    margin-top: 16px;
  }
}

@media (orientation: landscape) and (max-height: 500px) and (max-width: 960px) {
  .page {
    max-width: 100%;
    padding: 8px 8px 24px;
  }

  .page-header h1 {
    margin-bottom: 2px;
    font-size: 1.25rem;
  }

  .subtitle {
    max-width: none;
    font-size: 0.82rem;
    line-height: 1.3;
  }

  .meta {
    margin-top: 3px;
  }

  .header-links {
    gap: 6px;
    margin-top: 6px;
    font-size: 0.82rem;
  }

  .header-links-note {
    display: none;
  }

  .toolbar {
    margin-top: 8px;
    gap: 6px;
  }

  .filters-toggle {
    display: block;
  }

  /* Vertical space is the scarce one here, so the filters fold away as well,
     and the rail's labels become section lines over four-column rows. */
  .controls {
    display: none;
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
    gap: 6px;
    margin: 8px 0;
    padding: 8px;
  }

  .controls.is-open {
    display: grid;
  }

  .controls .control {
    flex-direction: column;
    align-items: stretch;
    min-width: 0;
    gap: 4px;
  }

  .controls .control-check {
    flex-direction: row;
    align-items: center;
  }

  .control-group-row {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    align-items: end;
    gap: 6px;
  }

  .control-search {
    grid-column: span 2;
    max-width: none;
  }

  .control select,
  .control input[type='search'],
  .picker summary {
    width: 100%;
    min-width: 0;
  }

  .control-view,
  .control-actions {
    display: flex;
  }

  .control-view .toggle,
  .control-actions button {
    min-width: 0;
    flex: 1;
    padding-inline: 5px;
  }

  .controls .control-actions {
    flex-direction: row;
    grid-column: span 2;
    margin-left: 0;
  }

  .card {
    padding: 6px 4px;
  }

  .legend {
    gap: 3px 10px;
    margin-bottom: 4px;
    font-size: 0.72rem;
  }

  .chart {
    height: 300px;
  }

  .data-table {
    min-width: 0;
    font-size: 0.8rem;
  }

  .data-table th,
  .data-table td {
    padding: 5px 6px;
  }

  .page-footer {
    margin-top: 12px;
  }
}
