/* ============================================================================
   Orbexa Precision Bearings — BLOCK LAYER  build/blocks.css
   ----------------------------------------------------------------------------
   THE SINGLE PAGE-LEVEL STYLESHEET. Page workers APPEND to this file. They do
   not create page-blocks-*.css, inner-pages.css or any second stylesheet: one
   selector defined in two files is the failure this rule exists to prevent
   (hard rule 54, checked by scripts/check-css-conflicts.py).

   Design language: STANDARD OF RECORD. Paper white, ink rules, cobalt as an
   event colour at roughly 2% of a screen, gold only on accreditation marks.
   Surfaces are divided by rules, not by cards or corners.

   THREE THINGS THIS FILE DELIBERATELY DOES NOT DO
     · No `:root` block. Custom properties live in /design-tokens.css only, so
       the same selector never appears in two stylesheets.
     · No `@keyframes`. Motion primitives are declared once in design-tokens.css
       (orbexa-rise, orbexa-sweep) so no two files declare the same animation.
     · No universal selector and no `body` rule collision — `*`, `html`, `hr`,
       `b, strong`, `::selection`, `:focus-visible` and the box-sizing reset all
       belong to design-tokens.css. Use `.selector:focus-visible` here.

   CLEARANCE RULES BAKED IN (verified by scripts/check-layout-edges.js)
     · every element carrying a border-top also carries padding-top >= 16px;
       no text ever sits closer than 16px to a rule, above or below it
     · every element carrying a border-left carries >= 24px of padding on the
       text side
     · the page container keeps >= 20px of padding at every width, so no text
       ever touches the viewport edge
     · a solo image is capped at 55vh; an image beside text or in a grid follows
       its container instead (that is what the __media class names signal)

   IMAGE CLASS NAMES ARE A CONTRACT
     Any wrapper whose class contains `__media` marks its image as a
     beside-text / in-grid image. Use it only where that is true. A figure that
     stands alone uses .figure__img, which is capped at 55vh.
   ========================================================================== */

/* ═══ 1 · Base ═════════════════════════════════════════════════════════════ */

body {
  margin: 0;
  background-color: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--w-body);
  line-height: var(--lh-body);
  overflow-wrap: break-word;
}

h1,
h2,
h3,
h4 {
  margin: 0 0 var(--sp-5);
  color: var(--ink);
  overflow-wrap: break-word;
}

h1 {
  font-family: var(--font-display);
  font-weight: var(--w-display);
  font-size: var(--fs-h1);
  line-height: var(--lh-h1);
  letter-spacing: var(--tr-title);
}

h2 {
  font-family: var(--font-display);
  font-weight: var(--w-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--tr-title);
}

h3 {
  font-family: var(--font-body);
  font-weight: var(--w-body-strong);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  letter-spacing: var(--tr-body);
}

h4 {
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  line-height: var(--lh-ui);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--ink-2);
}

p {
  margin: 0 0 var(--sp-4);
}

/* Inline links are ink with an ink underline: the underline is the second
   cue, so the link is never identified by colour alone (WCAG 1.4.1).
   INK #161616 on PAPER #FFFFFF = 18.10:1 · on PANEL #F5F5F3 = 16.58:1
   COBALT #1848A8 on PAPER = 8.30:1 (hover/focus)                          */
a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color var(--dur-1) var(--ease);
}

a:hover,
a:focus-visible {
  color: var(--accent);
}

ul,
ol {
  margin: 0 0 var(--sp-4);
  padding-left: var(--sp-5);
}

li + li {
  margin-top: var(--sp-2);
}

img {
  max-width: 100%;
}

figure {
  margin: 0;
}

/* ═══ 2 · Layout ══════════════════════════════════════════════════════════ */

/* The page container. --container-pad is 24px on desktop and 20px on the
   narrowest screen; the floor is 16px so text never touches the edge. */
.wrap {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

.wrap--tight {
  max-width: 880px;
}

/* The 12-column grid. Every vertical rhythm in the site is a multiple of this
   gutter, and the gutter is also the minimum clearance between text and media
   (32px desktop, 24px narrow, 20px narrowest — all above the 16px floor). */
.grid12 {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--gutter);
}

.col-2 {
  grid-column: span 2;
  min-width: 0;
}

.col-3 {
  grid-column: span 3;
  min-width: 0;
}

.col-4 {
  grid-column: span 4;
  min-width: 0;
}

.col-5 {
  grid-column: span 5;
  min-width: 0;
}

.col-6 {
  grid-column: span 6;
  min-width: 0;
}

.col-7 {
  grid-column: span 7;
  min-width: 0;
}

.col-8 {
  grid-column: span 8;
  min-width: 0;
}

.col-9 {
  grid-column: span 9;
  min-width: 0;
}

.col-12 {
  grid-column: span 12;
  min-width: 0;
}

/* Section rhythm. --section-y is 96px desktop, 64px at <=900px and 48px at
   <=560px — the hard floor is 48px and it is set in design-tokens.css, not
   overridden here. */
.section {
  padding-block: var(--section-y);
}

.section--panel {
  background-color: var(--panel);
}

.section--plate {
  background-color: var(--plate);
  color: var(--on-plate);
}

.section--plate h1,
.section--plate h2,
.section--plate h3 {
  color: var(--on-plate);
}

/* The article limit. Nothing on this site runs wider than 680px of running
   prose, at any viewport. */
.measure {
  max-width: var(--measure);
}

/* ═══ 3 · The rule system ═════════════════════════════════════════════════ */

.rule {
  height: 0;
  margin: var(--sp-6) 0;
  border: 0;
  border-top: var(--hair);
}

.rule--heavy {
  border-top: var(--heavy);
}

.rule--accent {
  border-top: var(--rule-heavy-w) solid var(--accent);
}

.rule--plate {
  border-top: var(--hair-on-plate);
}

/* An eyebrow sitting on a rule — the P3 layer of the design language. */
.rule-label {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}

.rule-label::after {
  content: "";
  flex: 1 1 auto;
  height: 0;
  border-top: var(--hair);
}

.rule-label--heavy::after {
  border-top: var(--heavy);
}

/* A cobalt short rule. Decorative only — put [data-reveal] on it and it fades
   in as the reader arrives; it is empty by construction, which is what makes
   the reveal incapable of hiding anything. */
.rule-sweep {
  display: block;
  width: 64px;
  height: var(--rule-heavy-w);
  margin-bottom: var(--sp-5);
  border: 0;
  background-color: var(--accent);
}

/* ═══ 4 · Type utilities ══════════════════════════════════════════════════ */

/* EYEBROW — IBM Plex Mono 500 11px, .12em, uppercase.
   INK-3 #6B6B6B on PAPER #FFFFFF = 5.33:1 · on PANEL #F5F5F3 = 4.88:1
   On the ink plate use .section--plate .eyebrow (ON-PLATE-2 = 12.42:1). */
.eyebrow {
  margin: 0 0 var(--sp-3);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  line-height: var(--lh-ui);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--ink-3);
}

.section--plate .eyebrow,
.cta-band--plate .eyebrow,
.modal__copy .eyebrow {
  color: var(--on-plate-2);
}

/* LEAD — Public Sans 400 19/17px lh 1.6.
   INK-2 #4A4A4A on PAPER = 8.86:1 · on PANEL = 8.12:1                     */
