/* components.css — every component in docs/03-components.md, and nothing
   else. No layout, no tokens, no raw values. */

/* ======================================================================
   Utilities
   ====================================================================== */

/* Available to a screen reader, absent from the page. Not display:none —
   that removes it from the accessibility tree as well. */
.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Uppercase micro label. The only place uppercase is allowed, and the
   tracking is what makes it readable there. */
.micro {
  font-size: var(--text-1);
  line-height: var(--leading-1);
  letter-spacing: var(--track-1);
  text-transform: uppercase;
  font-weight: var(--weight-medium);
}

/* Applied wherever a figure can change. Public Sans ships proportional
   figures — its "1" is 55% the width of its "8" — so a column of prices
   visibly jitters without this. See docs/02b-type.md. */
.tabular { font-variant-numeric: tabular-nums lining-nums; }

/* ======================================================================
   Category accents — docs/02a-color.md
   Identity only. The accent reaches the icon and nothing else.
   ====================================================================== */

[data-category="Economy"] { --accent: var(--accent-economy); }
[data-category="Business"] { --accent: var(--accent-business); }
[data-category="Weather"] { --accent: var(--accent-weather); }
[data-category="Geopolitics"] { --accent: var(--accent-geopolitics); }
[data-category="Technology"] { --accent: var(--accent-technology); }
[data-category="Politics"] { --accent: var(--accent-politics); }
[data-category="Culture"] { --accent: var(--accent-culture); }

/* ======================================================================
   Skip link
   ====================================================================== */

.skip {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  z-index: 10;
  transform: translateY(-120%);
  padding: var(--space-2) var(--space-4);
  background: var(--surface-card);
  color: var(--fg-1);
  border: var(--border) solid var(--line-strong);
  border-radius: 0 0 var(--radius-1) 0;
  text-decoration: none;
}

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

/* ======================================================================
   Site header — docs/03d-site-chrome.md
   ====================================================================== */

.site-header {
  position: sticky;
  inset-block-start: 0;
  z-index: 5;
  background: var(--surface-page);
  border-block-end: var(--border) solid var(--line);
}

.site-header__inner {
  min-height: var(--header-h);
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  text-decoration: none;
  flex: none;
}

.brand__name {
  font-size: var(--text-4);
  font-weight: var(--weight-semibold);
  color: var(--fg-1);
  letter-spacing: var(--track-5);
}

/* --text-4, stated rather than inherited. It matched the body at --text-3
   through Phase 4 and read undersized beside the 44px search well and the
   44px button either side of it — the only two boxes in the header — so the
   four links that are the site's navigation carried less weight than the
   chrome around them. One step up the scale. It is now the wordmark's size,
   which is the intended reading: brand and nav are the header's two pieces
   of structure, and the rest of it is a control. */
/* flex: none, so the nav does not shrink while the search still has width to
   give. Both were shrinkable, and flex shares an overflow in proportion to
   basis — so a header one pixel too narrow took the difference out of both,
   and the nav, which has overflow-x: auto, silently scrolled "About" out of
   sight while a 350px search field sat beside it. The search is the elastic
   element in this row by design (03g-search.md); the nav is content. At
   ≤56rem the nav takes its own row and sets flex itself, below. */
.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-size: var(--text-4);
  flex: none;
  overflow-x: auto;
  scrollbar-width: none;
  padding-block: var(--space-2);
  min-width: 0;
}

.site-nav::-webkit-scrollbar { display: none; }

.site-nav a {
  color: var(--fg-2);
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--dur-1) var(--ease);
}

.site-nav a:hover { color: var(--fg-1); }

/* The current page hangs off aria-current, not a class. */
.site-nav a[aria-current="page"] { color: var(--fg-1); }

.site-header__action { flex: none; }

/* ---- Search — docs/03c-filter-bar.md ---- */
/* A near-black well cut into the grey header bar: the same surface step the
   cards use, applied to a control. */
.search {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 1 1 var(--search-max);
  max-width: var(--search-max);
  min-width: 0;
  min-height: var(--control-h);
  padding-inline: var(--space-3);
  background: var(--surface-card);
  border: var(--border) solid var(--line);
  border-radius: var(--radius-1);
  transition: border-color var(--dur-1) var(--ease);
}

.search:hover { border-color: var(--line-strong); }
.search:focus-within { border-color: var(--fg-3); }

.search__glyph {
  flex: none;
  width: 1.125rem;
  height: 1.125rem;
  color: var(--fg-3);
}

.search input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--fg-1);
  font-size: var(--text-2);
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}

.search input::placeholder { color: var(--fg-3); opacity: 1; }
.search input::-webkit-search-cancel-button { -webkit-appearance: none; }

/* Inert until filters.js enables it. Never styled to look operable. */
.search input:disabled { cursor: not-allowed; }
.search:has(input:disabled) { opacity: 0.6; }

.search:focus-within {
  outline: var(--focus-width) solid var(--focus);
  outline-offset: var(--focus-offset);
}

/* ---- Preview badge — docs/04-content.md ---- */
/* Persistent, in the sticky header, at every scroll position and on every
   page. Bordered rather than filled: a filled chip in a semantic colour
   would read as a status light, and there is no status to report. */
.preview {
  flex: none;
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-2);
  border: var(--border) solid var(--fg-3);
  border-radius: var(--radius-1);
  color: var(--fg-1);
  cursor: default;
}

/* Four links do not justify a menu, so the nav never becomes a drawer. It
   narrows, then it narrows again, then it takes its own row, then it scrolls.

   The first step is new with the --text-4 nav: below 70rem a 20px nav and a
   usable search field no longer both fit, and the search is what gives — so
   the nav steps back to the --text-3 it carried through Phase 4 rather than
   squeezing the field to a glyph and four characters. */
