/*
  site.css — plan 02-09, Task 3. Applies DESIGN.md's approved "Quiet
  Ledger" comp to the real eleven-section landing page and both legal
  page shells (which extend the same base.html and therefore inherit
  every rule below) in one pass (D-36).

  Values below are copied verbatim from DESIGN.md — this file is not
  where a palette/spacing/type decision gets made, only where an
  already-approved one gets applied. See DESIGN.md's own tables for the
  measured WCAG AA contrast ratio behind every colour pair used here.

  Self-hosted webfonts only (D-35): both @font-face sources point
  exclusively at /static/fonts/, and src/hyti/web/static/fonts/ may be
  completely empty (see that directory's own README.md) — font-display:
  swap plus the system-font fallback stack below is what keeps that a
  loss of polish, never a blank page or a broken layout.

  Motion budget (D-37): exactly one CSS transition exists in this whole
  file — the form-to-receipt swap's opacity fade, driven by htmx's
  built-in `swap:`/`settle:` style modifiers (see _lead_form.html's own
  comment) — collapsed to an instant swap under prefers-reduced-motion.
  No scroll-triggered reveal, no parallax, no sticky stack, no
  JavaScript of any kind is declared here (the page's one page-wide JS
  budget item, beacon.js, is unrelated to this file).
*/

:root {
  /* DESIGN.md Palette — six named custom properties, each measured in
     .planning/phases/02-landing-acquisition/design/CONTRAST.md. */
  --surface: #f4f5f3; /* Dominant (60%) — page background */
  --surface-alt: #e4e8ec; /* Secondary (30%) — alternate section tone */
  --ink: #1b1e1c; /* Primary text */
  --ink-muted: #52585b; /* Helper text, labels, compliance sub-block, footer */
  --accent: #1d4e8f; /* CTA, inline links, focus rings, receipt acknowledgment ONLY */
  --error: #a6321b; /* 422 inline validation error text only */
  --destructive: #532a5e; /* plan 03-10, 03-UI-SPEC.md Color. SINGLE permitted
    use: the account-erasure confirmation control (GDPR erasure, IDN-06) — the
    one irreversible action this phase introduces. FORBIDDEN uses: session or
    device revoke (D-09 frames that as ordinary and reversible, styled with
    ordinary controls, not this role), and anything else. Measured 10.32:1 on
    --surface / 9.17:1 on --surface-alt (both clear 4.5:1 text / 3:1 UI);
    unambiguously distinct in hue from --accent (navy) and --error (clay-red)
    — see .planning/phases/02-landing-acquisition/design/CONTRAST.md A13-A17. */

  /* DESIGN.md Spacing — seven tokens, all 4px multiples. */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 32px;
  --space-2xl: 48px;
  --space-3xl: 64px;
}

/* Public Sans, exactly two weights (DESIGN.md Typography). Both
   @font-face sources are same-origin only; scripts/check_external_
   requests.py fails the build on anything else. */