.lead {
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  color: var(--ink-2);
  max-width: 46ch;
}

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

/* Numerals in a data context: mono, tabular, right-aligned.
   INK #161616 on PAPER = 18.10:1 · INK-3 #6B6B6B = 5.33:1                  */
.num {
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-data);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.accent-text {
  color: var(--accent);
}

/* The one emphasised phrase per page may be gold — text-safe gold only.
   GOLD-INK #8A6A1E on PAPER = 5.05:1 · on PANEL = 4.62:1.
   NOT permitted on --panel-2 (4.30:1, below the floor).                   */
.mark {
  color: var(--gold-ink);
}

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

/* Bot trap: off-screen, but NOT display:none and not type="hidden" — simple
   bots skip both of those. */
.sr-honeypot {
  position: absolute;
  left: -9999px;
  width: 0;
  height: 0;
  padding: 0;
  border: 0;
  overflow: hidden;
}

/* Skip link. Sits off the top of the viewport until it takes focus. */
.skip {
  position: absolute;
  left: var(--sp-4);
  top: -160px;
  z-index: calc(var(--z-modal) + 10);
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: var(--sp-3) var(--sp-5);
  background-color: var(--ink);
  color: var(--paper);
  font-weight: var(--w-body-strong);
  text-decoration: none;
  transition: top var(--dur-2) var(--ease);
}

.skip:focus {
  top: var(--sp-4);
  color: var(--paper);
}

/* ═══ 5 · Controls ════════════════════════════════════════════════════════ */

/* Every control clears 44px in both axes.
   PAPER #FFFFFF on COBALT #1848A8 = 8.30:1 (primary)
   PAPER on COBALT-DARK #123A8A = 10.51:1 (primary hover)
   INK #161616 on PAPER = 18.10:1 (line variant)                            */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  min-width: var(--tap);
  padding: var(--sp-3) var(--sp-5);
  border: var(--rule-heavy-w) solid transparent;
  border-radius: var(--radius-0);
  background-color: transparent;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: var(--w-body-strong);
  line-height: var(--lh-ui);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease),
    border-color var(--dur-1) var(--ease), color var(--dur-1) var(--ease),
    transform var(--dur-1) var(--ease);
}

.btn:active {
  transform: translate3d(0, 1px, 0);
}

.btn--primary {
  background-color: var(--accent);
  border-color: var(--accent);
  color: var(--paper);
}

.btn--primary:hover,
.btn--primary:focus-visible {
  background-color: var(--accent-dark);
  border-color: var(--accent-dark);
  color: var(--paper);
}

.btn--line {
  background-color: transparent;
  border-color: var(--ink);
  color: var(--ink);
}

.btn--line:hover,
.btn--line:focus-visible {
  background-color: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

.btn--quiet {
  min-height: var(--tap);
  padding-inline: 0;
  border-color: transparent;
  background-color: transparent;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.btn--quiet:hover,
.btn--quiet:focus-visible {
  color: var(--accent);
}

.section--plate .btn--primary,
.cta-band--plate .btn--primary {
  background-color: var(--paper);
  border-color: var(--paper);
  color: var(--ink);
}

.section--plate .btn--primary:hover,
.cta-band--plate .btn--primary:hover {
  background-color: var(--on-plate-2);
  border-color: var(--on-plate-2);
  color: var(--ink);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-5);
}

/* On the ink plate the cobalt focus ring is too close to the ground it sits
   on; the lifted cobalt replaces it.
   FOCUS-ON-PLATE #A8C4F5 on PLATE #161616 = 10.24:1                      */
.foot :focus-visible,
.section--plate :focus-visible,
.cta-band--plate :focus-visible,
.modal__copy :focus-visible {
  outline-color: var(--focus-on-plate);
}

/* ═══ 6 · Icons ═══════════════════════════════════════════════════════════ */

/* The sprite is hidden as a box rather than with display:none — display:none
   on an <svg> container breaks <use> in older engines, and a zero box keeps
   the layout checks from reading it as content. */
.icon-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.icon {
  width: var(--icon);
  height: var(--icon);
  flex: none;
  vertical-align: middle;
  color: inherit;
}

.icon-16 {
  width: 16px;
  height: 16px;
}

.icon-20 {
  width: 20px;
  height: 20px;
}

.icon-24 {
  width: 24px;
  height: 24px;
}

/* ═══ 7 · Masthead ════════════════════════════════════════════════════════ */

.masthead {
  position: sticky;
  top: 0;
  z-index: var(--z-nav);
  background-color: var(--paper);
  border-bottom: var(--hair);
}

.masthead__inner {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  min-height: 72px;
}

/* BRAND. The mark is a datum ring in the document-mark motif; the cobalt tick
   is the single event colour in the masthead.
   The mark is a PLACEHOLDER — the client supplied no original artwork. The
   footer legal row says so in public; see build/design-blueprint.md.
   BRAND-NAME #161616 on PAPER = 18.10:1 · BRAND-SUB #6B6B6B = 5.33:1        */
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--tap);
  color: var(--ink);
  text-decoration: none;
}

.brand:hover,
.brand:focus-visible {
  color: var(--ink);
}

.brand__mark {
  width: 34px;
  height: 34px;
  flex: none;
  color: var(--ink);
}

.brand__tick {
  stroke: var(--accent);
}

.brand__word {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.brand__name {
  font-family: var(--font-display);
  font-weight: var(--w-display);
  font-size: 22px;
  line-height: 1;
  letter-spacing: var(--tr-title);
  color: var(--ink);
}

.brand__sub {
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  line-height: var(--lh-ui);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--ink-3);
}

/* PRIMARY NAV.
   NAV-LINK #161616 on PAPER = 18.10:1. The current page is marked with a
   2px cobalt bar (an event colour use, one per page) plus the aria-current
   attribute that the page worker adds — never baked into the shared block. */
.nav {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  margin-left: auto;
}

.nav__list {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav__list li + li {
  margin-top: 0;
}

.nav__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding-inline: var(--sp-1);
  color: var(--ink);
  font-size: 15px;
  font-weight: var(--w-body);
  text-decoration: none;
  white-space: nowrap;
}

.nav__link:hover,
.nav__link:focus-visible {
  color: var(--accent);
  text-decoration: none;
}

.nav__link[aria-current="page"] {
  color: var(--ink);
  font-weight: var(--w-body-strong);
}

.nav__link[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--rule-heavy-w);
  background-color: var(--accent);
  transform-origin: left center;
  animation: orbexa-sweep var(--dur-3) var(--ease-out) both;
}

.nav__cta {
  white-space: nowrap;
}

/* The mobile menu control. It is a real box at every width below 900px, which
   is what makes the navigation reachable on a phone. */
/* Hidden above the breakpoint and switched on below it. The base MUST be
   `display: none` — leaving it inline-flex puts a MENU button in the middle of
   a 1440px masthead. */
.nav-toggle {
  display: none;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  min-width: var(--tap);
  margin-left: auto;
  padding: var(--sp-2) var(--sp-3);
  border: var(--rule-heavy-w) solid var(--ink);
  border-radius: var(--radius-0);
  background-color: transparent;
  color: var(--ink);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  cursor: pointer;
}

.nav-toggle__close {
  display: none;
}

