/* knotmeter — continuous benchmark of DO-hosted open-weight models.

   The two :root blocks below are copied VERBATIM from ~/github/serverless-inference so a
   diff against the sibling stays meaningful: light is a pure token flip, nothing more.
   Everything after them is knotmeter's own components (tokens only was the explicit
   decision -- no borrowed components).

   Two rules this file keeps, both load-bearing:

   1. NO @media queries, matching the sibling. Every colour comes from a token, and the
      layout flexes with relative units, clamp() and grid auto-fit instead of breakpoints.
   2. NO literal colours outside the two :root blocks. `grep -nE '#[0-9a-fA-F]{3,8}|rgba?\(' `
      may only hit those blocks -- any other hit is a light-theme bug waiting to happen,
      because a hard-coded colour cannot flip. That grep is Phase A check 10. */

:root {
  --bg: #000;
  --dim-border: #333;
  --dim-text: #6b6b6b;
  --mid-border: #5e5e5e;
  --mid-text: #a9a9a9;
  --bright: #ffffff;
  --line: #2a2a2a;
  --glow: rgba(255, 255, 255, .35);
  --tip-bg: #111111;
  --pos: #6ee7a8;
  --neg: #ff8f8f;
  --logo-filter: invert(1);
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
}

:root[data-theme="light"] {
  --bg: #ffffff;
  --dim-border: #d9d9d9;
  --dim-text: #8a8a8a;
  --mid-border: #b3b3b3;
  --mid-text: #555555;
  --bright: #111111;
  --line: #ececec;
  --glow: rgba(0, 0, 0, .22);
  --tip-bg: #ffffff;
  --pos: #128a5b;
  --neg: #c0362c;
  --logo-filter: none;
}

/* ───────────────────────── base (house style, from the sibling) ───────────────────────── */
* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--mid-text);
  font-family: var(--mono);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* live theme toggle — fixed top-right, icon only; injected by theme.js */
.theme-toggle {
  position: fixed; top: 14px; right: 16px; z-index: 50;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; padding: 4px; line-height: 0;
  color: var(--mid-text); cursor: pointer;
  transition: color .15s;
}
.theme-toggle:hover { color: var(--bright); }

/* main is the layout engine: vertical rhythm comes from its gap, never child margins */
main {
  max-width: 1180px;
  margin: 0 auto;
  padding: 48px 28px 110px;
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.title {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  font-size: clamp(44px, 8vw, 92px);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--bright);
  margin: 0 0 16px;
}
.lead { color: var(--mid-text); margin: -10px 0 0; }
.note { color: var(--dim-text); font-size: 12px; margin: 0; }
/* Kept although only ONE .note is left, now that the partial-history footnote is gone from
   the page and the stamp stands alone. It is here as a tripwire for the next note: this was
   -18px, which is exactly one 12px/1.5 line box, so the moment two notes both had text the
   second printed directly on top of the first. The negative margin was compensating for
   main's 26px flex gap, which never applied -- these are plain blocks inside a <section>. */
.note + .note { margin-top: 2px; }

h2 {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  font-size: 15px; font-weight: 600; letter-spacing: .01em;
  color: var(--bright); margin: 0 0 -14px;
}

a { color: var(--mid-text); text-decoration: none; border-bottom: 1px solid var(--dim-border); }
a:hover { color: var(--bright); border-bottom-color: var(--mid-border); }

/* ───────────────────────── knot line (the gauge) ───────────────────────── */
/* One shared axis for all 26 models. It is a LINKED VIEW, not an ornament: hovering a
   row glows its tick and vice versa, which answers "how fast is fast *here*?" -- the one
   question a table of per-row numbers cannot. */
.knotline { display: flex; flex-direction: column; gap: 6px; }
/* height matches the viewBox height, so the vertical scale is 1:1 and an 11px label is
   11px -- preserveAspectRatio="none" stretches x only. */
.knotline svg { width: 100%; height: 132px; display: block; }
/* vector-effect keeps 1px strokes exactly 1px under preserveAspectRatio="none", with no
   resize handler and no @media query. Without it the non-uniform scale smears them. */
.knotline svg line, .knotline svg path, .knotline svg polyline {
  vector-effect: non-scaling-stroke;
}
.kl-axis { stroke: var(--dim-border); stroke-width: 1; }
.kl-tick { stroke: var(--mid-border); stroke-width: 1; transition: stroke .15s; }
.kl-tick.on { stroke: var(--bright); stroke-width: 2; }
/* dashed means missing, the same semantic the sibling uses for a fallback node */
.kl-tick.nodata { stroke: var(--dim-border); stroke-dasharray: 2 3; }
.kl-grid { stroke: var(--line); stroke-width: 1; }
/* The pointer target, not the tick. Ticks are 1px because 26 of them are a distribution
   rather than 26 bars, and a 1px stroke is something you aim at rather than hover -- so
   each one gets an 8px transparent twin on top (8px and not 8 viewBox units,
   because non-scaling-stroke above pins stroke widths to device pixels). `pointer-events: stroke` is the
   load-bearing word: a transparent stroke is not "painted", so the default visiblePainted
   would never fire and the target would be as thin as the line it replaces. */
