/* ---------------------------------------------------------------------------
   HATCH ↔ chachng.

   Everything needed to make the vendored HATCH library agree with this site,
   in one file, loaded on every page that loads HATCH and immediately after it.

   It exists because the first page built on HATCH silently lost all three
   brand faces. HATCH states that "typography is specified separately and
   intentionally absent" — and it means the FAMILIES. Its components still
   carry 148 font sizes, 101 weights and a `body` rule setting
   `ui-sans-serif`, so loading them replaced Outfit, Poppins and — worst —
   JetBrains Mono on the figures, taking the tabular numerals every column on
   this site aligns on with it.

   That fix originally lived in the guide's own stylesheet, which meant the
   second HATCH page would have reintroduced the bug without anything saying
   so. It is here now. `test/guards.mjs` fails if a page loads
   `components.css` without this file after it.

   `_ds/hatch/components.css` is vendored and is never edited; every
   reconciliation lives here.
--------------------------------------------------------------------------- */

/* --- Surfaces: white ground, half the rounding --------------------------- */
:root {
  /* HATCH's radii are moulded — 48px on a tile, 64px on the largest surface.
     Halved here. The scale keeps its ratios so cards, tiles, chips, buttons
     and inputs all soften by the same amount rather than drifting apart. */
  --r-xs:  0.25rem;   /*  8px → 4  */
  --r-sm:  0.4375rem; /* 14px → 7  */
  --r-md:  0.625rem;  /* 20px → 10 */
  --r-lg:  0.875rem;  /* 28px → 14 */
  --r-xl:  1.125rem;  /* 36px → 18 */
  --r-2xl: 1.5rem;    /* 48px → 24 */
  --r-3xl: 2rem;      /* 64px → 32 */

  /* FLYING has a second radius ladder, and it was never halved -- which is why
     the corners on the calculator pages and on the Calculators panel stayed
     round while everything on HATCH's scale came in. Both scales now land on
     the same numbers, so there is one set of corners on this site rather than
     two that happen to be close. */
  --radius-xs: 0.25rem;   /*  8px →  4 */
  --radius-sm: 0.4375rem; /* 14px →  7 */
  --radius-md: 0.625rem;  /* 20px → 10 */
  --radius-lg: 0.875rem;  /* 28px → 14 */
  --radius-xl: 1.125rem;  /* 36px → 18 */
  --radius-2xl: 1.5rem;   /* 48px → 24 */
  /* `--radius-pill` is not on the ladder: half of "fully round" is still
     fully round, and halving it would turn every chip into a rectangle. */
}

/* --- The day ground -------------------------------------------------------
   TRYING WHITE. FLYING sets `--raw-paper` to #F7F6FA -- Ghost White, a very
   slightly cool off-white -- and the page ground follows it, so a white card
   on the page has about two per cent of tone between it and the ground.

   This makes the ground white, which means cards are separated by shadow
   alone. The two ambient radials are still painted on top of it; they are the
   other half of what makes this page feel tinted, and they can come off
   separately if the white is still not white enough.

   Night is unaffected: its own block redefines `--raw-paper` to #151219 and
   comes later. */
:root {
  --raw-paper: #FFFFFF;
}

/* HATCH paints the body itself -- `background-color: var(--canvas)` plus two
   mint and iris radials -- through a bare `body` selector, and it loads after
   app.css, so this rule is what the reader actually gets.

   It said `background-image: none` and `background-color: var(--surface-card)`,
   which did two wrong things that were invisible in daylight. It swapped the
   ground (#F7F6FA) for a card (#FFFFFF) -- a difference you cannot see, until
   night makes ground and card the same tone and every card stops lifting. And
   `none` did not only cancel HATCH's radials; it cancelled OURS, so
   `--canvas-ambient` has never once painted, and the glass surfaces FLYING
   built to refract it have been refracting a flat colour.

   Both fixed by naming what we want instead of naming nothing. */
body {
  background-color: var(--surface-ground);
  background-image: var(--canvas-ambient);
}

.t,
.table-wrap {
  border: 0;
  box-shadow: var(--sh-sm);
}

/* A tinted tile is separated by its own fill. A white one on a white page has
   nothing but its shadow, so it takes the faintest line HATCH defines — plum
   at 10%, not grey. */
.t--white,
.table-wrap {
  background: var(--surface-card);
  border: 1px solid var(--line);
}

/* --- Typography: ours, not the system stack ------------------------------
   HATCH states that "typography is specified separately and intentionally
   absent" — and it means the FAMILIES. Its components still carry 148 font
   sizes and 101 weights, and its `body` rule sets `ui-sans-serif`. Loaded on a
   page, that quietly replaced all three brand faces: the headings, the chips,
   the table and — worst — the figures, which lost JetBrains Mono and with it
   the tabular numerals every column on this site is aligned on.

   So the faces are put back. HATCH's sizes are kept: they are its rhythm, and
   nothing here is trying to redesign the components.

   Weights are clamped to what is actually served. HATCH asks for 720, 750 and
   800; Outfit is fetched as a 400–700 variable cut and Poppins as 400/500/600
   statics, so anything above that either clamps or gets synthesised into a
   faux-bold. Naming the real weight is better than letting the browser guess. */

body,
.t-lbl, .t-sub, .hint, .cap,
table td, .h-progress, .alert, .toast, .tooltip, .menu-item, .field, .input, .select, .textarea {
  font-family: var(--font-sans);
}

h1, h2, h3, h4,
.t-title, .h-eyebrow, .chip, .badge, .btn, .step-n, .sec-head,
table th, .card h4, .drawer-head, .modal-head {
  font-family: var(--font-display);
}

/* Figures are mono and tabular — a column of numbers must not shift. */
.t-k, .metric, .num, .count, .stat {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--track-mono, -0.01em);
}