@media (max-width: 70rem) {
  .site-nav { font-size: var(--text-3); }
}

@media (max-width: 62rem) {
  .site-header__inner { gap: var(--space-3); }
  .site-nav { font-size: var(--text-2); gap: var(--space-3); }
  .search { flex-basis: 14rem; }
}

/* The nav drops to a second row; the search stays beside the wordmark,
   where a search box belongs. Two rows, never three. */
@media (max-width: 56rem) {
  .site-header__inner {
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    padding-block: var(--space-2);
  }
  .brand__name { font-size: var(--text-3); }
  .search { flex: 1 1 8rem; max-width: none; }
  .site-nav { order: 5; flex: 1 1 100%; padding-block: 0; }
}

/* Under 480px the app link would squeeze the row to nothing. The same link
   is in the footer, so nothing becomes unreachable. The preview badge is
   never hidden at any width.

   Written as .btn.site-header__action, not .site-header__action. The button
   rule below this one in the sheet also sets display, at equal specificity,
   and won on source order — so through Phase 2 this link was never actually
   hidden and the header ran to three rows on a phone. Found by measuring
   the rendered header, not by reading the rule. */
@media (max-width: 30rem) {
  .btn.site-header__action { display: none; }
}

/* ======================================================================
   Ticker — docs/03e-ticker.md
   ====================================================================== */

.ticker {
  position: relative;
  display: flex;
  align-items: stretch;
  min-height: var(--ticker-h);
  background: var(--surface-card);
  border-block-end: var(--border) solid var(--line);
  overflow: hidden;
}

/* The mask is what makes a headline sliding out of the strip read as
   intentional rather than as a headline cut off by the pause control. */
.ticker__viewport {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  mask-image: linear-gradient(to right, transparent 0, #000 var(--space-6),
              #000 calc(100% - var(--space-6)), transparent 100%);
}

.ticker__belt {
  display: flex;
  width: max-content;
  animation: ticker-slide var(--dur-ticker) linear infinite;
}

@keyframes ticker-slide {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-50%, 0, 0); }
}

.ticker__run {
  display: flex;
  align-items: center;
  flex: none;
}

.ticker__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-inline: var(--space-4);
  font-size: var(--text-2);
  line-height: var(--leading-1);
  white-space: nowrap;
}

/* The separator is a border rather than a character, so it is never read
   aloud and never selected with the headline. */
.ticker__item + .ticker__item { border-inline-start: var(--border) solid var(--line-strong); }

.ticker__item a {
  color: var(--fg-2);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease);
}

.ticker__item a:hover { color: var(--fg-1); }

/* Pause on hover and on focus. A headline that scrolls out from under the
   pointer cannot be clicked. */
.ticker__viewport:hover .ticker__belt,
.ticker__viewport:focus-within .ticker__belt { animation-play-state: paused; }

/* ---- The motion switch ---- */
/* A native checkbox, so the strip can be stopped with no script at all —
   WCAG 2.2.2 applies to a marquee whether or not JavaScript is running.
   board.js reads the same checkbox and gates the drift, the count-up and
   the ambient wash on it, so one control governs every moving thing. */
.motion-switch {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.motion-switch:checked ~ .ticker .ticker__belt,
.motion-switch:checked ~ .site-main .feature__glow { animation-play-state: paused; }

.ticker__toggle {
  flex: none;
  display: inline-flex;
  align-items: center;
  padding-inline: var(--space-3);
  border-inline-start: var(--border) solid var(--line);
  background: var(--surface-card);
  color: var(--fg-3);
  font-size: var(--text-1);
  letter-spacing: var(--track-1);
  text-transform: uppercase;
  font-weight: var(--weight-medium);
  cursor: pointer;
  position: relative;
  transition: color var(--dur-1) var(--ease);
}

.ticker__toggle:hover { color: var(--fg-1); }

/* Same sizer pattern as the Ndio/La control: both words are laid out, the
   wider one sets the width, and the label never changes size. */
.ticker__toggle-sizer { display: grid; visibility: hidden; }
.ticker__toggle-sizer > span { grid-area: 1 / 1; }

.ticker__toggle-word {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}

.ticker__toggle-word--off { opacity: 0; }
.motion-switch:checked ~ .ticker .ticker__toggle-word--on { opacity: 0; }
.motion-switch:checked ~ .ticker .ticker__toggle-word--off { opacity: 1; }

.motion-switch:focus-visible ~ .ticker .ticker__toggle {
  outline: var(--focus-width) solid var(--focus);
  outline-offset: calc(var(--focus-offset) * -1);
}

/* Reduced motion: the strip stops sliding and becomes a list a reader
   scrolls themselves. board.js, if it is running, upgrades that to one
   headline at a time on an interval. Either way every headline is
   reachable — degrade toward showing content. */
@media (prefers-reduced-motion: reduce) {
  .ticker__belt { animation: none; transform: none; }
  .ticker__run + .ticker__run { display: none; }
  .ticker__viewport {
    overflow-x: auto;
    scrollbar-width: thin;
    mask-image: none;
  }
}

/* One headline at a time, swapped on an interval. Set by board.js only. */
.ticker--rotate .ticker__viewport { overflow: hidden; mask-image: none; }
.ticker--rotate .ticker__item { display: none; }
.ticker--rotate .ticker__item.is-current { display: flex; border-inline-start: 0; }

@media (max-width: 30rem) {
  .ticker__item { padding-inline: var(--space-3); }
  .ticker__toggle { padding-inline: var(--space-2); }
  .ticker__viewport { mask-image: none; }
}

/* ======================================================================
   Site footer — docs/03d-site-chrome.md
   ====================================================================== */

.site-footer {
  border-block-start: var(--border) solid var(--line);
  background: var(--surface-page);
  padding-block: var(--space-6) var(--space-7);
  margin-block-start: var(--space-6);
}

/* Four blocks: the mark and what the product is, the site links, the
   written documents, and the illustrative-data statement in full. */
.site-footer__inner {
  display: grid;
  gap: var(--space-5);
}

@media (min-width: 43.75rem) {
  .site-footer__inner {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-6) var(--space-5);
  }
}

