/* EvalIndex — stylesheet.
   Every value here is copied from design/DESIGN.md. Sections cited inline.
   Zero webfonts, zero external requests.

   THE COLOUR RULE, and it is absolute: every colour in this file is a
   var(--token). The only literal colour values in the file are the token
   definitions in the two :root blocks below (and the two rgba() values inside
   --shadow-sticky, which DESIGN.md §2.5 states literally). A literal colour
   anywhere else is a defect — DESIGN.md §2.1/§2.2 list the pairs that pass
   contrast, and a colour that is not a token is a pair nobody computed.

   ICONS (DESIGN.md §2.8): the external-link marker renders as the literal
   character ↗ inside a chip's text run, which §2.8 permits by name; the
   chevron is an inline SVG drawn in app.js. No icon webfont, no third-party
   asset, no third glyph. */

/* ── Tokens — LIGHT theme (DESIGN.md 2.1, 2.3, 2.4, 2.5, 2.6, 2.7, 8) ───── */
:root {
  --bg-canvas: #FFFFFF;
  --bg-subtle: #F7F8F9;
  --surface: #FFFFFF;
  --surface-alt: #F4F6F7;
  --component: #EDF0F2;
  --component-hover: #E4E8EB;
  --component-active: #DAE0E4;

  --text: #14181C;
  --text-secondary: #39424B;
  --text-muted: #5C6670;
  --text-disabled: #767F88;
  --text-onaccent: #FFFFFF;

  --link: #1F4FBF;
  --link-visited: #5B3FA8;

  --accent-a: #2F3AB2;
  --accent-a-hover: #28309A;
  --accent-a-active: #212882;
  --accent-a-subtle: #EEF0FC;

  --border-subtle: #E1E5E8;
  --border-interactive: #7E8891;
  --border-strong: #767F88;

  --sponsor-bg: #FBF6EC;
  --sponsor-border: #9A7F35;
  --sponsor-text: #6B4E10;

  --stale-bg: #FDF3E7;
  --stale-text: #8A4B00;

  --code-bg: #F4F6F7;

  /* The one permitted shadow — DESIGN.md 2.5, verbatim. It exists only to
     separate a sticky table header from the rows scrolling beneath it. */
  --shadow-sticky: 0 1px 0 var(--border-interactive), 0 4px 8px -4px rgba(0, 0, 0, 0.18);

  /* Type — DESIGN.md 2.3 */
  --font-ui: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
             "Helvetica Neue", Arial, sans-serif, "Segoe UI Emoji";
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --fw-regular: 400;
  --fw-medium: 600;
  --fw-bold: 700;

  /* Ramp D — DOCUMENT (small-screen steps; the 640px block below swaps the
     three responsive ones). Every line height is a multiple of 5px. */
  --d-1: 32px;      --d-1-lh: 35px;
  --d-2: 27px;      --d-2-lh: 30px;
  --d-3: 21px;      --d-3-lh: 25px;
  --d-body: 19px;   --d-body-lh: 25px;
  --d-s: 16px;      --d-s-lh: 20px;

  /* Ramp R — REFERENCE (DESIGN.md 2.3) */
  --r-1: 28px;      --r-1-lh: 35px;
  --r-2: 18px;      --r-2-lh: 25px;
  --r-3: 16px;      --r-3-lh: 25px;
  --r-body: 14px;   --r-body-lh: 20px;
  --r-s: 13px;      --r-s-lh: 20px;

  /* Measures — DESIGN.md 4 */
  --measure-prose: 700px;
  --measure-index: 1400px;

  /* Spacing — DESIGN.md 2.4, static scale (component internals) */
  --sp-0: 0;
  --sp-1: 5px;
  --sp-2: 10px;
  --sp-3: 15px;
  --sp-4: 20px;
  --sp-5: 25px;
  --sp-6: 30px;
  --sp-7: 40px;
  --sp-8: 50px;
  --sp-9: 60px;

  /* Spacing — DESIGN.md 2.4, responsive scale (page and section rhythm only).
     Small-screen values here; the 640px block swaps them. Kept as separate
     token names so the static scale above keeps the values it has always had. */
  --sp-4r: 15px;
  --sp-5r: 15px;
  --sp-6r: 20px;
  --sp-7r: 25px;
  --sp-8r: 30px;
  --sp-9r: 40px;

  /* Radii — DESIGN.md 2.5 */
  --radius-0: 0;
  --radius-1: 2px;
  --radius-2: 4px;

  /* Motion — DESIGN.md 2.6 */
  --motion-fast: 120ms;
  --motion-base: 180ms;
  --ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --ease-exit: cubic-bezier(0.4, 0, 1, 1);

  /* Layers — DESIGN.md 2.7. Four values; a fifth is a spec amendment. */
  --z-base: 0;
  --z-sticky-header: 100;
  --z-sticky-col: 200;
  --z-popover: 300;
}

