/* Hallmark · genre: modern-minimal · macrostructure: Split Studio
 * theme: tradergent brand, dual-mode · vibe: "lit solid, folded planes, institutional"
 * REBRANDED 2026-08-28. Light: paper oklch(93% 0.018 84) · accent oklch(30.8% 0.038 179).
 * Dark:  paper oklch(30.8% 0.038 179) · accent oklch(84% 0.078 81.1).
 * The light accent is GREEN, not gold -- gold on cream is 1.18:1. See tokens.css.
 * mark: SEKUYA (self-hosted, OFL — unicase display: the logotype only, 2 slots)
 * display: Montserrat (self-hosted, OFL — every heading, h1 through the small heads)
 * body: Karla (self-hosted, OFL)
 * outlier: Geist Mono (self-hosted, OFL — step ordinals only, 1 slot)
 * axes: dark / unicase-logotype + geometric-sans + institutional-grotesque / lime ~125°
 * nav: N1b SaaS three-section · knobs: centre-links=3, dropdowns=none, scroll=always-solid, cta=fill-only
 * footer: Ft5 Statement · knobs: sentence=34ch, wordmark=under-sentence, rule-above-meta=hairline,
 * legal-smallprint=below-meta (13px, --color-muted, 9.5:1 — subordinate by size and position, never dimmed)
 * hero: H2 Split diptych · knobs: ratio=7/5, right=lit 3D folded mark, divider=negative-space
 * sections: S2 hanging heads · F4 step sequence (numbering=01/02, layout=vertical-stack, connector=line)
 * · C1 outlined chip (shape=rectangular-soft, density=spacious, adornment=arrow)
 * enrichment: Tier-A WebGL folded-plane mark, CSS folded-plane fallback (hero only)
 * gradients: measured off the mark, interpolated `in oklch` · no glow, no text-shadow, no animation
 * contrast: pass (40–41) · slop: pass (42–45) · honest: pass (46) · chrome: pass (47)
 * tokens: pass (48) · responsive: pass (49) · icons: pass (30) · mobile: pass (34, 49, 50–57)
 * Hallmark · pre-emit critique: P5 H5 E5 S5 R5 V5
 */

/* Every rule below consumes tokens.css by name. There is no colour value and
   no font stack in this file. Component classes are page-agnostic so later
   pages of the site can reuse them without edits. */

/* ============================================================ 1 · Base ==== */

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

html {
  /* clip, not hidden: clip preserves position:sticky on descendants. */
  overflow-x: clip;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* In-page anchors must land below the sticky bar, not underneath it. */
  scroll-padding-block-start: calc(var(--nav-height) + var(--space-lg));
}

body {
  overflow-x: clip;
  margin: 0;
  background-color: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-weight: var(--weight-body);
  font-size: var(--text-base);
  line-height: var(--lh-body);
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, dl, dd, ol, ul, figure { margin: 0; }
ol, ul { padding: 0; list-style: none; }

a { color: inherit; }

::selection {
  background-color: var(--color-accent);
  color: var(--color-accent-ink);
}

/* Content column. Used by every band on every page of the site. */
.shell {
  width: 100%;
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--page-gutter);
}

/* ========================================== 2 · Focus (never animated) ==== */

/* Rings must appear instantly — a keyboard user has no indicator during a
   transition. No `transition` and no `animation` touches outline anywhere.
   --color-accent-peak clears 3:1 on paper, paper-2 and the lightest point of
   every gradient surface it can land on. */
:where(a, button, summary, input, select, textarea):focus-visible {
  outline: 2px solid var(--color-accent-peak);
  outline-offset: 3px;
  border-radius: var(--radius-control);
}

/* ================================= 3 · The single orchestrated entrance ==== */

/* One load fade for the whole document, staggered by DOM index, capped well
   under half a second in total. Nothing animates on scroll. */
.reveal {
  opacity: 0;
  transform: translateY(8px);
  animation: reveal var(--dur-long) var(--ease-out) forwards;
  animation-delay: calc(var(--i, 0) * 45ms);
}

@keyframes reveal {
  to { opacity: 1; transform: none; }
}

/* ============================================= 4 · N1b · The nav bar ====== */

.nav {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-sticky-nav);
  background-color: var(--color-paper);
  border-block-end: var(--rule-hair) solid var(--color-rule);
}

.nav__inner {
  width: 100%;
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--page-gutter);
  min-height: var(--nav-height);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;   /* gate 36: the bar mixes text and a filled control */
  gap: var(--space-md);
}

/* The wordmark is a LOCKUP, not a link that happens to be bold. It is set in
   the unicase face, which draws `tradergent` at cap height straight from the
   lowercase in the markup — there is no text-transform here and there must
   not be one: the caps are in the outlines, and transforming would only
   change what a screen reader says.

   font-synthesis is off because the face ships one weight; without it a UA
   asked for a bold it does not have will smear the stems and close up the
   counters of the one piece of type on the page that has to be exact. */
.nav__brand {
  justify-self: start;
  font-family: var(--font-mark);
  font-weight: var(--weight-mark);
  font-synthesis: none;
  font-size: var(--text-mark-sm);
  line-height: 1;
  letter-spacing: var(--track-mark-sm);
  color: var(--color-accent);
  text-decoration: none;
  white-space: nowrap;
  padding-block: var(--space-sm);
  transition: color var(--dur-micro) var(--ease-out);
}

.nav__brand:hover  { color: var(--color-accent-peak); }
.nav__brand:active { color: var(--color-accent-deep); }

.nav__centre {
  justify-self: center;
  display: flex;
  align-items: center;
  gap: var(--space-lg);
}

.nav__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--target-min);
  color: var(--color-muted);
  font-size: var(--text-sm);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;      /* gate 49: a nav link never wraps */
  transition: color var(--dur-micro) var(--ease-out);
}

.nav__link:hover  { color: var(--color-ink); }
.nav__link:active { color: var(--color-accent-deep); }

/* The page you are already on. The bar's resting state is muted, so the one
   destination that is not a destination is the one held at full ink — the
   distinction is weight, not colour, and no accent is spent on it. */
.nav__link[aria-current="page"] { color: var(--color-ink); }

/* justify-self positions the box; the flex properties below are needed now
   that .nav__right holds two items (the new auth link + the existing CTA)
   instead of one, so they sit apart rather than flush against each other. */
.nav__right {
  justify-self: end;
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

/* nav-auth.js: reserves this link's box so the header's layout doesn't
   shift when JS fills it in, and keeps neither label ("Sign in" vs
   "Dashboard") visible until the /api/me check resolves. nav-auth.js sets
   visibility back to "visible" itself, on both success and failure. */
.nav__auth-link { visibility: hidden; }

/* ================================================ 5 · Controls =========== */

/* The primary action carries the mark's LIT FACE as its fill — the same ramp,
   the same direction, the same falloff. It is the only filled lime surface in
   the page's chrome, which is what keeps a plane-scale gradient from reading
   as decoration. */
.cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: var(--target-min);
  padding-inline: var(--space-lg);
  border: var(--rule-hair) solid var(--color-lit-bottom);
  border-radius: var(--radius-control);
  background-image: var(--grad-lit-face);
  background-color: var(--color-accent);   /* fallback fill under the ramp */
  color: var(--color-accent-ink);
  font-family: var(--font-body);
  font-weight: var(--weight-display);
  font-size: var(--text-base);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition:
    border-color var(--dur-micro) var(--ease-out),
    transform var(--dur-press) var(--ease-out);
}

.cta:hover {
  background-image: none;
  background-color: var(--color-accent-peak);
  border-color: var(--color-accent-peak);
}

.cta:active {
  background-image: var(--grad-lit-face-press);
  border-color: var(--color-lit-press);
  transform: translateY(1px);
}

.cta--sm {
  min-height: var(--target-min);
  padding-inline: var(--space-md);
  font-size: var(--text-sm);
}

/* C1 · outlined chip. The closing action is quieter than the hero's: the lime
   is a boundary and a word, not a plane. */
.cta--ghost {
  background-image: none;
  background-color: var(--color-paper);
  border-color: var(--color-accent);
  color: var(--color-accent);
}

.cta--ghost:hover {
  background-color: var(--color-paper);
  border-color: var(--color-accent-peak);
  color: var(--color-accent-peak);
}

.cta--ghost:active {
  background-image: none;
  background-color: var(--color-paper-2);
  border-color: var(--color-accent-deep);
  color: var(--color-accent-deep);
}

.cta[aria-disabled="true"] {
  background-image: none;
  background-color: var(--color-rule);
  border-color: var(--color-rule);
  color: var(--color-muted);
  cursor: not-allowed;
  pointer-events: none;
}

/* Inline prose link. */
.link {
  color: var(--color-accent);
  text-decoration: underline;
  text-decoration-thickness: var(--rule-hair);
  text-underline-offset: 0.18em;
  transition: color var(--dur-micro) var(--ease-out);
}

.link:hover  { color: var(--color-accent-peak); }
.link:active { color: var(--color-accent-deep); }

.link[aria-disabled="true"],
.nav__link[aria-disabled="true"],
.nav__brand[aria-disabled="true"] {
  color: var(--color-muted);
  text-decoration-color: var(--color-rule);
  cursor: not-allowed;
  pointer-events: none;
}

/* ============================================= 6 · H2 · The hero ========= */

.hero {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: var(--space-2xl);
  align-items: center;
  /* gate 44a: the hero's own bottom padding is NOT the breathing room under
     the hero — the first module's --space-3xl top padding is, and the two ADD.
     Measured at 1280: 64px here + 96px there put 160px of edge-to-edge black
     between the last hero line and "How it works", which reads as the page
     having ended rather than as air. Cut to --space-md so the seam totals
     112px: the module still opens on a full --space-3xl of its own, and the
     first section heading arrives 48px higher up the first screen. */
  padding-block: var(--space-xl) var(--space-md);
}

/* The measure goes back OUT to 17ch. A ch in Montserrat is far narrower than a
   ch in the unicase face this slot used to hold, so the 13ch that face needed
   would break the headline to five lines; 17ch holds it to three at every
   width without hyphenating. */
.hero__title {
  max-width: 17ch;
  margin-block-end: var(--space-lg);
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-style: normal;
  font-size: var(--text-display);
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  overflow-wrap: anywhere;
  min-width: 0;
  text-wrap: balance;
}

.hero__lede {
  max-width: var(--measure-lede);
  font-size: var(--text-md);
  line-height: var(--lh-lede);
  color: var(--color-ink);
}

.hero__action { margin-block-start: var(--space-xl); }

.hero__aside {
  max-width: var(--measure-lede);
  margin-block-start: var(--space-md);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* ---- The interior-page head --------------------------------------------
   The hero's counterpart on the five pages that do not carry the mark. Same
   type scale as .hero__title so a page opens at the same weight the home page
   does, but single-column and on a wider measure: these headings are page
   names ("Risk and legal notice"), not a sentence, so 17ch would break them
   where the hero's 17ch holds a three-line statement. No .hero__form, no
   diptych, and nothing here references mark.js. */
.page-head { padding-block: var(--space-xl) var(--space-md); }

.page-head__title {
  max-width: 20ch;
  margin-block-end: var(--space-lg);
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-style: normal;
  font-size: var(--text-display);
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  overflow-wrap: anywhere;
  min-width: 0;
  text-wrap: balance;
}

.page-head__lede {
  max-width: var(--measure-lede);
  font-size: var(--text-md);
  line-height: var(--lh-lede);
  color: var(--color-ink);
}

.page-head__lede + .page-head__lede { margin-block-start: var(--space-md); }

/* ---- The folded form. A lit 3D solid, with a flat CSS form beneath it. ---
   The CSS planes below are in the markup from first paint and are what a
   client without WebGL keeps. mark.js adds .mark--live only once the real
   solid is on screen, which is the point the flat version stands down. */

.hero__form {
  display: flex;
  justify-content: flex-end;
  min-width: 0;
}

/* The form is PORTRAIT. Two tall strokes standing in the page's own paper,
   with the gap between them left open — the negative space is part of the
   mark, so there is no plate behind it to fill that gap in. */
.mark {
  position: relative;
  width: 100%;
  max-width: 19rem;
  aspect-ratio: 5 / 7;
}

.mark__face {
  position: absolute;
  inset: 0;
  display: block;
}

/* Every plane declares a flat `background-color` BEFORE its ramp. The OKLCH
   colour function and `in oklch` gradient interpolation shipped at different
   times (Firefox carried the first for ~15 releases before the second), and a
   browser that cannot parse the interpolation drops the whole background-image
   declaration. The fallback keeps the form a legible folded solid instead of
   an empty rounded square. */

/* The lit stroke: the near plane, turned toward the light, with a long
   diagonal taken off its top. It stops well short of the centre line — the
   gap is the point. */
.mark__face--lit {
  background-color: var(--color-lit-top);
  background-image: var(--grad-lit-face);
  clip-path: polygon(15% 4%, 44% 25%, 44% 88%, 15% 88%);
}

/* The far stroke: the second plane, folded away from the light, so its ramp
   runs horizontally and INVERTS. Its diagonal opposes the first one — taken
   off the bottom, not the top — which is what turns the pair. */
.mark__face--wing {
  background-color: var(--color-wing-start);
  background-image: var(--grad-wing);
  clip-path: polygon(56% 12%, 85% 12%, 85% 96%, 56% 75%);
}

/* ---- The lit solid ------------------------------------------------------
   Once the real form renders, the flat planes stand down and the solid is
   lit against the page's own paper. */
.mark--live .mark__face { display: none; }

.mark__canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* ================================ 7 · Split Studio · alternating modules == */

.mod {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-2xl);
  align-items: start;
  padding-block: var(--space-3xl);
}

