/* Atlas — the Parq product-intelligence catalog.
 *
 * Two things are true at once here, and the stylesheet has to serve both.
 *
 * It is a Parq surface: cream ground, white content, sharp corners, an 8px grid,
 * Montserrat, flat 1px rules, and red spent only where it means something. The tokens
 * below are transcribed from `Parq Design System/colors_and_type.css`; the mapping and the
 * two places Atlas deliberately departs from it are recorded in
 * `docs/archive/v2/ATLAS_UI_DESIGN_HANDOFF.md`.
 *
 * It is also a dense analytical working surface. The table is the product, so everything
 * else gets out of its way: tight rows, small type, monospace for anything a reader
 * compares character by character (SKUs, raw values, digests, locator coordinates), and
 * colour used only to encode state. Monospace on technical values is an Atlas extension to
 * the brand, not a lapse from it — Montserrat carries every label, control, heading and
 * sentence, and stops exactly where character-level comparison starts.
 */

:root {
  /* ---- Parq brand ---- */
  --parq-black:    #0E0C0C;
  --parq-cream:    #F5F1EC;
  --parq-red:      #EC3327;
  --parq-red-dark: #CF1003;

  /* ---- Parq neutral scale ---- */
  --gray-900: #1F2937;
  --gray-700: #374151;
  --gray-500: #6B7280;
  --gray-300: #D1D5DB;
  --gray-200: #E5E7EB;
  --gray-100: #F3F4F6;
  --white:    #FFFFFF;

  /* ---- Surfaces ---- */
  /* Cream ground, white content. Never pure white as the page background. */
  --bg: var(--parq-cream);
  --surface: var(--white);
  --wash: var(--gray-100);          /* row hover, inset panels — neutral, never branded */
  --line: var(--gray-200);
  --line-strong: var(--gray-300);
  --line-soft: #EDEAE5;             /* hairline between dense rows, tinted toward cream */

  /* ---- Ink ---- */
  --ink: var(--parq-black);
  --ink-2: var(--gray-700);
  --dim: var(--gray-500);           /* on white surfaces only; on cream use --ink-2 */

  /* ---- Red, spent deliberately ---- */
  /* Full-strength red is used where a 3:1 contrast ratio is the requirement: the logo
   * bracket, the active-navigation rule, the focus ring, large brand type. Small red text
   * — links, section labels — uses Parq Red Dark, which clears 4.5:1 on both cream and
   * white where #EC3327 does not. Both are brand colours; the choice between them is a
   * contrast decision, not a palette invention. */
  --brand: var(--parq-red);
  --link: var(--parq-red-dark);
  --link-hover: var(--parq-black);
  --focus: var(--parq-red);

  /* ---- Buttons: the dark authoritative button the brand prescribes on cream ---- */
  --btn-bg: var(--gray-900);
  --btn-bg-hover: var(--parq-black);
  --btn-fg: var(--white);

  /* ---- Semantic state (an Atlas extension; the brand has no status palette) ---- */
  /* Drawn from the Parq chart sequence and darkened only as far as legibility required.
   * These encode what the catalog knows about a value; they are not brand accents, and
   * red is not used for any of them except genuine failure. */
  --info: #1E40AF;        --info-wash: #EDF1FB;   /* comparable, normalized, located */
  --warn: #994B19;        --warn-wash: #FBF1E7;   /* figures differ, missing, ambiguous */
  --good: #146C51;        --good-wash: #E6F1EC;   /* supported */
  --stop: var(--parq-red-dark); --stop-wash: #FCEAE8;  /* failed, rejected */

  /* ---- Published-share ramp: KB status only, see the comment at `.band-b0` below ---- */
  --band-0: var(--stop);   /* 0–19%: least covered */
  --band-1: #A83D19;       /* 20–39% */
  --band-2: var(--warn);   /* 40–59% */
  --band-3: #4F6B27;       /* 60–79% */
  --band-4: var(--good);   /* 80%+: most covered */

  /* ---- Type ---- */
  /* Montserrat first, then faces that are actually present on the machine. The design
   * system ships no font files and loads Montserrat from a CDN; Atlas makes no external
   * font call, so this stack degrades to a local grotesque rather than to a fetch. */
  --sans: "Montserrat", "Helvetica Neue", "Segoe UI", Roboto, system-ui, -apple-system, Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* ---- Motion: minimal and businesslike ---- */
  --dur-fast: 120ms;
  --dur-base: 180ms;
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
}

* { box-sizing: border-box; }

/* App shell: the page itself does not scroll, the table region does. This is what lets
 * the header stay pinned without guessing how tall the filter panel is — the table gets
 * whatever vertical space is left, measured rather than hardcoded. */
body {
  margin: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg);
  color: var(--ink);
  font: 400 14px/1.5 var(--sans);
  letter-spacing: 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