@font-face {
  font-family: "Public Sans";
  src: url("/static/fonts/public-sans-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Public Sans";
  src: url("/static/fonts/public-sans-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

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

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

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  /* A real system-font fallback stack — this directory may be
     completely empty and the page must still render body text,
     headings, and the email form correctly (fonts/README.md). */
  font-family:
    "Public Sans",
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    Roboto,
    Helvetica,
    Arial,
    sans-serif;
  font-weight: 400;
  /* Never below 16px anywhere — iOS auto-zooms form inputs under 16px,
     which would break the email field at the exact viewport D-44
     tests (DESIGN.md Typography). */
  font-size: 17px;
  line-height: 1.55;
}

h1,
h2,
h3 {
  font-weight: 700;
  line-height: 1.25;
  margin: 0 0 var(--space-md) 0;
}

/* Exactly one <h1> per page, the hero headline. Collapses to <=32px at
   375px width with zero horizontal overflow (D-44/DESIGN.md). */
h1 {
  font-size: 44px;
  line-height: 1.15;
}

@media (max-width: 480px) {
  h1 {
    font-size: 30px;
  }
}

h2 {
  font-size: 26px;
}

h3 {
  font-size: 18px;
}

p,
li,
dd {
  margin: 0 0 var(--space-md) 0;
}

label {
  display: block;
  font-size: 15px;
  line-height: 1.45;
  margin-bottom: var(--space-sm);
}

/* Accent is reserved for the primary CTA, inline text links,
   focus-visible outlines, and the receipt-state's one visual
   acknowledgment — never anything decorative (DESIGN.md Palette). */
a {
  color: var(--accent);
}

a:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* 03-14 finish-review finding: form controls do not inherit font from
   `body` by default — every <button> this phase adds beyond #lead-form's
   own explicit CTA rule below (Surface 3's revoke Confirm/export
   controls, Surface 4's TOTP and Confirm-sign-in submits) rendered in
   the browser's own default UI font/size/colour until this rule, an
   uncontrolled third typeface DESIGN.md's two-weight constraint
   forbids. Border/background are deliberately left to the browser
   default here: no "secondary button" treatment exists anywhere in this
   system, and inventing one would be a design decision beyond a finish
   review's authority — only the typography defect is fixed. */
button {
  font-family: inherit;
  font-size: 17px;
  font-weight: 400;
  color: inherit;
}

/* Section separation: full-bleed two-tone alternation, no card, no
   box, no shadow-as-decoration anywhere (DESIGN.md "Section
   separation"). */
.surface {
  background: var(--surface);
}

.surface-alt {
  background: var(--surface-alt);
}

main section,
main footer,
#footer {
  padding: var(--space-2xl) var(--space-md);
}

#hero {
  /* Hero top padding, desktop: capped at 96px/6rem (DESIGN.md
     Spacing) — more reads as a layout bug, not intentional space. */
  padding-top: 96px;
}

@media (max-width: 480px) {
  #hero {
    padding-top: var(--space-2xl);
  }
}

/* Body measure capped at ~65 characters per line on every prose block
   (Typography) — applies uniformly to every section's content, the
   worked-contract example included, since a hidden clause would
   misrepresent the product regardless of column width. */
.section-inner {
  max-width: 65ch;
  margin: 0 auto;
  overflow-wrap: anywhere;
}

/* Price section: framed by two 1px hairlines, never a box (DESIGN.md
   "Section separation"). */
.price-section {
  border-top: 1px solid rgba(27, 30, 28, 0.2);
  border-bottom: 1px solid rgba(27, 30, 28, 0.2);
}

.price-figure {
  font-size: 22px;
  font-weight: 700;
}

.contract-fields {
  margin: 0;
}

.contract-field {
  margin-bottom: var(--space-md);
}

.contract-field dt {
  font-weight: 700;
  margin-bottom: var(--space-xs);
}

.contract-field dd {
  margin: 0;
}

.goal-examples-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.goal-examples-list li {
  margin-bottom: var(--space-sm);
}

.illustration-label {
  color: var(--ink-muted);
  font-weight: 700;
}

.mechanism-steps {
  padding-left: 1.25em;
}

/* "What it will never do" — two visibly separated blocks: a
   heading-weight change plus a single hairline, legible without
   reading the copy (D-06/DESIGN.md "Section separation"). */
.never-does-visitor {
  margin-bottom: var(--space-lg);
}

.never-does-compliance {
  border-top: 1px solid rgba(27, 30, 28, 0.2);
  padding-top: var(--space-md);
}

.never-does-compliance h3 {
  color: var(--ink-muted);
}

.android-only-notice {
  font-weight: 700;
}

/* android-only and form share one visual band (surface-alt, DESIGN.md
   "Section separation") — the default section padding on both sides
   left an oversized gap between them (a batched-inspection finding,
   Task 3), reading as two loosely related pieces rather than the
   notice sitting DIRECTLY above the form (D-10). Tightened so the pair
   reads as one continuous band. */
#android-only {
  padding-bottom: var(--space-md);
}

#form {
  padding-top: var(--space-md);
}