/* The diptych alternates direction down the page. */
.mod--flip .mod__text  { order: 2; }
.mod--flip .mod__aside { order: 1; }

/* S2 · hanging head — the heading floats in its own negative space above the
   body, no rule, no eyebrow, single column. */
.mod__title {
  margin-block-end: var(--space-lg);
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-style: normal;
  font-size: var(--text-title);
  line-height: var(--lh-title);
  letter-spacing: var(--track-title);
  overflow-wrap: anywhere;
  min-width: 0;
  text-wrap: balance;
}

.mod__body {
  max-width: var(--measure-prose);
  color: var(--color-ink);
}

.mod__body + .mod__body { margin-block-start: var(--space-md); }

/* ---- The single-column band ---------------------------------------------
   .mod is a diptych and needs two children. Several sections on the interior
   pages have one — a heading over prose, or a panel with no heading of its
   own. .band gives them the module's vertical rhythm without the split, and
   borrows .mod__title / .mod__body for their type, which are written without
   a .mod ancestor selector precisely so this works. */
.band { padding-block: var(--space-3xl); }

/* ---- F4 · step sequence ------------------------------------------------- */

.steps {
  display: grid;
  gap: var(--space-xl);
}

.step {
  position: relative;
  padding-inline-start: var(--space-lg);
}

/* The connector: a hairline that fades as it descends — the same falloff
   logic as the planes, applied at 1px. */
.step::before {
  content: "";
  position: absolute;
  inset-block: var(--space-2xs) 0;
  inset-inline-start: 0;
  width: var(--rule-hair);
  background-color: var(--color-rule);
  background-image: var(--grad-rule-fade);
}

.step__ord {
  display: block;
  font-family: var(--font-outlier);
  font-size: var(--text-sm);
  line-height: 1;
  letter-spacing: var(--track-label);
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
}

.step__title {
  margin-block: var(--space-xs) var(--space-2xs);
  font-family: var(--font-display);
  font-weight: var(--weight-serif-sm);
  font-size: var(--text-base);
  line-height: var(--lh-body);
  overflow-wrap: anywhere;
  min-width: 0;
}

.step__body {
  max-width: var(--measure-prose);
  color: var(--color-muted);
}

/* ---- The control panel · the wing ramp used at plane scale -------------- */

.panel {
  padding: var(--space-xl);
  border-radius: var(--radius-panel);
  background-image: var(--grad-wing);
  background-color: var(--color-paper-2);
  color: var(--color-ink);
}

.panel__row + .panel__row {
  margin-block-start: var(--space-lg);
  padding-block-start: var(--space-lg);
  border-block-start: var(--rule-hair) solid var(--color-rule-strong);
}

.panel__term {
  font-weight: var(--weight-display);
  color: var(--color-ink);
  overflow-wrap: anywhere;
  min-width: 0;
}

.panel__desc {
  margin-block-start: var(--space-2xs);
  max-width: var(--measure-prose);
  /* --color-muted is set at L76 precisely so it clears 4.5:1 on the LIGHTEST
     end of this ramp, not just its average. */
  color: var(--color-muted);
}

/* ---- .panel--spec · label-left, value-right, both left-aligned ----------
   The tier sheets on /plans. Here the LABEL is the short side and the value
   is the sentence ("Several — agreed with you before deployment"), so the
   label column is held at a fixed measure and the value takes the rest. Five
   tiers repeat the same five labels in the same order, and the fixed label
   column is what lets a reader scan straight down them. */
.panel--spec .panel__row {
  display: grid;
  grid-template-columns: minmax(0, 14ch) minmax(0, 1fr);
  gap: var(--space-md);
  align-items: baseline;
}

.panel--spec .panel__desc { margin-block-start: 0; }

/* ---- The tier stack ------------------------------------------------------
   Five tiers STACKED, never a five-column comparison grid: a comparison grid
   does not survive a phone, and it turns a spec sheet into a pricing table,
   which is the one thing this page must not be. Each tier is a name, a
   sentence, its sheet, and a quiet action — and every action here is ghost,
   because five filled buttons would spend the page's accent budget five times
   over. The one filled control on /plans is its closing CTA. */
.tiers { padding-block: var(--space-3xl); }

.tier + .tier {
  margin-block-start: var(--space-2xl);
  padding-block-start: var(--space-2xl);
  border-block-start: var(--rule-hair) solid var(--color-rule);
}

.tier__name {
  margin-block-end: var(--space-xs);
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-style: normal;
  font-size: var(--text-title);
  line-height: var(--lh-title);
  letter-spacing: var(--track-title);
  overflow-wrap: anywhere;
  min-width: 0;
}

.tier__lede {
  max-width: var(--measure-prose);
  margin-block-end: var(--space-lg);
  color: var(--color-ink);
}

.tier__note {
  max-width: var(--measure-prose);
  margin-block-start: var(--space-lg);
  color: var(--color-muted);
}

.tier__action { margin-block-start: var(--space-lg); }

/* ---- Qualitative traits · a hairline stack, not a three-icon card grid --- */

.traits {
  display: grid;
  gap: var(--space-lg);
}

.trait + .trait {
  padding-block-start: var(--space-lg);
  border-block-start: var(--rule-hair) solid var(--color-rule);
}

.trait__title {
  margin-block-end: var(--space-2xs);
  font-family: var(--font-display);
  font-weight: var(--weight-serif-sm);
  font-size: var(--text-base);
  line-height: var(--lh-body);
  overflow-wrap: anywhere;
  min-width: 0;
}

.trait__body {
  max-width: var(--measure-prose);
  color: var(--color-muted);
}

/* ============================================== 8 · Questions ============ */

.faq { padding-block: var(--space-3xl); }

.faq__title {
  margin-block-end: var(--space-xl);
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-style: normal;
  font-size: var(--text-title);
  line-height: var(--lh-title);
  letter-spacing: var(--track-title);
  overflow-wrap: anywhere;
  min-width: 0;
}

.faq__list {
  border-block-start: var(--rule-hair) solid var(--color-rule);
  max-width: var(--content-max);
}

.qa { border-block-end: var(--rule-hair) solid var(--color-rule); }

.qa__q {
  display: flex;
  align-items: center;         /* gate 36 */
  justify-content: space-between;
  gap: var(--space-md);
  min-height: var(--target-min);
  padding-block: var(--space-md);
  font-weight: var(--weight-display);
  line-height: var(--lh-body);
  cursor: pointer;
  list-style: none;
  transition: color var(--dur-micro) var(--ease-out);
}

.qa__q::-webkit-details-marker { display: none; }

.qa__q:hover  { color: var(--color-accent); }
.qa__q:active { color: var(--color-accent-deep); }

/* The disclosure indicator is a drawn chevron in currentColor — no icon
   library, no emoji, and it rotates by transform only. */
.qa__q::after {
  content: "";
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-inline-end: var(--rule-hair) solid currentColor;
  border-block-end: var(--rule-hair) solid currentColor;
  transform: translateY(-0.15em) rotate(45deg);
  transition: transform var(--dur-short) var(--ease-out);
}

.qa[open] > .qa__q::after {
  transform: translateY(0.1em) rotate(225deg);
}

.qa__a {
  max-width: var(--measure-prose);
  padding-block-end: var(--space-lg);
  color: var(--color-muted);
}

/* ================================================ 9 · The risk band ====== */

/* The one full-bleed surface. It takes the shadow face at plane scale, which
   is where a measured ramp belongs — a large solid catching light, not a
   glow behind text. */
.risk {
  margin-block-start: var(--space-2xl);
  padding-block: var(--space-3xl);
  background-image: var(--grad-shadow-face);
  background-color: var(--color-paper-2);
  color: var(--color-ink);
  border-block-start: var(--rule-hair) solid var(--color-accent);
}

.risk__title {
  margin-block-end: var(--space-lg);
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-style: normal;
  font-size: var(--text-title);
  line-height: var(--lh-title);
  letter-spacing: var(--track-title);
  color: var(--color-ink);
  overflow-wrap: anywhere;
  min-width: 0;
}

.risk__body {
  max-width: var(--measure-prose);
  font-size: var(--text-base);
  color: var(--color-ink);
}

.risk__body + .risk__body { margin-block-start: var(--space-md); }

/* ============================================== 10 · Closing action ====== */

/* Bottom padding matches the top, as it does in every other section on the
   page. The old --space-4xl foot reserved 144px under the one button and put
   two and a quarter times as much air above the closing hairline as the
   footer puts below it, which read as a section that had run out of content
   rather than as deliberate room. */
.close {
  padding-block: var(--space-3xl);
}

.close__title {
  margin-block-end: var(--space-sm);
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-style: normal;
  font-size: var(--text-title);
  line-height: var(--lh-title);
  letter-spacing: var(--track-title);
  overflow-wrap: anywhere;
  min-width: 0;
}

.close__body {
  max-width: var(--measure-lede);
  font-size: var(--text-md);
  line-height: var(--lh-lede);
  color: var(--color-muted);
}

.close__action { margin-block-start: var(--space-xl); }

/* Same role as .hero__aside, at the other end of the page: the action is a
   `mailto:` and the reader is told so before they click it. */
.close__aside {
  max-width: var(--measure-lede);
  margin-block-start: var(--space-md);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* =============================================== 11 · Ft5 · Statement ==== */

/* The footer is one centred column at every width. `text-align: center`
   is set once here and inherited by every row below — each row then only
   has to centre its own BOX (`margin-inline: auto`), because a capped
   measure with no auto margins parks itself at the inline-start edge and
   centres its text inside that left-hand box, which is not centred at all. */
.foot {
  padding-block: var(--space-2xl) var(--space-xl);
  border-block-start: var(--rule-hair) solid var(--color-rule);
  text-align: center;
}

.foot__line {
  max-width: 34ch;
  margin-inline: auto;
  font-family: var(--font-display);
  font-weight: var(--weight-serif);
  font-style: normal;
  font-size: var(--text-title);
  line-height: var(--lh-title);
  letter-spacing: var(--track-title);
  color: var(--color-ink);
  overflow-wrap: anywhere;
  min-width: 0;
  text-wrap: balance;
}

/* `justify-items: center` rather than the default `stretch`: .foot__note
   carries a --measure-prose cap, and a stretched item that cannot fill its
   column falls back to the start edge — the cap would pin the colophon
   left. Centred items are sized to their content and sit on the axis. */
.foot__meta {
  display: grid;
  justify-items: center;
  gap: var(--space-sm);
  margin-block-start: var(--space-xl);
  padding-block-start: var(--space-md);
  border-block-start: var(--rule-hair) solid var(--color-rule);
}

/* The same lockup as the nav, one step down. It is the last thing on the
   page and it repeats the first thing on it, so it has to be the identical
   treatment — same face, same tracking, same colour — at a smaller size. */
.foot__mark {
  font-family: var(--font-mark);
  font-weight: var(--weight-mark);
  font-synthesis: none;
  font-size: var(--text-sm);
  line-height: 1;
  letter-spacing: var(--track-mark-sm);
  color: var(--color-accent);
}

.foot__note {
  max-width: var(--measure-prose);
  font-size: var(--text-sm);
  line-height: var(--lh-body);
  color: var(--color-muted);
}

/* ---- The sibling-page rail ----------------------------------------------
   The footer is the only place every page of the site is listed, `/legal`
   included — the header bar holds five items and stops there. Links, not
   buttons: nothing here is filled, so the row costs the page no accent
   budget beyond the underline each link already carries.

   Targets are held at --target-min even though the type is --text-sm, so a
   wrapped row on a phone is still five separate 44px destinations rather
   than one dense line of near-misses.

   Centred by `justify-content`, not by `text-align` — the links are
   inline-flex flex ITEMS, so the inherited text-align only centres the
   glyphs inside each link, never the row. `flex-wrap: wrap` is kept: the
   row wraps to as many lines as the width needs and each line is centred
   on its own, so five items can never force a horizontal scroll. */
.foot__nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--space-xs) var(--space-lg);
  margin-block-start: var(--space-xl);
  padding-block-start: var(--space-lg);
  border-block-start: var(--rule-hair) solid var(--color-rule);
  font-size: var(--text-sm);
  line-height: var(--lh-body);
}

