/* ==========================================================================
   LLM WORLD — swim-lane release timeline
   Dual-theme instrument panel. No external assets, system fonts only.

   Theme model (three states):
     - no [data-theme]  → follow OS via prefers-color-scheme
     - [data-theme=light] / [data-theme=dark] → explicit, wins over OS
   Every colour token is defined on bare :root (the light values), then
   redefined in the OS-dark media block and again under [data-theme=dark].
   ========================================================================== */

:root {
  color-scheme: light;

  /* surfaces */
  --bg: #f7f8fa;
  --panel: #ffffff;
  --panel-2: #f1f3f7;
  --panel-3: #e9ecf2;
  --line: rgba(15, 23, 42, 0.12);
  --line-soft: rgba(15, 23, 42, 0.06);

  /* ink */
  --text: #0f172a;
  --text-dim: #475569;
  /* 5.07:1 on --bg, 5.38:1 on --panel. Was #77839a at 3.59:1 — a fail, and
     this token is the colour of 101 rules, 83 of them under 18.5px. */
  --muted: #5f6b80;

  /* accent */
  /* 5.22:1 on --bg. Was #2f6fe0 at 4.42:1, which put every body link on
     every static page marginally under AA in light mode. */
  --accent: #2563d1;
  --accent-ink: #ffffff;
  --accent-soft: rgba(47, 111, 224, 0.12);
  --accent-line: rgba(47, 111, 224, 0.4);
  --warn: #9a6400;

  /* ambient wash */
  --grid-line: rgba(15, 23, 42, 0.035);
  --card-sheen: rgba(15, 23, 42, 0.02);
  --shadow-card: 0 10px 24px rgba(15, 23, 42, 0.10);
  --shadow-modal: 0 30px 80px rgba(15, 23, 42, 0.28);
  --scrim: rgba(15, 23, 42, 0.42);

  /* company hues — light mode.
     16 brand-anchored slots. Colour is a SECONDARY channel here: the company
     name is always rendered beside the swatch (card, legend chip, dialog), so
     identity never rests on hue alone. See README for the measured CVD report. */
  --c-ai21:       #b2332f;
  --c-anthropic:  #c2643a;
  --c-mistral:    #a85c00;
  --c-alibaba:    #7d5200;
  --c-amazon:     #6f6a00;
  --c-nvidia:     #4e7e27;
  --c-bigscience: #00874a;
  --c-ai2:        #2f8a3f;
  --c-bfl:        #1f7a5c;
  --c-bytedance:  #0f7fa8;
  --c-liquid:     #42607f;
  --c-minimax:    #4353cc;
  --c-openai:     #00786a;
  --c-microsoft:  #0d8494;
  --c-xai:        #0079b8;
  --c-google:     #2f6fe0;
  --c-deepseek:   #5b3fc9;
  --c-meta:       #8b45c4;
  --c-moonshot:   #a86bb8;

  --c-tencent:    #b635af;
  --c-cloudflare: #c25e0c;
  --c-perplexity: #1f7f8a;
  --c-zhipu:      #c02f92;
  --c-cohere:     #c04a6e;
  --c-other:      #5b6779;

  /* Semantic states. Separate from the company hues on purpose: eight rules
     borrowed --c-bigscience for "verified", --c-anthropic for "correction"
     and --c-ai2 for "improved", which made a lab's brand colour the MEANING
     of a provenance state — re-hue that lab and the meaning moved with it.
     Deliberately close to those hues, so nothing looks different. */
  --state-ok:     #1f7a4d;
  --state-gap:    #8a5a00;
  --state-fix:    #b4562c;

  /* geometry & type */
  --radius: 12px;
  --radius-sm: 8px;
  /* The pill shape, written as a 999px literal 19 times before this. */
  --radius-pill: 999px;
  --font: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --shell: 1360px;
  --pad: clamp(14px, 3vw, 24px);
  --ease: cubic-bezier(0.22, 0.85, 0.3, 1);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --bg: #0e131c;
    --panel: #151c28;
    --panel-2: #1a2331;
    --panel-3: #212c3d;
    --line: rgba(148, 163, 184, 0.16);
    --line-soft: rgba(148, 163, 184, 0.08);

    --text: #e9eef7;
    --text-dim: #a9b4c8;
    --muted: #8794aa;

    --accent: #6aa5ff;
    --accent-ink: #06101f;
    --accent-soft: rgba(106, 165, 255, 0.16);
    --accent-line: rgba(106, 165, 255, 0.45);
    --warn: #f2c879;

    --grid-line: rgba(148, 163, 184, 0.032);
    --card-sheen: rgba(255, 255, 255, 0.028);
    --shadow-card: 0 10px 26px rgba(0, 0, 0, 0.45);
    --shadow-modal: 0 30px 80px rgba(0, 0, 0, 0.6);
    --scrim: rgba(4, 6, 10, 0.7);

    --c-ai21:       #e05c56;
    --c-anthropic:  #f0a07c;
    --c-mistral:    #d06d00;
    --c-alibaba:    #c9922e;
    --c-amazon:     #a89a00;
    --c-nvidia:     #8abf4a;
    --c-bigscience: #22b070;
    --c-ai2:        #4fbf68;
    --c-bfl:        #3fc79a;
    --c-bytedance:  #38b6dd;
    --c-liquid:     #7fa8cc;
    --c-minimax:    #7b8cf5;
    --c-openai:     #00b39b;
    --c-microsoft:  #0098b8;
    --c-xai:        #37b6ef;
    --c-google:     #4d9bff;
    --c-deepseek:   #7c5ce0;
    --c-meta:       #a86ad6;
    --c-moonshot:   #c98fd8;

    --c-tencent:    #d968d4;
    --c-cloudflare: #f6a04d;
    --c-perplexity: #4cc0cc;
    --c-zhipu:      #dc5cb0;
    --c-cohere:     #f07d9e;
    --c-other:      #94a3b8;
    --state-ok:     #4fbf88;
    --state-gap:    #d9a441;
    --state-fix:    #f0906a;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --bg: #0e131c;
  --panel: #151c28;
  --panel-2: #1a2331;
  --panel-3: #212c3d;
  --line: rgba(148, 163, 184, 0.16);
  --line-soft: rgba(148, 163, 184, 0.08);

  --text: #e9eef7;
  --text-dim: #a9b4c8;
  --muted: #8794aa;

  --accent: #6aa5ff;
  --accent-ink: #06101f;
  --accent-soft: rgba(106, 165, 255, 0.16);
  --accent-line: rgba(106, 165, 255, 0.45);
  --warn: #f2c879;

  --grid-line: rgba(148, 163, 184, 0.032);
  --card-sheen: rgba(255, 255, 255, 0.028);
  --shadow-card: 0 10px 26px rgba(0, 0, 0, 0.45);
  --shadow-modal: 0 30px 80px rgba(0, 0, 0, 0.6);
  --scrim: rgba(4, 6, 10, 0.7);

  --c-ai21:       #e05c56;
  --c-anthropic:  #f0a07c;
  --c-mistral:    #d06d00;
  --c-alibaba:    #c9922e;
  --c-amazon:     #a89a00;
  --c-nvidia:     #8abf4a;
  --c-bigscience: #22b070;
  --c-ai2:        #4fbf68;
  --c-bfl:        #3fc79a;
  --c-bytedance:  #38b6dd;
  --c-liquid:     #7fa8cc;
  --c-minimax:    #7b8cf5;
  --c-openai:     #00b39b;
  --c-microsoft:  #0098b8;
  --c-xai:        #37b6ef;
  --c-google:     #4d9bff;
  --c-deepseek:   #7c5ce0;
  --c-meta:       #a86ad6;
  --c-moonshot:   #c98fd8;

  --c-tencent:    #d968d4;
  --c-cloudflare: #f6a04d;
  --c-perplexity: #4cc0cc;
  --c-zhipu:      #dc5cb0;
  --c-cohere:     #f07d9e;
  --c-other:      #94a3b8;
  --state-ok:     #4fbf88;
  --state-gap:    #d9a441;
  --state-fix:    #f0906a;
}

* { box-sizing: border-box; }

/* Components below set explicit `display`, which would otherwise beat the UA
   rule for the `hidden` attribute. Keep `hidden` authoritative. */
[hidden] { display: none !important; }

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

/* Chart paper, not atmosphere. The two ambient radial glows that used to sit
   here were generic "AI product" wash; a plain ruled grid actually suits a
   timeline, so the grid stays and the glows are gone. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 46px 46px, 46px 46px;
}

::selection { background: var(--accent-soft); }

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--muted) 45%, transparent);
  border-radius: 8px;
  border: 3px solid var(--bg);
}
::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--muted) 70%, transparent); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 5px;
}

code, kbd {
  font-family: var(--mono);
  font-size: 0.88em;
  color: var(--text-dim);
  background: var(--panel-2);
  border: 1px solid var(--line-soft);
  padding: 1px 4px;
  border-radius: 5px;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 50%;
  top: 8px;
  translate: -50% -200%;
  z-index: 100;
  padding: 8px 16px;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  border-radius: var(--radius-pill);
  text-decoration: none;
  transition: translate 0.2s var(--ease);
}
.skip-link:focus-visible { translate: -50% 0; }

.shell {
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--pad);
}

/* --------------------------------------------------------------------------
   Header
   -------------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(1.5);
  backdrop-filter: blur(16px) saturate(1.5);
  border-bottom: 1px solid var(--line);
}

.header-inner {
  max-width: var(--shell);
  margin-inline: auto;
  padding: 12px var(--pad);
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  text-decoration: none;
  color: inherit;
}

.brand-mark {
  width: 32px;
  height: 32px;
  flex: none;
  /* Same artwork as favicon.svg, so the tab icon and the header agree. */
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='8' fill='%230e131c'/%3E%3Ccircle cx='9' cy='11' r='3' fill='%2300b39b'/%3E%3Ccircle cx='16' cy='19' r='3' fill='%234d9bff'/%3E%3Ccircle cx='23' cy='13' r='3' fill='%23f0a07c'/%3E%3C/svg%3E") center / contain no-repeat;
  border-radius: 8px;
}

.brand-text h1 {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.24em;
  line-height: 1.1;
}

/* Primary sections. These were in the footer, which is the wrong place for
   the things people come here to reach. */
.main-nav {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-inline: auto;
  flex-wrap: wrap;
}

.main-nav a {
  padding: 6px 12px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-dim);
  text-decoration: none;
  border-radius: 8px;
  white-space: nowrap;
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
}
.main-nav a:hover { color: var(--text); background: var(--panel-2); }
.main-nav a[aria-current="page"] {
  color: var(--accent);
  background: var(--accent-soft);
}

.header-icons { display: flex; align-items: center; gap: 6px; }

/* Content controls: period on the left, tools that reshape it on the right. */
.controlbar { border-top: 1px solid var(--line-soft); }

