/* ═══════════════════════════════════════════════════════════════════════
   Phaeacia : company front door
   Loads AFTER assets/fonts/fonts.css and assets/tokens.css.

   Rules this file obeys, so they can be checked:
   . Not one literal colour. Every colour is a var(--ph-*) from tokens.css,
     which declares the full light palette on bare :root. Nothing here
     declares a colour for the first time inside a media query.
   . Spacing comes from the 4/8/12/16/24/32/48/72/112 scale only. Button
     padding (13/22) is a component value fixed by DESIGN-GUIDE section 7,
     not spacing.
   . One grid, and the two tracks are the type split, not two widths. The
     200px rail carries machine-owned strings only: keys and values quoted
     from spec/passport-0.1.md and examples/daily-brief-passport.md, set in
     mono. The 664px column carries sentences a person wrote, set in sans.
     An English aside in the rail (.rail-note, .rail-src) is sans, because
     mono used as texture spends the meaning the split is there to carry.
     Wide material spans both tracks.
   . Phone layout follows DESIGN-GUIDE section 6.1, which this pass wrote.
     The four decisions that shape this file below 1000/640:
       - structural spacing comes from --ph-band/--ph-stack/--ph-block/--ph-pad,
         which step down one notch of the same scale at 640. Nothing here
         scales a 1440 measurement down; it picks a lower step.
       - one hairline per section boundary, and the air under it belongs to
         the section it opens. .rule is the desktop hairline and stands down
         below 1000, because .band's own border-top is already there.
       - the header is one row. Below 1000 the section anchors shed; the
         footer nav carries all six of them, verbatim.
       - the rail is marginalia at every width. Below 1000 it splits: the
         numeral leads the section, the machine block drops below the opening
         prose as a ruled aside. Done with `order`, so the DOM order and
         therefore the reading order stay the desktop order.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── reset ───────────────────────────────────────────────────────────── */

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

/* Anchor clearance is MEASURED from the header at runtime, not written down.
   The nav wraps at whatever width its four labels happen to need, so every
   hard-coded breakpoint here has been wrong at least once: a literal placed at
   the width the bug was observed at left a band either side still broken.
   --head-clear is set by site.js and covers every width by construction.
   The fallbacks below are the no-JS floors and are deliberately generous. */