.foot__nav .link {
  display: inline-flex;
  align-items: center;
  min-height: var(--target-min);
}

/* ---- The formal small print --------------------------------------------
   Subordinate by POSITION and SIZE only. It sits last, under its own
   hairline, at 13px — and that is the whole of the de-emphasis. It is not
   dimmed (--color-muted measures 9.5:1 on paper, twice the 4.5:1 floor;
   WCAG exempts no text this small from that floor), not collapsed behind a
   disclosure, not truncated, and not hidden at any breakpoint. Legal text a
   reader cannot read has not been disclosed.

   Measure is capped at --measure-legal rather than left to run the full
   72rem shell, and line-height stays at --lh-body (1.6) so four dense
   paragraphs remain scannable at this size. */
.foot__legal {
  margin-block-start: var(--space-xl);
  padding-block-start: var(--space-lg);
  border-block-start: var(--rule-hair) solid var(--color-rule);
}

.foot__legal-title {
  margin-block-end: var(--space-md);
  font-family: var(--font-display);
  font-weight: var(--weight-serif-sm);
  font-size: var(--text-sm);
  line-height: var(--lh-body);
  font-style: normal;
  color: var(--color-ink);
  overflow-wrap: anywhere;
  min-width: 0;
}

/* The centring lives HERE, on the base rule, not on the `.foot__nav + `
   variant below: every .foot__small is a block child of .shell, so the
   capped measure would park it at the inline-start edge and the inherited
   text-align would centre its text inside a left-hugging box. Carrying the
   auto margin on the base means centring does not depend on the paragraph
   staying adjacent to the sibling rail — reorder the footer and it holds. */
.foot__small {
  max-width: var(--measure-legal);
  margin-inline: auto;
  font-size: var(--text-xs);
  line-height: var(--lh-body);
  color: var(--color-muted);
  overflow-wrap: anywhere;
  min-width: 0;
}

.foot__small + .foot__small { margin-block-start: var(--space-sm); }

/* In the shared footer the condensed risk line follows the page rail with no
   .foot__legal wrapper to carry the gap, so the gap is carried here instead.
   This line is the site's live risk disclosure on every page — it is not
   decoration and it is not removed to tidy the footer.

   Its measure tightens from --measure-legal (74ch) to --measure-prose (62ch)
   here, and here only. Centred text is read by finding the start of each
   line, which moves on every line, so the return sweep costs more than it
   does in a flush-left column and a long measure hurts sooner — 62ch keeps
   this two-sentence disclosure inside the ~65ch a centred block can carry.
   The base .foot__small keeps its 74ch for the .foot__legal block, which no
   page currently renders.

   Measure and gap only. The auto margin that centres the box is on the base
   rule, so this variant states exactly the two things that are specific to
   the shared risk line and nothing that would be restated in two places. */
.foot__nav + .foot__small {
  max-width: var(--measure-prose);
  margin-block-start: var(--space-lg);
}

.foot__copy {
  max-width: var(--measure-legal);
  margin-block-start: var(--space-lg);
  margin-inline: auto;
  font-size: var(--text-xs);
  line-height: var(--lh-body);
  color: var(--color-muted);
}

/* ---- No masthead grid --------------------------------------------------
   A wide-viewport two-track grid used to live here: track 1 held the
   statement on a left rail, track 2 pinned the colophon to the shell's right
   edge with `justify-items: end` / `text-align: end`, and the four rows
   below it spanned `1 / -1`. It existed to answer an older defect — the
   footer running down one left-hand rail with the right 45% of the shell
   empty — and it answered it by splitting the row left/right.

   The footer is centred now, so both halves of that grid contradict it: a
   1fr/auto split cannot hold a centred column, and an end-aligned colophon
   is the one-sided alignment the centring removes. It is deleted rather
   than overridden, so the footer has ONE layout at every width and no rule
   is stated in one place and undone in another. The block-flow base rules
   above are that layout; .foot__meta keeps the hairline the grid used to
   strip from it, which is what closes the row above the sibling rail. */

/* ============================================ 12 · Narrow viewports ====== */

@media (max-width: 60rem) {
  /* The centre cluster leaves the bar; the wordmark and the one action stay.
     Every destination it held is still reachable by scrolling. */
  .nav__centre { display: none; }

  .nav__inner { grid-template-columns: minmax(0, 1fr) auto; }

  /* Both diptychs stack. The form follows the words rather than leading. */
  .hero,
  .mod {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-xl);
  }

  .hero { padding-block: var(--space-lg) var(--space-xl); }

  .hero__title,
  .hero__lede { max-width: none; }

  .hero__form { justify-content: flex-start; }

  /* Order is reset so the reading sequence is identical on every viewport. */
  .mod--flip .mod__text  { order: 0; }
  .mod--flip .mod__aside { order: 0; }

  .mod { padding-block: var(--space-2xl); }

  /* The interior head loses the hero's measure cap for the same reason the
     hero does: at this width the column IS the measure. */
  .page-head { padding-block: var(--space-lg) var(--space-md); }

  .page-head__title,
  .page-head__lede { max-width: none; }

  .band,
  .tiers { padding-block: var(--space-2xl); }

  .risk { padding-block: var(--space-2xl); }

  .close { padding-block: var(--space-2xl) var(--space-3xl); }
}

@media (max-width: 30rem) {
  /* The form never dominates a phone and never causes a horizontal scroll:
     it is width-capped, not viewport-sized. */
  .mark { max-width: 15rem; }

  .panel { padding: var(--space-lg); }

  /* On the tier sheets a 14ch label column leaves too little for the value at
     this width, so label and value stack. */
  .panel--spec .panel__row { grid-template-columns: minmax(0, 1fr); }

  .panel--spec .panel__desc { margin-block-start: var(--space-2xs); }

  /* At 320px the bar holds a 10-character wordmark and a 14-character action,
     both nowrap. The unicase face sets those ten characters far wider than
     the mono it replaced, so the step down goes one further than --text-sm:
     measured at 320px, 14px left the wordmark and the action about a pixel
     apart, which is not a margin, it is luck. 13px restores real separation
     without taking the lockup below the size it stays legible at (gate 49). */
  .nav__brand { font-size: var(--text-mark-xs); }

  .cta--sm { padding-inline: var(--space-sm); }
}

/* ============================================ 13 · Reduced motion ======== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }

  .reveal {
    opacity: 1;
    transform: none;
    animation: none;
  }

  .cta:active { transform: none; }
}

/* ============================================ 14 · /plans (additive) ==== */

/* The "True of every tier" panel sits directly under its own intro sentence,
   so it needs the gap .band does not otherwise supply between .mod__body and
   whatever follows it. */
.constants__panel { margin-block-start: var(--space-xl); }

/* ---- The run-length spine -------------------------------------------------
   A second, decorative reading of the one row the eye should track down the
   stack: Run length. It restates data the .panel--spec row beside it already
   states in text, so it is aria-hidden rather than doubly announced. Flat
   fills only, never a gradient — the page's one gradient surface is spent on
   the Diamond card below, and spending a second one here would blur which
   surface is the anchor. */
.tier__spine {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-block: var(--space-sm) var(--space-lg);
}

.tier__spine-track {
  flex: 1 1 auto;
  min-width: 0;
  height: calc(var(--rule-hair) * 3);
  border-radius: var(--radius-control);
  background-color: var(--color-rule);
  overflow: hidden;
}

.tier__spine-fill {
  display: block;
  height: 100%;
  width: var(--fill, 0%);
  background-color: var(--color-accent);
  border-radius: var(--radius-control);
}

.tier--anchor .tier__spine-track { background-color: var(--color-rule-strong); }

/* Geist Mono's second slot (the first is .step__ord) — the same numeric-label
   treatment, reused rather than a third face introduced for one column of
   digits. */
.tier__spine-num {
  flex: none;
  min-width: 2.4ch;
  font-family: var(--font-outlier);
  font-size: var(--text-sm);
  line-height: 1;
  letter-spacing: var(--track-label);
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
  text-align: end;
}

/* ---- The Diamond anchor ---------------------------------------------------
   The one lit/gradient surface this page spends beyond what .plans already
   spends: the shadow face, at card scale, once. Text on it stays --color-ink
   / --color-muted, both measured against this exact ramp in tokens.css
   (8.2:1 / 4.8:1) — no colour here is a guess. No filled CTA is added: the
   card carries the presence, so the action stays ghost like every other
   tier's and the page's accent budget still closes on one filled control. */
.tier--anchor {
  padding: var(--space-xl);
  border-radius: var(--radius-panel);
  background-image: var(--grad-shadow-face);
  background-color: var(--color-paper-2);
}

.tier__badge {
  margin-block-end: var(--space-xs);
  color: var(--color-accent);
  font-family: var(--font-outlier);
  font-size: var(--text-sm);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

@media (max-width: 30rem) {
  /* The two-column spine (track + numeral) still fits at 320px on its own,
     but the anchor card's padding is cut the same way .panel's already is at
     this breakpoint. */
  .tier--anchor { padding: var(--space-lg); }
}

/* ============================================ 15 · /pricing (additive) === */

/* Everything below is scoped to a class that exists ONLY on /pricing
   (.tier-grid, .page-head--centred, or a __ element name /plans does not
   use). /plans consumes .tier, .tier--anchor, .tier__spine* and .tier__badge
   from section 14 above and must keep rendering exactly as it does today, so
   nothing here reaches an unscoped .tier selector. */

/* ---- The centred page head ----------------------------------------------
   The tier grid is a symmetrical composition, so the head that introduces it
   is centred rather than left-ragged. Only the measure caps move; the type
   steps are the same .page-head steps every other interior page uses. */
.page-head--centred { text-align: center; }

.page-head--centred .page-head__title,
.page-head--centred .page-head__lede {
  margin-inline: auto;
}

/* A small pill above the H1. It takes the mono label treatment already used
   by .tier__badge and .step__ord rather than introducing a fourth face, and
   its border is the boundary hairline, not the accent — the accent on this
   page is spent on the five numerals and the one filled CTA. */
.page-head__eyebrow {
  display: inline-block;
  margin-block-end: var(--space-md);
  padding: var(--space-2xs) var(--space-sm);
  border: var(--rule-hair) solid var(--color-rule-strong);
  border-radius: var(--radius-control);
  font-family: var(--font-outlier);
  font-size: var(--text-sm);
  line-height: 1;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--color-accent);
}

/* ---- The 3 + 2 card grid -------------------------------------------------
   Six tracks, not three, and every card spans two of them. Three columns of
   equal width fall out of that exactly as they would from `repeat(3, 1fr)`,
   but the two-card second row can then start at track 2 and track 4 — so it
   sits CENTRED under the first row on the SAME track width, instead of two
   half-page cards. Cards in a row are equal height because a grid row
   stretches its items; nothing here measures or scripts a height. */
.tier-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-lg);
}

.tier-grid > .tier { grid-column: span 2; }
.tier-grid > .tier:nth-child(4) { grid-column: 2 / span 2; }
.tier-grid > .tier:nth-child(5) { grid-column: 4 / span 2; }

/* Section 7's stack rule puts a hairline and 64px between consecutive tiers.
   In a grid the gap does that job, and the hairline would draw across the
   inside of a card, so it is unwound here rather than edited there.

   The block-start PADDING is deliberately NOT stated here. This selector is
   one class heavier than `.tier-grid > .tier` above, so any value written on
   this line wins over the card's own padding for every card except the first
   — and the first card is then the only one padded differently. Measured at
   1440 with `padding-block-start: 0` on this line: Ruby 24px of top padding,
   Silver/Gold/Diamond/Platinum 0px, which lifted four of the five numerals
   25px above Ruby's and broke the shared baseline across a row. Section 7's
   `.tier + .tier` value is already overridden by `.tier-grid > .tier`'s own
   `padding` shorthand, which is the same specificity and comes later, so
   nothing needs to be unwound here — leaving the line out is the fix, and it
   tracks whatever block padding the card rule carries. */
.tier-grid > .tier + .tier {
  margin-block-start: 0;
  border-block-start: 0;
}