/* G-02-6 (plan 02-15, Task 3): reserve the band's height so the swap
   never reflows the page below it. Set on `.section-inner` (the h2 +
   sentinel + `_lead_form.html` wrapper), never on `#lead-form` itself
   — that element is replaced wholesale by the swap and any rule on it
   disappears with it.

   Measured 2026-09-10 with a real browser at 390/768/1280px across
   every state `_lead_form.html` can render (default, the 422/429
   notice, and the receipt) — the notice state is tallest at all three
   widths, not the receipt, contrary to a screenshot-only reading:

     390px:  default 182.25 / notice 250.94 / receipt 101.19
     768px:  default 182.25 / notice 224.59 / receipt  74.84
     1280px: default 182.25 / notice 224.59 / receipt  74.84 (same as 768)

   Base rule below covers 768/1280 (228px = 224.59 rounded up to the
   next 4px multiple, 3.41px slack); the existing 480px breakpoint
   carries the taller 390px value. These numbers come from the system
   font fallback stack — src/hyti/web/static/fonts/ is still empty
   pending the operator's own action (fonts/README.md) — and will
   shift once the real Public Sans WOFF2 files land. The geometry test
   in tests/web/landing/test_htmx_browser.py is what will say so, not
   a reader noticing a stale comment. */
#form .section-inner {
  min-height: 228px;
}

@media (max-width: 480px) {
  #form .section-inner {
    min-height: 252px;
  }
}

/* Minimum interactive touch target: 44x44px on the submit control and
   both legal links wherever they render as tap targets (Spacing). */
#lead-form label {
  font-size: 15px;
}

/* `#login-form` added to both selectors below (03-14 finish-review
   finding): plan 03-03/03-06's own comments say this form "reuses the
   SAME id and classes verbatim" as #lead-form, but the CSS selector
   list was never actually extended to include it — the "Send sign-in
   link" primary CTA (03-UI-SPEC Copywriting Contract: "used identically
   on Surface 1 and Surface 4's login step") and its email input
   rendered unstyled on both `/login` and `/ops/login` until this fix. */
#lead-form input[type="email"],
#login-form input[type="email"] {
  background: var(--surface);
  border: 1px solid var(--ink-muted);
  color: var(--ink);
  font-size: 17px;
  min-height: 44px;
  padding: var(--space-sm);
  width: 100%;
}

#lead-form input[type="email"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

#lead-form button[type="submit"],
#login-form button[type="submit"] {
  background: var(--accent);
  border: none;
  color: var(--surface);
  cursor: pointer;
  font-size: 16px;
  font-weight: 700;
  margin-top: var(--space-md);
  min-height: 44px;
  min-width: 44px;
  padding: var(--space-sm) var(--space-lg);
}

#lead-form button[type="submit"]:disabled,
#login-form button[type="submit"]:disabled {
  cursor: default;
  opacity: 0.6;
}

/* Pending-state label swap (_lead_form.html's own comment) — CSS only,
   no JavaScript. htmx toggles the `htmx-request` class on #lead-form
   for the duration of the in-flight request. */
.btn-label-pending {
  display: none;
}

#lead-form.htmx-request .btn-label-default,
#login-form.htmx-request .btn-label-default {
  display: none;
}

#lead-form.htmx-request .btn-label-pending,
#login-form.htmx-request .btn-label-pending {
  display: inline;
}

/* G-02-2 (plan 02-14), extended G-02-11 (plan 02-18): a settled notice
   from the PREVIOUS response must not sit above the next submit for
   ANY window the OLD form node can still be on screen — not only the
   in-flight window. htmx removes `.htmx-request` the instant the
   response lands, but `hx-swap="outerHTML swap:120ms settle:20ms"`
   (_lead_form.html) keeps the OLD node in the DOM ~117ms longer — the
   UAT measured the stale notice returning to `block` at full opacity
   for that whole window before the receipt replaced the form
   (02-UAT.md, gap G-02-11). An end-state check taken after settle is
   structurally incapable of seeing this: it samples after the old
   node is already gone. Keyed on the shared `.lead-form-notice` base
   class (Task 2 of 02-14), not the `data-testid` attribute — a class
   is a styling hook, a test id is not. A `display` toggle only: D-37's
   single motion budget is spent on the form-to-receipt swap below and
   is not touched here. The message returns only if the server
   re-reports it in the next response. Do not narrow this selector
   list back to `.htmx-request` alone — that is exactly the regression
   `tests/web/landing/test_htmx_browser.py::
   test_stale_notice_hidden_from_in_flight_through_swap_to_receipt`
   exists to catch.

   `.htmx-settling` is deliberately NOT in this list, and that absence
   is a measured decision, not an oversight. `.htmx-swapping` is
   carried by the OLD node (stale by definition — exactly what this
   rule should hide); `.htmx-settling` is added to the NEW node, whose
   notice has just arrived from the server and is fresh by definition.
   Measured directly (plan 02-18, Task 2,
   `test_freshly_reported_notice_visible_after_settle_in_both_motion_
   modes`) on a 422 -> 422 resubmit under `prefers-reduced-motion:
   reduce`: with `.htmx-settling` included, a sampled frame showed the
   new node fully opaque (plan 02-15 restores opacity there) while its
   own fresh notice was hidden by `display: none` — the notice would
   have popped in one frame later, a visible artefact landing on
   exactly the visitor G-02-3 exists to protect. Confirms 02-UAT.md's
   G-02-11 `node_ownership_measured` record, which predicted the same
   outcome. Re-adding `.htmx-settling` here without re-running that
   test is re-introducing the artefact it proved. */