html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: var(--head-clear, var(--ph-9));
  scroll-behavior: smooth;
}
@media (max-width: 1000px) {
  /* No-JS floor only. The header can no longer wrap below 1000: the nav sheds
     and the row is lockup + theme pill, both 44px targets in a 56px row, at
     every width down to 320. So this floor no longer has to guess at a third
     row. 72 + 48 = 120, on the scale, which clears 56 with room for the
     theme pill growing a line if a translation ever lengthens it.
     --head-clear still measures the real header at runtime and still wins. */
  html { scroll-padding-top: var(--head-clear, calc(var(--ph-8) + var(--ph-7))); }
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body { margin: 0; }

img, svg { display: block; max-width: 100%; }

h1, h2, h3, p, ul, ol, figure, table { margin: 0; }

/* The UA rule for [hidden] loses to any author `display`. This does not. */
[hidden] { display: none !important; }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Focus, per DESIGN-GUIDE section 8: 2px --ph-deep at 3px offset, never
   removed. No border-radius is set here, because the focus ring must not
   change an element's geometry. tokens.css does put a 4px radius in its own
   :focus-visible, so any surface whose real radius differs — pill, medium,
   large — re-declares it at equal specificity later in this file, which wins
   in all states. Surfaces that are already 4px, or inline like .prose a where
   a 4px corner on a line box is not visible, correctly declare nothing. */
:focus-visible {
  outline: 2px solid var(--ph-deep);
  outline-offset: 3px;
}

/* ── layout ──────────────────────────────────────────────────────────── */

.wrap {
  width: 100%;
  max-width: var(--ph-wrap);          /* 1160 */
  margin-inline: auto;
  padding-inline: 34px;
}
@media (max-width: 640px) { .wrap { padding-inline: 20px; } }

/* The page grid. 200 rail + 48 gap + 664 prose = 912, centred inside the
   1160 container. Only two tracks are declared, so there is no third column
   sitting empty: the space either side of 912 is margin, deliberately. */
.grid {
  display: grid;
  grid-template-columns: 200px minmax(0, 664px);
  column-gap: 48px;
  justify-content: center;
}
.grid > * { min-width: 0; }

/* NOT sticky. .span blocks take grid-column 1/-1 and therefore run back under
   column 1; a sticky rail is positioned, so it painted on top of the losses table
   and the rule cards with both grounds transparent. The rails are also short and
   the sections are short, so following the reader was decoration, not wayfinding. */
.rail { grid-column: 1; align-self: start; }
.col  { grid-column: 2; }
.span { grid-column: 1 / -1; }        /* 912px: tables, cards, the form */

/* A 912-wide block that keeps the same left edge as the rail. */
.block { width: 100%; max-width: 912px; margin-inline: auto; }

/* One column, and the rail splits in two around the opening prose.
   `order` groups, lowest first, DOM order breaking every tie:
     0  the section numeral                     .rail > .ph-label
     1  the heading and its opening paragraphs  .col.lead
     2  everything else, in DOM order, which puts the rail's machine block
        first in that group because it is first in the DOM.
   .rail goes to display:contents so its two halves become grid items and
   can carry an order of their own. Nothing about the DOM changes, so a
   screen reader and a keyboard still traverse this in the desktop order,
   which is also the order the argument was written in. */
@media (max-width: 1000px) {
  .grid {
    grid-template-columns: minmax(0, 1fr);
    max-width: 664px;
    margin-inline: auto;
    row-gap: var(--ph-block);
  }
  .rail, .col, .span { grid-column: 1; }
  .block { max-width: 664px; }

  .rail { display: contents; }
  .grid > * { order: 2; }
  .rail > .ph-label { order: 0; }
  .col.lead { order: 1; }
  .rail-body { order: 2; }

  /* Below 1000 the grid supplies the gap between rows. A block that also
     carries its own margin-top then spends it twice, which is where three
     of the half-screen holes came from: 32 of row-gap plus 32 of the
     block's own margin, at every seam between a paragraph and a table.
     On desktop the grid has no row-gap, so those margins ARE the gap and
     stay where they are. */
  /* .grid .stack-top, not .stack-top: the type-helpers section declares
     .stack-top later in this file, and at equal specificity later wins even
     from outside a media query. The extra class is what makes this override
     hold rather than the order it happens to be written in. */
  .grid .stack-top { margin-top: 0; }
  .span > :first-child { margin-top: 0; }
}

/* ── bands ───────────────────────────────────────────────────────────── */

.band { padding-block: var(--ph-stack); }
.band + .band { border-top: 1px solid var(--ph-line-soft); }
.band--well  { background: var(--ph-paper-deep); border-top-color: var(--ph-line); }
.band--shoal { background: var(--ph-shoal-soft); border-top-color: var(--ph-line); }

/* Asymmetric, per DESIGN-GUIDE 6.1: the air under a boundary belongs to the
   section it opens, so the numeral hangs close under the rule and the
   section signs off with the larger gap. Equal padding either side of a
   hairline is what made a transition read as a gap where something failed
   to load: the reader could not tell which side of it they were on. */
@media (max-width: 1000px) {
  .band { padding-block: var(--ph-block) var(--ph-band); }
}

/* ── type helpers ────────────────────────────────────────────────────── */

.lede {
  font-size: var(--ph-t-h3);
  line-height: 1.45;
  letter-spacing: -0.006em;
  max-width: var(--ph-measure-lede);
  color: var(--ph-ink);
}
.stack-top { margin-top: var(--ph-6); }

.prose > * + * { margin-top: var(--ph-5); }
.prose h3 { margin-top: var(--ph-6); }
.prose a { font-weight: 500; }

/* Sentence-case section headings, sitting under the rule. */
.sec-h { margin-bottom: var(--ph-5); }

/* The consequential passage. Brass is legal as a rule; never as body text.
   Both .turn blocks sit on --ph-paper, where brass measures 3.57:1 against
   the ground and clears the 3:1 non-text floor. It is never used this way on
   the tinted bands, where it measures 3.18 and 3.26. */
.turn {
  border-left: 1px solid var(--ph-brass);
  padding-left: var(--ph-5);
}
.turn > * + *   { margin-top: var(--ph-5); }
.turn > h3 + p  { margin-top: var(--ph-3); }
.prose > .turn  { margin-top: var(--ph-block); }

.kicker {
  font-family: var(--ph-mono);
  font-size: var(--ph-t-micro);
  letter-spacing: var(--ph-tr-micro);
  text-transform: uppercase;
  color: var(--ph-mute);
  margin-bottom: var(--ph-3);
}

/* Section numerals. Brass is a non-text graphic here per DESIGN-GUIDE 4, and
   it measures 3.57:1 on --ph-paper. On the two tinted bands it drops to 3.18
   and 3.26, which clears 3:1 by less than 0.2, so the numeral goes to mute
   there instead — 4.77:1 on the well, 4.88:1 on the shoal. */
.num { color: var(--ph-brass); font-weight: 500; }
.band--well .num, .band--shoal .num { color: var(--ph-mute); }

/* ── the rail: machine-owned strings only ────────────────────────────────
   .manifest is a real key/value list. Every key is a field name from
   spec/passport-0.1.md and every value is either that field's enum or a
   value read out of examples/daily-brief-passport.md. Nothing in here is
   invented, and nothing in here is an English sentence. */

.manifest {
  margin: var(--ph-4) 0 0;
  padding-top: var(--ph-4);
  border-top: 1px solid var(--ph-line);
  font-family: var(--ph-mono);
  font-size: var(--ph-t-micro);
  line-height: 1.6;
  color: var(--ph-ink);
}
.manifest .row {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  column-gap: var(--ph-2);
}
.manifest .row + .row { margin-top: var(--ph-2); }
.manifest dt { color: var(--ph-mute); white-space: nowrap; }
.manifest dt::after { content: ":"; }
.manifest dd { margin: 0; overflow-wrap: anywhere; }
/* A value too long for a 200px rail stacks under its key instead of being
   truncated, because a truncated machine string is a wrong machine string. */
.manifest .row--flat { display: block; }
.manifest .row--flat dd { padding-left: var(--ph-3); }

/* On a phone the rail's machine block sits inside the reading column instead
   of beside it, so it needs to say "aside" without a column to say it. A left
   hairline and an indent do that, which is the same device .turn uses for the
   opposite job: brass there because that passage is consequential, --ph-line
   here because this one is reference. The type does not change register when
   the block moves: the manifest is still mono, the gloss is still sans. */
@media (max-width: 1000px) {
  /* .turn is emphasis, not marginalia. Horizontal brass rule above; the
     vertical rail belongs to the aside alone, so the two devices stop colliding. */
  .turn {
    border-left: 0;
    padding-left: 0;
    border-top: 1px solid var(--ph-brass);
    padding-top: var(--ph-block);
  }
  .rail-body {
    border-left: 1px solid var(--ph-line);
    padding-left: var(--ph-5);
  }
  .rail-body .manifest {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
  }
  /* The rule and the padding go, because the aside now has a rule of its own
     down its left side, but the SPACE has to stay: without it the English
     gloss runs straight into the last machine value and the two registers
     become one block of text. */
  .rail-body .rail-note {
    margin-top: var(--ph-3);
    padding-top: 0;
    border-top: 0;
  }
}

/* The two sans exceptions in the rail: they are sentences a person wrote, so
   they are not dressed as data. */
.rail-src {
  margin-top: var(--ph-3);
  font-size: var(--ph-t-small);
  line-height: 1.5;
  color: var(--ph-mute);
  max-width: none;
}
.rail-note {
  margin-top: var(--ph-4);
  padding-top: var(--ph-4);
  border-top: 1px solid var(--ph-line);
  font-size: var(--ph-t-small);
  line-height: 1.5;
  color: var(--ph-mute);
  max-width: none;
}

/* Machine-owned strings inside prose inherit .ph-machine from tokens.css. */
.val { color: var(--ph-mute); }

/* ── header ──────────────────────────────────────────────────────────── */

.skip {
  position: absolute; left: var(--ph-4); top: var(--ph-4);
  transform: translateY(-200%);
  background: var(--ph-card); color: var(--ph-deep);
  border: 1px solid var(--ph-line); border-radius: var(--ph-r-md);
  padding: 12px 16px; font-size: var(--ph-t-small); z-index: 20;
}
.skip:focus { transform: none; }

.head {
  position: sticky; top: 0; z-index: 10;
  background: var(--ph-paper);
  border-bottom: 1px solid var(--ph-line-soft);
}
.head-in {
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: var(--ph-5);
  min-height: 72px;
}
/* font-size sits on the lockup, never on .ph-wordmark, so the mark's 1.42em
   resolves against the wordmark and the two scale as one unit per
   DESIGN-GUIDE section 3. 1.5rem = 24px wordmark, so the mark renders at
   34.1px, above the 28px duotone floor at every width. */
.head .ph-lockup { text-decoration: none; min-height: 44px; border-radius: var(--ph-r-sm); font-size: 1.5rem; }

.head-nav { display: flex; align-items: center; gap: var(--ph-5); margin-left: auto; }
.head-nav a {
  display: inline-flex; align-items: center;
  min-height: 44px; padding-block: var(--ph-2);
  font-size: var(--ph-t-small);
  color: var(--ph-ink);
  text-decoration: none;
  border-radius: var(--ph-r-sm);
}
.head-nav a:hover { color: var(--ph-deep); text-decoration: underline; text-underline-offset: 3px; }

/* Below 1000px the nav sheds. DESIGN-GUIDE 6.1: the header is one row at
   every width, and what the header sheds the footer carries.
   The four labels plus the lockup plus the theme pill need about 690px on one
   line, so below 1000 they cannot share a row. Wrapping them to a second row
   was the previous answer and it cost 133px of sticky chrome on a 844px
   screen — 16% of every viewport, before any content, forever. A hidden
   horizontal scroller was the answer before that and it put the fourth link
   off-screen with no affordance.
   The footer nav lists all six section anchors, including the two that were
   never in the header, so nothing is unreachable: it is one flick to the end
   of a page a phone reader is scrolling anyway. Below 1000 the page is also
   one column, so this is one breakpoint with one story rather than two ideas
   about what a small screen is. */
@media (max-width: 1000px) {
  .head-nav { display: none; }
  .head-in { min-height: 56px; column-gap: var(--ph-4); }
  .head .ph-lockup { margin-right: auto; }
}

/* The lockup steps down as one unit per DESIGN-GUIDE section 3, never by
   shrinking the mark against the wordmark. 1.375rem = 22px wordmark, so the
   mark renders at 31.2px, still above the 28px duotone floor. */
@media (max-width: 400px) {
  .head .ph-lockup { font-size: 1.375rem; }
  .theme-btn { padding: 0 var(--ph-3); }
}


.theme-btn {
  display: inline-flex; align-items: center; gap: var(--ph-2);
  min-height: 44px; padding: 0 var(--ph-4);
  background: transparent;
  border: 1px solid var(--ph-line);
  border-radius: var(--ph-r-pill);
  color: var(--ph-mute);
  font-family: var(--ph-mono);
  font-size: var(--ph-t-micro);
  letter-spacing: var(--ph-tr-micro);
  text-transform: uppercase;
  cursor: pointer;
}
.theme-btn:hover { color: var(--ph-ink); border-color: var(--ph-mute); }
.theme-btn .theme-val { min-width: 6ch; text-align: left; color: var(--ph-ink); }

/* ── hero ────────────────────────────────────────────────────────────── */

.hero { padding-block: var(--ph-stack) var(--ph-block); }

.hero-mark { color: var(--ph-deep); }
.hero-mark svg { height: 88px; width: auto; }
/* DESIGN-GUIDE 6.1 gives the mark a ceiling as well as a floor. At 88px on a
   350px column the oar is a quarter of the measure, and at that size the
   diagonal reads as a brush or a spoon rather than as a mark; it resolves
   back into an oar once it is small enough to be read as a sign. 48px is
   well clear of the 28px duotone floor. */
@media (max-width: 640px) { .hero-mark svg { height: 48px; } }

/* The pronunciation line lives in the hero colophon and nowhere else on the
   site. It is brand furniture: it says how to say the name and where the name
   came from, and neither is the argument. It sat above the headline until
   2026-08-08, where on a phone it took two lines of the first screen before
   the reader reached a word of the product. */
.hero-say {
  font-family: var(--ph-mono);
  font-size: var(--ph-t-micro);
  letter-spacing: var(--ph-tr-micro);
  text-transform: uppercase;
  color: var(--ph-mute);
  max-width: none;
}

.hero h1 { margin-top: var(--ph-block); max-width: 16ch; }
.hero .lede { margin-top: var(--ph-5); }
/* At 20px the lede runs to seven lines on a phone and stops being a lede.
   17px with a little more leading holds the same emphasis against a 41px
   headline, because on a phone the size difference is doing less work than
   the line count. */
@media (max-width: 640px) {
  .hero .lede { font-size: var(--ph-t-body); line-height: 1.55; }
}

.actions {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--ph-5); margin-top: var(--ph-block);
}
/* Two calls to action, one primary and one secondary, on a 350px row: they
   fit side by side only by luck. Stacked, the button is full width and the
   text link keeps its own line and its own 44px target. */