/* ── Tokens — DARK theme (DESIGN.md 2.2) ─────────────────────────────────── */
@media (prefers-color-scheme: dark) {
  :root {
    --bg-canvas: #0D1117;
    --bg-subtle: #11161C;
    --surface: #151A21;
    --surface-alt: #191F27;
    --component: #1D242C;
    --component-hover: #242C36;
    --component-active: #2C3540;

    --text: #E8EDF2;
    --text-secondary: #BFC8D2;
    --text-muted: #98A3AF;
    --text-disabled: #6B7784;
    --text-onaccent: #08111F;

    --link: #87A9FF;
    --link-visited: #C0A7F0;

    --accent-a: #9BAAFF;
    --accent-a-hover: #B3BEFF;
    --accent-a-active: #C7CFFF;
    --accent-a-subtle: #171C2E;

    --border-subtle: #242C35;
    --border-interactive: #6B7684;
    --border-strong: #75838F;

    --sponsor-bg: #1E1B14;
    --sponsor-border: #8A7440;
    --sponsor-text: #E3C98A;

    --stale-bg: #231A10;
    --stale-text: #F0B970;

    --code-bg: #11161C;

    --shadow-sticky: 0 1px 0 var(--border-interactive), 0 4px 10px -4px rgba(0, 0, 0, 0.55);
  }
}

/* ── Large screens — DESIGN.md 2.3 (Ramp D steps) and 2.4 (responsive
      spacing). Breakpoint 640px, GOV.UK's tablet breakpoint. ─────────────── */
@media (min-width: 640px) {
  :root {
    --d-1: 48px;   --d-1-lh: 50px;
    --d-2: 36px;   --d-2-lh: 40px;
    --d-3: 24px;   --d-3-lh: 30px;

    --sp-4r: 20px;
    --sp-5r: 25px;
    --sp-6r: 30px;
    --sp-7r: 40px;
    --sp-8r: 50px;
    --sp-9r: 60px;
  }
}

/* ── Shell — DESIGN.md 4 ─────────────────────────────────────────────────── */
* { box-sizing: border-box; }

html {
  font-size: 16px; /* Ramp D root */
  background: var(--bg-canvas);
}

body {
  margin: 0;
  background: var(--bg-canvas);
  color: var(--text);
  font-family: var(--font-ui);
  font-weight: var(--fw-regular);
  font-size: var(--d-body);
  line-height: var(--d-body-lh);
  -webkit-text-size-adjust: 100%;
}

.shell {
  margin: 0 auto;
  padding: var(--sp-6r) var(--sp-4r) var(--sp-9r);
}

.shell--prose { max-width: var(--measure-prose); }
.shell--index { max-width: var(--measure-index); }

/* The prose measure applies to prose even inside the wide index shell. */
.shell--index > main > h1,
.shell--index > main > .lead,
.shell--index > main > .table-heading,
.shell--index > main > .table-meta,
.shell--index > main > .table-note { max-width: var(--measure-prose); }

/* The index's intro block is tightened so the first data rows sit high enough
   to be read without scrolling at 1024px (cure D5). Nothing is removed — the
   selling sentences and the ordering/price-units note are both intact, they
   simply stop occupying half the viewport. */
.shell--index > main > .lead { margin-bottom: var(--sp-3); }