/* Column flow: name, rule, numeral, sheet, top to bottom. A grid row stretches
   its items, so cards in a row are equal height — the slack from a card with
   fewer rows below its numeral falls at its FOOT, which is empty, rather than
   being injected as a hole between the numeral and the sheet.

   The block padding is a step heavier than the inline padding, and heavier
   than it was when every card carried a description. Four of the five cards
   are now name, rule, numeral, qualifier and nothing else; against a row
   height set by the tallest card, 24px of top padding left that short sequence
   sitting on the card's top edge. 40px seats it. It is the same step the
   anchor card already pads with, so the anchor still reads as heavier through
   its inline padding, not through a value invented here. */
.tier-grid > .tier {
  display: flex;
  flex-direction: column;
  padding: var(--space-xl) var(--space-lg);
  border: var(--rule-hair) solid var(--color-rule);
  border-radius: var(--radius-panel);
}

/* With the description gone the rule sits directly under the name, so the gap
   above it has to be clearly smaller than the gap below it — otherwise the
   hairline reads as underlining the name rather than as separating the name
   from the figure. 16 above, 24 below (.tier__rule's own foot). */
.tier-grid .tier__name {
  display: flex;
  align-items: baseline;
  gap: var(--space-xs);
  margin-block-end: var(--space-md);
  font-size: var(--text-title);
}

.tier__dot {
  flex: none;
  color: var(--color-accent);
  font-size: var(--text-md);
  line-height: 1;
}

/* One card carries a description now — Platinum, because running several
   strategies at once is a real difference between the tiers rather than a
   restatement of their order. The two-line reserve this rule used to hold is
   gone with the other four: it existed to land the numerals of a row on one
   baseline, and with a single description in the grid it would only push
   Platinum's own numeral down against nothing. */
.tier-grid .tier__lede {
  margin-block-end: var(--space-md);
  max-width: none;
  color: var(--color-muted);
}

.tier__rule {
  margin-block: 0 var(--space-lg);
  border: 0;
  border-block-start: var(--rule-hair) solid var(--color-rule-strong);
}

/* ---- The hero numeral ----------------------------------------------------
   Structurally where a price sits on a pricing card, which is why it takes
   the display step and the display tracking. Accent on paper is 12.6:1 and
   accent on the lightest point of the shadow face (the Gold card) is above
   the 4.5:1 floor — both measured in tokens.css, neither guessed here. */
.tier__roi {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-display);
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
}

/* The qualifier is part of the figure, not a footnote to it: it sits directly
   under the numeral on every card, and the page repeats it once more in full
   below the grid. */
.tier__roi-note {
  margin-block: var(--space-2xs) var(--space-lg);
  font-size: var(--text-sm);
  line-height: var(--lh-body);
  color: var(--color-muted);
}

/* That 24px foot spaces the sheet below the qualifier. On the four cards that
   end at the qualifier there is no sheet to space, and the margin only lifts
   the card's own bottom padding off the last line — so it is dropped when the
   qualifier is the card's last element. The slack in a stretched card still
   falls at its foot; this just stops the foot carrying a stray gap. */
.tier-grid .tier__roi-note:last-child { margin-block-end: 0; }

/* ---- Spec rows inside a card --------------------------------------------
   The sheet drops .panel's own wing surface here. A card is already a
   surface; a second gradient plane nested inside it at this scale reads as
   mud rather than as depth, and on the Gold card the two ramps sit at nearly
   the same lightness. The rows keep their hairlines, which is what carried
   the structure anyway.

   The sheet sits DIRECTLY under the numeral's qualifier, on that qualifier's
   own --space-lg foot — not floated to the bottom of the card. A card with two
   spec rows beside a card with four would otherwise open a hole in its middle
   the height of two rows, which reads as a rendering fault rather than as
   space. */
.tier-grid .panel--spec {
  margin-block-start: 0;
  padding: 0;
  background-image: none;
  background-color: transparent;
  border-radius: 0;
}

.tier-grid .panel--spec .panel__row {
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: var(--space-sm);
  font-size: var(--text-sm);
}

.tier-grid .panel--spec .panel__desc { text-align: end; }

.tier-grid .panel--spec .panel__row + .panel__row {
  margin-block-start: var(--space-sm);
  padding-block-start: var(--space-sm);
}

/* Only one card still carries a sheet, so that sheet has to say why it is
   there — unlabelled, it reads as a stray table left on the last card rather
   than as the two things this one tier does differently. Muted, not accent:
   the accent on this page is already spent on the five numerals, the eyebrow,
   the dots, the badge and the one filled CTA. */
.tier-grid .tier__spec-label {
  margin-block-end: var(--space-sm);
  font-family: var(--font-outlier);
  font-size: var(--text-sm);
  line-height: 1;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--color-muted);
}

/* An unfinished value, and deliberately legible as one: the tier's run length
   has not been set yet, so the card says that in place of a number rather
   than carrying a plausible-looking guess. */
.spec__todo {
  font-family: var(--font-outlier);
  letter-spacing: var(--track-label);
  color: var(--color-accent);
}

/* ---- The Gold anchor -----------------------------------------------------
   The gradient surface itself comes from .tier--anchor in section 14,
   unchanged. What is added here is the raise: heavier padding (already on
   that rule) plus a negative block margin, so the card breaks the top and
   bottom edges of its row the way a featured card does. It is geometry, not
   a shadow — the page has no shadow token and does not need one. */
.tier-grid > .tier.tier--anchor {
  /* Restated because `.tier-grid > .tier` above outranks the single-class
     `.tier--anchor` rule in section 14 that sets it. Doubled up on .tier so
     it also outranks `.tier-grid > .tier + .tier`, which is one class heavier
     and would otherwise take both the padding and the margin back off the
     anchor — Gold is the third card, so that sibling selector matches it. */
  padding: var(--space-xl);
  margin-block: calc(var(--space-md) * -1);
  border-color: var(--color-accent);
}

.tier-grid > .tier--anchor .tier__rule,
.tier-grid > .tier--anchor .panel--spec .panel__row + .panel__row {
  border-block-start-color: var(--color-rule-strong);
}

/* ---- The compliance line -------------------------------------------------
   Immediately below the grid, in the same muted step as the per-card
   qualifier. It is not in the footer and not behind a link, because it
   qualifies the five figures a metre above it. */
.tiers__legal {
  margin-block-start: var(--space-xl);
  font-size: var(--text-sm);
  line-height: var(--lh-body);
  text-align: center;
  color: var(--color-muted);
}

/* ---- Narrow viewports ----------------------------------------------------
   The same two widths section 12 already turns on. Four tracks, cards still
   spanning two: two per row, and the fifth centred under them on the same
   track width by the same trick the second row uses above. */
@media (max-width: 60rem) {
  .tier-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  .tier-grid > .tier:nth-child(4) { grid-column: auto / span 2; }
  .tier-grid > .tier:nth-child(5) { grid-column: 2 / span 2; }

  /* The raise is a three-column composition. In two columns the Gold card is
     beside a single neighbour, so it sits in the row rather than above it. */
  .tier-grid > .tier.tier--anchor { margin-block: 0; }
}

@media (max-width: 30rem) {
  .tier-grid { grid-template-columns: minmax(0, 1fr); }

  .tier-grid > .tier,
  .tier-grid > .tier:nth-child(4),
  .tier-grid > .tier:nth-child(5) { grid-column: auto; }

  /* One column means one card per row, so no card is stretched to a taller
     neighbour and none of them has slack to seat their content against. The
     heavier block padding goes back to the inline step — the same cut the
     anchor card's padding already takes at this width.

     The anchor half is doubled on .tier for the same reason the Gold rule
     above is: at one class it no longer outranks that rule, and Gold would
     keep 40px of padding at 320px while every card around it took 24. */
  .tier-grid > .tier,
  .tier-grid > .tier.tier--anchor { padding: var(--space-lg); }

  /* At 320px a right-aligned value column leaves the longer sentences three
     words wide, so label and value stack, exactly as section 12 already stacks
     them for the /plans sheets. */
  .tier-grid .panel--spec .panel__row { grid-template-columns: minmax(0, 1fr); }

  .tier-grid .panel--spec .panel__desc {
    margin-block-start: var(--space-2xs);
    text-align: start;
  }
}

/* ---- One composition, not a phone with margins --------------------------
   Everything below hangs off `.page-pricing`, a class that exists only on
   this page's <body>. .band, .close, .constants, .mod__title and .mod__body
   are all shared with /plans, /control and /legal, so none of them may be
   touched unscoped — the ancestor is what keeps this page's alignment off
   the other six.

   The head and the card grid are symmetrical and centred. Left-ragged prose
   under them at 72rem left the lower third sitting in a narrow column with a
   third of the width empty beside it, which read as the narrow layout with
   the margins widened rather than as a composition. Centring the section
   heads and their prose finishes the axis the head starts. The measure caps
   are untouched (--measure-prose is 62ch, inside a comfortable measure), so
   this moves where the column sits, not how wide it is. */
.page-pricing .band,
.page-pricing .close { text-align: center; }

.page-pricing .band .mod__title,
.page-pricing .band .mod__body,
.page-pricing .close__title,
.page-pricing .close__body,
.page-pricing .close__aside { margin-inline: auto; }

/* The data band inside the centred section stays left-read: a label and its
   value are a table, and a centred table has no column to scan down. */
.page-pricing .constants__panel { text-align: start; }

/* ---- The constants band across the width --------------------------------
   Six label/value pairs stacked in a 14ch label column used about 40% of a
   72rem panel and still wrapped "Minimum withdrawal threshold" onto three
   lines with empty panel beside it. The pairs go ACROSS instead: each label
   then owns a whole column's width, and the rule that separated them
   horizontally turns ninety degrees to separate them vertically.

   Two steps, not one, because the longest label sets the floor and the COUNT
   sets the shape. There are six pairs, so three across x two rows is the only
   arrangement that fills BOTH rows: four across left the entire right half of
   the second row empty, and six across would put every label in a 150px
   column.

   The widths are measured in a browser at each width, not estimated.
   "Minimum withdrawal threshold" is 242px of text. A third of the panel's
   content box is 268px of usable cell at 1440 and 245px at 60rem (the cells
   that are not first in a row give 24px of that back to the hairline inset),
   so the label holds on ONE line from about 1030px up and takes TWO between
   60rem and there. Two is the floor this section exists to hold: it never
   reaches three at any width where the band is in columns. Below 60rem a
   third of the panel drops under 200px, so 40rem to 60rem takes two across
   (236px a cell at 40rem, the same two lines at worst), and below 40rem the
   stacked form is untouched — three columns of this at phone width is what
   the stack was for. */
@media (min-width: 40rem) {
  .page-pricing .constants__panel {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-lg);
    align-items: start;
  }

  /* Label over value: the 14ch label column is what forced the wrap, and in a
     cell this narrow a right-hand value column would only rebuild it. */
  .page-pricing .constants__panel .panel__row {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
    margin-block-start: 0;
    padding-block-start: 0;
    border-block-start: 0;
  }

  /* The hairline goes between COLUMNS, so it is carried by every cell that is
     not first in its row — the even ones in a 2 x 2. */
  .page-pricing .constants__panel .panel__row:nth-child(even) {
    padding-inline-start: var(--space-lg);
    border-inline-start: var(--rule-hair) solid var(--color-rule-strong);
  }

  .page-pricing .constants__panel .panel__desc {
    margin-block-start: var(--space-2xs);
  }
}