::selection { background: var(--parq-red); color: #fff; }

/* Sharp corners are a brand signature. The only rounded thing in Atlas is the pill badge
 * below, which is a genuine pill. */

/* ---- top bar ---- */
.topbar {
  display: flex;
  align-items: center;
  gap: 40px;
  padding: 0 24px;
  height: 56px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 30;
}

/* Parq is the company mark, Atlas is the product. The rule between them is the lockup —
 * it says "Parq's Atlas" without inventing a combined logo. */
.lockup { display: flex; align-items: center; gap: 12px; text-decoration: none; }
.lockup:focus-visible { outline: 2px solid var(--focus); outline-offset: 4px; }
/* 22px, not smaller. The wordmark is drawn as outlined letterforms with counters only a
 * few units wide in a 293-unit viewBox; below about 20px those counters fall under a pixel
 * and the R and Q fill in solid. The mark stops reading as PARQ at that point. */
.parqmark { height: 22px; width: auto; display: block; }
.lockup-rule { width: 1px; height: 24px; background: var(--line-strong); }
.lockup-product {
  font-size: 15px; font-weight: 600; letter-spacing: -0.01em; color: var(--ink);
}

/* The links stretch to the bar's full height so the active rule below lands on the bar's
 * own bottom edge. A shorter anchor would draw it floating in mid-air. */
.mainnav { display: flex; gap: 4px; align-self: stretch; }
.mainnav a {
  display: flex; align-items: center;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  padding: 0 12px;
  transition: color var(--dur-fast) var(--ease);
}
.mainnav a:hover { color: var(--parq-red-dark); }
.mainnav a:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
/* The active page: a red rule on the bar's own edge, so it reads as a tab rather than as
 * a second, competing button. This is one of the few places full-strength red is spent. */
.mainnav a[aria-current="page"] {
  color: var(--ink);
  font-weight: 600;
  box-shadow: inset 0 -2px 0 var(--parq-red);
}

/* ---- account ----
 *
 * Pushed to the far end of the bar with `margin-left: auto`, so it sits opposite the
 * lockup whatever the navigation holds. The panel is floating UI, which is the one case
 * the brand allows a shadow; it is still sharp-cornered and flat otherwise.
 */
.account { position: relative; margin-left: auto; align-self: stretch; display: flex; }
.account > summary {
  display: flex; align-items: center; gap: 8px;
  padding: 0 12px; cursor: pointer; list-style: none;
  font-size: 13px; color: var(--ink-2);
  transition: color var(--dur-fast) var(--ease);
}
.account > summary::-webkit-details-marker { display: none; }
.account > summary::marker { content: ""; }
.account > summary:hover { color: var(--ink); }
.account > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
/* An address can be long and the bar cannot grow, so it truncates rather than pushing the
 * navigation off the edge. The full value stays in the DOM for a screen reader. */
.account-who {
  max-width: 22ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.account-caret {
  width: 5px; height: 5px; flex: none;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--dur-base) var(--ease);
}
.account[open] > summary .account-caret { transform: rotate(-135deg) translate(-3px, -3px); }

.account-menu {
  position: absolute; top: 100%; right: 0; z-index: 50;
  min-width: 220px; padding: 12px;
  background: var(--surface);
  border: 1px solid var(--ink);
  box-shadow: 0 8px 24px rgba(14, 12, 12, 0.12);
}
.account-role {
  margin: 0 0 12px; padding-bottom: 12px; border-bottom: 1px solid var(--line);
  font-size: 11px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--dim);
}
.account-menu button {
  width: 100%;
  font: 500 14px/1 var(--sans);
  background: var(--btn-bg); color: var(--btn-fg); border: 0;
  padding: 10px 16px; cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.account-menu button:hover { background: var(--btn-bg-hover); }
.account-menu button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

main {
  flex: 1;
  min-height: 0;          /* without this a flex child refuses to shrink below content */
  display: flex;
  flex-direction: column;
  overflow: auto;         /* the page scroller: filter panel, table and pager together */
  padding: 16px 24px 24px;
}
#results { display: block; }

/* Red all-caps story-beat label, top-left. Parq Red Dark so a 10px glyph still clears
 * 4.5:1 on cream. */
.section-label {
  margin: 0 0 8px;
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.15em; color: var(--parq-red-dark);
}

/* ---- dashboard ---- */
.hero { max-width: 720px; padding: 16px 0 40px; }
.hero h1 {
  margin: 0 0 24px;
  font-size: clamp(28px, 4vw, 40px); font-weight: 700;
  line-height: 1.15; letter-spacing: -0.02em;
}
.hero-search { display: flex; gap: 8px; }
.hero-search input[type="search"] {
  flex: 1; min-width: 0;
  font: 400 16px/1.4 var(--sans);
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong);
  padding: 14px 16px;
  -webkit-appearance: none; appearance: none;
}
.hero-search input::placeholder { color: var(--dim); }
.hero-search input:focus { border-color: var(--ink); outline: 2px solid var(--focus); outline-offset: -1px; }
.hero-search button {
  font: 500 15px/1 var(--sans);
  background: var(--btn-bg); color: var(--btn-fg); border: 0;
  padding: 0 24px; cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.hero-search button:hover { background: var(--btn-bg-hover); }
.hero-search button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.hero-note { margin: 12px 0 0; max-width: 62ch; font-size: 13px; color: var(--ink-2); }
.hero-actions { margin: 24px 0 0; }
.action {
  font-size: 14px; font-weight: 500; color: var(--link); text-decoration: none;
  border-bottom: 1px solid currentColor; padding-bottom: 1px;
  transition: color var(--dur-fast) var(--ease);
}
.action:hover { color: var(--link-hover); }
.action:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

.stats { padding-bottom: 40px; }
/* The 1px grid gap draws the rules, so a card wrapping to a second row never strands a
 * dangling border. */
.statgrid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1px; background: var(--line);
  border: 1px solid var(--line);
}
.stat { background: var(--surface); padding: 16px 20px 20px; }
/* Two lines of label are reserved whether or not this one needs them, so a tile whose
 * label wraps ("Unclassified products") does not push its figure below the figures beside
 * it. A row of metrics that does not share a baseline reads as a mistake. */
.stat .lbl {
  font-size: 11px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--dim); margin-bottom: 8px;
  line-height: 1.4; min-height: 2.8em;
}
/* Numbers are SemiBold — they read as metrics by weight alone, not by colour. */
.stat .num {
  font-size: 28px; font-weight: 600; line-height: 1.1; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
/* The denominator gets its own line rather than trailing the figure and wrapping wherever
 * the tile happens to run out of room. */
.stat .num .of {
  display: block; margin-top: 6px;
  font-size: 12px; font-weight: 400; color: var(--dim); letter-spacing: 0;
}
.stats-note { margin: 12px 0 0; max-width: 78ch; font-size: 12px; color: var(--ink-2); }

/* ---- sign in ----
 *
 * Centred in the viewport, both axes. The brand's layout note warns off timid centred
 * cards floating on huge empty backgrounds, and that rule is right for a content screen
 * with a grid to align to — this page has neither. It is one short column with nothing
 * else on it, so anchoring it to the top-left leaves it stranded against the bar it no
 * longer has.
 *
 * `margin: auto` inside the flex column does both axes at once. The fields keep their
 * left-aligned labels: a centred form field is harder to scan than a centred page.
 */
.signin {
  width: 100%; max-width: 340px;
  margin: auto;
  padding: 40px 0;
  text-align: center;
}
/* Atlas is the product and Parq is the company, so the product is the word and the mark
 * stands in for the company beneath it. Stacked rather than inline: on one line the
 * wordmark's own letterforms compete with the 32px "Atlas" beside them. */
.signin-lockup { margin-bottom: 40px; }
.signin-product {
  display: block;
  font-size: 32px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.1;
  color: var(--ink);
}
.signin-by {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin-top: 10px;
  font-size: 13px; color: var(--dim);
}
.signin-by .parqmark { height: 16px; }
.signin h1 {
  margin: 0 0 24px;
  font-size: 20px; font-weight: 600; line-height: 1.2; letter-spacing: -0.01em;
}
.signin form { display: flex; flex-direction: column; gap: 16px; text-align: left; }
.signin-field { display: flex; flex-direction: column; gap: 4px; }
.signin-field > span {
  font-size: 11px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--dim);
}
.signin-field input {
  width: 100%;
  font: 400 15px/1.4 var(--sans);
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong);
  padding: 11px 14px;
  -webkit-appearance: none; appearance: none;
}
.signin-field input:focus {
  border-color: var(--ink); outline: 2px solid var(--focus); outline-offset: -1px;
}
.signin button {
  font: 500 15px/1 var(--sans);
  background: var(--btn-bg); color: var(--btn-fg); border: 0;
  padding: 14px 24px; cursor: pointer; margin-top: 4px;
  transition: background var(--dur-fast) var(--ease);
}
.signin button:hover { background: var(--btn-bg-hover); }
.signin button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.signin .error { margin: 0; text-align: left; }
.field-error { font-size: 12px; color: var(--stop); }

