/* ---------------------------------------------------------------------------
   chachng — /au/stamp-duty
   Implements "Stamp Duty - Web.dc.html" on the FLYING design system.
   Every colour resolves through a _ds token; no raw hex except the two the
   mockup itself pins (the full-bleed white bands and the footer's off-white).
--------------------------------------------------------------------------- */

/* The width of a classic scrollbar, or 0 where the browser overlays them. On
   the root, a percentage resolves against the viewport MINUS that scrollbar
   while `100vw` includes it, so the difference is the scrollbar itself. Every
   full-bleed block on this site is `width: 100vw` with a negative margin, and
   on a browser that reserves scrollbar space each one overhangs the page by
   this much and gives it horizontal scroll.

   REGISTERED, not a plain custom property, and that is the whole point. An
   unregistered `--sbw: calc(100vw - 100%)` is inherited as unresolved text and
   re-evaluated wherever it is used, so `100%` becomes the width of whatever
   element reads it. Used inside a 544px column it computed the scrollbar at
   736px and took a third off the tables. Registering it as a <length> makes it
   compute once, at the root, and inherit as a number. */
@property --sbw {
  syntax: '<length>';
  inherits: true;
  initial-value: 0px;
}

:root {
  /* The colour channels live with the other base colours, in the design
     system's `tokens/colors.css`. Nothing here redefines them. */

  /* The primary action, in tokens. These lived in `selected-sections.css`,
     which only the homepage loads — which is how the same button ended up
     56px and borderless there and 48px with a rim everywhere else. */
  --cta-primary-height: 56px;
  --cta-primary-min-width: 14.25rem;
  --cta-primary-padding: 1.5rem;
  --cta-primary-type: 500 0.9375rem/1 var(--font-display);
  --cta-secondary-height: 3rem;

  /* Section opener rhythm. Three sections open the same way — eyebrow, heading,
     lede — and each had grown its own spacing: 0.875rem/1.25rem in the proof
     docket, 0.75rem/1rem in the two comparison sections. Same component, three
     answers. These are the one answer. */
  --opener-heading-gap: 0.875rem;
  /* 1.25rem read cramped under a 2.875rem heading — the eyebrow sits 0.875rem
     above it, and at 1.25rem below the heading did not own a band of its own.
     One token, so all three openers move together. */
  --opener-lede-gap: 2rem;

  /* Page-level aliases the mockup declares over the DS tokens. */
  --sec: var(--raw-ink);
  --sec-deep: #0F0F0F;
  /* The focus ring, and the same ring in both themes. Focus used to turn the
     field's border to `--sec` -- the ink -- which drew a dark box round the
     control the moment it was tapped. Lime says "here" without doing that, and
     lime does not move between day and night, so neither does this. */
  --focus-halo: color-mix(in oklab, var(--surface-accent) 34%, transparent);
  --sec-surface: var(--raw-vanilla-wash);
  /* HATCH's law: ink is a text colour, it never fills a shape. The "on" fill —
     the selected year, the finished step, the completed bar — climbs toward
     lime instead. See DEC-118. */
  --ter: var(--raw-vanilla);
  --on-ter: var(--raw-ink);
  --ter-surface: var(--raw-vanilla-wash);

  --shell: 83.9375rem;
  --gutter: 1.5rem;
  --section-gap: 10.5rem;
  /* The header's real height. Declared as a sensible default and then
     MEASURED at runtime — see `chrome.js` — because everything that has to
     clear the header reads this, and the header is not the same height at
     every breakpoint or every reader's font size. */
  --nav-offset: 3.5rem;
  /* One content edge for every section below the answer, whether it is in a
     card, in a panel or in neither. Three of them had drifted apart — 76px,
     109px and 112px on the same page — because each treatment set its own
     padding and the border on one of them was never counted. */
  --sec-inset: 2.25rem;
  --sbw: calc(100vw - 100%);
}

/* Anchors ease to their target instead of teleporting. Global rather than
   per-page: every in-page link on the site wants the same behaviour, and a
   jump that lands mid-article gives no sense of how far it moved.

   Behind `prefers-reduced-motion` — a long smooth scroll is exactly the kind
   of large-area movement that setting exists to stop, and a reader who has
   asked for less motion still needs the link to work. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--surface-ground);
  background-image: var(--canvas-ambient);
  background-repeat: no-repeat;
  font-family: var(--font-sans);
  color: var(--text-primary);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--text-primary); text-decoration-color: var(--raw-ink-40); text-underline-offset: 3px; }
a:hover { color: var(--sec-deep); text-decoration-color: currentColor; }

button { font-family: inherit; }

/* There is no focus rule here, and that is the correction rather than the
   omission. There was one -- a 2px ink outline -- and it never once applied.
   HATCH styles `:focus-visible` through a BARE selector (`components.css:62`),
   which scores 0-1-0, and `:where(…)` contributes nothing, so ours scored
   0-1-0 too and HATCH's came later. Every focus ring on this site has been
   HATCH's lime one since the day HATCH landed.

   Keeping ours would have meant winning a specificity race to replace a ring
   that is contrast-tested at 3:1 with one that is not obviously better. One
   focus language, and it is the one that has been shipping. */

.shell { max-width: var(--shell); margin: 0 auto; padding: 0 var(--gutter); }
.eyebrow {
  font: var(--type-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-secondary);
}
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --- Nav ----------------------------------------------------------------- */

/* A bar, edge to edge, not a pill floating in a margin.

   The pill left a transparent band between its own bottom edge and the bottom
   of the sticky header's box — 16px of page showing through, which was
   invisible until something else had to sit flush underneath it and then read
   as a seam. Every width below 900px already drew the header this way; this
   only brings the desktop into line with the rest of the site.

   The rounding, the floating shadow and the inset highlight all belonged to an
   object with air around it. A bar that meets both edges keeps a hairline and
   a shadow shallow enough to say "this is above the page" and nothing more. */
.nav {
  position: sticky; top: 0; z-index: var(--z-chrome); padding: 0;
  background: var(--surface-glass);
  backdrop-filter: blur(28px) saturate(180%);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
  border-bottom: 1px solid var(--border-glass);
  box-shadow: 0 1px 2px rgba(var(--cast-rgb),.03), 0 10px 24px -18px rgba(var(--cast-rgb),.28);
}
.nav__pill {
  max-width: var(--shell); margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  min-height: 3.5rem; padding: 0 var(--gutter);
  border: 0; border-radius: 0; background: transparent; box-shadow: none;
}
.nav__left, .nav__right { display: flex; align-items: center; flex: none; }
.nav__left { gap: 1.25rem; }
.nav__right { gap: 0.75rem; }
/* The mark is artwork now, so the only thing left to say about it is how big
   it is. Height, never width: the drawing has one aspect ratio and the nav has
   a line to sit on. */
.wordmark { display: inline-flex; align-items: center; text-decoration: none; }
.wordmark__art { display: block; height: 1.125rem; width: auto; }
.nav__links {
  display: flex; align-items: center; gap: 1.125rem;
  font: var(--type-caption); font-weight: 500; color: var(--text-secondary); white-space: nowrap;
}
/* Direct children only. These are the nav's own links, and they used to be the
   only links inside `.nav__links` -- then the Calculators menu grew a panel of
   them. A descendant selector reached in and repainted the menu's current-page
   tile: `.nav__links a[aria-current]` scores 0-2-1 against the tile's own
   0-2-0, so on the calculator you were already on, the name went the colour of
   the page text -- which on the lime tile at night is lime on lime -- and
   picked up a 2px underline it was never meant to have. */
.nav__links > a { flex: none; text-decoration: none; color: inherit; }
.nav__links > a[aria-current="page"] { color: var(--text-primary); padding-bottom: 2px; border-bottom: 2px solid var(--sec); }

.fy { display: flex; flex: none; gap: 3px; padding: 0.25rem; border: 1px solid var(--border-subtle); border-radius: var(--radius-pill); background: var(--surface-sunken); }
.fy button {
  min-height: 1.875rem; flex: none; white-space: nowrap; padding: 0.375rem 0.875rem;
  border: none; border-radius: var(--radius-pill); cursor: pointer;
  font: var(--type-mono-sm); background: transparent; color: var(--text-primary);
  transition: background var(--dur-micro) var(--ease-out);
}
.fy button[aria-pressed="true"] { background: var(--surface-card); box-shadow: var(--shadow-1); color: var(--text-primary); }
.fy button:active { transform: scale(var(--press-scale)); }

.version { font: var(--type-mono-sm); color: var(--text-tertiary); text-decoration: none; }

/* --- Day or night ---------------------------------------------------------
   A switch with both states on it, not a button showing one glyph. The reader
   can see where it is going as well as where it is, which is the whole
   argument for a track over a toggle button.

   The knob sits under the icons rather than over them: a solid knob on top of
   a line icon hides the icon, and then only one state is visible again. */
.themetoggle {
  position: relative; flex: none;
  display: inline-flex; align-items: center; justify-content: space-between;
  width: 3.5rem; height: 1.875rem; padding: 0 0.3125rem;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-pill);
  background: var(--surface-sunken);
  cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out);
}
.themetoggle:hover { background: var(--raw-ink-12); }
.themetoggle__knob {
  position: absolute; top: 50%; left: 0.1875rem;
  width: 1.4375rem; height: 1.4375rem; border-radius: 50%;
  background: var(--surface-card);
  box-shadow: 0 1px 2px rgba(var(--cast-rgb), .16), 0 2px 6px -2px rgba(var(--cast-rgb), .18);
  transform: translateY(-50%);
  transition: transform var(--dur-base) var(--ease-out);
}
.themetoggle[aria-checked="true"] .themetoggle__knob { transform: translate(1.625rem, -50%); }

.themetoggle__ico {
  position: relative; z-index: 1;
  width: 1rem; height: 1rem;
  fill: none; stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
  color: var(--text-tertiary);
  transition: color var(--dur-base) var(--ease-out);
}
/* The icon the knob is sitting on is the state you are in, so it takes the
   full ink; the other stays quiet. */
.themetoggle[aria-checked="false"] .themetoggle__ico--sun,
.themetoggle[aria-checked="true"] .themetoggle__ico--moon { color: var(--text-primary); }
/* The moon is drawn slightly smaller: a crescent reads heavier than an
   outlined disc with rays at the same box size. */
.themetoggle__ico--moon { width: 0.875rem; height: 0.875rem; }

@media (prefers-reduced-motion: reduce) {
  .themetoggle__knob, .themetoggle__ico { transition: none; }
}

/* The drawer trigger. Left of the wordmark, so the first thing under a thumb
   is the way out of the page rather than the way back to the homepage. */
/* No disc behind it. The mark is three rules and reads as itself; a circle
   around it made a second object out of what is already a button, and put two
   competing round shapes at either end of a bar that is itself round. */
.nav__burger {
  display: none; flex: none;
  width: 2.25rem; height: 2.25rem; padding: 0;
  align-items: center; justify-content: center;
  border: 0; border-radius: 50%;
  background: none; color: var(--text-primary); cursor: pointer;
}
.nav__burger:hover { color: var(--text-secondary); }

/* --- The drawer ------------------------------------------------------------
   Off-canvas from the left, over a scrim. It is `position: fixed` and a child
   of the nav, which carries a `backdrop-filter` -- so, like the Calculators
   panel, it is moved to the end of the page after render. That is done once,
   for both, in `renderChrome`. */
.nav__scrim {
  position: fixed; inset: 0; z-index: calc(var(--z-chrome) + 1);
  background: rgba(var(--cast-deep-rgb), .44);
  opacity: 0; transition: opacity var(--dur-base) var(--ease-out);
}
.nav__scrim.is-open { opacity: 1; }
.nav__scrim[hidden] { display: none; }

.nav__drawer {
  position: fixed; z-index: calc(var(--z-chrome) + 2); top: 0; bottom: 0; left: 0;
  width: min(20rem, 86vw);
  display: flex; flex-direction: column;
  padding: 1rem 1rem 1.5rem;
  overflow-y: auto; overscroll-behavior: contain;
  background: var(--surface-card);
  border-right: 1px solid var(--border-hairline);
  box-shadow: var(--shadow-3);
  transform: translateX(-100%);
  transition: transform var(--dur-base) var(--ease-out);
}
.nav__drawer.is-open { transform: translateX(0); }
.nav__drawer:focus { outline: none; }
.nav__drawer[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) {
  .nav__drawer, .nav__scrim { transition: none; }
}

.nav__drawerhead {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding-bottom: 1rem; margin-bottom: 0.5rem;
  border-bottom: 1px solid var(--border-hairline);
}
.nav__drawerclose {
  flex: none; display: grid; place-items: center;
  width: 2.25rem; height: 2.25rem; padding: 0;
  border: 0; border-radius: 50%;
  background: none; color: var(--text-primary); cursor: pointer;
}
.nav__drawerclose:hover { color: var(--text-secondary); }

.nav__drawerlabel {
  display: block; margin: 1rem 0 0.5rem; padding: 0 0.75rem;
  font: var(--type-label); letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-tertiary);
}
.nav__drawernav { display: flex; flex-direction: column; }
.nav__drawernav a {
  display: flex; align-items: start; gap: 0.75rem;
  padding: 0.6875rem 0.75rem; border-radius: var(--radius-md);
  text-decoration: none; font: 500 0.9375rem/1.25 var(--font-display); color: var(--text-primary);
}
.nav__drawernav a > svg { flex: none; margin-top: 0.0625rem; color: var(--text-tertiary); }
.nav__drawertext { display: block; }
.nav__drawertext span {
  display: block; margin-top: 0.1875rem;
  font: var(--type-caption); font-weight: 400; color: var(--text-secondary);
}
.nav__drawernav a[aria-current="page"] { background: var(--surface-accent); color: var(--text-on-accent); }
.nav__drawernav a[aria-current="page"] > svg { color: var(--text-on-accent); }
.nav__drawernav a[aria-current="page"] .nav__drawertext span { color: var(--text-on-accent-soft); }
.nav__drawernav--plain a { font-weight: 500; }

.nav__drawermeta {
  margin-top: auto; padding: 1rem 0.75rem 0;
  border-top: 1px solid var(--border-hairline);
  font: var(--type-mono-sm); color: var(--text-tertiary); text-decoration: none;
}

/* One navigation, everywhere. The comment here used to say the homepage nav was
   "the canonical primary navigation everywhere" while being scoped to a
   `.nav--home` class — which the homepage and the five calculators passed and
   the six content pages did not, so those six quietly rendered a second nav
   with the links left-aligned and the version chip showing. The modifier is
   gone; this is simply the nav.

   Category links stay geometrically centred even when the wordmark and the
   right-side utilities are different widths. */
@media (min-width: 901px) {
  .nav__pill { position: relative; }
  .nav__links { position: absolute; left: 50%; transform: translateX(-50%); }
  .version { display: none; }
}

.btn-accent {
  flex: none; white-space: nowrap; height: 2.75rem; padding: 0 1.25rem;
  border: 1px solid var(--raw-vanilla-deep); border-radius: var(--radius-pill); cursor: pointer;
  background: var(--raw-vanilla-deep); color: var(--text-on-accent);
  font: 500 0.875rem/1 var(--font-display);
  box-shadow: 0 1px 2px rgba(var(--cast-rgb),.06), 0 10px 22px -8px rgba(var(--cast-rgb),.16), inset 0 1px 0 rgba(var(--paper-rgb),.5);
  transition: filter var(--dur-micro), transform var(--dur-micro);
}
.btn-accent:hover { filter: brightness(.975); }
.btn-accent:active { transform: scale(var(--press-scale)); }

.btn-quiet {
  flex: none; height: 2.25rem; padding: 0 0.875rem;
  border: 1px solid var(--border-strong); border-radius: var(--radius-pill);
  background: var(--surface-sunken); cursor: pointer;
  font: 500 0.75rem/1 var(--font-display); color: var(--text-primary);
  transition: background var(--dur-micro) var(--ease-out), border-color var(--dur-micro), transform var(--dur-micro);
}
.btn-quiet:hover { background: var(--surface-tint-mist); border-color: var(--sec); }
.btn-quiet:active { transform: scale(var(--press-scale)); }
.btn-quiet--lg { height: 3rem; padding: 0 1.25rem; font: 500 0.875rem/1 var(--font-display); }

/* --- Hero ---------------------------------------------------------------- */

/* `dvh`, not `vh`. On mobile the URL bar shows and hides, and `100vh` is the
   TALLEST state — so a full-height element is taller than the visible area
   whenever the bar is showing, and the page jumps as it collapses. The dynamic
   unit tracks the viewport that is actually visible. */
.page { min-height: 100dvh; }
/* The heading block is the only thing between a reader and the calculator, so
   it is sized as an introduction rather than a title page. It was 88px of top
   padding, a 68px heading and an 18px lede — around 460px before the first
   control, at every screen size. */
.main { max-width: var(--shell); margin: 0 auto; padding: 2.75rem var(--gutter) 0; }
/* The homepage hero supplies its own top offset in `.herowrap`, so the body
   shell below it must not add a second one. */
.main--body { padding-top: 0; }

/* Calculator routes now use the same wide, contained editorial canvas as the
   homepage hero and selected sections. Inner workspaces retain their readable
   920px measure; only the page frame changes. */