/* Outfit tops out at 700 and Poppins at 600; above that a browser fakes it. */
.t-title { font-weight: 600; }
.t-k { font-weight: 600; }
.t-lbl { font-weight: 500; }
.h-eyebrow, table th { font-weight: 500; }
.chip, .badge { font-weight: 500; }
.btn { font-weight: 500; }
h1, h2, h3 { font-weight: 500; }
/* --- The site's own tables, kept out of HATCH's -----------------------------
   HATCH styles tables through ELEMENT selectors, not classes:

     table          { min-width: 640px }
     thead th       { background; padding: 12px var(--s-6); border-block }
     tbody td       { padding: 14px var(--s-6); border-bottom; color }
     tbody tr:hover { background }
     thead th.num,
     td.num         { text-align: right }

   Every one of those lands on `.srctable` the moment a page loads HATCH, and
   `.srctable` is a site component with its own answers to all of them. That is
   where the header band came from, and the row hover, and the left inset on
   every cell, and the 640px floor that made four different tables overflow
   their container by exactly the same 98px.

   This is not a disagreement about how tables should look. It is that a
   vendored library styling bare elements reaches things it was never pointed
   at. The site's table keeps the site's rules; HATCH's own `.table-wrap`
   tables are untouched.

   Only the properties HATCH ADDS are undone here. Anything `.srctable` sets
   for itself already wins on specificity — a class beats a bare element — so
   restating it would be noise, and the one time I did restate it I typed
   `text-transform: none` over the uppercase the site had asked for. */
/* HATCH sets `min-width: 640px` on every bare `table`. That is right for its
   own `.table-wrap` surfaces, which scroll, and wrong for every table this
   site draws — it forced a 640px scale table inside a 552px card and clipped
   the last column clean off.

   Scoped to the site's tables rather than to one class, because this is the
   THIRD time the same declaration has surfaced somewhere new: first the duty
   scales, then the source register, now `.scale`. Naming classes one at a time
   was always going to keep losing. HATCH's own tables keep the floor. */
main table { min-width: 0; }   /* HATCH's own 640px floor */
.srctable thead th {
  background: none; border-block: 0;
  /* HATCH sets `white-space: nowrap` on a header cell. Removing the site's own
     nowrap only handed the column back to HATCH's, so the duty scale went on
     needing 425px in 388 — the change looked applied and measured identical.
     A heading that wraps costs a taller header row; a heading that cannot
     costs the reader a third of the table. */
  white-space: normal;
}
.srctable tbody td { border-bottom: 0; color: inherit; }
.srctable tbody tr { transition: none; }
.srctable tbody tr:hover { background: none; }
.srctable thead th.num,
.srctable td.num { text-align: left; }

/* An alert is a page-level wash, not a chip. HATCH's `--info-bg` is the FILL
   step of the accent glass (alpha .22), which is drawn for chips and badges —
   small surfaces that have to hold their own against body text around them. On
   a block the width of the measure it reads as a panel rather than a tint.
   Down to the wash step (.14), which is what the scale calls page-level. */
.alert { background: var(--iris-wash); }

/* --- The answers register --------------------------------------------------
   Five sections, each one heading and its copy on the left against its answers
   on the right. No cards: a card is a surface for something that has to be
   picked up and moved, and these are lines in a list. The only furniture is a
   hairline between rows and the number at the head of each one.

   The numbers run 01 to 34 straight through, across sections, because this is
   one register shown in five parts rather than five lists that each start
   again at one.

   The left column sticks while its own answers scroll past. Two of these
   sections are eleven and nine rows long, and a heading that scrolls away
   leaves the reader halfway down a column of country names with nothing left
   on screen saying what the question was. */
.ansecs { margin: 3rem 0 0; display: grid; gap: 3.5rem; }
.ansecs__empty {
  margin: 3rem 0 0; max-width: 44rem;
  font: var(--type-body); line-height: 1.6; color: var(--text-secondary);
}

.ansec { display: grid; gap: 1.25rem 3rem; align-items: start; }
@media (min-width: 56rem) {
  .ansec { grid-template-columns: minmax(0, 17rem) 1fr; }
  .ansec__intro { position: sticky; top: calc(var(--nav-offset, 6rem) + 1rem); }
}

.ansec__h {
  margin: 0;
  font: 500 1.125rem/1.3 var(--font-display); letter-spacing: -0.02em;
  color: var(--text-primary); text-wrap: balance;
}
.ansec__lede {
  margin: 0.625rem 0 0;
  font: 400 0.8125rem/1.55 var(--font-sans); color: var(--text-secondary);
  text-wrap: pretty;
}

.ansec__list { margin: 0; padding: 0; list-style: none; }
.ansec__list li { border-top: 1px solid var(--border-hairline); }
.ansec__list li:first-child { border-top: 0; }
.ansec__list a {
  display: grid; grid-template-columns: 2.5rem 1fr auto; align-items: baseline;
  gap: 0.875rem; padding: 0.6875rem 0;
  color: var(--text-primary); text-decoration: none;
}
.ansec__list a:hover .ansec__q { text-decoration: underline; text-underline-offset: 0.2em; }

