/* ==========================================================================
   Epic floonet explorer
   --------------------------------------------------------------------------
   The design system is inherited from epic-devhub (devdocs.btlabs.uk) so the
   two read as siblings. Tokens, type rules and geometry are transcribed from
   epic-devhub/site/src/css/custom.css; the components are new, because an
   explorer is a dense-data surface rather than a prose surface.

   Rules carried over verbatim, and worth not relitigating:
     * Never write a raw length for padding, margin or gap. Use --sp-*.
     * Depth comes from surface value plus a 1px hairline, never a drop shadow.
     * No hover transforms. Promoting a card to a compositing layer switches its
       text to grayscale antialiasing and looks blurry.
     * Square geometry for anything data-shaped. Radii only on cards and chips.
     * Gold is a mark and a state, never a large fill and never body text on
       paper. On paper, gold-as-text is #8a5a1d and gold-as-mark is #b87a2a;
       real #f6d471 appears only inside charcoal chrome.
     * Montserrat at heading sizes only. Below ~14px a geometric display face
       renders mushy, so small UI text uses the platform stack and all chain
       data uses monospace.
   ========================================================================== */

/* Montserrat, variable weight 100-900, self-hosted (SIL OFL). Self-hosted
   rather than Google Fonts so no reader IP leaves this host, which is also why
   the CSP can say font-src 'self'. */
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/montserrat-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
    U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/montserrat-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308,
    U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
    U+2C60-2C7F, U+A720-A7FF;
}

/* ---------------------------------------------------------------- tokens */