.main--calculator {
  width: min(calc(100% - (2 * var(--gutter))), var(--shell));
  max-width: none;
  /* Clear of the nav. The card began flush against the bottom of a sticky
     header, so on any screen shorter than 1120px — where the rules below trim
     its inner padding to get the first question into the fold — the eyebrow
     sat about sixteen pixels under the wordmark with nothing between them.
     This margin is outside those rules and does not move. */
  margin: 1.25rem auto 6rem; padding: 5.5rem clamp(1.5rem, 4vw, 4rem) 6rem;
  overflow: clip; border: 1px solid var(--border-subtle); border-radius: 1.75rem;
  /* No ground of its own, deliberately. It asked for `--surface-page`, which
     is a token nobody ever defined — so the declaration has been invalid and
     dropped since the day it was written, and the panel has been showing the
     body's paper and its ambient wash through the whole time. That is what it
     should do: the border and the radius are what make it a panel, and an
     opaque fill here would cut the ambient off at the card's edge. */
}

.hero { display: grid; grid-template-columns: minmax(0,1.1fr) minmax(0,1fr); gap: 3rem; align-items: start; }
.status {
  display: inline-flex; align-items: center; gap: 0.5rem; margin: 0 0 1.125rem;
  font: var(--type-caption); font-weight: 500; color: var(--text-secondary);
}
.hero h1 { margin: 0; font: 500 3.25rem/1.08 var(--font-display); letter-spacing: -0.025em; text-wrap: pretty; }
.hero p { margin: 0.375rem 0 0; font: var(--type-body); line-height: 1.6; color: var(--text-secondary); text-wrap: pretty; }

.hero--calculator {
  display: flex; flex-direction: column; align-items: center; gap: 0;
  max-width: 56.25rem; margin: 0 auto; text-align: center;
}
.hero--calculator .hero__utility { display: flex; align-items: center; justify-content: center; gap: 0.875rem; margin-bottom: 0.75rem; }
.hero--calculator .hero__fy { background: rgba(var(--paper-rgb),.44); }
.hero--calculator h1 { max-width: 46rem; font-size: clamp(2rem, 3.4vw, 3rem); line-height: 1.05; }
.hero--calculator .hero__body { margin-top: 0.75rem; max-width: 40rem; font-size: 1rem; line-height: 1.5; }

/* --- Input card ---------------------------------------------------------- */

.inputs {
  margin: 1.5rem auto 0; max-width: 57.5rem;
  display: flex; flex-direction: column; min-width: 0; width: 100%;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-2xl);
  background: var(--surface-card);
  box-shadow: 0 2px 4px rgba(var(--cast-rgb),.05), 0 44px 88px -28px rgba(var(--cast-rgb),.30);
  overflow: hidden; scroll-margin-top: 6.25rem;
}
.inputs__head { padding: 1.5rem 1.5rem 0.375rem; }
.inputs__headrow { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; }
.inputs__head h2 { margin: 0; font: 500 1.25rem/1.25 var(--font-display); letter-spacing: -0.02em; }
.inputs__lead { margin: 0.5rem 0 0; font: var(--type-body-sm); color: var(--text-secondary); }
.inputs__controls { display: flex; align-items: center; gap: 0.75rem; }
.inputs__step { font: var(--type-mono-sm); color: var(--text-secondary); white-space: nowrap; }

.progress { display: flex; align-items: center; gap: 0.625rem; margin-top: 0.875rem; }

.qs { padding: 0.875rem 1.5rem 1.5rem; }
.q { border-bottom: 1px solid var(--border-hairline); }
.q__trigger {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
  padding: 1rem 2px; background: transparent; border: none; cursor: pointer; text-align: left;
  transition: opacity var(--dur-micro);
}
.q__trigger:hover { opacity: .7; }
.q__id { display: flex; align-items: center; gap: 0.75rem; min-width: 0; }
.q__glyph {
  width: 1.375rem; height: 1.375rem; border-radius: 50%;
  border: 1px solid var(--border-strong); background: var(--surface-card); color: var(--text-tertiary);
  display: flex; align-items: center; justify-content: center;
  font: 600 0.6875rem/1 var(--font-mono); flex: none;
}
.q[data-open="true"] .q__glyph { border-color: var(--sec); color: var(--text-primary); }
.q[data-done="true"][data-open="false"] .q__glyph { border-color: var(--ter); background: var(--ter); color: var(--on-ter); }
.q__label { font: 500 0.9375rem/1.3 var(--font-display); color: var(--text-secondary); }
.q[data-done="true"] .q__label, .q[data-open="true"] .q__label { color: var(--text-primary); }
.q[data-open="true"] .q__trigger { padding-top: 1.375rem; padding-bottom: 0.75rem; }
.q[data-open="true"] .q__label { font: 500 1.5rem/1.16 var(--font-display); letter-spacing: -0.02em; }
.q[data-open="true"] .q__glyph { width: 1.75rem; height: 1.75rem; font-size: 0.75rem; }
.q__meta { display: flex; align-items: center; gap: 0.625rem; flex: none; }
.q__value {
  border-radius: var(--radius-pill); font: 500 0.8125rem/1.2 var(--font-mono);
  font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--text-tertiary);
}
.q[data-done="true"][data-open="false"] .q__value { padding: 0.3125rem 0.625rem; background: var(--sec-surface); color: var(--text-primary); }
.q__edit { font: var(--type-caption); color: var(--text-tertiary); text-decoration: underline; text-decoration-color: var(--raw-ink-40); }

/* No transition until the page has painted once.

   The panel's height is set by script after render, and animating it up from
   zero meant the first question opened over 260ms while everything below it
   stepped down the page — a cascade of layout shifts on every load, for an
   animation nobody asked for because nobody had interacted yet. It is the
   larger part of this page's CLS.

   The transition is worth having when a reader opens a question. It is worth
   nothing at all on arrival, so `.is-animated` is added a frame after the
   first render and the opening state simply is. */
.q__panel { overflow: hidden; max-height: 0; }
.is-animated .q__panel { transition: max-height var(--dur-base) var(--ease-out); }
.q__panelinner { padding: 0 2px 1.125rem; }
.q__explain { margin: 0 0 0.875rem; font: var(--type-caption); line-height: 1.5; color: var(--text-tertiary); text-wrap: pretty; }

.pills { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.pill {
  height: 2.875rem; padding: 0 1.125rem; border: 1px solid var(--border-subtle); border-radius: var(--radius-pill);
  cursor: pointer; background: var(--surface-sunken); color: var(--text-primary); font: var(--type-body-sm);
  transition: background var(--dur-micro) var(--ease-out), box-shadow var(--dur-micro), transform var(--dur-micro);
}
.pill--num { font: 500 1rem/1 var(--font-mono); font-variant-numeric: tabular-nums; }
.pill:hover { background: var(--surface-tint-mist); border-color: var(--sec); }
.pill:active { transform: scale(var(--press-scale)); background: var(--sec-surface); }
.pill[aria-pressed="true"] { background: var(--sec-surface); border-color: var(--sec); box-shadow: inset 0 0 0 1px var(--sec); }

/* Searchable jurisdiction control. Its data source can expand without the
   question changing shape, unlike the old row of state pills. */
.state-picker { max-width: 100%; }
.state-picker__field {
  min-height: 3.25rem; display: flex; align-items: center; gap: 0.75rem; padding: 0 1rem;
  border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  background: var(--surface-raised); color: var(--text-secondary);
  box-shadow: inset 0 1px 0 rgba(var(--paper-rgb),.7);
}
.state-picker__field:focus-within { border-color: var(--surface-accent); box-shadow: 0 0 0 3px var(--focus-halo); }
.state-picker__field input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; font: var(--type-body-sm); color: var(--text-primary); }
.state-picker__field input::placeholder { color: var(--text-tertiary); }
.state-picker__chevron { font: 1rem/1 var(--font-mono); }
.state-picker__options {
  margin-top: 0.5rem; overflow: hidden; border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  background: var(--surface-card); box-shadow: 0 18px 42px -28px rgba(var(--cast-rgb),.38);
}
.state-picker__option {
  width: 100%; min-height: 2.75rem; display: grid; grid-template-columns: 4rem 1fr; align-items: center;
  padding: 0 1rem; border: 0; border-bottom: 1px solid var(--border-hairline); background: transparent;
  color: var(--text-primary); text-align: left; cursor: pointer;
}
.state-picker__option[hidden] { display: none; }
.state-picker__option:last-of-type { border-bottom: 0; }
.state-picker__option:hover, .state-picker__option[aria-selected="true"] { background: var(--sec-surface); }
.state-picker__option span { font: var(--type-mono-sm); color: var(--text-secondary); }
.state-picker__option strong { font: 500 0.875rem/1.3 var(--font-display); }
.state-picker__empty { padding: 1.125rem 1rem; font: var(--type-body-sm); color: var(--text-secondary); }
/* No `!important`. It carried three, and there is nothing in this stylesheet
   for them to beat — a class beats the bare `p` they were written against. */
.state-picker__note { margin: 0.625rem 0 0; font: var(--type-caption); color: var(--text-tertiary); }

/* Shared searchable selector used by every calculator with a growing option
   set. It deliberately inherits the jurisdiction picker's proportions. */
.custom-select { max-width: 100%; }
.custom-select > label { display: block; margin-bottom: 0.5rem; font: var(--type-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--text-tertiary); }
.custom-select__field {
  min-height: 3.25rem; display: flex; align-items: center; gap: 0.75rem; padding: 0 1rem;
  border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  background: var(--surface-raised); box-shadow: inset 0 1px 0 rgba(var(--paper-rgb),.7);
}
.custom-select__field:focus-within { border-color: var(--surface-accent); box-shadow: 0 0 0 3px var(--focus-halo); }
.custom-select__field input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; font: var(--type-body-sm); color: var(--text-primary); }
.custom-select__field input::placeholder { color: var(--text-tertiary); }

/* HATCH's `:focus-visible` ring is a box-shadow, and a text input matches
   `:focus-visible` on EVERY interaction -- the spec says so for text entry,
   which is why this showed on a phone tap and not on a click in a desktop
   check that focused the field programmatically. On these controls the
   wrapper carries the focus, so the ring landed inside it as a second, darker
   box around the text. `border` and `outline` were already reset here; the
   shadow is the one that was missed. */
.custom-select__field input,
.state-picker__field input,
.pricefield input,
.datefield input[type="date"] { box-shadow: none; }

/* These are `type="search"`, and a search field carries a UA appearance that
   nothing here had turned off: on Android Chrome that is a second rounded box
   drawn INSIDE the field, which is the inner border showing up on a phone and
   on no desktop I checked. `appearance: none` removes it, and the two
   pseudo-elements remove the decorations that survive it. */
.custom-select__field input,
.state-picker__field input {
  -webkit-appearance: none; appearance: none;
  border-radius: 0;
}
.custom-select__field input::-webkit-search-decoration,
.custom-select__field input::-webkit-search-cancel-button,
.state-picker__field input::-webkit-search-decoration,
.state-picker__field input::-webkit-search-cancel-button {
  -webkit-appearance: none; appearance: none;
}
.custom-select__options { max-height: 17.5rem; margin-top: 0.5rem; overflow: auto; border: 1px solid var(--border-subtle); border-radius: var(--radius-md); background: var(--surface-card); box-shadow: 0 18px 42px -28px rgba(var(--cast-rgb),.38); }
/* A sticky heading has to be OPAQUE. `--surface-sunken` is `rgba(ink, .04)`,
   so every option scrolled under this one showed straight through it — the
   selected row's lime came up into the heading and the row below it bled into
   the same band, which read as a label with no room around it. It is the same
   fault the receipt's fills had: a translucent layer over something that moves.

   Paper first, then the same wash over it, so it looks identical at rest and
   hides what passes beneath. The hairline is what the next row butts against. */
.custom-select__group-label {
  position: sticky; top: 0; z-index: 1;
  padding: 0.625rem 1rem 0.5rem;
  background-color: var(--surface-card);
  background-image: linear-gradient(var(--surface-sunken), var(--surface-sunken));
  border-bottom: 1px solid var(--border-hairline);
  font: var(--type-label); letter-spacing: var(--track-label);
  text-transform: uppercase; color: var(--text-tertiary);
}
.custom-select__option { width: 100%; min-height: 2.875rem; padding: 0.6875rem 1rem; border: 0; border-bottom: 1px solid var(--border-hairline); background: transparent; color: var(--text-primary); font: 500 0.875rem/1.35 var(--font-display); text-align: left; cursor: pointer; }
.custom-select__option:hover, .custom-select__option[aria-selected="true"] { background: var(--sec-surface); }
.custom-select__option[hidden], .custom-select__group[hidden], .custom-select__empty[hidden] { display: none; }
.custom-select__empty { padding: 1.125rem 1rem; font: var(--type-body-sm); color: var(--text-secondary); }

/* A closed dropdown for short answer sets. It borrows the searchable select's
   field and option skin so the two read as one control with and without a
   search box; only the folding away is new. The list stays IN FLOW rather than
   floating over the card: the accordion panel it sits in is clipped to a
   measured height, so an absolutely positioned list was cut off after two
   options. Opening one re-measures the panel instead. */
.dropdown { max-width: 100%; }
.dropdown__btn { width: 100%; justify-content: space-between; text-align: left; cursor: pointer; font: var(--type-body-sm); color: var(--text-primary); }
.dropdown__value { flex: 1; min-width: 0; }
.dropdown__value:not([data-chosen]) { color: var(--text-tertiary); }
.dropdown__caret { flex: none; color: var(--text-tertiary); transition: transform var(--dur-micro) var(--ease-out); }
.dropdown[data-open="true"] .dropdown__caret { transform: rotate(180deg); }
.dropdown[data-open="true"] .dropdown__btn { border-color: var(--surface-accent); box-shadow: 0 0 0 3px var(--focus-halo); }
.dropdown__list { margin-top: 0.5rem; }

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1rem; }
.optcard {
  display: flex; align-items: center; gap: 0.75rem; width: 100%; text-align: left; padding: 1rem;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  background: var(--surface-sunken); cursor: pointer;
  transition: background var(--dur-micro) var(--ease-out), box-shadow var(--dur-micro), transform var(--dur-micro);
}
.optcard:hover { background: var(--surface-tint-mist); border-color: var(--sec); }
.optcard:active { transform: scale(.99); background: var(--sec-surface); }
.optcard[aria-pressed="true"] { background: var(--sec-surface); border-color: var(--sec); box-shadow: inset 0 0 0 1px var(--sec); }
.optcard__text { flex: 1; }
.optcard__label { display: block; font: 500 0.9375rem/1.3 var(--font-display); color: var(--text-primary); }
.optcard__desc { display: block; margin-top: 3px; font: var(--type-caption); color: var(--text-secondary); }
.optcard__tick {
  width: 1.25rem; height: 1.25rem; border-radius: 50%; background: var(--surface-sunken);
  display: flex; align-items: center; justify-content: center; color: var(--text-tertiary);
  font: 600 0.6875rem/1 var(--font-mono); flex: none;
}
.optcard[aria-pressed="true"] .optcard__tick { background: var(--ter); color: var(--on-ter); }

.pricefield { margin-top: 0.75rem; }
.pricefield__label { font: var(--type-caption); color: var(--text-secondary); }
.pricefield__box {
  display: flex; align-items: center; gap: 0.5rem; margin-top: 0.5rem; height: 3rem; padding: 0 0.875rem;
  border: 1px solid var(--border-hairline); border-radius: var(--radius-sm); background: var(--surface-raised);
}
.pricefield__prefix { font: var(--type-mono-body); color: var(--text-tertiary); }
.pricefield input {
  flex: 1; min-width: 0; border: none; background: transparent;
  font: var(--type-mono-body); font-variant-numeric: tabular-nums; color: var(--text-primary); outline: none;
  -webkit-appearance: none; appearance: none; border-radius: 0;
}
/* The box carries the focus, not the input inside it -- otherwise a ring is
   drawn round the text and the box around it stays inert, which reads as two
   controls. This box had no focus state at all: tabbing into the price landed
   somewhere invisible. */
.pricefield__box:focus-within {
  border-color: var(--surface-accent);
  box-shadow: 0 0 0 3px var(--focus-halo);
}

.pricefield__use {
  flex: none; height: 2.125rem; padding: 0 0.75rem; border: 1px solid var(--border-subtle);
  border-radius: var(--radius-pill); background: var(--surface-card); cursor: pointer; font: 500 0.75rem/1 var(--font-display);
}
.pricefield__use:active { transform: scale(var(--press-scale)); }

/* Calendar answer. Borrows the price field's chrome so the two controls read as
   one family; only the native date UI differs. */
.datefield { margin-top: 0.75rem; }
.datefield .pricefield__box { padding-right: 0.5rem; }
.datefield input[type="date"] {
  flex: 1; min-width: 0; border: none; background: transparent;
  font: var(--type-mono-body); font-variant-numeric: tabular-nums; color: var(--text-primary); outline: none;
  /* A date input is the most heavily UA-styled control there is -- iOS gives
     it an inset rounded box and its own inner padding. */
  -webkit-appearance: none; appearance: none; border-radius: 0;
}
.datefield input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .62; }
.datefield__help { margin: 0.5rem 0 0; font: var(--type-caption); color: var(--text-secondary); }

/* Shared monetary value control. The slider is deliberately bounded for
   quick exploration; the exact field below remains unrestricted. */