.ansec__n {
  font: 500 0.75rem/1.6 var(--font-mono); color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
.ansec__q { font: 500 0.9375rem/1.4 var(--font-display); letter-spacing: -0.01em; text-wrap: pretty; }
/* The figure is the answer, so it is set in the mono the rest of the site uses
   for figures and right-aligned, which is the only way a column of them can be
   read against each other. */
.ansec__fig {
  font: 500 0.8125rem/1.4 var(--font-mono); font-variant-numeric: tabular-nums;
  color: var(--text-primary); white-space: nowrap;
}

@media (max-width: 56rem) {
  .ansec__list a { grid-template-columns: 2rem 1fr auto; gap: 0.625rem; }
}
.compare__bar i.is-here { background: var(--brand); }

/* --- The concession cliff's figures ----------------------------------------
   HATCH's bento, three tiles wide rather than its default four, because there
   are three figures and a four-column grid would leave a hole.

   `.k` and `.lbl` are the design system's stat pair; the value sits above its
   label, which is the way round that lets a reader scan the three figures
   without reading three labels first. */
.cliff__bento {
  /* Three across when the card can hold three, fewer when it cannot. Fixed at
     three inside a half-width card each tile came out 146px wide and the
     labels broke to four lines — "Room / before / the / concession / ends". */
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  grid-auto-rows: auto;
  gap: 0.75rem;
  margin: 1.5rem 0 1.25rem;
}
/* HATCH pads a bento tile for a tile the size of a card. These are three
   figures in a column half the page wide. */
.cliff__bento > * { padding: 1rem 1.125rem; gap: 0.375rem; }
.cliff__bento .h-stat { gap: 0.5rem; }
.cliff__bento .k { font-size: clamp(1.25rem, 2.4vw, 1.75rem); }
.cliff__bento .lbl { font-size: 0.75rem; line-height: 1.35; text-wrap: pretty; }

/* --- The band ruler --------------------------------------------------------
   Each band is a segment as wide as the range it covers, so the table below
   gains the one dimension it cannot show: how much of the market each band
   actually spans. Ink for the bands, lime for the one the reader is in, and a
   marker at their price.

   Hairlines BETWEEN segments rather than around them — a border on each would
   add its own width to a bar whose widths are the whole point. */
.bandstrip { margin: 0 0 1.25rem; }
.bandstrip__track {
  position: relative; display: flex; height: 1.25rem;
  border-radius: var(--r-pill); overflow: hidden;
  background: rgba(var(--ink-rgb), .06);
}
/* Every band its own colour, from HATCH's accent glass at its lightest.

   One grey for all of them made the ruler a single object with a lime notch in
   it, and the thing the ruler exists to say is that these are SEPARATE bands
   charging separate rates — the wide pale stretch in the middle of the NSW
   scale is one rate covering $387,000 to $1,290,000, and that only reads if it
   is visibly one slab rather than part of a continuous bar.

   Five accents, not six: butter sits at (227,234,91), a few degrees off lime,
   and the reader's own band is lime. Next to each other they would be the one
   comparison on this chart that has to be unambiguous. Five cycling over seven
   bands never repeats adjacently, which is the only constraint that matters. */
.bandstrip__band {
  display: block; height: 100%;
  background: rgba(var(--ink-rgb), .12);
  box-shadow: inset -1px 0 0 rgba(var(--paper-rgb), .9);
}
.bandstrip__band:nth-child(5n+1) { background: var(--sprout-fill); }
.bandstrip__band:nth-child(5n+2) { background: var(--mint-fill); }
.bandstrip__band:nth-child(5n+3) { background: var(--iris-fill); }
.bandstrip__band:nth-child(5n+4) { background: var(--sand-fill); }
.bandstrip__band:nth-child(5n+5) { background: var(--rose-fill); }
/* Last, so it beats whichever accent the cycle would have given it. */
.bandstrip__band.is-here { background: var(--brand); }

/* The marker sits ON the ruler, so it carries a ring in the page's own ground
   to stay visible over a lime band and an ink one alike. */
.bandstrip__you {
  position: absolute; top: 50%; width: 0.625rem; height: 0.625rem;
  border-radius: 50%; background: var(--text-primary);
  transform: translate(-50%, -50%);
  box-shadow: 0 0 0 0.1875rem var(--surface-solid);
}

.bandstrip__cap {
  display: flex; justify-content: space-between; gap: 1rem;
  margin-top: 0.5rem;
  font: 400 0.6875rem/1.3 var(--font-mono); color: var(--text-tertiary);
}
.bandstrip__cap span:nth-child(2) { color: var(--text-primary); font-weight: 500; }

/* --- Same house, three states ----------------------------------------------
   Three columns rather than three rows. A table asks the reader to compare
   down a column; three panels side by side put the figures where the eye
   compares them without being asked.

   The bar runs VERTICALLY here, which is the whole reason this reads
   differently from the row version: three columns of different height beside
   each other are a chart of the thing the section is about, and the tallest
   column is the state that costs the most. */
.states {
  /* Eight jurisdictions since 30 Aug 2026, not three, so the track count is no
     longer a constant. `auto-fit` wraps them into as many rows as the width
     needs without a breakpoint per count, and the minimum is set by the widest
     thing a column has to hold: a formatted total like $103,830.00. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
  gap: 1rem;
  margin: 0; padding: 0; list-style: none;
}
/* The column IS the panel. It used to be a `<button>` inside one, and the
   button carried all of this — the padding, the border, the hover lift. It
   reports now, so the panel is the list item and there is no hover state,
   because there is nothing to hover towards. */
.states__col {
  display: grid; align-content: start; gap: 0.5rem;
  padding: 1.125rem 1rem 1rem;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-lg);
  background: var(--surface-solid);
}
.states__col.is-here {
  border-color: var(--brand-edge);
  box-shadow: 0 0 0 1px var(--brand-edge);
}

.states__name {
  font: 500 0.6875rem/1.3 var(--font-mono); letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--text-tertiary);
}

/* A column of fixed height so the three bars share one scale.

   The bar is the gross duty. Inside it, the part actually handed over is
   solid; the part the concession removes is HATCH's hatch texture, the same
   language the waterfall uses for an amount that comes off. So a state where
   the concession wipes the duty shows a full column of hatch with nothing
   solid in it, which is exactly what happened, and a state where it does not
   shows the solid part standing up out of the texture. */
.states__bar {
  display: block; position: relative; height: 6.5rem;
  border-radius: var(--radius-sm); background: rgba(var(--ink-rgb), .05);
  overflow: hidden;
}
.states__gross {
  position: absolute; inset: auto 0 0 0; display: block;
  height: var(--pct);
  border-radius: var(--radius-sm);
  background-color: rgba(var(--ink-rgb), .07);
  background-image: var(--hatch);
}
/* Opaque, and this one is not cosmetic. A translucent fill let the hatch
   underneath show through, so a state with NO concession — where the kept part
   is the whole bar — rendered as a fully hatched column, which is the picture
   the chart uses for a concession that wipes the duty. The two opposite cases
   drew the same bar. Paper first, then the ink over it. */
.states__kept {
  position: absolute; inset: auto 0 0 0; display: block;
  height: var(--kept);
  border-radius: var(--radius-sm);
  background-color: var(--surface-solid);
  background-image: linear-gradient(rgba(var(--ink-rgb), .34), rgba(var(--ink-rgb), .34));
}
.states__col.is-here .states__kept { background: var(--brand); }

.states__fig {
  font: 500 1.375rem/1 var(--font-mono); font-variant-numeric: tabular-nums;
  letter-spacing: -0.03em; color: var(--text-primary);
}
.states__delta {
  font: 500 0.75rem/1.35 var(--font-display); color: var(--text-secondary);
  text-wrap: pretty;
}
.states__col.is-here .states__delta { color: var(--text-primary); }
.states__break {
  font: 400 0.625rem/1.5 var(--font-mono); color: var(--text-tertiary);
  text-wrap: pretty;
}