.controlbar-inner {
  max-width: var(--shell);
  margin-inline: auto;
  padding: 8px var(--pad);
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.control-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

/* search ---------------------------------------------------------------- */

.search-field {
  position: relative;
  display: flex;
  align-items: center;
  min-width: 0;
}

.search-icon {
  position: absolute;
  left: 11px;
  width: 15px;
  height: 15px;
  color: var(--muted);
  pointer-events: none;
}

.search-input {
  width: clamp(180px, 26vw, 300px);
  padding: 8px 32px 8px 32px;
  font-family: inherit;
  font-size: 13px;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  transition: border-color 0.18s var(--ease), box-shadow 0.18s var(--ease), background 0.18s var(--ease);
}
.search-input::placeholder { color: var(--muted); }
.search-input::-webkit-search-cancel-button { display: none; }

.search-input:focus-visible {
  outline: none;
  border-color: var(--accent-line);
  background: var(--panel-2);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.search-kbd {
  position: absolute;
  right: 10px;
  font-size: 10px;
  line-height: 1.4;
  padding: 1px 6px;
  color: var(--muted);
  background: var(--panel-2);
  pointer-events: none;
}
.search-field:focus-within .search-kbd,
.search-field:has(.search-input:not(:placeholder-shown)) .search-kbd { display: none; }

.search-clear {
  position: absolute;
  right: 8px;
  display: grid;
  place-items: center;
  width: 20px; height: 20px;
  font-size: 10px;
  color: var(--muted);
  background: var(--panel-3);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}
.search-clear:hover { color: var(--text); }

/* segmented control & icon buttons -------------------------------------- */

.seg {
  display: inline-flex;
  gap: 2px;
  padding: 4px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
}

.seg-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: color 0.16s var(--ease), background 0.16s var(--ease);
}
.seg-btn svg { width: 13px; height: 13px; }
.seg-btn:hover { color: var(--text-dim); }
.seg-btn[aria-pressed="true"] {
  color: var(--accent-ink);
  background: var(--accent);
}

.icon-btn {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  color: var(--text-dim);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  cursor: pointer;
  transition: color 0.16s var(--ease), background 0.16s var(--ease), border-color 0.16s var(--ease);
}
.icon-btn svg { width: 16px; height: 16px; }
.icon-btn:hover {
  color: var(--text);
  background: var(--panel-2);
  border-color: color-mix(in srgb, var(--muted) 40%, var(--line));
}

#refresh-btn[data-spinning="true"] svg { animation: spin 0.8s linear infinite; }
#refresh-btn:disabled { cursor: default; }

@keyframes spin { to { rotate: 360deg; } }

/* theme toggle shows exactly one glyph for its current state */
#theme-toggle svg { display: none; }
#theme-toggle[data-theme-state="system"] .i-system,
#theme-toggle[data-theme-state="light"] .i-light,
#theme-toggle[data-theme-state="dark"] .i-dark { display: block; }

/* year bar --------------------------------------------------------------- */


.year-selector {
  display: inline-flex;
  gap: 2px;
  padding: 4px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 11px;
  overflow-x: auto;
  scrollbar-width: none;
  max-width: 100%;
}
.year-selector::-webkit-scrollbar { display: none; }

.year-btn {
  flex: none;
  padding: 6px 16px;
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--muted);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  cursor: pointer;
  transition: color 0.18s var(--ease), background 0.18s var(--ease), border-color 0.18s var(--ease);
}
.year-btn:hover { color: var(--text-dim); background: var(--panel-2); }

.year-btn[aria-selected="true"] {
  color: var(--accent);
  background: var(--accent-soft);
  border-color: var(--accent-line);
}

.year-btn .yb-count {
  margin-left: 6px;
  font-size: 10px;
  opacity: 0.65;
}

/* --------------------------------------------------------------------------
   Cadence ribbon — the page's thesis.

   One column per month across the entire dataset, stacked by release. The
   read is the SHAPE: a sparse 2022 giving way to a crowded 2026. Height
   carries the signal; company hue is texture the cards already decode.
   -------------------------------------------------------------------------- */

.cadence-inner {
  max-width: var(--shell);
  margin-inline: auto;
  padding: 24px var(--pad) 6px;
}

.cadence-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.cadence-title {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.cadence-facts {
  margin: 0;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.ribbon {
  --unit: 7px;              /* one release */
  --unit-gap: 2px;          /* surface gap between stacked units */
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  align-items: end;
  height: calc((var(--unit) + var(--unit-gap)) * var(--peak, 4) + 4px);
  padding-bottom: 4px;
  border-bottom: 1px solid var(--line);
}

.ribbon-col {
  display: flex;
  flex-direction: column-reverse;
  justify-content: flex-start;
  align-items: center;
  gap: var(--unit-gap);
  height: 100%;
  min-width: 0;
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer;
  transition: background 0.15s var(--ease);
}

.ribbon-col:hover { background: var(--accent-soft); }
.ribbon-col[data-current="true"] { background: color-mix(in srgb, var(--accent) 8%, transparent); }

.ribbon-seg {
  width: min(11px, 74%);
  height: var(--unit);
  flex: none;
  border-radius: 1.5px;
  background: var(--c);
  transition: opacity 0.18s var(--ease);
}
.ribbon-seg[data-dim="true"] { opacity: 0.16; }

.ribbon-axis {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 1px;
  margin-top: 8px;
}

.ribbon-yr {
  min-width: 0;
  padding-right: 8px;
  border-left: 1px solid var(--line-soft);
  padding-left: 8px;
}
.ribbon-yr[data-current="true"] { border-left-color: var(--accent-line); }

.ribbon-yr-num {
  display: block;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.ribbon-yr[data-current="true"] .ribbon-yr-num { color: var(--accent); }

/* The era name says what that year was actually about — structure that
   encodes the content rather than decorating it. */
.ribbon-yr-era {
  display: block;
  margin-top: 1px;
  font-size: 11px;
  line-height: 1.25;
  color: var(--muted);
  opacity: 0.72;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Filter bar
   -------------------------------------------------------------------------- */

.filterbar-inner {
  max-width: var(--shell);
  margin-inline: auto;
  padding: 14px var(--pad) 4px;
  display: flex;
  align-items: flex-start;
  gap: 24px;
  flex-wrap: wrap;
}

.filter-group { min-width: 0; }

.filter-title {
  margin: 0 0 8px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.chip {
  --c: var(--c-other);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px 4px 8px;
  font-family: inherit;
  font-size: 12px;
  color: var(--text-dim);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: color 0.15s var(--ease), background 0.15s var(--ease),
              border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

.chip:hover {
  color: var(--text);
  border-color: color-mix(in srgb, var(--c) 50%, var(--line));
  background: color-mix(in srgb, var(--c) 8%, var(--panel));
}

.chip[aria-pressed="true"] {
  color: var(--text);
  background: color-mix(in srgb, var(--c) 16%, var(--panel));
  border-color: color-mix(in srgb, var(--c) 62%, var(--line));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--c) 32%, transparent);
}

.chip-dot {
  width: 8px;
  height: 8px;
  flex: none;
  border-radius: 50%;
  background: var(--c);
}

/* Capability chips take a Lucide stroke icon — deliberately a different
   visual language from the companies' filled brand logos, because a
   capability is not an organisation. */
.tag-glyph { width: 13px; height: 13px; color: var(--muted); }
.chip-tag[aria-pressed="true"] .tag-glyph { color: var(--accent); }
.chip-tag .chip-dot {
  border-radius: 2px;
  rotate: 45deg;
  width: 7px;
  height: 7px;
  background: var(--muted);
}
.chip-tag[aria-pressed="true"] { --c: var(--accent); }
.chip-tag[aria-pressed="true"] .chip-dot { background: var(--accent); }

.chip-count {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--muted);
  background: var(--panel-2);
  border-radius: var(--radius-pill);
  padding: 1px 6px;
}
.chip[aria-pressed="true"] .chip-count {
  color: var(--text-dim);
  background: color-mix(in srgb, var(--c) 22%, transparent);
}

.reset-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 24px;
  padding: 4px 12px;
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--accent);
  background: transparent;
  border: 1px solid var(--accent-line);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background 0.15s var(--ease);
}
.reset-btn svg { width: 11px; height: 11px; }
.reset-btn:hover { background: var(--accent-soft); }

/* --------------------------------------------------------------------------
   Timeline lanes
   -------------------------------------------------------------------------- */

.timeline {
  max-width: var(--shell);
  margin-inline: auto;
  padding: 18px var(--pad) 10px;
  min-height: 40vh;
}

.lane-group-head {
  position: sticky;
  top: var(--stick, 104px);
  z-index: 6;
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 24px 0 2px;
  padding: 6px 2px;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.lane-group-head:first-child { margin-top: 0; }

.lane-group-year {
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--text);
}

.lane-group-count {
  font-size: 12px;
  color: var(--muted);
}

.lane {
  display: grid;
  grid-template-columns: 112px 1fr;
  align-items: start;
  border-bottom: 1px solid var(--line-soft);
  content-visibility: auto;
  contain-intrinsic-size: auto 74px;
}

.lane:nth-of-type(even) {
  background: linear-gradient(90deg, var(--card-sheen), transparent 60%);
}

.lane-label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 16px 14px 16px 4px;
  border-right: 1px solid var(--line-soft);
}

.lane-abbr {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: var(--muted);
}

.lane-name {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
  line-height: 1.15;
}

.lane-count {
  align-self: flex-start;
  margin-top: 4px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  line-height: 1;
  color: var(--text-dim);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  padding: 4px 8px;
}
.lane-count[data-zero="true"] {
  color: var(--muted);
  background: transparent;
  border-color: var(--line-soft);
}

.lane-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-content: flex-start;
  padding: 12px 4px 12px 16px;
  min-height: 62px;
}

.lane-empty {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  color: var(--muted);
  font-size: 12px;
}
.lane-empty::before {
  content: "";
  width: 14px;
  height: 1px;
  background: var(--line);
}

/* grid view: one flat, date-sorted wall of cards */
.lanes[data-view="grid"] {
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(auto-fill, minmax(212px, 1fr));
}
.lanes[data-view="grid"] .card { width: 100%; }

/* --------------------------------------------------------------------------
   Release cards
   -------------------------------------------------------------------------- */

.card {
  --c: var(--c-other);
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 214px;
  min-width: 0;
  padding: 10px 12px 10px 12px;
  background: linear-gradient(180deg, var(--card-sheen), transparent 55%), var(--panel);
  border: 1px solid var(--line);
  border-left: 3px solid var(--c);
  border-radius: var(--radius);
  color: var(--text);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: transform 0.18s var(--ease), border-color 0.18s var(--ease),
              box-shadow 0.18s var(--ease), background 0.18s var(--ease);
}

.card:hover,
.card:focus-visible {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--c) 55%, var(--line));
  background: linear-gradient(180deg, color-mix(in srgb, var(--c) 9%, transparent), transparent 60%), var(--panel-2);
  box-shadow: var(--shadow-card), 0 0 0 1px color-mix(in srgb, var(--c) 30%, transparent);
}
.card:active { transform: translateY(0); }

.card-top {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

/* Company glyph — the shape channel. Carries the company hue via
   `color`, which the sprite's paths pick up as `currentColor`. */
.sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.glyph {
  width: 13px;
  height: 13px;
  flex: none;
  color: var(--c);
}

.chip .glyph { width: 12px; height: 12px; }

/* Keep the mark readable when the row is pressed or hovered. */
.chip[aria-pressed="true"] .glyph { color: var(--c); }

/* Model names are the proper nouns of this subject — given weight and tight
   tracking so they read as names, not list items. */
.card-name {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: -0.012em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card-company {
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 4px;
}

/* Dates and counts are data — mono and tabular so columns line up. */
.card-date {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.lane-count, .chip-count, .year-btn, .data-status { font-variant-numeric: tabular-nums; }

.card mark,
.modal-note mark {
  color: var(--text);
  background: color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: 3px;
  padding: 0 1px;
}

.tag {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  background: var(--panel-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-pill);
  padding: 2px 6px;
}

/* --------------------------------------------------------------------------
   Empty state / footer
   -------------------------------------------------------------------------- */

.empty-state {
  display: grid;
  justify-items: center;
  gap: 6px;
  padding: 64px 20px;
  text-align: center;
}

.empty-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
}

.empty-sub {
  margin: 0;
  max-width: 40ch;
  color: var(--muted);
  font-size: 13px;
  text-wrap: balance;
}

.empty-action {
  margin-top: 10px;
  padding: 8px 16px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--accent-ink);
  background: var(--accent);
  border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
}

.site-footer {
  max-width: var(--shell);
  margin-inline: auto;
  margin-top: 40px;
  padding: 32px var(--pad) 48px;
  border-top: 1px solid var(--line);
}

.foot-grid {
  display: grid;
  gap: 24px 48px;
  grid-template-columns: repeat(auto-fit, minmax(200px, auto));
  justify-content: start;
  align-items: start;
}

.foot-col { display: flex; flex-direction: column; gap: 8px; min-width: 0; }

.foot-title {
  margin: 0 0 4px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--muted);
}