@media (min-width: 60rem) {
  /* Three across, two rows — the shape the six pairs actually want. Both rows
     are full and the band uses its width evenly. At this exact width the
     longest label takes two lines in a 221px cell; the cell passes the 242px
     the label needs at about 1030px and is on one line from there up. */
  .page-pricing .constants__panel {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  /* The hairline still separates COLUMNS, so it is carried by every cell that
     is not first in its row — in a 3 x 2 that is everything except the 1st
     and the 4th. Both halves are stated, because the 2 x 2 rule above is
     still in force at this width and would otherwise leave its hairline on
     the 4th cell, which now opens a row. */
  .page-pricing .constants__panel .panel__row:nth-child(3n + 1) {
    padding-inline-start: 0;
    border-inline-start: 0;
  }

  .page-pricing .constants__panel .panel__row:not(:nth-child(3n + 1)) {
    padding-inline-start: var(--space-lg);
    border-inline-start: var(--rule-hair) solid var(--color-rule-strong);
  }
}

/* ====================================== 16 · /pricing · five across ====== */

/* SCOPING — load-bearing, and the reason this is a section of its own rather
   than edits to section 15.

   Section 15's opening note says .tier-grid "exists ONLY on /pricing". That is
   no longer true: /plans currently serves the PREVIOUS build of this same page
   and carries the identical hooks — same `<body class="page-pricing">`, same
   .tier-grid, .tier--anchor, .tier__badge, .tier__dot, .tier__roi. Measured:
   every one of those strings returns a hit count of 1 (5 for the per-card
   ones) in BOTH plans/index.html and pricing/index.html. Nothing in the DOM
   separates the two pages, so a bare `.tier-grid` rule — or a `.page-pricing`
   one — reaches both, and editing section 15 in place would silently
   re-lay-out the live page.

   Everything below therefore hangs off `.page-pricing--row5`, a modifier
   carried only by /pricing's <body>. Section 15 above is left byte-for-byte
   as it was: it is what /plans still renders from, and it must keep doing so
   until the two are deliberately swapped. */

/* ---- Five across, one row ------------------------------------------------
   Five equal tracks, no spans, no second row. The 3 + 2 arrangement this
   replaces was right when a card carried spec rows; a card is now a name, a
   rule, a figure and its qualifier, so 3 + 2 put three short cards above two
   and offset the second row into the first row's gaps. Five items divide into
   two or three columns only by orphaning one, which is the same defect again
   — so there is no intermediate step: five across, or one column.

   Gold is the third of five, which lands the anchor on the exact centre of
   the row. Every card holds the same four elements now, so a stretched grid
   row makes all five equal height without anything here measuring one. */
.page-pricing--row5 .tier-grid {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}

/* Section 15 places cards 4 and 5 by hand to centre its second row. There is
   no second row here, so all five go back to auto flow. Stated at one class
   heavier than those rules and outside a media query, because section 15
   repeats them inside its 60rem block as well. */
.page-pricing--row5 .tier-grid > .tier,
.page-pricing--row5 .tier-grid > .tier:nth-child(4),
.page-pricing--row5 .tier-grid > .tier:nth-child(5) { grid-column: auto; }

/* ---- What fits in a fifth of the column ----------------------------------
   Widths below are advance widths read out of the shipped woff2 files at the
   weights and letter-spacing this page actually sets — Montserrat instanced
   to wght 700 through its HVAR table, not the file's wght 100 default — not
   estimates. The two that bind are the WIDEST name, "Diamond" at 4.725em
   (not "Platinum", 4.691em), and the WIDEST figure, "140%" at 2.549em.

   The card's content box is the constraint. A fifth of the content column,
   less the gaps, less the card's own inline padding:

     viewport 960 (the 60rem floor)  card 153.6px   content 121.6px
     viewport 1440                   card 182.4px   content 150.4px
     viewport 1600+ (gutter capped)  card 179.2px   content 147.2px

   960 is the worst case, and 1440 is wider than 1600 because --page-gutter is
   still climbing while --content-max has already capped. */

/* Inline padding drops a step, 24 to 16: at 960 a 24px inset spent a third of
   the card on its own margins and left 105.6px for a figure the type scale
   wanted 143px for. Block padding stays at 40 — it is what seats four short
   lines against the row height. The anchor takes the SAME padding, so all
   five cards share one measure and the qualifier wraps identically on each;
   Gold reads as featured through its gradient, its accent border, its badge
   and its raise, not through an 8px inset nobody can see. */
.page-pricing--row5 .tier-grid > .tier,
.page-pricing--row5 .tier-grid > .tier.tier--anchor {
  padding: var(--space-xl) var(--space-md);
}

/* The raise, restated at one class heavier than section 15 sets it. Section 15
   turns it off inside `max-width: 60rem`, which still matches at exactly 960 —
   the width at which this row is specified to be five across and Gold to be
   featured. Measured at 960 before this rule: all five cards y=970, h=260.8,
   the anchor flat in the row while every width above it carried the raise.
   The stack below turns it off again on its own line. */
.page-pricing--row5 .tier-grid > .tier.tier--anchor {
  margin-block: calc(var(--space-md) * -1);
}

/* 20px floor / 26px cap. "Diamond" is 94.5px at 20 and 122.9px at 26; the dot
   and its gap add 13px. Against 121.6px of content at 960 that leaves 14.1px,
   and against 147.2px at 1600 it leaves 11.3px. The unclamped --text-title
   caps at 36px, where "Diamond" alone is 170.1px — a third wider than the
   card — and .tier__name's `overflow-wrap: anywhere` would have broken it
   mid-word rather than overflowed visibly. */
.page-pricing--row5 .tier-grid .tier__name {
  font-size: clamp(1.25rem, 1.6vw + 0.05rem, 1.625rem);
}

/* The dot is sized off the name it sits beside, not off the body step. At
   --text-md it was a 20px bullet against a 20px name — the same height as the
   word — because that value was set when the name was 36px. */
.page-pricing--row5 .tier-grid .tier__dot { font-size: var(--text-sm); }

/* 34px floor / 48px cap, on the same fluid shape as --text-display but
   pitched at a fifth of the column rather than the full width. "140%" is
   86.7px at 34 and 122.4px at 48, inside 121.6px at 960 and 147.2px at 1600.
   --text-display itself resolves to 52.5px at 960, where "140%" is 133.8px
   and overflows a 121.6px card. */
.page-pricing--row5 .tier-grid .tier__roi {
  font-size: clamp(2.125rem, 2.6vw + 0.1rem, 3rem);
}

/* ---- The Platinum footnote -----------------------------------------------
   The two things that are true only of Platinum used to sit inside its card,
   which made that card taller than the other four and was the reason the row
   could not be equal-height. They are the same two facts, moved below the
   grid: one sentence, in the qualifier's own muted step, directly above the
   compliance line it now shares a block with. */
.page-pricing--row5 .tiers__note {
  max-width: var(--measure-legal);
  margin-block-start: var(--space-xl);
  margin-inline: auto;
  font-size: var(--text-sm);
  line-height: var(--lh-body);
  text-align: center;
  color: var(--color-muted);
}

/* The compliance line carries its own 40px off the grid. With the footnote
   between them that 40px would open a hole inside what should read as one
   two-sentence block, so it closes to 16 when the footnote precedes it. */
.page-pricing--row5 .tiers__note + .tiers__legal {
  margin-block-start: var(--space-md);
}

/* ---- One column, and no arrangement in between ---------------------------
   Below 60rem the row becomes a stack. Section 15's 4-track / 2-across step
   is overridden rather than inherited: two columns of five orphans the fifth
   card, which is the exact fault the five-across row exists to remove.

   Every type step written above is a function of a fifth of the column, so
   all of them go back to the page's own steps here, where a card is the full
   width and the constraint is gone.

   59.9375rem, not 60rem — 959px, one pixel under. Section 15's blocks pair
   `max-width: 60rem` with `min-width: 60rem`, which both match at exactly
   960 and let the later one settle it. That is harmless for a hairline and a
   padding value; it is not harmless here, where it stacked all five cards at
   the exact width the row is specified to hold them. Measured at 960 with
   `max-width: 60rem` on this line: grid-template-columns resolved to a single
   864px track. The row now holds from 960 up, which is what >= 60rem means. */
@media (max-width: 59.9375rem) {
  .page-pricing--row5 .tier-grid { grid-template-columns: minmax(0, 1fr); }

  .page-pricing--row5 .tier-grid > .tier,
  .page-pricing--row5 .tier-grid > .tier.tier--anchor {
    padding: var(--space-xl) var(--space-lg);
  }

  /* The raise is a row composition. In a stack the anchor has no row to break
     out of, and a negative block margin would only close the gap to the cards
     above and below it. */
  .page-pricing--row5 .tier-grid > .tier.tier--anchor { margin-block: 0; }

  .page-pricing--row5 .tier-grid .tier__name { font-size: var(--text-title); }
  .page-pricing--row5 .tier-grid .tier__dot  { font-size: var(--text-md); }
  .page-pricing--row5 .tier-grid .tier__roi  { font-size: var(--text-display); }
}

@media (max-width: 30rem) {
  /* Restated at this width for the same reason section 15 restates it: the
     scoped padding above is a class heavier than the rule there that cuts a
     card back to 24px at phone width, and would otherwise hold 40px of block
     padding on a 320px card. */
  .page-pricing--row5 .tier-grid > .tier,
  .page-pricing--row5 .tier-grid > .tier.tier--anchor {
    padding: var(--space-lg);
  }
}

/* ---- The vertical rhythm -------------------------------------------------
   Adjacent sections each carry --space-3xl, so every seam on this page was
   96 + 96 = 192px. That was tuned for a page whose cards carried spec sheets;
   with four lines to a card the seams read as the page having run out rather
   than as room. One step down to --space-2xl takes every seam to 128px — a
   third off — and the head-to-band seam from 112 to 80.

   Only above 60rem: below it, section 12 already steps these sections down to
   --space-2xl, so this would be a no-op there and a regression on .close,
   whose deliberate --space-3xl foot against the footer is section 12's, not
   this page's, to set. The footer's own padding is untouched at every width. */
@media (min-width: 60rem) {
  .page-pricing--row5 .band,
  .page-pricing--row5 .tiers,
  .page-pricing--row5 .close { padding-block: var(--space-2xl); }
}

/* =============================== 17 · /pricing · checklist cards ========= */

/* SCOPING — load-bearing, and the reason this is a new section rather than
   edits to section 16.

   Section 16 opens by asserting that `.page-pricing--row5` is "a modifier
   carried only by /pricing's <body>". That premise has since DRIFTED and is
   now false. Measured 2026-08-10 with /usr/bin/grep (not the shell's ugrep
   shim, which honours .gitignore):

     /usr/bin/grep -n "page-pricing--row5" plans/index.html pricing/index.html
       plans/index.html:14:<body class="page-pricing page-pricing--row5">
       pricing/index.html:14:<body class="page-pricing page-pricing--row5">

   The two files are separate inodes (1313260 / 1312924) but byte-identical
   (md5 644a001bb3b470879f8a8db09d005546), because /plans currently serves the
   previous build of this page. So section 16 styles BOTH pages, and editing
   it in place would silently re-lay-out the LIVE /plans page.

   The fix is a hook, not a rewrite. /pricing's <body> DROPS
   `page-pricing--row5` and gains `page-pricing--cards`; /plans keeps `--row5`
   and section 16 is left byte-for-byte, so /plans renders exactly as it does
   today. Dropping the modifier also drops /pricing back onto section 15's
   6-track 3 + 2 grid and its full type steps — which is what these taller
   cards want anyway — so this section only has to state what is genuinely
   new: the checklist, the per-card action, and the 3-or-1 column rule.

   Every selector below carries `.page-pricing--cards`, which exists on
   /pricing's <body> and nowhere else in the site. */

/* ---- Three across, or one column, and nothing between --------------------
   Cards now carry a subtitle, a six-item checklist and an action, so five
   across no longer holds a readable measure. Section 15's base already lays
   six tracks with every card spanning two — three equal columns, with cards 4
   and 5 starting at tracks 2 and 4 so the second row sits CENTRED under the
   first on the SAME track width. That is exactly the 3 + 2 asked for, so it
   is inherited rather than restated.

   What IS restated is the >= 60rem case. Section 15 pairs `max-width: 60rem`
   with the base rules, and `max-width: 60rem` still matches at exactly 960px —
   the width at which this grid is specified to be three across. At one class
   heavier these win there. Above 960 they agree with section 15's base and are
   a no-op. */
@media (min-width: 60rem) {
  .page-pricing--cards .tier-grid {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }

  .page-pricing--cards .tier-grid > .tier { grid-column: span 2; }
  .page-pricing--cards .tier-grid > .tier:nth-child(4) { grid-column: 2 / span 2; }
  .page-pricing--cards .tier-grid > .tier:nth-child(5) { grid-column: 4 / span 2; }
}

/* Below 60rem the grid is a stack. Section 15's own narrow step is two across
   with the fifth card centred beneath; two columns of five orphans one card,
   and a card this tall in half a phone-width column is not a second option.

   59.9375rem, not 60rem — 959px, one pixel under — so this and the block above
   never both match. Section 15's blocks pair `max-width: 60rem` with
   `min-width: 60rem` and let the later one settle 960; that is harmless for a
   hairline, and not harmless for the column count. */
@media (max-width: 59.9375rem) {
  .page-pricing--cards .tier-grid { grid-template-columns: minmax(0, 1fr); }

  .page-pricing--cards .tier-grid > .tier,
  .page-pricing--cards .tier-grid > .tier:nth-child(4),
  .page-pricing--cards .tier-grid > .tier:nth-child(5) { grid-column: auto; }
}

/* ---- One measure across the row ------------------------------------------
   Section 15 pads the anchor 40px on all four sides and every other card
   40/24, which gives Gold a content column 16px narrower than its neighbours'
   — so the same checklist line wraps at a different word on Gold than on
   Silver, and the two cards' interiors stop lining up. All five take the same
   padding here. Gold reads as featured through its gradient surface, its
   accent border, its badge and its filled action, not through an 8px inset. */
.page-pricing--cards .tier-grid > .tier,
.page-pricing--cards .tier-grid > .tier.tier--anchor {
  padding: var(--space-xl) var(--space-lg);
}

/* The raise is off. Section 15 breaks the anchor 16px out of the top and
   bottom of its row with a negative block margin; that makes Gold's card
   32px TALLER than the row and drops its action 16px below Ruby's and
   Silver's. A common action baseline across the row was specified, and a
   negative margin is the one thing that can break it, so it goes. Four
   classes, so it outranks section 15's three. */
.page-pricing--cards .tier-grid > .tier.tier--anchor { margin-block: 0; }

@media (max-width: 30rem) {
  /* Restated at this width for the reason sections 15 and 16 both restate it:
     the padding rule above is a class heavier than section 15's phone-width
     cut and would otherwise hold 40px of block padding on a 320px card. */
  .page-pricing--cards .tier-grid > .tier,
  .page-pricing--cards .tier-grid > .tier.tier--anchor { padding: var(--space-lg); }
}

/* ---- The subtitle --------------------------------------------------------
   Colour, measure and foot all come from section 15's `.tier-grid
   .tier__lede`. Only the step moves: at the body size it competed with the
   name it sits under, and a subtitle is a caption to the name rather than a
   second sentence of it. */
.page-pricing--cards .tier-grid .tier__lede { font-size: var(--text-sm); }

/* ---- The checklist -------------------------------------------------------
   The six things the plan includes, restated on every card rather than once
   in a band above the grid — a card that has to be read against a table
   elsewhere on the page is not a pricing card.

   The mark is a text glyph (U+2713) in a flex-none span, not an icon font and
   not an image: no extra request, and it scales with the row it sits in. It
   is aria-hidden because the item's text already carries the whole meaning,
   and a screen reader announcing "check mark" six times a card, five cards
   over, is noise.

   Muted, not ink: the list is the supporting detail under the figure, and on
   the Gold card --color-muted is the same 4.8:1 against the shadow face that
   section 14 already measured for the qualifier. The mark itself is accent,
   which section 15 measured on both surfaces. No colour here is new. */
.page-pricing--cards .tier__list { margin-block-end: var(--space-lg); }

.page-pricing--cards .tier__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-xs);
  font-size: var(--text-sm);
  line-height: var(--lh-body);
  color: var(--color-muted);
}