/* ── Skip link — accessibility floor ────────────────────────────────────── */
.skip-link {
  position: absolute;
  left: var(--sp-2);
  top: var(--sp-2);
  z-index: var(--z-popover);
  padding: var(--sp-2) var(--sp-3);
  background: var(--component);
  color: var(--link);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius-1);
  transform: translateY(-200%);
}

.skip-link:focus { transform: translateY(0); }

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

/* ── Nav — DESIGN.md 3.13, 8 ────────────────────────────────────────────── */
/* NO full-width header hairline (cure N18). DESIGN.md §8 specifies exactly one
   rule in the header — the wordmark's 3px accent underscore — and §3.13 asks
   for no other. A second, near-parallel hairline a few pixels below it read as
   a misaligned slip rather than as two deliberate layers, so the hairline is
   gone on all 15 pages and the accent underscore is the header's only rule.
   The padding stays: it is the spacing the hairline used to imply. */
.site-header {
  margin-bottom: var(--sp-7r);
  padding-bottom: var(--sp-3);
}

.nav {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--sp-4);
}

/* Cure N19: at phone widths the wrapped nav row put "Pricing" hard against the
   viewport edge. An end gap of --sp-2 gives it the same air as its start. */
@media (max-width: 400px) {
  .nav { padding-inline-end: var(--sp-2); }
}

.wordmark {
  display: inline-block;
  font-family: var(--font-ui);
  font-weight: var(--fw-medium);
  font-size: var(--d-2);
  line-height: var(--d-2-lh);
  letter-spacing: -0.01em;
  color: var(--text);
  text-decoration: none;
  border-bottom: 3px solid var(--accent-a);
  padding-bottom: var(--sp-1);
}

.wordmark:visited { color: var(--text); }

.nav__link {
  font-size: var(--d-s);
  line-height: var(--d-s-lh);
  font-weight: var(--fw-medium);
  color: var(--link);
  text-decoration: none;
  padding: var(--sp-1) var(--sp-1);
  min-height: 24px;
  border-bottom: 2px solid transparent;
}

.nav__link:hover { text-decoration: underline; }
.nav__link[aria-current="page"] { border-bottom-color: var(--accent-a); }

/* A real <form> now (cure D8), on every page — hence the margin reset. */
.nav__search {
  margin: 0 0 0 auto;
}

.nav__search-label {
  display: block;
  font-size: var(--r-s);
  line-height: var(--r-s-lh);
  color: var(--text-muted);
}

.nav__search-input {
  font-family: var(--font-ui);
  font-size: var(--r-body);
  line-height: var(--r-body-lh);
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius-1);
  padding: var(--sp-1) var(--sp-2);
  min-height: 28px;
  width: 15em;
  max-width: 100%;
}

.nav__search-input:hover { border-color: var(--border-strong); }

/* ── Headings and prose — DESIGN.md 2.3, sentence case per 5 ────────────── */
h1 {
  font-weight: var(--fw-bold);
  font-size: var(--d-1);
  line-height: var(--d-1-lh);
  margin: 0 0 var(--sp-5r);
  color: var(--text);
}

h2 {
  font-weight: var(--fw-bold);
  font-size: var(--d-2);
  line-height: var(--d-2-lh);
  margin: var(--sp-7r) 0 var(--sp-4);
  color: var(--text);
}

h3 {
  font-weight: var(--fw-bold);
  font-size: var(--d-3);
  line-height: var(--d-3-lh);
  margin: 0 0 var(--sp-2);
  color: var(--text);
}

p { margin: 0 0 var(--sp-5); }

/* The index page title is Ramp R, not Ramp D (DESIGN.md 2.3: --r-1 28/35 is
   "Index page title"; --d-1 belongs to the prose pages). Cure D5. */
.index-title {
  font-size: var(--r-1);
  line-height: var(--r-1-lh);
  margin-bottom: var(--sp-4);
}

/* The table group heading — --r-2 18/25 (DESIGN.md 2.3). It carries the words
   that used to be a visible <caption> inside the scrolling wrapper (cure D10);
   the caption is still there for semantics, visually hidden. */
.table-heading {
  font-size: var(--r-2);
  line-height: var(--r-2-lh);
  margin: var(--sp-4) 0 var(--sp-1);
}