.foot-col a {
  font-size: 13px;
  color: var(--text-dim);
  text-decoration: none;
  width: fit-content;
}
.foot-col a:hover { color: var(--accent); }

/* The year links sit in a row while every other footer column stacks, and
   as bare links that read as a wrapping accident rather than a choice. They
   ARE different — five numeric tokens, not five destinations, and stacking
   them would spend five lines on an index. So they are styled as the token
   row they are, which makes the difference deliberate. */
.foot-yearlinks { display: flex; flex-wrap: wrap; gap: 6px; }
.foot-yearlinks a {
  font-family: var(--mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  padding: 4px 8px;
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--muted);
}
.foot-yearlinks a:hover { color: var(--text); border-color: var(--accent); }

.foot-note {
  margin: 2px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
  max-width: 30ch;
}

.data-status {
  margin: 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.03em;
  color: var(--muted);
}
.data-status[data-warn="true"] { color: var(--warn); }

.noscript {
  max-width: var(--shell);
  margin: 32px auto;
  padding-inline: var(--pad);
  color: var(--muted);
  font-family: var(--mono);
  font-size: 12px;
}

/* --------------------------------------------------------------------------
   Dialogs — native <dialog>: focus trap, Esc and inertness come free
   -------------------------------------------------------------------------- */

.modal {
  --c: var(--c-other);
  width: min(480px, calc(100vw - 32px));
  max-height: min(84dvh, 620px);
  padding: 0;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--line);
  border-top: 3px solid var(--c);
  border-radius: 16px;
  box-shadow: var(--shadow-modal);
  overflow: hidden;
}

.modal::backdrop {
  background: var(--scrim);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.modal[open] { animation: modalIn 0.22s var(--ease); }
.modal[open]::backdrop { animation: fadeIn 0.22s var(--ease); }

@keyframes modalIn {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
}
@keyframes fadeIn { from { opacity: 0; } }

.modal-body {
  max-height: inherit;
  overflow-y: auto;
  padding: 20px 24px 16px;
}

.modal-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.modal-mark {
  display: grid;
  place-items: center;
  flex: none;
  margin-top: 2px;
}
.modal-mark .glyph { width: 22px; height: 22px; }

.modal-heading { min-width: 0; margin-right: auto; }

.modal-title {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  line-height: 1.2;
  text-wrap: balance;
}

.modal-company {
  margin: 4px 0 0;
  font-size: 13px;
  color: var(--text-dim);
}

.modal-close { width: 30px; height: 30px; }

.modal-facts {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
  margin: 16px 0 0;
}
.modal-facts div { margin: 0; }
.modal-facts dt {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
}
.modal-facts dd {
  margin: 4px 0 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}

/* Release cadence for the lab — the page's theme, restated per model. */
.modal-cadence {
  margin: 12px 0 0;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}

.cadence-link {
  padding: 0;
  font: inherit;
  color: var(--accent);
  background: none;
  border: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  cursor: pointer;
}
.cadence-link:hover { border-bottom-color: currentColor; }

.prov-badge {
  display: inline-block;
  margin-left: 2px;
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: var(--radius-pill);
  color: var(--muted);
  background: var(--panel-2);
  border: 1px solid var(--line-soft);
}
.prov-badge[data-status="verified"] {
  color: color-mix(in srgb, var(--accent) 80%, var(--text));
  border-color: var(--accent-line);
}
.prov-badge[data-status="estimated"] { color: var(--warn); }

.modal-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 14px;
}
.modal-tags:empty { display: none; }

.modal-note {
  margin: 14px 0 0;
  font-size: 14px;
  line-height: 1.62;
  color: var(--text-dim);
  text-wrap: pretty;
}

.modal-source {
  margin: 14px 0 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.03em;
  color: var(--muted);
}

.modal-source a {
  color: var(--accent);
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.modal-source a:hover { text-decoration-color: currentColor; }

.modal-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--line-soft);
}
#modal-copy { margin-inline: auto; }

.ghost-btn {
  padding: 6px 12px;
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-dim);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
}
.ghost-btn:hover:not(:disabled) { color: var(--text); background: var(--panel-3); }
.ghost-btn:disabled { opacity: 0.4; cursor: default; }

.shortcuts {
  display: grid;
  gap: 8px;
  margin: 18px 0 0;
  padding: 0;
  list-style: none;
}
.shortcuts li {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text-dim);
}
.shortcuts kbd { flex: none; min-width: 24px; text-align: center; }
.shortcuts span { margin-left: 4px; }

/* --------------------------------------------------------------------------
   View Transitions — cross-fade year/view swaps where supported
   -------------------------------------------------------------------------- */

::view-transition-old(lanes) { animation: vtOut 0.16s var(--ease) both; }
::view-transition-new(lanes) { animation: vtIn 0.22s var(--ease) both; }

@keyframes vtOut { to { opacity: 0; transform: translateY(-4px); } }
@keyframes vtIn { from { opacity: 0; transform: translateY(6px); } }

#lanes { view-transition-name: lanes; }

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 980px) {
  .search-input { width: clamp(150px, 34vw, 240px); }
}

@media (max-width: 720px) {
  .header-inner { gap: 10px; }
  .brand { margin-right: 0; }
  .search-field { flex: 1 1 160px; }
  .search-input { width: 100%; }

  /* The ribbon stays — it is the point of the page — but the era captions
     don't survive a narrow column, so only the years remain. */
  /**
   * Was a hard 34px, which ignored --peak entirely.
   *
   * That was fine when the busiest month held four releases. It now holds
   * seventeen, so the columns — which are sized from --peak — ran 117px past
   * the container and, because .ribbon does not clip, painted straight over
   * the company filters below it. The height has to follow the data, so it
   * scales like the desktop rule and is capped so a busy month cannot eat the
   * screen. Smaller units keep the whole range on a phone.
   */
  /* The year strip scrolls but said so nowhere, so 2026 simply looked cut
     off. A fading right edge is the usual affordance and costs no layout. */
  .year-selector {
    mask-image: linear-gradient(to right, #000 88%, transparent);
    scroll-snap-type: x proximity;
  }
  .year-btn { scroll-snap-align: start; }

  .ribbon {
    --unit: 5px;
    --unit-gap: 1px;
    height: min(calc((var(--unit) + var(--unit-gap)) * var(--peak, 4) + 4px), 132px);
  }
  /* A cap means a very busy month would otherwise overflow silently again. */
  .ribbon-col { overflow: hidden; }
  .ribbon-yr-era { display: none; }
  .ribbon-yr { padding-inline: 4px; }

  .lane { grid-template-columns: 1fr; }

  .lane-label {
    flex-direction: row;
    align-items: baseline;
    gap: 8px;
    padding: 10px 4px;
    border-right: 0;
    border-bottom: 1px solid var(--line-soft);
  }
  .lane-count { margin-top: 0; }
  .lane-cards { padding: 12px 2px; }
  .card { width: 100%; }

  .seg-btn span { display: none; }
  .seg-btn { padding: 6px 10px; }
}

@media (max-width: 420px) {
  .modal-foot { flex-wrap: wrap; }
  #modal-copy { order: 3; width: 100%; margin-inline: 0; }
}

/* --------------------------------------------------------------------------
   Reduced motion & forced colours
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  ::view-transition-old(lanes),
  ::view-transition-new(lanes) { animation: none; }
  .card:hover, .card:focus-visible { transform: none; }
}

@media (forced-colors: active) {
  .card, .chip, .stat, .modal { border: 1px solid CanvasText; }
  /* Hue is stripped here by design — the glyph shape still tells companies
     apart, which is the whole point of the second channel. */
  .glyph { color: CanvasText; }
  .chip-dot { forced-color-adjust: none; }
  .chip[aria-pressed="true"] { outline: 2px solid Highlight; }
}

@media print {
  .site-header, .filterbar, .cadence, .site-footer, .skip-link { display: none; }
  .card { break-inside: avoid; box-shadow: none; }
}

/* --------------------------------------------------------------------------
   Generated document pages (/models, /companies, /timeline)

   Static, indexable, and readable with JavaScript off. They share the app's
   tokens so the two never drift apart visually.
   -------------------------------------------------------------------------- */

.doc-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  max-width: 860px;
  margin-inline: auto;
  padding: 16px var(--pad);
  border-bottom: 1px solid var(--line);
}

.doc-main {
  max-width: 860px;
  margin-inline: auto;
  padding: 24px var(--pad) 64px;
}

.crumbs {
  margin-bottom: 20px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--muted);
}
.crumbs a { color: var(--text-dim); }

.doc-hero {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 20px;
}
/* Company marks ------------------------------------------------------------
   Third-party logos are drawn for their own clear space, never for a shared
   list. Set loose next to each other they disagree on every axis that matters
   for scanning: Meta's wide infinity against MiniMax's thin bars, Anthropic's
   heavy A against Google's light ring — different visual weights, different
   widths, a ragged left edge down the column.

   A fixed container is the standard answer (GitHub org avatars, App Store
   icons, integration directories) because it restores one optical frame and
   one alignment edge. Rounded square rather than circle: the convention reads
   circles as people and squares as organisations, and a circle crops wide
   marks harder.

   The tint and hairline are mixed from the company's own hue, so the frame
   carries identity instead of competing with it — colour stays the secondary
   channel it is everywhere else here, under the mark and the name.

   The marks were 13-17px, below the ~16px floor where a logo is still
   recognisable rather than a smudge. --mark sizes the box; the glyph is a
   share of it, so one number moves both. */
/* Record provenance -------------------------------------------------------
   The evidence state of a record, stated where the record starts.

   Deliberately not a success/failure palette. A verified record is not good
   news and an unresearched field is not an error — they are different amounts
   of work done, and colouring them green and red would tell the reader to feel
   something the data does not support. States differ by the dot and by weight;
   hue is a third channel that never carries the meaning alone. */
.pv {
  margin: 14px 0 18px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel-2);
}

.pv-head {
  margin: 0 0 6px;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.005em;
}

.pv-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex: none;
  background: var(--muted);
  box-shadow: inset 0 0 0 2px var(--panel-2);
}
/* Filled = traced through. Ringed = partial. Hollow = nothing checked yet.
   The shape carries it; the tint only reinforces. */
.pv[data-status="verified"] .pv-dot {
  background: var(--state-ok);
  box-shadow: none;
}
.pv[data-status="partially_verified"] .pv-dot {
  background: transparent;
  box-shadow: inset 0 0 0 2.5px var(--state-gap);
}
.pv[data-status="estimated"] .pv-dot,
.pv[data-status="unverified"] .pv-dot {
  background: transparent;
  box-shadow: inset 0 0 0 2.5px var(--muted);
}

.pv-facts {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  font-size: 13px;
  color: var(--muted);
}
.pv-fact strong { color: var(--text); font-variant-numeric: tabular-nums; }
/* A gap is worth pointing at, not hiding — but it is not a warning. */
.pv-gap strong { color: var(--state-gap); }

/* The full reason runs to a paragraph once capability quotes are appended, and
   a wall of text at the top of a page is not read. It stays one click away
   rather than being trimmed — a truncated justification is worse than a
   collapsed one. */
.pv-why { margin: 8px 0 0; font-size: 13px; }
.pv-why > summary {
  cursor: pointer;
  color: var(--muted);
  width: fit-content;
}
.pv-why > summary:hover { color: var(--text); }
.pv-why > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
.pv-why > p {
  margin: 6px 0 0;
  line-height: 1.55;
  color: var(--muted);
  max-width: 76ch;
}
.pv-more { margin: 8px 0 0; font-size: 12px; }

@media (max-width: 640px) {
  .pv-facts { gap: 4px 12px; font-size: 12px; }
}

/* List rows, made to respond ----------------------------------------------
   The timeline's cards carry a coloured edge, a hover lift and a count; the
   static lists were plain text rows that did nothing under the cursor, which
   is the difference in liveliness between the two surfaces — not hue.

   The edge appears on hover rather than at rest, so a list of sixteen labs is
   never sixteen colours at once, and the row still tells you which lab it
   belongs to the moment you point at it. */