/* ---- breakdowns ----
 *
 * Folded away by default, and when open it is the application's own dense table rather
 * than a widget invented for this page. `table.grid` already solves the small-caps header,
 * the hairline rows, the hover and the right-aligned `.numeric` columns; reusing it means a
 * breakdown reads like the catalog and the value browser, which is most of what "looks
 * right" amounts to here.
 *
 * There is no bar. An earlier version drew one per row and it turned fifty rows into
 * banding: against a bucket holding 47% of the catalog every other bar was a sliver, so
 * the drawing carried less information than the figure beside it. The brand's own rule
 * covers this — numbers are SemiBold so they read as metrics by weight alone.
 */
.breakdowns { border-top: 1px solid var(--line); padding: 16px 0 40px; }
.breakdowns > summary {
  display: flex; align-items: baseline; gap: 12px;
  padding: 4px 0; cursor: pointer; list-style: none;
}
.breakdowns > summary::-webkit-details-marker { display: none; }
.breakdowns > summary::marker { content: ""; }
.breakdowns > summary::after {
  content: "+"; color: var(--dim); font-size: 13px; margin-left: auto;
}
.breakdowns[open] > summary::after { content: "−"; }
.breakdowns > summary .section-label { margin: 0; }
.breakdowns > summary:hover .section-label { color: var(--parq-black); }
.breakdowns > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* Exactly two tables, exactly two columns. Stated rather than derived: `auto-fit` with a
 * minmax track happened to look right at 1440px only because it collapses the empty third
 * track it had already created, and a layout that depends on that is one panel away from
 * rearranging itself. Below 760px the three-column tables have nowhere to put their
 * numbers, so they stack. */
.breakdown-grid {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 24px 32px; margin-top: 20px; align-items: start;
}
@media (max-width: 760px) { .breakdown-grid { grid-template-columns: 1fr; } }
.breakdown { min-width: 0; }
/* On the list pages .tablewrap is the growing scrollport for the results; here it is an
 * ordinary bordered box of natural height, the same override the detail page makes. */
.breakdown .tablewrap { flex: none; min-height: 0; overflow: visible; }
/* Nothing scrolls, so a sticky header would only risk painting over the rows beneath.
 *
 * It also gets a band and full-strength ink. On the catalog the header is deliberately
 * quiet — it sits above fifty rows the reader is scanning, and a loud header there would
 * compete with them. Here the table is short, read in one glance, and a header in `--dim`
 * at 11px was lighter than the 13px ink rows below it: it read as a weaker row rather than
 * as the thing naming the columns. */
.breakdown table.grid th {
  position: static;
  background: var(--wash);
  color: var(--ink);
  box-shadow: inset 0 -1px 0 var(--line-strong);
}
.breakdown table.grid td { padding: 6px 12px; }
/* The one place `td a` is not red. Everywhere else a link is one column among data
 * columns; here the label *is* the row's only text, so the brand colour on every line
 * turns a fifty-row table into a list of links and spends the page's single accent on its
 * least important surface. Ink at rest, red on hover, so the affordance is still there
 * when a pointer looks for it. */
.breakdown td a { color: var(--ink); }
.breakdown td a:hover { color: var(--link); text-decoration: underline; }
/* The unclassified bucket is a real number and a gap at the same time. */
.breakdown tr.muted td a { color: var(--dim); }

/* ---- filters ---- */
.filters {
  background: var(--surface);
  border: 1px solid var(--line);
  padding: 12px;
  margin-bottom: 12px;
}
/* The dominant control on the catalog page. */
.searchrow { display: flex; gap: 8px; }
.searchrow input[type="search"] {
  flex: 1; min-width: 0;
  font: 400 15px/1.4 var(--sans);
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong);
  padding: 11px 14px;
  -webkit-appearance: none; appearance: none;
}
.searchrow input::placeholder { color: var(--dim); }
.searchrow input:focus { border-color: var(--ink); outline: 2px solid var(--focus); outline-offset: -1px; }
.searchrow button {
  font: 500 14px/1 var(--sans);
  background: var(--btn-bg); color: var(--btn-fg); border: 0;
  padding: 0 20px; cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.searchrow button:hover { background: var(--btn-bg-hover); }
.searchrow button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* A <details>, so the specialist filters fold away without JavaScript and every control
 * inside is still submitted whether it is open or closed. Nothing was removed. */
.advanced { margin-top: 12px; border-top: 1px solid var(--line); }
.advanced > summary {
  padding: 12px 0 0; cursor: pointer; list-style: none;
  font-size: 12px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-2);
}
.advanced > summary::-webkit-details-marker { display: none; }
.advanced > summary::marker { content: ""; }
.advanced > summary::after { content: " +"; color: var(--dim); }
.advanced[open] > summary::after { content: " −"; }
.advanced > summary:hover { color: var(--parq-red-dark); }
.advanced > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.advanced .filter-grid { margin-top: 12px; }

.filter-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(208px, 1fr));
  gap: 12px 16px;
}
.filter-grid label { display: flex; flex-direction: column; gap: 4px; }
/* `> span`, not any descendant. A bare `.filter-grid span` also matched the option text
 * inside a combobox listbox rendered in this cell and shouted every category name in
 * uppercase tracked caps. */