.nav-toggle[aria-expanded="true"] {
  border-color: var(--accent);
  color: var(--accent);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__open {
  display: none;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__close {
  display: block;
}

/* ═══ 8 · Footer ══════════════════════════════════════════════════════════ */

/* The footer is an ink plate: PAPER #FFFFFF on PLATE #161616 = 18.10:1,
   ON-PLATE-2 #D6D6D2 on PLATE = 12.42:1.                                    */
.foot {
  border-top: var(--hair-on-plate);
  background-color: var(--plate);
  color: var(--on-plate);
  padding-block: var(--section-y) var(--sp-8);
}

.foot h2 {
  color: var(--on-plate-2);
}

.foot__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gutter);
}

/* min-width:0 on every column: a long address or an unbreakable mono label
   must wrap, not widen the column and push the grid past the viewport. */
.foot__col {
  min-width: 0;
}

.foot__head {
  margin: 0 0 var(--sp-4);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  line-height: var(--lh-ui);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--on-plate-2);
}

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

.foot__list li + li {
  margin-top: 0;
}

.foot__list a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  color: var(--on-plate);
  text-decoration: none;
}

.foot__list a:hover,
.foot__list a:focus-visible {
  color: var(--accent-lift);
  text-decoration: underline;
}

.foot__promise {
  max-width: 34ch;
  color: var(--on-plate-2);
}

.foot__mail {
  margin-bottom: var(--sp-4);
}

.foot__mail a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--tap);
  color: var(--on-plate);
  text-decoration: none;
}

.foot__mail a:hover,
.foot__mail a:focus-visible {
  color: var(--accent-lift);
}

.foot__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4) var(--sp-7);
  justify-content: space-between;
  align-items: flex-start;
  margin-top: var(--sp-8);
  padding-top: var(--sp-5);
  border-top: var(--hair-on-plate);
}

.foot__copy {
  margin: 0;
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-data);
  color: var(--on-plate);
}

.foot__note {
  margin: 0;
  max-width: 62ch;
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--on-plate-2);
}

/* ═══ 9 · Hero ════════════════════════════════════════════════════════════ */

.hero {
  padding-block: var(--sp-10) var(--sp-9);
}

.hero__inner {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--gutter);
  align-items: center;
}

.hero__text {
  grid-column: 1 / span 7;
  min-width: 0;
}

.hero__media {
  grid-column: 8 / span 5;
  position: relative;
  min-height: 280px;
  background-color: var(--panel);
}

.hero__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero__title {
  max-width: 20ch;
  margin-bottom: var(--sp-5);
}

.hero__lead {
  max-width: 44ch;
  margin-bottom: var(--sp-6);
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  color: var(--ink-2);
}

/* The signature move: the measured runout figure set in mono at display size,
   sitting on a 2px ink rule. Used exactly twice on the site — the home page
   and /products/. */
.hero__signature {
  margin-top: var(--sp-6);
  padding-top: var(--sp-4);
  border-top: var(--heavy);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: clamp(26px, 4.4vw, 52px);
  line-height: 1.1;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.hero__signature span {
  display: block;
  margin-top: var(--sp-2);
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--ink-3);
}

/* Full-bleed hero: the photograph is a backdrop layer (absolutely positioned,
   full viewport width), the text sits on a graded plate. */
.hero--bleed {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: 62vh;
  padding-block: 0;
  background-color: var(--plate);
  overflow: hidden;
}

.hero--bleed .hero__media {
  position: absolute;
  inset: 0;
  grid-column: auto;
  min-height: 0;
  background-color: transparent;
}

.hero--bleed .hero__inner {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  padding-block: var(--section-y);
  background-image: var(--grade-plate);
}

.hero--bleed .hero__title,
.hero--bleed .hero__signature {
  color: var(--on-plate);
  text-shadow: var(--shadow-text-plate);
}

.hero--bleed .hero__title {
  max-width: 24ch;
}

.hero--bleed .hero__lead {
  color: var(--on-plate-2);
}

.hero--bleed .eyebrow {
  color: var(--on-plate-2);
}

/* ═══ 10 · Split media ════════════════════════════════════════════════════ */

.split {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--gutter);
  align-items: stretch;
}

.split__text {
  grid-column: 1 / span 7;
  min-width: 0;
  align-self: center;
}

.split__media {
  grid-column: 8 / span 5;
  position: relative;
  min-width: 0;
  min-height: 300px;
  /* The media column stretches to the text column's height — that is what
     "half width, full height" means, and it is right while the text is a
     screen or so. Left uncapped it is not: a long list opposite a 5/12 image
     stretched the picture to 1209px at a 900px viewport, 134% of the screen,
     which is the "one huge image in your face" defect the layout gate exists
     to catch. Above the cap the image sits at the top of its column. */
  max-height: 88vh;
  background-color: var(--panel);
}

.split__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.split--flip .split__text {
  grid-column: 6 / span 7;
  order: 2;
}

.split--flip .split__media {
  grid-column: 1 / span 5;
  order: 1;
}

/* ═══ 11 · Figures ════════════════════════════════════════════════════════ */

/* A figure that stands alone. Capped at 55vh so a 3/2 image at full measure
   can never become a wall — the container crops instead of growing. */
.figure__img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3/2;
  max-height: 55vh;
  object-fit: cover;
  background-color: var(--panel);
}

.figure__img--portrait {
  aspect-ratio: 2/3;
}

.figure__img--square {
  aspect-ratio: 1/1;
}

/* FIG. N captions — Plex Mono, uppercase, on a hairline.
   FIG-NO INK-2 #4A4A4A on PAPER = 8.86:1
   FIG-CAP INK-3 #6B6B6B on PAPER = 5.33:1 (the caption is supporting text,
   not body copy — if it ever becomes a paragraph, promote it to .muted)   */
.fig-cap {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-3);
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: var(--hair);
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--ink-3);
}

.fig-cap__no {
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
}

/* ═══ 12 · Spec table ═════════════════════════════════════════════════════ */

/* Desktop: the table takes the measure it is given and never forces a horizontal
   scroll (min-width: 0). Below 900px the columns would crush, so the table takes
   a 560px floor and the wrapper scrolls instead — readable columns, no squashed
   digits, and no horizontal overflow of the document itself. */
.spec-scroll {
  border-top: var(--heavy);
  padding-top: var(--sp-4);
  overflow-x: auto;
}

table.spec {
  width: 100%;
  min-width: 0;
  border-collapse: collapse;
  text-align: left;
  font-size: var(--fs-body);
}

table.spec caption {
  caption-side: top;
  padding-bottom: var(--sp-4);
  text-align: left;
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--ink-3);
}

table.spec th,
table.spec td {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: var(--hair);
  vertical-align: top;
}

/* The plate rule: a 2px ink line under the header row. It is the second thing
   the eye lands on in a parameter screen, after the header itself. */
table.spec thead th {
  padding-block: var(--sp-4);
  border-bottom: var(--heavy);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  line-height: var(--lh-ui);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
}

table.spec tbody th {
  font-weight: var(--w-body-strong);
  color: var(--ink);
}

table.spec tbody tr:last-child th,
table.spec tbody tr:last-child td {
  border-bottom: 0;
}

/* Right-aligned mono numbers. DATA INK #161616 on PAPER = 18.10:1;
   DATA-3 #6B6B6B = 5.33:1 when a column is deliberately de-emphasised. */