.value-control { margin-top: 1.125rem; }
.value-control__top {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1.25rem;
}
.value-control__label { font: 600 0.875rem/1.3 var(--font-display); color: var(--text-primary); }
.value-control__output {
  font: 500 1.375rem/1 var(--font-mono); font-variant-numeric: tabular-nums; color: var(--text-primary);
}
/* One slider. This block used to be a second implementation — 32px tall against
   44px, a 6px track against 10px, and a thumb that was 22px in Chrome and 18px
   in Firefox — so changing the slider changed it on three pages and not the
   other two. It now carries layout only; the look is `.rangex`, once. */
.value-control__range { --range-progress: 0%; margin: 0.5rem 0 0; }
.value-control__help { margin: -1px 0 0.875rem; font: var(--type-caption); color: var(--text-tertiary); }
.value-control > .pricefield__label { margin-top: 2px; }

@media (max-width: 600px) {
  .value-control__top { align-items: center; }
  .value-control__output { font-size: 1.1875rem; }
}

/* --- Full-bleed white bands ---------------------------------------------- */

.band-white {
  background: rgb(var(--paper-rgb));
  box-shadow: 0 0 0 100vmax rgb(var(--paper-rgb));
  clip-path: inset(0 -100vmax);
}

.working { margin-top: var(--section-gap); padding: var(--section-gap) 0; scroll-margin-top: var(--nav-offset); }
.working__head { padding-bottom: 3.5rem; margin-bottom: 3.5rem; border-bottom: 1px solid var(--border-hairline); text-align: center; }
.working__head h2 { margin: 1.25rem auto 0; font: 500 clamp(1.875rem, 4.4vw, 3.5rem)/1.12 var(--font-display); letter-spacing: -0.025em; }
.working__head h2 > span { display: block; white-space: nowrap; }
.working__figure { font-family: var(--font-mono); letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
.working__story { margin: 1.5rem auto 0; font: var(--type-body-lg); line-height: 1.5; color: var(--text-secondary); max-width: 72ch; text-wrap: pretty; }
.working__story strong { color: var(--text-primary); font-weight: 500; }

.split { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 3rem; align-items: stretch; }
.split > * { display: flex; flex-direction: column; min-width: 0; }

.story { display: flex; flex-direction: column; gap: 0.625rem; }
.story__item { display: flex; align-items: flex-start; gap: 0.875rem; padding: 1.125rem; border-radius: var(--radius-md); background: var(--surface-sunken); }
.story__mark {
  width: 1.5rem; height: 1.5rem; border-radius: 50%; background: var(--raw-ink-12); color: var(--text-primary);
  display: flex; align-items: center; justify-content: center; font: 600 0.75rem/1 var(--font-mono);
  flex: none; margin-top: 2px;
}
/* Filled for a rule that applied, hollow for one that did not. Both survive
   greyscale, and neither carries a hue. */
.story__item[data-tone="applied"] .story__mark { background: var(--raw-ink); color: rgb(var(--paper-rgb)); }
.story__item[data-tone="none"] .story__mark { background: transparent; box-shadow: inset 0 0 0 1px var(--raw-ink-40); color: var(--text-tertiary); }
.story__body { flex: 1; min-width: 0; }
/* No figure here any more, so no row to hold one and no baseline to share.
   The label is a heading over its sentence, which is what it always was once
   the number beside it stopped competing with the receipt's copy of it. */
.story__label { display: block; font: 500 0.9375rem/1.3 var(--font-display); }
.story__note { display: block; margin-top: 0.5rem; font: var(--type-caption); color: var(--text-secondary); text-wrap: pretty; }

.verdict { margin-top: 0.875rem; padding: 1.375rem; border-radius: var(--radius-lg); background: var(--raw-vanilla-wash); border: 1px solid var(--raw-vanilla); }
.verdict__top { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.verdict__meta { font: var(--type-mono-sm); color: var(--text-secondary); }
.verdict__line { margin-top: 0.75rem; font: var(--type-body-sm); line-height: 1.55; color: var(--text-primary); text-wrap: pretty; }

.basedon { margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--border-hairline); }
.basedon__chips { display: flex; flex-wrap: wrap; gap: 0.375rem; margin-top: 0.625rem; }
.basedon__chip { padding: 0.375rem 0.75rem; border-radius: var(--radius-pill); background: var(--sec-surface); font: 500 0.8125rem/1.2 var(--font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }

.working__lead { margin: 1.375rem 0 0; font: var(--type-body-sm); line-height: 1.55; color: var(--text-secondary); max-width: 62ch; text-wrap: pretty; }
.formula { margin-top: 1.125rem; padding: 1.125rem; border-radius: 0.25rem; background: var(--surface-raised); border: 1px dashed var(--raw-ink-40); font: var(--type-mono-body); font-variant-numeric: tabular-nums; color: var(--text-primary); }
.sourceline { margin-top: 1.125rem; display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.sourceline__verified { font: var(--type-mono-sm); color: var(--text-tertiary); }
.sourceline a { font: var(--type-caption); color: var(--text-secondary); text-decoration: underline; text-decoration-color: var(--raw-ink-40); }
.sourceline a:hover { text-decoration-color: var(--sec); }

/* --- Ledger -------------------------------------------------------------- */

.ledger { width: 100%; border-collapse: collapse; background: var(--surface-card); border: 1px solid var(--border-hairline); border-radius: 0.25rem; }
.ledger caption {
  text-align: center; padding: 1.25rem; font: var(--type-label); letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--text-secondary); border-bottom: 1px dashed var(--raw-ink-40);
}
.ledger th, .ledger td { font-variant-numeric: tabular-nums; }
.ledger th { text-align: left; font-weight: 400; }
.ledger td { text-align: right; white-space: nowrap; }

.ledger tr[data-kind="line"] > * { border-top: 1px dotted var(--raw-ink-12); padding: 0.75rem 1.25rem; font: var(--type-mono-body); color: var(--text-secondary); }
.ledger tr[data-kind="sub"] > * { border-top: 1px solid var(--raw-ink-40); padding: 0.875rem 1.25rem; font: var(--type-mono-body); font-weight: 500; color: var(--text-primary); }
.ledger tr[data-kind="group"] > * {
  border-top: 1px dashed var(--raw-ink-40); padding: 0.75rem 1.25rem; font: var(--type-label);
  letter-spacing: var(--track-label); text-transform: uppercase; font-weight: 500; color: var(--text-secondary);
}
.ledger tr[data-kind="group"] td { font: var(--type-caption); text-transform: none; letter-spacing: normal; }
.ledger tr[data-kind="total"] > * {
  border-top: 3px double var(--raw-ink); padding: 1.5rem 1.25rem; font: var(--type-label);
  letter-spacing: var(--track-label); text-transform: uppercase; font-weight: 500; color: var(--text-primary);
}
.ledger tr[data-kind="total"] td { font: 500 1.375rem/1.2 var(--font-mono); text-transform: none; letter-spacing: var(--track-mono); }

.ledger__note {
  margin: 0.625rem 0 0; font: var(--type-caption); line-height: 1.45;
  color: var(--text-tertiary); text-wrap: pretty;
}

.actions { margin-top: 4rem; padding-top: 3rem; border-top: 1px solid var(--border-hairline); text-align: center; }
/* No sheet, no button. `navigator.share` is absent on most desktop browsers,
   and a Share that silently copies would be lying about what it does. The
   class is set once at boot by `share.js`; the copy button is always present,
   so nothing is lost by hiding this one. */
.no-native-share [data-act="share"] { display: none; }

.actions__row { display: flex; flex-wrap: wrap; gap: 0.625rem; justify-content: center; }
.actions__note { margin: 0.875rem auto 0; font: var(--type-caption); color: var(--text-tertiary); max-width: 56ch; text-wrap: pretty; }
/* --- The floating calculator offer -----------------------------------------
   Raised by the homepage between its sections, and by a guide article once the
   reader is past the opening. It never fabricates an action for a page that
   has none.

   It lives here rather than in the homepage's stylesheet because two pages
   raise it and only one of them loaded that file — an article got the markup
   and the behaviour with none of the appearance. `.ctabar__go` was already
   here; the rest of it is now beside it. */

.ctabar {
  position: fixed; inset: auto 0 0 0; z-index: var(--z-chrome);
  display: flex; justify-content: center;
  padding: 0 var(--gutter) max(1rem, env(safe-area-inset-bottom));
  transform: translateY(120%); visibility: hidden;
  transition: transform var(--dur-reveal) var(--ease-out), visibility 0s linear var(--dur-reveal);
  pointer-events: none;
}
.ctabar.is-up {
  transform: none; visibility: visible;
  transition: transform var(--dur-reveal) var(--ease-out), visibility 0s;
}
.ctabar__inner {
  /* Sized to its content, not to the page. A full-width bar reads as chrome;
     a pill reads as an offer, and leaves the page visible around it. */
  display: inline-flex; align-items: center; justify-content: flex-start; gap: 1.125rem;
  width: max-content; max-width: min(42.5rem, 100%); margin: 0 auto;
  padding: 0.75rem 0.75rem 0.75rem 1.375rem; border-radius: var(--radius-pill);
  background: var(--surface-glass-strong, rgba(var(--paper-rgb),.86));
  backdrop-filter: blur(22px) saturate(170%);
  -webkit-backdrop-filter: blur(22px) saturate(170%);
  box-shadow: 0 1px 2px rgba(var(--cast-rgb),.05), 0 20px 48px -18px rgba(var(--cast-rgb),.30),
              inset 0 1px 0 rgba(var(--paper-rgb),.7);
  pointer-events: auto;
}
.ctabar__label {
  margin: 0; min-width: 0; flex: 0 1 auto;
  font: 500 0.875rem/1.35 var(--font-display); color: var(--text-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ctabar__go:focus-visible { outline: 3px solid var(--surface-accent); outline-offset: 3px; }

@media (max-width: 720px) {
  .ctabar__inner { padding: 0.625rem 0.625rem 0.625rem 1rem; gap: 0.75rem; }
  .ctabar__label { font-size: 0.7812rem; }
  .ctabar__go { --cta-primary-height: 2.75rem; --cta-primary-padding: 0.9375rem; --cta-primary-type: 500 0.8125rem/1 var(--font-display); }
}

/* --- Primary action ------------------------------------------------------
   Every primary action on the site, defined once. It rests filled in lime with
   ink type and no border; hover and keyboard focus add a soft shadow and a
   small lift, and nothing else moves. There is no second declaration of any of
   this anywhere — a component that needs a smaller one overrides the tokens
   above, it does not restyle the button. */
.btn-vanilla,
.ctabar__go,
.prefill-reset a {
  box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5625rem;
  min-width: var(--cta-primary-min-width);
  height: var(--cta-primary-height); min-height: var(--cta-primary-height);
  padding: 0 var(--cta-primary-padding);
  border: 0; border-radius: var(--radius-pill);
  background: var(--raw-vanilla); color: var(--text-on-accent);
  font: var(--cta-primary-type); text-align: center; text-decoration: none; white-space: nowrap;
  cursor: pointer; box-shadow: none;
  transition: box-shadow var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out);
}
.btn-vanilla:hover, .btn-vanilla:focus-visible,
.ctabar__go:hover, .ctabar__go:focus-visible,
.prefill-reset a:hover, .prefill-reset a:focus-visible {
  /* The fill does not change. The shadow is the whole signal. */
  background: var(--raw-vanilla); color: var(--text-on-accent);
  box-shadow: var(--shadow-lift);
  transform: translateY(-1px);
}
.btn-vanilla:active,
.ctabar__go:active,
.prefill-reset a:active { transform: translateY(0) scale(var(--press-scale)); }

/* The two floating bars are compact by nature. They read the same rule through
   smaller tokens rather than declaring a button of their own. */
.ctabar__go, .prefill-reset a {
  flex: none;
  --cta-primary-height: 2.875rem;
  --cta-primary-min-width: 0;
  --cta-primary-padding: 1.25rem;
  --cta-primary-type: 500 0.875rem/1 var(--font-display);
}

/* --- Pair of reference cards --------------------------------------------- */

/* --- Tiers below the answer ----------------------------------------------
   Seven sections all landing within 300px of each other said nothing about
   which of them matters. Two can change what a reader does — the refund they
   did not know to ask for, and the cliff, which prices the next $25,000 of
   offer. The rest is reference and scope, and reference should read as
   reference.

   So a finding gets the full width and its own ground, the same panel the gold
   card has always had; everything else runs quiet on the page's own ground.
   The pairing was never the problem — the flatness was. */
.tier { padding: var(--section-gap) 0 0; }
/* Air is weight too. A finding gets the full section gap above it; reference
   material gets less, which reinforces the tier and pays back part of the
   height that splitting the pair into two rows cost. */
.tier--quiet, .compare-wrap { padding-top: calc(var(--section-gap) * 0.62); }
/* Every section is a card. Taking the box off the quiet ones did not read as
   hierarchy, it read as an omission — the sequence came out card, no card,
   card, no card, which is alternation, not a tier.

   So the container is ONE primitive and the tier is its ELEVATION. A reference
   card sits flat on the page behind its own hairline; a finding lifts off it
   on a deep shadow with no border at all. Same box, same edge, same padding —
   the difference is whether it is on the page or above it, which is exactly
   what the two tiers mean. */
.refcard--wide { grid-column: 1 / -1; }
.pair { padding: var(--section-gap) 0 0; display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; align-items: start; }
.refcard {
  display: flex; flex-direction: column; min-width: 0;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); background: var(--surface-card);
  /* Flat. It carried the same 48px shadow the gold card does, so "lifted" and
     "on the page" looked identical and neither said anything. */
  box-shadow: 0 1px 2px rgba(var(--cast-rgb),.03);
  /* Less the border, so the CONTENT lands on the shared edge rather than the
     box doing. */
  padding: 2rem calc(var(--sec-inset) - 1px);
}
/* A finding, not a reference card: the same ground and the same lift as the
   gold card, because they are the same KIND of thing — something the page
   found that the reader did not come looking for. Two uses of one treatment
   rather than a second treatment. */
.refcard--find {
  border: 0; background: var(--surface-card);
  box-shadow: 0 1px 2px rgba(var(--cast-rgb),.04), 0 22px 48px -16px rgba(var(--cast-rgb),.22);
  padding: 2.125rem var(--sec-inset);
}
.refcard h3 { margin: 0.875rem 0 1.25rem; font: 500 1.375rem/1.15 var(--font-display); letter-spacing: -0.02em; }
/* A footnote belongs to the content above it, not to the bottom of the box.
   `margin-top: auto` pinned it to the floor of a card stretched to match its
   taller neighbour — 230px of nothing between the five tests and the sentence
   explaining their marks, which is how a note stops looking like a note and
   starts looking like a stray line at the end of a card. It follows its
   content now, and the cards below size to theirs. */
.refcard__foot { margin-top: 1.75rem; padding-top: 1rem; border-top: 1px solid var(--border-hairline); margin-bottom: 0; font: var(--type-caption); color: var(--text-secondary); max-width: 62ch; text-wrap: pretty; }

.scale { width: 100%; border-collapse: collapse; }
.scale th[scope="col"] { text-align: left; padding: 0.625rem 0.75rem; font: var(--type-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--text-tertiary); font-weight: 500; }
.scale th[scope="col"]:not(:first-child) { text-align: right; }
.scale tbody tr[data-row] > * { border-top: 1px solid var(--border-hairline); padding: 0.75rem; font: var(--type-mono-body); font-variant-numeric: tabular-nums; }
.scale th[scope="row"] { text-align: left; color: var(--text-primary); font-weight: 400; }
.scale td { text-align: right; }
.scale td:first-of-type { color: var(--text-secondary); }
.scale tr[data-here="true"] > * { background: var(--sec-surface); font-weight: 600; }

.cliff__lead { margin: 0; font: var(--type-body-sm); line-height: 1.55; color: var(--text-secondary); max-width: 62ch; text-wrap: pretty; }
.cliff__strip { position: relative; height: 7.375rem; margin: 2rem 3.75rem; }
/* The three regimes, as a band rather than a hairline. Each zone is a slab of
   its own accent at the lightest tier, the same treatment and the same family
   the duty ruler uses — and the progression is not arbitrary here: the
   concession is whole, then partial, then gone, so the colour walks the same
   way. Lime stays off it entirely; that belongs to the reader's own price. */
.cliff__zones { position: absolute; left: 0; right: 0; top: 3.25rem; height: 0.75rem; }
.cliff__zone {
  position: absolute; top: 0; height: 100%;
  box-shadow: inset -1px 0 0 rgba(var(--paper-rgb), .9);
  transition: filter var(--dur-micro) var(--ease-out);
}
.cliff__zone:first-child { border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
.cliff__zone:last-child { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; box-shadow: none; }
/* The `press` tier, not `fill`. These zones are twelve pixels tall against a
   white card, where the band ruler's are forty and sit shoulder to shoulder —
   at .22 the middle one read as a gap between the other two rather than as the
   widest of the three. HATCH's own alphas, one step up. */
.cliff__zone--1 { background: rgba(var(--sprout-hue), var(--a-press)); }
.cliff__zone--2 { background: rgba(var(--sand-hue), var(--a-press)); }
.cliff__zone--3 { background: rgba(var(--rose-hue), var(--a-press)); }
.cliff__zone:hover { filter: brightness(.94); }
.cliff__zone:focus-visible { outline-offset: -2px; }

/* Your span, under the band rather than over it. Laid on top it was a
   translucent wash across three colours and muddied all of them. */
.cliff__shade {
  position: absolute; top: 4.375rem; height: 3px; border-radius: 2px;
  background: rgba(var(--ink-rgb), .28);
  transition: left var(--dur-base) var(--ease-out), width var(--dur-base) var(--ease-out);
}
.cliff__mark { position: absolute; top: 0; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; gap: 0.3125rem; }
.cliff__mark span:nth-child(1) { font: var(--type-caption); color: var(--text-tertiary); white-space: nowrap; }
.cliff__mark span:nth-child(2) { font: var(--type-mono-sm); font-variant-numeric: tabular-nums; color: var(--text-secondary); white-space: nowrap; }
.cliff__tick { width: 1px; height: 1rem; background: var(--raw-ink-40); }
.cliff__dot { position: absolute; top: 2.9375rem; transform: translateX(-50%); transition: left var(--dur-base) var(--ease-out); }
.cliff__dot span { display: block; width: 1.125rem; height: 1.125rem; border-radius: 50%; background: var(--sec-surface); border: 2px solid var(--sec); box-shadow: 0 2px 6px rgba(var(--cast-rgb),.18); }
.cliff__you { display: flex; align-items: baseline; gap: 0.5rem; margin: -1rem 0 1.5rem; }
.cliff__you span:first-child { font: var(--type-caption); color: var(--text-tertiary); }
.cliff__you span:last-child { font: 500 0.8125rem/1 var(--font-mono); font-variant-numeric: tabular-nums; }
.cliff__stats { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }
.stat { padding: 1.125rem; border: 1px solid var(--border-subtle); border-radius: var(--radius-md); }
.stat--risk { border-color: var(--sec); background: var(--surface-card); }
.stat__label { font: var(--type-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--text-tertiary); text-wrap: pretty; }
.stat__value { margin-top: 0.5rem; font: 500 1.375rem/1.1 var(--font-mono); font-variant-numeric: tabular-nums; }

/* --- Sage compare panel --------------------------------------------------- */

.compare-wrap { padding: var(--section-gap) 0 0; }
/* No slab. This was the only coloured ground on the page and the loudest thing
   on it, for a section that is context rather than a finding — it out-shouted
   both of the things a reader can act on. It keeps its own space and nothing
   else. */
/* Reference, so the same flat card as the band and the eligibility pair. It
   was a lime slab, which made the loudest thing on the page a section nobody
   can act on; it is not a naked block either, which made it the second thing
   that looked like a rendering fault. */
.compare-panel {
  padding: 2rem calc(var(--sec-inset) - 1px);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-lg);
  background: var(--surface-card); box-shadow: 0 1px 2px rgba(var(--cast-rgb),.03);
}
.compare-grid { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 3rem; align-items: start; }
.compare-grid > * { display: flex; flex-direction: column; min-width: 0; }
.compare__eyebrow { font: var(--type-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--text-secondary); }
.compare h2 { margin: 0.875rem 0 0; font: 500 1.75rem/1.16 var(--font-display); letter-spacing: -0.02em; text-wrap: pretty; }
.compare p { margin: 1rem 0 0; font: var(--type-body-sm); line-height: 1.55; color: rgba(var(--ink-rgb),.72); max-width: 62ch; text-wrap: pretty; }
/* Same fault as `.refcard__foot`: pinned to the floor of a column stretched to
   match the table beside it, 197px below the paragraph it belongs to. */
.compare__foot { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(var(--ink-rgb),.14); display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.compare__foot span:first-child { font: var(--type-mono-sm); color: var(--text-secondary); }
.compare__foot span:last-child { font: var(--type-caption); color: rgba(var(--ink-rgb),.7); }
.compare__table { border-radius: var(--radius-lg); overflow: hidden; background: rgba(var(--paper-rgb),.72); }
.compare__thead { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; padding: 0.75rem 1.125rem; font: var(--type-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--text-secondary); }
/* A row that reports rather than selects. Three of the comparison tables list
   figures nobody can pick between — the employer side, the passports — and
   said so with `style="cursor:default"` in the markup. `disabled` already says
   it; the cursor is a style and belongs here. */
.compare__row[disabled] { cursor: default; }
.compare__row {
  display: block; width: 100%; text-align: left; padding: 1rem 1.125rem; border: none;
  border-top: 1px solid rgba(var(--ink-rgb),.1); cursor: pointer; background: transparent; font-variant-numeric: tabular-nums;
  transition: background var(--dur-micro) var(--ease-out);
}
.compare__row[aria-pressed="true"] { background: var(--sec-surface); }
.compare__row:hover { background: rgba(var(--paper-rgb),.5); }
.compare__row[aria-pressed="true"]:hover { background: var(--sec-surface); }
.compare__top { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; font: 500 1rem/1.2 var(--font-mono); color: var(--text-primary); }
.compare__row[aria-pressed="true"] .compare__top { font-weight: 600; }
.compare__break { display: block; margin-top: 0.5rem; font: var(--type-mono-sm); color: var(--text-secondary); }
.compare__note { margin: 1rem 0 0; font: var(--type-caption); color: rgba(var(--ink-rgb),.66); }

/* --- Tests / excluded ----------------------------------------------------- */

.qualify { margin-top: var(--section-gap); padding: var(--section-gap) 0; display: grid; grid-template-columns: 1fr 1fr; gap: 3rem; align-items: start; }
.tests { display: flex; flex-direction: column; }
.test { display: flex; gap: 1rem; padding: 1.375rem 0; border-top: 1px solid var(--border-hairline); }
.test__glyph { font: var(--type-mono-body); color: var(--text-secondary); width: 1rem; flex: none; }
.test__title { font: var(--type-body-sm); text-wrap: pretty; }
.test__note { margin-top: 0.4375rem; font: var(--type-caption); color: var(--text-tertiary); text-wrap: pretty; }

.excluded { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; align-items: stretch; }
.excluded__card { display: flex; flex-direction: column; padding: 1.375rem; border-radius: var(--radius-md); background: var(--surface-sunken); box-shadow: inset 0 1px 0 rgba(var(--paper-rgb),.7); }
.excluded__label { font: var(--type-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--text-tertiary); text-wrap: pretty; }
.excluded__note { margin-top: 0.5rem; font: var(--type-caption); color: var(--text-secondary); text-wrap: pretty; }

/* --- Placeholder ---------------------------------------------------------- */

.pending { max-width: 57.5rem; margin-left: auto; margin-right: auto; padding: 1.5rem 0 0; }
.pending__receipt {
  position: relative; padding: 1.75rem 2rem 1.5rem; border: 1px solid var(--border-subtle); border-radius: 0.25rem;
  background: rgba(var(--paper-rgb),.72); box-shadow: 0 22px 52px -38px rgba(var(--cast-rgb),.34);
}
.pending__receipt::before, .pending__receipt::after {
  content: ''; position: absolute; left: -0.3125rem; right: -0.3125rem; height: 0.625rem;
  background: radial-gradient(circle at 5px 5px, transparent 4px, rgba(var(--paper-rgb),.72) 4.5px) 0 0 / 10px 10px repeat-x;
}
.pending__receipt::before { top: -0.3125rem; }
.pending__receipt::after { bottom: -0.3125rem; transform: rotate(180deg); }
.pending__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1.5rem; }
.pending__head p { margin: 0.4375rem 0 0; font: var(--type-caption); color: var(--text-secondary); }
.pending__step { font: var(--type-mono-sm); color: var(--text-tertiary); }
.pending__ledger { margin: 1.25rem 0 0; }
.pending__ledger > div { display: grid; grid-template-columns: 1fr auto; gap: 1.25rem; padding: 0.75rem 0; border-bottom: 1px dotted var(--border-hairline); }
.pending__ledger dt, .pending__ledger dd { margin: 0; font: var(--type-mono-sm); }
.pending__ledger dd { color: var(--text-tertiary); }
.pending__ledger .pending__total { border-top: 1px solid var(--text-primary); border-bottom: 0; font-weight: 600; }
.pending__remaining { margin-top: 1rem; font: var(--type-mono-sm); color: var(--text-tertiary); }

/* --- FAQ ------------------------------------------------------------------
   One centred column, on every page this section appears on. It was two
   columns — the heading and its copy pinned left, the questions in a column
   beside them — which is the shape the rest of the site uses for a section
   that INTRODUCES something on its right. This introduces nothing; the
   questions are the section. Centred, the head reads as a title over its own
   list, which is the same rule the guides and answers roots already follow.

   The questions themselves stay left-aligned inside their rows. A question
   with a `+` at the end of it has to start at a fixed left edge, or the sign
   floats away from a ragged line and the rows stop scanning. */
.faq { padding: var(--section-gap) 0 0; }
.faq__intro { max-width: 40rem; margin-inline: auto; text-align: center; }
.faq h2 { margin: 0.875rem 0 0; font: 500 1.375rem/1.15 var(--font-display); letter-spacing: -0.02em; }
.faq__intro p { margin: 0.875rem 0 0; font: var(--type-body-sm); color: var(--text-secondary); }
/* No rule above it now. A hairline the width of centred text draws a box
   around nothing; the date simply follows the copy, quieter than it. */
.faq__stamp { margin-top: 1.125rem; font: var(--type-mono-sm); color: var(--text-tertiary); }
.faq__list { max-width: 46rem; margin: 2.75rem auto 0; border-bottom: 1px solid var(--border-hairline); }
.faq__item { border-top: 1px solid var(--border-hairline); }
.faq__q {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
  padding: 1.25rem 2px; background: transparent; border: none; cursor: pointer; text-align: left;
  font: var(--type-body-sm); color: var(--text-primary);
}
.faq__sign { font: 400 1.125rem/1 var(--font-mono); color: var(--text-tertiary); }
.faq__a { overflow: hidden; max-height: 0; }
.is-animated .faq__a { transition: max-height var(--dur-base) var(--ease-inout); }
.faq__a p { margin: 0 0 1.5rem; padding-right: 4rem; font: var(--type-body-sm); color: var(--text-secondary); text-wrap: pretty; }

.more { border-top: 1px solid var(--border-hairline); padding: 0 1.5rem; }

.result { border-top: 1px solid var(--border-hairline); padding: 1.375rem 1.5rem 1.625rem; background: var(--surface-raised); }

.wid { display: flex; flex-direction: column; gap: 1.125rem; margin: 0.25rem 0 1.375rem; }
.wid__row { display: flex; flex-direction: column; gap: 0.5rem; }
.wid__label { font: 500 0.8125rem/1.2 var(--font-display); }
.wid__label span { display: block; margin-top: 2px; font: var(--type-caption); font-weight: 400; color: var(--text-tertiary); }
.wid__bar { display: flex; height: 2.125rem; border-radius: var(--radius-xs); overflow: visible; background: var(--surface-sunken); }
.wid__base { background: var(--surface-sunken); box-shadow: inset 0 0 0 1px var(--border-hairline); }
.wid__hot { background: var(--raw-vanilla); box-shadow: inset 0 0 0 1px var(--raw-vanilla-deep); }
.wid__note { font: var(--type-mono-sm); font-variant-numeric: tabular-nums; color: var(--text-tertiary); }

/* Each block explains itself — the shading alone does not say which is which.
   The explaining is `tips.js`'s job now, the same as every other chart on the
   site; all that is left here is the shape. */
.wid__seg { position: relative; }
.wid__seg:first-child { border-radius: var(--radius-xs) 0 0 var(--radius-xs); }
.wid__seg:last-child { border-radius: 0 var(--radius-xs) var(--radius-xs) 0; }
.wid__seg:only-child { border-radius: var(--radius-xs); }

.wid__legend { display: flex; flex-wrap: wrap; align-items: center; gap: 0.375rem 1rem; font: var(--type-caption); color: var(--text-secondary); }
.wid__legend span { display: inline-flex; align-items: center; gap: 0.375rem; }
.wid__key { width: 0.75rem; height: 0.75rem; border-radius: 3px; flex: none; }
.wid__hint { color: var(--text-tertiary); }

.closing { margin-top: 1.5rem; padding-top: 1.125rem; border-top: 1px solid var(--border-hairline); }

.entitle { margin-top: 1rem; padding: 1.125rem; border-radius: var(--radius-md); background: var(--surface-sunken); }
.entitle p { margin: 0.5rem 0 0; font: var(--type-body-sm); line-height: 1.5; color: var(--text-primary); text-wrap: pretty; }

.gap { margin-top: 1rem; padding: 1.125rem; border: 1px dashed var(--border-strong); border-radius: var(--radius-md); }
.gap p { margin: 0.5rem 0 0; font: var(--type-body-sm); line-height: 1.5; color: var(--text-secondary); text-wrap: pretty; }
.gap ul { margin: 0.625rem 0 0; padding-left: 1.125rem; font: var(--type-mono-sm); color: var(--text-primary); }

@media (max-width: 900px) {
}

/* --- Not-built routes ------------------------------------------------------
   Directional, not apologetic: the page says what it will show and points at
   the tool that already works (DESIGN-PRD §15). */

.soon { max-width: 62ch; padding-top: 1.5rem; }
.soon h1 { margin: 0; font: 500 2.75rem/1.08 var(--font-display); letter-spacing: -0.025em; text-wrap: pretty; }

.soon + .tools { padding-top: 6rem; }

@media (max-width: 640px) {
  .soon h1 { font-size: 1.875rem; }
}

/* --- Tool footer row ------------------------------------------------------ */

.tools { padding: var(--section-gap) 0 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; align-items: stretch; }
.tool {
  display: flex; align-items: center; gap: 1rem; padding: 1.375rem; text-decoration: none;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); background: var(--surface-card);
  box-shadow: 0 1px 2px rgba(var(--cast-rgb),.04), 0 22px 48px -16px rgba(var(--cast-rgb),.20);
  transition: transform var(--dur-micro) var(--ease-out);
}
.tool:hover { transform: translateY(-2px); }
.tool__text { flex: 1; }
.tool__title { display: block; font: var(--type-body-sm); }
.tool__desc { display: block; font: var(--type-caption); color: var(--text-tertiary); margin-top: 2px; }

/* --- Footer --------------------------------------------------------------- */

/* Above the pinned stack, so the footer RISES OVER the last card rather than
   appearing beside it. The five calculators pin one after another and the
   fifth stays put; the footer is the thing that finally covers it, which is
   what ends the page instead of the stack simply running out.

   `position: relative` only to give z-index something to apply to -- the
   footer stays in normal flow and nothing about its layout changes. */
/* The footer keeps its section spacing on the homepage like everywhere else.
   It carried `margin-top: 0` while the calculators were a pinned stack -- the
   gap was empty page between a card being deliberately held still and the
   thing meant to cover it. There is no stack and no footer reveal now, so the
   exception has nothing left to justify it, and without it the last card sat
   hard against the footer with none of the section rhythm the rest of the page
   keeps. */

.footer { position: relative; z-index: 5; margin-top: var(--section-gap); background: var(--raw-ink); color: rgb(var(--on-ink-rgb)); overflow: hidden; }
.footer__grid { max-width: var(--shell); margin: 0 auto; padding: 5rem var(--gutter) 0; display: grid; grid-template-columns: 1.5fr 1fr 1fr 1fr; gap: 2rem; }
.footer__notice { margin: 0.875rem 0 0; font: var(--type-caption); color: rgba(var(--on-ink-rgb),.62); max-width: 52ch; text-wrap: pretty; }
.footer__col { display: flex; flex-direction: column; gap: 0.5rem; font: var(--type-caption); color: rgba(var(--on-ink-rgb),.62); }
.footer__col--mono { font: var(--type-mono-sm); color: rgba(var(--on-ink-rgb),.62); }
.footer__coltitle { font: var(--type-label); letter-spacing: var(--track-label); text-transform: uppercase; color: rgba(var(--on-ink-rgb),.72); font-family: var(--font-display); }
.footer a { color: inherit; text-decoration-color: rgba(var(--on-ink-rgb),.3); }
/* Brighten toward the footer's OWN text colour, not toward paper. The footer
   links sit at 62% of `--on-ink-rgb`, so full strength is the brighter state.

   It was `--paper-rgb`, which is white in day and reads as a lift — and at
   night is #221F26, the raised surface, which is the colour of the footer
   itself. Hovering a link turned it into the background. */
.footer a:hover { color: rgb(var(--on-ink-rgb)); text-decoration-color: currentColor; }
.footer .wordmark { text-decoration: none; }
/* The name, set edge to edge and sunk into the floor of the page: the top of
   the word shows and the rest is below the fold of the footer.

   It sat inside the shell with a gutter, at a size that left it floating in
   the middle of a band -- a large word rather than a mark. Full bleed and
   cropped, it reads as the page ending on the name instead.

   The crop is the parent's job. `.footer__mark` is a window `--wordmark-show`
   tall; the word is laid out at its natural height inside and the remainder
   falls out the bottom. Doing it that way rather than with a fixed height on
   the text itself keeps the glyphs' own metrics intact, so the descender of
   the g is cut by the window rather than by a line box that no longer matches
   the letters. */
/* The copyright notice. Quiet, in the same mono the ruleset column uses, and
   given room from the cropped wordmark below it so the two do not read as one
   block. Wraps to two lines on a narrow screen rather than shrinking. */
.footer__legal {
  /* SAME SHELL AND GUTTER AS `.footer__grid`, which is the whole point and was
     missing: without them this sat full-bleed, its rule running edge to edge
     and its text starting at the window's left while every column above it
     began one gutter in. It read as a bar bolted under the footer rather than
     as the last line of it.

     And left-aligned, not `space-between`. Pushing the two halves to opposite
     ends of a 1200px shell stretches one sentence across the whole page; they
     belong together, as one statement, where the reading starts. */
  max-width: var(--shell); margin: 3rem auto 0;
  padding: 1.25rem var(--gutter) 0;
  display: flex; flex-wrap: wrap; gap: 0.375rem 1.25rem;
  align-items: baseline;
  border-top: 1px solid rgba(var(--on-ink-rgb), .12);
  font: 400 0.6875rem/1.5 var(--font-mono);
  /* .62 not .55: at 11px this was the dimmest text in the footer at 5.39:1,
     and a copyright notice being the least legible thing on the page is the
     wrong order. Still quiet, now level with the disclaimer beside it. */
  color: rgba(var(--on-ink-rgb), .62);
}

.footer__mark {
  /* Full bleed is a division, not a guess. Set at this tracking the word
     measures 3.397em wide, so the size that makes it exactly one viewport
     across is that width over it. Measured with a Range around the text -- the
     element's own `scrollWidth` reports the block, not the letters, which is
     how the first attempt left 56px of air at each end and still looked right
     to a width check.

     `cqw` and not `vw`: on any machine with classic scrollbars `100vw` is the
     viewport INCLUDING the scrollbar, so the word would be set a dozen pixels
     too wide and lose a slice off each end. The container query unit is this
     box, which is the thing the word is supposed to match. */
  container-type: inline-size;
  --wordmark-advance: 3.397;
  --wordmark-size: calc(100cqw / var(--wordmark-advance));
  /* Half the WORD, not half the line box. Those are different things, and
     cropping the second showed 24% when it claimed 40%.

     The line box is 0.92em tall and so is the word -- cap top to the foot of
     the g -- but they do not start in the same place: negative leading puts
     the box top 0.151em above the caps. So the window is that dead space plus
     the share of the letters that should show. */
  --wordmark-lead: 0.151em;
  --wordmark-span: 0.92em;
  --wordmark-show: calc(var(--wordmark-lead) + (var(--wordmark-span) * 0.5));
  width: 100%; max-width: none; margin: 0; padding: 0;
  /* `em` on this element would resolve against the footer's font size, so the
     window is set in the same unit the word is. */
  font-size: var(--wordmark-size);
  height: var(--wordmark-show);
  overflow: hidden;
}
.footer__wordmark {
  margin: 0;
  font: 500 1em/0.92 var(--font-display);
  letter-spacing: -0.055em; text-align: center; white-space: nowrap;
  color: rgba(var(--on-ink-rgb),.10); user-select: none;
}

.prefill-reset { position:fixed; z-index:calc(var(--z-chrome) + 2); left:50%; bottom:1.375rem; display:flex; align-items:center; gap:1.125rem; width:max-content; max-width:calc(100vw - (2 * var(--gutter))); padding:0.5rem 0.5rem 0.5rem 1.125rem; border:1px solid var(--border-glass); border-radius:var(--radius-pill); background:rgba(var(--on-ink-rgb),.9); box-shadow:0 22px 60px rgba(var(--cast-rgb),.18),inset 0 1px 0 rgba(var(--paper-rgb),.85); backdrop-filter:blur(18px); transform:translateX(-50%); }
.prefill-reset > span { color:var(--text-secondary); font:500 0.6875rem/1 var(--font-mono); }
.prefill-reset__close {
  flex:none; width:2rem; height:2rem; margin-left:2px; padding:0;
  border:0; border-radius:50%; background:transparent; color:var(--text-secondary);
  font:400 1.25rem/1 var(--font-display); cursor:pointer;
  transition:background var(--dur-micro) var(--ease-out), color var(--dur-micro) var(--ease-out);
}
.prefill-reset__close:hover { background:var(--surface-sunken); color:var(--text-primary); }
/* While the bar is up it floats over the page. Reserve the room it occupies so
   the end of the page can always be scrolled clear of it. */
body.has-prefill-reset .page { padding-bottom:6.5rem; }

/* --- Responsive ----------------------------------------------------------- */

@media (max-width: 900px) {
  :root { --section-gap: 6rem; --gutter: 1.25rem; --sec-inset: 1.5rem; }
  .nav__pill { max-width: none; }
  .main { padding-top: 3.5rem; }
  .main--calculator {
    width: 100%;
    margin-bottom: 4.5rem;
    padding: 4rem clamp(1.25rem, 4vw, 2.5rem) 4.5rem;
    border-right: 0;
    border-left: 0;
    border-radius: 0;
  }
  .hero { grid-template-columns: 1fr; gap: 1.25rem; }
  .hero h1 { font-size: 2.125rem; }
  .hero--calculator h1 { font-size: clamp(2.25rem, 7vw, 3.25rem); }
  .split, .pair, .compare-grid { grid-template-columns: 1fr; gap: 1.5rem; }
  /* Card grids keep the doubled gap when they stack. */
  .qualify, .tools { grid-template-columns: 1fr; gap: 3rem; }
  .working__head h2 > span { white-space: normal; }
  .compare-panel { padding: 2rem calc(var(--sec-inset) - 1px); }
  .footer__grid { grid-template-columns: 1fr 1fr; }
  .cliff__strip { margin: 2rem 2.5rem; }

  /* Tool links move into the sheet rather than disappearing. */
  .nav__links { display: none; }
  .nav__burger { display: flex; }
}

/* --- Mobile, from Stamp Duty - Mobile.dc.html -----------------------------
   The phone layout is its own design, not a squeezed desktop: the nav becomes
   a full-width glass bar rather than a floating pill, the ground goes to the
   sunken tint so the white cards carry the page, the rhythm drops to 72px, and
   every primary action goes full width. */
@media (max-width: 640px) {
  :root { --section-gap: 4.5rem; --gutter: 1rem; --sec-inset: 1.125rem; }
  body { background: var(--surface-sunken); background-image: none; }

  .nav__pill { min-height: 3.25rem; }
  .main { padding: 2rem 1rem 1.5rem; }
  .main--calculator {
    width: 100%;
    margin-bottom: 3.5rem;
    padding: 3rem 1rem 4rem;
    border-right: 0;
    border-left: 0;
    border-radius: 0;
  }

  .hero h1 { font: 500 1.625rem/1.16 var(--font-display); letter-spacing: -0.02em; }
  .hero--calculator .hero__utility { gap: 0.625rem; margin-bottom: 1.125rem; }
  .hero--calculator .hero__body { margin-top: 0.875rem; font-size: 0.9375rem; }
  .inputs { margin-top: 1.75rem; }
  .q[data-open="true"] .q__label { font-size: 1.25rem; }
  /* The question and its answer stop competing for one line. `.q__meta` was
     `flex: none`, so the answer chip and Edit never gave ground and the question
     was squeezed into a four-word column — "When did your Medicare-free period
     start this financial year?" set six lines deep with the chip sitting across
     it. Stacked, each gets the full width: the question on its own line, the
     answer under it, indented to the label and with Edit still on the right
     where it is in every other row. */
  .q__trigger { flex-direction: column; align-items: stretch; gap: 0.4375rem; }
  .q__meta { justify-content: space-between; padding-left: 2.125rem; }
  .q[data-open="true"] .q__meta { padding-left: 2.5rem; }
  .q__value { white-space: normal; }
  .pending__receipt { padding: 1.5rem 1.125rem 1.25rem; }
  .working__head h2 { font-size: 1.625rem; line-height: 1.16; }
  .refcard h3, .compare h2, .faq h2 { font: 500 1.375rem/1.18 var(--font-display); letter-spacing: -0.02em; }
  .refcard { padding: 1.25rem calc(var(--sec-inset) - 1px); border-radius: var(--radius-lg); }
  .inputs__head { padding: 1rem 1rem 0.375rem; }
  .qs { padding: 0.875rem 1rem 1rem; }

  /* Primary actions go full width and stack. */
  /* No sheet, no button. `navigator.share` is absent on most desktop browsers,
   and a Share that silently copies would be lying about what it does. The
   class is set once at boot by `share.js`; the copy button is always present,
   so nothing is lost by hiding this one. */
.no-native-share [data-act="share"] { display: none; }

.actions__row { flex-direction: column; gap: 0.5rem; }
  .actions__row > * { width: 100%; }
  /* Size through the token, so the button is still declared in one place. */
  .btn-vanilla { --cta-primary-height: 3rem; }
  .btn-quiet--lg { height: 3rem; }
  .pills { gap: 0.5rem; }
  .pill { flex: 1 1 auto; min-width: 0; }

  .compare-panel { padding: 1.25rem calc(var(--sec-inset) - 1px); border-radius: var(--radius-lg); }
  .working { padding: 4.5rem 0; }
  .qualify { margin-top: 4.5rem; padding: 4.5rem 0; }
  .scale th[scope="col"] { padding: 0.625rem 0.25rem; }
  .scale tbody tr > * { padding: 0.625rem 0.25rem; }
}

@media (max-width: 640px) {
  .inputs { border-radius: var(--radius-lg); }
  /* Named with the gold card's own selector rather than shouted over it. The
     `!important` here existed to beat `.gold__extra .ledger tr > *`, which is
     one class more specific; matching the specificity says the same thing and
     leaves the cascade readable. */
  .ledger tr > *, .gold__extra .ledger tr > * { padding-left: 0.75rem; padding-right: 0.75rem; font-size: 0.75rem; }
  .ledger tr[data-kind="total"] td { font-size: 1.0625rem; }
  .cliff__stats, .excluded { grid-template-columns: 1fr; }
  .footer__grid { grid-template-columns: 1fr; }
  .cliff__strip { margin: 2rem 1.75rem; }

  /* The nav keeps the wordmark and the year picker. The version stamp moves to
     the footer, and "Run my numbers" goes because the input card is already the
     next thing on screen. */
  .version { display: none; }
  .nav__right .btn-accent { display: none; }
  .nav__right { gap: 0.5rem; }
  .fy button { padding: 0.375rem 0.625rem; }
  .status { margin-bottom: 0.875rem; }
  .prefill-reset { bottom:0.75rem; width:calc(100vw - 1.5rem); padding:0.375rem; }
  .prefill-reset > span { display:none; }
  .prefill-reset a { width:100%; min-height:3rem; }
}

/* The ledger keeps its two columns to 320px — the alignment is the point, so it
   tightens rather than stacking (design PRD §5.1). Figures carry no spaces, so
   letting values wrap only breaks ranges and text, never a number. */
@media (max-width: 400px) {
  :root { --gutter: 0.875rem; }
  .ledger tr > *, .gold__extra .ledger tr > * { padding-left: 0.5rem; padding-right: 0.5rem; font-size: 0.6875rem; }
  .ledger td { white-space: normal; }
  .ledger caption { padding: 0.875rem 0.5rem; font-size: 0.6875rem; letter-spacing: 0.06em; }
  .ledger tr[data-kind="total"] td { font-size: 0.9375rem; }
  .formula { font-size: 0.75rem; word-break: break-word; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
}

/* --- Print ---------------------------------------------------------------
   A courtesy print of the page itself. The document a buyer actually sends to
   a conveyancer is statement.html, not this. */
@media print {
  :root { --section-gap: 1.5rem; }
  body { background: rgb(var(--paper-rgb)); background-image: none; }
  .nav, .tools, .footer, .inputs .qs, .actions, .faq, .pair, .compare-wrap, .qualify { display: none !important; }
  .band-white { box-shadow: none; clip-path: none; }
  .working { margin-top: 0; padding: 0; }
  .split { grid-template-columns: 1fr; gap: 1.5rem; }
  .working__head h2 > span { white-space: normal; }
  .inputs { box-shadow: none; border-radius: 0; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font: var(--type-mono-sm); }
}

/* =========================================================================
   RANGE — one slider for the whole build

   The old one never set `appearance: none` on the input itself, so it ran a
   mixed native/custom model: `accent-color` painted the native track while the
   pseudo-element rules painted another, and the thumb's `margin-top` was
   measured against the wrong box — which is why it did not sit on the bar.

   Track and thumb are both fully defined here, and the filled portion is a
   gradient driven by `--fill` so it reads the same in every engine rather than
   relying on `::-moz-range-progress`, which only Firefox has.
========================================================================= */

.rangex {
  -webkit-appearance: none; appearance: none;
  display: block; width: 100%; height: 2.75rem;
  margin: 0; padding: 0; background: transparent; cursor: grab;
}
.rangex:active { cursor: grabbing; }
.rangex:focus { outline: none; }

/* --- track --------------------------------------------------------------- */
.rangex::-webkit-slider-runnable-track {
  height: 0.625rem; border-radius: var(--radius-pill);
  background: linear-gradient(to right,
    var(--surface-accent) 0 var(--fill, var(--range-progress, 50%)),
    var(--surface-sunken) var(--fill, var(--range-progress, 50%)) 100%);
}
.rangex::-moz-range-track { height: 0.625rem; border-radius: var(--radius-pill); background: var(--surface-sunken); }
.rangex::-moz-range-progress { height: 0.625rem; border-radius: var(--radius-pill); background: var(--surface-accent); }

/* --- thumb ---------------------------------------------------------------
   Centred arithmetically: (track 10 − thumb 26) / 2 = −8. */
.rangex::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 1.625rem; height: 1.625rem; margin-top: -0.5rem;
  border: 4px solid var(--surface-card); border-radius: 50%;
  background: var(--raw-ink);
  box-shadow: 0 1px 3px rgba(var(--cast-rgb),.28), 0 4px 12px rgba(var(--cast-rgb),.18);
  transition: box-shadow var(--dur-micro) var(--ease-out), transform var(--dur-micro) var(--ease-out);
}
.rangex::-moz-range-thumb {
  width: 1.625rem; height: 1.625rem; box-sizing: border-box;
  border: 4px solid var(--surface-card); border-radius: 50%;
  background: var(--raw-ink);
  box-shadow: 0 1px 3px rgba(var(--cast-rgb),.28), 0 4px 12px rgba(var(--cast-rgb),.18);
}

.rangex:hover::-webkit-slider-thumb { box-shadow: 0 0 0 7px var(--focus-halo), 0 1px 3px rgba(var(--cast-rgb),.28); }
.rangex:active::-webkit-slider-thumb { transform: scale(1.06); box-shadow: 0 0 0 9px var(--surface-accent), 0 1px 3px rgba(var(--cast-rgb),.3); }
.rangex:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 7px var(--surface-accent), 0 1px 3px rgba(var(--cast-rgb),.3); }
.rangex:hover::-moz-range-thumb { box-shadow: 0 0 0 7px var(--focus-halo); }
.rangex:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 7px var(--surface-accent); }