@media (min-width: 62rem) {
  .site-footer__inner {
    grid-template-columns:
      minmax(0, 1.1fr) minmax(0, 0.8fr) minmax(0, 1fr) minmax(0, 1.2fr);
    gap: var(--space-5);
    align-items: start;
  }
}

.site-footer__desc {
  margin-block-start: var(--space-2);
  font-size: var(--text-2);
  line-height: var(--leading-2);
  color: var(--fg-3);
  max-width: 32ch;
}

.footer-links {
  display: grid;
  gap: var(--space-2);
  font-size: var(--text-2);
}

.footer-links a { color: var(--fg-2); text-decoration-color: var(--line-strong); }
.footer-links a:hover { color: var(--fg-1); text-decoration-color: var(--fg-1); }

.footer-links__heading {
  color: var(--fg-3);
  margin-block-end: var(--space-1);
}

.site-footer__statement {
  font-size: var(--text-2);
  line-height: var(--leading-2);
  color: var(--fg-2);
  max-width: 46ch;
}

/* The registered company line. No licence text, no regulator name, no
   certification mark and no payment-network logo sits beside it — there is
   nothing true to put in any of those slots yet, and a placeholder in a
   footer reads as a claim. */
.site-footer__colophon {
  margin-block-start: var(--space-6);
  padding-block-start: var(--space-4);
  border-block-start: var(--border) solid var(--line);
  font-size: var(--text-2);
  line-height: var(--leading-2);
  color: var(--fg-3);
}

/* ======================================================================
   Button — one button, used for Clear filters and Get the app
   ====================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--control-h);
  padding-inline: var(--space-4);
  border: var(--border) solid var(--line-strong);
  border-radius: var(--radius-1);
  background: var(--surface-card);
  color: var(--fg-1);
  font-size: var(--text-2);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease);
}

.btn:hover { border-color: var(--fg-3); }

/* ======================================================================
   Illustrative badge and statement — docs/04-content.md
   ====================================================================== */

/* Written in sentence case and uppercased here, so a screen reader says
   "illustrative" rather than spelling out four capitals. */
.badge {
  display: inline-block;
  flex: none;
  padding: 0 var(--space-1);
  border: var(--border) solid var(--line-strong);
  border-radius: var(--radius-1);
  color: var(--fg-3);
}

.statement {
  font-size: var(--text-2);
  line-height: var(--leading-2);
  color: var(--fg-2);
  max-width: 62ch;
}

/* A document body that opens with a paragraph had no gap above it at all.
   `.doc__body > * + *` cannot space a first child, and the blocks that
   happened to open the Phase 2 documents carried their own margin — an `h2`
   at space-6, `.tiles` at space-5 — so the hole only appeared once a page
   started with prose. Measured, not eyeballed: 0px against the 24 every
   other opening block gets, which reads as the first paragraph belonging to
   the lede.

   Scoped to `p`, because the four documents open with a nav, an h2 or a div
   and were assumed to be spaced already. Three of them were. `.toc` was the
   exception the Phase 4 comment claimed it was not: it sets padding and no
   margin, so /terms — the one document that opens with a table of contents,
   and the first written page a reader is likely to open — carried the same
   0px hole for a further phase. The margin belongs on the component, as it
   does on .tiles and .registry, so a .toc is spaced wherever it appears. */
.doc__body > p:first-child { margin-block-start: var(--space-5); }

/* On the board the statement is a rule above the first price rather than a
   paragraph, so it costs one line of fold rather than three. The wording is
   fixed and complete either way. The result count shares the line: it is
   board status, it does not belong in the scrolling filter row, and putting
   it here keeps every category and region chip on one line at 1280. */
.statement-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-2) var(--space-4);
  flex-wrap: wrap;
}

.statement--bar {
  flex: 1 1 24rem;
  max-width: none;
  color: var(--fg-3);
}

/* The whole of the board's positioning copy: one line, at body size, sharing
   its row with the statement that qualifies it and the result count. The
   96px two-line hero this replaced pushed the first market off the fold. */
.board__title {
  flex: none;
  font-size: var(--text-3);
  line-height: var(--leading-2);
  letter-spacing: 0;
  font-weight: var(--weight-semibold);
  color: var(--fg-1);
}

/* ======================================================================
   Market card — docs/03a-market-card.md
   One grid anatomy, five blocks, every block always present.
   ====================================================================== */

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--space-3);
  background: var(--surface-card);
  border: var(--border) solid var(--line);
  border-radius: var(--radius-2);
  transition: border-color var(--dur-1) var(--ease);
}

.card:hover { border-color: var(--line-strong); }

/* ---- Block 1 — icon, labels, short title ---- */
.card__head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

/* The tile is drawn on the SVG itself: viewBox scales the 24-unit drawing
   into the padded content box, so there is no wrapper element and no second
   node to keep in step. Single colour, inherited from --accent. */