.filter-grid label > span {
  font-size: 11px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--dim);
}
.filter-grid input, .filter-grid select {
  font: 400 13px var(--sans);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  padding: 8px 10px;
  min-width: 0;
}
.filter-grid input:focus, .filter-grid select:focus {
  outline: 2px solid var(--focus); outline-offset: -1px; border-color: var(--ink);
}
.filter-actions {
  display: flex; align-items: center; gap: 16px;
  margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--line);
}
.filter-actions button {
  font: 500 14px/1 var(--sans);
  background: var(--btn-bg); color: var(--btn-fg); border: 0;
  padding: 10px 20px; cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.filter-actions button:hover { background: var(--btn-bg-hover); }
.filter-actions button:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.filter-actions .reset { font-size: 13px; color: var(--link); text-decoration: none; }
.filter-actions .reset:hover { color: var(--link-hover); }
.facet-note { margin-left: auto; font-family: var(--mono); font-size: 11px; color: var(--dim); }

/* ---- combobox ----
 *
 * The listbox is anchored to the field, so it is exactly as wide as what it belongs to —
 * the one thing a native <datalist> would not do. It is floating UI, which is the single
 * case the brand allows a shadow (`--shadow-pop`); it is still sharp-cornered and flat
 * everywhere else.
 */
.combo { position: relative; min-width: 0; }
.searchrow .combo, .hero-search .combo { flex: 1; }
/* The field is no longer a flex child of the search row — the wrapper is — so it needs an
 * explicit width. Without this it falls back to an <input>'s intrinsic ~20-character size
 * and sits narrow inside a full-width listbox. */
.combo > input { width: 100%; }

.combo-list {
  position: absolute;
  top: calc(100% - 1px);
  left: 0;
  right: 0;
  z-index: 40;
  /* Overridden per variant below. */
  margin: 0;
  padding: 0;
  list-style: none;
  max-height: 320px;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--surface);
  border: 1px solid var(--ink);
  box-shadow: 0 8px 24px rgba(14, 12, 12, 0.12);
}
.combo-list[hidden] { display: none; }

.combo-option {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 8px 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--line-soft);
}
.combo-option:last-child { border-bottom: 0; }
/* One highlight, driven by aria-selected's own class, so what is announced and what is
 * drawn cannot disagree. Keyboard and pointer land on the same state. */
.combo-option.active { background: var(--wash); }
.combo-option.active .combo-value { color: var(--ink); }

/* Two variants, because the two lists hold different kinds of thing.
 *
 * A SKU suggestion is a technical value compared character by character, so it is
 * monospace and the list spans the whole search field. A category or brand name is prose:
 * sans, sentence case, and a list sized to its own content rather than stretched to a
 * narrow filter cell — which is what made "Fiber Optic Connectivity" wrap onto three
 * lines. */
.combo-value { font-size: 13px; color: var(--ink-2); }
.combo-list--mono .combo-value { font-family: var(--mono); font-size: 12.5px; }
.combo-list--text {
  right: auto;
  min-width: 100%;
  width: max-content;
  max-width: 340px;
}
.combo-list--text .combo-option { display: block; }
.combo-label {
  font-size: 10px;
  color: var(--dim);
  white-space: nowrap;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* The text field that stands in front of a filterable <select>. It has to sit on exactly
 * the same box as the native controls beside it, or the filter grid goes ragged. */
.combo-input {
  width: 100%;
  font: 400 13px var(--sans);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  padding: 8px 10px;
  min-width: 0;
}
.combo-input:focus { outline: 2px solid var(--focus); outline-offset: -1px; border-color: var(--ink); }

/* ---- result bar ---- */
.resultbar {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-bottom: 8px; font-size: 13px; color: var(--ink-2);
}
.count strong { font-weight: 600; font-variant-numeric: tabular-nums; }
.term strong { font-family: var(--mono); font-weight: 600; color: var(--ink); }
.hint { color: var(--dim); font-size: 12px; }
.warn {
  color: var(--warn); background: var(--warn-wash);
  padding: 3px 8px; font-size: 12px;
}
.error {
  background: var(--stop-wash); color: var(--stop);
  border: 1px solid var(--stop);
  padding: 12px 16px; font-size: 13px;
}

/* ---- the grid ---- */
/* `overflow-x: auto` makes this a scroll container on BOTH axes — CSS computes a
 * non-visible overflow on one axis to `auto` on the other. That means `position: sticky`
 * on the header resolves against this box, not the viewport, so any `top` offset here
 * opens a gap that the first data row shows through. The header therefore sticks at
 * `top: 0`, and the box owns its own vertical scroll so sticky has something to stick to. */
.tablewrap {
  background: var(--surface);
  border: 1px solid var(--line);
  overflow: auto;
  flex: 1;
  min-height: 220px;
}
/* The list page is the exception: its table is not a scrollport at all. Confined to the
 * space left over by the filter panel it showed seven of fifty rows on a 900px screen,
 * with its own scrollbar inside a page that did not scroll — two scrollbars arguing about
 * one list. Here the table renders at its full height and `main` scrolls the page.
 *
 * The header still sticks, and better than before: with no overflow on this box, sticky
 * resolves against `main` instead, so the column names pin under the top bar for the whole
 * page rather than only within the table's own box. Nothing needs `overscroll-behavior`
 * once the box does not scroll. The detail page keeps `.tablewrap` as written above — it
 * is a horizontal scroller there, which is a different job. */
#results .tablewrap { overflow: visible; flex: none; min-height: 0; }

[aria-busy="true"] { opacity: .55; transition: opacity var(--dur-fast) var(--ease); }

table.grid { border-collapse: collapse; width: 100%; font-size: 13px; }
table.grid th {
  /* top: 0 — the scrollport is .tablewrap, not the viewport. See the note there. */
  position: sticky; top: 0; z-index: 10;
  background: var(--surface);
  text-align: left; white-space: nowrap;
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--dim); font-weight: 600;
  padding: 10px 12px;
  /* A sticky header has rows moving under it, so its edge must be drawn on the header
   * itself: a border-bottom on a sticky th does not repaint reliably while scrolling. */
  box-shadow: inset 0 -1px 0 var(--line-strong);
}
table.grid th.sortable a { color: inherit; text-decoration: none; }
table.grid th.sortable a:hover { color: var(--parq-red-dark); }
table.grid th.sortable a:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
table.grid th.active { color: var(--ink); }
.arrow { margin-left: 4px; color: var(--parq-red-dark); }
table.grid td {
  padding: 8px 12px;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: top;
  max-width: 460px;
  /* Locators, digests and excerpts are long tokens with no spaces in them. `max-width`
   * alone cannot break those — the cell just overflows and paints over the next column,
   * which is what broke the identity table. `anywhere` lets them wrap mid-token. */
  overflow-wrap: anywhere;
}
/* Row hover is a neutral wash. Red on every row a pointer crosses is exactly the sprinkle
 * the brand forbids, and it would also drown the state colours the row is carrying. */