@media (prefers-reduced-motion: reduce) {
  .rangex::-webkit-slider-thumb { transition: none; }
  .rangex:active::-webkit-slider-thumb { transform: none; }
}

/* =========================================================================
   WITHHELD MARKER

   A distinct highlighter yellow, deliberately not the acid accent: the accent
   marks quantity and interaction, this marks a figure we will not state. Two
   acid fills stacked in one card were competing.

   This is not semantic colour on a verdict — it says nothing about whether the
   reader's result is good or bad, only that a rule behind it is unresolved.
========================================================================= */

:root {
  /* The live-status dot and the accent's deeper stop: named because they are
     brand decisions, not incidental values. */
  --status-live:        #A8CC00;
  --surface-accent-deep: #F5F5BD;

  /* --- Leading, overridden ------------------------------------------------
     The system sets `--type-caption` at 0.75rem/1.35. That is a HEADING's
     leading on the size this site uses for almost all of its explanatory
     prose — the note under every step, every chart key, every card's footnote,
     the figure notes on the guides index: fifty uses, nearly all of them
     running text. Small type needs MORE room between lines than large type,
     not less, and 1.35 on 12px is about a third of a line short of readable.

     Raised here rather than in the design system, which is vendored and never
     edited, and which is right to ship a tighter default for the label work
     these tokens also do. The site reads more than it labels. */
  --type-caption:   400 0.75rem/1.6 var(--font-sans);
  --type-body-sm:   400 0.9375rem/1.62 var(--font-sans);
  --type-mono-sm:   400 0.75rem/1.55 var(--font-mono);
  /* One step below the design system's smallest mono, for the key-and-value
     rows on the guides index. It was being asked for as a fallback on a name
     nothing defined, which renders correctly and belongs to no scale. */
  --type-mono-xs:   500 0.625rem/1.5 var(--font-mono);
  /* --- Motion, the two steps above the design system's -------------------
     FLYING stops at `--dur-enter` (400ms), which is right for a thing
     appearing where it already was. It has no step for a thing that has to
     TRAVEL -- a card rising a section's worth of distance, a digit rolling up
     from under its own baseline, a slider running to its default.

     Declared here, and collapsed under `prefers-reduced-motion` below, in
     FLYING's own pattern. That collapse is why durations must come from a
     token: HATCH's `--dur-1..4` are plain numbers that keep animating at full
     length when the reader has asked the whole system to stop. */
  --dur-reveal: 420ms;
  --dur-travel: 700ms;
  /* The homepage's opening run: the hero calculator working itself out from
     its floor. Longer than anything else on the site, and the only step that
     is allowed to be -- it is a sequence, not a transition, and it happens
     once. */
  --dur-run: 1100ms;
  --type-mono-body: 400 0.875rem/1.6 var(--font-mono);
  /* The lift under a floating question on the answers cover. Three casts:
     a long soft one for the throw, a mid one for body, a tight one so the
     bubble still meets the page somewhere. */
  --shadow-say:
    0 26px 60px -18px rgba(var(--cast-rgb), .18),
    0 10px 26px -12px rgba(var(--cast-rgb), .10),
    0 2px 5px -2px rgba(var(--cast-rgb), .06);
  --mark-withheld:      #FFE86B;
  --mark-withheld-wash: #FFF8DC;
}