:root {
  /* Brand */
  --gold-light: #f6d471;
  --gold-deep: #c98a3e;
  --gradient-gold: linear-gradient(93.95deg, #f6d471 0%, #c98a3e 100%);

  /* Type */
  --font-ui: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-display: 'Montserrat', var(--font-ui);
  /* Chosen for an unambiguous zero and distinguishable l/1/I, which matters a
     great deal when the page is mostly 64-character hex strings. */
  --font-mono: 'JetBrains Mono', 'Cascadia Code', ui-monospace, SFMono-Regular, 'SF Mono', Menlo,
    Consolas, 'Liberation Mono', monospace;

  --t-micro: 0.6875rem;
  --t-small: 0.8125rem;
  --t-body: 0.9375rem;
  --t-h1: clamp(1.9rem, 1.4rem + 1.6cqi, 2.6rem);
  --t-hero: clamp(2.5rem, 1.6rem + 3.4cqi, 4rem);
  --lh-base: 1.6;

  /* 4px grid, rem-valued so a reader's own font size survives. */
  --sp-2: 0.125rem;
  --sp-4: 0.25rem;
  --sp-6: 0.375rem;
  --sp-8: 0.5rem;
  --sp-12: 0.75rem;
  --sp-16: 1rem;
  --sp-20: 1.25rem;
  --sp-24: 1.5rem;
  --sp-32: 2rem;
  --sp-40: 2.5rem;
  --sp-48: 3rem;
  --sp-64: 4rem;

  /* Control heights. All clear the 24px WCAG 2.2 SC 2.5.8 floor. */
  --ctl-sm: 1.75rem;
  --ctl-md: 2rem;
  --ctl-lg: 2.5rem;

  --content-max: 1320px;
  --measure: 68ch;
  --topbar-h: 3.5rem;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur: 180ms;
}

/* Dark is the default, and the richer of the two modes. Elevation steps UP from
   the page background. */
[data-theme='dark'] {
  --s1: #0d0d10;
  --s2: #131318;
  --s3: #1a1a21;
  --s4: #22222b;
  --line: #24242a;
  --line-strong: #33333d;

  --bg: #08080a;
  --bg-chrome: #0c0c0f;
  --ink: #fff9f9;
  --ink-dim: #8a8a8a;
  --primary: #f6d471;
  --mark: #f6d471;
  --code-bg: rgba(246, 212, 113, 0.08);
  --code-ink: #fbe5a6;
  --stripe: rgba(255, 249, 249, 0.03);

  --ok-ink: #b2f2bb;
  --ok-bg: rgba(64, 192, 87, 0.12);
  --ok-line: rgba(64, 192, 87, 0.4);
  --warn-ink: #ffd8a8;
  --warn-bg: rgba(201, 138, 62, 0.18);
  --warn-line: rgba(201, 138, 62, 0.5);
  --danger-ink: #ffc9c9;
  --danger-bg: rgba(224, 49, 49, 0.15);
  --danger-line: rgba(224, 49, 49, 0.5);
  --info-ink: #b9dcff;
  --info-bg: rgba(51, 154, 240, 0.14);
  --info-line: rgba(51, 154, 240, 0.5);
}

/* Light mode is "charcoal chrome on warm paper", not white-with-dark-text.
   Elevation runs DOWN from the page on paper; the two modes disagree on
   purpose. */
[data-theme='light'] {
  --s1: #f8f5ef;
  --s2: #efe9dd;
  --s3: #e6dece;
  --s4: #ddd3c0;
  --line: #d5ccbb;
  --line-strong: #b7ac98;

  --bg: #f8f5ef;
  --bg-chrome: #1b1a18;
  --ink: #221f1a;
  --ink-dim: #675e54;
  /* Gold as text, 5.42:1 on paper. */
  --primary: #8a5a1d;
  /* Gold as a mark, 3.30:1 - active states and focus rings, never body text. */
  --mark: #b87a2a;
  --code-bg: rgba(138, 90, 29, 0.07);
  --code-ink: #6b4518;
  --stripe: rgba(34, 31, 26, 0.03);

  --chrome: #1b1a18;
  --chrome-ink: #f7f2e8;
  --chrome-dim: #b3aa9c;
  --chrome-line: #322e29;

  --ok-ink: #237032;
  --ok-bg: rgba(64, 192, 87, 0.12);
  --ok-line: rgba(64, 192, 87, 0.45);
  --warn-ink: #7a4a12;
  --warn-bg: rgba(201, 138, 62, 0.16);
  --warn-line: rgba(201, 138, 62, 0.5);
  --danger-ink: #b02020;
  --danger-bg: rgba(224, 49, 49, 0.1);
  --danger-line: rgba(224, 49, 49, 0.45);
  --info-ink: #1b5f9f;
  --info-bg: rgba(51, 154, 240, 0.12);
  --info-line: rgba(51, 154, 240, 0.45);
}

/* Chrome islands. Rather than restyling each component for light mode, the
   island root redeclares the token NAMES with dark values, so every child is
   correct without being enumerated. This is the single trick that makes light
   mode maintainable. */
[data-theme='light'] .topbar,
[data-theme='light'] .footer,
[data-theme='light'] .code {
  --s1: #1b1a18;
  --s2: #201f1c;
  --s3: #262421;
  --s4: #2e2b27;
  --line: var(--chrome-line);
  --line-strong: #443f38;
  --ink: var(--chrome-ink);
  --ink-dim: var(--chrome-dim);
  --primary: #f6d471;
  --mark: #f6d471;
  --code-bg: rgba(246, 212, 113, 0.1);
  --code-ink: #fbe5a6;
  color-scheme: dark;
  color: var(--chrome-ink);
  background: var(--chrome);
}

/* ------------------------------------------------------------------ base */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  /* The variable font covers 100-900; never let the browser fake a weight. */
  font-synthesis: none;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

@media (min-width: 1548px) {
  :root {
    font-size: clamp(100%, 0.809rem + 0.198vw, 112.5%);
  }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--t-body);
  line-height: var(--lh-base);
  /* A wash that ramps in over 6rem rather than opening at full alpha, so there
     is no visible step under the topbar. Light mode lifts with warmth, not cool
     grey, which made paper look dirty. */
  background-image: linear-gradient(
    to bottom,
    rgba(255, 249, 249, 0) var(--topbar-h),
    rgba(255, 249, 249, 0.03) calc(var(--topbar-h) + 6rem),
    rgba(255, 249, 249, 0) 40rem
  );
  background-repeat: no-repeat;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

[data-theme='light'] body {
  background-image: linear-gradient(
    to bottom,
    rgba(138, 90, 29, 0) var(--topbar-h),
    rgba(138, 90, 29, 0.05) calc(var(--topbar-h) + 6rem),
    rgba(138, 90, 29, 0) 40rem
  );
}

main {
  flex: 1 0 auto;
  container-type: inline-size;
  container-name: page;
}

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.18;
  margin: 0;
}
/* Negative tracking is scoped by SIZE, not by tag: below 2rem it pushes glyphs
   onto fractional subpixel positions and costs crispness for no gain. */
h1 {
  font-size: var(--t-h1);
  letter-spacing: -0.022em;
}
h2 {
  font-size: 1.125rem;
}
@media (min-width: 600px) {
  h2 {
    letter-spacing: -0.014em;
  }
}
h3 {
  font-size: 1rem;
}
[data-theme='light'] h1,
[data-theme='light'] h2,
[data-theme='light'] h3 {
  font-weight: 650;
}