/* The count line and the column toggle share one row (cure D5's "merge the
   note line into the count line area"): the count is what the toggle changes,
   and stacking them cost 40px of vertical chrome above the first data row. */
.table-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-3);
  margin-bottom: var(--sp-2);
}

/* The one polite live region on the index: the column count and, when a search
   term is active, the record count (cures B2(e), D7). One region, because two
   announcing at once talk over each other. */
.table-status {
  font-size: var(--r-body);
  line-height: var(--r-body-lh);
  color: var(--text-secondary);
  margin: 0;
}

.lead { color: var(--text); }

/* 404: the sentence app.js completes with the requested path (cure D9). */
.path-line {
  font-size: var(--d-3);
  line-height: var(--d-3-lh);
  color: var(--text);
  margin-bottom: var(--sp-4);
}

.verdict {
  font-size: var(--d-3);
  line-height: var(--d-3-lh);
  color: var(--text);
  margin-bottom: var(--sp-4);
}

.freshness {
  font-size: var(--d-s);
  line-height: var(--d-s-lh);
  color: var(--text-secondary);
  margin-bottom: var(--sp-5);
}

/* Ramp R, with the table it belongs to (DESIGN.md 2.3: Ramp R covers "the
   index table and its controls"). Cure D5's tightening. */
.table-note {
  font-size: var(--r-body);
  line-height: var(--r-body-lh);
  color: var(--text-secondary);
  margin-bottom: var(--sp-2);
}

.assessment { max-width: var(--measure-prose); }

/* Cure N12: the correction path is prose in the record's flow at --d-body, not
   small print at the bottom of it. */
.correction {
  margin-top: var(--sp-7r);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--border-subtle);
  font-size: var(--d-body);
  line-height: var(--d-body-lh);
}

/* Cure N14: the contact route repeated at the pricing decision point. */
.contact-line { margin-top: var(--sp-5); }

/* ── Links — DESIGN.md 3.7 ──────────────────────────────────────────────── */
a {
  color: var(--link);
  text-decoration: underline;
  transition: color var(--motion-fast) var(--ease-standard),
              background-color var(--motion-fast) var(--ease-standard),
              border-color var(--motion-fast) var(--ease-standard);
}

a:visited { color: var(--link-visited); }

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

/* Dense cells: the offset drops to 1px, never to 0 (DESIGN.md 2.2). */
.index-table :focus-visible,
.matrix :focus-visible { outline-offset: 1px; }

/* ── Buttons — DESIGN.md 3.7 ────────────────────────────────────────────── */
.button {
  font-family: var(--font-ui);
  font-size: var(--r-body);
  line-height: var(--r-body-lh);
  font-weight: var(--fw-medium);
  min-height: 24px;
  padding: var(--sp-2);
  border-radius: var(--radius-1);
  cursor: pointer;
  transition: background-color var(--motion-fast) var(--ease-standard),
              border-color var(--motion-fast) var(--ease-standard),
              color var(--motion-fast) var(--ease-standard);
}

.button--secondary {
  background: var(--component);
  color: var(--text);
  border: 1px solid var(--border-interactive);
}

.button--secondary:hover {
  background: var(--component-hover);
  border-color: var(--border-strong);
}

.button--secondary:active {
  background: var(--component-active);
  border-color: var(--border-strong);
}

.table-controls {
  display: flex;
  gap: var(--sp-2);
}

.table-controls:empty { display: none; }

.button__chevron {
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
  margin-left: var(--sp-1);
}

/* §2.6 forbids animating position or scale, so the chevron's rotation on the
   open state is instant — a state, not a transition. */
[aria-expanded="true"] .button__chevron { transform: rotate(180deg); }

/* ── Tables — DESIGN.md 3.1, 4 ──────────────────────────────────────────── */
/* DESIGN.md §4's verbatim wrapper, and NO max-height (cure B2(a)). The frozen
   box that used to live here capped the index at 80vh, so 12 records became
   whatever fitted and the rest needed a scroll gesture inside a box that gave
   no hint it had more: the page silently truncated the dataset it exists to
   publish. Every row now renders and the PAGE scrolls. */