/* Below the two-column breakpoint the panel is already narrow; three columns
   of figures would each be about 90px. One per row, bar on its side. */
@media (max-width: 34rem) {
  .states { grid-template-columns: 1fr; }
  .states__col { grid-template-columns: 1fr auto; align-items: baseline; }
  .states__bar { grid-column: 1 / -1; height: 0.625rem; }
  .states__gross { inset: 0 auto 0 0; width: var(--pct); height: 100%; }
  .states__kept { inset: 0 auto 0 0; width: var(--kept); height: 100%; }
  .states__delta, .states__break { grid-column: 1 / -1; }
}

/* --- The key to a chart --------------------------------------------------
   Every bar on these pages says something in its fill, and the vocabulary is
   the same on all of them: solid ink is an amount charged, hatch is an amount
   that comes off, lime is the figure the page exists to produce. This is where
   that is stated, under the chart it applies to, in the chart's own fills
   rather than in words describing them. */
.ckey {
  list-style: none; margin: 0.875rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.625rem 1.5rem;
  font: var(--type-caption); color: var(--text-secondary);
}
.ckey__i { display: inline-flex; align-items: center; gap: 0.5rem; }
.ckey__sw {
  flex: none; width: 1.125rem; height: 0.6875rem; border-radius: 3px;
  background-color: rgba(var(--ink-rgb), .06);
}
/* The swatches are the fills themselves, not approximations of them — each one
   takes the same declaration as the bar it stands for, so a change to a bar
   cannot leave its key describing the old colour. */
.ckey__sw--ink   { background: rgba(var(--ink-rgb), .38); }
.ckey__sw--hatch { background-image: var(--hatch); background-color: rgba(var(--ink-rgb), .05); }
.ckey__sw--lime  { background: var(--brand); }
.ckey__sw--limehatch { background-image: var(--hatch-lime); background-color: rgba(var(--ink-rgb), .05); }
/* The bands are five different colours, so their swatch is the cycle rather
   than one of them — a single tint would read as "the bands that are THIS
   colour mean something", which is the one thing they do not. */
.ckey__sw--band {
  background: linear-gradient(90deg,
    var(--sprout-fill) 0 20%, var(--mint-fill) 20% 40%, var(--iris-fill) 40% 60%,
    var(--sand-fill) 60% 80%, var(--rose-fill) 80% 100%);
}
.ckey__sw--bandhere { background: var(--brand); }
.ckey__sw--shade { background: rgba(var(--ink-rgb), .28); }
.ckey__sw--zone1 { background: rgba(var(--sprout-hue), var(--a-press)); }
.ckey__sw--zone2 { background: rgba(var(--sand-hue), var(--a-press)); }
.ckey__sw--zone3 { background: rgba(var(--rose-hue), var(--a-press)); }
/* Two marks are points on a line rather than areas, so they are drawn at the
   size they appear on the chart and centred in the swatch box. */
.ckey__sw--mark {
  position: relative; background: none;
}
.ckey__sw--mark::before {
  content: ''; position: absolute; top: 50%; left: 50%; width: 0.625rem; height: 0.625rem;
  border-radius: 50%; background: var(--text-primary); transform: translate(-50%, -50%);
}
.ckey__sw--tick { position: relative; background: none; }
.ckey__sw--tick::before {
  content: ''; position: absolute; top: 0; bottom: 0; left: 50%;
  width: 1px; background: var(--raw-ink-40);
}

/* The three-state columns are a report, not a control. They were buttons, and
   a tap silently reloaded the whole calculator against that state — so the
   cursor, the hover lift and the pressed state all have to go with the button,
   or the column still offers something it no longer does. */
.states__col { cursor: default; }

/* --- The tooltip ---------------------------------------------------------
   One panel, fixed to the viewport and moved to whatever is being pointed at.
   Fixed rather than absolute because these triggers live inside clipped boxes:
   a band inside the strip's rounded track, a bar inside a panel that scrolls
   on its own. Positioned in the flow, half of them would be cut off by the
   thing they belong to. */
.tip {
  /* Above the sticky header, which sits at 30. */
  position: fixed; z-index: 60; top: 0; left: 0;
  max-width: 20rem; padding: 0.625rem 0.75rem;
  border-radius: var(--radius-md);
  background: var(--text-primary); color: var(--surface-solid);
  font: var(--type-caption); line-height: 1.45; text-wrap: pretty;
  box-shadow: 0 4px 10px rgba(var(--cast-rgb), .18), 0 18px 36px -12px rgba(var(--cast-rgb), .4);
  pointer-events: none;   /* never stands between the pointer and the chart */
}
/* It ARRIVES rather than blinks. `display: none` cannot be transitioned, so
   the panel is now always in the layout and hidden by `visibility` -- which
   can, and which keeps it out of the accessibility tree and off the pointer
   just as completely.

   A small rise and a small scale, from the edge it is anchored against: a tip
   that grows out of the thing it explains reads as belonging to it, where one
   that fades in place reads as a layer over the page. Fast, because it is an
   answer to a pointer and any wait at all feels like lag. */
.tip {
  transition: opacity var(--dur-micro) var(--ease-out),
              transform var(--dur-micro) var(--ease-out),
              visibility var(--dur-micro);
  transform-origin: 50% 100%;
}
.tip[hidden] {
  display: revert;            /* `hidden` would otherwise take it out entirely */
  visibility: hidden; opacity: 0;
  transform: translateY(4px) scale(.97);
}

/* Something has to say a segment will answer if you ask it. Not a cursor
   alone — that is invisible until the pointer is already there, and says
   nothing at all on a touch screen — so the segments also lift or brighten on
   hover and take the page's focus ring from the keyboard. */
/* `pointer`, not `help`. The question-mark cursor is a browser convention for
   documentation, and these are not documents -- they are parts of a chart that
   answer when you point at them. A hand says "this responds", which is what is
   actually true, and it is the same cursor every other interactive thing on
   the page uses. */