p {
  margin: 0 0 var(--sp-12);
}
p:last-child {
  margin-bottom: 0;
}

a {
  color: var(--primary);
  text-decoration: none;
}
/* Underlines are added back only in prose. Links distinguished by colour alone
   fail WCAG 1.4.1, but underlining every hash in a table would be unreadable,
   so prose gets underlines and data cells get hover state. */
.prose a,
.footer-note a,
.empty-hint a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.19em;
  text-decoration-color: color-mix(in srgb, var(--primary) 55%, transparent);
}
.prose a:hover {
  text-decoration-color: currentColor;
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--mark);
  outline-offset: 2px;
}

.mono,
code,
pre,
input[type='search'] {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'zero' 1;
}

.dim {
  color: var(--ink-dim);
}
.strong {
  font-weight: 600;
}

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

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 300;
  padding: var(--sp-8) var(--sp-16);
  background: var(--s2);
  border: 1px solid var(--line-strong);
}
.skip:focus {
  left: var(--sp-8);
  top: var(--sp-8);
}

/* --------------------------------------------------------------- topbar */

.topbar {
  position: sticky;
  top: 0;
  z-index: 200;
  background: var(--bg-chrome);
  border-bottom: 1px solid var(--line);
  /* No backdrop-filter: it forces grayscale antialiasing and softens the text. */
}

/* The signature gradient accent hairline. */
.topbar::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  pointer-events: none;
  background-image: linear-gradient(
    to right,
    transparent 0%,
    var(--gold-deep) 14%,
    rgba(201, 138, 62, 0.35) 42%,
    transparent 72%
  );
}
[data-theme='light'] .topbar::after {
  background-image: linear-gradient(
    to right,
    transparent 0%,
    var(--gold-light) 14%,
    rgba(246, 212, 113, 0.4) 42%,
    transparent 72%
  );
}

.topbar-inner {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--sp-8) var(--sp-16);
  display: flex;
  align-items: center;
  gap: var(--sp-16);
  min-height: var(--topbar-h);
  flex-wrap: wrap;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-8);
  color: var(--ink);
  flex: none;
}
.brand-mark {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
  background-image: var(--gradient-gold);
  /* A hexagon: a mark rather than a logo, since we do not own the Epic wordmark
     for this use and a geometric glyph scales to any size for free. */
  clip-path: polygon(50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%);
}
.brand-text {
  display: flex;
  flex-direction: column;
  line-height: 1.05;
}
.brand-text strong {
  font-family: var(--font-display);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.brand-text span {
  font-size: var(--t-micro);
  color: var(--ink-dim);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.mainnav {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  overflow-x: auto;
  scrollbar-width: none;
  flex: 1 1 auto;
}
.mainnav::-webkit-scrollbar {
  display: none;
}
.mainnav a {
  display: inline-flex;
  align-items: center;
  min-height: var(--ctl-md);
  padding: 0 var(--sp-12);
  font-size: 0.875rem;
  color: var(--ink-dim);
  white-space: nowrap;
  /* Reserved at rest so nothing reflows on hover or activation. */
  box-shadow: inset 0 -2px 0 transparent;
  transition: color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.mainnav a:hover {
  color: var(--ink);
}
.mainnav a.active {
  color: var(--primary);
  box-shadow: inset 0 -2px 0 var(--mark);
  font-weight: 600;
}

.search {
  display: flex;
  align-items: stretch;
  flex: 0 1 22rem;
  min-width: 12rem;
  border: 1px solid var(--line-strong);
  background: var(--s1);
}
.search input {
  flex: 1 1 auto;
  min-width: 0;
  height: var(--ctl-md);
  padding: 0 var(--sp-8);
  border: 0;
  background: transparent;
  color: var(--ink);
  font-size: 0.8125rem;
}
.search input::placeholder {
  color: var(--ink-dim);
}
.search input:focus {
  outline: none;
}
.search:focus-within {
  border-color: var(--mark);
}
.search button {
  flex: none;
  width: var(--ctl-md);
  height: var(--ctl-md);
  border: 0;
  border-left: 1px solid var(--line);
  background: transparent;
  color: var(--primary);
  cursor: pointer;
  font-size: 0.875rem;
}

.theme {
  flex: none;
  width: var(--ctl-md);
  height: var(--ctl-md);
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--ink-dim);
  cursor: pointer;
  font-size: 0.875rem;
}
.theme:hover {
  color: var(--primary);
  border-color: var(--mark);
}

/* ------------------------------------------------------------ join strip */

/* A slim band under the topbar carrying the one-line installer. Deliberately not a
   dismissible banner: it is one row tall, it is the most useful thing a first-time
   visitor can copy, and a dismiss control would cost more pixels than it saves.
   Hairline and hollow, in keeping with everything else here - no filled call to action. */
.joinbar {
  border-bottom: 1px solid var(--line);
  background: var(--s1);
}
.joinbar-inner {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--sp-8) var(--sp-16);
  display: flex;
  align-items: center;
  gap: var(--sp-12);
  flex-wrap: wrap;
}
.joinbar-label {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--primary);
  flex: none;
}
/* The command is the point, so it gets to be the wide element and to overflow
   scrollably on a phone rather than wrapping mid-flag. */