/* The collapse that makes the two steps above safe to use anywhere. FLYING
   does this for its own three; ours have to say it themselves or a reader who
   has asked for less motion still gets 700ms of travel. Not zero -- 1ms, the
   same as FLYING uses, so a transitionend still fires and nothing that waits
   on one hangs. */
@media (prefers-reduced-motion: reduce) {
  :root { --dur-reveal: 1ms; --dur-travel: 1ms; --dur-run: 1ms; }
}

/* =========================================================================
   CAVEATS

   A figure that stands but carries a stated limit. Distinct from the withheld
   treatment, which marks a figure we will not give at all.
========================================================================= */

.caveats { margin-top: var(--section-gap); padding: 2rem 2.125rem; border-radius: var(--radius-lg); background: var(--surface-card); box-shadow: 0 1px 2px rgba(var(--cast-rgb),.04), 0 22px 50px -28px rgba(var(--cast-rgb),.18); }
.caveats h3 { margin: 0.75rem 0 0; font: 500 clamp(1.25rem, 2vw, 1.625rem)/1.2 var(--font-display); letter-spacing: -0.02em; text-wrap: pretty; }
.caveats__list { margin: 1.375rem 0 0; padding: 0; list-style: none; display: grid; gap: 1.125rem; }
.caveats__list > li { padding-left: 1.125rem; border-left: 3px solid var(--mark-withheld); }
.caveats__what { margin: 0; font: 500 0.9375rem/1.35 var(--font-display); }
.caveats__why { margin: 0.4375rem 0 0; max-width: 68ch; font: 400 0.8125rem/1.55 var(--font-sans); color: var(--text-secondary); }
.caveats__src { display: inline-block; margin-top: 0.5625rem; font: 500 0.75rem/1 var(--font-display); color: var(--text-primary); text-decoration: underline; text-decoration-color: var(--raw-ink-40); text-underline-offset: 4px; }
.caveats__src:hover { text-decoration-color: currentColor; }