.kl-hit { stroke: transparent; stroke-width: 8; pointer-events: stroke; cursor: default; }
/* the no-data stubs sit 4 units apart, so a wide target would swallow its neighbours */
.kl-hit.narrow { stroke-width: 4; }
/* labels must never intercept the pointer: the hovered name is drawn where the pointer is
   heading, and one hit on it would read as "left the gauge" and clear the name it just set */
.kl-label { fill: var(--dim-text); font-family: var(--mono); font-size: 11px;
  pointer-events: none; }
.kl-label.edge { fill: var(--mid-text); }
.kl-label.on { fill: var(--bright); }
.kl-caption { color: var(--dim-text); font-size: 12px; }

/* ───────────────────────── metric pills ───────────────────────── */
/* Three metrics x 26 models x 3 windows is unreadable as columns. The switch divides the
   visible numbers by three and drives the sort, the gauge scale and the sparklines. */
.pills { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.pill {
  font-family: var(--mono); font-size: 13px;
  background: transparent; color: var(--mid-text);
  border: 1px solid var(--dim-border); border-radius: 2px;
  padding: 6px 13px; cursor: pointer;
  transition: color .15s, border-color .15s;
}
.pill:hover { color: var(--bright); border-color: var(--mid-border); }
.pill[aria-pressed="true"] {
  color: var(--bright); border-color: var(--mid-border);
  box-shadow: 0 0 0 1px var(--glow);
}
.pill-hint { color: var(--dim-text); font-size: 12px; margin-left: 4px; }

/* ───────────────────────── banner (degraded states) ───────────────────────── */
.banner {
  border: 1px solid var(--mid-border); border-radius: 2px;
  padding: 11px 14px; color: var(--mid-text); font-size: 13px;
}
/* dashed carries "incomplete / not yet trustworthy" throughout this page */
.banner.warn { border-style: dashed; border-color: var(--bright); color: var(--bright); }
.banner.bad { border-color: var(--neg); color: var(--neg); }

/* ───────────────────────── the board ───────────────────────── */
.board-wrap { overflow-x: auto; }
.board {
  width: 100%; border-collapse: collapse;
  font-variant-numeric: tabular-nums;   /* so digits line up column-wise between rows */
}
.board th, .board td { text-align: right; padding: 9px 12px; white-space: nowrap; }
/* only the model cell is left-aligned. This was nth-child(-n+2) when a "#" column sat in
   front of it; with that column gone the same selector would have left-aligned "now"
   instead, which is the kind of breakage removing a column causes without erroring. */
.board th:first-child, .board td:first-child { text-align: left; }
.board thead th {
  color: var(--dim-text); font-weight: 400; font-size: 12px; letter-spacing: .05em;
  border-bottom: 1px solid var(--mid-border);
}
.board thead th.sortable { cursor: pointer; }
.board thead th.sortable:hover { color: var(--bright); }
.board thead th.sorted { color: var(--bright); }
.board tbody td { color: var(--mid-text); border-bottom: 1px solid var(--line); }
.board tbody tr.row { cursor: pointer; }
.board tbody tr.row:hover td { color: var(--bright); }
.board tbody tr.row.on td { color: var(--bright); }

/* ── the model-name filter ──
   `.mdl-h` is the positioning context and `.finput` is ABSOLUTE inside it, which is the
   whole design. The model column is sized by the widest thing in it and it is the first
   column, so an in-flow 150px input would widen that column and shove every number on the
   page sideways the instant the funnel was clicked. Out of flow, opening the box changes no
   dimension of the cell in any direction -- nothing below the header moves by a pixel. */
.mdl-h { position: relative; display: inline-flex; align-items: center; gap: 6px; }
.fbtn {
  display: inline-flex; align-items: center; line-height: 0; padding: 2px;
  background: transparent; border: 0; cursor: pointer; color: var(--dim-text);
  transition: color .15s;
}
.fbtn:hover { color: var(--bright); }
/* a filter in effect is the reason the table is short, so the funnel has to say so -- it is
   the only thing on screen that can, now that the row count is whatever the reader typed */
.fbtn.active { color: var(--bright); }
.finput {
  position: absolute; left: 100%; top: 50%; transform: translateY(-50%);
  margin-left: 8px; z-index: 2; width: 150px;
  font-family: var(--mono); font-size: 12px; letter-spacing: normal;
  background: var(--tip-bg); color: var(--bright);
  border: none; border-radius: 2px; padding: 2px 4px;
}
.finput::placeholder { color: var(--dim-text); }
.finput:focus { outline: none; box-shadow: 0 0 0 1px var(--glow); }
/* filtered down to nothing. left-aligned by the first-child rule above, like the model cell */
.empty { color: var(--dim-text); padding: 16px 12px; }

.mdl-slug { color: var(--bright); }
.mdl-sub { color: var(--dim-text); font-size: 12px; }
/* dashed left border marks a model with no usable data in the current window */
.mdl.nodata { border-left: 1px dashed var(--mid-border); padding-left: 10px; }

.val-now { color: var(--bright); font-size: 17px; }
.val-win { font-size: 13px; }
.delta { font-size: 11px; margin-left: 5px; }
.delta.pos { color: var(--pos); }
.delta.neg { color: var(--neg); }
/* the sort-direction arrow, borrowing .delta's size and offset so the two glyphs on this
   page match, and deliberately NOT its colour: .delta is coloured because a move up or down
   is good or bad news, whereas an ordering is neither. Colour comes from the th, which means
   the arrow brightens with the sorted header for free and dims if it ever appears elsewhere. */
.sarrow { font-size: 11px; margin-left: 5px; }
.dash { color: var(--dim-text); }
/* the latest run failed: show the error kind, NEVER a stale number dressed as "now" */
.err { color: var(--neg); font-size: 13px; }
.spark { color: var(--mid-text); line-height: 0; }
.spark svg { display: block; }

/* ───────────────────────── expanded detail ───────────────────────── */
.detail > td {
  padding: 4px 12px 22px; border-bottom: 1px solid var(--line);
  /* Undo the board's nowrap, which is INHERITED into this cell.

     `white-space: nowrap` is right for the data columns -- it keeps "189.5 tok/s" from
     breaking across two lines mid-figure. But it applies to every descendant of every td,
     and the tiles below are full sentences: "3 of 2880 expected sweeps reached this model.
     this is knotmeter's gap, not the endpoint's." refused to wrap, ran out of its tile and
     printed across the two tiles beside it. The honesty tiles were the worst hit, which is
     the wrong thing to lose -- they are the ones that qualify the numbers.

     overflow-wrap: anywhere rather than break-word because it also shrinks the intrinsic
     min-content width, which is what stops a long unbroken token (a hugging face id like
     mistralai/Ministral-3-14B-Instruct-2512) from pushing its tile wider than its grid
     track and out over the next one. */
  white-space: normal;
  overflow-wrap: anywhere;
}
.tiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px;
}
.tile {
  border: 1px solid var(--dim-border); border-radius: 2px; padding: 11px 13px;
  display: flex; flex-direction: column; gap: 5px; min-width: 0;
}
.tile.wide { grid-column: span 2; }
.t-label { color: var(--dim-text); font-size: 12px; letter-spacing: .06em; }
.t-value { color: var(--bright); font-size: 22px; line-height: 1; word-break: break-word; }
.t-value.text { font-size: 15px; }
.t-value .unit { font-size: 13px; color: var(--mid-text); }
.t-sub { color: var(--mid-text); font-size: 12px; letter-spacing: .02em; }
.t-sub.warn { color: var(--neg); }
/* the windows inside one metric tile -- 24h / 7d / 30d abreast, where each was its own
   tile. auto-fit rather than a fixed 3 columns so the strip folds to one column when the
   grid track is narrow, instead of overflowing its own tile. */