.ask-list li,
.doc-list li {
  position: relative;
  border-radius: 8px;
  transition: background 120ms var(--ease);
}
.ask-list li::before,
.doc-list li::before {
  content: "";
  position: absolute;
  inset-block: 6px;
  left: -10px;
  width: 2px;
  border-radius: 2px;
  background: var(--c, var(--c-other));
  opacity: 0;
  transition: opacity 120ms var(--ease);
}
.ask-list li:hover,
.doc-list li:hover { background: var(--panel-2); }
.ask-list li:hover::before,
.doc-list li:hover::before { opacity: 0.9; }

@media (prefers-reduced-motion: reduce) {
  .ask-list li, .ask-list li::before,
  .doc-list li, .doc-list li::before { transition: none; }
}

/* The dataset's own measurements, under the field ------------------------- */
.hero-stats {
  margin: 18px 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--muted);
}
.hero-stats strong {
  color: var(--text);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.hero-sep { opacity: 0.45; }
/* Freshness wraps to its own line on a phone rather than shortening: how
   current the data is deserves the whole sentence. */
.hero-fresh { flex-basis: 100%; }

/* The chart on the homepage ------------------------------------------------
   Same module as the analytics study, so it inherits .cs-* and needs only its
   own framing here. */
.home-chart {
  margin: 32px 0 0;
  padding-top: 24px;
  border-top: 1px solid var(--line);
}
.home-chart-lead {
  margin: 0 0 6px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
  max-width: 68ch;
}
.home-chart .cs-figure { margin: 10px 0 4px; }

/* Below about 560px the year marks collide and the dots crowd the axis. The
   chart keeps its aspect ratio and scrolls rather than being squeezed into
   something unreadable — a deliberate mobile treatment, not a squashed one. */
@media (max-width: 560px) {
  .home-chart .cs-figure { overflow-x: auto; }
  .home-chart .cs-chart { min-width: 480px; }
}

/* Why the record is worth citing ------------------------------------------
   Six claims, each linking to the page that backs it. A trust section whose
   assertions cannot be checked is marketing, which is the one thing this
   project cannot afford to look like. */
.trust {
  margin: 40px 0 8px;
  padding-top: 24px;
  border-top: 1px solid var(--line);
}
.trust-h {
  margin: 0 0 18px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.trust-grid {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 24px 32px;
  grid-template-columns: 1fr;
}
.trust-grid > li { display: grid; gap: 4px; align-content: start; }
.trust-grid h3 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.011em;
}
.trust-grid p {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
  max-width: 46ch;
}
.trust-grid a { font-size: 13px; }

@media (min-width: 660px) { .trust-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .trust-grid { grid-template-columns: repeat(3, 1fr); } }

/* Direct answer above the results ----------------------------------------- */
.ask-answer {
  margin: 0 0 14px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--panel-2);
}
.ask-answer-text {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.25;
}
.ask-answer-detail {
  margin: 4px 0 0;
  font-size: 13px;
  color: var(--muted);
}

/* Pricing table ------------------------------------------------------------ */
.pr-table { border-collapse: collapse; width: 100%; font-size: 13px; }
.pr-table th, .pr-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--line-soft);
  text-align: left;
  vertical-align: baseline;
}
.pr-table thead th { color: var(--muted); font-weight: 600; font-size: 12px; }
.pr-table th[scope="row"] { font-weight: 600; }
.pr-lab { display: block; font-size: 12px; color: var(--muted); font-weight: 400; }
.pr-num { font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
.pr-when { font-variant-numeric: tabular-nums; color: var(--muted); white-space: nowrap; }
.pr-src { font-size: 12px; }
.pr-nosrc { color: var(--muted); }

@media (max-width: 640px) {
  .pr-table { font-size: 12px; }
  .pr-table th, .pr-table td { padding: 8px 6px; }
}

/* Dataset changelog -------------------------------------------------------- */
.chg-list { list-style: none; margin: 0 0 8px; padding: 0; }
.chg { padding: 14px 0; border-bottom: 1px solid var(--line-soft); }
.chg-when {
  margin: 0;
  display: flex;
  gap: 10px;
  align-items: baseline;
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.chg-sha { font-family: var(--mono); font-size: 12px; }
.chg-subject { margin: 2px 0 6px; font-weight: 600; }
.chg-facts { margin: 0; padding-left: 18px; display: grid; gap: 4px; font-size: 13px; }
.chg-facts li { color: var(--muted); }
.chg-facts strong { color: var(--text); font-weight: 600; }

/* A correction is the entry a reader came here to find, so it is the one that
   is marked. Not styled as an error: publishing the fix is the good outcome. */
.chg-fix::marker { color: var(--state-fix); }
.chg-fix strong { color: var(--state-fix); }

/* Context-window study ----------------------------------------------------- */
.cs-figure { margin: 18px 0 24px; }
.cs-chart {
  width: 100%;
  height: auto;
  overflow: visible;
  color: var(--accent);
}
.cs-grid { stroke: var(--line-soft); stroke-width: 1; }
.cs-tick { fill: var(--muted); font-size: 11px; font-family: var(--mono); }
.cs-step {
  stroke: currentColor;
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.cs-dot { fill: currentColor; }
.cs-figure figcaption {
  margin-top: 8px;
  font-size: 13px;
  color: var(--muted);
  max-width: 62ch;
}

/* Capability evolution --------------------------------------------------- */
.cap-firsts {
  list-style: none;
  margin: 0 0 8px;
  padding: 0;
  display: grid;
  gap: 2px;
}
.cap-firsts li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1.1fr);
  gap: 10px;
  align-items: baseline;
  padding: 8px 0;
  border-bottom: 1px solid var(--line-soft);
}
.cap-first-name { font-weight: 600; }
.cap-first-when {
  font-size: 13px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Year × capability grid. One hue at varying strength: every cell is the same
   measurement at a different magnitude, so a categorical palette would imply
   the capabilities are what is being compared. --fill carries the share and
   the count is printed in the cell, so colour is never the only channel. */
.table-scroll { overflow-x: auto; margin-bottom: 8px; }

/* A config block is one long line and the viewport is 440px on a phone. The
   page body must never scroll sideways, so the code block scrolls inside
   itself — the same contract the wide tables already follow. */
.prose pre {
  overflow-x: auto;
  max-width: 100%;
  padding: 12px 14px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  font-size: 12px;
  line-height: 1.55;
}
.prose pre code { white-space: pre; }

/* The boundary between what this dataset holds and what it deliberately does
   not. Set apart from the record so it reads as a signpost, not as data. */
.elsewhere {
  margin: 34px 0 8px;
  padding: 18px 20px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
}
.elsewhere h2 { margin: 0 0 8px; }
.elsewhere p { margin: 0 0 12px; color: var(--muted); }
.elsewhere-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; }
.elsewhere-list li {
  display: flex; flex-wrap: wrap; gap: 4px 12px;
  align-items: baseline; justify-content: space-between;
  padding-bottom: 8px; border-bottom: 1px solid var(--line);
}
.elsewhere-list li:last-child { border-bottom: 0; padding-bottom: 0; }
.el-what { color: var(--fg); }
.cap-matrix {
  border-collapse: collapse;
  width: 100%;
  font-size: 13px;
}
.cap-matrix th,
.cap-matrix td {
  padding: 6px 8px;
  text-align: center;
  border-bottom: 1px solid var(--line-soft);
}
.cap-matrix th[scope="row"] {
  text-align: left;
  font-weight: 600;
  white-space: nowrap;
}
.cap-matrix thead th { color: var(--muted); font-weight: 600; }
.cap-cell {
  background: color-mix(in oklab, var(--accent) calc(var(--fill, 0) * 100%), transparent);
  font-variant-numeric: tabular-nums;
}
.cap-n { font-weight: 600; }
.cap-of { color: var(--muted); }

@media (max-width: 640px) {
  .cap-firsts li { grid-template-columns: minmax(0, 1fr) auto; }
  .cap-first-model { grid-column: 1 / -1; }
  .cap-matrix { font-size: 12px; }
}

/* Family lineage rail ------------------------------------------------------
   Spacing between tiers is set per-tier by --lead, computed from the actual
   gap between releases, so the shape of a family's cadence is visible rather
   than described. The rail itself is decoration over an <ol> that still reads
   correctly unstyled and in a screen reader. */
.lineage-graph {
  list-style: none;
  margin: 0 0 8px;
  padding: 0;
}

.lin-tier {
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr);
  gap: 0 14px;
  padding-top: var(--lead, 0px);
}

.lin-rail { position: relative; }

/* One continuous line, trimmed at both ends so it starts and stops at a node
   rather than running off into the margin. */
.lin-rail::before {
  content: "";
  position: absolute;
  /* --lead is the tier's padding-top, so the rail must reach back UP through
     it to meet the previous node. The node itself sits at the top of the rail
     box and must not add --lead again — doing so counted the gap twice and
     dropped every dot a full gap below its own date. */
  top: calc(-1 * var(--lead, 0px));
  bottom: 0;
  left: 50%;
  width: 2px;
  transform: translateX(-50%);
  /* Tinted from the family's own hue: one lab per page, so this is identity
     rather than decoration, and no rainbow is possible. */
  background: color-mix(in oklab, var(--c, var(--c-other)) 38%, var(--line));
}
.lineage-graph > .lin-tier:first-child .lin-rail::before { top: 9px; }
.lineage-graph > .lin-tier:last-child .lin-rail::before { bottom: calc(100% - 9px); }

.lin-node {
  position: absolute;
  top: 4px;
  left: 50%;
  width: 10px;
  height: 10px;
  margin-left: -4px;
  border-radius: 50%;
  background: var(--c, var(--c-other));
  box-shadow: 0 0 0 3px var(--bg);
}

/* Step connectors.
   A bare spine leaves each card floating beside a line it has no visible tie
   to. An elbow — down the spine, then out to the card — makes the path an
   actual route between generations, and reads as a staircase once the tiers
   alternate above 900px. Orthogonal on purpose: a diagonal would imply a rate
   of change between two points that are dated events, not a trend. */
.lin-rail::after {
  content: "";
  position: absolute;
  top: 9px;
  left: 50%;
  /* Reaches the full gutter so the elbow visibly meets the card instead of
     stopping in empty space. Set from the same gap the grid uses. */
  width: 14px;
  height: 2px;
  background: color-mix(in oklab, var(--c, var(--c-other)) 38%, var(--line));
}

/* A tier holding same-day siblings gets a squared node, so "these shipped
   together" is legible without reading the caption. */
.lin-tier[data-siblings="true"] .lin-node {
  border-radius: 3px;
  width: 12px;
  height: 12px;
  margin-left: -6px;
  top: 3px;
}

.lin-body { min-width: 0; padding-bottom: 2px; }