/* =========================================================================
   RECOVERY

   The exemption is claimed every year, not once. This states what the open
   years are worth if they were never claimed -- a possibility rather than a
   charge.

   There is no card rule here. There was one, `.recover`, and it was inside
   the unterminated comment this header used to be, so it never applied --
   and the table it would have wrapped is `extra:` inside a gold card, which
   already gives it a ground. Restoring a card around a card is not what
   closing the comment was for.
========================================================================= */

/* The recovery working: one row per open year, because the surcharge
   thresholds move every 1 July and the same salary is not worth the same in
   each of them. */
.recover__years { margin-top: 1.625rem; }
.recover__years caption { margin-bottom: 0.625rem; text-align: left; font: var(--type-caption); color: var(--text-secondary); }
.recover__years td:last-child, .recover__years th[scope="col"]:last-child { text-align: right; }
.recover__years tr[data-kind="total"] > * { border-top: 2px solid var(--raw-ink); font-weight: 600; }

/* --- The gold card -------------------------------------------------------
   One card, every calculator. It sits above the reference cards because it is
   the thing the reader did not know to ask, and it has to be seen before the
   material they came for. */
.gold {
  margin-top: var(--section-gap); padding: 2.125rem var(--sec-inset);
  border-radius: var(--radius-lg); background: var(--surface-card);
  box-shadow: 0 1px 2px rgba(var(--cast-rgb),.04), 0 22px 48px -16px rgba(var(--cast-rgb),.16);
}
.gold h3 { margin: 0.75rem 0 0; font: 500 1.875rem/1.15 var(--font-display); letter-spacing: -.03em; text-wrap: balance; max-width: 26ch; }
/* The finding, at the size the sentence gives it. It is the one number on the
   card the reader did not come looking for, so it is the one thing on the card
   set at display size — and the only place lime appears.

   Lime as TEXTURE, not a slab. The lead was a filled lime rectangle the height
   of a card, which is a great deal of the page's one accent spent on a block
   of colour rather than on a figure; hatched, the same lime marks the same
   block and the number is read in ink, where a number belongs. */
.gold__lead {
  position: relative; overflow: hidden;
  padding: 1.5rem 1.5rem 1.375rem; border-radius: var(--radius-md);
  background: var(--surface-sunken);
}
.gold__lead::before {
  content: ''; position: absolute; inset: 0; opacity: .62;
  background-image: var(--hatch-lime); pointer-events: none;
}
.gold__lead > * { position: relative; }
.gold__n {
  display: block; font: 500 3rem/1 var(--font-mono);
  letter-spacing: -.05em; font-variant-numeric: tabular-nums;
}
.gold__lab {
  display: block; margin-top: 0.625rem; max-width: 28ch;
  font: var(--type-caption); color: var(--text-secondary);
}

/* The working behind it: a hairline list, not two more slabs. These are the
   figures the lead was subtracted out of, and at the same weight as the lead
   they competed with it — three tiles of one size say three findings. */
.gold__stats { list-style: none; margin: 0.75rem 0 0; padding: 0; display: grid; gap: 0.125rem; }
.gold__stat {
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: baseline;
  column-gap: 1rem; row-gap: 0.5rem; padding: 0.875rem 0.25rem 0.9375rem;
  border-bottom: 1px solid var(--border-subtle);
}
.gold__stat:last-child { border-bottom: 0; }
.gold__sn { font: 500 1.375rem/1 var(--font-mono); letter-spacing: -.035em; font-variant-numeric: tabular-nums; }
.gold__sl { font: var(--type-caption); color: var(--text-secondary); }
/* The ground is the lime hatch and the fill covers it, so what is left
   uncovered on the shorter row is exactly the amount the lead names. One row
   runs the full width and shows none of it; the other stops short and the
   difference between them is the figure, drawn. */
.gold__track {
  grid-column: 1 / -1; display: block; height: 0.5rem; overflow: hidden;
  border-radius: var(--radius-sm);
  background-color: rgba(var(--ink-rgb), .05); background-image: var(--hatch-lime);
}
/* Opaque, not a wash: a translucent fill lets the lime ground through and the
   two halves of the bar then differ in texture but not in kind. Paper first,
   the ink over it. */
.gold__track i {
  display: block; width: var(--pct); height: 100%; border-radius: var(--radius-sm);
  background-color: var(--surface-card);
  background-image: linear-gradient(rgba(var(--ink-rgb), .32), rgba(var(--ink-rgb), .32));
}

.gold__note { margin: 1.375rem 0 0; max-width: 72ch; font: var(--type-body-sm); line-height: 1.6; color: var(--text-secondary); text-wrap: pretty; }
.gold__foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.875rem; margin-top: 1.5rem; }
.gold__src { font: var(--type-mono-sm); color: var(--text-secondary); }
.gold__go { font: 500 0.875rem/1 var(--font-display); }
@media (max-width: 640px) {
  .gold { padding: 1.625rem var(--sec-inset); }
  .gold h3 { font-size: 1.5rem; }
  .gold__n { font-size: 2.25rem; }
  .gold__lead { padding: 1.25rem 1.125rem; }
}
/* The gold card's own control, where a card asks a question of its own.

   Drawn as a line because that is what it is: the consecutive years the
   question can honestly be answered for, in order. A pill group would say
   "filter"; a rail says "these four, and no others", which is the scope the
   note underneath spells out in words. */
.gold__rail { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; width: 100%; max-width: 34rem; margin-top: 1.5rem; }
.gold__yr {
  position: relative; display: grid; justify-items: center; gap: 0.5rem;
  padding: 0.5rem 0.25rem 0.375rem; border: 0; background: none; cursor: pointer;
  font: 500 0.8125rem/1 var(--font-mono); color: var(--text-secondary);
  transition: color var(--dur-micro);
}
/* The rail itself, drawn per cell and stopped at the centre of the end ones so
   the line spans dot to dot rather than running off both sides. */
.gold__yr::before {
  content: ''; position: absolute; top: 0.9375rem; left: 0; right: 0;
  height: 1px; background: var(--border-subtle);
}
.gold__yr:first-child::before { left: 50%; }
.gold__yr:last-child::before { right: 50%; }
.gold__dot {
  position: relative; width: 0.875rem; height: 0.875rem; border-radius: 50%;
  background: var(--surface-card); box-shadow: inset 0 0 0 1.5px var(--border-strong);
  transition: background var(--dur-micro), box-shadow var(--dur-micro);
}
.gold__yr:hover { color: var(--text-primary); }
.gold__yr:hover .gold__dot { box-shadow: inset 0 0 0 1.5px var(--text-primary); }
.gold__yr[aria-pressed="true"] { color: var(--text-primary); }
.gold__yr[aria-pressed="true"] .gold__dot { background: var(--brand); box-shadow: inset 0 0 0 1.5px var(--brand), 0 0 0 4px rgba(var(--ink-rgb), .05); }
.gold__railnote {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.75rem;
  margin: 0.875rem 0 0; max-width: 52ch;
  font: var(--type-caption); color: var(--text-secondary);
}
/* The control and the figure it produces were a hair apart, so the timeline,
   its scope note and the lead panel read as one undifferentiated block. The
   gap says which of them asks and which answers. */
.gold__ctrl { margin-bottom: 2rem; }
.gold__body { margin-top: 0.25rem; }
.gold__clear { border: 0; background: none; padding: 0; cursor: pointer; font: var(--type-mono-sm); color: var(--text-secondary); text-decoration: underline; text-underline-offset: 3px; }
.gold__clear:hover { color: var(--text-primary); }
/* Four year labels across a phone do not fit side by side — they wrapped to
   "2023-" over "24" — so the rail turns and runs down the card. Still a line,
   still in order. It has to come AFTER the rules it overrides: written up in
   the earlier phone block it lost to the base rail, which is declared later. */
@media (max-width: 640px) {
  .gold__rail { grid-auto-flow: row; grid-auto-columns: auto; justify-items: start; max-width: none; margin-top: 1.25rem; }
  .gold__yr { grid-template-columns: auto auto; justify-items: start; align-items: center; gap: 0.75rem; padding: 0.4375rem 0; }
  .gold__yr .gold__dot { grid-row: 1; }
  .gold__yr::before { top: 0; bottom: 0; left: 0.4375rem; right: auto; width: 1px; height: auto; background: var(--border-subtle); }
  .gold__yr:first-child::before { top: 50%; left: 0.4375rem; }
  .gold__yr:last-child::before { bottom: 50%; right: auto; }
}

/* --- Short viewports: the calculator has to be reachable ------------------
   Measured before this rule: on a 1280×800 laptop the first question began
   496px down and took 38% of the fold; on a 1024×768 iPad in landscape, 442px
   and 42%. A reader landed on a heading and had to scroll to find the thing
   that does the work — which is the one thing on the page they came to use.

   Scaled by viewport HEIGHT rather than width, because that is what actually
   runs out. A 1920×1080 screen keeps the full-size heading; a 1280×800 laptop
   does not, and neither does an iPad in landscape. */
@media (min-width: 900px) and (max-height: 920px) {
  .main { padding-top: 3.25rem; }
  .hero--calculator .hero__utility { margin-bottom: 0.875rem; }
  .hero--calculator h1 { font-size: clamp(2.125rem, 3.6vw, 3rem); }
  .hero--calculator .hero__body { margin-top: 0.875rem; font-size: 1rem; }
  .inputs { margin-top: 1.625rem; }
}
@media (min-width: 900px) and (max-height: 760px) {
  .main { padding-top: 2.25rem; }
  .hero--calculator h1 { font-size: clamp(1.875rem, 3.2vw, 2.5rem); }
  .hero--calculator .hero__body { margin-top: 0.625rem; font-size: 0.9375rem; }
  .inputs { margin-top: 1.25rem; }
}

/* --- Getting the calculator into the fold ---------------------------------
   Measured stack on a 1280×800 laptop before this block: 149px of nav and page
   padding, a 213px heading, a 26px gap and a 123px card header — the first
   question began 511px down, at 64% of the fold, with one row visible under it.

   The cuts are made where the space actually is, and in order of how much each
   element is doing. The heading carries the question and stays. The lede under
   it repeats the heading in longer form, and the card's own lead sentence
   repeats the step counter beside it; on a screen that is running out of height
   both are the first things a reader would skip anyway. */
@media (max-height: 1120px) {
  .main { padding-top: 2.5rem; }
  .hero--calculator h1 { font-size: clamp(1.875rem, 3vw, 2.5rem); }
  .hero--calculator .hero__body { margin-top: 0.5rem; font-size: 0.9375rem; }
  .inputs { margin-top: 1rem; }
  .inputs__head { padding-bottom: 0.25rem; }
  .inputs__lead { margin-top: 0.5rem; font-size: 0.875rem; }
}
@media (max-height: 960px) {
  .main { padding-top: 2rem; }
  /* Supporting copy, not the question. The h1 above it already asks it. */
  .hero--calculator .hero__body { display: none; }
  .inputs__lead { display: none; }
  .hero--calculator h1 { font-size: clamp(1.75rem, 2.6vw, 2.125rem); }
}

/* --- The Calculators menu ---------------------------------------------------
   Five worked answers rather than five links. The trade a menu makes is that
   the tool names stop being visible at a glance; it pays that back by showing
   what each tool actually returns, which the flat nav never could. */
.megawrap { position: relative; display: inline-flex; }
.nav__mega {
  display: inline-flex; align-items: center; gap: 0.4375rem;
  min-height: 2.5rem; padding: 0; border: 0; background: transparent;
  color: inherit; cursor: pointer; font: inherit;
}
.nav__caret {
  width: 0.375rem; height: 0.375rem; border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor; transform: translateY(-1px) rotate(45deg);
  transition: transform var(--dur-micro) var(--ease-out);
}
.nav__mega[aria-expanded="true"] .nav__caret { transform: translateY(1px) rotate(-135deg); }

/* Two panels: the calculators, and what they rest on. The right-hand column is
   where a product menu usually puts a picture -- this one puts the offices,
   with a record count each, because that is the claim the whole site makes and
   the one thing a reader might want before clicking anything.

   Full bleed, flush under the header. A floating panel centred on the window
   is a second object hovering over the page; a band that runs edge to edge and
   meets the header with no seam reads as the header opening up, which is what
   it is. `--nav-offset` is the header's measured height, set at runtime by
   chrome.js, so there is no gap to guess at. */
.mega {
  position: fixed; z-index: var(--z-chrome);
  top: var(--nav-offset); left: 0; right: 0;
  max-height: calc(100dvh - var(--nav-offset));
  overflow: auto; overscroll-behavior: contain;
  padding: 2rem var(--gutter) 2.25rem;
  border-bottom: 1px solid var(--border-hairline);
  background: var(--surface-card); box-shadow: var(--shadow-lift);
}
.mega[hidden] { display: none; }
/* The band bleeds; its contents do not. This rail keeps the menu lined up with
   the rest of the page rather than with the window. */
.mega__inner {
  max-width: var(--shell); margin: 0 auto;
  display: grid; grid-template-columns: minmax(0, 1fr) 15rem;
}
.mega__main { padding-right: 1.5rem; }
.mega__title { margin: 0; font: 500 1.25rem/1.2 var(--font-display); letter-spacing: -0.02em; }
.mega__lede {
  margin: 0.5rem 0 1.25rem; max-width: 42ch;
  font: var(--type-body-sm); color: var(--text-secondary); text-wrap: pretty;
}
.mega__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 0.5rem; }

/* Separated by a rule rather than by a second surface. A card inside a card is
   the thing the homepage was rebuilt to stop doing. */
.mega__aside { padding-left: 1.5rem; border-left: 1px solid var(--border-hairline); }
.mega__asidetitle {
  margin: 0 0 0.875rem;
  font: var(--type-label); letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--text-tertiary);
}
.mega__offices { margin: 0; padding: 0; list-style: none; display: grid; gap: 0.5rem; }
.mega__office {
  display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem;
  font: var(--type-mono-sm); color: var(--text-secondary);
}
.mega__officename { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The count is the point of the row, so it does not wrap and it lines up. */
.mega__officen {
  flex: none; font-variant-numeric: tabular-nums; color: var(--text-primary);
}
.mega__asidelinks {
  display: grid; gap: 0.5rem;
  margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--border-hairline);
}
.mega__asidelinks a {
  font: 500 0.8125rem/1.3 var(--font-display); color: var(--text-primary);
  text-decoration-color: var(--raw-ink-40); text-underline-offset: 4px;
}
.mega__asidelinks a:hover { text-decoration-color: currentColor; }
.mega__item {
  /* `min-width: auto` is a grid item's default and it refuses to shrink below
     its longest unbreakable run, so "Visa tax & Medicare exemption" pushed out
     of its column and over its neighbour. */
  min-width: 0;
  display: grid; align-content: start; gap: 0.5rem;
  padding: 1rem; border-radius: var(--radius-md);
  color: var(--text-primary); text-decoration: none;
  transition: background var(--dur-micro) var(--ease-out);
}
.mega__item:hover, .mega__item:focus-visible { background: var(--surface-sunken); }
.mega__item[aria-current="page"] { background: var(--surface-accent); color: var(--text-on-accent); }
.mega__ico {
  display: grid; place-items: center; width: 2.25rem; height: 2.25rem;
  border-radius: var(--radius-md); background: var(--surface-sunken);
  color: var(--text-primary);
}
/* On the current page the tile is already lime, so the icon's own tint would
   be a second wash on top of it. */