.wins { display: grid; grid-template-columns: repeat(auto-fit, minmax(82px, 1fr)); gap: 11px; }
.win { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.w-lab { color: var(--dim-text); font-size: 11px; letter-spacing: .05em; }
.w-val { color: var(--bright); font-size: 19px; line-height: 1; }
.w-val .unit { font-size: 12px; color: var(--mid-text); }
.w-sub { color: var(--mid-text); font-size: 11px; }
.chart { margin-top: 14px; color: var(--mid-text); }
.chart svg { width: 100%; height: 180px; display: block; }
.chart svg polyline, .chart svg line, .chart svg path { vector-effect: non-scaling-stroke; }
.ch-axis { stroke: var(--dim-border); stroke-width: 1; }
.ch-grid { stroke: var(--line); stroke-width: 1; }
.ch-label { fill: var(--dim-text); font-family: var(--mono); font-size: 11px; }
.ch-line { fill: none; stroke: currentColor; stroke-width: 1.5; }
.ch-gap { fill: none; stroke: var(--dim-border); stroke-width: 1; stroke-dasharray: 3 3; }
.ch-fail { stroke: var(--neg); stroke-width: 1; }

/* ───────────────────────── method ───────────────────────── */
.method { color: var(--mid-text); font-size: 13px; display: flex; flex-direction: column; gap: 9px; }
.method p { margin: 0; }
.method code { color: var(--bright); }
.method dl { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; }
.method dt { color: var(--bright); }
.method dd { margin: 0; }

/* ───────────────────────── cellar / footer (from the sibling) ───────────────────────── */
.cellar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 10;
  width: 100%;
  padding: 16px 28px;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 16px;
  color: var(--dim-text);
}
.cellar a { display: inline-flex; border-bottom: 0; }
.cellar .do-logo {
  height: 22px;
  width: auto;
  filter: var(--logo-filter);   /* invert to white on dark; original black on light */
  opacity: .8;
  transition: opacity .15s;
}
.cellar a:hover .do-logo { opacity: 1; }