#lead-form.htmx-request .lead-form-notice,
#lead-form.htmx-swapping .lead-form-notice {
  display: none;
}

/* Plan 03-03 Task 2: `#login-form`'s own notice carries the identical
   stale-notice hazard `/leads` above already closed (G-02-2/G-02-11) —
   same rule, same reasoning, scoped to this form's own notice class
   rather than re-deriving it. `/login` never actually reaches a SECOND
   render with a notice present (there is no distinct rate-limited
   state, 03-UI-SPEC Surface 1), but a 422-then-422 resubmit is a real,
   reachable path and gets the same protection. */
#login-form.htmx-request .login-form-notice,
#login-form.htmx-swapping .login-form-notice {
  display: none;
}

/* G-02-5: the two notice kinds must render apart — the rate-limited
   visitor did nothing wrong, so the advisory notice is never the
   422-reserved --error colour. Class-scoped on the modifier Task 2
   added, replacing the previous unconditional data-testid rule.
   `--ink-muted` on `--surface-alt` measured at 5.87:1 in
   design/CONTRAST.md (row A12, D-38). */
.lead-form-notice--validation {
  color: var(--error);
}

.lead-form-notice--advisory {
  color: var(--ink-muted);
}

/* Same contract, `#login-form`'s own notice classes (plan 03-03 Task 2)
   — `login-form-notice--validation` is the only kind this form ever
   actually renders (Surface 1 has no distinct rate-limited state), but
   both are declared for the same reason `_login_form.html`'s own
   resolved-once `notice_kind` variable exists: one mechanism, not a
   special case for the kind that happens to be unreachable today. */
.login-form-notice--validation {
  color: var(--error);
}

.login-form-notice--advisory {
  color: var(--ink-muted);
}

/* The receipt state's one visual acknowledgment (DESIGN.md Palette) —
   the only other permitted use of --accent besides the CTA, inline
   links and focus rings. A confirmation, not a celebration: weight and
   colour only, no icon, no animation beyond the swap itself.
   `.login-receipt-message` added (03-14 finish-review finding): the
   magic-link receipt (`auth/_login_form.html` and
   `ops_console/_login_form.html`, both Surface 1 and Surface 4) used
   this class name from the start but no rule ever styled it — the
   receipt rendered as plain unstyled text on both surfaces. */
.lead-receipt-message,
.login-receipt-message {
  color: var(--accent);
  font-weight: 700;
}

/* The footer links stand alone (one link, its own line, no adjacent
   punctuation) — horizontal padding is safe there and gives comfortable
   side-by-side spacing between the two. The money-back paragraph's
   links sit inside a flowing sentence immediately followed by "and
   the"/a full stop; the SAME horizontal padding there pushed the
   surrounding words and the trailing period visibly away from the
   link text (a batched-inspection finding, plan 02-09 Task 3). Both
   still meet the 44px minimum touch target: line-height alone supplies
   the height, and each phrase's natural text width already exceeds
   44px, so dropping the horizontal padding costs nothing on either
   axis. */