table.grid tbody tr:hover { background: var(--wash); }
table.grid tbody tr:last-child td { border-bottom: 0; }
td a { color: var(--link); text-decoration: none; }
td a:hover { color: var(--link-hover); text-decoration: underline; }
td a:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.mono { font-family: var(--mono); font-size: 12px; }
.dim { color: var(--dim); }
.value { word-break: break-word; }
.coord { line-height: 1.35; white-space: nowrap; }
.empty { padding: 32px 12px; text-align: center; color: var(--dim); }
.empty strong { font-family: var(--mono); color: var(--ink); }

/* ---- state pills ---- */
/* A pill badge is the one rounded object the brand allows, at 14px. */
.pill {
  display: inline-block; font-family: var(--mono); font-size: 10.5px;
  padding: 3px 8px; border-radius: 14px; white-space: nowrap;
  background: var(--gray-100); color: var(--ink-2);
}
.pill-normalized, .pill-located { background: var(--info-wash); color: var(--info); }
.pill-failed { background: var(--stop-wash); color: var(--stop); }
.pill-unprocessed, .pill-unassessed { background: var(--gray-100); color: var(--dim); }
.pill-supports { background: var(--good-wash); color: var(--good); }
.pill-ambiguous, .pill-internal_conflict, .pill-unclear { background: var(--warn-wash); color: var(--warn); }

/* ---- pager ---- */
.pager {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 12px 0;
}
.pager .pages { display: flex; align-items: center; gap: 4px; }
.pager .page, .pager .step {
  font-family: var(--mono); font-size: 12px; font-variant-numeric: tabular-nums;
  color: var(--ink-2); background: var(--surface);
  border: 1px solid var(--line-strong);
  padding: 7px 10px; text-decoration: none; min-width: 34px; text-align: center;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.pager a.page:hover, .pager a.step:hover {
  border-color: var(--ink); color: var(--ink);
}
.pager a.page:focus-visible, .pager a.step:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 2px;
}
.pager .page.current {
  background: var(--ink); border-color: var(--ink); color: var(--white); font-weight: 600;
}
.pager .step.disabled { color: var(--dim); opacity: .5; }
.pager .gap { color: var(--dim); padding: 0 2px; }
.pager .jump { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.pager .jump span {
  font-size: 11px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--dim);
}
.pager .jump select {
  font: 12px var(--mono); color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong); padding: 6px 8px;
}

@media (max-width: 720px) {
  /* On a small screen the shell gives way to ordinary page scrolling. */
  body { height: auto; overflow: visible; }
  main { overflow: visible; }
  table.grid th { position: static; }
  .tablewrap { flex: none; min-height: 0; }
  main { padding: 16px; }
  .topbar { padding: 0 16px; gap: 16px; }
  /* Both search rows stack rather than pushing their button off the viewport. */
  .hero-search, .searchrow { flex-direction: column; }
  .hero-search button, .searchrow button { padding: 14px 20px; }
  /* One column, so an odd tile count never leaves an empty cell showing the 1px-gap
   * background as a dead grey tile beside the last figure. */
  .statgrid { grid-template-columns: 1fr; }
}


/* ---- product detail ---- */
/* The detail page is a document, not a working surface: it flows top to bottom and `main`
 * scrolls. The list page's growth chain does not apply — a .tablewrap here is a horizontal
 * scroller of natural height, and giving it flex:1 inside a scrolling parent is what made
 * the table spill past its own container. */
.detail { display: flex; flex-direction: column; gap: 32px; padding-bottom: 40px; }
.detail .tablewrap {
  flex: none; min-height: 0; max-height: none;
  overflow-x: auto; overscroll-behavior: auto;
  background: none; border: 0;
}

.pmast { border-bottom: 1px solid var(--line-strong); padding-bottom: 24px; }

/* ---- source documents ----
 *
 * A card each, rather than one sentence each. These rows carry four properties and up to
 * three links, and as prose that became a dash-separated line ending in a parenthesis —
 * unreadable at the exact moment someone is checking whether the right sheet is attached.
 *
 * The digest gets its own line and stays whole: it is how a document is identified
 * everywhere else in this system, so truncating it to look tidy would cost the one thing
 * the line is for. The properties reuse `.fact` from the masthead above, so a document
 * reads like the product it hangs off rather than like a widget invented for this list. */
/* As many columns as fit, never one card spanning the window: a card 2000px wide puts
 * its four short values an arm's length apart. The grid itself takes the full width —
 * what bounds a card is the column floor, and 460px is a measurement rather than a
 * taste: the 64-character digest is ~420px of 11px mono and wraps into nonsense below
 * it, so a narrower column would break the one line that identifies the document. */
.doclist {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(460px, 1fr));
}
.doc { background: var(--surface); border: 1px solid var(--line); padding: 16px; }
.doc-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 12px; }
.doc-head h3 { margin: 0; font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
.doc-id {
  margin: 6px 0 0; font-family: var(--mono); font-size: 11px;
  color: var(--dim); overflow-wrap: anywhere;
}
.doc dl.facts { margin-top: 14px; gap: 12px 32px; }
/* The associations under one file. The label is the same small caps as a fact's `dt`,
 * because that is what it is: one more property of this document, which happens to hold a
 * row per provenance record rather than a single value. */
.doc-uses-label {
  margin: 14px 0 6px; font-size: 10px; font-weight: 500; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--dim);
}
.doc-uses { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.doc-uses li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.doc-uses .use { font-size: 13px; }
.doc-uses .batch { font-family: var(--mono); font-size: 11.5px; color: var(--dim); }
/* Same row shape as a card's "Linked for" list -- a dataset line is that same kind of
 * fact, just standing outside any card. The gap only sets it off from the card grid (or
 * the empty-state line) above; everything else it reuses. */
.dataset-lines { margin-top: 16px; }
.doc-links {
  display: flex; flex-wrap: wrap; gap: 8px 20px;
  margin: 14px 0 0; padding-top: 12px; border-top: 1px solid var(--line-soft);
  font-size: 13px;
}
.doc-links a { color: var(--link); text-decoration: none; }
.doc-links a:hover { color: var(--link-hover); text-decoration: underline; }
.doc-links a:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* What this link hands over, worn by the link rather than trailing it: tight against
 * the word, and a chip rather than a word of its own so the row still reads as three
 * links and not six. Square, like everything here except the one genuine pill. */
.doc-links .ext {
  margin-left: 5px; padding: 1px 4px;
  font-family: var(--mono); font-size: 9.5px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--ink-2); background: var(--wash); border: 1px solid var(--line);
  vertical-align: 1px;
}
.doc-links .ext:empty { display: none; }
/* Which import a row came from answers a different question than the row itself, and
 * gets the weight to match: a reader scans names and values, then looks here only when
 * a value surprises them. */