.joinbar-cmd {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--ink);
  background: var(--s3);
  border: 1px solid var(--line);
  padding: var(--sp-4) var(--sp-8);
  border-radius: 0;
  overflow-x: auto;
  white-space: nowrap;
  flex: 1 1 20rem;
  min-width: 0;
  scrollbar-width: none;
}
.joinbar-cmd::-webkit-scrollbar {
  display: none;
}
.joinbar-more {
  flex: none;
}
@media (max-width: 40rem) {
  .joinbar-inner {
    padding: var(--sp-8) var(--sp-12);
    gap: var(--sp-8);
  }
  /* On a narrow screen the label is the first thing to go; the command itself stays. */
  .joinbar-label {
    display: none;
  }
}

/* ----------------------------------------------------------------- page */

.page {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--sp-32) var(--sp-16) var(--sp-64);
  display: flex;
  flex-direction: column;
  gap: var(--sp-24);
}
.page--narrow {
  max-width: 46rem;
}

/* Measure is capped PER ELEMENT, not on the container, so tables, code blocks
   and 64-character hashes keep the full width they need. */
.lede,
.prose,
.footer-note,
.panel-foot {
  max-width: var(--measure);
}
.lede {
  font-size: 1.0625rem;
  color: var(--ink-dim);
  line-height: 1.55;
}

.page-head h1 {
  margin-bottom: var(--sp-8);
}
.page-head--detail h1 {
  font-size: clamp(1.6rem, 1.2rem + 1.4cqi, 2.2rem);
}
.h1-hash {
  font-family: var(--font-mono);
  font-size: clamp(0.8rem, 0.55rem + 1.6cqi, 1.25rem) !important;
  letter-spacing: 0;
  word-break: break-all;
  font-weight: 500;
}
.hash-lede {
  font-size: 0.8125rem;
  word-break: break-all;
  color: var(--ink-dim);
}

/* An eyebrow: a short gold rule then sentence-case text. Replaces the dated
   uppercase-letterspaced-mono section opener. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--sp-12);
  font-size: var(--t-small);
  color: var(--primary);
  margin: 0 0 var(--sp-6);
}
.eyebrow::before {
  content: '';
  width: 28px;
  height: 1px;
  background: currentColor;
  flex: none;
}

/* ----------------------------------------------------------------- hero */

.hero {
  border-bottom: 1px solid var(--line);
  background: linear-gradient(to bottom, var(--s1), transparent);
  position: relative;
  overflow: hidden;
}
/* A 56px measurement lattice, radially masked so it frames the hero and then
   gets out of the way. */
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: linear-gradient(to right, rgba(255, 249, 249, 0.035) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255, 249, 249, 0.035) 1px, transparent 1px);
  background-size: 56px 56px;
  mask-image: radial-gradient(150% 130% at 26% -10%, #000 0%, #000 38%, transparent 88%);
}
[data-theme='light'] .hero::before {
  background-image: linear-gradient(to right, rgba(28, 30, 33, 0.1) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(28, 30, 33, 0.1) 1px, transparent 1px);
}

.hero-main,
.hero-side {
  position: relative;
}
.hero {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-24);
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--sp-40) var(--sp-16) var(--sp-32);
}
@media (min-width: 60rem) {
  .hero {
    grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
    align-items: end;
  }
}

.hero h1 {
  display: flex;
  align-items: baseline;
  gap: var(--sp-12);
  flex-wrap: wrap;
  margin: 0 0 var(--sp-12);
}
.hero-height {
  font-family: var(--font-mono);
  font-size: var(--t-hero);
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1;
  background-image: var(--gradient-gold);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
[data-theme='light'] .hero-height {
  /* Gold-on-paper as a large fill fails contrast; use ink and let the mark
     appear elsewhere. */
  background-image: none;
  color: var(--ink);
}
.hero-height-label {
  font-family: var(--font-ui);
  font-size: 0.9375rem;
  font-weight: 400;
  color: var(--ink-dim);
  letter-spacing: 0;
}
.hero-sub {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-6);
  margin: 0;
}