.footer-links a {
  display: inline-block;
  line-height: 44px;
  min-height: 44px;
  padding: 0 var(--space-sm);
}

.legal-links a {
  display: inline-block;
  line-height: 44px;
  min-height: 44px;
}

/* D-37: the ONLY animated moment on the entire page. htmx's
   `swap:120ms`/`settle:20ms` style modifiers (see _lead_form.html)
   add/remove the `htmx-swapping`/`htmx-settling` classes around the
   outerHTML swap; this is what they animate. Exactly one `transition`
   declaration exists in this file. Timings shortened in plan 02-15
   Task 2 (G-02-6): 120ms fade-out + 20ms settle hold + 120ms fade-in =
   260ms, landing inside DESIGN.md's <=300ms motion budget (measured by
   sampling real frames, tests/web/landing/test_htmx_browser.py — the
   previous 200ms/200ms/200ms timings measured ~600ms end to end). */
/* Plan 03-03 Task 2 extends this SAME selector list (never a second
   `transition` declaration — 03-UI-SPEC.md's Motion & Interaction
   Contract: "the same mechanism reused, unmodified" for every htmx
   partial swap this phase adds, `#login-form`'s receipt swap included)
   rather than duplicating the rule for a second form id. */
#lead-form,
#login-form {
  transition: opacity 120ms ease;
}

#lead-form.htmx-swapping,
#lead-form.htmx-settling,
#login-form.htmx-swapping,
#login-form.htmx-settling {
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  #lead-form,
  #login-form {
    transition: none;
  }

  /* G-02-3 (plan 02-15): htmx's swap:/settle: windows are
     attribute-declared and completely blind to this media query, so
     neutralising `transition` above is not enough on its own — the
     .htmx-swapping/.htmx-settling opacity:0 rule above still applies
     for the whole of htmx's swap+settle duration, turning what should
     be an instant swap into a hard cut held fully invisible. Repeats
     the exact id-plus-class selector pair (not a looser one) so this
     rule wins at equal specificity, never by source order alone — a
     media query contributes no specificity of its own. */
  #lead-form.htmx-swapping,
  #lead-form.htmx-settling,
  #login-form.htmx-swapping,
  #login-form.htmx-settling {
    opacity: 1;
  }
}

/* Plan 03-06, UI-SPEC Surface 4 — the operator boundary band
   (src/hyti/ops_console/templates/_boundary_band.html). Full-width,
   `--ink` background with `--surface` text — this project's own
   ink/surface pair inverted, no new colour token (03-UI-SPEC.md's own
   instruction: never a new token for this element). Sized to
   comfortably fit its one line of text at the Label role's size;
   ~48px total height is the UI-SPEC's own suggested default. No motion
   — this element is static on every render, never swapped or
   transitioned. */
/* Plan 03-08, UI-SPEC Surface 4 — the TOTP QR code and chunked base32
   secret (src/hyti/ops_console/templates/totp.html). Minimum 220x220
   CSS px — a scannability floor, not a decorative choice. The quiet
   zone (>=4 modules on every side) is baked into the SVG's own viewBox
   by `qrcode`'s default `border=4` (ops_console/service.py's
   `render_totp_qr_svg`), not reproduced here. The chunked secret reuses
   the Body role's EXISTING size and weight — only letter-spacing is
   added below; this block declares no weight value of its own. */
.ops-totp-qr {
  height: 220px;
  width: 220px;
}

.ops-totp-qr svg {
  display: block;
  height: 100%;
  width: 100%;
}

.ops-totp-secret {
  font-size: 17px; /* Body role, DESIGN.md — unchanged size/weight */
  letter-spacing: 0.3em;
}

/* 03-14 finish-review finding: UI-SPEC Typography requires increased
   letter-spacing (~0.3em) on BOTH the chunked secret above AND the
   6-digit code input itself — the input shipped with no class and no
   rule at all, rendering at the browser's own tiny default input font
   (well under the project's 16px-minimum-anywhere constraint, since
   below that iOS auto-zooms the field) with no letter-spacing. */
#ops-totp-code {
  background: var(--surface);
  border: 1px solid var(--ink-muted);
  color: var(--ink);
  font-size: 17px;
  letter-spacing: 0.3em;
  min-height: 44px;
  padding: var(--space-sm);
  width: 100%;
}