td.batch { font-family: var(--mono); font-size: 11.5px; }
/* A link and its badge wrap together or not at all. */
.doc-links .doc-link { display: inline-flex; align-items: baseline; }

/* The preview button is a link in weight and colour — it sits in the same row as the
 * links and does the same kind of thing — but it is a button, because it opens a
 * dialog on this page rather than navigating. */
.doc-preview-open {
  font: inherit; font-size: 13px; color: var(--link); background: none; border: 0;
  padding: 0; cursor: pointer;
}
.doc-preview-open:hover { color: var(--link-hover); text-decoration: underline; }
.doc-preview-open:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* Nearly the whole viewport: a datasheet is read, not glanced at, and the point of
 * previewing in place is to keep the product page one keypress away, not to see both
 * at once. Sharp corners, like everything else here. */
dialog.doc-preview {
  width: min(96vw, 1400px); height: 92vh; padding: 0; border: 1px solid var(--line-strong);
  background: var(--surface); color: var(--ink);
  display: none; flex-direction: column;
}
dialog.doc-preview[open] { display: flex; }
dialog.doc-preview::backdrop { background: rgba(14, 12, 12, 0.55); }
.doc-preview-bar {
  display: flex; align-items: center; gap: 16px; padding: 10px 16px;
  border-bottom: 1px solid var(--line); font-size: 13px;
}
.doc-preview-title { flex: 1; font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.doc-preview-newtab { color: var(--link); text-decoration: none; }
.doc-preview-newtab:hover { color: var(--link-hover); text-decoration: underline; }
.doc-preview-newtab:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.doc-preview-close {
  font: 500 13px/1 var(--sans); background: var(--btn-bg); color: var(--btn-fg); border: 0;
  padding: 8px 14px; cursor: pointer;
}
.doc-preview-close:hover { background: var(--btn-bg-hover); }
.doc-preview-close:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.doc-preview-frame { flex: 1; width: 100%; border: 0; background: var(--wash); }

/* Where this page sits in the application. The Catalog crumb carries the reader's own
 * result set, so the trail does not quietly drop their filters. */
.crumbs {
  display: flex; flex-wrap: wrap; align-items: center;
  font-size: 12px; font-weight: 500; margin-bottom: 12px;
}
.crumbs a { color: var(--link); text-decoration: none; }
.crumbs a:hover { color: var(--link-hover); text-decoration: underline; }
.crumbs a:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.crumbs > * + *::before { content: "/"; margin: 0 8px; color: var(--line-strong); }
.crumb-here { color: var(--ink-2); font-family: var(--mono); font-size: 11.5px; }

.backlink {
  display: inline-block; margin-bottom: 16px;
  font-size: 11px; font-weight: 500; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-2); text-decoration: none;
}
.backlink:hover { color: var(--parq-red-dark); }
.backlink:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.eyebrow {
  display: flex; flex-wrap: wrap; font-size: 10px; font-weight: 700;
  letter-spacing: 0.15em; text-transform: uppercase; color: var(--dim);
}
.eyebrow span + span::before { content: "/"; margin: 0 8px; color: var(--line-strong); }
.eyebrow .on { color: var(--parq-red-dark); }
/* Who makes it. The brand is half of a product's identity — a SKU means nothing
 * without it, and two manufacturers reuse the same digits — so on this page it is read
 * at a glance rather than squinted at. `.eyebrow` stays as it is for every other page
 * that uses it as a quiet label. */
.eyebrow.pbrand { font-size: 13px; letter-spacing: 0.08em; color: var(--ink); }

/* Where the product sits. Big enough to read before the SKU is compared, small enough
 * not to compete with it. The adopted steps are separated by "/" like every other
 * breadcrumb in this application; the decoder's leaf label gets "·" and the dim colour
 * because it is a finer label from the source, not a third level of the taxonomy. */
.ptrail { display: flex; flex-wrap: wrap; align-items: baseline; margin: 10px 0 0; font-size: 14px; }
.ptrail-step { font-weight: 500; }
.ptrail-step + .ptrail-step::before { content: "/"; margin: 0 8px; color: var(--line-strong); font-weight: 400; }
.ptrail-leaf { font-weight: 400; color: var(--ink-2); }
.ptrail-step + .ptrail-leaf::before { content: "·"; }
.ptrail-none { font-weight: 400; color: var(--dim); }

/* The SKU is the heading, and a SKU is compared character by character. */
.detail h1 {
  margin: 12px 0 0; font-family: var(--mono); font-size: clamp(24px, 4vw, 36px);
  font-weight: 600; letter-spacing: 0;
}
dl.facts { display: flex; flex-wrap: wrap; gap: 12px 40px; margin: 16px 0 0; }
.fact { display: flex; flex-direction: column; gap: 4px; }
.fact dt {
  font-size: 10px; font-weight: 500; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--dim);
}
.fact dd { margin: 0; font-family: var(--mono); font-size: 13px; }
/* A classification name is prose, not a value anyone compares character by character. */
.fact dd.prose { font-family: var(--sans); }

/* Label above number: the subject has to arrive before the figure. The 1px grid gap draws
 * the rules, so a card wrapping to a second row never strands a dangling border. */