/* The glyph and the text carry the same line-height, so aligning their tops
   lands the mark on the first line's baseline even when the item wraps. */
.page-pricing--cards .tier__check {
  flex: none;
  line-height: var(--lh-body);
  color: var(--color-accent);
}

.page-pricing--cards .tier__item + .tier__item {
  margin-block-start: var(--space-sm);
}

/* ---- The per-card action -------------------------------------------------
   `auto` rather than section 7's fixed --space-lg top margin. The card is a
   flex column inside a stretched grid row, so `auto` hands the row's slack to
   this one margin and seats the button on the card's bottom padding — which
   puts every action in a row on ONE baseline regardless of how differently
   their checklists wrapped. The list's own 24px foot is the floor, so the
   button never touches the last item on the tallest card in the row.

   Full card width, so the action reads as the card's own control rather than
   as a link that happens to sit at its foot. `.cta` is inline-flex with its
   content packed left; `flex` plus `justify-content: center` is what makes a
   full-width control centre its label. */
.page-pricing--cards .tier__action { margin-block-start: auto; }

.page-pricing--cards .tier__action .cta {
  display: flex;
  width: 100%;
  justify-content: center;
}

/* ---- The Platinum footnote and the compliance line -----------------------
   Both restated because dropping `--row5` drops section 16's copies. Same
   values, so the block below the grid reads exactly as it does today: the
   footnote in the qualifier's muted step, and the compliance line closing to
   16px against it so the two read as one block rather than as two stranded
   sentences. */
.page-pricing--cards .tiers__note {
  max-width: var(--measure-legal);
  margin-block-start: var(--space-xl);
  margin-inline: auto;
  font-size: var(--text-sm);
  line-height: var(--lh-body);
  text-align: center;
  color: var(--color-muted);
}

.page-pricing--cards .tiers__note + .tiers__legal {
  margin-block-start: var(--space-md);
}

/* ================== 18 · /pricing · icons, asymmetry, field ============== */

/* SCOPING — load-bearing, same discipline as sections 16 and 17.
   Every selector below carries `.page-pricing--cards`. Measured 2026-08-10
   with /usr/bin/grep across all seven page HTMLs: that modifier appears on
   pricing/index.html:24 and nowhere else (plans/index.html carries
   `page-pricing--row5`; the other five pages carry no body class at all).
   The four new block names it hangs off — .plans-sprite, .asym, .run-scale,
   .plane-field — return zero hits in the other six pages, so nothing here can
   reach the LIVE /plans page or the five pages that share this stylesheet.

   NO-JS SAFETY — also load-bearing. Nothing below sets `opacity: 0`, `display:
   none` on content, or `visibility: hidden` on anything a reader needs. This
   page's entrance is still section 3's CSS `.reveal`, which runs with no
   JavaScript at all. plans.js supplies its OWN from-values at animation time,
   so if that module 404s, fails to parse, or is blocked, every card, figure
   and section on this page is fully visible in its final state.

   No colour value is written here. Every one is a tokens.css name that this
   page already spends: accent on the checks and the two section marks (12.6:1
   on paper, and section 15 measured the same accent on the Gold card's ramp),
   --color-rule / --color-rule-strong on the two structural graphics, and
   --color-muted on the scale's two numerals. */

/* ---- The icon sprite ------------------------------------------------------
   One <symbol> in the document, referenced by <use> from all thirty checklist
   rows, so the mark is defined once rather than pasted thirty times. Taken out
   of flow at zero size rather than `display: none`, which stops some engines
   resolving a same-document <use> reference. */
.page-pricing--cards .plans-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* ---- The check mark -------------------------------------------------------
   Replaces the U+2713 text glyph section 17 shipped. Same slot, same accent
   (still inherited from that section's `.tier__check` rule, which sets colour
   and `flex: none`), but a DRAWN mark: one stroke weight, mitred join, square
   caps — so it sits with the page's hairlines instead of with whatever a text
   face's tick happens to look like. It is `currentColor`, so it takes the
   accent on paper and on the Gold card's ramp without a second value.

   Sized to the text at 1em, then pushed down half the difference between the
   row's line box (--lh-body, 1.6em) and the mark itself. That lands it on the
   first line's optical centre and keeps it there when an item wraps to two
   lines, which `align-items: flex-start` on the row otherwise would not. */
.page-pricing--cards svg.tier__check {
  width: 1em;
  height: 1em;
  margin-block-start: calc((var(--lh-body) - 1) * 0.5em);
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: square;
  stroke-linejoin: miter;
}

/* ---- The two section marks -----------------------------------------------
   One per lower section, at the head of its left column, so neither heading
   is bare. Same construction as the check — stroke only, one weight, square
   caps, mitred joins, drawn on the same 24-unit grid — so the three marks on
   the page read as one set rather than as three borrowed glyphs. Nothing is
   filled and nothing is rounded. */
.page-pricing--cards .asym__mark {
  display: block;
  width: 1.5rem;
  height: 1.5rem;
  margin-block-end: var(--space-md);
  fill: none;
  stroke: var(--color-accent);
  stroke-width: 1.5;
  stroke-linecap: square;
  stroke-linejoin: miter;
}

/* ---- Off the centre axis --------------------------------------------------
   Section 15 centres .band and .close on this page, which was right when they
   were the only two blocks under a centred grid. With five cards, a checklist
   in each and an action on each, everything below the grid centred as well
   left the page reading as one narrow ribbon down the middle with the width
   unused on both sides.

   The page head stays centred — it is the anchor over a symmetrical grid, and
   the card grid stays a centred grid, which is what a row of cards wants.
   What moves is the two prose sections: heading left, body offset right, a
   structural graphic in the last column. Section 15's rules are the same
   specificity and earlier in the file, so these settle it. */
.page-pricing--cards .band,
.page-pricing--cards .close { text-align: start; }

.page-pricing--cards .band .mod__title,
.page-pricing--cards .band .mod__body,
.page-pricing--cards .close__title,
.page-pricing--cards .close__body,
.page-pricing--cards .close__aside { margin-inline: 0; }

/* The compliance line under the grid is deliberately NOT touched: it qualifies
   the five figures directly above it and stays centred under them with
   section 15's own `.tiers__legal` rule. */

@media (min-width: 60rem) {
  /* Three tracks, none of them equal, and the head column is the narrow one:
     a heading at --text-title over two or three lines against a body column
     half again its width is what makes the block read as hung rather than as
     stacked. The third track holds the graphic and is the narrowest of all —
     it exists to close the right edge, not to compete for attention.

     60rem is the same floor sections 15, 16 and 17 all turn on, so the page
     has one breakpoint story rather than four. */
  .page-pricing--cards .asym {
    display: grid;
    grid-template-columns: minmax(0, 4fr) minmax(0, 6fr) minmax(0, 2fr);
    gap: var(--space-xl);
    align-items: start;
  }

  /* The closing block carries a filled action and a note under it, so its body
     column gives a track back to the field beside it. */
  .page-pricing--cards .asym--close {
    grid-template-columns: minmax(0, 4fr) minmax(0, 5fr) minmax(0, 3fr);
  }

  /* The visible structural edge between the head and the body. It is the
     decorative hairline, not the boundary one: it separates two halves of one
     block, it does not bound a surface. */
  .page-pricing--cards .asym__body {
    padding-inline-start: var(--space-xl);
    border-inline-start: var(--rule-hair) solid var(--color-rule);
  }

  /* Both graphics are fixed-size, so the track is wider than they are; they
     are pushed to the page's right edge rather than floated in their track,
     which is what actually closes the empty region beside the prose. */
  .page-pricing--cards .asym__aside { justify-self: end; }
}

/* 59.9375rem, not 60rem — 959px, one pixel under, for the same reason sections
   16 and 17 both state it: `max-width: 60rem` still matches at exactly 960,
   the width at which the block above is specified to be in three columns. */
@media (max-width: 59.9375rem) {
  /* One readable column. .asym is not a grid at this width at all, so the
     three children stack in source order: mark, heading, prose. */
  .page-pricing--cards .asym__head { margin-block-end: var(--space-lg); }

  /* Both graphics go. They are aria-hidden decoration that exists to close a
     horizontal void, and below 60rem there is no void to close — kept, they
     would only add height to a phone.

     `.asym >` is not decoration on this line, it is the whole point. The run
     scale's own `display: flex` is written BELOW this block at the same two
     classes — (0,2,0) against (0,2,0), later wins — so with a bare
     `.page-pricing--cards .asym__aside` here the ladder would keep its flex
     box and stay laid out at 320px. The child combinator makes this rule
     (0,3,0), which settles it by specificity rather than by source order. */
  .page-pricing--cards .asym > .asym__aside { display: none; }
}

/* ---- The run scale --------------------------------------------------------
   Thirty rungs on one spine, at 8 units each: the 30-day run drawn as a count.
   It is deliberately NOT data-shaped — no axis, no series, no curve, nothing
   that rises or falls. Every rung is the same length and the same colour; only
   the first and the last run the full width, which is what marks the ends of
   the interval.

   The rungs come from an SVG <pattern> rather than thirty hand-written lines,
   so the markup stays four rects. The tile carries `fill="currentColor"` as a
   presentation attribute AND the class below, so it renders either way. */
.page-pricing--cards .run-scale {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-xs);
  color: var(--color-rule-strong);
}

.page-pricing--cards .run-scale__svg {
  display: block;
  width: 3rem;
  height: 15rem;
}

/* The rungs sit a step back from the spine and the two end stops, so the
   interval's ends read first and the count reads second. */
.page-pricing--cards .run-scale__rung { fill: var(--color-rule); }

/* Geist Mono's third slot on this page, after .tier__badge and
   .page-head__eyebrow — the same numeric-label treatment, not a new face. */
.page-pricing--cards .run-scale__tick {
  font-family: var(--font-outlier);
  font-size: var(--text-sm);
  line-height: 1;
  letter-spacing: var(--track-label);
  color: var(--color-muted);
  font-variant-numeric: tabular-nums;
}

/* ---- The plane field ------------------------------------------------------
   Four vertical hairlines with two open squares standing in them: the page's
   own column rule system, and two of the mark's folded planes seen square-on.
   Orthogonal only — every line is vertical or horizontal, so there is nothing
   here that can be read as a trend, a series or a direction. */