.table-wrapper {
  display: block;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  border: 1px solid var(--border-subtle);
  margin-bottom: var(--sp-6r);
}

/* Cure B1 — the wrapper that does NOT clip, used by the record matrix. A
   table-layout:fixed table at width:100% cannot overflow horizontally, so
   there is nothing here to scroll and clipping only ever cost the reader the
   provenance panel's source URL. */
.table-wrapper--open { overflow: visible; }

/* Cure B2(d) — the EXPANDED index. app.js adds this class with the expansion:
   `scroll` rather than `auto` so the scrollbar is present rather than implied,
   and scrollbar-gutter keeps the layout from stepping when it appears. */
.table-wrapper--scrolling {
  overflow-x: scroll;
  scrollbar-gutter: stable;
}

.table-caption {
  text-align: left;
  font-size: var(--r-3);
  line-height: var(--r-3-lh);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
  background: var(--bg-subtle);
  padding: var(--sp-2);
  border-bottom: 1px solid var(--border-subtle);
}

.index-table {
  border-collapse: collapse;
  font-size: var(--r-body);
  line-height: var(--r-body-lh);
  width: 100%;
}

.index-table th,
.index-table td {
  text-align: left;
  vertical-align: top;
  padding: var(--sp-2);
  border-bottom: 1px solid var(--border-subtle);
  scroll-margin-top: 4em; /* the sticky header never covers a focused row */
}

/* The sticky header is kept (DESIGN.md 3.1 asks for it) with one honest
   caveat, and it is the accepted trade of cure B2(f): with the frozen box
   gone, the default state's nearest scrollport is the wrapper itself, which no
   longer scrolls vertically, and in the expanded state the horizontal
   scrollport takes over — so the header does not lift off the rows the way a
   frozen box made it. Fighting that back would mean restoring the box that hid
   the data. Header labels WRAP (they used to be nowrap) because a nowrap
   header sets the column's minimum width, and six nowrap headers could not fit
   1024px without the wrapper clipping — cure B2(c). */
.index-table thead th {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky-header);
  background: var(--component);
  color: var(--text);
  font-weight: var(--fw-medium);
  white-space: normal;
  box-shadow: var(--shadow-sticky);
}

.index-table th.col-platform {
  position: sticky;
  left: 0;
  /* Higher than the header deliberately (DESIGN.md 2.7): the corner cell
     belongs to both, and takes the column's layer so the row-identity column
     reads as continuous down the whole table. */
  z-index: var(--z-sticky-col);
  background: var(--bg-canvas);
  font-weight: var(--fw-medium);
  border-right: 1px solid var(--border-subtle);
  min-width: 11em;
}

.index-table thead th.col-platform { background: var(--component); }

.index-table td { color: var(--text-secondary); }

.index-table tbody tr:nth-child(even) td,
.index-table tbody tr:nth-child(even) th.col-platform { background: var(--surface-alt); }

.index-table tbody tr:hover td,
.index-table tbody tr:hover th.col-platform { background: var(--component-hover); }

.index-table .col-attr { min-width: 12em; }
.index-table .col-num { text-align: right; }
.index-table td { overflow-wrap: break-word; }

/* ── Index column tiers — cure B2(b), DESIGN.md 4 ────────────────────────
   Three tiers, hidden by CSS, revealed by one control:
     tier 1  every width          licence · self-host · price
     tier 2  from 1024px up       OpenTelemetry · free tier · retention
     tier 3  only when expanded   data residency · SDK languages · last release
   The hiding is gated on .has-tiers, which app.js adds — without JavaScript
   there is no control that could bring a column back, so nothing is hidden and
   the wrapper scrolls instead. A hidden column with no way to show it is the
   defect this cures, not a smaller version of it. */
.index-table.has-tiers .col-tier-2,
.index-table.has-tiers .col-tier-3 { display: none; }

@media (min-width: 1024px) {
  .index-table.has-tiers .col-tier-2 { display: table-cell; }
}

.index-table.has-tiers.is-expanded .col-tier-2,
.index-table.has-tiers.is-expanded .col-tier-3 { display: table-cell; }