.spec__num {
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-data);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
  color: var(--ink);
}

.spec__num--soft {
  color: var(--ink-3);
}

/* A tolerance column. GOLD-INK #8A6A1E on PAPER = 5.05:1 — never on panel-2. */
.spec__tol {
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-data);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
  color: var(--gold-ink);
}

.spec__note {
  margin-top: var(--sp-3);
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--ink-3);
}

/* Comparison matrix — same rules, narrower type. */
.cmp-scroll {
  border-top: var(--heavy);
  padding-top: var(--sp-4);
  overflow-x: auto;
}

table.cmp {
  width: 100%;
  min-width: 0;
  border-collapse: collapse;
  text-align: left;
}

table.cmp th,
table.cmp td {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: var(--hair);
}

table.cmp thead th {
  border-bottom: var(--heavy);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--ink-2);
}

table.cmp tbody th {
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-data);
  color: var(--ink-2);
}

/* ═══ 13 · Process steps ══════════════════════════════════════════════════ */

/* Numbered and ruled, vertically. Numbers are one large digit or an 01–09
   pair in mono; each step is separated by a hairline rule. No time estimates —
   a delivery promise belongs in the response promise, not beside a step. */
.steps {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}

.steps > li {
  display: grid;
  grid-template-columns: 3.5rem minmax(0, 1fr);
  gap: var(--sp-4);
  padding-block: var(--sp-5);
  border-top: var(--hair);
}

.steps > li:first-child {
  border-top: var(--heavy);
}

.steps__num {
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--ink-3);
}

.steps h3 {
  margin-bottom: var(--sp-2);
}

.steps p:last-child {
  margin-bottom: 0;
}

/* ═══ 14 · Numbered ruled list ════════════════════════════════════════════ */

.numbered {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: num;
}

.numbered > li {
  display: grid;
  grid-template-columns: 3.5rem minmax(0, 1fr);
  gap: var(--sp-4);
  padding-block: var(--sp-5);
  border-top: var(--hair);
}

.numbered > li:first-child {
  border-top: var(--heavy);
}

.numbered > li::before {
  counter-increment: num;
  content: counter(num, decimal-leading-zero);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--ink-3);
}

.numbered h3 {
  margin-bottom: var(--sp-2);
}

.numbered p:last-child {
  margin-bottom: 0;
}

/* ═══ 15 · Card grid ══════════════════════════════════════════════════════ */

.card-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gutter);
  margin: 0;
  padding: 0;
  list-style: none;
}

.card-grid > li {
  margin-top: 0;
  min-width: 0;
}

.card {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding-top: var(--sp-4);
  border-top: var(--heavy);
}

.card__media {
  display: block;
  min-width: 0;
  aspect-ratio: 3/2;
  overflow: hidden;
  background-color: var(--panel);
}

.card__media--square {
  aspect-ratio: 1/1;
}

.card__media--portrait {
  aspect-ratio: 2/3;
}

.card__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.card__body {
  padding-top: var(--sp-4);
}

.card__no {
  display: block;
  margin-bottom: var(--sp-2);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--ink-3);
}

.card h3 {
  margin-bottom: var(--sp-2);
}

.card p:last-child {
  margin-bottom: 0;
}

/* ═══ 16 · Data strip ═════════════════════════════════════════════════════ */

/* The data strip is the one place numbers are allowed to be the largest thing
   on the screen. Values are mono and tabular so columns of figures line up. */
.data-strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin: 0;
  padding: 0;
  border-top: var(--heavy);
  list-style: none;
}

.data-strip > li {
  margin-top: 0;
  min-width: 0;
}

.data-strip__cell {
  height: 100%;
  padding: var(--sp-5) var(--sp-5);
  border-left: var(--hair);
  border-bottom: var(--hair);
}

.data-strip__cell--first {
  padding-left: 0;
  border-left: 0;
}

/* DATA-STRIP VALUE #161616 on PAPER = 18.10:1
   DATA-STRIP LABEL #6B6B6B on PAPER = 5.33:1                              */
.data-strip__val {
  display: block;
  margin-bottom: var(--sp-2);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: clamp(20px, 2.4vw, 30px);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.data-strip__label {
  display: block;
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  line-height: var(--lh-ui);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ═══ 17 · Certificate wall ═══════════════════════════════════════════════ */

/* Certificate scans are shown as images ONLY. Certificate numbers are never
   transcribed into page text anywhere on this site (content-policy R1). The
   gold rule under the plate is the accreditation mark colour.
   Gold here is a RULE, not text: it never has to clear a text ratio. */
.cert-wall {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gutter);
  margin: 0;
  padding: 0;
  list-style: none;
}

.cert {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding-top: var(--sp-4);
  border-top: var(--rule-heavy-w) solid var(--gold);
}

/* aspect-ratio with no min-height and no max-height: a max-height would
   transfer through the ratio into a max-WIDTH of 55vh x 2/3, which at a 844px
   viewport caps every certificate at 309px and leaves the wall visibly short
   of its column. The scan follows the column instead. */
.cert__media {
  display: block;
  min-width: 0;
  aspect-ratio: 2/3;
  overflow: hidden;
  background-color: var(--panel);
}

.cert__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
}

.cert__cap {
  margin-top: var(--sp-3);
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--ink-2);
}

.cert__cap b {
  display: block;
  color: var(--ink);
}

.cert__mark {
  display: block;
  margin-bottom: var(--sp-2);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--gold-ink);
}

/* ═══ 18 · Icon row ═══════════════════════════════════════════════════════ */

.icon-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gutter);
  margin: 0;
  padding: 0;
  list-style: none;
}

.icon-row__item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  min-width: 0;
}

.icon-row__item .icon {
  margin-top: 2px;
  color: var(--accent);
}

.icon-row__label {
  display: block;
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--ink-2);
}

.icon-row__label b {
  display: block;
  color: var(--ink);
}

/* ═══ 19 · FAQ ════════════════════════════════════════════════════════════ */

.faq {
  margin: 0;
  border-top: var(--heavy);
}

.faq-item {
  border-top: var(--hair);
}

.faq-item:first-child {
  border-top: 0;
}

/* FAQ QUESTION #161616 on PAPER = 18.10:1 */
.faq-q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  width: 100%;
  min-height: var(--tap);
  padding-block: var(--sp-4);
  border: 0;
  background-color: transparent;
  font-family: var(--font-body);
  font-weight: var(--w-body-strong);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  text-align: left;
  color: var(--ink);
  cursor: pointer;
}

.faq-q:hover {
  color: var(--accent);
}

.faq-q__icon {
  flex: none;
  color: var(--accent);
  transition: transform var(--dur-2) var(--ease);
}

.faq-item.open .faq-q__icon {
  transform: rotate(180deg);
}

/* FAQ ANSWER #4A4A4A on PAPER = 8.86:1 */
.faq-a {
  max-width: var(--measure);
  padding-bottom: var(--sp-5);
  color: var(--ink-2);
}

.faq-a > :last-child {
  margin-bottom: 0;
}

/* Without the `js` class — which only /app.js adds — every answer stays on
   screen. A reader with scripting off reads the whole page rather than an
   accordion that cannot open. */
html.js .faq-a {
  display: none;
}