.card__icon {
  flex: none;
  box-sizing: border-box;
  width: var(--card-icon);
  height: var(--card-icon);
  padding: var(--space-2);
  border-radius: var(--radius-1);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

.card__headings { min-width: 0; }

.card__labels { color: var(--fg-3); }

/* Two lines maximum, ellipsised beyond. The full formal wording of the
   question is on the market page; this is the display title. */
.card__title {
  margin-block-start: var(--space-1);
  min-height: var(--slot-title);
  font-size: var(--text-3);
  line-height: var(--leading-5);
  font-weight: var(--weight-medium);
  color: var(--fg-1);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.card__link { color: inherit; text-decoration: none; }

/* The stretched link: one accessible name, one tab stop for the card body,
   and the two controls stay independently clickable. */
.card__link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius-2);
}

/* ---- Block 2 — the probability and the sparkline ---- */
.card__figures {
  margin-block-start: var(--space-2);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: var(--slot-probability);
}

.card__probability {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
  min-width: 0;
}

.probability__value {
  font-size: var(--text-7);
  line-height: var(--leading-7);
  letter-spacing: var(--track-7);
  font-weight: var(--weight-semibold);
  color: var(--fg-1);
}

.probability__unit {
  font-size: var(--text-4);
  font-weight: var(--weight-medium);
  color: var(--fg-1);
}

.probability__side {
  margin-inline-start: var(--space-1);
  color: var(--fg-3);
}

/* The sparkline plots the Ndio series, so it is drawn in --ndio. It is not
   coloured by direction: a red line for a falling number would make the
   colour mean sentiment rather than side. aria-hidden — the first and last
   value are written out on the market page. */
.spark {
  flex: none;
  width: var(--spark-w);
  height: var(--spark-h);
  overflow: visible;
}

.spark__line {
  fill: none;
  stroke: var(--ndio);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

/* Drawn once, when the card first scrolls into view. board.js sets the dash
   properties and removes them again when the transition ends, so a card it
   never reaches keeps an ordinary, complete line. */
.spark__line.is-drawing {
  transition: stroke-dashoffset var(--dur-spark) var(--ease);
}

/* ---- Block 3 — the split bar ---- */
/* --p is set inline per market and updated by the drift. aria-hidden: it
   restates the numeral beside it. Sits on --surface-card, never on
   --surface-page, where the flag fills fall below 3:1. */
.card__bar {
  margin-block-start: var(--space-2);
  height: var(--bar-h);
  border-radius: var(--radius-full);
  background: linear-gradient(
    to right,
    var(--ndio-fill) 0 var(--p),
    var(--la-fill) var(--p) 100%
  );
  transition: background var(--dur-3) var(--ease);
}

/* ---- Block 4 — the two controls, above the stretched-link overlay ---- */
/* minmax(0, 1fr), not 1fr: a bare 1fr track floors at the content's
   min-content width, and "KSh 3.80" does not wrap, so at 320px the pair
   would push the whole page into horizontal scroll. */
.card__positions {
  position: relative;
  z-index: 1;
  margin-block-start: var(--space-2);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-2);
}

/* ---- Block 5 — volume, the badge, the settlement source, the close date ---- */
.card__footer {
  margin-block-start: var(--space-2);
  min-height: var(--slot-footer);
  display: grid;
  font-size: var(--text-2);
  line-height: var(--leading-2);
  color: var(--fg-3);
}

.card__footer-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  min-width: 0;
}

/* The source is never omitted; it is ellipsised. The full string is in the
   DOM and is set out in full on the market page. */