/* ---------------------------------------------------------------- panel */

.panel {
  border: 1px solid var(--line);
  background: var(--s1);
  position: relative;
  border-radius: 0;
}

/* Corner ticks: 9px L-brackets marking a real enclosure. Only the two drawn
   sides are declared - never a `border` shorthand plus `border-*: none`,
   because cssnano merges equal-specificity rules and can hoist the shorthand
   after the longhands, shipping closed squares in production while dev looks
   fine. */
.panel.ticked::before,
.panel.ticked::after {
  content: '';
  position: absolute;
  width: 9px;
  height: 9px;
  pointer-events: none;
  z-index: 1;
}
.panel.ticked::before {
  top: -1px;
  left: -1px;
  border-top: 1px solid var(--gold-deep);
  border-left: 1px solid var(--gold-deep);
}
.panel.ticked::after {
  bottom: -1px;
  right: -1px;
  border-bottom: 1px solid var(--gold-deep);
  border-right: 1px solid var(--gold-deep);
}

.panel-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-16);
  padding: var(--sp-16) var(--sp-20);
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.panel-head-main {
  min-width: 0;
}
.panel-meta {
  margin: var(--sp-4) 0 0;
  font-size: var(--t-small);
  color: var(--ink-dim);
}
.panel-body {
  padding: var(--sp-20);
}
.panel-body > .table-wrap:only-child,
.panel-body > .empty:only-child {
  margin: calc(var(--sp-20) * -1);
}
.panel-foot {
  margin: var(--sp-16) 0 0;
  font-size: var(--t-small);
}
.table-wrap + .panel-foot {
  padding: var(--sp-16) var(--sp-20) 0;
  margin: 0;
}

.grid-2 {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-24);
}
@media (min-width: 56rem) {
  .grid-2 {
    grid-template-columns: 1fr 1fr;
  }
}

.split {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-24);
}
@media (min-width: 68rem) {
  .split {
    grid-template-columns: minmax(0, 1fr) minmax(0, 20rem);
    align-items: start;
  }
}
.side-stack {
  display: flex;
  flex-direction: column;
  gap: var(--sp-24);
}

.chart-pair {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-24);
}
@media (min-width: 56rem) {
  .chart-pair {
    grid-template-columns: 1fr 1fr;
  }
}

/* ----------------------------------------------------------------- stat */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
}
@media (min-width: 40rem) {
  .stat-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (min-width: 64rem) {
  .stat-grid {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }
}

.stat {
  background: var(--s1);
  padding: var(--sp-12) var(--sp-16);
  min-width: 0;
}
.stat-label {
  margin: 0 0 var(--sp-4);
  font-size: var(--t-micro);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-dim);
  font-family: var(--font-mono);
}
.stat-value {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.25;
  overflow-wrap: anywhere;
}
.stat-sub {
  margin: var(--sp-2) 0 0;
  font-size: var(--t-micro);
  color: var(--ink-dim);
}
/* A value the chain cannot supply, as distinct from a value that is zero. */
.stat--muted .stat-value {
  color: var(--ink-dim);
  font-weight: 500;
  font-size: 1rem;
}
/* Momentary highlight when SSE replaces a value, so a change is noticeable
   without being animated furniture. */
.stat-value.flash,
.hero-height.flash {
  animation: flash var(--dur) var(--ease);
}
@keyframes flash {
  from {
    background-color: var(--warn-bg);
  }
  to {
    background-color: transparent;
  }
}

/* ---------------------------------------------------------------- table */