html.js .faq-item.open .faq-a {
  display: block;
}

/* ═══ 20 · CTA band ═══════════════════════════════════════════════════════ */

.cta-band {
  padding-block: var(--section-y);
  border-top: var(--heavy);
}

.cta-band--plate {
  background-color: var(--plate);
  color: var(--on-plate);
}

.cta-band--plate h2,
.cta-band--plate h3 {
  color: var(--on-plate);
}

.cta-band--plate .lead {
  color: var(--on-plate-2);
}

.cta-band__inner {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--gutter);
  align-items: start;
}

.cta-band__text {
  grid-column: 1 / span 5;
  min-width: 0;
}

.cta-band__form {
  grid-column: 6 / span 7;
  min-width: 0;
}

/* ═══ 21 · The ruled coupon (inline reply form) ═══════════════════════════ */

/* A reply slip, not a card: a 2px rule across the top, fields ruled beneath
   each other, and a mono label above every field. This is the second of the
   site's three conversion forms; a page carries only one of them. */
.coupon {
  display: grid;
  gap: var(--sp-5);
  padding-top: var(--sp-5);
  border-top: var(--heavy);
}

.coupon--plain {
  padding-top: var(--sp-4);
  border-top: var(--hair);
}

.field-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-4);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin: 0;
  min-width: 0;
}

/* FIELD LABEL INK-2 #4A4A4A on PAPER = 8.86:1 */
.field label {
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  line-height: var(--lh-ui);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--ink-2);
}

/* FIELD INPUT INK #161616 on PAPER = 18.10:1, input text.
   The input border is a non-text boundary: INK-FROZEN-3 #7A7A7A on PAPER
   = 4.29:1, comfortably above the 3:1 floor for a UI boundary — this is one
   of the two places the frozen third grey is still allowed to be used. */
.field input,
.field textarea,
.field select {
  width: 100%;
  min-height: var(--tap);
  padding: var(--sp-2) var(--sp-3);
  border: var(--rule-w) solid var(--ink-frozen-3);
  border-radius: var(--radius-1);
  background-color: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-ui);
  transition: border-color var(--dur-1) var(--ease),
    box-shadow var(--dur-1) var(--ease);
}

.field textarea {
  min-height: 132px;
  resize: vertical;
}

.field input:hover,
.field textarea:hover {
  border-color: var(--ink);
}

.field input:focus,
.field textarea:focus,
.field select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
  outline: none;
}

.field select option {
  color: var(--ink);
  background-color: var(--paper);
}

.field__hint {
  margin: var(--sp-4) 0 0;
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--ink-3);
  max-width: 62ch;
}

.ctl-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-5);
}

/* The tick list — document choices, one ruled row each. */
.ticks {
  display: grid;
  gap: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.tick {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  min-height: var(--tap);
  padding-block: var(--sp-3);
  border-bottom: var(--hair);
  cursor: pointer;
}

.tick:last-child {
  border-bottom: 0;
}

.tick input {
  flex: none;
  width: 20px;
  height: 20px;
  margin: 3px 0 0;
  accent-color: var(--accent);
}

.tick__doc {
  display: block;
  font-size: var(--fs-body);
  line-height: var(--lh-ui);
  color: var(--ink);
}

.tick__doc b {
  display: block;
  font-weight: var(--w-body);
  font-size: var(--fs-data);
  color: var(--ink-2);
}

/* ═══ 22 · Reveal (decorative only) ═══════════════════════════════════════ */

/* Scoped to [data-reveal]:empty — an element with no children and no text.
   A rule flourish, a wash layer, an icon plate. Text, tables and figures are
   structurally outside the reach of this rule: they are never :empty, so no
   observer failure can ever leave them invisible. The `html.js` guard means
   nothing is hidden at all unless /app.js ran and added the class. */
html.js [data-reveal]:empty {
  opacity: 0;
  transform: translate3d(0, 12px, 0);
  transition: opacity var(--dur-3) var(--ease-out),
    transform var(--dur-3) var(--ease-out);
}

html.js [data-reveal]:empty.is-revealed {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* ═══ 23 · Modal ══════════════════════════════════════════════════════════ */

/* The scroll lock is a class. document.body.style.overflow would be an inline
   style, and style-src 'self' drops inline styles — the page would scroll
   behind the dialog. See /app.js. */
body.modal-open {
  overflow: hidden;
}

.modal {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--sp-4);
}

.modal.open {
  display: flex;
}

/* The scrim is a real <button>: "click the background to close" needs no extra
   binding and a keyboard can reach it. */
.modal__scrim {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background-color: var(--scrim);
  backdrop-filter: blur(3px) saturate(0.9);
  -webkit-backdrop-filter: blur(3px) saturate(0.9);
  cursor: pointer;
}

.modal__dialog {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  /* The row must be an explicitly shrinkable track. A default `auto` track is
     sized by its content and ignores the container's max-height, so the dialog
     grows past 92vh and the submit button is clipped off the bottom edge. */
  grid-template-rows: minmax(0, 1fr);
  width: 100%;
  max-width: 1040px;
  max-height: 92vh;
  border: var(--hair);
  background-color: var(--paper);
  overflow: hidden;
}

/* Grid children default to min-height:auto — "never shorter than my content" —
   which silently defeats overflow-y:auto on the panel and min-width on the
   media column. Both have to be zeroed. */
.modal__media,
.modal__panel {
  min-width: 0;
  min-height: 0;
}

/* System dark mode must not repaint the dialog. */
.modal__dialog *,
.modal__dialog *::before,
.modal__dialog *::after {
  color-scheme: light;
}

/* ── Left: the photograph and its claim. The wrapper is named __media so the
   layout checks read it as a beside-text image, which it is. ───────────── */
.modal__media {
  position: relative;
  min-height: 320px;
  background-color: var(--panel-2);
  background-image: var(--glow-accent);
  overflow: hidden;
}

.modal__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The copy sits on an opaque ink plate graded into the photograph.
   The plate's alpha is above 0.5 on purpose: below that a contrast checker
   walks past it to the light panel behind, and white type over a light
   surface measures about 1.1:1. Here:
     B #FFFFFF on the plate, PLATE #161616 = 18.10:1
     SPAN ON-PLATE-2 #D6D6D2 on the plate = 12.42:1                         */
.modal__copy {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: var(--sp-6) var(--sp-5);
  background-color: var(--scrim-deep);
  background-image: var(--grade-plate);
  color: var(--on-plate);
}

/* The global `b, strong { color: var(--ink) }` in design-tokens.css would
   otherwise paint this heading dark on a dark plate. It must be re-declared. */
.modal__copy b {
  display: block;
  margin-bottom: var(--sp-2);
  font-family: var(--font-display);
  font-weight: var(--w-display);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  letter-spacing: var(--tr-title);
  color: var(--on-plate);
  text-shadow: var(--shadow-text-plate);
}

.modal__copy span {
  display: block;
  max-width: 40ch;
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--on-plate-2);
}

/* ── Right: the request slip ───────────────────────────────────────────── */
.modal__panel {
  position: relative;
  padding: var(--sp-7) var(--sp-6) var(--sp-6);
  background-color: var(--paper);
  color: var(--ink);
  overflow-y: auto;
}