/* Cure B2(c): the DEFAULT tier set must FIT its shell at 1024/1400/1920, so
   the default columns take a smaller minimum and their cells wrap. The roomier
   12em minimum returns in the expanded state, which scrolls on purpose. */
.index-table.has-tiers:not(.is-expanded) .col-attr { min-width: 7em; }

/* Sort buttons are injected by app.js — DESIGN.md 3.1 wants a visible active
   state, and a control that cannot sort is worse than no control. */
.th-sort {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  width: 100%;
  min-height: 24px;
  padding: var(--sp-1);
  background: transparent;
  border: 0;
  border-radius: var(--radius-1);
  font: inherit;
  color: var(--link);
  text-align: inherit;
  cursor: pointer;
  transition: background-color var(--motion-fast) var(--ease-standard);
}

.th-sort:hover { background: var(--component-hover); }
.th-sort:active { background: var(--component-active); color: var(--link); }

.index-table th[aria-sort="ascending"],
.index-table th[aria-sort="descending"] { background: var(--component-active); }

.index-table th[aria-sort="ascending"] .th-sort,
.index-table th[aria-sort="descending"] .th-sort { font-weight: var(--fw-bold); }

.th-sort__chev {
  width: 1em;
  height: 1em;
  color: var(--text);
  transition: none;
}

/* Cure D6. The UA stylesheet's [hidden] { display: none } rule does not apply
   to an SVG element, so the resting state's chevron was drawn on every sortable
   header — three states, one glyph, and no way to tell them apart. The author
   sheet has to say it. */
.th-sort__chev[hidden] { display: none; }

/* The glyph is drawn pointing DOWN, so descending is the untransformed state
   and ascending is the rotation. Instant, not transitioned — §2.6 forbids
   animating position or scale. */
.th-sort__chev--asc { transform: rotate(180deg); }
.th-sort__chev--desc { transform: none; }

/* The relative half of a date, appended by app.js (DESIGN.md 5). */
.rel { color: inherit; }

/* Density drops before layout breaks (DESIGN.md 4). The column hiding itself
   is applied by app.js — without JavaScript every column is present and the
   table scrolls. */
@media (max-width: 639px) {
  .index-table {
    font-size: var(--r-s);
    line-height: var(--r-s-lh);
  }
  .index-table th,
  .index-table td { padding: var(--sp-1); }
  .index-table th.col-platform { min-width: 7em; }
  .index-table .col-attr { min-width: 9em; }
  .index-table.has-tiers:not(.is-expanded) .col-attr { min-width: 5.5em; }
}

/* ── The record attribute matrix — DESIGN.md 3.2 ────────────────────────── */
.matrix {
  border-collapse: collapse;
  table-layout: fixed;
  width: 100%;
  font-size: var(--r-body);
  line-height: var(--r-body-lh);
}

.matrix th,
.matrix td {
  text-align: left;
  vertical-align: top;
  padding: var(--sp-2);
  border-bottom: 1px solid var(--border-subtle);
}

.matrix tr:last-child th,
.matrix tr:last-child td { border-bottom: 0; }

/* overflow-wrap: anywhere on both halves (cure B3/D4). table-layout: fixed
   holds the column widths, so a word longer than its column does not widen the
   cell — it spills over the neighbour's text. At 390px that put the attribute
   label on top of its own value. */
.matrix__key {
  width: 30%;
  font-weight: var(--fw-medium);
  color: var(--text);
  background: var(--bg-subtle);
  border-right: 1px solid var(--border-subtle);
  overflow-wrap: anywhere;
}

.matrix__value {
  color: var(--text-secondary);
  overflow-wrap: anywhere;
}

/* Cure B3/D4, and it has to sit AFTER the rules above to beat them: 30% of a
   390px viewport minus padding starves the key column — "OpenTelemetry" had
   nowhere to go, so it broke mid-word and crowded its own value. Density drops
   with it (DESIGN.md §4), which is what buys the extra width. */
@media (max-width: 639px) {
  .matrix {
    font-size: var(--r-s);
    line-height: var(--r-s-lh);
  }
  .matrix th,
  .matrix td { padding: var(--sp-1); }
  .matrix__key { width: 44%; }
}