.table-wrap {
  overflow-x: auto;
  max-width: 100%;
  /* A scrollable region must be keyboard reachable; it carries tabindex="0" and
     therefore needs a visible focus state. */
}
.table-wrap:focus-visible {
  outline: 2px solid var(--mark);
  outline-offset: -2px;
}

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8125rem;
}
table.data thead th {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-dim);
  text-align: left;
  padding: var(--sp-8) var(--sp-12);
  border-bottom: 1px solid var(--line-strong);
  white-space: nowrap;
  background: var(--s2);
  position: sticky;
  top: 0;
}
table.data td {
  padding: var(--sp-8) var(--sp-12);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
table.data tbody tr:last-child td {
  border-bottom: 0;
}
table.data tbody tr:nth-child(even) {
  background: var(--stripe);
}
table.data tbody tr:hover {
  background: var(--s2);
}
.align-right {
  text-align: right;
}
/* Rows arriving over SSE fade in, so the eye catches the change. */
table.data tbody tr.new {
  animation: rowin 400ms var(--ease);
}
@keyframes rowin {
  from {
    background-color: var(--warn-bg);
  }
  to {
    background-color: transparent;
  }
}

/* A block that took well over the 60 s target. Colour only - no icon, no bold - because
   this is a whole column of numbers and the eye should catch the pattern, not each cell. */
.slow {
  color: var(--warn-ink);
}

.hash {
  overflow-wrap: anywhere;
}
.hash-full {
  word-break: break-all;
  font-size: 0.8125rem;
}

.copy {
  border: 0;
  background: transparent;
  color: var(--ink-dim);
  cursor: pointer;
  padding: 0 var(--sp-4);
  font-size: 0.75rem;
  line-height: 1;
  /* Meets the 24px target floor without adding visual weight. */
  min-width: 1.5rem;
  min-height: 1.5rem;
}
.copy:hover {
  color: var(--primary);
}
.copy.done {
  color: var(--ok-ink);
}

/* ------------------------------------------------------------------- kv */

.kv {
  margin: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
}
.kv-row {
  display: grid;
  grid-template-columns: minmax(8rem, 12rem) minmax(0, 1fr);
  gap: var(--sp-16);
  padding: var(--sp-8) 0;
  border-bottom: 1px solid var(--line);
  align-items: baseline;
}
.kv-row:last-child {
  border-bottom: 0;
}
.kv dt {
  font-size: var(--t-small);
  color: var(--ink-dim);
}
.kv dd {
  margin: 0;
  min-width: 0;
  font-size: 0.8125rem;
  overflow-wrap: anywhere;
}
.kv-note {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1rem;
  height: 1rem;
  margin-left: var(--sp-4);
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  font-size: 0.625rem;
  cursor: help;
  color: var(--ink-dim);
}
@media (max-width: 34rem) {
  .kv-row {
    grid-template-columns: 1fr;
    gap: var(--sp-2);
  }
}

/* ----------------------------------------------------------------- pill */

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-4);
  font-family: var(--font-ui);
  font-size: var(--t-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: var(--sp-2) var(--sp-8);
  border-radius: 4px;
  border: 1px solid transparent;
  white-space: nowrap;
  vertical-align: middle;
}
.pill--ok {
  color: var(--ok-ink);
  background: var(--ok-bg);
  border-color: var(--ok-line);
}
.pill--warn {
  color: var(--warn-ink);
  background: var(--warn-bg);
  border-color: var(--warn-line);
}
.pill--danger {
  color: var(--danger-ink);
  background: var(--danger-bg);
  border-color: var(--danger-line);
}
.pill--info {
  color: var(--info-ink);
  background: var(--info-bg);
  border-color: var(--info-line);
}
.pill--neutral {
  color: var(--ink-dim);
  background: var(--s3);
  border-color: var(--line);
}

/* The borderless bracket link: no box at all, the chrome is literal [ ] glyphs. */
.bracket {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-4);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: var(--ink-dim);
  white-space: nowrap;
  min-height: 1.5rem;
}
.bracket::before {
  content: '[ ';
  color: var(--primary);
}
.bracket::after {
  content: ' ]';
  color: var(--primary);
}
.bracket:hover {
  color: var(--primary);
}
.bracket.dim::before,
.bracket.dim::after {
  color: var(--line-strong);
}

/* --------------------------------------------------------------- notice */

.notice {
  border: 1px solid var(--line-strong);
  border-left-width: 3px;
  background: var(--s2);
  padding: var(--sp-12) var(--sp-16);
  font-size: 0.875rem;
}
.notice--info {
  border-left-color: var(--info-line);
}
.notice--ok {
  border-left-color: var(--ok-line);
}
.notice--warn {
  border-left-color: var(--warn-line);
}
.notice--danger {
  border-left-color: var(--danger-line);
}
.notice-title {
  margin: 0 0 var(--sp-6);
  font-weight: 600;
  font-family: var(--font-display);
  font-size: 0.9375rem;
}
.notice-body p {
  margin: 0 0 var(--sp-8);
  max-width: var(--measure);
  color: var(--ink-dim);
}
.notice-body p:last-child {
  margin-bottom: 0;
}
.notice-body strong {
  color: var(--ink);
}
ul.tight {
  margin: 0;
  padding-left: 1.1rem;
  color: var(--ink-dim);
}