@media (max-width: 640px) {
  .actions { gap: var(--ph-3); align-items: stretch; flex-direction: column; }
  .actions .btn { width: 100%; }
}

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 13px 22px;
  background: var(--ph-deep); color: var(--ph-paper);
  border: 1px solid var(--ph-deep);
  border-radius: var(--ph-r-md);
  font-family: var(--ph-sans); font-size: var(--ph-t-small); font-weight: 500;
  text-decoration: none; cursor: pointer;
}
.btn:hover { background: var(--ph-tide); border-color: var(--ph-tide); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn:disabled:hover { background: var(--ph-deep); border-color: var(--ph-deep); }

.btn-text {
  display: inline-flex; align-items: center; gap: var(--ph-2);
  min-height: 44px;
  color: var(--ph-deep); font-size: var(--ph-t-small); font-weight: 500;
  text-decoration: underline; text-decoration-thickness: from-font;
  text-underline-offset: 3px;
  border-radius: var(--ph-r-sm);
}
.btn-text:hover { text-decoration-thickness: 2px; }

/* The hero colophon: what the thing is called, what version it is, and where
   it is up to. One hairline holds both lines, so the two brand-furniture rows
   read as one block rather than as two afterthoughts. */
.hero-foot {
  margin-top: var(--ph-stack);
  padding-top: var(--ph-4);
  border-top: 1px solid var(--ph-line);
}
.hero-foot .status-strip { margin-top: var(--ph-3); }

.status-strip {
  display: flex; flex-wrap: wrap; gap: var(--ph-3) var(--ph-5);
  font-size: var(--ph-t-small);
  color: var(--ph-mute);
  max-width: none;
}
.status-strip .machine {
  font-family: var(--ph-mono);
  font-size: var(--ph-t-micro);
  letter-spacing: var(--ph-tr-micro);
  text-transform: uppercase;
  color: var(--ph-ink);
}
/* Four items on a 350px row wrap into a ragged 2x2, and the first cell is the
   only one in mono, so the grid it lands in looks like an accident rather
   than a decision. Split it the way the content is already split: the version
   string is a machine value and takes its own line; the three status phrases
   are one sentence a person wrote and run on, separated by middots.
   The separator is punctuation between siblings, so it is generated rather
   than typed into the markup, where it would be read as content. */
@media (max-width: 640px) {
  .status-strip { display: block; }
  .status-strip .machine { display: block; margin-bottom: var(--ph-2); }
  .status-strip .machine ~ span + span::before { content: " \00B7  "; }

  /* The pronunciation line breaks where it means something rather than where
     350px runs out, and the middot that joined the two halves on one row is
     not carried to the head of the second line, where it would read as a
     bullet. */
  .hero-say .say-sep { display: none; }
  .hero-say .say-gloss { display: block; }
}

/* ── section furniture ───────────────────────────────────────────────── */

.rule { grid-column: 1 / -1; border-top: 1px solid var(--ph-line); margin-bottom: var(--ph-5); }

/* Below 1000 this is the second of two hairlines about 48px apart with
   nothing between them, which is exactly the pattern that reads as a render
   failure. It goes, and the band's own edge-to-edge border-top becomes the
   single boundary. display:none rather than border:0, because an empty grid
   item still costs a row and a row-gap.
   The first band follows .hero, not another .band, so `.band + .band` never
   reached it and .rule was its only boundary. It gets one here. */
@media (max-width: 1000px) {
  .rule { display: none; }
  .band { border-top: 1px solid var(--ph-line-soft); }
  .band + .band--well, .band + .band--shoal { border-top-color: var(--ph-line); }
  /* A legal page opens with a band directly under the header, which already
     draws a bottom border. Two hairlines touching is a 2px line nobody asked
     for. The index does not hit this: its first band follows the hero. */
  .legal > .band:first-child { border-top: 0; }
}

/* ── cards ───────────────────────────────────────────────────────────── */

.cards { display: grid; gap: var(--ph-4); margin-top: var(--ph-6); }
.card {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  column-gap: var(--ph-7);
  background: var(--ph-card);
  border: 1px solid var(--ph-line);
  border-radius: var(--ph-r-lg);
  padding: var(--ph-pad);
}
/* The card key is a string quoted out of the spec, so it is set as a machine
   value and not as an eyebrow: no uppercase, no wide tracking. The eyebrow
   style would render `## 6. Losses` as `## 6. LOSSES`, which is a heading the
   validator would reject — a label style that silently rewrites the thing it
   is quoting is the same failure as inventing the string in the first place.
   Same treatment as .manifest in the rail, deliberately. */
.card-key {
  margin: 0;
  font-family: var(--ph-mono);
  font-size: var(--ph-t-micro);
  letter-spacing: 0;
  line-height: 1.6;
  color: var(--ph-mute);
  overflow-wrap: anywhere;
}
.card h3 { margin-bottom: var(--ph-3); }
.card p { max-width: 64ch; }
@media (max-width: 1000px) {
  .card { grid-template-columns: minmax(0, 1fr); row-gap: var(--ph-3); }
}

/* ── table ───────────────────────────────────────────────────────────── */

.tbl-scroll { overflow-x: auto; margin-top: var(--ph-6); }
.tbl { width: 100%; border-collapse: collapse; text-align: left; }
.tbl th {
  font-family: var(--ph-mono);
  font-size: var(--ph-t-micro);
  letter-spacing: var(--ph-tr-micro);
  text-transform: uppercase;
  font-weight: 400;
  color: var(--ph-mute);
  padding: 0 var(--ph-5) var(--ph-3) 0;
  border-bottom: 1px solid var(--ph-line);
  vertical-align: bottom;
  white-space: nowrap;
}
.tbl td {
  padding: var(--ph-4) var(--ph-5) var(--ph-4) 0;
  vertical-align: top;
  font-size: var(--ph-t-small);
  line-height: 1.55;
}
.tbl tbody tr + tr td { border-top: 1px solid var(--ph-line-soft); }
.tbl th:last-child, .tbl td:last-child { padding-right: 0; }
/* These numerals are the whole content of their cell, so they are text and
   get no large-text exemption. Brass would be 3.57:1; mute is 5.35:1. */
.tbl .c-num {
  font-family: var(--ph-mono); color: var(--ph-mute);
  width: 4ch; white-space: nowrap;
}
.tbl .c-name { font-weight: 500; min-width: 15ch; }


/* ── the losses table ────────────────────────────────────────────────────
   The only place the product colour layer appears on a company page, and it
   appears because it is carrying real data: --pp-granted for a capability
   that installed, --pp-refused for one that did not. Measured on --ph-paper:
   granted 5.67:1, refused 6.15:1. At night, 8.12:1 and 6.96:1. */

.loss { margin-top: var(--ph-6); }
.loss > .ph-label { margin-bottom: var(--ph-4); }
.loss figcaption {
  margin-top: var(--ph-4);
  font-size: var(--ph-t-small);
  color: var(--ph-mute);
  max-width: var(--ph-measure);
}
.loss figcaption p { max-width: none; }
.loss figcaption p + p { margin-top: var(--ph-3); }
.tbl .c-what { white-space: nowrap; min-width: 14ch; }
.tbl .c-tag  { color: var(--ph-mute); }
.tbl .c-stat { white-space: nowrap; }
/* On a phone the third column carries the whole point of the table, so the
   two machine columns give up their reserved width and their gutter rather
   than pushing the sentence off the scroll. The table still scrolls inside
   .tbl-scroll if it needs to; it mostly does not need to. */
@media (max-width: 640px) {
  .tbl .c-what { min-width: 0; white-space: normal; }
  .tbl th { white-space: normal; }
  .tbl td, .tbl th { padding-right: var(--ph-3); }
  .tbl td:last-child, .tbl th:last-child { padding-right: 0; }

  /* The losses table stacks rather than scrolling. Its third column is the
     whole point of the table and a phone gives it about twenty characters as
     a column, which is a ribbon, not a sentence. The two machine cells keep
     one line and the sentence takes the full width beneath them.
     Changing `display` on table elements drops their implicit roles, so the
     markup carries role="table|rowgroup|row|columnheader|cell" explicitly and
     the table is still announced as a table with its header row. */
  .tbl--loss, .tbl--loss tbody { display: block; }
  .tbl--loss thead {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  /* 17ch is the widest machine string in the column, so the status lines up
     down the table instead of tracking each row's capability name. */
  .tbl--loss tr {
    display: grid;
    grid-template-columns: 17ch minmax(0, 1fr);
    column-gap: var(--ph-4);
    padding-block: var(--ph-4);
  }
  .tbl--loss td { padding: 0; }
  .tbl--loss td:last-child { grid-column: 1 / -1; margin-top: var(--ph-2); }
  .tbl--loss tbody tr + tr { border-top: 1px solid var(--ph-line-soft); }
  .tbl--loss tbody tr + tr td { border-top: 0; }

/* The six-section table stacks below 640 for the same reason the losses table
   does: its third column is the whole content of the row, and three columns
   on a 350px measure give it a ribbon about twenty characters wide. The
   numeral and the section name keep one line, in the reserved 4ch and the
   rest; the sentence takes the full width beneath them. Roles are on the
   markup because changing `display` drops the implicit ones. */
  .tbl--stack, .tbl--stack tbody { display: block; }
  .tbl--stack thead {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .tbl--stack tr {
    display: grid;
    grid-template-columns: 4ch minmax(0, 1fr);
    column-gap: var(--ph-4);
    padding-block: var(--ph-4);
  }
  .tbl--stack td { padding: 0; }
  .tbl--stack .c-name { min-width: 0; }
  .tbl--stack td:last-child { grid-column: 1 / -1; margin-top: var(--ph-2); }
  .tbl--stack tbody tr + tr { border-top: 1px solid var(--ph-line-soft); }
  .tbl--stack tbody tr + tr td { border-top: 0; }
}
.stat { font-family: var(--ph-mono); font-size: 0.94em; }
.stat--in   { color: var(--pp-granted); }
.stat--part { color: var(--ph-mute); }     /* substituted: neither kept nor gone */
.stat--out  { color: var(--pp-refused); }

/* ── ordered argument ────────────────────────────────────────────────────
   A real ordered list, so VoiceOver still announces "list, three items".
   The ordinal is the ::marker, not a ::before counter that replaces it.
   letter-spacing is not an allowed ::marker property, so it is not set. */

/* 48px of indent on a 350px column is an eighth of the measure spent on a
   two-character ordinal. --ph-block steps it to 32 on a phone. */
.claims { padding-left: var(--ph-block); margin-top: var(--ph-5); list-style: decimal-leading-zero; }
.claims li + li { margin-top: var(--ph-5); }
.claims li::marker {
  font-family: var(--ph-mono);
  font-size: var(--ph-t-micro);
  color: var(--ph-mute);
}
.claims strong { font-weight: 600; }

/* ── access form ─────────────────────────────────────────────────────── */

.form { margin-top: var(--ph-6); max-width: 560px; }
.form-row { display: flex; flex-wrap: wrap; gap: var(--ph-3); align-items: flex-start; }
/* The field and the button stop sharing a row well before 640, so below it
   they stack and the button takes the full measure. Same shape as the hero's
   primary action, so the page has one idea of what a phone button is. */
@media (max-width: 640px) {
  .form-row { flex-direction: column; align-items: stretch; }
  .form-row .btn { width: 100%; }
  /* .field carries flex: 1 1 260px for the desktop row. In a COLUMN container
     flex-basis is a height, so that 260px made the email input a 260px-tall
     empty box with the placeholder stranded at the bottom. Reset the basis
     here; min-height keeps the 44px target. */
  .form-row .field { flex: 0 0 auto; }
}
.field {
  flex: 1 1 260px; min-width: 0; min-height: 44px;
  padding: 12px var(--ph-4);
  background: var(--ph-card);
  color: var(--ph-ink);
  border: 1px solid var(--ph-line);
  border-radius: var(--ph-r-md);
  font-family: var(--ph-sans);
  font-size: var(--ph-t-body);
}
.field::placeholder { color: var(--ph-mute); }
.field:hover { border-color: var(--ph-mute); }
.form label {
  display: block; margin-bottom: var(--ph-2);
  font-family: var(--ph-mono);
  font-size: var(--ph-t-micro);
  letter-spacing: var(--ph-tr-micro);
  text-transform: uppercase;
  color: var(--ph-mute);
}
.form-note { margin-top: var(--ph-3); font-size: var(--ph-t-small); color: var(--ph-mute); }
.form-msg {
  margin-top: var(--ph-4);
  padding: var(--ph-4) var(--ph-5);
  border: 1px solid var(--ph-line);
  border-radius: var(--ph-r-md);
  background: var(--ph-card);
  font-size: var(--ph-t-small);
  max-width: 560px;
}
.form-msg .form-msg-h {
  font-family: var(--ph-mono);
  font-size: var(--ph-t-micro);
  letter-spacing: var(--ph-tr-micro);
  text-transform: uppercase;
  display: block;
  margin-bottom: var(--ph-2);
}
.form-msg--ok  { border-color: var(--ph-good); }
.form-msg--ok  .form-msg-h { color: var(--ph-good); }
.form-msg--bad { border-color: var(--ph-bad); }
.form-msg--bad .form-msg-h { color: var(--ph-bad); }

/* ── placeholders that must not ship as live links ───────────────────── */

.slot {
  font-family: var(--ph-mono);
  font-size: 0.94em;
  color: var(--ph-mute);
  border: 1px dashed var(--ph-line);
  border-radius: var(--ph-r-sm);
  padding: var(--ph-1);
  white-space: nowrap;
}

/* ── legal pages ─────────────────────────────────────────────────────── */

/* A legal h1 is a page title, not the display headline. tokens.css ships
   --ph-t-h1 for exactly this; without it these rendered at the 66px display
   size, which reads as a marketing headline on a privacy notice. */
.legal h1 { font-size: var(--ph-t-h1); }
.legal .prose h2 { margin-top: var(--ph-stack); margin-bottom: var(--ph-4); }
.legal .prose ul { padding-left: var(--ph-5); }
.legal .prose li + li { margin-top: var(--ph-2); }
.legal .prose li { max-width: var(--ph-measure); }
.legal dl { margin: 0; }
.legal dt {
  font-family: var(--ph-mono);
  font-size: var(--ph-t-micro);
  letter-spacing: var(--ph-tr-micro);
  text-transform: uppercase;
  color: var(--ph-mute);
  margin-top: var(--ph-5);
}
.legal dd { margin: var(--ph-2) 0 0; font-size: var(--ph-t-small); }

.notice {
  border: 1px solid var(--ph-line);
  border-left: 1px solid var(--ph-brass);
  border-radius: var(--ph-r-md);
  background: var(--ph-card);
  padding: var(--ph-pad);
  font-size: var(--ph-t-small);
  margin-top: var(--ph-5);
}
.notice p { max-width: 62ch; }

/* ── footer ──────────────────────────────────────────────────────────── */

.foot {
  background: var(--ph-paper-deep);
  border-top: 1px solid var(--ph-line);
  padding-block: var(--ph-stack) var(--ph-block);
}
.foot-top { display: flex; flex-wrap: wrap; gap: var(--ph-6); justify-content: space-between; }
/* Same rule as the header: the size goes on the lockup, not on the parts. */
.foot .ph-lockup { text-decoration: none; min-height: 44px; border-radius: var(--ph-r-sm); font-size: 1.5rem; }
.foot-said { margin-top: var(--ph-4); font-size: var(--ph-t-small); color: var(--ph-mute); max-width: 44ch; }

.foot-nav { display: flex; flex-wrap: wrap; gap: var(--ph-6); }
.foot-nav h2 {
  font-family: var(--ph-mono);
  font-size: var(--ph-t-micro);
  font-weight: 400;
  letter-spacing: var(--ph-tr-micro);
  text-transform: uppercase;
  color: var(--ph-mute);
  margin-bottom: var(--ph-3);
}
.foot-nav ul { list-style: none; padding: 0; }
/* 44px per DESIGN-GUIDE 8. The row margin is gone because the target height
   now supplies the spacing; stacking both would space the list twice. */
.foot-nav a {
  display: flex; align-items: center;
  min-height: 44px; padding-block: var(--ph-2);
  font-size: var(--ph-t-small); color: var(--ph-ink);
  text-decoration: none; border-radius: var(--ph-r-sm);
}
.foot-nav a:hover { color: var(--ph-deep); text-decoration: underline; text-underline-offset: 3px; }

.foot-base {
  display: flex; flex-wrap: wrap; gap: var(--ph-3) var(--ph-5);
  align-items: baseline;
  margin-top: var(--ph-stack);
  padding-top: var(--ph-4);
  border-top: 1px solid var(--ph-line);
  font-size: var(--ph-t-small);
  color: var(--ph-mute);
}
.foot-base p { max-width: none; }