/* ── Provenance chip — DESIGN.md 3.3, the signature component ───────────── */
.chip-wrap {
  position: relative;
  display: inline-block;
}

.chip {
  display: inline-block;
  min-height: 24px;
  min-width: 24px;
  padding: var(--sp-1);
  border-radius: var(--radius-1);
  font-size: var(--r-s);
  line-height: var(--r-s-lh);
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
}

.chip:visited { color: var(--text-muted); }
.chip:hover { color: var(--link); text-decoration: underline; }

.chip__mark { margin-right: 0.2em; }

/* The panel: top layer, always (DESIGN.md 2.7) — a provenance panel a sticky
   header can cover is a provenance panel the reader cannot read. It touches
   the chip on the shared edge, so the pointer can travel from one to the
   other without crossing a gap (SC 1.4.13, hoverable). */
.chip-panel {
  position: absolute;
  left: 0;
  top: 100%;
  z-index: var(--z-popover);
  display: block;
  width: max-content;
  max-width: min(320px, 80vw);
  padding: var(--sp-2);
  background: var(--surface);
  color: var(--text-secondary);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-1);
  box-shadow: var(--shadow-sticky);
  font-size: var(--r-s);
  line-height: var(--r-s-lh);
}

/* opacity as well as display, so the close is the fade cure N15 asks for and
   not an abrupt disappearance halfway through one. */
.chip-panel[hidden] {
  display: none;
  opacity: 0;
}

.chip-panel--above {
  top: auto;
  bottom: 100%;
}

/* Cure B1, belt and braces: a chip near the right edge of the viewport used to
   open a panel that ran off it. The panel flips to the chip's right edge
   instead — app.js measures and adds the class. */
.chip-panel--right {
  left: auto;
  right: 0;
}

.chip-panel__host,
.chip-panel__url,
.chip-panel__date { display: block; margin-bottom: var(--sp-1); }

.chip-panel__host {
  font-weight: var(--fw-medium);
  color: var(--text);
}

.chip-panel__url { overflow-wrap: anywhere; }

.chip-panel__date { color: var(--text-muted); }

.chip-panel__close {
  font-family: var(--font-ui);
  font-size: var(--r-s);
  line-height: var(--r-s-lh);
  min-height: 24px;
  padding: 0 var(--sp-2);
  background: var(--component);
  color: var(--text);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius-1);
  cursor: pointer;
}

.chip-panel__close:hover {
  background: var(--component-hover);
  border-color: var(--border-strong);
}

/* ── STALE — DESIGN.md 3.4 ──────────────────────────────────────────────── */
.stale-label {
  display: block;
  background: var(--stale-bg);
  color: var(--stale-text);
  border-radius: var(--radius-1);
  padding: var(--sp-2);
  font-size: var(--r-body);
  line-height: var(--r-body-lh);
  margin-bottom: var(--sp-5);
}

.stale-label strong { font-weight: var(--fw-bold); }

.stale-marker {
  display: inline-block;
  margin-left: var(--sp-1);
  background: var(--stale-bg);
  color: var(--stale-text);
  border-radius: var(--radius-1);
  padding: 0 var(--sp-1);
  font-size: var(--r-s);
  line-height: var(--r-s-lh);
  white-space: nowrap;
}

/* ── Sponsored band — DESIGN.md 3.5 (renders only when a slot is sold) ──── */
.sponsor-band {
  background: var(--sponsor-bg);
  border: 2px solid var(--sponsor-border);
  border-radius: var(--radius-2);
  padding: var(--sp-3);
  margin-bottom: var(--sp-7r);
}

.sponsor-band__label { margin-bottom: var(--sp-3); }

.sponsor-band__word {
  font-size: var(--d-3);
  line-height: var(--d-3-lh);
  font-weight: var(--fw-medium);
  color: var(--sponsor-text);
}

.sponsor-band__qualifier {
  font-size: var(--r-body);
  line-height: var(--r-body-lh);
  color: var(--sponsor-text);
}

.sponsor-card { margin-bottom: var(--sp-3); }
.sponsor-card:last-child { margin-bottom: 0; }

.sponsor-card__name {
  font-weight: var(--fw-medium);
  color: var(--text);
  margin-bottom: var(--sp-1);
}