/* ---------------------------------------------------------------- empty */

.empty {
  padding: var(--sp-32) var(--sp-20);
  text-align: center;
  color: var(--ink-dim);
  font-size: 0.875rem;
}
.empty-hint {
  margin: var(--sp-8) auto 0;
  font-size: var(--t-small);
  max-width: 44ch;
  opacity: 0.85;
}

/* --------------------------------------------------------------- charts */

.spark {
  display: block;
  width: 100%;
  height: 4rem;
}
.spark-line {
  fill: none;
  stroke: var(--gold-deep);
  stroke-width: 1.5;
  vector-effect: non-scaling-stroke;
}
.spark-area {
  fill: color-mix(in srgb, var(--gold-deep) 14%, transparent);
  stroke: none;
}
.spark .bar {
  fill: color-mix(in srgb, var(--gold-deep) 65%, transparent);
}
.spark .bar--over {
  fill: var(--warn-line);
}
.spark .bar-target {
  stroke: var(--line-strong);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  vector-effect: non-scaling-stroke;
}
.spark-empty {
  margin: 0;
  padding: var(--sp-20) 0;
  text-align: center;
  color: var(--ink-dim);
  font-size: var(--t-small);
}

/* ---------------------------------------------------------------- algos */

.algo-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-16);
}
.algo {
  display: flex;
  align-items: center;
  gap: var(--sp-8);
  padding: var(--sp-8) var(--sp-12);
  border: 1px solid var(--line);
  background: var(--s2);
}
.algo-count {
  font-size: 1rem;
  font-weight: 600;
}

/* ----------------------------------------------------------- code / raw */

pre.code,
.raw pre {
  margin: var(--sp-12) 0 0;
  padding: var(--sp-16);
  border: 1px solid var(--line);
  background: var(--s2);
  overflow-x: auto;
  font-size: 0.8125rem;
  line-height: 1.55;
  border-radius: 0;
}
/* In light mode a code block is a charcoal island, which is the only way real
   gold can appear on paper. */
[data-theme='light'] pre.code,
[data-theme='light'] .raw pre {
  background: var(--chrome);
  border-color: var(--chrome-line);
  color: var(--chrome-ink);
}
pre code {
  font-size: inherit;
}

/* Copy button on a code block. The wrapper is injected by app.js; the button sits on
   the wrapper rather than inside the <pre> so it stays put when the block scrolls. */
.codewrap {
  position: relative;
}
.copy--code {
  position: absolute;
  top: var(--sp-6);
  right: var(--sp-6);
  min-width: 1.75rem;
  min-height: 1.75rem;
  padding: 0 var(--sp-6);
  border: 1px solid var(--line-strong);
  background: var(--s3);
  color: var(--ink-dim);
  font-size: 0.8125rem;
  /* Present but quiet until the block is hovered or the button is focused, so it does
     not compete with the code for attention. */
  opacity: 0;
  transition: opacity var(--dur) var(--ease), color var(--dur) var(--ease);
}
.codewrap:hover .copy--code,
.copy--code:focus-visible,
.copy--code.done {
  opacity: 1;
}
/* Touch devices have no hover, so never hide it there - it would be unreachable. */
@media (hover: none) {
  .copy--code {
    opacity: 1;
  }
}
.copy--code:hover {
  color: var(--primary);
  border-color: var(--mark);
}
/* In light mode a code block is a charcoal island, so the button has to live in the
   island's palette rather than the page's. */
[data-theme='light'] .codewrap .copy--code {
  background: #2e2b27;
  border-color: #443f38;
  color: var(--chrome-dim);
}
[data-theme='light'] .codewrap .copy--code:hover {
  color: var(--gold-light);
  border-color: var(--gold-deep);
}
/* Keep the glyph off the code itself. */
pre.code,
.raw pre {
  padding-right: var(--sp-40);
}
:not(pre) > code {
  padding: 0.1em 0.3em;
  background: var(--code-bg);
  color: var(--code-ink);
  border: 1px solid color-mix(in srgb, var(--primary) 18%, transparent);
  font-size: 0.9em;
}

.raw {
  margin-top: var(--sp-16);
}.raw summary {
  cursor: pointer;
  font-size: var(--t-small);
  color: var(--ink-dim);
  padding: var(--sp-4) 0;
  min-height: 1.5rem;
}
.raw summary:hover {
  color: var(--primary);
}
.raw pre {
  max-height: 26rem;
  overflow: auto;
}