.modal__close {
  position: absolute;
  top: var(--sp-3);
  right: var(--sp-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  padding: 0;
  border: var(--rule-w) solid var(--rule);
  border-radius: var(--radius-0);
  background-color: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease),
    color var(--dur-1) var(--ease);
}

.modal__close:hover {
  border-color: var(--ink);
  color: var(--accent);
}

.modal__close svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
}

.modal__panel h3 {
  margin: var(--sp-2) 0 0;
  max-width: 26ch;
}

.modal__sub {
  max-width: 46ch;
  margin: var(--sp-2) 0 var(--sp-5);
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--ink-2);
}

.modal__form {
  display: grid;
  gap: var(--sp-4);
  width: 100%;
}

.modal__form .ticks {
  margin-bottom: var(--sp-2);
}

.modal__form .ctl-row {
  margin-top: var(--sp-2);
}

/* ── Narrow screens: one column, the photograph reduced to a banner ─────── */
@media (max-width: 780px) {
  .modal__dialog {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto;
    overflow-y: auto;
  }

  .modal__media {
    min-height: 132px;
    max-height: 180px;
  }

  /* The photograph's own caption says the same thing as the panel heading
     below it. Keeping both squeezes the picture until the claim is clipped,
     and squeezes the form until the submit button is off-screen. */
  .modal__copy {
    display: none;
  }

  .modal__panel {
    padding: var(--sp-6) var(--sp-4) var(--sp-5);
    overflow-y: visible;
  }
}

/* ═══ 24 · Responsive ═════════════════════════════════════════════════════ */

@media (max-width: 900px) {
  /* Below 900px a spec table gets a floor and scrolls; above it, it fits. */
  table.spec {
    min-width: 560px;
  }

  .grid12 {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-5);
  }

  .grid12 > * {
    grid-column: auto;
  }

  .hero__text,
  .hero__media,
  .split__text,
  .split__media,
  .split--flip .split__text,
  .split--flip .split__media,
  .cta-band__text,
  .cta-band__form {
    grid-column: auto;
    order: 0;
  }

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

  .hero__media {
    min-height: 0;
    aspect-ratio: 3/2;
  }

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

  /* ⛔ Aspect-ratio and min-height together are a trap: the min-height is
     TRANSFERRED through the ratio into a minimum WIDTH. 240px against 3/2
     becomes `min-width: 360px`, the item stops shrinking at 360px inside a
     335px track, and the document scrolls 5px sideways at 375px wide.
     min-height is therefore zeroed wherever an aspect-ratio is set.          */
  .split__media {
    min-height: 0;
    aspect-ratio: 3/2;
  }

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

  .card-grid,
  .icon-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .cert-wall {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .data-strip {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .data-strip__cell {
    padding-left: 0;
    border-left: 0;
  }

  .foot__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-6);
  }

  /* The promise, the address and the CTA need the full width once the page
     is narrow — they are one sentence and one button, not a link column. */
  .foot__col--request {
    grid-column: 1 / -1;
  }

  /* ── The mobile menu ─────────────────────────────────────────────────── */
  .nav-toggle {
    display: inline-flex;
  }

  .nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin-left: 0;
    padding: var(--sp-4) var(--container-pad) var(--sp-6);
    background-color: var(--paper);
    border-bottom: var(--heavy);
  }

  .nav.is-open {
    display: flex;
  }

  .nav__list {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }

  .nav__link {
    min-height: var(--tap);
    padding-block: var(--sp-3);
    border-bottom: var(--hair);
  }

  .nav__link[aria-current="page"]::after {
    display: none;
  }

  .nav__link[aria-current="page"] {
    border-bottom: var(--rule-heavy-w) solid var(--accent);
  }

  .nav__cta {
    margin-top: var(--sp-5);
  }
}

@media (max-width: 620px) {
  .field-row {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 560px) {
  .card-grid,
  .icon-row,
  .cert-wall,
  .data-strip {
    grid-template-columns: minmax(0, 1fr);
  }

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

  .steps > li,
  .numbered > li {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-2);
  }

  .foot__legal {
    flex-direction: column;
  }
}

/* ═══ 25 · Home page only ═════════════════════════════════════════════════
   Appended by the home-page worker. Everything below belongs to / and to no
   other page; the shared blocks are untouched. Same file, not a second
   stylesheet (hard rule 54): design-tokens.css holds no class, and no selector
   below repeats one already declared above. */

/* ── Hero: the photograph is the ground, the copy sits on its clear left ───
   Block contract: role hero-bg, display overlay, text zone left-55, subject
   zone right-45. The frame is bright with a low-contrast left side, so the copy
   stays ink and the 2px rule under the measured figure stays legible; a scrim
   over a light frame would have turned it to mud. Five columns of the twelve
   keep the copy clear of the ring, which starts 45.6% across the frame. */
.hero--photo {
  position: relative;
  overflow: hidden;
}

.hero--photo .hero__media {
  position: absolute;
  inset: 0;
  grid-column: auto;
  min-height: 0;
}

.hero--photo .hero__inner {
  position: relative;
  z-index: 1;
}

.hero--photo .hero__text {
  grid-column: 1 / span 5;
}

@media (max-width: 900px) {
  /* At phone width the copy takes the whole column, so the photograph leaves
     the backdrop and becomes the 3/2 plate above it — the same step the site's
     own .hero__media already takes at this breakpoint. */
  .hero--photo .hero__media {
    position: relative;
    inset: auto;
  }
}

/* ── Data strip: five figures, so five tracks ─────────────────────────────
   The base strip carries four. Five cells in four tracks would leave an orphan
   row; in one track at 900px and below, each cell keeps its bottom hairline as
   the separator. */
.data-strip--five {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}

/* The third line of a cell: where the figure comes from. DATA-STRIP BASIS
   INK-2 #4A4A4A on PAPER #FFFFFF = 8.86:1 · on PANEL #F5F5F3 = 8.12:1 */
.data-strip__basis {
  display: block;
  margin-top: var(--sp-2);
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--ink-2);
}

/* The document mark that opens each label needs its own clearance: at 16px and
   with no gap it reads as one glyph with the first letter. */
.data-strip__label .icon {
  margin-right: var(--sp-2);
}