.mega__item[aria-current="page"] .mega__ico {
  /* Off the on-accent ink, not the theme's: the tile is the same lime in both
     themes, so a tint taken from `--ink-rgb` would be a dark wash in day and a
     pale one at night, on an identical ground. */
  background: color-mix(in oklab, var(--text-on-accent) 12%, transparent);
  color: var(--text-on-accent);
}
.mega__name { font: 500 0.875rem/1.3 var(--font-display); text-wrap: pretty; }
.mega__desc {
  max-width: 24ch;
  font: 400 0.75rem/1.45 var(--font-sans); color: var(--text-secondary); text-wrap: pretty;
}
.mega__item[aria-current="page"] .mega__desc,
.mega__item[aria-current="page"] .mega__country { color: var(--text-on-accent-soft); }
.mega__country {
  margin-top: 0.125rem;
  font: var(--type-mono-sm); font-size: 0.625rem; color: var(--text-tertiary);
}
/* Below the widest desktop the panel would be wider than the window. The
   offices column goes under the calculators rather than off the side. */
@media (max-width: 1080px) {
  .mega__inner { grid-template-columns: 1fr; }
  .mega__main { padding-right: 0; }
  .mega__aside {
    margin-top: 1.25rem; padding-left: 0; padding-top: 1.25rem;
    border-left: 0; border-top: 1px solid var(--border-hairline);
  }
  .mega__offices { grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
  .mega__asidelinks { grid-auto-flow: column; justify-content: start; gap: 1.5rem; }
}
/* The burger already carries the whole list below this width, and a 44rem panel
   on a phone is just a page. */
@media (max-width: 900px) { .megawrap, .nav__links > a { display: none; } }

/* --- Index pages: /guides/ and /answers/ ------------------------------------
   Both are lists the registry already knows. A row states what it is, how much
   of it exists, and what it contains — a step or an answer that is not built is
   stated as such rather than linked. */
.idx { display: flex; flex-direction: column; gap: 1rem; margin-top: 2.5rem; }

/* `.section-head--root` stood here: a modifier that centred the two landing
   pages so they opened "the same way" as `.guide-head`, while the hubs and the
   articles stayed left-aligned. Three head treatments held in agreement by
   hand, and the comment argued for the split as a considered one.

   They are one head now — `art__head`, written by page-head.js — so the
   modifier, the pattern it modified and the argument for keeping them apart
   all go together. */

/* --- The answers cover ------------------------------------------------------
   Six faces at six sizes, drifting, loosely joined, three of them saying
   something. A cover, not a menu — see answers-hero.js.

   The stage has NO ground of its own. The only colour on this block is the six
   discs the portraits sit on, so the cover reads as things floating on the
   page rather than as a panel laid over it.

   Everything is positioned in PERCENTAGES of the stage — x, y and the circle
   diameter alike — so the arrangement scales as one composition instead of the
   faces sliding apart from each other as the band narrows. `--size` is a share
   of the stage's WIDTH: taking it from height would let a short viewport
   shrink the faces while the connectors between them stayed the same length. */
.ahero {
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-bottom: 1rem;
}
/* 5:2 of the viewport, but capped: on a 1440 screen an uncapped band is 576px
   and takes two thirds of the fold for a decorative cover. The cap flattens
   the band rather than shrinking it — the width stays 100vw — so the faces,
   which are sized off that width, keep their size and the composition just
   gets wider. There is a floor to that: the biggest face is 14% of the width,
   and once the band is flatter than about 3.4:1 it starts to reach past the
   top edge. 27rem is the flattest that still holds it at 1920. */
.ahero__stage {
  position: relative;
  /* `width: 100%` is load-bearing, not tidiness. With width left at `auto`,
     `aspect-ratio` plus a clamped `max-height` makes the browser shrink the
     WIDTH to keep the ratio: the band came out 1080px inside a 2000px hero and
     the whole composition sat in the left half of the screen with the right
     half empty. A definite width means only the height clamps. */
  width: 100%;
  aspect-ratio: 5 / 2;
  max-height: 27rem;

  /* Makes the stage a size container, so the circles can be measured against
     either of its axes — see `.ahero__face`. */
  container-type: size;
}

.ahero__links {
  position: absolute; inset: 0; width: 100%; height: 100%;
  stroke: rgba(var(--ink-rgb), .16);
}
/* The stage stretches; the hairline must not. Without this a line running
   mostly sideways and one running mostly up come out different weights. */
.ahero__links line { vector-effect: non-scaling-stroke; stroke-width: 1; }

/* Nodes and dots are placed by their CENTRE — that is what `translate(-50%,
   -50%)` buys: the numbers say where the thing IS, not where its top-left
   corner is, so changing a size does not move it. The drift is a second
   transform, so both have to live in one declaration.

   A node is NOT clipped. Only the circle inside it is — a bubble inside the
   clipped circle is a bubble you never see. */
/* A diameter measured against whichever axis is scarcer. At the design ratio
   of 5:2 the two terms are equal — 1cqh is 0.4cqw there, and the 2.5 puts them
   back in step — so this is just "a share of the width" on a normal band. Once
   the height cap flattens the band, the height term becomes the smaller one
   and wins, which is what stops the biggest face from growing out through the
   top of a 2000px-wide strip. */
.ahero__node, .ahero__dot {
  position: absolute; left: var(--x); top: var(--y);
  width: min(calc(var(--size) * 1cqw), calc(var(--size) * 2.5cqh));
  aspect-ratio: 1;
}
.ahero__node { animation: ahero-float var(--dur) ease-in-out var(--delay) infinite; }

.ahero__face {
  width: 100%; height: 100%;
  border-radius: 50%; overflow: hidden;
}
/* HATCH's accent glass, one per portrait — glass rather than a solid, so the
   page shows through and the discs stay quieter than the faces on them. */
.ahero__face--sprout { background: var(--sprout-fill); }
.ahero__face--mint   { background: var(--mint-fill); }
.ahero__face--butter { background: var(--butter-fill); }
.ahero__face--iris   { background: var(--iris-fill); }
.ahero__face--rose   { background: var(--rose-fill); }
.ahero__face--sand   { background: var(--sand-fill); }

/* Top-anchored: these are head-and-shoulders crops of slightly different
   heights, and centring them would put six chins at six different places in
   their circles. */
.ahero__pic, .ahero__pic img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 0;
}

/* The only lime on the cover. The fill ladder puts emphasis on hue, and one
   hue used three times in a composition this size is still emphasis. */
.ahero__dot {
  border-radius: 50%;
  background: var(--brand);
  box-shadow: 0 0 0 0.25rem var(--surface-solid);
  opacity: 0;
  /* Two animations on one element: the same drift every other floater has,
     and its own wink. They compose because one moves `transform` and the other
     touches only `opacity` and `scale`. */
  animation: ahero-float var(--dur) ease-in-out var(--delay) infinite,
             ahero-spark var(--dur) ease-in-out var(--delay) infinite;
}

/* --- What they say ---------------------------------------------------------
   A chat bubble, hung off its own portrait rather than placed against the
   stage. Positioned against the stage it had to be re-guessed for every aspect
   ratio and was wrong at two widths out of three; positioned against its face
   it is simply above or below that circle, everywhere, and it drifts with the
   portrait because the float animation sits on the node above them both.

   It WRAPS. A nowrap pill cannot get smaller than its longest sentence, which
   is how the questions ended up off the side of a phone; a bubble that wraps
   to two lines fits any width, and a chat bubble is allowed two lines. */
.ahero__say {
  position: absolute;
  width: max-content; max-width: min(13rem, 44vw);
  padding: 0.4375rem 0.6875rem;
  border-radius: var(--radius-lg);
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  /* Deeper and softer than a card's. These sit ON the composition rather than
     in the page's flow, and the cast is what says so: long throw, low opacity,
     wide blur — it lifts the bubble clear of the portraits without drawing a
     hard edge under it. Further than `--shadow-3`, the site's deepest, and
     fainter, because that one reads as a modal. */
  box-shadow: var(--shadow-say);
  font: 500 0.75rem/1.35 var(--font-display); letter-spacing: -0.01em;
  color: var(--text-primary);
  text-wrap: balance;

  opacity: 0;
  animation: ahero-say 13.2s var(--ease-out, ease) var(--wait) infinite;
}

/* Above a face low in the frame, below one high in it, so a bubble never
   reaches past the top or bottom of the stage. */
.ahero__say--above { bottom: calc(100% + 0.5rem); }
.ahero__say--below { top: calc(100% + 0.5rem); }

/* Pinned by the end NEAREST its face's side of the frame, so it always grows
   inward and can never run off the edge, at any width. */
.ahero__say--start { left: 0; }
.ahero__say--end   { right: 0; }
.ahero__say--mid   { left: 50%; transform: translateX(-50%); }

/* The tail. Same ground and hairline as the bubble, rotated into a corner and
   half-covered by the bubble itself so only the point shows. */
.ahero__say::after {
  content: ''; position: absolute;
  width: 0.5rem; height: 0.5rem;
  background: var(--surface-card);
  border-right: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-subtle);
}
.ahero__say--above::after { bottom: -0.28rem; transform: rotate(45deg); box-shadow: var(--shadow-say); }
.ahero__say--below::after { top: -0.28rem; transform: rotate(225deg); box-shadow: var(--shadow-say); }
.ahero__say--start::after { left: 1rem; }
.ahero__say--end::after   { right: 1rem; }
.ahero__say--mid::after   { left: calc(50% - 0.25rem); }

@keyframes ahero-float {
  0%, 100% { transform: translate(-50%, -50%); }
  50%      { transform: translate(-50%, calc(-50% - 0.9rem)); }
}

/* Spoken, not typed. It arrives small and slightly low, overshoots a little,
   holds long enough to be read twice, then goes.

   The visible window is a SIXTH of the cycle because there are six speakers
   and they take turns — widening it without shortening another puts two
   bubbles on screen at once, which is the one thing the turn-taking exists to
   prevent. Change the count in answers-hero.js and these numbers change with
   it. */
@keyframes ahero-say {
  0%          { opacity: 0; scale: 0.86; translate: 0 0.375rem; }
  3%          { opacity: 1; scale: 1.05; translate: 0 0; }
  5%          { opacity: 1; scale: 1;    translate: 0 0; }
  14%         { opacity: 1; scale: 1;    translate: 0 0; }
  16.7%, 100% { opacity: 0; scale: 0.96; translate: 0 -0.25rem; }
}

/* Each accent is up for about a third of its own cycle, and the cycles are all
   different lengths and out of step, so which two or three are lit changes
   every second or so without any of them moving. */
@keyframes ahero-spark {
  0%, 6%     { opacity: 0; scale: 0.4; }
  16%        { opacity: 1; scale: 1; }
  40%        { opacity: 1; scale: 1; }
  52%, 100%  { opacity: 0; scale: 0.4; }
}

/* Nothing may leave the viewport, so the band gets TALLER as it narrows rather
   than squeezing the composition. The faces are sized off the stage width, so
   otherwise they would shrink to thumbnails on a phone while the connectors
   between them kept their length. */
@media (max-width: 52rem) {
  .ahero__stage { aspect-ratio: 2 / 1; }
}
/* Nearly square on a phone. Not for its own sake — the faces are a share of
   the WIDTH, so a short band packs six of them into 250px of height with the
   speech bubbles fighting for the gaps, and two of them landed on a portrait.
   Taller spreads the same faces over more vertical room and the bubbles fit
   between them. Checked by measuring every bubble against every face, not by
   looking. */
@media (max-width: 34rem) {
  .ahero__stage { aspect-ratio: 5 / 4; }
  .ahero__say { font-size: 0.6875rem; max-width: min(11rem, 52vw); }
}

/* Decoration is the first thing that should stop moving. The bubbles still
   have to be READABLE, though — stopping the animation on something that
   starts at `opacity: 0` would delete the questions rather than still them.
   Only the first one is shown: three at once would overlap. */
@media (prefers-reduced-motion: reduce) {
  .ahero__node, .ahero__dot {
    animation: none; transform: translate(-50%, -50%);
  }
  .ahero__say { animation: none; opacity: 0; }
  .ahero__node:first-of-type .ahero__say { opacity: 1; }
  .ahero__dot { animation: none; opacity: 1; }
}

/* --- Methodology: sources and changelog -------------------------------------
   Both are generated from the records, so both are dense tables of dated fact.
   Type is small and monospaced where it is data, and the row is the unit. */
.srcwarn {
  display: grid; gap: 0.4375rem; margin-top: 2rem; padding: 1.125rem 1.375rem;
  border-radius: var(--radius-md); background: var(--surface-accent);
  font: 400 0.8125rem/1.5 var(--font-sans); color: var(--text-on-accent);
}
.srcwarn strong { font: 500 0.875rem/1.4 var(--font-display); }
.srcwarn span { font: 400 0.75rem/1.5 var(--font-mono); }

.srcgroup { margin-top: 2.75rem; }
.srcgroup h2 {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  margin: 0 0 0.875rem; padding-bottom: 0.625rem; border-bottom: 1px solid var(--border-strong);
  font: 500 1.0625rem/1.25 var(--font-display);
}
.srcgroup h2 span { flex: none; font: 400 0.6875rem/1.3 var(--font-mono); color: var(--text-tertiary); }

/* The box that lets a wide table scroll instead of the page.

   It had no rule at all. `.scroller` has been in the markup around every duty
   scale, source register and changelog on this site, and nothing ever defined
   what it does — so on a screen narrower than the table, the table pushed the
   whole page sideways instead of scrolling inside its own box. Invisible on a
   desktop, where the tables fit; 104px of horizontal page scroll on a phone.

   `overscroll-behavior-x: contain` so swiping to the end of a table does not
   then swipe the page or trigger a browser back gesture. */
.scroller { overflow-x: auto; overscroll-behavior-x: contain; }

.srctable { width: 100%; border-collapse: collapse; }
/* Headings WRAP; figures do not.

   A heading only breaks when its column is squeezed, so this changes nothing
   where there is room and everything where there is not: the duty scale on a
   guide needed 425px against 388 available with `nowrap`, and needs exactly
   388 without it. A taller header beats a horizontal scrollbar — the reader
   sees the whole table instead of discovering a third of it by swiping.

   It does not rescue every table, and that is why `.scroller` still exists.
   The source register is held open by its DATA, not its headings — record ids
   and date ranges, both mono and both unbreakable — and wrapping its headings
   moves it not one pixel: 495 before, 495 after. That one has to scroll. */
.srctable th {
  text-align: left; padding: 0 1rem 0.625rem 0;
  font: 500 0.625rem/1.3 var(--font-mono); letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--text-tertiary);
}
.srctable td { padding: 0.75rem 1rem 0.75rem 0; border-top: 1px solid var(--border-hairline); vertical-align: top; }
.srctable td:last-child, .srctable th:last-child { padding-right: 0; }
.srctable a { font: 500 0.8125rem/1.35 var(--font-mono); }
.srctable__note { display: block; margin-top: 0.5rem; max-width: 52ch; font: var(--type-caption); color: var(--text-secondary); }
.srctable__by { max-width: 30ch; font: 400 0.6875rem/1.45 var(--font-sans); color: var(--text-tertiary); }

/* A band is two figures with a dash between them, and the dash is the one
   place it can break. Each bound stays whole — a number split across lines is
   unreadable — but the pair may sit on two rows, which is what stops the
   widest column in the table from setting the width of the whole page.

       $1,290,000 –
       $3,870,000

   This is the same trade as the wrapping headings: a taller row beats a
   horizontal scrollbar, because the reader can see all of it. */

/* Victoria's flat band reads "5.50% of the whole value" in a cell that is
   `nowrap` because it is a figure. The figure is the 5.50%; the rest is prose
   riding along with it, and holding it on one line made this the only scale
   that could not fit a phone. */
.scale__of { white-space: normal; }

/* Tighter gutters where the screen is the constraint. Four columns of money at
   1rem apart is 48px of air the reader is paying for in scrollbar. */
@media (max-width: 34rem) {
  .srctable th, .srctable td { padding-right: 0.625rem; }
}
.srctable tr.is-open td { background: var(--raw-vanilla-wash); }

/* --- The page head, for every page that is a document ---------------------
   Written by page-head.js, and styled here rather than in article.css because
   article.css is linked only by guides and answers. See the note there. */
/* --- The opening --------------------------------------------------------- */
/* `margin-inline: auto` is the difference between a head that centres and one
   that only centres its text. Inside `.art` the wrapper's own `> *` rule was
   doing it; on the compare page, the two indexes and the methodology pages
   there is no such wrapper, so the block sat hard left with centred lines
   inside it. It centres itself now, wherever it is put. */
.art__head { max-width: 44rem; margin: 0 auto 5rem; text-align: center; }
.art__date {
  margin: 0; font: 500 0.6875rem/1 var(--font-mono);
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-tertiary);
}
/* The ceiling was 3.25rem, which rendered at 52px on a laptop — larger than
   the homepage's 40px and nearly double this article's own section root at
   30px. A guide is the most substantial page on the site but it is not the
   loudest thing on it, and a title that outshouts the front door is the tell
   that a page was styled on its own rather than against the others.

   2.25rem tops out at 36px: above the roots because an article's title is the
   page, below the homepage because nothing here outranks the front door. */