.page-pricing--cards .plane-field {
  display: block;
  width: 7.5rem;
  height: 12.5rem;
  color: var(--color-rule-strong);
}

.page-pricing--cards .plane-field__rule { fill: var(--color-rule); }

/* ---- The card hover -------------------------------------------------------
   The one hover response on the grid: the card's hairline steps from the
   decorative rule to the boundary rule. No lift, no shadow, no scale — the
   page has no shadow token and the anchor's raise is the only geometry that
   moves. The anchor is excluded because its border is already the accent, and
   stepping it back to a grey on hover would read as the featured card
   switching off. Section 13 already flattens this transition to 1ms under
   `prefers-reduced-motion: reduce`. */
.page-pricing--cards .tier-grid > .tier {
  transition: border-color var(--dur-micro) var(--ease-out);
}

.page-pricing--cards .tier-grid > .tier:not(.tier--anchor):hover {
  border-color: var(--color-rule-strong);
}

/* ============================================ 19 · Home (additive) ======= */

/* SCOPING — load-bearing, same discipline as sections 15 through 18. Every
   selector below carries `.page-home`. Measured 2026-08-10 with
   /usr/bin/grep across all seven page HTMLs: that class appears on
   index.html and nowhere else, which is on its own what confines every rule
   in this section to this one page.

   The block names it hangs off were checked too. Eight of them —
   .home-sprite, .home-sec, .home-head, .home-mark, .home-rail, .home-ctrl,
   .home-nots, .home-field — return zero hits in the other six pages. The
   ninth, .commit, does NOT: the string appears three times elsewhere, at
   faq/index.html:97 ("The commitments table on the home page") and
   legal/index.html:63-64 ("capital committed", "commit capital"). All three
   are prose inside a text node; `grep -o 'class="[^"]*commit[^"]*"'` returns
   nothing on any of the six. So no element outside this page carries the
   class, and the `.page-home` ancestor would settle it even if one did.

   Nothing here can reach /plans, /pricing or the four other pages that share
   this stylesheet.

   The home page also consumes .hero, .mod__title, .close__*, .shell, .cta
   and .reveal from sections 1 through 13 unchanged. Where a rule below has
   to settle a conflict with one of those it is noted at the rule.

   NO-JS SAFETY — also load-bearing. Nothing below sets `opacity: 0`,
   `display: none` on content, or `visibility: hidden` on anything a reader
   needs. The page's entrance is section 3's CSS `.reveal`, which runs with
   no JavaScript at all. home.js supplies its own from-values at animation
   time and never at init, so if that module 404s, fails to parse, or is
   blocked, every section, row and figure on this page is fully visible in
   its final state.

   No colour value is written here. Every one is a tokens.css name the site
   already spends: --color-accent on the section marks and the one Built
   flag, --color-muted on the notes and the Not yet flags, --color-rule and
   --color-rule-strong on the hairlines and the two structural graphics. */

/* ---- The icon sprite ------------------------------------------------------
   Three <symbol>s defined once and referenced by <use> from the four negative
   lines and the eleven table rows. Taken out of flow at zero size rather than
   `display: none`, which stops some engines resolving a same-document <use>
   reference. */
.page-home .home-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* ---- The marks ------------------------------------------------------------
   Every mark on this page is drawn on the same 24-unit grid, at one stroke
   weight, with square caps and mitred joins, so the section heads, the step
   rail and the control set read as one drawn family rather than as borrowed
   glyphs. Nothing is filled and nothing is rounded. `currentColor` on the
   two inline sets means they take their colour from the block they sit in
   without a second value being written. */
.page-home .home-mark,
.page-home .home-rail__icon,
.page-home .home-ctrl__icon,
.page-home .home-nots__icon,
.page-home .commit__icon {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: square;
  stroke-linejoin: miter;
}

/* The section head mark. Accent, at the head of its own column, so no
   heading on the page is bare. */
.page-home .home-mark {
  display: block;
  width: 1.5rem;
  height: 1.5rem;
  margin-block-end: var(--space-md);
  color: var(--color-accent);
}

/* ---- Section shell --------------------------------------------------------
   The same vertical rhythm .band and .close already use, so the new blocks
   sit in the page's existing measure rather than introducing a second one. */
.page-home .home-sec { padding-block: var(--space-3xl); }

/* The closing block carries `.close` as well, whose section 10 rule sets the
   identical padding — stated here only so the two cannot drift apart. */
.page-home .home-sec--close { padding-block: var(--space-3xl); }

/* ---- The hanging head -----------------------------------------------------
   Heading left, note under it, a link through to the page that carries the
   full text. The note is the section's one line of prose; the detail lives
   on the interior page and is not restated here. */
.page-home .home-head__note {
  max-width: var(--measure-prose);
  margin-block-start: var(--space-sm);
  color: var(--color-muted);
}

.page-home .home-head__more {
  margin-block-start: var(--space-md);
  font-size: var(--text-sm);
}

/* The head above the commitments table spans the table's full width rather
   than sitting in a narrow column, because the table under it is the widest
   block on the page. */
.page-home .home-head--wide .home-head__note { max-width: var(--measure-lede); }

/* ---- F4 · the five-wide step rail ----------------------------------------
   The five steps of /how-it-works, one short line each, in order. The rail is
   an <ol> so the order is in the markup and not only in the numerals. */