.card__source {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card__closes { flex: none; }

.card__footer .badge { font-size: var(--text-1); letter-spacing: var(--track-1); }

/* Filtered out. The node is never removed. */
.card[hidden] { display: none; }

/* ======================================================================
   Featured panel — docs/05a-board.md
   ====================================================================== */

/* min-width: 0 on both panel children. A grid item floors at its own
   min-content width by default, and the rail's min-content width is four
   14rem tracks — 896px — which would push the whole page sideways on a
   phone before the scroll container ever got a chance to clip. */
.feature {
  position: relative;
  min-width: 0;
  overflow: hidden;
  background: var(--surface-card);
  border: var(--border) solid var(--line);
  border-radius: var(--radius-2);
}

/* Ambient depth, featured panel only. Category accents, never --ndio or
   --la: a green wash behind a price would be a semantic colour used as
   decoration. Kept under 16% so it reads as depth rather than as a graphic,
   and it sits behind the body, never over it. */
.feature__glow {
  position: absolute;
  inset: -20%;
  pointer-events: none;
  opacity: 0.85;
  background:
    radial-gradient(38rem 20rem at 14% 20%,
      color-mix(in srgb, var(--accent) 15%, transparent), transparent 62%),
    radial-gradient(30rem 18rem at 86% 8%,
      color-mix(in srgb, var(--accent-geopolitics) 11%, transparent), transparent 64%),
    radial-gradient(34rem 22rem at 68% 98%,
      color-mix(in srgb, var(--accent-technology) 9%, transparent), transparent 60%);
  animation: mesh-drift var(--dur-ambient) var(--ease) infinite alternate;
}

@keyframes mesh-drift {
  from { transform: translate3d(-2%, -1.5%, 0) scale(1); }
  to { transform: translate3d(2%, 1.5%, 0) scale(1.1); }
}

@media (prefers-reduced-motion: reduce) {
  .feature__glow { animation: none; transform: none; }
}

.feature__body {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: var(--space-3) var(--space-4);
}

.feature__icon { width: 3rem; height: 3rem; }

.feature__title {
  margin-block-start: var(--space-1);
  font-size: var(--text-6);
  line-height: var(--leading-6);
  letter-spacing: var(--track-6);
  font-weight: var(--weight-semibold);
  color: var(--fg-1);
}

.feature__figures {
  margin-block-start: var(--space-2);
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.feature__figures .probability__value { font-size: var(--text-display); }
.feature__figures .probability__unit { font-size: var(--text-5); }

.feature__meter { flex: 1 1 14rem; min-width: 0; }
.feature__meter .card__bar { margin-block-start: 0; }

.feature__settle {
  margin-block-start: var(--space-2);
  font-size: var(--text-2);
  line-height: var(--leading-2);
  color: var(--fg-2);
}

.feature .card__positions { margin-block-start: var(--space-3); }

/* ---- The chart ---- */
/* Twelve points, plotted straight. No smoothing — a curve through twelve
   values implies readings between them that do not exist. No axis, no range
   control and no timestamp: this is a trailing shape, not a feed. */
.chart {
  margin-block-start: var(--space-2);
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 0;
}

.chart__svg {
  width: 100%;
  height: 5.5rem;
  overflow: visible;
}

.chart__line {
  fill: none;
  stroke: var(--ndio);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.chart__area {
  fill: color-mix(in srgb, var(--ndio) 10%, transparent);
  stroke: none;
}

/* Not a flex row. A flex container turns every run of text between two
   elements into its own anonymous item, and the row gap then opens a hole
   inside the sentence — "45 % now" rather than "45% now". */
.chart__note {
  margin-block-start: var(--space-2);
  font-size: var(--text-2);
  line-height: var(--leading-2);
  color: var(--fg-3);
}

.chart__note .badge {
  margin-inline-start: var(--space-2);
  font-size: var(--text-1);
  letter-spacing: var(--track-1);
}

/* On a phone the featured panel is the whole first screen, so it gives back
   what it can: the title steps down one rung, the numeral steps down to the
   card's own size, and the chart loses a third of its height. The panel
   still carries the same five markets. */
@media (max-width: 34rem) {
  .feature__title {
    font-size: var(--text-5);
    line-height: var(--leading-5);
    letter-spacing: var(--track-5);
  }

  .feature__figures .probability__value { font-size: var(--text-7); }
  .feature__figures .probability__unit { font-size: var(--text-4); }
  .feature__icon { width: var(--card-icon); height: var(--card-icon); }
  .chart__svg { height: 4.5rem; }
}

/* ---- The rail ---- */
.rail {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

.rail__heading { color: var(--fg-3); }

.rail__cards {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  gap: var(--space-2);
  align-content: stretch;
}

.rail-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  background: var(--surface-card);
  border: var(--border) solid var(--line);
  border-radius: var(--radius-2);
  transition: border-color var(--dur-1) var(--ease);
}

.rail-card:hover { border-color: var(--line-strong); }

.rail-card__icon {
  flex: none;
  box-sizing: border-box;
  width: 2rem;
  height: 2rem;
  padding: var(--space-1);
  border-radius: var(--radius-1);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

.rail-card__body { flex: 1 1 auto; min-width: 0; }

.rail-card__title {
  font-size: var(--text-2);
  line-height: var(--leading-5);
  font-weight: var(--weight-regular);
  color: var(--fg-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rail-card__body .card__bar { margin-block-start: var(--space-2); }

.rail-card__probability {
  flex: none;
  display: flex;
  align-items: baseline;
  gap: 1px;
}

.rail-card__probability .probability__value {
  font-size: var(--text-5);
  letter-spacing: var(--track-5);
}

.rail-card__probability .probability__unit { font-size: var(--text-2); }

@media (min-width: 62rem) {
  .rail__cards { grid-auto-rows: 1fr; }
}

/* Below the two-column breakpoint the rail becomes one scrolling row rather
   than four stacked cards. Stacked, it costs 300px of a 390px viewport
   before the grid starts; scrolled, it costs 80px and carries exactly the
   same four markets. */
@media (max-width: 61.99rem) {
  .rail__cards {
    grid-auto-flow: column;
    grid-auto-columns: minmax(16rem, 1fr);
    overflow-x: auto;
    padding-block-end: var(--space-2);
    scrollbar-width: thin;
  }

  /* In a 16rem track the title has about 130px after the icon and the
     numeral, which ellipsises most of these to two words. It wraps to two
     lines instead — the row is one row either way. */
  .rail-card__title {
    white-space: normal;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
  }
}

/* ======================================================================
   The Ndio / La control — docs/03b-ndio-la-toggle.md
   ====================================================================== */

.side {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--control-h);
  padding-inline: var(--space-2);
  border: var(--border) solid;
  border-radius: var(--radius-1);
  background: transparent;
  text-decoration: none;
  font-size: var(--text-2);
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease);
}

.side--ndio { border-color: var(--ndio); color: var(--ndio-text); }
.side--la { border-color: var(--la); color: var(--la-text); }

.side__price {
  color: var(--fg-1);
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
}

/* Two real text spans that cross-fade on opacity. Never content: "Yes" on a
   pseudo-element — generated content cannot be selected, is not reliably
   exposed to assistive technology, is skipped by find-in-page, and vanishes
   from a translated page. */
.side__labels {
  position: relative;
  display: inline-block;
}

/* The only in-flow child. Holds both words stacked in one grid cell, so its
   width resolves to the wider of the two and the control never moves.
   visibility: hidden, not display: none — a display-none element
   contributes no width. */
.side__sizer { display: grid; visibility: hidden; }
.side__sizer > span { grid-area: 1 / 1; }

.side__word {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  transition: opacity var(--dur-2) var(--ease);
}

.side__word--sw { opacity: 1; }
.side__word--en { opacity: 0; }

/* Gated, so a touch device never latches a half-hovered state after a tap. */
@media (hover: hover) and (pointer: fine) {
  .side:hover .side__word--sw { opacity: 0; }
  .side:hover .side__word--en { opacity: 1; }
  .side--ndio:hover { background: color-mix(in srgb, var(--ndio) 10%, transparent); }
  .side--la:hover { background: color-mix(in srgb, var(--la) 10%, transparent); }
}

/* Deliberately not gated: a keyboard user is never stuck in a stale focus
   state, and gating would deny the swap on a touch-primary device. */
.side:focus-visible .side__word--sw { opacity: 0; }
.side:focus-visible .side__word--en { opacity: 1; }

/* The swap still happens; it is instant instead of a cross-fade. Stated
   locally because the component would look broken if the global rule in
   base.css were ever scoped. */
@media (prefers-reduced-motion: reduce) {
  .side__word { transition: none; }
}

/* On a market page the control takes no action. aria-disabled, not
   disabled: a disabled button leaves the tab order and goes unannounced. */
.side[aria-disabled="true"] { cursor: default; }

/* ======================================================================
   Filter bar — docs/03c-filter-bar.md
   One row: category, a rule, region, and the count.
   ====================================================================== */

.filters {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  border: 0;
  padding: 0;
  margin: 0;
}

.filters__row {
  flex: 1 1 20rem;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  overflow-x: auto;
  padding-block: var(--space-2);
  scrollbar-width: thin;
}

/* The legend is visually hidden and absolutely positioned, so the browser's
   special legend placement takes no part in this row; the visible label
   beside it is an ordinary flex item. The fieldset still names the group. */
.filters__axis {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: none;
  border: 0;
  padding: 0;
  margin: 0;
  min-inline-size: 0;
}

.filters__label {
  flex: none;
  color: var(--fg-3);
  white-space: nowrap;
}

.filters__divider {
  flex: none;
  align-self: stretch;
  width: var(--border);
  background: var(--line);
  margin-inline: var(--space-2);
}

/* Off-screen, never display:none or visibility:hidden — either removes the
   input from the tab order and from the accessibility tree. */
.filters input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.filters__axis label {
  display: inline-flex;
  align-items: center;
  flex: none;
  min-height: var(--chip-h);
  padding-inline: var(--space-3);
  border: var(--border) solid var(--line-strong);
  border-radius: var(--radius-1);
  background: transparent;
  color: var(--fg-2);
  font-size: var(--text-2);
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--dur-2) var(--ease),
              border-color var(--dur-2) var(--ease),
              background-color var(--dur-2) var(--ease);
}

.filters__axis label:hover { color: var(--fg-1); }

/* Never green, never red — a green "Economy" chip would read as a Ndio
   position — and never a category accent either: the accent is identity on
   an icon, and a filled accent chip would read as a selected colour. */
.filters input:checked + label {
  color: var(--fg-1);
  border-color: var(--fg-1);
  background: var(--surface-card);
}

/* The ring belongs to the label, because the input itself is off-screen. */
.filters input:focus-visible + label {
  outline: var(--focus-width) solid var(--focus);
  outline-offset: var(--focus-offset);
}

/* ======================================================================
   Result count and empty state
   ====================================================================== */

.board__count {
  flex: none;
  font-size: var(--text-2);
  color: var(--fg-3);
  white-space: nowrap;
}

/* Shown only when the selected pair is one of the eight that hold nothing,
   or — with JavaScript — when a search matches nothing. Never a blank
   region and never an illustration. */
.board__empty {
  display: none;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  font-size: var(--text-3);
  color: var(--fg-2);
  padding-block: var(--space-5);
}

.board.is-empty .board__empty { display: flex; }

/* ======================================================================
   Breadcrumb — docs/05b-market-page.md
   ====================================================================== */

.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-2);
  color: var(--fg-3);
}

.crumbs a { color: var(--fg-3); text-decoration-color: var(--line-strong); }
.crumbs a:hover { color: var(--fg-1); text-decoration-color: var(--fg-1); }

/* ======================================================================
   Market page
   ====================================================================== */

.market__icon {
  flex: none;
  box-sizing: border-box;
  width: 3rem;
  height: 3rem;
  padding: var(--space-3);
  border-radius: var(--radius-2);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

.market__labels {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3);
  color: var(--fg-3);
}

.market__id {
  font-family: var(--font-mono);
  font-size: var(--text-2);
  color: var(--fg-3);
  text-transform: none;
  letter-spacing: 0;
}

.market__question {
  margin-block-start: var(--space-2);
  font-size: var(--text-6);
  line-height: var(--leading-6);
  letter-spacing: var(--track-6);
  font-weight: var(--weight-semibold);
  max-width: 26ch;
}

/* The question is the reason this page exists, so it is never truncated —
   but at 390px it runs to five lines at --text-6, so it steps down a rung
   and the icon steps down with it. */
@media (max-width: 34rem) {
  .market__question {
    font-size: var(--text-5);
    line-height: var(--leading-5);
    letter-spacing: var(--track-5);
  }

  .market__icon {
    width: var(--card-icon);
    height: var(--card-icon);
    padding: var(--space-2);
    border-radius: var(--radius-1);
  }

  .market__head { gap: var(--space-3); }
}

.market__probability {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.market__probability .probability__value {
  font-size: var(--text-display);
}

.market__probability .probability__unit {
  font-size: var(--text-5);
}

.market__probability .probability__side {
  font-size: var(--text-2);
  letter-spacing: var(--track-1);
  text-transform: uppercase;
}

.market__ratio {
  font-size: var(--text-2);
  color: var(--fg-3);
}

.market__bar { margin-block-start: var(--space-3); }

.market__positions {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-2);
}

.market__positions .side { font-size: var(--text-4); }

/* At --text-4 the price alone is 91px, and two controls no longer fit two
   columns at 320px or at 400% zoom. Step down to --text-3 rather than let
   the page scroll sideways. docs/07-accessibility.md. */
@media (max-width: 26rem) {
  .market__positions .side { font-size: var(--text-3); }
}

.market__positions-note {
  margin-block-start: var(--space-3);
  font-size: var(--text-2);
  color: var(--fg-2);
}

/* ---- Panel: the shared container for the summary, the settlement block,
   the ticket, the context block and the volume line. ---- */
.panel {
  background: var(--surface-card);
  border: var(--border) solid var(--line);
  border-radius: var(--radius-2);
  padding: var(--space-4);
}

.panel__title {
  font-size: var(--text-4);
  line-height: var(--leading-4);
  font-weight: var(--weight-medium);
  color: var(--fg-1);
  margin-block-end: var(--space-3);
}

.panel > p { color: var(--fg-2); }

/* ---- Settlement block — the reason the page exists ---- */
.settle-row {
  display: grid;
  gap: var(--space-1);
  padding-block: var(--space-3);
  border-block-start: var(--border) solid var(--line);
}

.settle-row:first-of-type { border-block-start: 0; padding-block-start: 0; }

@media (min-width: 34rem) {
  .settle-row {
    grid-template-columns: 11rem minmax(0, 1fr);
    gap: var(--space-4);
  }
}

.settle-row__key {
  font-size: var(--text-2);
  line-height: var(--leading-3);
  color: var(--fg-3);
}

.settle-row__val {
  font-size: var(--text-3);
  line-height: var(--leading-3);
  color: var(--fg-1);
}

.settle-row__val--ndio { color: var(--ndio-text); }
.settle-row__val--la { color: var(--la-text); }

/* ---- Order ticket ---- */
/* Every displayed figure is derivable by hand from the figures above it.
   See docs/05b-market-page.md. */
.ticket__field {
  display: grid;
  gap: var(--space-2);
  margin-block-end: var(--space-4);
}

/* A fieldset needs its UA border, padding and margin removed explicitly. */
.ticket__fieldset {
  border: 0;
  padding: 0;
  margin: 0 0 var(--space-4);
  min-inline-size: 0;
}

.ticket__fieldset .ticket__label { padding: 0; }
.ticket__fieldset .ticket__sides { margin-block-start: var(--space-2); }

.ticket__label {
  font-size: var(--text-2);
  color: var(--fg-2);
}

.ticket__sides {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
}

.ticket__sides input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.ticket__sides label {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--control-h);
  border: var(--border) solid var(--line-strong);
  border-radius: var(--radius-1);
  color: var(--fg-2);
  font-size: var(--text-2);
  cursor: pointer;
  transition: color var(--dur-2) var(--ease),
              border-color var(--dur-2) var(--ease),
              background-color var(--dur-2) var(--ease);
}