.scoreboards { display: grid; gap: 16px; margin-top: 24px; }
.scoreboard {
  display: grid; grid-template-columns: 116px minmax(0, 1fr);
  gap: 12px; align-items: stretch;
}
.scoreboard > h2 {
  margin: 0; padding: 14px 0;
  font-size: 11px; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-2);
}
.scores {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(216px, 1fr));
  gap: 1px; background: var(--line); margin: 0;
  border: 1px solid var(--line);
}
.score { background: var(--surface); padding: 16px 20px 20px; }
.score .lbl {
  font-size: 10px; font-weight: 500; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--dim); margin-bottom: 8px;
}
.score .num {
  font-size: 24px; font-weight: 600; line-height: 1.1;
  font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
}
.score .num .of { font-size: 13px; font-weight: 400; color: var(--dim); letter-spacing: 0; }
.score.alarm { background: var(--warn-wash); box-shadow: inset 0 2px 0 var(--warn); }
.score.alarm .num, .score.alarm .lbl { color: var(--warn); }
.score.alarm .num .of { color: var(--warn); opacity: .75; }

.sechead { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 16px; margin-bottom: 12px; }
.sechead h2 {
  margin: 0; font-size: 11px; font-weight: 700;
  letter-spacing: 0.15em; text-transform: uppercase;
}
.sechead .tally { font-size: 11.5px; color: var(--dim); }
/* .warn is a filled badge on the list page; here it is only a colour. */
.sechead .tally.warn { color: var(--warn); background: none; padding: 0; }

/* Every row uses identical fixed tracks, so the chip and count columns line up down the
 * page. Auto tracks would size to each row's own content and read as ragged. */
.ledger {
  border: 1px solid var(--line);
  overflow: hidden; background: var(--surface);
}
.ledger.flat { border: 0; }
.attr { border-bottom: 1px solid var(--line-soft); }
.attr:last-child { border-bottom: 0; }
.attr > summary, .attr.absent {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 2.6fr) 132px 82px 16px;
  gap: 16px; align-items: baseline; padding: 12px 16px;
  border-left: 3px solid transparent; cursor: pointer; list-style: none;
  font-family: var(--mono); font-size: 12.5px;
}
.attr.absent { cursor: default; opacity: .7; }
.attr > summary::-webkit-details-marker { display: none; }
.attr > summary::marker { content: ""; }
.attr > summary:hover { background: var(--wash); }
.attr > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.attr.differs > summary { border-left-color: var(--warn); }
.attr[open] > summary { background: var(--wash); border-bottom: 1px solid var(--line-soft); }
/* The attribute's name is prose, not a value to compare. */
.aname { font-family: var(--sans); font-weight: 500; color: var(--ink); overflow-wrap: anywhere; }

/* The compared figures share a track split, so every "vs" stacks on one vertical line. */
.vals { display: grid; grid-template-columns: 1fr auto 1fr; align-items: baseline; width: 100%; }
.vals.one { display: block; }
.vals.many { display: flex; flex-wrap: wrap; gap: 4px 12px; }
.vals .v.a { text-align: right; }
.vals .v.b { text-align: left; }
.attr.differs .v { color: var(--warn); font-weight: 600; }
.vals .sep {
  color: var(--dim); margin: 0 8px; font-size: 10px;
  text-transform: uppercase; letter-spacing: 0.08em;
}
.vals.one b { font-weight: 600; }
.vals.raw { color: var(--ink-2); font-weight: 400; }
.vals .none, .cells .none { color: var(--dim); font-style: italic; }
/* Scoped: .gap is also the pager's ellipsis. */
.aval .gap {
  display: block; margin-top: 4px; font-size: 10.5px;
  color: var(--warn); text-align: center;
}
.acount { color: var(--dim); font-size: 11.5px; white-space: nowrap; text-align: right; }
.acaret {
  justify-self: end; align-self: center; width: 6px; height: 6px;
  border-right: 1.5px solid var(--dim); border-bottom: 1.5px solid var(--dim);
  transform: rotate(-45deg); transition: transform var(--dur-base) var(--ease);
}
.attr[open] .acaret { transform: rotate(45deg); }
/* An attribute with nothing linked has nothing to disclose. Drawing a caret beside it
 * would promise a row that opens, which is exactly the lie `_attr_row.html` avoids by
 * rendering it as a plain div rather than a <details>. */
.attr.absent .acaret { display: none; }

/* A square tag, 0px, per the brand's square-tag specimen — distinct from the rounded pill
 * so the two kinds of state never read as the same object. */
.chip {
  display: inline-block; padding: 3px 8px; font-family: var(--sans);
  font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em;
  white-space: nowrap; border: 1px solid;
}
.chip-differs { color: var(--warn); background: var(--warn-wash); border-color: var(--warn); }
.chip-comparable { color: var(--info); background: var(--info-wash); border-color: var(--info); }
.chip-published, .chip-absent { color: var(--dim); background: none; border-color: var(--line-strong); }

.attrbody { padding: 4px 16px 16px; background: var(--wash); }
table.cells {
  width: 100%; border-collapse: collapse;
  font-family: var(--mono); font-size: 11.5px;
}
table.cells th {
  text-align: left; font-family: var(--sans); font-weight: 600; color: var(--dim);
  font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 12px 16px 8px 0;
  white-space: nowrap; border-bottom: 1px solid var(--line-strong);
}
table.cells td {
  padding: 12px 16px 12px 0; border-bottom: 1px solid var(--line-soft);
  vertical-align: top; color: var(--ink-2);
}
table.cells tr:last-child td { border-bottom: 0; }
table.cells td.src { min-width: 200px; color: var(--ink); }
.src .pub { display: block; font-family: var(--sans); font-weight: 500; color: var(--ink); }
.src .doc { display: block; margin-top: 4px; }
.src .prov { display: block; margin-top: 4px; color: var(--dim); font-size: 10px; }
.src .host { max-width: 26ch; }
.src abbr { text-decoration: none; border-bottom: 1px dotted var(--line-strong); cursor: help; }
table.cells td.coord, table.cells td.ex {
  overflow-wrap: anywhere; white-space: normal; min-width: 170px; max-width: none;
}
.collapse-note { margin: 12px 0 0; font-size: 11px; color: var(--dim); }
.panel {
  border: 1px solid var(--line);
  background: var(--surface); padding: 4px 16px 8px;
}

details.bulk {
  border: 1px solid var(--line);
  background: var(--surface); overflow: hidden;
}
details.bulk > summary {
  padding: 12px 16px; cursor: pointer; font-size: 13px; font-weight: 500;
  list-style: none;
}
details.bulk > summary::-webkit-details-marker { display: none; }
details.bulk > summary::marker { content: ""; }
details.bulk > summary:hover { background: var(--wash); }
details.bulk > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
details.bulk[open] > summary { border-bottom: 1px solid var(--line-soft); }