.sponsor-card__desc,
.sponsor-card__link {
  font-size: var(--r-body);
  line-height: var(--r-body-lh);
  color: var(--text-muted);
  margin-bottom: var(--sp-1);
}

/* ── Search empty state — DESIGN.md 6.1, cure D7 ────────────────────────── */
/* It is a ROW now, inside the tbody: §6.1 puts the empty state in the "row
   area", and a message under the wrapper sat below an empty grid where a
   reader who had just typed was not looking. */
.search-empty-row[hidden] { display: none; }

.search-empty-cell {
  background: var(--bg-subtle);
  padding: var(--sp-4);
  text-align: left;
}

.search-empty__text {
  color: var(--text);
  font-size: var(--r-body);
  line-height: var(--r-body-lh);
  margin-bottom: var(--sp-3);
}

/* ── Pricing — DESIGN.md 3.12 ───────────────────────────────────────────── */
.vendor-explainer { max-width: var(--measure-prose); }

.label-sample {
  background: var(--sponsor-bg);
  color: var(--sponsor-text);
  border: 2px solid var(--sponsor-border);
  border-radius: var(--radius-2);
  padding: var(--sp-2);
  font-size: var(--r-body);
  line-height: var(--r-body-lh);
  font-weight: var(--fw-medium);
}

.cards { max-width: var(--measure-prose); }

.card {
  background: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-2);
  padding: var(--sp-4);
  margin-bottom: var(--sp-4);
}

.card__price {
  font-size: var(--d-2);
  line-height: var(--d-2-lh);
  font-weight: var(--fw-bold);
  color: var(--text);
  margin-bottom: var(--sp-1);
}

.card__tax {
  font-size: var(--d-s);
  line-height: var(--d-s-lh);
  color: var(--text-muted);
  margin-bottom: var(--sp-3);
}

.card__desc {
  color: var(--text-secondary);
  margin-bottom: 0;
}

/* ── Footer — DESIGN.md 3.13 ────────────────────────────────────────────── */
.site-footer {
  margin-top: var(--sp-8r);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--border-subtle);
  color: var(--text-muted);
  font-size: var(--d-s);
  line-height: var(--d-s-lh);
}

.site-footer__disclosure { margin-bottom: var(--sp-2); }

.site-footer__links {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  margin: 0;
  padding: 0;
}

.site-footer__links a {
  display: inline-block;
  min-height: 24px;
  padding: var(--sp-1) 0;
}

/* ── Disclosure motion — DESIGN.md 2.6, cure N15 ────────────────────────── */
/* §2.6 assigns --motion-base 180ms to "disclosure open/close … chip tooltip
   appear", and permits opacity, colour, border and height — never position or
   scale. So both disclosures fade and neither moves.
   The entering easing is --ease-standard: §2.6 names it for "entering and
   state changes" and defines no separate entrance token, so the entrance uses
   that one and the exit uses --ease-exit.
   `display` is carried in the transition with allow-discrete because both
   states are display:none at rest; where a browser does not support that, the
   change is instant, which §2.6 explicitly accepts ("a collapsed section that
   expands instantly is correct"). The reduced-motion block below is the guard
   over all of it — it zeroes every duration here. */
.chip-panel {
  transition: opacity var(--motion-base) var(--ease-exit),
              display var(--motion-base) var(--ease-exit) allow-discrete;
}

.chip-panel:not([hidden]) {
  transition: opacity var(--motion-base) var(--ease-standard),
              display var(--motion-base) var(--ease-standard) allow-discrete;
}

.index-table.has-tiers.is-expanded .col-tier-2,
.index-table.has-tiers.is-expanded .col-tier-3 {
  transition: opacity var(--motion-base) var(--ease-standard),
              display var(--motion-base) var(--ease-standard) allow-discrete;
}

@starting-style {
  .chip-panel:not([hidden]) { opacity: 0; }
  .index-table.has-tiers.is-expanded .col-tier-2,
  .index-table.has-tiers.is-expanded .col-tier-3 { opacity: 0; }
}

/* ── Reduced motion — DESIGN.md 2.6, verbatim ───────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