.page-home .home-rail {
  display: grid;
  gap: var(--space-xl);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Each step hangs off a hairline that fades as it runs, which is the same
   falloff the site's existing .step connector uses, applied at 1px. On the
   rail the hairline is vertical below 60rem and horizontal above it, so the
   two rules are written at their breakpoints rather than here. */
.page-home .home-rail__item {
  position: relative;
  min-width: 0;
}

.page-home .home-rail__icon {
  display: block;
  width: 1.5rem;
  height: 1.5rem;
  color: var(--color-rule-strong);
}

/* Geist Mono's slot on this page: the ordinals and nothing else, matching
   the .step__ord treatment the interior pages already use. */
.page-home .home-rail__ord {
  margin-block-start: var(--space-md);
  font-family: var(--font-outlier);
  font-size: var(--text-sm);
  line-height: 1;
  letter-spacing: var(--track-label);
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
}

.page-home .home-rail__name {
  margin-block: var(--space-xs) var(--space-2xs);
  font-family: var(--font-display);
  font-weight: var(--weight-serif-sm);
  font-size: var(--text-base);
  line-height: var(--lh-body);
  overflow-wrap: anywhere;
  min-width: 0;
}

.page-home .home-rail__line {
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* ---- The four controls ----------------------------------------------------
   A 2x2 set above 60rem, a stack below it. Each item is a hairline-topped
   block rather than a bordered card: the page already spends its boxes on
   the commitments table, and four boxes beside it would read as two grids
   competing. */
.page-home .home-ctrl {
  display: grid;
  gap: var(--space-xl);
  margin: 0;
  padding: 0;
  list-style: none;
}

.page-home .home-ctrl__item {
  min-width: 0;
  padding-block-start: var(--space-md);
  border-block-start: var(--rule-hair) solid var(--color-rule);
}

.page-home .home-ctrl__icon {
  display: block;
  width: 1.5rem;
  height: 1.5rem;
  color: var(--color-rule-strong);
}

.page-home .home-ctrl__name {
  margin-block: var(--space-md) var(--space-2xs);
  font-family: var(--font-display);
  font-weight: var(--weight-serif-sm);
  font-size: var(--text-base);
  line-height: var(--lh-body);
  overflow-wrap: anywhere;
  min-width: 0;
}

.page-home .home-ctrl__body {
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* ---- The four negative lines ---------------------------------------------
   One mark, struck through, repeated beside each line. The mark is the same
   for all four because the four statements are the same kind of statement —
   four different glyphs here would imply four different kinds of limit. */
.page-home .home-nots {
  display: grid;
  gap: var(--space-md);
  margin: 0;
  padding: 0;
  list-style: none;
}

.page-home .home-nots__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  min-width: 0;
}

/* Sized to the text, then pushed down half the difference between the row's
   line box and the mark itself, which lands it on the first line's optical
   centre and keeps it there when the line wraps. */
.page-home .home-nots__icon {
  flex: none;
  width: 1em;
  height: 1em;
  margin-block-start: calc((var(--lh-body) - 1) * 0.5em);
  color: var(--color-accent);
}

.page-home .home-nots__caveat {
  max-width: var(--measure-prose);
  margin-block-start: var(--space-xl);
  padding-block-start: var(--space-md);
  border-block-start: var(--rule-hair) solid var(--color-rule);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* ---- The commitments table ------------------------------------------------
   The table /faq promises exists on this page. Its whole job is that a reader
   can tell, per row, whether the thing is built — so the status column is the
   only place on the page other than the section marks that spends the accent,
   and it spends it on exactly one row. */
.page-home .commit-wrap { margin-block-start: var(--space-xl); }

.page-home .commit {
  width: 100%;
  border-collapse: collapse;
  text-align: start;
}

.page-home .commit__caption {
  margin-block-end: var(--space-md);
  font-size: var(--text-sm);
  color: var(--color-muted);
  text-align: start;
}

.page-home .commit__h {
  padding-block: var(--space-xs);
  border-block-end: var(--rule-hair) solid var(--color-rule-strong);
  font-family: var(--font-outlier);
  font-size: var(--text-xs);
  font-weight: var(--weight-body);
  line-height: 1;
  letter-spacing: var(--track-label);
  color: var(--color-muted);
  text-align: start;
  text-transform: uppercase;
}

.page-home .commit__part,
.page-home .commit__status,
.page-home .commit__note {
  padding-block: var(--space-md);
  border-block-end: var(--rule-hair) solid var(--color-rule);
  text-align: start;
  vertical-align: baseline;
}

.page-home .commit__part {
  font-family: var(--font-display);
  font-weight: var(--weight-serif-sm);
  font-size: var(--text-base);
  line-height: var(--lh-body);
  overflow-wrap: anywhere;
  min-width: 0;
}

.page-home .commit__note {
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* The flag is the row's answer. Mono, so it reads as a status token and not
   as more prose, and drawn with its own mark so the answer survives being
   skimmed. */
.page-home .commit__flag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-family: var(--font-outlier);
  font-size: var(--text-sm);
  line-height: 1;
  letter-spacing: var(--track-label);
  white-space: nowrap;
}

.page-home .commit__icon {
  flex: none;
  width: 1em;
  height: 1em;
  stroke-width: 2;
}

.page-home .commit__flag--built   { color: var(--color-accent); }
.page-home .commit__flag--pending { color: var(--color-muted); }

.page-home .commit__foot {
  max-width: var(--measure-prose);
  margin-block-start: var(--space-lg);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* ---- The structural graphics ---------------------------------------------
   Hairline rule systems with the mark's folded planes standing in them.
   Orthogonal only — every line is vertical or horizontal, so there is
   nothing here that can be read as a series, a trend or a direction, and
   nothing that carries a value. They are aria-hidden decoration whose one
   job is to close the empty region beside the prose. */
.page-home .home-field {
  display: block;
  color: var(--color-rule-strong);
}

.page-home .home-field--wide {
  width: 15rem;
  height: 6rem;
}

.page-home .home-field--tall {
  width: 7.5rem;
  height: 12.5rem;
}

.page-home .home-field__rule  { fill: var(--color-rule); }

.page-home .home-field__plane {
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
}

/* ---- Wide viewports -------------------------------------------------------
   60rem is the floor every other section of this stylesheet turns on, so the
   page has one breakpoint story rather than five.

   The rhythm is deliberately not one grid repeated: a head with a graphic
   beside it over a five-wide rail, then a head against a 2x2, then a
   three-part hung block, then a full-width table, then a three-part hung
   block again. The point is that the reader never settles into a single
   left-right beat down the page. */
@media (min-width: 60rem) {
  .page-home .home-sec {
    display: grid;
    gap: var(--space-xl) var(--space-2xl);
    align-items: start;
  }

  /* Head and graphic share the top row; the rail spans both tracks beneath
     them, which is what stops the right half of this section being empty. */
  .page-home .home-sec--steps {
    grid-template-columns: minmax(0, 6fr) minmax(0, 4fr);
  }

  .page-home .home-sec--steps .home-rail {
    grid-column: 1 / -1;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: var(--space-lg);
    margin-block-start: var(--space-lg);
  }

  /* The rail's connector, above 60rem only: one hairline across the top of
     the five items, with each item hanging a short tick off it. It marks the
     order of a sequence and measures nothing. */
  .page-home .home-sec--steps .home-rail__item {
    padding-block-start: var(--space-md);
    border-block-start: var(--rule-hair) solid var(--color-rule);
  }

  .page-home .home-sec--steps .home-rail__item::before {
    content: "";
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: 0;
    width: var(--rule-hair);
    height: var(--space-sm);
    background-color: var(--color-rule-strong);
  }

  /* A narrow head against a wider 2x2: the heading reads as hung rather than
     as the first cell of a four-cell grid. */
  .page-home .home-sec--ctrl {
    grid-template-columns: minmax(0, 4fr) minmax(0, 6fr);
  }

  .page-home .home-sec--ctrl .home-ctrl {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* Three tracks, none of them equal, head narrowest and graphic narrower
     still: the same hung proportion the interior pages use. */
  .page-home .home-sec--nots,
  .page-home .home-sec--close {
    grid-template-columns: minmax(0, 4fr) minmax(0, 5fr) minmax(0, 3fr);
  }

  /* The visible structural edge between a head and its body. It is the
     decorative hairline, not the boundary one: it separates two halves of
     one block, it does not bound a surface. */
  .page-home .home-sec--nots .home-sec__body,
  .page-home .home-sec--close .home-sec__body {
    padding-inline-start: var(--space-xl);
    border-inline-start: var(--rule-hair) solid var(--color-rule);
  }

  /* The table wants the full measure, so its section stays one column and
     only the head above it is capped. */
  .page-home .home-sec--commit { grid-template-columns: minmax(0, 1fr); }

  /* Every graphic is fixed-size and its track is wider than it is, so each
     is pushed to the page's right edge rather than floated in its track,
     which is what actually closes the empty region beside the prose. */
  .page-home .home-sec__aside { justify-self: end; }
}

/* ---- Narrow viewports -----------------------------------------------------
   59.9375rem, not 60rem — 959px, one pixel under, for the same reason
   sections 16, 17 and 18 all state it: `max-width: 60rem` still matches at
   exactly 960, the width at which the blocks above are specified to be in
   two and three columns. */
@media (max-width: 59.9375rem) {
  .page-home .home-sec { padding-block: var(--space-2xl); }

  .page-home .home-head { margin-block-end: var(--space-lg); }

  /* Below 60rem the rail is a vertical stack, and its connector turns with
     it: the same hanging hairline the site's .step sequence already uses. */
  .page-home .home-rail__item {
    padding-inline-start: var(--space-lg);
  }

  .page-home .home-rail__item::before {
    content: "";
    position: absolute;
    inset-block: var(--space-2xs) 0;
    inset-inline-start: 0;
    width: var(--rule-hair);
    background-color: var(--color-rule);
    background-image: var(--grad-rule-fade);
  }

  /* Every graphic goes. They are aria-hidden decoration that exists to close
     a horizontal void, and below 60rem there is no void to close — kept,
     they would only add height to a phone.

     `.home-sec >` is not decoration on this line: it lifts the rule to
     (0,3,0) so it settles by specificity rather than by source order against
     anything later in the file that names the same two classes. */
  .page-home .home-sec > .home-sec__aside { display: none; }

  /* The table becomes eleven stacked blocks rather than a three-column grid,
     because three columns at 320px is not a readable table, it is a
     horizontal scroll. Each row keeps its part, its status and its note in
     that order, and the column headers go — every cell is self-describing
     once the row is a block. */
  .page-home .commit,
  .page-home .commit__caption,
  .page-home .commit__row,
  .page-home .commit__part,
  .page-home .commit__status,
  .page-home .commit__note {
    display: block;
    width: auto;
  }

  .page-home .commit thead { display: none; }

  .page-home .commit__row {
    padding-block: var(--space-md);
    border-block-end: var(--rule-hair) solid var(--color-rule);
  }

  .page-home .commit__part,
  .page-home .commit__status,
  .page-home .commit__note {
    padding-block: 0;
    border-block-end: 0;
  }

  .page-home .commit__status { margin-block-start: var(--space-xs); }

  .page-home .commit__note { margin-block-start: var(--space-2xs); }
}

/* ---- The one hover response -----------------------------------------------
   A table row's hairline steps from the decorative rule to the boundary one
   while the pointer is on it, which is the same single-step response the
   /plans cards give. No lift, no shadow, no scale — the page has no shadow
   token. Section 13 already flattens this transition to 1ms under
   `prefers-reduced-motion: reduce`. */
@media (min-width: 60rem) {
  .page-home .commit tbody .commit__row > * {
    transition: border-color var(--dur-micro) var(--ease-out);
  }

  .page-home .commit tbody .commit__row:hover > * {
    border-block-end-color: var(--color-rule-strong);
  }
}

/* ============================================ 16 · /signup /signin (additive) ==
 * Both pages share this block: they are the same form shape (email, a
 * passwordless link, a handful of states while a backend does not exist yet),
 * differing only in whether a second optional field is present. Nothing here
 * is scoped to a page class, so either page — or a future one with the same
 * shape — draws from it unchanged, the same discipline .panel and .band
 * already follow.
 *
 * The panel sits left-aligned inside its own .band, at --measure-lede rather
 * than the page's centred hero/gate compositions: this is a utility page, not
 * a second hero, and it takes its place in the shell's normal rhythm instead
 * of floating as an isolated card.
 *
 * Inputs pick up the global :focus-visible ring directly — section 2's
 * selector now covers input/select/textarea, so nothing here has to restate
 * it the way gate.css did before that fix existed.
 */

.auth-panel {
  max-width: var(--measure-lede);
  padding: var(--space-xl);
  border: var(--rule-hair) solid var(--color-rule-strong);
  border-radius: var(--radius-panel);
  background-color: var(--color-paper-2);
}

.auth-panel__form { min-width: 0; }

/* ---- Fields --------------------------------------------------------------
 * Label always visible, above the control, never a placeholder standing in
 * for it. Hint and error are both wired via aria-describedby from the markup
 * side; the error paragraph starts [hidden] and stays that way until a real
 * validation failure — or, on this page today, a ?state= preview — sets it.
 */

.field + .field { margin-block-start: var(--space-lg); }

.field__label {
  display: block;
  margin-block-end: var(--space-2xs);
  font-weight: var(--weight-display);
  font-size: var(--text-sm);
  color: var(--color-ink);
}

.field__input,
.field__textarea {
  display: block;
  width: 100%;
  min-height: var(--target-min);
  padding: var(--space-sm) var(--space-md);
  border: var(--rule-hair) solid var(--color-rule-strong);
  border-radius: var(--radius-control);
  background-color: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--lh-body);
}

.field__textarea {
  min-height: calc(var(--target-min) * 2);
  resize: vertical;
}

.field__hint {
  margin-block-start: var(--space-2xs);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* Same "no red/danger token" discipline as gate.css: no new hue, just more
 * structure than bold-on-its-own gave it. Before this rule, .field__error
 * shared its exact weight AND colour with .field__label two lines above —
 * the only thing telling them apart was position — so the border+background
 * chip below is what actually adds signal, not another shade of ink. It also
 * now sits ABOVE .field__hint (here and in the markup) so the eye meets the
 * problem before it meets the helper text. Still backed by text that is
 * never silent (role="alert" on the paragraph itself). */
.field__error {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  margin-block-start: var(--space-2xs);
  padding: var(--space-2xs) var(--space-sm);
  border: var(--rule-hair) solid var(--color-ink);
  border-radius: var(--radius-control);
  background-color: var(--color-paper);
  font-weight: var(--weight-display);
  font-size: var(--text-sm);
  color: var(--color-ink);
}

/* Restated after the UA [hidden] rule for the same reason gate.css restates
 * it: a same-specificity class declared later in the cascade would otherwise
 * win over [hidden] and show the row on first paint, every time. */
.field__error[hidden] { display: none; }

.field__input[aria-invalid="true"],
.field__textarea[aria-invalid="true"] {
  border-color: var(--color-ink);
}

.auth-panel__form > .cta { margin-block-start: var(--space-lg); }

/* ---- The form-level error summary -----------------------------------------
 * role="alert" so it is announced the moment it stops being [hidden];
 * tabindex="-1" so script can move focus onto it without adding it to the
 * normal tab order. */
.auth-alert {
  margin-block-end: var(--space-lg);
  padding: var(--space-md);
  border: var(--rule-hair) solid var(--color-ink);
  border-radius: var(--radius-control);
  background-color: var(--color-paper);
  font-weight: var(--weight-display);
  color: var(--color-ink);
}

.auth-alert[hidden] { display: none; }

/* A disabled REAL button (the submitting state) — distinct from .cta's own
 * [aria-disabled="true"], which only ever decorates an anchor. Same values,
 * new selector, because item 1's fix is the only existing rule this file
 * touches. */
.cta:disabled {
  background-image: none;
  background-color: var(--color-rule);
  border-color: var(--color-rule);
  color: var(--color-muted);
  cursor: not-allowed;
}

/* ---- Success / expired / locked --------------------------------------- *
 * Each is its own labelled region, not a toast: it replaces the form in
 * place (the form itself goes [hidden] alongside it) and takes the page's
 * own heading and body type, not a floating notification. */
.auth-region[hidden] { display: none; }

.auth-region__title {
  margin-block-end: var(--space-sm);
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-md);
  line-height: var(--lh-title);
}

.auth-region__body { color: var(--color-ink); }

.auth-region__action { margin-block-start: var(--space-lg); }

.auth-note {
  max-width: var(--measure-lede);
  margin-block-start: var(--space-md);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

.auth-switch {
  max-width: var(--measure-lede);
  margin-block-start: var(--space-lg);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* ---- Desktop: a right column, not empty space -----------------------------
 * Below 60rem .auth-aside stays [display: none] and .auth-grid stays a plain
 * block — the desktop rules in the media query below never match at that
 * width, so nothing here touches the already-approved narrow layout.
 *
 * >=60rem the empty right half becomes a second column: /signup gets an
 * honest "what happens next" (the panel is the form, the aside is what
 * filling it out leads to); /signin — where there is genuinely less to say
 * without just repeating the panel's own hint text three times — gets a
 * small static figure drawn from the same folded-plane tokens as .mark and
 * .panel's wing ramp, in a NEW silhouette, so it reads as the same material
 * without being the WebGL mark shown a third time (it already appears on /
 * and /_gate/). */
.auth-aside { display: none; }

@media (min-width: 60rem) {
  body.page-signup .band,
  body.page-signin .band {
    /* Was --space-3xl (96px) here, stacked on .page-head's own 16px bottom
       padding, for a 112px void neither page's copy explains. --space-xl is
       .page-head's OWN top padding, so the lede now sits the same distance
       under the H1 as the panel sits under the lede — one deliberate rhythm
       instead of an arbitrary one. Scoped to these two pages only: .band is
       shared with /control, /plans, /legal and /pricing, which keep their
       existing --space-3xl. */
    padding-block-start: var(--space-xl);
  }

  .auth-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-2xl);
    /* No align-items override: the default (stretch) lets .auth-aside's box
       take the row's full height — set by .auth-main, almost always the
       taller of the two — so the flex rule below can centre the aside's
       content inside that height. .mod uses align-items:start instead
       because its two columns are each an independent block of text; here
       the right column exists to sit alongside the left one, not apart. */
  }

  .auth-aside {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
  }
}

/* ---- /signin's figure ------------------------------------------------
 * Two planes cut from the same rectangle, using the exact tokens .mark's
 * lit and wing faces use — but a fresh silhouette (one horizontal fold)
 * rather than .mark's vertical twin-stroke, so this reads as the same lit
 * material without being the same mark. Purely decorative (aria-hidden in
 * the markup) and entirely static: nothing here waits on mark.js or a
 * canvas, so it never falls back the way the home page's mark can. Each
 * face declares a flat background-color before its gradient for the same
 * reason .mark__face does — a browser that cannot parse `in oklch` keeps a
 * flat lit plane instead of losing the background entirely. */
.auth-figure {
  position: relative;
  width: 100%;
  max-width: 18rem;
  aspect-ratio: 4 / 5;
  margin-inline: auto;
}

.auth-figure__face {
  position: absolute;
  inset: 0;
  display: block;
}

.auth-figure__face--lit {
  background-color: var(--color-lit-top);
  background-image: var(--grad-lit-face);
  clip-path: polygon(0 0, 100% 0, 100% 58%, 0 82%);
}

.auth-figure__face--wing {
  background-color: var(--color-wing-start);
  background-image: var(--grad-wing);
  clip-path: polygon(0 82%, 100% 58%, 100% 100%, 0 100%);
}

/* The theme toggle's styles live in theme-toggle.css, linked by every page.
   They were here, but the dashboard and admin pages do NOT load styles.css --
   they load tokens.css + dashboard.css + a page sheet -- so the control shipped
   there completely unstyled: no box, no sizing, and BOTH icons visible at once.
   One shared file, linked everywhere, is the fix. */