.ticket__sides input:checked + label {
  background: var(--surface-well);
  color: var(--fg-1);
}

.ticket__sides input:checked + label.is-ndio { border-color: var(--ndio); }
.ticket__sides input:checked + label.is-la { border-color: var(--la); }

.ticket__sides input:focus-visible + label {
  outline: var(--focus-width) solid var(--focus);
  outline-offset: var(--focus-offset);
}

.ticket__amount {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--control-h);
  padding-inline: var(--space-3);
  border: var(--border) solid var(--line-strong);
  border-radius: var(--radius-1);
  background: var(--surface-well);
}

.ticket__prefix { color: var(--fg-3); font-size: var(--text-2); }

.ticket__amount input {
  width: 100%;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--fg-1);
  font-size: var(--text-4);
  font-variant-numeric: tabular-nums lining-nums;
  outline: none;
}

.ticket__amount:focus-within {
  outline: var(--focus-width) solid var(--focus);
  outline-offset: var(--focus-offset);
}

.ticket__rows {
  border-block-start: var(--border) solid var(--line);
  padding-block-start: var(--space-2);
}

.ticket__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-2);
  font-size: var(--text-2);
  color: var(--fg-2);
}

.ticket__row dd,
.ticket__row .ticket__val {
  margin: 0;
  color: var(--fg-1);
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
}