.ops-boundary-band {
  background: var(--ink);
  color: var(--surface);
  font-size: 15px; /* Label role, DESIGN.md */
  line-height: 1.45;
  padding: var(--space-sm) var(--space-md);
  text-align: center;
}

/* Plan 03-13, UI-SPEC Surface 3 — the account/session-list page. First
   list-with-per-row-action shape in this codebase (03-PATTERNS.md); no
   card, no box — the existing surface/surface-alt tonal alternation
   IS the section separation, same discipline as every other page. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.account-row {
  align-items: flex-start;
  border-top: 1px solid rgba(27, 30, 28, 0.2);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  justify-content: space-between;
  padding: var(--space-md) 0;
}

.account-row:first-of-type {
  border-top: none;
}

/* Rows stack (label above, action below) rather than clipping at
   narrow viewports — the project-wide 375px no-horizontal-scroll
   discipline (UI-SPEC Surface 3 Overflow row). */
@media (max-width: 480px) {
  .account-row {
    flex-direction: column;
  }
}

.account-row-label {
  font-weight: 700;
  margin: 0;
}

.account-row-meta {
  color: var(--ink-muted);
  font-size: 15px;
  margin: var(--space-xs) 0 0 0;
}

.account-row-action {
  min-width: 44px;
}

.account-row-error {
  color: var(--error);
  font-size: 15px;
  margin: var(--space-xs) 0 0 0;
}

.account-empty {
  color: var(--ink-muted);
}

/* Plan 03-13 Task 2 — the inline revoke-confirm form's own pending
   label swap, reusing the SAME btn-label-default/btn-label-pending
   pair and .htmx-request toggle `_lead_form.html`/`_login_form.html`
   already established (site.css's own existing rule above), just
   scoped to this form's own class rather than a second id. */
.account-revoke-form.htmx-request .btn-label-default {
  display: none;
}

.account-revoke-form.htmx-request .btn-label-pending {
  display: inline;
}

/* Plan 03-13 Task 3 — the erasure confirmation's destructive submit is
   the ONLY element in this phase styled with --destructive (D-09: a
   session/device revoke is ordinary and reversible and never gets this
   role). */
.btn-destructive {
  background: var(--destructive);
  border: none;
  color: var(--surface);
  cursor: pointer;
  font-size: 16px;
  font-weight: 700;
  min-height: 44px;
  min-width: 44px;
  padding: var(--space-sm) var(--space-lg);
}

.btn-destructive:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* Plan 03-09/03-14, UI-SPEC Surface 5 — the appeal-shell's plain
   wordmark (DESIGN.md "Wordmark treatment": Bold, tight tracking,
   `--ink` colour, no mark, no lockup box). 03-14 finish-review finding:
   this class shipped in the template with no matching rule, rendering
   as plain Regular-weight body text — indistinguishable from ordinary
   prose instead of the plain brand identifier the surface's own "no
   marketing chrome... a plain wordmark" instruction calls for. */
.wordmark {
  color: var(--ink);
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0 0 var(--space-lg) 0;
}

/* 03-14 finish-review finding: Surfaces 1, 2, 4 and 5 (the magic-link
   request/redeem pages, the operator login/TOTP pages, and the appeal
   shell) are single-purpose pages with no `<section>` inside `<main>`
   — DESIGN.md's tone-alternating section rhythm is for the eleven-
   section marketing page, not these small pages, matching the existing
   legal-page-shell precedent (`landing/legal/*.html`) of a plain `main`
   with no surface/surface-alt alternation. But every one of these pages
   shipped with NEITHER a section wrapper NOR `.section-inner`, so the
   binding ~65ch body-measure cap (Typography, restated for legal pages
   via their own inline style) and basic padding were simply absent —
   content rendered edge-to-edge at full viewport width. Reuses
   `.section-inner` (already the measure-cap mechanism, DESIGN.md
   Typography) rather than a second inline rule, plus the same compact
   padding the legal-page shells already use for a single-purpose page
   (`padding: 1rem` there, expressed here as the equivalent `--space-md`
   token). */
main > .section-inner {
  padding: var(--space-md);
}