.art__head h1 {
  margin: 1.5rem 0 0;
  font: 500 clamp(1.875rem, 3.2vw, 2.25rem)/1.12 var(--font-display);
  letter-spacing: -0.03em; text-wrap: balance;
}
.art__lede {
  margin: 1.5rem auto 0; max-width: 38rem;
  font: var(--type-body-lg); line-height: 1.55; color: var(--text-secondary);
  text-wrap: pretty;
}
@media (max-width: 45rem) {
  .art__head { margin-bottom: 3rem; }
}

.changelog { margin: 0; padding: 0; list-style: none; }
.changelog li {
  display: grid; grid-template-columns: 8rem minmax(0, 1fr) auto; gap: 1rem;
  align-items: baseline; padding: 0.6875rem 0; border-top: 1px solid var(--border-hairline);
}
.changelog__date { color: var(--text-tertiary); }
.changelog__what { font: var(--type-body-sm); }
.changelog__what a { font: 500 0.8125rem/1.35 var(--font-mono); }
.changelog__src { flex: none; font: 400 0.6875rem/1.3 var(--font-mono); color: var(--text-tertiary); }

/* Our own history reads differently from the government's: each entry is a
   sentence and a reason, not a rule id, so it needs room to be a paragraph
   rather than a row. The label sits at the end, quiet -- a correction should be
   findable, not shouted. */
.changelog__intro { font: var(--type-body-sm); color: var(--text-secondary); max-width: 60ch; margin: 0 0 0.5rem; }
.changelog--ours li { align-items: start; }
.changelog--ours .changelog__what { line-height: 1.5; }
.changelog__why { color: var(--text-secondary); }
.changelog--ours .changelog__src { text-transform: uppercase; letter-spacing: 0.04em; }
@media (max-width: 640px) {
  .changelog li { grid-template-columns: 1fr; gap: 0.25rem; }
  .srctable__by { display: none; }
}

/* --- Methodology index ---------------------------------------------------- */
/* Five figures about the ruleset. A GRID of equal columns, not a flex strip:
   flex packs by content width, so "Confirmed against the publisher" took three
   times the room of "Offices" and the fifth figure fell to a line of its own,
   half the width of the row above it. Equal columns wrap evenly or not at all.

   No boxes, so a short last row is invisible rather than an empty cell — which
   is what the boxed version could not do. */
.methstats {
  /* Five columns, stated. `auto-fit` at a 8.5rem minimum needed about 808px to
     keep them on one line, so a tablet broke them into four and a stranded
     fifth. Five equal tracks hold the row down to the tablet width instead,
     and the long label wraps inside its own column rather than pushing the
     others off the end.

     Below that they divide evenly rather than reflowing: three, then two with
     the last spanning both, so there is never one figure sitting alone in a
     row of empty cells. */
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 1.25rem;
  /* Bottom-aligned, because one label runs to two lines and the rest to one.
     Aligned at the top, its figure sat a line lower than the other four. */
  align-items: end;
  margin: 2.25rem 0 0; padding: 1.25rem 0;
  border-top: 1px solid var(--border-strong);
  border-bottom: 1px solid var(--border-strong);
}
.methstats > div { padding: 0; min-width: 0; }
/* 44rem, not 48. A tablet is 768 and `max-width: 48rem` is 768 inclusive, so
   the one width this was meant to hold in a single line was the one it broke.
   Five tracks fit 768 at about 134px each; the long label wraps inside its own
   column, which is what the bottom alignment above is for. */
@media (max-width: 44rem) {
  .methstats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 30rem) {
  .methstats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .methstats > div:last-child { grid-column: 1 / -1; }
}
.methstats dt { font: 500 0.625rem/1.35 var(--font-mono); letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-tertiary); }
.methstats dd { margin: 0.375rem 0 0; font: 500 1.375rem/1 var(--font-mono); font-variant-numeric: tabular-nums; letter-spacing: -0.03em; }
/* After the base rule, not before it: the shorthand above sets `font`, which
   resets font-size, and at equal specificity the later rule wins. Placed above
   it this did nothing at all.

   Below the full measure the figures come down a step so the longest of them
   fits its track — "26 Aug 2026" is eleven monospaced characters, about 145px
   at 1.375rem, and a fifth of a 768px measure is 130. It wrapped to two lines,
   which on a row of single-line figures reads as a mistake. */
@media (max-width: 56rem) {
  .methstats dd { font-size: 1.0625rem; }
}
/* Two links at the foot of a page do not need two cards. Hairlines, like the
   answers register: a card is a surface for something meant to be picked up,
   and these are two lines that say where to go next. */
.methlinks { display: grid; gap: 0; margin-top: 3.5rem; border-top: 1px solid var(--border-strong); }
.methlinks a {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; gap: 0.3125rem 1.5rem; padding: 1.125rem 0;
  border-bottom: 1px solid var(--border-hairline);
  text-decoration: none; color: var(--text-primary);
}
.methlinks a:hover strong { text-decoration: underline; text-underline-offset: 0.2em; }
.methlinks strong { grid-column: 1; font: 500 1rem/1.25 var(--font-display); }
.methlinks span { grid-column: 1; font: var(--type-body-sm); color: var(--text-secondary); }
/* A chevron, so the row reads as somewhere to go rather than as a heading with
   a note under it. Two borders on a rotated square rather than a character:
   generated text content is announced by some screen readers, and this arrow
   says nothing the link text does not already say.

   It spans both rows so it sits against the middle of the pair, and it moves
   on hover the way the tile links do. */
.methlinks a::after {
  content: ''; grid-column: 2; grid-row: 1 / -1; align-self: center;
  width: 0.5rem; height: 0.5rem;
  border-top: 1.5px solid var(--text-tertiary);
  border-right: 1.5px solid var(--text-tertiary);
  transform: rotate(45deg);
  transition: transform var(--dur-micro) var(--ease-out),
              border-color var(--dur-micro) var(--ease-out);
}
.methlinks a:hover::after {
  border-color: var(--text-primary);
  transform: rotate(45deg) translate(2px, -2px);
}

/* --- Components that moved to their own pages -------------------------------
   `.proof2` and `.indictment` were in the homepage's #why; `.keep__list` in
   #keep. Both sections are now /about/ and /methodology/no-accounts/, which
   load app.css and not the homepage stylesheets — so the styles move with the
   markup rather than the pages rendering unstyled. */

.indictment {
  margin: 1.875rem 0 0; padding: 1.625rem 1.75rem;
  border-left: 3px solid var(--raw-ink); border-radius: 0 var(--radius-md) var(--radius-md) 0;
  background: var(--surface-sunken);
}
.indictment blockquote {
  margin: 0; font: 500 clamp(1.625rem, 2.6vw, 2.125rem)/1.15 var(--font-display);
  letter-spacing: -.03em; text-wrap: balance;
}
.indictment figcaption { margin-top: 0.875rem; font: var(--type-body-sm); line-height: 1.55; color: var(--text-secondary); text-wrap: pretty; }

.proof2 { margin: 1.125rem 0 0; }
.proof2__row { padding: 1.125rem 0; border-top: 1px solid var(--border-subtle); }
.proof2__row:first-child { border-top: 1px solid var(--border-strong); }
.proof2 dt { display: flex; align-items: baseline; gap: 0.75rem; flex-wrap: wrap; }
.proof2__n { font: 500 1.375rem/1 var(--font-mono); letter-spacing: -.03em; font-variant-numeric: tabular-nums; white-space: nowrap; }
.proof2__claim { font: 500 0.9375rem/1.3 var(--font-display); }
.proof2 dd { margin: 0.5rem 0 0; font: var(--type-body-sm); line-height: 1.55; color: var(--text-secondary); text-wrap: pretty; }
  .proof2__row { padding: 0.9375rem 0; }
.keep__list { margin: 0.375rem 0 0; padding: 0; list-style: none; border-top: 1px solid var(--border-hairline); }
.keep__list li {
  display: grid; grid-template-columns: 7.375rem minmax(0, 1fr); gap: 1.125rem;
  padding: 1.125rem 0; border-bottom: 1px solid var(--border-subtle);
  font: var(--type-body-sm); line-height: 1.55; color: var(--text-secondary); text-wrap: pretty;
}
.keep__k { font: 500 0.8125rem/1.5 var(--font-mono); color: var(--text-primary); }
  .keep__list li { grid-template-columns: 1fr; gap: 0.3125rem; }
.keep__list--page { margin-top: 2.5rem; }

/* --- Answer pages ----------------------------------------------------------
   An article whose last section is generated from the records it cites: the
   sources list cannot drift from the engine because it is read from it. */
.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin-bottom: 1.75rem; font: 400 0.75rem/1.4 var(--font-mono); color: var(--text-tertiary); }
.crumbs a { color: var(--text-secondary); }
.crumbs span[aria-current] { color: var(--text-primary); }
.answer__sources { margin-top: 3.5rem; padding: 1.875rem; border-radius: var(--radius-lg); background: var(--surface-raised); box-shadow: inset 0 0 0 1px var(--border-hairline); }
.answer__sources h2 { margin: 0 0 0.5rem; font: 500 1.0625rem/1.25 var(--font-display); }
.answer__sources > p { margin: 0 0 1.375rem; max-width: 62ch; font: var(--type-body-sm); color: var(--text-secondary); }
.answer__records { margin: 0 0 1.75rem; display: grid; gap: 0.875rem; }
.answer__records dt { font: 500 0.875rem/1.35 var(--font-display); }
.answer__records dd { margin: 0.25rem 0 0; font: 400 0.6875rem/1.5 var(--font-mono); color: var(--text-tertiary); }
.changelog__id { font: 400 0.625rem/1.3 var(--font-mono); color: var(--text-tertiary); }

/* --- The gold card, laid out ------------------------------------------------
   It was a single column: three figure cards, then a table, then a paragraph,
   stacked down a 1,100px card. The figures and the working are two different
   things a reader does two different things with — glance at one, read the
   other — so they sit side by side where there is room.

   The table is `.ledger` now, the same component the working section uses. It
   was `.wledger`, which is styled only in `home.css` — a stylesheet the
   calculator pages do not load, so on the page it actually appeared on it had
   almost no styling at all. */
.gold__extra { min-width: 0; }
/* Four columns of mono figures have a floor. Where the column is narrower than
   that — a phone, or the tight end of the tablet band — the table scrolls
   inside its own box rather than being clipped or pushing the page sideways.
   Same rule the wide tables elsewhere follow. */
.gold__extra { overflow-x: auto; }
.gold__extra .ledger { margin-top: 0; min-width: 21.75rem; }
/* The gold table is the SAME component as the working section's ledger, so it
   carries the same row kinds — `group` for the header row, `line` for each
   year, `total` for the sum. Without them none of `.ledger`'s row styling
   applied at all: no dotted rules, no mono figures, sans-serif headers wrapping
   mid-phrase. Two treatments for one table. */
.gold__extra .ledger { font-size: 0.9375rem; }
.gold__extra .ledger tr > * { padding-left: 0.875rem; padding-right: 0.875rem; }
.gold__extra .ledger tr[data-kind="total"] > * { padding-top: 1rem; padding-bottom: 1rem; }
.gold__extra .ledger tr[data-kind="total"] td { font-size: 1.125rem; }

@media (min-width: 901px) {
  .gold {
    display: grid;
    /* The right column holds the ledger, so it carries the ledger's floor. */
    grid-template-columns: minmax(0, 1fr) minmax(21.75rem, 1fr);
    column-gap: 2.75rem;
    align-items: start;
  }
  .gold > .eyebrow, .gold h3, .gold__ctrl, .gold__foot { grid-column: 1 / -1; }
  .gold__body { grid-column: 1; }
  .gold__extra { grid-column: 2; margin-top: 1.375rem; }
  .gold__note { grid-column: 2; max-width: none; margin-top: 1.5rem; }
  .gold__foot { margin-top: 1.75rem; }
}

/* Tablet: the figures read across the top, then the working and the condition
   share the second row — the table leads because it is the wider of the two.

   The band starts at 840px, not 600. The ledger needs 347px for four columns of
   mono figures, and a paragraph needs about 300 before the measure stops being
   readable — 660px of content, which a 768px card does not have once its
   padding and the page gutter are taken out. Below 840 they stack, full width
   each, which is the honest answer rather than a 228px column of text. */
@media (min-width: 840px) and (max-width: 900px) {
  .gold {
    display: grid;
    grid-template-columns: minmax(21.75rem, 1fr) minmax(0, 1fr);
    column-gap: 1.75rem;
    align-items: start;
  }
  .gold > .eyebrow, .gold h3, .gold__ctrl, .gold__body, .gold__foot { grid-column: 1 / -1; }
  .gold__extra { grid-column: 1; }
  .gold__note { grid-column: 2; max-width: none; margin-top: 1.625rem; }
}

/* The footer sits on ink, so its wordmark takes the on-ink text colour. This
   was an inline raw colour, twice — one step from the token that already holds
   it, in the one place a theme change would need to reach. */
.wordmark--footer { color: var(--text-on-ink); }

/* Two spacing values that were inline pixel margins. */
.q__explain--tight { margin: 0.375rem 0 0; }
.pills--spaced { margin-top: 0.625rem; }

/* --- /methodology/how-…-is-calculated/ ------------------------------------
   The provenance line under each table, and the calculator at the end. */
.method-source {
  margin: 0.625rem 0 2.5rem;
  font: 400 0.75rem/1.5 var(--font-mono); color: var(--text-tertiary);
}
.method-source a { color: var(--text-secondary); }
.method-source span { display: block; margin-top: 0.1875rem; }

.method-cta {
  margin-top: 3.5rem; padding: 2rem;
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  box-shadow: inset 0 0 0 1px var(--border-hairline);
}
.method-cta h2 { margin: 0; font: 500 1.25rem/1.25 var(--font-display); }
.method-cta p { margin: 0.625rem 0 1.5rem; color: var(--text-secondary); max-width: 58ch; }
.method-scale { margin-top: 0.25rem; }
/* No floor under the first column. It was 13rem so a band range would never
   wrap; ranges break at their dash now, by design, so the floor bought nothing
   and cost 208px to a column whose widest cell is "$600,000" — which is the
   gap between the first column and the rest on the concession table.

   Every column sizes to its own content, and the space between them is the
   cell gutter, the same on all of them. */
.answer__method { margin: 1.25rem 0 0; font: var(--type-body-sm); color: var(--text-secondary); }

/* =========================================================================
   BETWEEN PAGES

   A cross-document view transition: the browser holds the outgoing page,
   paints the incoming one, and animates between them. Declared, not scripted
   -- `navigation: auto` is the whole opt-in, and a browser without it simply
   navigates as it always did.

   This is worth having on this site specifically. A reader here moves between
   pages that are largely the same furniture holding different figures --
   guide to guide, calculator to calculator, home to calculator -- and a hard
   cut makes each of those feel like starting over rather than turning a page.

   WHAT IS NAMED IS WHAT SHOULD NOT MOVE. Naming an element lifts it out of
   the page snapshot and gives it its own transition, so the header stays put
   while the content changes underneath it, instead of cross-fading with
   everything else and flickering on every navigation.

   Scoped to the nav pill's own wordmark, NOT `.wordmark`: the mobile drawer
   holds a second one, and two elements sharing a view-transition-name aborts
   the entire transition rather than failing quietly on that one element.

   NOT the PDF statement. That opens in a new tab, and a cross-document
   transition is between two documents in the same tab -- there is nothing to
   transition from. The statement's stylesheet does not carry this rule.
========================================================================= */

@view-transition { navigation: auto; }

.nav__pill { view-transition-name: site-chrome; }
.nav__left > .wordmark { view-transition-name: site-mark; }
.ctabar { view-transition-name: cta-bar; }

@keyframes vt-leave { to { opacity: 0; transform: translateY(-0.375rem); } }
@keyframes vt-arrive { from { opacity: 0; transform: translateY(0.625rem); } }

::view-transition-old(root) { animation: vt-leave var(--dur-base) var(--ease-out) both; }
::view-transition-new(root) { animation: vt-arrive var(--dur-reveal) var(--ease-out) both; }

/* The preference reaches the transition tree too. `--dur-*` collapsing to 1ms
   would already make this almost instant, but "almost" is not what was asked
   for: these stop outright. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none; }
}

/* --- The sliding selection marker ----------------------------------------
   One element per control, measured onto whichever button is pressed by
   `slide-indicator.js`. The painted markers below it stay in the stylesheet
   and are only switched off by `.is-slid`, which that module adds once it has
   mounted and measured -- so a page without it keeps the control it had. */
.slide-ind {
  position: absolute; z-index: 0; pointer-events: none; opacity: 0;
  transition: transform var(--dur-base) var(--ease-out),
              width var(--dur-base) var(--ease-out),
              opacity var(--dur-micro) var(--ease-out);
}
.slide-ind--pill {
  top: 0.25rem; bottom: 0.25rem; left: 0;
  border-radius: var(--radius-pill);
  background: var(--surface-card);
  box-shadow: var(--shadow-1);
}
.slide-ind--underline {
  bottom: -1px; left: 0; height: 2px;
  background: var(--raw-vanilla-deep);
}
/* The buttons have to sit above the marker, and the container has to be the
   thing it is positioned against. */
.is-slid { position: relative; }
.is-slid > button { position: relative; z-index: 1; }
/* Painted markers off, now that a real one is moving. */
.fy.is-slid button[aria-pressed="true"],
.hero-answer__years.is-slid button[aria-pressed="true"] { background: transparent; box-shadow: none; }
.hero-calculator-tabs.is-slid button[aria-pressed="true"] { border-bottom-color: transparent; }