.ticket__row--total {
  border-block-start: var(--border) solid var(--line);
  margin-block-start: var(--space-2);
  padding-block-start: var(--space-3);
  font-size: var(--text-3);
  color: var(--fg-1);
}

/* The single clearest signal that the product is described honestly. It is
   a row like any other; it is not styled as a warning, because it is not a
   warning — it is the other half of the arithmetic. */
.ticket__row--zero .ticket__val { color: var(--la-text); }

.ticket__note {
  margin-block-start: var(--space-3);
  padding-block-start: var(--space-3);
  border-block-start: var(--border) solid var(--line);
  font-size: var(--text-2);
  line-height: var(--leading-2);
  color: var(--fg-3);
}

.ticket__note li { margin-block-start: var(--space-2); }

/* ---- Volume line ---- */
.market__volume {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  font-size: var(--text-3);
  color: var(--fg-2);
}

.market__volume .badge { font-size: var(--text-1); letter-spacing: var(--track-1); }

/* ---- More in this category ---- */
.more__title {
  font-size: var(--text-5);
  line-height: var(--leading-5);
  letter-spacing: var(--track-5);
  font-weight: var(--weight-semibold);
  margin-block-end: var(--space-4);
}

/* ======================================================================
   Drift — docs/02c-space-motion.md
   The flash names the side the number moved toward, which is the one thing
   green and red are allowed to mean.
   ====================================================================== */