[data-tip] { cursor: pointer; }
/* HATCH rings `:focus-visible` on a bare selector with a two-layer lime
   box-shadow. On a threshold marker — a label, a figure and a tick in one box
   118px tall — that drew a lime rectangle around the words and nothing around
   the thing being described. A tight outline instead, and the shadow off. */
[data-tip]:focus-visible {
  outline: 2px solid var(--text-primary); outline-offset: 2px; box-shadow: none;
}

.bandstrip__band[data-tip] { transition: filter var(--dur-micro) var(--ease-out); }
.bandstrip__band[data-tip]:hover { filter: brightness(.92); }
/* The strip's segments are 11px tall and butt against each other, so the ring
   goes inside rather than outside where it would cover its neighbours. */
.bandstrip__band[data-tip]:focus-visible { outline-offset: -2px; }

.states__bar[data-tip] { transition: box-shadow var(--dur-micro) var(--ease-out); }
.states__bar[data-tip]:hover { box-shadow: 0 0 0 2px rgba(var(--ink-rgb), .12); }
.gold__track[data-tip] { transition: box-shadow var(--dur-micro) var(--ease-out); }
.gold__track[data-tip]:hover { box-shadow: 0 0 0 2px rgba(var(--ink-rgb), .12); }

/* --- The calculation ------------------------------------------------------
   Three columns, and the first of them is the point: a narrow gutter carrying
   nothing but the operator. Read down it and the receipt is the sum it claims
   to be. Kept out of the value cell because a leading minus is a character
   wide, and inside the column it pushed that one figure out of line with every
   other figure on the page — the reason ledgers have set signs this way for as
   long as there have been ledgers. */
/* --- The paper -----------------------------------------------------------
   One sheet. A receipt, with the two things that make it one: a head torn
   from the body along a perforation, and a bottom edge that ends in a row of
   half-circles rather than a straight cut.

   No frame and no ground. It was a white card inside a tinted pad, which read
   as two objects — a receipt in a tray — where it should read as one piece of
   paper lying on the page. What holds it off the page now is the shadow. */
/* The shadow lives out here, on a wrapper with no ground of its own. A filter
   is generated BEFORE the element's mask is applied, so a `drop-shadow` on the
   masked sheet is drawn and then clipped off by the very mask it was meant to
   trace — the sheet came out with no edge and no visible teeth at all. */