@media (max-width: 720px) {
  .scoreboard { grid-template-columns: 1fr; gap: 0; }
  .scoreboard > h2 { padding-bottom: 8px; }
}

.legend {
  padding-top: 16px; border-top: 1px solid var(--line-strong);
  font-size: 12px; color: var(--ink-2); display: grid; gap: 8px;
}
.legend p { margin: 0; max-width: 80ch; line-height: 1.5; }
.legend .chip { margin-right: 8px; }

/* ---- clean catalog page head ---- */
/* The title block is read once and then never again; the table under it is read all day.
 * It keeps the eyebrow, the name and the sentence that says what the rows are, at the size
 * that costs the list the least. */
.pagehead { margin-bottom: 12px; }
.pagehead .eyebrow { display: block; color: var(--parq-red-dark); }
.pagehead h1 { margin: 2px 0 0; font-size: 20px; font-weight: 700; letter-spacing: -0.02em; }
.lede { margin: 4px 0 0; max-width: 80ch; font-size: 12px; color: var(--ink-2); }

/* `table.grid th` sets text-align:left and, at one class plus two elements, outranks a
 * bare `.numeric`. Without this the header sat hard left while its figures sat hard right,
 * so each pair straddled a wide column and read as two different columns.
 * `width: 1%` with nowrap collapses these to their content under auto table layout, which
 * keeps the figures together at the right rather than sprawling across the free space. */
.numeric { text-align: right; }
table.grid th.numeric { text-align: right; }
table.grid th.numeric, table.grid td.numeric { width: 1%; white-space: nowrap; }
td.numeric { font-variant-numeric: tabular-nums; }
.normalized { display: block; font-size: 11px; }

/* Coverage meters. Twelve families across nine columns is a hundred figures; the bar is
 * what makes a row scannable, and the figure above it is what makes the bar accountable.
 * 3px and no label: it sits under a number that already says the same thing exactly.
 *
 * Ink on a grey track, one colour for every cell. Colouring the bar by value would put a
 * verdict on each figure, and there is no threshold in the publication rules to draw it
 * at; length already carries the comparison, which is all this table is asking the reader
 * to make. It also keeps the page's own rule intact — see `.short` below, where amber is
 * the attention colour and red belongs to the brand and to genuine failure.
 *
 * The width is inline because it is data: it is the percentage in the cell above it. */
td.numeric .meter {
  display: block; width: 64px; height: 3px; margin: 4px 0 0 auto;
  background: var(--line); overflow: hidden;
}
td.numeric .meter > span { display: block; height: 100%; background: var(--ink); }
/* A shortfall against the category contract. Amber is the one attention colour on this
 * surface and is spent only where a person has to do something. Red is not used here: red
 * belongs to the brand and to genuine failure, and a missing attribute is neither. */
.short { color: var(--warn); font-weight: 600; }

/* KB status's Published cell is the one place on this surface that colours a meter by
 * value, and it reads red at the bottom. Both look like the rule just above was
 * forgotten; it was not. Published share is a progress-to-goal measure, not a fact
 * about a single value, so a red-through-green traffic light reads correctly here in
 * a way it would not for a shortfall against a fixed contract. `--band-1` and
 * `--band-3` are chosen to sit in the same desaturated, print-like register as
 * `--warn`/`--good` rather than as flat web red/orange — this is still Atlas's muted
 * palette, just run across five steps instead of two. Scoped to `.band-bN`, which only
 * this page's template ever adds, so the generic `.meter` above is untouched.
 */
.band-b0 { color: var(--band-0); }
.band-b1 { color: var(--band-1); }
.band-b2 { color: var(--band-2); }
.band-b3 { color: var(--band-3); }
.band-b4 { color: var(--band-4); }
td.numeric .meter.band-b0 > span { background: var(--band-0); }
td.numeric .meter.band-b1 > span { background: var(--band-1); }
td.numeric .meter.band-b2 > span { background: var(--band-2); }
td.numeric .meter.band-b3 > span { background: var(--band-3); }
td.numeric .meter.band-b4 > span { background: var(--band-4); }

/* The legend's swatches carry the mapping in colour; the numbers beside them carry it
 * in text, so the swatch itself can stay decorative (aria-hidden) without the meaning
 * going colour-only. Sized to a small text run, not a UI control. */
.band-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; }
.band-swatch {
  display: inline-block; width: 10px; height: 10px; border-radius: 2px;
  vertical-align: -1px;
}
.band-swatch.band-b0 { background: var(--band-0); }
.band-swatch.band-b1 { background: var(--band-1); }
.band-swatch.band-b2 { background: var(--band-2); }
.band-swatch.band-b3 { background: var(--band-3); }
.band-swatch.band-b4 { background: var(--band-4); }

.pill-sku { background: var(--info-wash); color: var(--info); }
.pill-family_stem, .pill-series { background: var(--warn-wash); color: var(--warn); }
.pill-fetched { background: var(--good-wash); color: var(--good); }
.pill-not_fetched, .pill-unavailable { background: var(--warn-wash); color: var(--warn); }

@media (max-width: 760px) {
  .attr > summary, .attr.absent { grid-template-columns: minmax(0, 1fr) auto; gap: 8px 16px; }
  .aval { grid-column: 1 / -1; }
  .acount { text-align: left; }
  .acaret { display: none; }
  .vals { grid-template-columns: auto auto auto; justify-content: start; }
  .vals .v.a, .aval .gap { text-align: left; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0ms !important; }
}


/* ---- external links ---- */
/* A link that leaves the app says so: the arrow marks it visually, and the
 * visually-hidden text says it for a screen reader without cluttering the table. */
a.ext { color: var(--link); text-decoration: none; }
a.ext:hover { color: var(--link-hover); text-decoration: underline; }
a.ext:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.extmark { font-size: 10px; margin-left: 4px; }
.host {
  display: block; font-family: var(--mono); font-size: 11px;
  max-width: 34ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The key half of a locator part: `member`, `wave`, `campaign`, `document_sha256`… The
 * catalog addresses its sources six different ways, so the key is shown rather than
 * assumed. */
.lkey { color: var(--dim); }
.coord { white-space: normal; max-width: 32ch; line-height: 1.4; }