.lin-date {
  margin: 0 0 2px;
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.lin-gap { opacity: 0.72; }
.lin-sibs {
  margin: 0 0 4px;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
}

.lin-cards { display: flex; flex-wrap: wrap; gap: 6px 18px; }
.lin-card { min-width: 0; }
.lin-name { font-weight: 600; letter-spacing: -0.01em; }

.lin-firsts, .lin-changes {
  margin: 4px 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 6px;
}
/* Names the series predecessor a card's changes are measured from, so a chip
   never reads as a change from whatever the family shipped just before. */
.lin-from {
  color: var(--muted);
  font-size: 0.85em;
  align-self: center;
}

.lin-chip {
  font-size: 12px;
  line-height: 1.5;
  padding: 1px 8px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
/* Direction is a secondary cue on top of the arrow already in the text, never
   the only one — and "down" is not styled as bad, because a smaller model or a
   cheaper licence is not a regression. */
.lin-up { border-color: color-mix(in oklab, var(--state-ok) 42%, var(--line)); }
.lin-new {
  border-color: color-mix(in oklab, var(--c, var(--c-other)) 45%, var(--line));
  color: var(--text);
}

/* Zig-zag, but only where there is width for it.
   Alternating around a centre spine is the shape people expect from a
   timeline, and it uses the horizontal room a family page otherwise wastes.
   It also halves the column, and these chips are long — "Modalities in text,
   image · out text → in text, image, audio · out text" wraps even at full
   width. So it switches on at 900px and the single left rail stays below
   that, which is also what mobile needs anyway.

   DOM order is unchanged: the list still reads top to bottom in sequence, and
   only the painted side alternates. */
@media (min-width: 900px) {
  .lin-tier {
    grid-template-columns: minmax(0, 1fr) 16px minmax(0, 1fr);
    gap: 0 20px;
  }
  .lin-rail { grid-column: 2; grid-row: 1; }

  .lin-tier:nth-child(odd) .lin-body {
    grid-column: 1;
    grid-row: 1;
    text-align: right;
  }
  .lin-tier:nth-child(odd) .lin-rail::after { left: auto; right: 50%; }
  /* A centred sibling tier has cards on both sides; give it both arms. */
  .lin-tier[data-siblings="true"] .lin-rail::after { display: none; }
  .lin-tier:nth-child(even) .lin-body { grid-column: 3; grid-row: 1; }

  /* Chips are flex rows, so they need telling which edge to gather on. */
  .lin-tier:nth-child(odd) .lin-firsts,
  .lin-tier:nth-child(odd) .lin-changes,
  .lin-tier:nth-child(odd) .lin-cards { justify-content: flex-end; }

  /* A tier of same-day siblings is about the tier, not about a side — centre
     it across both columns so five Nova models do not crush into half a page. */
  .lin-tier[data-siblings="true"] .lin-body {
    grid-column: 1 / -1;
    grid-row: 2;
    text-align: center;
    padding-top: 6px;
  }
  .lin-tier[data-siblings="true"] .lin-cards,
  .lin-tier[data-siblings="true"] .lin-firsts,
  .lin-tier[data-siblings="true"] .lin-changes { justify-content: center; }
  .lin-tier[data-siblings="true"] .lin-rail { grid-row: 1 / -1; }
}

@media (max-width: 640px) {
  .lin-tier { grid-template-columns: 14px minmax(0, 1fr); gap: 0 10px; }
  .lin-chip { font-size: 11px; }
}

@media (prefers-reduced-motion: no-preference) {
  .lin-name { transition: color 120ms ease; }
}

.doc-mark,
.modal-mark,
.ask-mark {
  --mark: 30px;
  --mark-hue: var(--c, var(--c-other));
  inline-size: var(--mark);
  block-size: var(--mark);
  display: grid;
  place-items: center;
  flex: none;
  border-radius: calc(var(--mark) * 0.3);
  color: var(--mark-hue);
  background: color-mix(in oklab, var(--mark-hue) 11%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--mark-hue) 20%, transparent);
}

.doc-mark .glyph,
.modal-mark .glyph,
.ask-mark .glyph {
  inline-size: 58%;
  block-size: 58%;
}

/* The hero mark scales with the viewport so it stays proportional to a
   clamp()ed page title on every screen. */
.doc-mark { --mark: clamp(42px, 7vw, 54px); }
.doc-mark.sm { --mark: 28px; }
.ask-mark { --mark: 26px; }

/* Initials, when a lab has no logo. Sized off the same box, so it drops into
   any of the above without a second set of numbers. */
.mark-mono {
  font-size: calc(var(--mark) * 0.34);
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1;
  font-feature-settings: "tnum" 0;
}

/* In forced-colours mode the tint and hairline are dropped by the UA anyway;
   make sure the box does not collapse to an invisible square. */
@media (forced-colors: active) {
  .doc-mark, .modal-mark, .ask-mark { border: 1px solid CanvasText; }
}

/* Three index pages emit a bare <main><h1> instead of the .doc-hero wrapper,
   so they rendered at the browser default — 28px/700 against 32px/750 with
   -0.02em elsewhere. The wrapper is hand-typed at 14 sites in build.mjs and
   there is no docHero() helper, so this matches on the heading rather than
   the wrapper: it is one rule instead of fourteen edits, and it cannot drift
   again the next time a page is added. */
.doc-hero h1,
.doc-main > h1 {
  margin: 0;
  font-size: clamp(24px, 4vw, 32px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
  text-wrap: balance;
}

.doc-sub {
  margin: 4px 0 0;
  font-size: 13px;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.doc-note {
  margin: 0 0 18px;
  font-size: 15px;
  line-height: 1.65;
  color: var(--text-dim);
  text-wrap: pretty;
}

.doc-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 24px; }

.doc-main h2 {
  margin: 32px 0 12px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--muted);
}

.doc-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.doc-table th, .doc-table td {
  padding: 8px 12px 8px 0;
  text-align: left;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: top;
}
.doc-table th { width: 180px; font-weight: 600; color: var(--muted); }
.doc-table td { color: var(--text); font-variant-numeric: tabular-nums; }

.doc-prov { margin: 0 0 12px; font-size: 13px; color: var(--muted); }

.doc-sources, .doc-list, .doc-lineage { margin: 0; padding: 0; list-style: none; }

/* Real columns. These were flex rows with `margin-left:auto` on the trailing
   spans, which right-aligned them as a group and left the middle column
   ragged — every row's company name ended at a different x. A grid with
   declared tracks makes the columns actually line up. */
.doc-list li {
  display: grid;
  /* The last track was a hard 132px, sized for a bare date. /latest/ appends
     the gap since the previous release — "August 14, 2026 · +62d" needs 159px
     — so the column clipped mid-word on every row of that page. max-content
     sizes it to the widest cell actually present, so a list of plain dates is
     unchanged and one carrying a suffix simply fits. */
  /* Five tracks: mark, name, lab, modality glyphs, date.
     The glyphs had their own column added rather than sharing the lab's,
     because "Google DeepMind" plus four glyphs overflowed a 168px track and
     truncated the lab name on every Gemini row. An empty glyph track collapses
     to nothing, so rows without researched modalities are unchanged. */
  /* Five FIXED trailing tracks, so the name column keeps whatever is left.
     Two max-content tracks here collapsed the 1fr name column instead —
     "GLM-5.3" rendered as "GL…" — which is the opposite of the clipping it was
     meant to fix. 68px fits four 13px glyphs with their gaps; 164px fits
     "August 14, 2026 · +62d", the longest date /latest/ produces. */
  grid-template-columns: 20px minmax(0, 1fr) minmax(0, 168px) 68px 164px;
  align-items: center;
  gap: 14px;
  padding: 8px 2px;
  font-size: 14px;
  border-bottom: 1px solid var(--line-soft);
}
.doc-list li:hover { background: color-mix(in srgb, var(--accent) 4%, transparent); }

/* Below a comfortable desktop the four trailing tracks leave the name column
   too little to work with — 20 + 168 + 68 + 164 plus four gaps is 476px, so at
   an 800px viewport "GLM-5.3" rendered as "GL…". The glyphs are the least
   load-bearing column, so they go first, then the lab column narrows. The name
   is the one thing on the row that must always be readable. */
@media (max-width: 1000px) {
  /* FOUR tracks, not five-with-a-zero. `display: none` removes the glyph span
     from grid flow entirely, so a zero-width fifth track leaves the date to
     land in it and clip — the same shift that broke this before, arriving from
     the opposite direction. With the span gone there are four children, so
     there must be four tracks. */
  /* The glyphs STAY. Hiding them below 1000px meant they were invisible in
     every pane narrower than a full desktop — which is where they were first
     looked for and not found. They are the newest thing on the row and the
     reason it exists; the lab name and the date give up width instead. */
  .doc-list li { grid-template-columns: 20px minmax(0, 1fr) minmax(0, 132px) 56px 150px; }
  .doc-list .mod { width: 11px; height: 11px; }
  .doc-list .mods { gap: 3px; margin-left: 0; }
}

/* Only when even that will not fit do the glyphs go, and then the row returns
   to its four-track shape — `display: none` removes the span from grid flow, so
   a fifth track would leave the date to land in it. */
@media (max-width: 520px) {
  .doc-list li { grid-template-columns: 20px minmax(0, 1fr) minmax(0, 96px) 132px; }
  .doc-list .mods { display: none; }
}


/* Two-metadata variant (companies index: count + latest date). */
.doc-list.cols-3 li { grid-template-columns: 20px minmax(0, 1fr) 120px max-content 168px; }

.cell-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
}

/* On a phone a long name WRAPS rather than truncating.
   Four of 180 model names exceed twenty characters — Qwen3-Coder-480B-A35B-
   Instruct is the worst — and at 375px two of them clipped. Per-column
   horizontal scrolling was considered and rejected: it would put 180
   independent scroll containers on /models/, each with no visible affordance,
   each fighting the page scroll on touch. A second line costs one row of
   height on two rows and hides nothing. */
@media (max-width: 760px) {
  .cell-name {
    white-space: normal;
    overflow-wrap: anywhere;
    line-height: 1.25;
  }
}


.cell-meta {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
  color: var(--text-dim);
}