.rcpt {
  --tooth: 0.6875rem;       /* the half-circles along the bottom */
  --stub: rgba(var(--ink-rgb), .045);   /* the tint on the torn-off stub */
  /* `drop-shadow`, not `box-shadow`: a box shadow is drawn from the border box
     and would run straight across the teeth as though they were not cut.
     Three of them — a hairline that traces the mask — which is how the sheet gets an
     edge at all, since a real `border` would be sliced off by the same mask
     that cuts the teeth — then a tight shadow that seats it, then a wide soft
     one that lifts it. Without the first, white paper on a white page has no
     outline and the torn edge is invisible. */
  filter: drop-shadow(0 0 0.5px rgba(var(--cast-rgb), .28))
          drop-shadow(0 2px 2px rgba(var(--cast-rgb), .05))
          drop-shadow(0 14px 26px rgba(var(--cast-rgb), .10));
}
.rcpt__paper {
  position: relative;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  padding-bottom: var(--tooth);
  /* The strip the teeth are cut from takes the stub's tint, because it is part
     of the stub. Left white it made the total row look like a band laid ON the
     receipt rather than the bottom OF it — a tinted row with white paper
     showing underneath, which is exactly what a broken row looks like. */
  background-color: var(--surface-card);
  background-image: linear-gradient(var(--stub) 0 0);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% var(--tooth);
  /* Two mask layers: the sheet itself, stopping a tooth short of the bottom,
     and a row of half-circles hanging off it. */
  --sheet: linear-gradient(#000 0 0) 0 0 / 100% calc(100% - var(--tooth)) no-repeat;
  /* The radius is stated. Without it the circle sizes itself to the tile's
     farthest corner — 15.6px in a 22×11 tile — which paints the whole tile
     black and produces a perfectly flat edge that looks like the mask never
     applied at all. */
  --teeth: radial-gradient(var(--tooth) at 50% 0, #000 98%, #0000 100%)
           0 100% / calc(2 * var(--tooth)) var(--tooth) repeat-x;
  -webkit-mask: var(--sheet), var(--teeth);
  mask: var(--sheet), var(--teeth);
}
.rcpt__cap {
  margin: 0; padding: 1.25rem 1.25rem 1.125rem; text-align: left;
  font: var(--type-label); letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-secondary); text-wrap: pretty;
}

/* The tear line, and the only rule on the sheet that is not a divider between
   two figures. Dashed, full width, and nothing else on that row. */
.rcpt__perf, .ldg__perf td {
  height: 0; padding: 0; border: 0;
  border-top: 2px dashed rgba(var(--ink-rgb), .16);
}

/* HATCH styles tables through BARE ELEMENT selectors, and this is the fourth
   time that has drawn something on this site nobody asked for. `tbody td`
   carries a bottom border and its own padding; the receipt's operator and
   figure cells are `<td>` and its label is a `<th>`, so two columns of every
   row got a rule the third did not — the broken divider, and it survived the
   switch to separate borders because it was never the collapse algorithm's
   doing. Undone at the top of the component, before anything else is set. */
/* Only the declarations HATCH actually sets, and nothing more. Written as a
   blanket `border: 0; padding: 0` the first time, this reset outranked the
   component's own rules — `.ldg tbody td` is one class and two elements, which
   beats `.ldg__row > *` — and the receipt lost every rule and every scrap of
   padding it had. HATCH's `padding` and `color` on `td` are bare element
   selectors and already lose to the classes below; the bottom border is the
   one thing that needs undoing, because nothing below ever sets it. */
.ldg tbody td { border-bottom: 0; }
.ldg tbody tr { transition: none; }
.ldg tbody tr:hover { background: none; }

.ldg {
  width: 100%;
  /* SEPARATE, not collapse. Under `collapse` the browser resolves each shared
     edge to one cell or the other, and with three cells of differing padding
     it resolved them inconsistently down the table — a subtotal's rule drawn
     under its operator and its figure but not under its label, which is
     exactly the broken divider you could see. Separate borders are drawn by
     the cell that declares them, every time, and since only `border-top` is
     ever set there is nothing left to double. */
  border-collapse: separate; border-spacing: 0;
}
/* The caption is now the paper's printed head, above the perforation and
   outside the table. This keeps the table captioned for anything reading the
   markup rather than looking at it. */
.ldg__sr-cap {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
/* A step, not a row with a different border. The number says how many moves
   this calculation takes and which one you are reading — the thing sixteen
   identical rows could not say at all.

   The flex box is INSIDE the cell, never on it. `display: flex` on a `colspan`
   cell takes it out of the table layout altogether: the span stops applying,
   the three columns are then sized by the step header as if it were a
   first-column entry, and every row below it is pushed across. */
.ldg__stepcell { padding: 1.375rem 1.25rem 0.5rem; text-align: left; }
.ldg__stepin { display: flex; align-items: baseline; gap: 0.75rem; }
/* No divider directly under a step head — the head is already the break. */
.ldg__step + .ldg__row > * { border-top: 0; }
.ldg__no {
  flex: none; width: 1.5rem;
  font: 500 0.75rem/1 var(--font-mono); color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
.ldg__stepname {
  font: var(--type-label); letter-spacing: var(--track-label);
  text-transform: uppercase; font-weight: 600; color: var(--text-primary);
}
.ldg__chip {
  margin-left: auto; padding: 0.1875rem 0.5rem; border-radius: var(--radius-pill);
  background: rgba(var(--ink-rgb), .06);
  font: var(--type-caption); color: var(--text-secondary); text-align: right;
}

.ldg__row > * { border-top: 1px solid var(--border-hairline); padding: 0.6875rem 0; }
.ldg__op {
  width: 2.75rem; padding-left: 1.25rem; padding-right: 0;
  font: 500 1rem/1.4 var(--font-mono); color: var(--text-secondary);
  text-align: left; vertical-align: baseline;
}
.ldg__lbl {
  text-align: left; font: 400 0.875rem/1.4 var(--font-display);
  color: var(--text-secondary); font-weight: 400; text-wrap: pretty;
}
.ldg__qual { margin-left: 0.5rem; font: var(--type-caption); color: var(--text-tertiary); }

/* Only amounts are set as figures. A parameter the calculation READS — a price
   band, "part of it, on a sliding scale" — is prose, and setting it in the
   money column as tabular monospace was what stopped that column reading as a
   column of money. */
.ldg__val {
  padding-right: 1.25rem; padding-left: 1rem; text-align: right; vertical-align: baseline;
}
.ldg__val.is-money {
  font: 500 0.9375rem/1.4 var(--font-mono); font-variant-numeric: tabular-nums;
  color: var(--text-primary); white-space: nowrap;
}
.ldg__val.is-note {
  font: 400 0.8125rem/1.4 var(--font-display); color: var(--text-secondary); text-wrap: pretty;
}
/* Retained, and quieter. A $0 line is the evidence a rule was checked and did
   not apply; it is not one of the figures that moved the answer. */
.ldg__val.is-zero { color: var(--text-tertiary); }
/* The operator belongs to the figure for anyone reading the row aloud, and a
   gutter cell two columns to the left is not where a screen reader meets it. */
.ldg__sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* A subtotal closes its step: the rule above it is the line you draw under a
   column before you write what it comes to.

   And it is the line you were looking for. The tint is a whisper — two and a
   half percent of ink, half a step off the paper — because on a receipt the
   figure a reader hunts for is the one that comes to something, and the eye
   should find it while scrolling without anything on the page shouting. Any
   more than this and the receipt becomes a striped table. */
.ldg__row--sub > * {
  border-top: 1px solid var(--border-strong);
  padding-top: 0.8125rem; padding-bottom: 0.8125rem;
  background: rgba(var(--ink-rgb), .025);
}
.ldg__row--sub .ldg__lbl { font-weight: 600; color: var(--text-primary); }
.ldg__row--sub .ldg__val.is-money { font-size: 1.0625rem; }

/* No rule of its own: the perforation above it is the line. The stub is
   tinted a shade deeper than the subtotals — still under five percent — so the
   three landing points read as a set with the last of them the deepest. */
.ldg__row--total > * {
  border-top: 0;
  padding-top: 0.875rem; padding-bottom: 1.125rem;
  background: var(--stub);
}
.ldg__row--total .ldg__lbl {
  font: var(--type-label); letter-spacing: var(--track-label);
  text-transform: uppercase; font-weight: 600; color: var(--text-primary);
}
.ldg__row--total .ldg__val.is-money { font: 500 1.5rem/1.15 var(--font-mono); letter-spacing: -.02em; }

/* Narrow: the gutter closes up and the label is allowed to wrap, which it must
   — "Charged on the part above $387,000" does not fit one line beside a figure
   in a 320px column, and forcing it to would either clip it or push the table
   sideways. */
@media (max-width: 30rem) {
  .rcpt__cap, .ldg__stepcell { padding-left: 0.875rem; padding-right: 0.875rem; }
  .ldg__op { width: 1.75rem; padding-left: 0.875rem; }
  .ldg__val { padding-right: 0.875rem; padding-left: 0.625rem; }
  .ldg__val.is-money { font-size: 0.875rem; }
  .ldg__row--total .ldg__val.is-money { font-size: 1.25rem; }
  .ldg__lbl { font-size: 0.8125rem; }
}

/* --- Pay-cycle tabs ------------------------------------------------------
   HATCH's `tabs` — an underline on a rule — rather than the segmented pill
   these used to wear. The pill is this site's shape for a control that CHANGES
   an input; the ledger's period tabs change nothing, they pick which view of
   one calculation you are reading. Dressed as a pill they read as a sixth
   question. Everything structural comes from HATCH; only the marker below is
   ours. */
.ltabs { margin-bottom: 1.125rem; }
/* Which cycle the reader actually answered, as opposed to which they are
   looking at. They are usually the same and occasionally not, and that is the
   one thing this control has to say that the selected state does not. */
.ltabs__dot {
  display: inline-block; width: 0.3125rem; height: 0.3125rem; margin-left: 0.4375rem;
  border-radius: 50%; background: var(--brand); vertical-align: 0.125rem;
}

/* --- What is below the answer --------------------------------------------
   The map, placed after the answer rather than before it. It sits in the gap
   that used to be a third of a screen of nothing between the actions row and
   the first section of evidence — so it costs the page no height at all, and
   it replaces the one signal that used to say "this is the end".

   Two columns: the head states how much working there is, the list says what
   each piece of it does. Numbered, because these ARE in order — the schedule
   before the concession, the concession before the comparison — which is the
   order the calculation itself runs in. */
.below {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
  gap: 3rem; align-items: start;
  /* No margin of its own. The working section above it already ends with a
     full section gap, and adding a second one here is what made the void this
     block exists to fill. */
  margin-top: 0; padding-top: 0;
}
.below__head { position: sticky; top: calc(var(--nav-offset) + 1.5rem); }
.below__head h2 {
  margin: 0.875rem 0 0; font: 500 1.75rem/1.12 var(--font-display);
  letter-spacing: -0.025em; text-wrap: balance;
}
.below__head p {
  margin: 0.875rem 0 0; max-width: 34ch;
  font: var(--type-body-sm); color: var(--text-secondary); text-wrap: pretty;
}
.below__list {
  list-style: none; margin: 0; padding: 0;
  border-top: 1px solid var(--border-hairline);
}
.below__item { border-bottom: 1px solid var(--border-hairline); }
.below__item a {
  display: grid; grid-template-columns: 2.5rem minmax(0, 1fr) 1.5rem;
  align-items: baseline; gap: 0.875rem;
  padding: 1.375rem 0.25rem; text-decoration: none; color: inherit;
  transition: background var(--dur-micro) var(--ease-out);
}
.below__item a:hover { background: rgba(var(--ink-rgb), .028); }
.below__no {
  font: 500 0.75rem/1.4 var(--font-mono); color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
.below__label { display: block; font: 500 1rem/1.3 var(--font-display); color: var(--text-primary); text-wrap: pretty; }
.below__note { display: block; margin-top: 0.5rem; font: var(--type-caption); color: var(--text-secondary); text-wrap: pretty; }
.below__go {
  justify-self: end; font: 400 0.9375rem/1.4 var(--font-mono); color: var(--text-tertiary);
  transition: transform var(--dur-micro) var(--ease-out), color var(--dur-micro);
}
.below__item a:hover .below__go { transform: translateY(2px); color: var(--text-primary); }

/* Every target the list points at has to stop clear of the sticky header, or
   the link lands with the section's own heading hidden behind the nav. */
#band, #cliff, #compare, #qualify, #excluded, #gold-duty, #faq { scroll-margin-top: calc(var(--nav-offset) + 1rem); }

@media (max-width: 900px) {
  .below { grid-template-columns: 1fr; gap: 1.75rem; }
  .below__head { position: static; }
  .below__head p { max-width: none; }
}

/* --- The section bar ------------------------------------------------------
   One line under the header, up for as long as the reader is in the evidence.
   `fixed`, and appended to the body rather than rendered into the page: the
   calculator card sets `overflow: clip`, and a bar living inside it would be
   clipped by the very panel it has to sit above.

   Its height is a token because two other things have to know it — the space
   an anchored section leaves above itself, and the offset the sticky heading
   inside the contents block used to claim. */
:root { --secbar-h: 2.75rem; }
.secbar {
  position: fixed; z-index: 29;   /* under the nav at 30, over everything else */
  top: var(--nav-offset); left: 0; right: 0;
  height: var(--secbar-h);
  background: var(--surface-solid);
  border-bottom: 1px solid var(--border-hairline);
  box-shadow: 0 6px 16px -10px rgba(var(--cast-rgb), .3);
  /* Opaque, deliberately. It passes over the full-bleed lime panel, and a
     translucent ground would put the labels on a colour that changes as the
     reader scrolls. */
  transform: translateY(calc(-100% - var(--nav-offset)));
  opacity: 0; pointer-events: none;
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base);
}
.secbar.is-up { transform: none; opacity: 1; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) { .secbar { transition: none; } }

.secbar__list {
  display: flex; align-items: stretch; gap: 0;
  height: 100%; max-width: var(--shell); margin: 0 auto; padding: 0 var(--gutter);
  list-style: none;
  /* Seven labels need about 770px. That fits the 1129px of a 1280 card and
     does not fit the 707px of a tablet or the 343px of a phone, so below that
     it scrolls and the current entry is kept in view — the same treatment the
     pay-cycle tabs and HATCH's own tabs use. */
  overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.secbar__list::-webkit-scrollbar { display: none; }
.secbar__item { flex: none; }
.secbar__item a {
  display: flex; align-items: center; gap: 0.5rem; height: 100%;
  padding: 0 0.875rem; text-decoration: none; white-space: nowrap;
  border-bottom: 2px solid transparent;
  font: 500 0.8125rem/1 var(--font-display); color: var(--text-tertiary);
  transition: color var(--dur-micro), border-color var(--dur-micro);
}
.secbar__item a:hover { color: var(--text-primary); }
.secbar__no { font: 500 0.6875rem/1 var(--font-mono); color: var(--text-tertiary); font-variant-numeric: tabular-nums; }
/* HATCH's tabs mark the selected one with an ink underline, and this is the
   same kind of control, so it wears the same mark. */
.secbar__item a[aria-current="true"] { color: var(--text-primary); border-bottom-color: var(--text-primary); }
.secbar__item a[aria-current="true"] .secbar__no { color: var(--text-secondary); }
/* The heading of an arrived-at section is focused so the keyboard carries on
   from there, and a screen reader announces it. It is not a control and must
   not be ringed as though it were — HATCH rings `:focus-visible` on a BARE
   selector, so without this the whole reference card wore a lime ring the
   moment a bar link was followed. A heading has no shadow of its own, so
   clearing it here takes nothing else with it. */
:is(h1, h2, h3)[tabindex="-1"]:focus { outline: none; box-shadow: none; }

@media (max-width: 30rem) {
  .secbar__no { display: none; }
  .secbar__item a { padding: 0 0.75rem; }
}

/* A section arrived at from the bar has to clear BOTH fixed things above it.
   Without this the heading of every section the bar points at lands underneath
   the bar itself, which is the whole point of the bar failing at the moment it
   is used.

   Written against the attribute rather than a list of ids, so a page that
   marks a section gets the clearance with it and cannot forget. */
[data-nav] { scroll-margin-top: calc(var(--nav-offset) + var(--secbar-h) + 1rem); }

/* The contents block's own heading used to stick, at exactly the height the
   bar now occupies. Two things cannot hold the same line. */
.below__head { position: static; }

/* --- Night ----------------------------------------------------------------

   HATCH ships a `[data-theme="night"]` block: 45 of its 147 tokens and 10
   component rules. FLYING ships nothing — 163 tokens, one set, no media query.
   And FLYING is the palette these pages actually read: 27 of its colour tokens
   carry 562 uses here, against 27 HATCH tokens carrying 67. So switching
   HATCH's night on alone turned the text near-white and left the ground white.

   This is the missing half. It lives in the bridge because that is what the
   bridge is for and because `_ds/` is never edited (guards 12, 13).

   It is short because FLYING derives well. `--raw-ink`, `--raw-ink-06/-12/-40/
   -56/-80`, `--surface-sunken`, all four border tokens and all three glass
   tokens are expressed as `rgba(var(--ink-rgb), …)` or
   `rgba(var(--paper-rgb), …)`, so flipping the two primitives carries most of
   the 562 uses with it. What is listed below is what does NOT derive: the
   literals.

   THE ONE IDEA WORTH KEEPING: paper is the RAISED surface, ground is the page,
   and paper is always a step brighter than ground. In day that is #FFFFFF on
   #F7F6FA, a difference you can barely see, and cards read by their shadow. In
   night it is #221F26 on #151219 — because a shadow cannot separate two dark
   surfaces, and tone has to do the work the shadow did. Anything painted
   `rgba(var(--paper-rgb), …)` therefore keeps meaning "lift this off the page"
   in both themes, which is why the trays and cards did not need touching. */
/* Screen only. A statement printed from this site is a document somebody
   files, and a dark one is a page of toner with white letters knocked out of
   it. Print has no theme: it gets the day tokens, which are the ones the
   receipt and the schedule were designed against. */
@media screen {
  [data-theme="night"] {
    --ink-rgb: 242, 240, 246;
    --paper-rgb: 34, 31, 38;
    --raw-paper: #151219;

    /* Shadows go true black. Tinted shadow is a light-mode idea: on a dark
       ground a shadow carrying the ink hue is lighter than what it falls on. */
    --cast-rgb: 0, 0, 0;
    --cast-deep-rgb: 0, 0, 0;

    /* HATCH's own night ramp, which is contrast-tested against its canvas. */
    --text-secondary: #C9C5D4;
    --text-tertiary: #A19BAF;

    --surface-card: #221F26;
    --surface-raised: rgba(34, 31, 38, .92);

    /* The ambient wash still has to give the glass something to refract, but at
       day's alphas it reads as two grey clouds. */
    --ambient-cool: rgba(96, 104, 140, .20);
    --ambient-warm: rgba(126, 130, 62, .14);

    /* These mix toward white in day, which is the one word in them that is not
       a token. On a dark ground they have to mix toward the ground. */
    --surface-tint-mist: color-mix(in oklab, var(--raw-mist) 30%, #151219);
    --surface-tint-sage: color-mix(in oklab, var(--raw-sage) 26%, #151219);

    --shadow-1: 0 8px 28px -10px rgba(0,0,0,.55), 0 2px 6px -3px rgba(0,0,0,.40);
    --shadow-2: 0 34px 80px -22px rgba(0,0,0,.62), 0 10px 28px -14px rgba(0,0,0,.42);
    --shadow-3: 0 54px 120px -34px rgba(0,0,0,.70), 0 16px 44px -22px rgba(0,0,0,.45);
    --shadow-lift: 0 18px 50px -14px rgba(0,0,0,.58), 0 4px 14px -6px rgba(0,0,0,.38);

    /* A link hover that went darker than the text has to go brighter instead. */
    --sec-deep: #FFFFFF;
    --status-live: #C7E82E;

    /* Lime does not change between themes, so what sits ON lime cannot either.
       This is `var(--raw-ink)` in FLYING, which follows the ink -- correct in
       day, and near-white on a lime card in night. */
    --text-on-accent: #1F1B24;

    /* A wash is a faint tint OF THE GROUND, and the ground moved. Left at
       #F5FFC4 it stops being a highlight on a dark page and becomes a bright
       slab -- with inherited light text on it. Mixed into the ground instead, it
       goes back to meaning "this row is picked out", and the text that inherits
       is correct without any rule saying so. */
    --raw-vanilla-wash: color-mix(in oklab, #DDFF4F 13%, #151219);
  }

  /* The footer was the page's ending because it was the one dark band. On a dark
     page it cannot be that, and inverting it to a light slab would fight the
     ghost wordmark inside it, which is drawn at 10% white. So it becomes the
     raised tone instead, with a rule to say where the page stops. */
  /* Lime is the same colour in both themes, so the ink on it is too. Components
     that paint an accent surface and then let text inherit were reading the
     theme's ink -- correct in day by coincidence, near-white on lime at night.
     `--text-on-accent` already existed for this; `-soft` is the 80% step those
     components were taking off `--raw-ink-80`. Neither is in the night block,
     because neither changes. */
  :root { --text-on-accent-soft: color-mix(in oklab, var(--text-on-accent) 80%, transparent); }

  [data-theme="night"] .footer {
    background: var(--surface-card);
    border-top: 1px solid var(--border-hairline);
  }
}

/* --- Numbers in a table ----------------------------------------------------
   `.num` is HATCH's: `thead th.num, td.num { text-align: right;
   font-variant-numeric: tabular-nums }`. Ours adds the mono face and the
   tracking, and it lives HERE rather than in app.css because reskinning a
   HATCH component is what this file is for -- guard 42 says so, and it said so
   about this rule.

   `--range` is ours alone and comes with it, because splitting a modifier from
   the class it modifies across two files is how one of them gets deleted. */
.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--track-mono);
}
/* Inside a source table the figure is smaller and never wraps. */
.srctable .num { font: 400 0.75rem/1.35 var(--font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.num--range { white-space: normal; }
.num--range span { white-space: nowrap; }