.probability__value.is-up { animation: flash-ndio var(--dur-flash) var(--ease) 1; }
.probability__value.is-down { animation: flash-la var(--dur-flash) var(--ease) 1; }

@keyframes flash-ndio {
  0% { color: var(--ndio-text); }
  40% { color: var(--ndio-text); }
  100% { color: var(--fg-1); }
}

@keyframes flash-la {
  0% { color: var(--la-text); }
  40% { color: var(--la-text); }
  100% { color: var(--fg-1); }
}

/* ======================================================================
   Written pages — the four documents in docs/05c-legal-pages.md
   ====================================================================== */

.eyebrow { color: var(--fg-3); }

/* ---- Brand block — /download only. docs/05-pages.md ---- */
/* getndiola.com and getndioaula.com both root at /download, so for anyone
   arriving from a poster or a shared link this page is their first sight of
   NdioLa, and a 26px mark in the header bar is not an arrival.

   The tile lockup, not the untiled letterforms: on the one page that explains
   how to put NdioLa on a home screen, the tile is the thing being described,
   and 02d permits the untiled mark only where a tile would be redundant. It
   replaces the eyebrow rather than joining it — two labels stacked above one
   h1 is one label too many — and it sits at --text-6 against an h1 at
   --text-display, so it identifies the page without competing with it.

   64px with --space-4 either side of it clears the 25% minimum clear space
   in 02d at every edge: 16px against the 16px required. */
.brand-block {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.brand-block__mark {
  flex: none;
  width: var(--mark-landing);
  height: var(--mark-landing);
}

.brand-block__name {
  font-size: var(--text-6);
  line-height: var(--leading-6);
  letter-spacing: var(--track-6);
  font-weight: var(--weight-semibold);
  color: var(--fg-1);
}

/* --space-2 is the gap under an 11px eyebrow. Under a 64px tile it would put
   the h1 inside the mark's clear space. */
.brand-block + .doc__title { margin-block-start: var(--space-5); }

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

.doc__title {
  margin-block-start: var(--space-2);
  font-size: var(--text-display);
  line-height: var(--leading-7);
  letter-spacing: var(--track-7);
  font-weight: var(--weight-semibold);
}

.doc__meta {
  margin-block-start: var(--space-4);
  font-size: var(--text-2);
  color: var(--fg-3);
}

.doc__lede {
  margin-block-start: var(--space-5);
  font-size: var(--text-4);
  line-height: var(--leading-4);
  color: var(--fg-2);
}

.doc__body { color: var(--fg-2); }
.doc__body h2 { color: var(--fg-1); }
.doc__body h3 { color: var(--fg-1); }
.doc__body strong { color: var(--fg-1); font-weight: var(--weight-medium); }

/* The reset removes list markers; prose lists need them back. */
.doc__body ul,
.doc__body ol { padding-inline-start: var(--space-5); }
.doc__body ul { list-style: disc; }
.doc__body ol { list-style: decimal; }
.doc__body li { margin-block-start: var(--space-2); }
.doc__body li::marker { color: var(--fg-3); }

/* Table of contents. Carries its own top margin — see the first-child note
   beside `.doc__body > p:first-child` above. */
.toc {
  margin-block-start: var(--space-5);
  padding: var(--space-4);
  background: var(--surface-card);
  border: var(--border) solid var(--line);
  border-radius: var(--radius-2);
}

.toc ol {
  margin-block-start: var(--space-2);
  padding-inline-start: var(--space-5);
  list-style: decimal;
  columns: 2;
  column-gap: var(--space-5);
}

@media (max-width: 34rem) { .toc ol { columns: 1; } }

/* Data table. */
.table-wrap { overflow-x: auto; }

.table th,
.table td {
  text-align: start;
  padding: var(--space-3) var(--space-4) var(--space-3) 0;
  border-block-end: var(--border) solid var(--line);
  font-size: var(--text-2);
  line-height: var(--leading-2);
}

.table th {
  color: var(--fg-3);
  font-weight: var(--weight-medium);
}

.table td { color: var(--fg-2); }
.table td.num {
  color: var(--fg-1);
  font-variant-numeric: tabular-nums lining-nums;
  white-space: nowrap;
}

/* Contact tiles. */
.tiles {
  display: grid;
  gap: var(--space-4);
  margin-block-start: var(--space-5);
}

@media (min-width: 43.75rem) {
  .tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.tile {
  padding: var(--space-4);
  background: var(--surface-card);
  border: var(--border) solid var(--line);
  border-radius: var(--radius-2);
}

.tile h3 { font-size: var(--text-3); font-weight: var(--weight-medium); }
.tile p { margin-block-start: var(--space-2); font-size: var(--text-2); color: var(--fg-2); }
.tile a { font-family: var(--font-mono); font-size: var(--text-2); }

/* Registered-address rows. */
.registry { margin-block-start: var(--space-4); }

.reg-row {
  display: grid;
  gap: var(--space-1);
  padding-block: var(--space-3);
  border-block-start: var(--border) solid var(--line);
}

@media (min-width: 34rem) {
  .reg-row { grid-template-columns: 12rem minmax(0, 1fr); gap: var(--space-4); }
}

.reg-row__key { font-size: var(--text-2); color: var(--fg-3); }
.reg-row__val { color: var(--fg-1); font-size: var(--text-3); }
.reg-row__val--mono { font-family: var(--font-mono); font-size: var(--text-2); }

.doc__signoff {
  margin-block-start: var(--space-6);
  padding-block-start: var(--space-4);
  border-block-start: var(--border) solid var(--line);
  font-size: var(--text-2);
  line-height: var(--leading-2);
  color: var(--fg-3);
}