@media (max-width: 900px) {
  .data-strip--five {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ── Certificate scans: landscape documents on a portrait wall ────────────
   The four sheets on this page are 882x649 and 850x570. A 2/3 window would
   show a vertical strip of each; a 3/2 window with contain shows the whole
   sheet, and the spare width reads as the document's own margin because the
   well behind it is --panel. Nothing is cropped: a certificate with its seal
   cut off is evidence of nothing. */
.cert__media--flat {
  aspect-ratio: 3/2;
}

.cert__media--flat img {
  object-fit: contain;
  object-position: center;
}

/* ── The nine-stage chain ────────────────────────────────────────────────
   Drawn rather than generated: every station carries a number or a figure and
   an image model cannot be trusted with either. Below 900px the chain keeps its
   labels legible and scrolls inside its own box, so the document itself never
   widens. */
.proc-chain {
  display: block;
  width: 100%;
  height: auto;
  color: var(--ink);
}

.proc-chain-scroll {
  overflow-x: auto;
  padding-bottom: var(--sp-2);
}

/* CHAIN NUMBER INK-3 #6B6B6B on PAPER = 5.33:1
   CHAIN NAME INK-2 #4A4A4A on PAPER = 8.86:1
   Sizes are set in viewBox units and the chain is drawn at 1300 units wide, so
   they are written about an eighth larger than their rendered size: the labels
   land back on the site's 11px mono and 13px numeral steps. */
.proc-chain__no {
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: 14px;
  letter-spacing: 0.08em;
  fill: var(--ink-3);
}

.proc-chain__name {
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: 12.5px;
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  fill: var(--ink-2);
}

.proc-chain__gate {
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: 12.5px;
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  fill: var(--ink-3);
}

@media (max-width: 900px) {
  .proc-chain {
    min-width: 1100px;
  }
}

/* ── The reply slip on the ink plate ─────────────────────────────────────
   The band's contract is overlay-right: the photograph's left half is the dark,
   clear side, and the slip sits on it. The three rules below repaint the form
   for a dark ground; the inputs stay paper, so the slip still reads as paper
   laid on a photograph. */
.cta-band--photo {
  position: relative;
  overflow: hidden;
}

.cta-band__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.cta-band__bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.cta-band--photo .wrap {
  position: relative;
  z-index: 1;
}

.cta-band--plate .coupon {
  border-top-color: var(--on-plate);
}

/* An ink-outlined button is a ghost on an ink plate: its border and label are
   the same colour as the ground. PAPER on PLATE = 18.10:1. */
.cta-band--plate .btn--line {
  border-color: var(--on-plate);
  color: var(--on-plate);
}

.cta-band--plate .btn--line:hover,
.cta-band--plate .btn--line:focus-visible {
  background-color: var(--on-plate);
  border-color: var(--on-plate);
  color: var(--ink);
}

.cta-band--plate .field label,
.cta-band--plate .field__hint {
  color: var(--on-plate-2);
}

/* ═══ 26 · About, Contact, Blog, 404 ══════════════════════════════════════
   Appended by the batch-3 worker. Everything below belongs to /about/,
   /contact/, /blog/ and /404.html; the shared blocks and every block declared
   above are untouched. Same stylesheet, not a second one (hard rule 54): no
   selector here repeats one already in this file, and none of these is a
   `:root`, `@keyframes` or element selector — those belong to the token layer. */

/* ── The ruled index line under the blog hero ─────────────────────────────
   Six subjects, no links. A base-site note has no article page, so the line
   states the coverage and offers no route that would 404.
   RUBRIC INK-3 #6B6B6B on PAPER #FFFFFF = 5.33:1 */
.rubric {
  grid-column: 1 / -1;
  margin: 0;
  padding-top: var(--sp-4);
  border-top: var(--hair);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  line-height: var(--lh-ui);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ── A three-column comparison table on a 390px screen ────────────────────
   The base sheet gives table.spec exactly this treatment (min-width 0 on the
   desktop, a 560px floor inside the mobile media query, scrolling in its own
   wrapper). table.cmp carried no floor, so three columns of prose squeezed to
   335px and the rows stacked 1,297px tall. Same rule, same wrapper. */
@media (max-width: 900px) {
  table.cmp {
    min-width: 560px;
  }
}

/* ── The chain: six stations between the steel mill and the machine ───────
   A grid rather than a drawn diagram on purpose: the labels have to stay
   legible at 390px, and an SVG that scales to the rail width renders 11px
   type at 3px. Six is a multiple of three, of two and of one, so no
   breakpoint leaves an orphan row. */
.chain {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gutter);
  margin: 0;
  padding: 0;
  list-style: none;
}

.chain > li {
  min-width: 0;
  margin-top: 0;
  padding-top: var(--sp-4);
  border-top: var(--heavy);
}

/* CHAIN NUMBER INK-3 #6B6B6B on PAPER = 5.33:1 · on PANEL #F5F5F3 = 4.88:1 */
.chain__no {
  display: block;
  margin-bottom: var(--sp-2);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-eyebrow);
  line-height: var(--lh-ui);
  letter-spacing: var(--tr-eyebrow);
  color: var(--ink-3);
}

.chain h3 {
  margin-bottom: var(--sp-2);
}

.chain p {
  margin-bottom: 0;
  color: var(--ink-2);
}

@media (max-width: 900px) {
  .chain {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .chain {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ── Two certificate scans side by side ───────────────────────────────────
   The base wall carries four sheets; two sheets in four tracks would leave a
   half row, so the pair gets two tracks of its own. Both scans are landscape
   documents, so each keeps a 3/2 window (the site's .figure__img) and the
   whole sheet stays visible, rather than a 2/3 strip of its middle. */
.cert-wall--pair {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (max-width: 560px) {
  .cert-wall--pair {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ── The pager: one page, said without a link to a page that is not there ──
   There is no second page of notes, so the controls are disabled buttons and
   not anchors: a control that looks clickable and goes nowhere is the defect
   this is written to avoid. The page number keeps the cobalt, the way the
   masthead marks the page you are on. */
.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4) var(--sp-5);
  padding-top: var(--sp-4);
  border-top: var(--heavy);
}

.pager__count {
  margin: 0;
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--ink-2);
}

.pager__pages {
  display: flex;
  gap: var(--sp-2);
  margin: 0;
}

.pager__page {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--tap);
  min-height: var(--tap);
  padding-inline: var(--sp-3);
  border: var(--rule-heavy-w) solid var(--accent);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: var(--fs-data);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.pager__ctl {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-left: auto;
}

/* A disabled control is exempt from the contrast floor; at full strength it
   would read as the enabled button beside it. */
.pager__ctl .btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

@media (max-width: 560px) {
  .pager__ctl {
    margin-left: 0;
  }
}

/* ── The transactional FAQ in two columns ────────────────────────────────
   Six rows in two tracks: three even rows, no orphan. The second row-1 item
   drops its hairline so no rule doubles up under the heavy rule that opens
   the list; below 900px it is one ruled column again, in reading order. */
.faq--two-col {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: var(--gutter);
}

.faq--two-col .faq-item:nth-child(2) {
  border-top: 0;
}

@media (max-width: 900px) {
  .faq--two-col {
    grid-template-columns: minmax(0, 1fr);
  }

  .faq--two-col .faq-item:nth-child(2) {
    border-top: var(--hair);
  }
}

/* ── The receipt slip laid on the photograph (blog B4) ────────────────────
   The band's contract is overlay-right: the photograph is dark and clear on
   its left half and the slip sits there. Paper under ink keeps the form at
   exactly the contrast it has on /contact/, and the slip still reads as paper
   on a photograph. */
.slip {
  max-width: 34rem;
  padding: var(--sp-6);
  background-color: var(--paper);
}

@media (max-width: 560px) {
  .slip {
    padding: var(--sp-5) var(--sp-4);
  }
}

/* ── Why a field is on the form ──────────────────────────────────────────
   FIELD-WHY INK-3 #6B6B6B on PAPER #FFFFFF = 5.33:1 */
.field__why {
  font-size: var(--fs-data);
  line-height: var(--lh-ui);
  color: var(--ink-3);
}

/* Three icon cells: the base row drops to two tracks at 900px and a third
   cell would sit alone on its own row. One track instead of 2 + 1. */
@media (max-width: 900px) {
  .icon-row--trio {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ── 404 ─────────────────────────────────────────────────────────────────
   The skeleton from references/page-templates.md, with its inline style
   attributes moved into these classes — style-src 'self' silently drops an
   inline style, so the skeleton as written would have rendered unstyled. The
   page carries the site's nav, footer and modal, so the reader is one click
   from anywhere. */
.nf {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 62vh;
  text-align: center;
}

.nf__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* NF CODE COBALT #1848A8 on PAPER #FFFFFF = 8.30:1. This is the page's one
   emphasised element; both controls below stay ink, so the cobalt is a single
   event on the screen. */
.nf__code {
  margin: 0 0 var(--sp-4);
  font-family: var(--font-mono);
  font-weight: var(--w-mono);
  font-size: clamp(72px, 12vw, 144px);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
}

.nf__title {
  max-width: 20ch;
}

.nf__text {
  max-width: 48ch;
  margin-bottom: 0;
  color: var(--ink-2);
}

.nf__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  margin: var(--sp-6) 0 0;
}

/* ═══ 26 · Products and capabilities pages ═════════════════════════════════
   Appended by the batch-2 page worker (/products/ and /capabilities/). Same
   file, not a second stylesheet (hard rule 54). No selector below repeats one
   already declared above; no :root and no @keyframes, which belong to
   design-tokens.css. */

/* ── The load and speed column of the flagship table ──────────────────────
   It carries two figures, grease and oil. Letting this one cell wrap is what
   keeps the eleven-column table inside the desktop measure, so the table never
   scrolls sideways. */
table.spec .spec__pair {
  white-space: normal;
}

/* ── Self-drawn diagrams ──────────────────────────────────────────────────
   The Kia scale on /products/ and the nine-step chain on /capabilities/ are
   inline SVG: every figure in them is one an image model would redraw wrongly.
   Colour comes from the tokens, never from the markup. */
.diagram {
  display: block;
  width: 100%;
  height: auto;
  color: var(--ink-2);
}

.diagram text {
  font-family: var(--font-mono);
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  fill: currentColor;
}

.diagram .diagram__val {
  fill: var(--ink);
}

.diagram .diagram__band {
  font-size: 14px;
}

.diagram .diagram__step {
  fill: var(--ink-2);
}

.diagram .diagram__tick {
  stroke: var(--rule);
  stroke-width: 1;
  fill: none;
}

.diagram .diagram__axis {
  stroke: var(--ink);
  stroke-width: 2;
  fill: none;
}

/* The one event-colour use on the scale: the market's best figure, as the line
   the standard's own limits are read against. */
.diagram .diagram__limit {
  stroke: var(--accent);
  stroke-width: 2;
  stroke-dasharray: 8 5;
  fill: none;
}

/* Below 900px a diagram keeps a floor width and scrolls inside its own box, so
   its numbers stay legible and the document itself never widens. Same device
   the spec table already uses at the same breakpoint. */
.diagram-scroll {
  overflow-x: auto;
  padding-bottom: var(--sp-2);
}

@media (max-width: 900px) {
  .diagram-scroll > .diagram {
    min-width: 900px;
  }
}

/* ── Four plant photographs, four tracks ──────────────────────────────────
   Fixed column count, and the card count is a multiple of it at every width
   down to one column: no orphan row. */
.card-grid--four {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

/* ── Three plaques, three tracks ─────────────────────────────────────────── */
.cert-wall--three {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 900px) {
  .card-grid--four {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .cert-wall--three {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 560px) {
  .card-grid--four {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* The numbered caption on the graded plate of the /capabilities/ hero: on an
   ink ground the two paper greys are unreadable, so the caption takes the
   plate's own text steps. */
.hero--bleed .fig-cap {
  border-top-color: rgba(255, 255, 255, 0.28);
  color: var(--on-plate-2);
}

.hero--bleed .fig-cap__no {
  color: var(--on-plate);
}

/* ── /products/ B9 · the reply slip printed on the left half of the plate ──
   Contract: overlay-right. The band's subject keeps the right half; the left
   half is an opaque ink panel, because type over a photograph needs an opaque
   ground (design-blueprint R5) and the slip's field labels only clear 4.5:1 on
   the darker step. The panel is a hard 50% stop in one gradient, so no line of
   the slip ever sits on a half-lit pixel. */
.cta-band--split {
  position: relative;
  background-color: var(--plate);
  overflow: hidden;
}

.cta-band--split .cta-band__media {
  position: relative;
  width: 100%;
}

.cta-band--split .cta-band__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.cta-band--split .coupon {
  border-top-color: var(--on-plate);
}

.cta-band--split .field label,
.cta-band--split .field__hint {
  color: var(--on-plate-2);
}

@media (max-width: 900px) {
  /* Stacked: the plate becomes a 3/2 window above the slip. */
  .cta-band--split .cta-band__media {
    aspect-ratio: 3/2;
  }
}

@media (min-width: 901px) {
  .cta-band--split .cta-band__media {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 50%;
  }

  .cta-band--split .cta-band__inner {
    background-image: linear-gradient(
      90deg,
      var(--plate) 0,
      var(--plate) 50%,
      rgba(22, 22, 22, 0) 50%
    );
  }
}

/* The eleven-column flagship table on /products/ is the one table on the site
   wide enough to need this: at 8px of inline padding each column keeps its
   figures and no column falls below its own content, so the table fits the
   measure at every width above the 900px breakpoint and never scrolls on a
   desktop. Below 900px the site's own table rule takes over. */
table.spec.spec--dense th,
table.spec.spec--dense td {
  padding-inline: var(--sp-2);
}

/* ── /capabilities/ B1 · the graded plate under the hero ──────────────────
   The photograph is the backdrop and the copy sits on the band the prompt
   reserved for it. The site's own --grade-plate leaves the top of that band at
   8% opacity, where an 11px eyebrow over a bright machine hall measures about
   2:1; the stronger stop keeps the smallest line on the band above 4.5:1
   without turning the band into a solid bar. Same ruling as the copy plate in
   the inquiry modal (design-blueprint.md R5). */
.hero--bleed .hero__inner--plate {
  background-image: linear-gradient(
    180deg,
    rgba(22, 22, 22, 0.88) 0%,
    rgba(22, 22, 22, 0.95) 100%
  );
}

/* ═══ 27 · Masthead fit band (901–940px) ═════════════════════════════════
   Measured 2026-09-28 with Playwright against build/index.html:
   at 901px the document scrollWidth was 938px against a 901px clientWidth.
   The desktop masthead (brand + six nav links + utility row + cobalt CTA)
   needs 938px of intrinsic width; at 900px the mobile masthead takes over and
   the overflow is gone, so the fault lives only in 901–937px.
   Every existing gate missed it: check-mobile-reach.js measures 375 and 768,
   check-layout-edges.js measures its own two viewports, and neither samples
   this band. Found by the coordinating agent sampling 900/901/920/960/1024/
   1100/1280/1440 directly.
   Fix: tighten spacing in this band only. No third masthead is invented and
   nothing at or below 900px changes. Re-measured after the fix: scrollWidth
   equals clientWidth at 901, 920 and 940. */
@media (min-width: 901px) and (max-width: 940px) {
  .masthead__inner { gap: var(--sp-3); }
  .nav { gap: var(--sp-3); }
  .nav__list { gap: var(--sp-3); }
  .nav__link { font-size: 14px; }
  .nav__cta { padding-inline: var(--sp-3); }
}