.cell-num {
  text-align: right;
  font-family: var(--mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  white-space: nowrap;
}

/* Sources and lineage are two-column: label left, note right. */
.doc-sources li, .doc-lineage li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  padding: 8px 2px;
  font-size: 14px;
  border-bottom: 1px solid var(--line-soft);
}
.doc-sources li span, .doc-lineage li span {
  font-family: var(--mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  text-align: right;
}
.doc-lineage li[aria-current="true"] { color: var(--accent); }

/* Sources carry a third column from schema 1.6: who published them. Authority
   is the trust signal, so it reads as a word, never as colour alone. */
.doc-sources li { grid-template-columns: minmax(0, 1fr) auto auto; }
.src-authority {
  padding: 2px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.src-authority[data-authority="primary"] {
  color: var(--accent);
  border-color: currentColor;
}
.src-archive { font-family: var(--mono); font-size: 12px; }

/* Lifecycle events: date, what happened, and the source that evidences it. */
.doc-events { margin: 0 0 18px; padding: 0; list-style: none; }
.doc-events li {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) minmax(0, auto);
  align-items: baseline;
  gap: 14px;
  padding: 8px 2px;
  font-size: 14px;
  border-bottom: 1px solid var(--line-soft);
}
.doc-events .event-date {
  font-family: var(--mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.doc-events .event-type { font-weight: 500; }
.doc-events .event-src {
  font-family: var(--mono);
  font-size: 12px;
  text-align: right;
  overflow-wrap: anywhere;
}

@media (max-width: 560px) {
  .doc-sources li { grid-template-columns: minmax(0, 1fr) auto; }
  .doc-sources li .src-authority { grid-column: 2; }
  .doc-events li { grid-template-columns: minmax(0, 1fr) auto; }
  .doc-events .event-src { grid-column: 1 / -1; text-align: left; }
}

/* Dialog lifecycle events — the interactive twin of .doc-events. */
.modal-events {
  margin: 0 0 14px;
  padding: 10px 0 0;
  list-style: none;
  border-top: 1px solid var(--line-soft);
}
.modal-events li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 4px 0;
  font-size: 13px;
}
.modal-events time {
  flex: 0 0 auto;
  min-width: 8.5em;
  font-family: var(--mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.modal-source .source-count {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
}
/* A primary source is the lab's own word. Marked with weight, not colour
   alone — the tooltip always spells the authority out. */
.modal-source a[data-authority="primary"] { font-weight: 500; }

/* Why a record is not verified. The badge states the grade; this states the
   cause, which is the part a reader can actually act on. */
.doc-reason {
  margin: -6px 0 18px;
  max-width: 62ch;
  font-size: 13px;
  line-height: 1.55;
  color: var(--muted);
}
.prov-reason {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
}

/* Data quality: a three-way coverage table. The "not researched" column is the
   only real gap, so it is the only one marked. */
.quality-table { width: 100%; border-collapse: collapse; margin: 0 0 10px; }
.quality-table th, .quality-table td {
  padding: 8px 10px;
  text-align: left;
  font-size: 14px;
  border-bottom: 1px solid var(--line-soft);
}
.quality-table thead th {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}
.quality-table td { font-variant-numeric: tabular-nums; }
.quality-table tbody th { font-weight: 500; }
.quality-table .cell-note {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  color: var(--muted);
}
/* Weight, not colour alone — the column header already names the meaning. */
.quality-table .cell-gap { font-weight: 600; color: var(--warn); }

.quality-list li { align-items: start; }
.quality-list .cell-meta {
  grid-column: 1 / -1;
  max-width: 74ch;
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
}

/* Fact-level evidence: the source behind one published number, sitting beside
   it rather than in a list at the bottom of the page. Quiet by default — it
   should be there when questioned, not compete with the value. */
.fact-cite {
  display: block;
  margin-top: 2px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
}
.fact-cite a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.fact-cite a:hover { color: var(--accent); }

/* Disagreement is louder than agreement, because it is the thing a reader
   most needs to know and the thing a lesser dataset would hide. */
.fact-conflict {
  display: block;
  margin-top: 4px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--warn);
}
.fact-conflict a { color: inherit; }

/* A milestone is not a release. It sits in the same lane so the chronology
   reads correctly, but it is deliberately a different shape — dashed, wider,
   no company swatch bar — so it can never be mistaken for a model card or
   counted as one by eye. */
.milestone {
  /* The lane is flex in one view and grid in the other; span the row in both. */
  flex: 1 1 100%;
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  padding: 10px 12px;
  border: 1px dashed var(--line);
  border-left: 3px solid var(--c, var(--accent));
  border-radius: 8px;
  background: transparent;
  text-decoration: none;
  color: inherit;
}
.milestone:hover { border-color: var(--c, var(--accent)); }
.milestone:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.milestone-tag {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  padding: 1px 8px;
}
.milestone-title { font-size: 14px; font-weight: 600; }
.milestone-note {
  flex: 1 1 100%;
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
}

/* Family lineage — one generation per row, dates on the right. */

/* What changed: one block per consecutive pair of generations. */
.changeset {
  margin: 0 0 20px;
  padding: 14px 16px;
  border: 1px solid var(--line-soft);
  border-radius: 10px;
}
.changeset h3 {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  margin: 0 0 10px;
  font-size: 14px;
  font-weight: 500;
}
.changeset-gap {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.change-list { margin: 0; }
.change-list > div {
  display: grid;
  grid-template-columns: minmax(0, 10em) minmax(0, 1fr);
  gap: 10px;
  padding: 6px 0;
  border-top: 1px solid var(--line-soft);
}
.change-list > div:first-child { border-top: 0; }
.change-list dt {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}
.change-list dd {
  margin: 0;
  font-size: 14px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
}
.delta-from { color: var(--muted); }
.delta-arrow { color: var(--muted); font-family: var(--mono); }
.delta-to { font-weight: 600; }
/* Direction is carried by the arrow and the values themselves; colour only
   reinforces it, never states it alone. */
.delta-to[data-direction="up"] { color: var(--accent); }
.delta-add, .delta-drop {
  font-family: var(--mono);
  font-size: 12px;
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--line);
}
.delta-add { color: var(--accent); border-color: currentColor; }
.delta-drop { color: var(--warn); border-color: currentColor; }

/* The gaps matter as much as the changes — same block, quieter voice. */
.change-gaps {
  margin: 10px 0 0;
  padding-top: 8px;
  border-top: 1px dashed var(--line-soft);
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
}

@media (max-width: 560px) {
  .change-list > div { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .changeset-gap { margin-left: 0; }
}

.doc-main a { color: var(--accent); text-underline-offset: 3px; }

.doc-cta { margin: 24px 0 0; font-size: 14px; }

.doc-nav {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 24px;
  padding-top: 16px;
  border-top: 1px solid var(--line-soft);
  font-size: 13px;
}

@media (max-width: 620px) {
  .doc-table th { width: 118px; }
  .doc-list li, .doc-list.cols-3 li {
    grid-template-columns: 20px minmax(0, 1fr) auto;
    gap: 4px 12px;
  }
  .cell-meta { grid-column: 2 / 3; font-size: 12px; }
  .cell-num  { grid-column: 3 / 4; grid-row: 1; }
}

/* Route out of the app into the generated pages. Without these the static
   pages exist but are unreachable, which defeats the point of building them. */
.modal-detail {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  margin: 16px 0 0;
  font-size: 13px;
}
.modal-detail a {
  color: var(--accent);
  text-underline-offset: 3px;
}

@media (max-width: 560px) {
  .doc-head { flex-wrap: wrap; gap: 10px; }
}

/* --------------------------------------------------------------------------
   Charts (analytics)

   Every chart here encodes magnitude, so each takes ONE hue rather than a
   categorical palette. The only two-class chart — open weights vs
   proprietary — carries a legend and a direct percentage label, so it never
   depends on colour alone.
   -------------------------------------------------------------------------- */

.chart-note {
  /* Prose by default. Flex here split inline <strong> onto its own line,
     fragmenting the sentence. The legend variant opts back in below. */
  margin: 0 0 14px;
  font-size: 13px;
  line-height: 1.55;
  color: var(--muted);
  max-width: 68ch;
}

.chart-note:has(.key) { display: flex; flex-wrap: wrap; gap: 6px 18px; }
.key { display: inline-flex; align-items: center; gap: 6px; }
.key-swatch { width: 11px; height: 11px; border-radius: 3px; }
.key-swatch.open { background: var(--accent); }
.key-swatch.closed { background: color-mix(in srgb, var(--muted) 45%, transparent); }

.chart-rows { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; }

.chart-row {
  display: grid;
  grid-template-columns: minmax(0, 190px) minmax(0, 1fr) 84px;
  align-items: center;
  gap: 14px;
  padding: 4px 2px;
}

.chart-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
  color: var(--text-dim);
}
.chart-label a { color: inherit; text-decoration: none; }
.chart-label a:hover { color: var(--accent); }

.chart-track {
  display: flex;
  height: 15px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--muted) 12%, transparent);
  overflow: hidden;
}

.chart-bar {
  height: 100%;
  border-radius: 4px;
  background: var(--accent);
  min-width: 3px;
}
/* 2px surface gap between the two stacked classes */
.chart-track.split .chart-bar.open { border-radius: 4px 0 0 4px; margin-right: 2px; }
.chart-bar.closed {
  border-radius: 0 4px 4px 0;
  background: color-mix(in srgb, var(--muted) 45%, transparent);
}

.chart-value {
  text-align: right;
  font-family: var(--mono);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
}

.chart-cols {
  display: flex;
  align-items: flex-end;
  gap: clamp(8px, 3vw, 24px);
  margin-bottom: 10px;
  padding-bottom: 4px;
  overflow-x: auto;
}

.chart-col { display: flex; flex-direction: column; align-items: center; gap: 6px; flex: 1 1 0; min-width: 44px; }

.chart-colvalue {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.chart-colbar {
  width: 100%;
  max-width: 76px;
  border-radius: 4px 4px 2px 2px;
  background: var(--accent);
}

.chart-collabel {
  font-family: var(--mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

/* --------------------------------------------------------------------------
   Compare
   -------------------------------------------------------------------------- */

.cmp-pickers { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }

.cmp-pick { display: inline-flex; align-items: center; gap: 4px; }

.cmp-pick select {
  max-width: 260px;
  padding: 8px 10px;
  font-family: inherit;
  font-size: 13px;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 9px;
}

.cmp-remove, .cmp-add {
  padding: 8px 12px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-dim);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 9px;
  cursor: pointer;
}
.cmp-remove:hover:not(:disabled), .cmp-add:hover { color: var(--text); background: var(--panel-3); }
.cmp-remove:disabled { opacity: 0.35; cursor: default; }
.cmp-add { color: var(--accent); border-color: var(--accent-line); background: transparent; }

.cmp-scroll { overflow-x: auto; }

.cmp-table {
  width: 100%;
  min-width: 520px;
  border-collapse: collapse;
  font-size: 13px;
}
.cmp-table th, .cmp-table td {
  padding: 10px 14px 10px 0;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line-soft);
}
.cmp-table tr:first-child th { font-size: 14px; padding-bottom: 12px; }
.cmp-table th[scope="row"] {
  width: 150px;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}
.cmp-table td { color: var(--text-dim); font-variant-numeric: tabular-nums; }
/* Only rows where the models actually differ get emphasis. */
.cmp-table td[data-differs="true"] { color: var(--text); font-weight: 600; }

/* What changed — the diff between consecutive picks, in release order. */
.cmp-changed { margin-top: 32px; }
/* Inherits mono + uppercase + 0.15em tracking from .doc-main h2, so setting
   only the size rendered a 17px uppercase MONOSPACE eyebrow — the loudest
   string on the page, where every other section heading is 11px. */
.cmp-changed h2 {
  font-family: var(--font);
  font-size: 11px;
  letter-spacing: 0.08em;
  margin: 0 0 6px;
}
.cmp-diff { margin-top: 20px; }
.cmp-diff h3 {
  font-size: 13px;
  font-weight: 600;
  margin: 0 0 8px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.cmp-diff-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.cmp-diff-list li {
  display: grid;
  grid-template-columns: minmax(0, 170px) minmax(0, 1fr);
  gap: 12px;
  align-items: baseline;
  font-size: 13px;
  padding-left: 12px;
  border-left: 2px solid var(--line-soft);
}
/* Direction is carried by the border AND spelled out in the value beside it,
   so the colour is never the only signal (README, design notes). */
.cmp-diff-list li[data-direction="up"]   { border-left-color: var(--state-ok); }
.cmp-diff-list li[data-direction="down"] { border-left-color: var(--state-fix); }
.cmp-diff-label { color: var(--muted); }
.cmp-diff-value { color: var(--text); font-variant-numeric: tabular-nums; }
.cmp-diff-gap { font-size: 13px; color: var(--muted); margin: 8px 0 0; }

@media (max-width: 620px) {
  .cmp-diff-list li { grid-template-columns: minmax(0, 1fr); gap: 2px; }
}

@media (max-width: 620px) {
  .chart-row { grid-template-columns: minmax(0, 120px) minmax(0, 1fr) 62px; gap: 10px; }
  .chart-label { font-size: 12px; }
}

/* Header responsiveness: the nav is the first thing to compress, then the
   search field, then the segmented control's labels. */
@media (max-width: 1080px) {
  .main-nav a { padding: 6px 8px; font-size: 13px; }
  .search-input { width: clamp(150px, 22vw, 230px); }
}

@media (max-width: 860px) {
  .header-inner { gap: 10px; }
  .main-nav {
    order: 3;
    width: 100%;
    margin-inline: 0;
    gap: 0 2px;
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: 2px;
  }
  .main-nav::-webkit-scrollbar { display: none; }
  .header-icons { margin-left: auto; }
  .controlbar-inner { gap: 10px; }
  .control-tools { width: 100%; margin-left: 0; }
  .search-field { flex: 1 1 160px; }
  .search-input { width: 100%; }
}

/* ==========================================================================
   The landing page. One question, not a control panel — the timeline is a
   tool, and tools make poor front doors.
   ========================================================================== */

.home-main { max-width: 780px; margin: 0 auto; padding: 0 20px 64px; }

/* The landing page ---------------------------------------------------------
   This is a reference desk, not a product page. The field is the instrument:
   everything above it states what the record contains, everything below it
   shows how the question was read. No gradient, no card, one accent.

   Mobile first — the field is full width and 16px (any smaller and iOS zooms
   on focus), and every control clears 44px. */
.ask { padding: 32px 0 6px; }

.ask-eyebrow {
  margin: 0 0 10px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}

.ask-title {
  margin: 0 0 8px;
  font-size: clamp(30px, 8vw, 46px);
  font-weight: 700;
  letter-spacing: -0.032em;
  line-height: 1.02;
  text-wrap: balance;
}
.ask-sub {
  margin: 0 0 20px;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.45;
  max-width: 54ch;
}

/* The field. A 14px radius rather than a pill: a pill reads as a chat box,
   and this takes structured queries, not conversation. */
.ask-form {
  position: relative;
  display: flex;
  align-items: center;
}
.ask-input {
  flex: 1 1 auto;
  min-width: 0;
  height: 56px;
  padding: 0 48px 0 48px;
  font: inherit;
  font-size: 16px;
  color: inherit;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  transition: border-color 140ms var(--ease), box-shadow 140ms var(--ease);
}
.ask-input::placeholder { color: var(--muted); }
.ask-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 22%, transparent);
}
/* The UA's own clear affordance would sit beside ours. */
.ask-input::-webkit-search-cancel-button { display: none; }

.ask-glass,
.ask-clear {
  position: absolute;
  /* Both were positioned on one axis only, so they fell to their static
     position vertically and sat above the text baseline. */
  top: 50%;
  translate: 0 -50%;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  color: var(--muted);
  pointer-events: none;
}
.ask-glass { left: 14px; }
.ask-glass svg, .ask-clear svg { width: 18px; height: 18px; }

/* The clear control replaces the keyboard hint once there is something to
   clear, so the right-hand slot always holds exactly one thing. */
.ask-clear {
  right: 11px;
  width: 30px;
  height: 30px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  cursor: pointer;
  pointer-events: auto;
}
.ask-clear:hover { color: var(--text); background: var(--panel-2); }
.ask-clear:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* A 30px button is under the touch minimum, so the tappable area is widened
   without moving the glyph. */
.ask-clear::after {
  content: "";
  position: absolute;
  inset: -8px;
}

.ask-key {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  right: 14px;
  padding: 2px 8px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 6px;
  pointer-events: none;
}
/* A keyboard hint is noise where there is no keyboard. */
@media (hover: none), (max-width: 640px) { .ask-key { display: none; } }

/* How the question was understood. Never hidden: a query that silently
   ignores half of what you typed is worse than one that admits it. */
.ask-reading {
  margin: 12px 0 0;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.7;
  color: var(--muted);
}
.read-chip {
  display: inline-block;
  padding: 1px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
}

/* Entry points, grouped by intent ------------------------------------------
   A flat row of five reads as a random sample of the query language. Grouped
   and labelled, each one demonstrates a different thing the dataset can be
   asked — a filter, a superlative, a capability, a comparison — and the labels
   double as a description of what the field understands.

   Still quiet. The field is the hero; these sit under it and must not compete. */
.ask-tries {
  display: grid;
  gap: 14px 24px;
  margin: 16px 0 0;
  grid-template-columns: 1fr;
}
.try-group { min-width: 0; }
.try-label {
  display: block;
  margin-bottom: 6px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.try-group ul {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.try-group button {
  padding: 6px 12px;
  font: inherit;
  font-size: 13px;
  color: var(--muted);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: color 120ms var(--ease), border-color 120ms var(--ease);
}
.try-group button:hover { color: var(--text); border-color: var(--accent); }
.try-group button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (min-width: 620px) { .ask-tries { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 980px) { .ask-tries { grid-template-columns: repeat(4, 1fr); } }

@media (prefers-reduced-motion: reduce) {
  .try-group button { transition: none; }
}

@media (min-width: 720px) {
  .ask { padding: 64px 0 8px; }
  .ask-input { height: 62px; padding: 0 56px 0 48px; font-size: 16px; }
  .ask-glass { left: 18px; }
  .ask-glass svg, .ask-clear svg { width: 19px; height: 19px; }
}

/* The result list scrolls in place, so the field and the answer stay put.
   Only the LIST scrolls — the answer and the count sit above it and remain
   visible, because scrolling away the thing that answered the question to
   reach the evidence for it is backwards.

   The page keeps its own scroll: overscroll chaining is left at its default
   so reaching the end of the list continues down the page rather than
   trapping, which is what makes nested scrolling unpleasant on a phone. */
.ask-list {
  max-height: clamp(300px, 56vh, 720px);
  overflow-y: auto;
  /* Room for the scrollbar so rows do not shift when it appears. */
  padding-right: 4px;
  /* A soft edge signals there is more below without adding furniture. */
  mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent 100%);
}
.ask-list:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

/* A short list needs neither the cap nor the fade. */
.ask-list.is-short { max-height: none; overflow: visible; mask-image: none; }

.ask-count { margin: 32px 0 4px; font-family: var(--mono); font-size: 12px; color: var(--muted); }

/* Models the dataset can neither confirm nor rule out. Set apart from the
   matches, because "this fits" and "nobody has checked whether this fits" are
   different answers and must not read as one list. The dashed rule and lowered
   contrast say "provisional" without hiding anything. */
.ask-near-head {
  margin-top: 28px;
  padding-top: 18px;
  border-top: 1px dashed var(--line);
  color: var(--state-gap, var(--muted));
}
.ask-list.is-near li { opacity: .82; }
.ask-list.is-near li:hover { opacity: 1; }
/* The significance tier, legible without touching the filter. Only canonical
   and major are marked: badging every row would make the mark meaningless,
   which is the same reason `flagship` is not on every model. */
/* Interpretation, set apart from the facts above it. The rule and the indent
   say "this is a reading" without needing the reader to have read the note
   explaining the distinction. */
.ms-why {
  margin: 28px 0 8px;
  padding-left: 16px;
  border-left: 2px solid var(--line);
}
.ms-why h2 { margin: 0 0 6px; font-size: 15px; }
.ms-why p { margin: 0 0 8px; }
.ms-why-note { color: var(--muted); font-size: 13px; }

.ms-rank {
  display: inline-block;
  margin-right: 6px;
  padding: 1px 7px;
  border-radius: var(--radius-pill, 999px);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .02em;
  border: 1px solid currentColor;
}
.ms-rank[data-sig="canonical"] { color: var(--state-ok, #2f8a3f); }
.ms-rank[data-sig="major"]    { color: var(--muted); }

.ask-unknown {
  grid-column: 2 / -1;
  margin-top: 2px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--state-gap, var(--muted));
}
.ask-none { margin: 8px 0 0; font-size: 14px; color: var(--muted); max-width: 58ch; }

.ask-list { margin: 0; padding: 0; list-style: none; }
.ask-list li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 2px 14px;
  padding: 12px 2px;
  border-bottom: 1px solid var(--line-soft);
}
.ask-name { font-size: 15px; font-weight: 600; text-decoration: none; color: var(--accent); }
.ask-name:hover { text-decoration: underline; }
.ask-meta { font-family: var(--mono); font-size: 12px; color: var(--muted); text-align: right; }
.ask-facts { grid-column: 1 / -1; font-family: var(--mono); font-size: 12px; color: var(--muted); }
.ask-chips { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }

.ask-foot { margin: 40px 0 0; font-size: 13px; color: var(--muted); text-align: center; }

@media (max-width: 560px) {
  .ask { padding-top: 32px; }
  .ask-list li { grid-template-columns: minmax(0, 1fr); }
  .ask-meta { text-align: left; }
}

.read-ignored { color: var(--warn); }
/* The logo sits with the name, never instead of it — colour is a secondary
   channel and identity rests on mark plus name together. */
.ask-name { display: inline-flex; align-items: center; gap: 8px; }
.ask-name .glyph { width: 17px; height: 17px; flex: 0 0 auto; color: var(--c, currentColor); }

/* Screen-reader-only. styles.css had no such utility, so a label marked
   "visually-hidden" rendered and stretched the ask form to 101px tall. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.ask-form { align-items: stretch; }

/* Dataset at a glance — computed from the records, so it cannot go stale. */
.pulse-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px;
  margin: 32px 0 0;
}
.pulse-card {
  display: flex; flex-direction: column; gap: 4px;
  padding: 12px 14px;
  border: 1px solid var(--line-soft);
  border-radius: 10px;
  text-decoration: none; color: inherit;
}
.pulse-card:hover { border-color: var(--accent); }
.pulse-k {
  font-family: var(--mono); font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.pulse-v { font-size: 20px; font-weight: 600; letter-spacing: -0.01em; }
.pulse-s { font-size: 12px; color: var(--muted); }

@media (max-width: 560px) {
  .home-main { padding-left: 14px; padding-right: 14px; }
  .ask-form { flex-wrap: wrap; }
  .ask-input { flex: 1 1 100%; }
  .pulse-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .pulse-v { font-size: 16px; }
  .try-group button { font-size: 12px; }
}

/* Share and discovery controls.
   Rendered hidden and revealed by script: a copy button without a clipboard,
   or a shuffle without a dataset, is a control that lies about what it does. */
.doc-share {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 24px 0 0;
  padding-top: 18px;
  border-top: 1px solid var(--line-soft);
}
.copy-btn {
  font: inherit;
  font-size: 13px;
  line-height: 1;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--text-dim);
  cursor: pointer;
  text-decoration: none;
  transition: border-color .15s ease, color .15s ease;
}
.copy-btn:hover { border-color: var(--accent); color: var(--text); }
.copy-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.copy-btn[disabled] { opacity: .55; cursor: progress; }
.copy-btn--link { display: inline-flex; align-items: center; }

@media (prefers-reduced-motion: reduce) {
  .copy-btn { transition: none; }
}

/* Per-value evidence.
   Provenance is the one part of this site that must work when nothing else
   does, so this is a <details> and not a scripted popover. */
/* The summary sits immediately after the value it evidences, so it needs its
   own leading space — without it the row read "May 13, 2024stated in
   openai.com". */
.fact-ev { display: inline; margin-left: 8px; }
.fact-ev > summary {
  display: inline;
  cursor: pointer;
  font-size: 12px;
  color: var(--muted);
  list-style: none;
}
.fact-ev > summary::-webkit-details-marker { display: none; }
.fact-ev > summary::after {
  content: " ▸";
  font-size: 10px;
  vertical-align: middle;
}
.fact-ev[open] > summary::after { content: " ▾"; }
.fact-ev > summary:hover { color: var(--text); }
.fact-ev > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.fact-ev-body {
  margin: 8px 0 4px;
  padding: 10px 12px;
  border-left: 2px solid var(--accent-line);
  background: var(--panel-2);
  border-radius: var(--radius-sm);
}
.fact-ev-claim { margin: 0 0 8px; font-size: 13px; color: var(--text); }
.fact-ev-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.fact-ev-list li {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: baseline;
  font-size: 12px;
  color: var(--muted);
}
.fact-ev-type { color: var(--text-dim); }
.fact-ev-when { font-variant-numeric: tabular-nums; }

.ms-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 18px 24px;
  margin: 24px 0 0;
  padding: 16px 0 4px;
  border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
}
.ms-controls .reset-btn { margin-top: 18px; }

.ms-status {
  margin: 12px 0 0;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
}
.ms-empty {
  margin: 18px 0 0;
  padding: 16px;
  font-size: 14px;
  color: var(--text-dim);
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
}

/* `hidden` has to win over the layout below, or a filtered-out card keeps its
   grid/flex display and stays on screen. */
.ms-item[hidden], .ms-year[hidden], .ms-controls[hidden] { display: none; }

.ms-year { margin: 32px 0 0; }
.ms-year-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin: 0;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
}
.ms-year-n {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.ms-year-link { margin: 8px 0 0; font-size: 13px; }

.ms-list { margin: 14px 0 0; padding: 0; list-style: none; }

.ms-item {
  margin: 0 0 14px;
  padding: 16px;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-left: 3px solid color-mix(in oklab, var(--c, var(--c-other)) 55%, var(--line));
  border-radius: var(--radius-sm);
}

.ms-head {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.ms-headtext { min-width: 0; }

.ms-title { margin: 0; font-size: 16px; letter-spacing: -0.01em; }

.ms-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
  margin: 4px 0 0;
  font-size: 12px;
  color: var(--muted);
}
.ms-meta time { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.ms-kind {
  padding: 1px 8px;
  font-size: 11px;
  color: var(--text-dim);
  background: var(--panel-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-pill);
}
.ms-lab { color: var(--text-dim); }

.ms-note { margin: 12px 0 0; font-size: 14px; color: var(--text); }
.ms-sig {
  margin: 10px 0 0;
  padding-left: 12px;
  font-size: 13px;
  color: var(--text-dim);
  border-left: 2px solid var(--line);
}

/* The dataset link. Two rows at most, so a definition list beats a table —
   and it degrades to stacked label/value on a narrow screen for free. */
.ms-line {
  display: grid;
  grid-template-columns: minmax(0, 210px) minmax(0, 1fr);
  gap: 4px 16px;
  margin: 14px 0 0;
  padding: 12px 0 0;
  font-size: 13px;
  border-top: 1px solid var(--line-soft);
}
.ms-line dt {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.ms-line dd { margin: 0; color: var(--text-dim); }
.ms-line time { font-variant-numeric: tabular-nums; }

.ms-gap {
  margin: 14px 0 0;
  padding: 12px 0 0;
  font-size: 13px;
  color: var(--muted);
  border-top: 1px solid var(--line-soft);
}

.ms-prov { margin: 14px 0 0; font-size: 12px; color: var(--muted); }
.ms-sources { margin: 6px 0 0; padding: 0; list-style: none; }
.ms-sources li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  font-size: 12px;
}
.ms-sources li > span {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
}

@media (max-width: 620px) {
  .ms-controls { gap: 14px 18px; }
  .ms-item { padding: 14px 12px; }
  .ms-line { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .ms-line dd { margin-bottom: 8px; }
  .ms-line dd:last-child { margin-bottom: 0; }
  .ms-title { font-size: 15px; }
}

/* ------------------------------------------------------------------ pills */

/* ------------------------------------------- static cadence ribbon + labs */
/*
 * Two components that existed only on the timeline, now shared with the
 * generated pages. Inserted here rather than at the end of the file only
 * because another edit owns the tail of this file.
 *
 * The ribbon: one tile is exactly one release, and the twelve columns are
 * twelve real months, so the empty space is the lab's silence rather than
 * layout. Rows are sized off the busiest month (--rows), which build.mjs
 * stamps inline, so a tile means the same thing on every row.
 *
 * It borrows --unit / --unit-gap from .ribbon deliberately: the interactive
 * ribbon already named the "one release" measurement, and two names for one
 * quantity is how the two ribbons would drift apart.
 */
.ribbon-static {
  --unit: 8px;
  --unit-gap: 2px;
  display: grid;
  gap: 5px;
  margin: 0 0 8px;
  padding: 0;
  list-style: none;
}

.rib-year {
  display: grid;
  grid-template-columns: 3.25rem minmax(0, 1fr) 2.25rem;
  align-items: end;
  gap: 10px;
}

/* Muted, like the interactive ribbon's axis — a row of accent-blue years down
   the left edge competes with the tiles, which are the reading. The link
   affordance comes back on hover. Scoped past `.doc-main a`. */
.doc-main .rib-year-num {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  text-decoration: none;
  padding-bottom: 3px;
}
.doc-main .rib-year-num:hover { color: var(--accent); text-decoration: underline; }

/* Quarter rules, at the faintest token on the page: enough to place a tile in
   the year, not enough to compete with the tiles themselves. */
.rib-months {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  align-items: end;
  gap: 0 2px;
  min-height: calc((var(--unit) + var(--unit-gap)) * var(--rows, 1));
  margin: 0;
  padding: 0 0 3px;
  list-style: none;
  border-bottom: 1px solid var(--line-soft);
  background-image: repeating-linear-gradient(
    to right, var(--grid-line) 0 1px, transparent 1px calc(100% / 4));
}

.rib-month {
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: var(--unit-gap);
  min-width: 0;
}

.rib-tile {
  position: relative;
  display: block;
  width: min(12px, 100%);
  height: var(--unit);
  flex: none;
  border-radius: 1.5px;
  background: var(--c, var(--c-other));
  transition: box-shadow 0.15s var(--ease);
}
.rib-tile:hover { box-shadow: 0 0 0 1px var(--bg), 0 0 0 3px var(--c, var(--c-other)); }
.rib-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A tile is a unit of the chart, so it cannot be made bigger; the hit area
   grows sideways into the column gutter instead. Vertically it is left alone —
   tiles in a busy month stack 2px apart, and an overlapping hit area would
   steal its neighbour's click. Every tile also appears as a full-size row
   further down the same page, which is WCAG 2.5.8's "Equivalent" exception. */
.rib-tile::after { content: ""; position: absolute; inset: 0 -4px; }

.rib-year-n {
  font-family: var(--mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--muted);
  padding-bottom: 3px;
}
/* A year the lab shipped nothing in still gets a row — that is the point of
   the ribbon — but its count should not read as data. */
.rib-year-n[data-zero="true"] { opacity: 0.4; }

@media (max-width: 620px) {
  .ribbon-static { --unit: 7px; }
  .rib-year { grid-template-columns: 2.6rem minmax(0, 1fr) 1.6rem; gap: 6px; }
  .rib-year-num, .rib-year-n { font-size: 11px; }
  .rib-tile { width: min(10px, 100%); }
}

@media (prefers-reduced-motion: reduce) {
  .rib-tile { transition: none; }
}

/* A tile is pure colour, which forced-colors would flatten to nothing. */
@media (forced-colors: active) {
  .rib-tile { forced-color-adjust: none; outline: 1px solid CanvasText; }
}

/*
 * The lab chip, as a link. Everything visual is inherited from .chip, which is
 * the whole point — the labs index and the timeline's filter bar are now the
 * same object. Only the things an anchor needs differently are restated.
 */
.lab-grid {
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.lab-grid > li { display: flex; max-width: 100%; min-width: 0; }

/* Scoped past `.doc-main a { color: var(--accent) }`, which would otherwise
   paint all 21 chips link-blue and lose the chip entirely. The name stays
   --text-dim exactly as it is on the timeline; the hue arrives via the logo. */
.doc-main a.lab-chip {
  max-width: 100%;
  padding: 5px 10px 5px 8px;
  color: var(--text-dim);
  text-decoration: none;
}
.doc-main a.lab-chip:hover { color: var(--text); }
.doc-main a.lab-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.chip-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/*
 * Fifteen rules independently rebuilt the same object — a small label with a
 * full-radius border — differing only by a pixel of padding and half a point of
 * type. The shared half is stated once here; each rule below keeps only what
 * genuinely differs (its size, and whether it is interactive).
 *
 * Grouped selectors rather than a new .pill class on purpose: adopting a class
 * would mean editing every emission site across build.mjs, app.js and
 * search.js for a result that renders identically.
 */
.chip,
.tag,
.prov-badge,
.lin-chip,
.milestone-tag,
.read-chip,
.ms-kind,
.delta-add,
.delta-drop,
.lane-count,
.src-authority {
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* The interactive ones additionally share their affordances. */
.seg-btn,
.ghost-btn,
.reset-btn,
.copy-btn,
.empty-action {
  font: inherit;
  cursor: pointer;
  border-radius: var(--radius-pill);
  transition: border-color .15s var(--ease), color .15s var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  .seg-btn, .ghost-btn, .reset-btn, .copy-btn, .empty-action { transition: none; }
}

/* ----------------------------------------------------------------- tables */
/*
 * Five table implementations shared border-collapse, a soft bottom rule,
 * left alignment, a muted header and tabular numerals, and disagreed only on
 * padding and type. The agreement is stated once.
 */
.doc-table,
.pr-table,
.quality-table,
.cmp-table,
.cap-matrix {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
  font-variant-numeric: tabular-nums;
}
.doc-table th, .pr-table th, .quality-table th, .cmp-table th, .cap-matrix th,
.doc-table td, .pr-table td, .quality-table td, .cmp-table td, .cap-matrix td {
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line-soft);
}

/* ------------------------------------------------------------- filter bar */
/*
 * /models/ is 181 rows — about fourteen screens. The list still ships whole in
 * the HTML because these pages exist to be indexable and readable without
 * JavaScript; this only hides rows once it loads, and ships `hidden` so a
 * reader without JavaScript never sees a control that does nothing.
 */
.filterbar {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin: 20px 0 4px;
  padding: 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.filterbar-search { flex: 1 1 200px; min-width: 0; }
.filterbar-search input,
.filterbar-sel select {
  width: 100%;
  font: inherit;
  font-size: 13px;
  padding: 8px 10px;
  color: var(--text);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.filterbar-sel select { width: auto; cursor: pointer; }
.filterbar-search input:focus-visible,
.filterbar-sel select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.filterbar-count {
  flex: 1 0 100%;
  margin: 0;
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.filterbar-empty {
  margin: 24px 0;
  font-size: 14px;
  color: var(--muted);
}

@media (max-width: 620px) {
  /* One control per row put the bar at 323px — 40% of a phone screen, on the
     page whose whole point is less scrolling. Two up halves it, and the
     labels are short enough to stay readable. */
  .filterbar { gap: 6px; padding: 10px; }
  .filterbar-search { flex: 1 0 100%; }
  .filterbar-sel { flex: 1 1 calc(50% - 3px); min-width: 0; }
  .filterbar-sel select { width: 100%; }
}

/* Modality glyphs on index rows.
   Shape only, never colour: the one hue channel this design has is spent on
   company identity, and a second colour-coded axis would collide with it. */
.mods {
  display: inline-flex;
  gap: 5px;
  margin-left: 8px;
  vertical-align: -2px;
}
.mod {
  width: 13px;
  height: 13px;
  flex: none;
  fill: none;
  stroke: var(--muted);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.doc-list li:hover .mod { stroke: var(--text-dim); }

@media (max-width: 620px) {
  /* The row already stacks here; the glyphs sit under the lab name. */
  .mods { margin-left: 6px; }
}

/* Modality glyphs on a lineage card.
   The card is a flex column, not a grid row, so the glyphs simply follow the
   name rather than needing a track of their own. */
.lin-card .mods {
  /* The shorthand here reset margin-left to 0 and the glyphs collided with the
     model name — "GPT-4o" ran straight into its first icon. */
  margin: 0 0 0 7px;
  gap: 4px;
}
.lin-card .mod { width: 12px; height: 12px; }

/* --------------------------------------------------------------------------
   Posts: list or grid
   --------------------------------------------------------------------------
   The timeline already offers this choice, so this reuses its vocabulary —
   the same ?view=grid parameter, the same auto-fill sizing — and the toggle
   means one thing everywhere on the site.

   The grid is NOT a second component. It re-flows the same <li> the list
   already emits, so there is no second markup path to keep in sync: a card
   here is the list row with its tracks stacked. */
.doc-list[data-view="grid"] {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
}
.doc-list[data-view="grid"] li {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  padding: 12px;
  background: linear-gradient(180deg, var(--card-sheen), transparent 55%), var(--panel);
  border: 1px solid var(--line);
  border-left: 3px solid var(--c, var(--c-other));
  border-radius: var(--radius);
}
/* The question wraps rather than truncating. A question clipped at "How has
   Claude's cont…" is precisely the one thing a reader needs to see whole —
   it is the reason the card exists. */
.doc-list[data-view="grid"] .cell-name {
  white-space: normal;
  overflow: visible;
  overflow-wrap: anywhere;
  line-height: 1.3;
  font-size: 15px;
}
.doc-list[data-view="grid"] .cell-num { text-align: left; }
.doc-list[data-view="grid"] .mods { display: none; }
/* The left hairline is the list's hover affordance; in grid the whole card
   already carries the lab's hue on its border. */
.doc-list[data-view="grid"] li::before { content: none; }

.view-switch {
  display: flex;
  justify-content: flex-end;
  margin: 0 0 10px;
}
.view-switch button {
  padding: 4px 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--text-dim);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.view-switch button:hover { background: var(--panel-2); color: var(--text); }