/* --------------------------------------------------------------- prose */

ul.prose,
.prose ul {
  padding-left: 1.15rem;
  color: var(--ink-dim);
}
ul.prose li {
  margin-bottom: var(--sp-8);
}
.prose {
  color: var(--ink-dim);
}
.prose strong,
ul.prose strong {
  color: var(--ink);
}

/* ------------------------------------------------------ pager / detail */

.pager,
.detail-nav {
  display: flex;
  gap: var(--sp-16);
  align-items: center;
  padding: var(--sp-16) var(--sp-20);
}
.detail-nav {
  padding: var(--sp-12) 0 0;
}

/* --------------------------------------------------------------- footer */

.footer {
  position: relative;
  margin-top: var(--sp-64);
  background: var(--bg-chrome);
  border-top: 1px solid var(--line-strong);
  padding-block: var(--sp-40) var(--sp-24);
  flex: none;
}
/* A gold tab on the top edge, framing the footer as a deliberate boundary. */
.footer::before {
  content: '';
  position: absolute;
  top: -1px;
  left: 0;
  width: clamp(7rem, 22vw, 16rem);
  height: 2px;
  background-image: linear-gradient(to right, var(--gold-light) 0%, var(--gold-deep) 55%, transparent 100%);
}
.footer-inner {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0 var(--sp-16);
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-32);
}
@media (min-width: 48rem) {
  .footer-inner {
    grid-template-columns: 2fr 1fr 1fr;
  }
}
.footer-col {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  position: relative;
  padding-top: var(--sp-12);
}
/* The eyebrow rule device, reused as a column marker. */
.footer-col::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: var(--sp-32);
  height: 2px;
  background-image: linear-gradient(to right, var(--gold-light), var(--gold-deep));
}
.footer-title {
  margin: 0 0 var(--sp-4);
  font-family: var(--font-display);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: -0.012em;
  color: var(--ink);
}
.footer-note {
  margin: 0;
  font-size: var(--t-small);
  color: var(--ink-dim);
  line-height: 1.5;
}
.footer-col a {
  display: inline-flex;
  align-items: center;
  min-height: var(--ctl-sm);
  padding-inline-start: var(--sp-12);
  font-size: 0.875rem;
  color: var(--ink-dim);
  /* Same left-rail idiom as the active nav row, reserved at rest so nothing
     reflows on hover. */
  box-shadow: inset 2px 0 0 transparent;
  transition: color 120ms ease, box-shadow 120ms ease;
}
.footer-col a:hover {
  color: var(--gold-light);
  box-shadow: inset 2px 0 0 var(--gold-light);
}
.footer-legal {
  max-width: var(--content-max);
  margin: var(--sp-32) auto 0;
  padding: var(--sp-16) var(--sp-16) 0;
  border-top: 1px solid var(--line);
  font-size: var(--t-micro);
  color: var(--ink-dim);
}

/* ------------------------------------------------------------ responsive */

@media (max-width: 60rem) {
  .topbar-inner {
    padding: var(--sp-8) var(--sp-12);
    gap: var(--sp-8);
  }
  /* The search field takes its own row below the nav rather than squeezing
     both onto one line. */
  .search {
    order: 4;
    flex: 1 1 100%;
  }
  .mainnav {
    order: 3;
    flex: 1 1 auto;
  }
  .theme {
    order: 2;
  }
}

@media (max-width: 40rem) {
  .page {
    padding: var(--sp-24) var(--sp-12) var(--sp-48);
    gap: var(--sp-16);
  }
  .hero {
    padding: var(--sp-24) var(--sp-12);
  }
  .panel-body,
  .panel-head {
    padding: var(--sp-12);
  }
  .panel-body > .table-wrap:only-child,
  .panel-body > .empty:only-child {
    margin: calc(var(--sp-12) * -1);
  }
  .table-wrap + .panel-foot {
    padding: var(--sp-12) var(--sp-12) 0;
  }
  table.data {
    font-size: 0.75rem;
  }
  table.data th,
  table.data td {
    padding: var(--sp-6) var(--sp-8);
  }
  /* Table headers stop being sticky on small screens: with a sticky topbar as
     well, two sticky layers leave almost no room for rows. */
  table.data thead th {
    position: static;
  }
  .hero-side {
    display: none;
  }
}

@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;
  }
}

@media print {
  .topbar,
  .footer,
  .copy,
  .pager,
  .detail-nav {
    display: none;
  }
  body {
    background: #fff;
    color: #000;
  }
}
