/* Components: one definition each (BRIEF §3). The styleguide (/styleguide) shows every one with real doc copy; in Figma
   each becomes a component master (data-comp) and page uses become instances (data-inst) or named frames (data-name).
   Rule-clearance law (C6): any text keeps --clear (24 / 16 px) from every rule, hairline, band and grid line, so every
   ruled box below pads its text by at least that much. */

/* =============================== primitives =============================== */

/* ---------- Band: the A motif, leads the hero and each section. 96 x 12 (L) or 48 x 6 ---------- */
.band { display: block; flex: none; width: 48px; height: 6px; background: var(--blue); }
.band--l { width: 96px; height: 12px; }
.band--paper { background: var(--on-blue); }

/* ---------- Eyebrow: band, then the label 24 px under it ---------- */
.eyebrow { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-24); }
.eyebrow__label { color: var(--ink); }
.on-blue .eyebrow__label { color: var(--on-blue); }   /* never ink on blue (BRIEF §2) */

/* ---------- Button/Primary: square, 48 tall, the logo's own chevron ----------
   States (BRIEF §0f.4): hover shifts the fill one step darker and nudges the chevron 2 px forward (150 ms, ease-out:
   the arrow says "this goes somewhere"); pressed takes the deepest fill and brings the chevron home, so the press reads
   as a click, not a hover; focus = the 2 px ring. Hover only where a pointer hovers (no sticky hover after a tap). The
   drawn states (.btn--hover / --press / --focus) are the /styleguide masters.
   The label and chevron are one packed group, centred: where a layout gives a button a minimum width (a CTA band or the
   home hero crossing a column line) it grows evenly on both sides, so every Button/Primary keeps the same 16 px
   label-to-chevron step at every width (Oct 8 critique: stretched to 240 px the chevron drifted 114 px off its label
   and read as a different, bar-like control). Only the Block variant spreads them (label left, chevron at the end),
   the full-width thumb button of the phone menu. */
.btn { display: inline-flex; flex: none; align-items: center; justify-content: center; gap: var(--s-8);
  position: relative; isolation: isolate; height: var(--control-h); padding: 0 16px 0 24px; background: var(--blue); color: var(--on-blue);
  font-family: var(--sans); font-weight: 600; font-size: 16px; line-height: 24px; white-space: nowrap;
  transition: background-color var(--t-hover), color var(--t-hover), border-color var(--t-hover); }
.chev-window { position: relative; display: inline-flex; flex: none; align-items: center; justify-content: flex-end;
  width: 8px; height: 14px; overflow: hidden; overflow: clip; }
.chev-window > svg { flex: none; width: 8px; height: 14px; fill: currentColor; }
.btn > .chev-window { width: 16px; height: 14px; }
.btn > .chev-window > svg { position: relative; z-index: 2; }
.btn > .chev-window::after { content: ""; position: absolute; z-index: 1; right: 0; top: 4px; width: 6px; height: 6px;
  border-top: 1.5px solid currentColor; border-right: 1.5px solid currentColor; opacity: 0; rotate: 45deg; }
/* The arrow exits right, leaves a faint echo, then returns through its clipped 16 px window. */
@keyframes button-chevron { 0%, 100% { translate: 0 0; opacity: 1; }
  30% { translate: 10px 0; opacity: 0; } 31% { translate: -16px 0; opacity: 0; }
  46% { translate: -8px 0; opacity: .18; } 68% { translate: 0 0; opacity: 1; } }
@keyframes button-echo { 0%, 25% { translate: 0 0; opacity: 0; }
  34% { translate: 4px 0; opacity: .38; } 48% { translate: 10px 0; opacity: .2; }
  61%, 100% { translate: 14px 0; opacity: 0; } }
@keyframes logo-chevron { 0%, 100% { translate: 0 0; opacity: 1; }
  30% { translate: calc(15px * 1915 / 119) 0; opacity: 0; }
  31% { translate: calc(-16px * 1915 / 119) 0; opacity: 0; }
  46% { translate: calc(-8px * 1915 / 119) 0; opacity: .18; }
  68% { translate: 0 0; opacity: 1; } }
@keyframes logo-echo { 0%, 25% { translate: 0 0; opacity: 0; }
  34% { translate: calc(4px * 1915 / 119) 0; opacity: .38; }
  48% { translate: calc(10px * 1915 / 119) 0; opacity: .2; }
  61%, 100% { translate: calc(14px * 1915 / 119) 0; opacity: 0; } }
@media (hover: hover) {
  .btn:hover { background: var(--blue-hover); }
}
@media (prefers-reduced-motion: no-preference) {
  .btn:hover > .chev-window > svg, .btn:focus-visible > .chev-window > svg { animation: button-chevron 520ms var(--ease-out) both; }
  .btn:hover > .chev-window::after, .btn:focus-visible > .chev-window::after { animation: button-echo 520ms var(--ease-out) both; }
}
.btn:active { background: var(--blue-press); }
.btn:active > .chev-window > svg { translate: none; transition-duration: 60ms; }
.btn--block { display: flex; justify-content: space-between; width: 100%; }
.btn--hover { background: var(--blue-hover); }   /* drawn states for the styleguide */
/* the drawn hover steps its chevron in the layout, not by translate (a Figma auto-layout master cannot hold one): the
   gap grows by the nudge and the end padding gives it back, so the box stays put and only the chevron moves */
.btn--hover { gap: calc(var(--s-8) + var(--nudge)); padding-right: calc(16px - var(--nudge)); }
.btn--press { background: var(--blue-press); }
.btn--focus { outline: 2px solid var(--focus); outline-offset: 2px; }
/* Button/Primary · Current (the page the button leads to: the header's Work With Us on /hire-roger): the button keeps
   its fill and a clean label, and the header marks it as it marks a current nav item, a 2 px Roger Blue bar on the
   header's bottom rule across the button (Oct 8 critique: an underline inside the fill read as a link or hover
   artefact, a second "you are here" language in one bar). The bar sits half the header's spare height under the
   button (the drawn master keeps the desktop 16); the menu's block button carries no mark, the menu's own links
   underline the current page. Never colour alone. */
.btn[aria-current], .btn--current { position: relative; }
.site-header__bar .btn[aria-current]::after, .btn--current::after { content: ""; position: absolute; left: 0; right: 0;
  bottom: calc((var(--header-h) - var(--control-h)) / -2); height: 2px; background: var(--blue); pointer-events: none; }
.btn--current::after { bottom: calc(var(--s-16) * -1); }
/* ---------- Button/Secondary: the line button, for genuinely secondary actions only ("More about our model"). Every
   primary action is the filled Roger Blue button, the header's Work With Us included (Tidjane, Oct 2 and Oct 8:
   "Primary buttons and top nav button dark blue fill. Only secondary buttons are line/border based."; supersedes
   D-64 / BRIEF §0 F9). 1 px Roger Blue outline, blue label, paper fill; hover fills Sky (and nudges the chevron).
   Same 48 px box as the primary (the stroke sits inside the box and the padding gives it back), so the label and
   chevron sit exactly where the primary puts them. ---------- */
.btn--secondary { background: var(--paper); color: var(--blue); border: 1px solid var(--blue); padding: 0 15px 0 23px; }
@media (hover: hover) { .btn--secondary:hover { background: var(--sky); } }
.btn--secondary.btn--hover { background: var(--sky); padding-right: calc(15px - var(--nudge)); }
.btn--secondary:active, .btn--secondary.btn--press { background: var(--sky); border-color: var(--blue-press); color: var(--blue-press); }
/* busy (form sending): pressed fill, progress cursor; the label reads ui form.sending */
.btn[aria-disabled="true"] { cursor: progress; background: var(--blue-press); }
.btn[aria-disabled="true"] > .chev-window > svg { translate: none; animation: none; }

/* ---------- Link/Chevron: blue label + chevron, 44 px tall target. Hover: the label's underline grows from the left
   (base.css · grow underline) and the chevron steps 2 px forward; pressed: ink-blue, chevron home. ---------- */
.link-chev { display: inline-flex; flex: none; align-items: center; gap: var(--s-8); min-height: var(--tap);
  color: var(--blue); font-family: var(--sans); font-weight: 600; font-size: 16px; line-height: 24px; white-space: nowrap;
  transition: color var(--t-hover); }
.link-chev .chev-window { width: 7px; height: 12px; }
.link-chev svg { flex: none; width: 7px; height: 12px; fill: currentColor; transition: translate var(--t-hover); }
@media (hover: hover) { .link-chev:hover svg, :is(.card, .insight-hit):hover .link-chev svg { translate: var(--nudge) 0; } }
.link-chev--hover { gap: calc(var(--s-8) + var(--nudge)); }   /* the drawn hover: the step as spacing (see .btn--hover) */
.link-chev:active { color: var(--blue-press); }
.link-chev:active svg { translate: none; }
.on-blue .link-chev { color: var(--on-blue); }

/* ---------- Logo (real vectors only) ----------
   Two inks (Oct 8, Tidjane: the ">" of the R> mark and of the full logotype in the main accent's dark blue): the
   letters in currentColor (ink on paper), the chevron (.wm-chev, partials/svg/wordmark-paths.html) in Roger Blue, on
   every logo, MediaFrame mark, letterhead and the favicon. On a Roger Blue surface the chevron takes Sky, so it never
   sinks into its ground. A document stamp is a single-ink drawing: its wordmark stays all stamp blue.
   Hover (Oct 2 Replit pass: the logo turns blue) keeps the two inks apart: the letters take Roger Blue and the
   chevron the deepest blue (--blue-press). Its clipped window lets only the mark perform the out-right / echo /
   re-entry gesture used by buttons; the letterforms remain still and legible. Reduced motion leaves the mark at rest. */
.logo { display: inline-flex; flex: none; align-items: center; min-height: var(--tap); color: var(--ink); }
.logo svg { width: 119px; height: 24px; fill: currentColor; overflow: visible; }
.logo { transition: color var(--dur-hover) var(--ease-out); }
.wm-chev-window { clip-path: inset(0); }
.wm-chev-echo { opacity: 0; }
.wm-chev { fill: var(--blue); }
.wm-chev-echo { fill: var(--blue); }
:is(.on-blue, .section--blue) :is(.wm-chev, .wm-chev-echo) { fill: var(--sky); }
.on-blue .logo { color: var(--on-blue); }   /* paper letters on the blue field (the OG card, Logo/Wordmark · On blue) */
.dstamp__wm .wm-chev { fill: currentColor; }
@media (hover: hover) {
  .logo:hover { color: var(--blue); }
  .logo:hover .wm-chev { fill: var(--blue-press); }
  .logo:hover .wm-chev-echo { fill: var(--blue-press); }
  .on-blue .logo:hover { color: var(--on-blue); }
  .on-blue .logo:hover :is(.wm-chev, .wm-chev-echo) { fill: var(--sky); }
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .logo:hover .wm-chev { animation: logo-chevron 520ms var(--ease-out) both; }
  .logo:hover .wm-chev-echo { animation: logo-echo 520ms var(--ease-out) both; }
}
@media (prefers-reduced-motion: no-preference) {
  .logo:focus-visible .wm-chev { animation: logo-chevron 520ms var(--ease-out) both; }
  .logo:focus-visible .wm-chev-echo { animation: logo-echo 520ms var(--ease-out) both; }
}

/* ---------- Stamp/Warranty (C): 0 deg, drawn in the document-stamp family the client praised (A 1.4 "Stamp ·
   Reviewed", F1/F2/F4): 1 px frame, 4 px gap, 1 px inner rule, a ruled header band carrying the label, the promise in
   Newsreader below it, 20 px inner padding, width hugs content. Same anatomy as the kit's Stamp/Warranty.
   L = desktop · M = tablet/mobile + service pages · S = collateral. Never on a photograph, never rotated.
   No fill of its own colour: it takes its ground's (--surface), so on a Sky panel only the blue frames and the type
   show (C3, never a pasted label) while the bay lines stop at its frame. */
.stamp { display: inline-flex; flex: none; padding: 4px; border: 1px solid var(--blue); color: var(--blue); background: var(--surface, var(--paper)); }
.stamp__frame { display: flex; flex-direction: column; align-items: stretch; border: 1px solid var(--blue); }
.stamp__label { padding: 8px 20px; border-bottom: 1px solid var(--blue); font-family: var(--narrow); font-weight: 600; font-size: 12px;
  line-height: 16px; letter-spacing: 0.14em; text-transform: uppercase; white-space: nowrap; }
.stamp__line { padding: 16px 20px; font-family: var(--serif); font-weight: 600; font-size: 24px; line-height: 30px; letter-spacing: 0; white-space: nowrap; }
.stamp--m .stamp__label { padding: 8px 16px; }
.stamp--m .stamp__line { padding: 12px 16px; font-size: 20px; line-height: 26px; }
.stamp--s .stamp__label { padding: 4px 12px; font-size: 10px; line-height: 12px; }
.stamp--s .stamp__line { padding: 8px 12px; font-size: 16px; line-height: 20px; }

/* ---------- Document stamps (Signature, Review x4), carried over from A at 0 deg ----------
   Drawn in document pixels: --px is 1px at full size and shrinks when the stamp sits on a drawn page (.doc). */
.dstamp { --px: 1px; display: flex; flex: none; padding: calc(5 * var(--px)); border: calc(2 * var(--px)) solid var(--blue); background: var(--paper); color: var(--blue); }
.dstamp__in { display: flex; flex-direction: column; width: calc(346 * var(--px)); border: calc(1 * var(--px)) solid var(--blue); }
.dstamp__head { display: flex; align-items: center; justify-content: space-between; height: calc(32 * var(--px)); padding: 0 calc(12 * var(--px)); border-bottom: calc(1 * var(--px)) solid var(--blue); }
.dstamp__wm { width: calc(59.4 * var(--px)); height: calc(12 * var(--px)); fill: currentColor; }
.dstamp__lab { font-family: var(--narrow); font-weight: 600; font-size: calc(13 * var(--px)); line-height: calc(14 * var(--px)); letter-spacing: 0.1em; text-transform: uppercase; white-space: nowrap; }
.dstamp__row { display: flex; height: calc(48 * var(--px)); border-bottom: calc(1 * var(--px)) solid var(--blue); }
.dstamp__f { display: flex; flex-direction: column; gap: calc(13 * var(--px)); padding: calc(8 * var(--px)) calc(12 * var(--px)) 0; flex: 1 1 0; }
.dstamp__f--l { flex: 0 0 calc(192 * var(--px)); border-right: calc(1 * var(--px)) solid var(--blue); }
.dstamp__entry { display: block; height: calc(1 * var(--px)); background: var(--blue); }
.dstamp__band { display: flex; align-items: center; height: calc(32 * var(--px)); padding: 0 calc(12 * var(--px)); background: var(--blue); }
.dstamp__band-l { font-family: var(--narrow); font-weight: 700; font-size: calc(14 * var(--px)); line-height: calc(16 * var(--px)); letter-spacing: 0.04em; text-transform: uppercase; white-space: nowrap; color: var(--paper); }
.dstamp__list { display: flex; flex-direction: column; gap: calc(4 * var(--px)); padding: calc(8 * var(--px)) calc(12 * var(--px)); border-bottom: calc(1 * var(--px)) solid var(--blue); }
.dstamp__opt { display: flex; align-items: center; gap: calc(10 * var(--px)); min-height: calc(20 * var(--px)); }
.dstamp__box { display: block; flex: none; width: calc(12 * var(--px)); height: calc(12 * var(--px)); border: calc(1 * var(--px)) solid var(--blue); }
.dstamp__box--on { display: flex; align-items: center; justify-content: center; background: var(--blue); color: var(--on-blue); }
.dstamp__check { display: block; width: 100%; height: 100%; }   /* the product-frame previews tick the middle row */
.dstamp__opt-l { min-width: 0; font-family: var(--narrow); font-weight: 600; font-size: calc(13 * var(--px)); line-height: calc(14 * var(--px)); letter-spacing: 0.1em; text-transform: uppercase; white-space: nowrap; }
.dstamp__opt-l--on { font-weight: 700; font-size: calc(14 * var(--px)); line-height: calc(16 * var(--px)); letter-spacing: 0.04em; }
.dstamp__cert { display: flex; align-items: center; height: calc(32 * var(--px)); padding: 0 calc(12 * var(--px)); border-bottom: calc(1 * var(--px)) solid var(--blue); }

/* ---------- Drawn document (collateral boards only, BRIEF §0e.4: the website shows partials/mock-*.html instead): a US
   Letter cover sheet on the paper-3 desk, cropped by its panel.
   Drawn in Letter pixels (816 x 1056 at 96 dpi); --px maps them so the page is 93.3% of the panel at every width. ---------- */
.doc-panel { container-type: inline-size; position: relative; width: 100%; aspect-ratio: 4 / 3; overflow: hidden; background: var(--paper-3);
  display: flex; flex-direction: column; align-items: center; padding-top: 6.6667%; }
.doc { --px: calc(100cqw * 560 / 600 / 816); flex: none; width: calc(816 * var(--px)); display: flex; flex-direction: column;
  padding: calc(48 * var(--px)); background: var(--paper); border: 1px solid var(--rule-2); color: var(--ink); }
.doc .dstamp { --px: calc(100cqw * 560 / 600 / 816); }
.doc__top { display: flex; justify-content: space-between; align-items: stretch; }
.doc__head { display: flex; flex-direction: column; justify-content: space-between; padding-bottom: calc(24 * var(--px)); }
.doc__wm { width: calc(118.8 * var(--px)); height: calc(24 * var(--px)); fill: var(--ink); }
.doc__tb { display: flex; flex-direction: column; gap: calc(12 * var(--px)); }
.doc__kind { font-family: var(--sans); font-weight: 500; font-size: calc(13 * var(--px)); line-height: calc(16 * var(--px)); color: var(--ink-2); white-space: nowrap; }
.doc__title { font-family: var(--serif); font-weight: 600; font-size: calc(36 * var(--px)); line-height: calc(40 * var(--px)); letter-spacing: -0.01em; white-space: nowrap; }
.doc__fields { display: flex; flex-direction: column; border-top: calc(2 * var(--px)) solid var(--ink); }
.doc__fr { display: flex; height: calc(56 * var(--px)); border-bottom: calc(1 * var(--px)) solid var(--rule-2); }
.doc__fc { display: flex; flex-direction: column; gap: calc(6 * var(--px)); padding-top: calc(8 * var(--px)); flex: 1 0 10%; min-width: 0; overflow: hidden; }
/* the column rule is its own 12 px element (1 px line + 11 px clear), so every field cell has the same (zero) side
   padding: Figma shares FILL widths by outer size, CSS after padding, and equal padding makes the two agree */
.doc__vr { display: block; flex: 0 0 calc(12 * var(--px)); border-left: calc(1 * var(--px)) solid var(--rule-2); }
.doc__lab { font-family: var(--narrow); font-weight: 600; font-size: calc(12 * var(--px)); line-height: calc(16 * var(--px)); letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-2); white-space: nowrap; }
.doc__val { font-family: var(--sans); font-weight: 400; font-size: calc(13 * var(--px)); line-height: calc(16 * var(--px)); color: var(--ink); white-space: nowrap; }
.doc__tab { display: flex; flex-direction: column; border-top: calc(2 * var(--px)) solid var(--ink); }
.doc__tr { display: flex; height: calc(32 * var(--px)); border-bottom: calc(1 * var(--px)) solid var(--rule-2); }
.doc__tc { display: flex; align-items: center; flex: 0 0 auto; }
.doc__tc + .doc__tc { padding-left: calc(11 * var(--px)); border-left: calc(1 * var(--px)) solid var(--rule-2); }
.doc__tc--60 { flex-basis: calc(60 * var(--px)); } .doc__tc--120 { flex-basis: calc(120 * var(--px)); }
.doc__tc--360 { flex-basis: calc(360 * var(--px)); } .doc__tc--fill { flex: 1 1 0; }

/* ================================ header ================================ */
.site-header { position: sticky; top: 0; z-index: 40; width: 100%; display: flex; flex-direction: column; align-items: center;
  background: var(--paper); border-bottom: 1px solid var(--rule); }
.site-header__bar { position: relative; width: 100%; max-width: var(--container); height: var(--header-h); display: flex;
  align-items: center; justify-content: space-between; padding: 0 var(--margin); }
.nav { display: flex; align-items: center; gap: var(--s-32); }
.nav__list { display: flex; align-items: center; gap: var(--s-8); }
.nav__item { position: relative; display: flex; }
/* nav item: the label's grow underline (base.css · .u-grow) draws from the left on hover; the current page holds it
   in Roger Blue at 2 px; the open Services item holds it in ink while its dropdown shows */
.nav__link { display: inline-flex; align-items: center; gap: var(--s-8); min-height: var(--tap); padding: 0 12px;
  font-family: var(--sans); font-weight: 500; font-size: var(--ui-fs); line-height: var(--ui-lh); color: var(--ink); white-space: nowrap; }
.nav__label { position: relative; }
.nav__label::after { content: ""; position: absolute; left: 0; right: 0; top: calc(100% + 7px); height: 1px; background: var(--ink);
  scale: 0 1; transform-origin: 0 50%; transition: scale var(--dur-exit) var(--ease-in); pointer-events: none; }
@media (hover: hover) { .nav__link:hover > .nav__label::after { scale: 1 1; transition: scale var(--dur-hover) var(--ease-out); } }
.nav__item--open > .nav__link > .nav__label::after { scale: 1 1; }
/* current: the page's own link, an article's section link (aria-current="true", build.mjs) and, on a service page,
   the Services item whose dropdown holds the current link: a 2 px Roger Blue rule, so it never rests on colour alone */
/* (Oct 2 Replit pass, consolidated Oct 8) the current item is Roger Blue with a 2 px bar sitting on the header's bottom
   rule across the item ("border-anchored active tabs"), not an underline under the word */
.nav__link[aria-current], .nav__item--menu:has(.nav__menu-link[aria-current]) > .nav__link { position: relative; color: var(--blue); }
.nav__link[aria-current]::after, .nav__item--menu:has(.nav__menu-link[aria-current]) > .nav__link::after { content: "";
  position: absolute; left: 0; right: 0; bottom: calc((var(--header-h) - var(--tap)) / -2); height: 2px; background: var(--blue);
  pointer-events: none; }
.nav__link[aria-current] > .nav__label::after,
.nav__item--menu:has(.nav__menu-link[aria-current]) > .nav__link > .nav__label::after { display: none; }
/* hover: the label turns Roger Blue while its underline grows (Oct 2: "blue hover states") */
.nav__link { transition: color var(--dur-hover) var(--ease-out); }
@media (hover: hover) { .nav__link:hover { color: var(--blue); } .nav__link:hover > .nav__label::after { background: var(--blue); } }
.nav__link svg { flex: none; width: 10px; height: 6px; fill: currentColor; transition: scale var(--t-exit); }
.nav__item--open > .nav__link svg { scale: 1 -1; transition: scale var(--t-enter); }   /* the caret turns up while open */
/* opened by hover the item looks opened (rule held, caret up) for as long as the pointer is on the item or its panel,
   the panel being inside the item; Esc (--hold) closes it, so it drops the look too */
@media (hover: hover) {
  .nav__item--menu:not(.nav__item--hold):hover > .nav__link > .nav__label::after { scale: 1 1; transition: scale var(--dur-hover) var(--ease-out); }
  .nav__item--menu:not(.nav__item--hold):hover > .nav__link svg { scale: 1 -1; transition: scale var(--t-enter); }
}
/* Services dropdown: menu only (no Services page). Opens on hover, click, Enter/Space/ArrowDown; Esc closes and
   returns focus. Without JS it opens on hover and focus-within. Its top edge lands on the header's bottom rule. A real
   surface (paper-2) whose links start on the Services label's own text edge. It is a layer over the page, so it keeps
   a 1 px rule edge: borderless, it melted into any paper-2 hero or panel under the header (a stepped paper-2 shape on
   /approach) and cut a hard notch out of a photo; with its own edge it reads on paper, paper-2, Sky and photos alike.
   Its top rule redraws the header rule it sits on, so the edge stays one hairline. With an edge drawn, its labels keep
   the clearance from it (C6, 24): the panel steps 13 px left of the item (12 - 1 - 24) so the labels still start on
   the Services label's text edge, 24 in from the rule, with 16 above the first and below the last link.
   Motion (BRIEF §0f.4): it fades in while rising 8 px into place (240 ms, enter curve) and leaves quicker (180 ms,
   ease-in) after a 120 ms grace, so a pointer that slips off the item on its way down does not slam it shut; a
   transparent bridge (::before) spans the gap between the item and the panel for the same reason. The list is the
   data list (R2-F4): 3-6 services lengthen it, nothing else changes. */
.nav__menu { position: absolute; left: calc(var(--s-12) - 1px - var(--s-24)); top: calc(100% + (var(--header-h) - var(--tap)) / 2); z-index: 45; min-width: 256px;
  display: flex; flex-direction: column; padding: var(--s-16) 0; background: var(--paper-2); border: 1px solid var(--rule);
  visibility: hidden; opacity: 0; translate: 0 8px;
  transition: opacity var(--t-exit) 120ms, translate var(--t-exit) 120ms, visibility 0s linear calc(var(--dur-exit) + 120ms); }
.nav__menu::before { content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: calc((var(--header-h) - var(--tap)) / 2 + 1px); }
.nav__item--menu:hover > .nav__menu, .nav__item--menu:focus-within > .nav__menu, .nav__item--open > .nav__menu, .nav__menu:target {
  visibility: visible; opacity: 1; translate: none; transition: opacity var(--t-enter), translate var(--t-enter), visibility 0s; }
.js .nav__item--menu:not(.nav__item--open):not(:hover) > .nav__menu:not(:target),
.nav__item--menu.nav__item--hold > .nav__menu { visibility: hidden; opacity: 0; translate: 0 8px;
  transition: opacity var(--t-exit) 120ms, translate var(--t-exit) 120ms, visibility 0s linear calc(var(--dur-exit) + 120ms); }
/* Esc closes at once: no grace for an explicit close */
.nav__item--menu.nav__item--hold > .nav__menu { transition: opacity var(--t-exit), translate var(--t-exit), visibility 0s linear var(--dur-exit); }
.nav__menu-link { display: flex; align-items: center; gap: var(--s-12); min-height: 48px; padding: 12px 24px; font-family: var(--sans); font-weight: 500;
  font-size: var(--ui-fs); line-height: var(--ui-lh); color: var(--ink); white-space: nowrap; transition: background-color var(--t-hover); }
@media (hover: hover) { .nav__menu-link:hover { background: var(--paper-3); } }
.nav__menu-link:active { background: var(--rule); }
/* the current service: Roger Blue with a 2 px rule under its label (colour alone is not a state) */
.nav__menu-link[aria-current], .menu__link[aria-current] { color: var(--blue); }
:is(.nav__menu-link, .menu__link)[aria-current] > span[data-name="Label"], .menu__link[aria-current]:not(:has(> span)),
.menu__svc[aria-current] .menu__svc-name > span[data-name="Label"] {
  text-decoration-line: underline; text-decoration-thickness: 2px; text-decoration-color: var(--blue); text-underline-offset: 6px; }

/* menu button + full-height panel (< 1024). Without JS the button is a link to #menu and the panel opens on :target.
   Motion (BRIEF §0f.4): the panel fades in over the page (its bar sits exactly where the header bar is, so the logo
   never moves and only Menu turns into Close) while its body slides down 16 px into place, the links following 30 ms
   apart; closing reverses it, quicker (180 ms, ease-in). display is animated discretely (allow-discrete +
   @starting-style), so the panel stays in the flow until its exit has played, and the focus trap (site.js) holds
   throughout. A browser without @starting-style shows it at once; reduced motion shows and hides it at once. */
.menu-toggle { display: none; align-items: center; gap: var(--s-12); min-height: var(--tap); padding: 0 0 0 12px;
  font-family: var(--sans); font-weight: 500; font-size: var(--ui-fs); line-height: var(--ui-lh); color: var(--ink); }
.menu-toggle svg { flex: none; width: 20px; height: 12px; fill: currentColor; }
.menu-toggle--close svg { width: 16px; height: 16px; }
/* the panel itself never fades (F9): half-transparent, the page and its lines showed through it for 240 ms. It is
   opaque from its first frame, so Menu turns into Close in one step (one label at a time, never a cross-fade), and
   only its content moves; display stays on through the exit (allow-discrete) so the links fade out before it goes */
.menu { position: fixed; left: 0; top: 0; z-index: 60; width: 100%; height: 100%; display: none; flex-direction: column;
  align-items: center; background: var(--paper); overflow-y: auto;
  transition: display var(--dur-exit) allow-discrete; }
.menu:target, .menu--open { display: flex; transition: display var(--dur-enter) allow-discrete; }
.menu__bar { width: 100%; display: flex; flex-direction: column; align-items: center; border-bottom: 1px solid var(--rule); }
.menu__bar-in { width: 100%; max-width: var(--container); height: var(--header-h); display: flex; align-items: center; justify-content: space-between; padding: 0 var(--margin); }
/* the body: the Services group, then the links + button beside a 1:1 photo (tablets) or over it (phones). Its parts
   fade in one after another (--menu-i: the group, the three links, the button, the photo) */
.menu__body { width: 100%; max-width: var(--container); flex: 1 0 auto; display: flex; flex-direction: column; justify-content: flex-start;
  gap: var(--s-32); padding: var(--s-16) var(--margin) var(--s-32);
  translate: 0 -16px; transition: translate var(--t-exit); }
.menu:target .menu__body, .menu--open .menu__body { translate: none; transition: translate var(--dur-enter) var(--ease-out); }
.menu__row { display: flex; flex-direction: column; gap: var(--s-48); }
.menu__col { display: flex; flex-direction: column; gap: var(--s-48); }
.menu__list { display: flex; flex-direction: column; }
:is(.menu__group, .menu__list > li, .menu__col > .btn, .menu__photo) { opacity: 0; transition: opacity var(--t-exit); }
:is(.menu:target, .menu--open) :is(.menu__group, .menu__list > li, .menu__col > .btn, .menu__photo) { opacity: 1;
  transition: opacity var(--dur-enter) var(--ease-out) calc(var(--menu-i, 0) * 30ms + 40ms); }
.menu__list > li:nth-child(1) { --menu-i: 1; } .menu__list > li:nth-child(2) { --menu-i: 2; } .menu__list > li:nth-child(3) { --menu-i: 3; }
.menu__col > .btn { --menu-i: 4; } .menu__photo { --menu-i: 5; }
@starting-style {
  .menu:target .menu__body, .menu--open .menu__body { translate: 0 -16px; }
  :is(.menu:target, .menu--open) :is(.menu__group, .menu__list > li, .menu__col > .btn, .menu__photo) { opacity: 0; }
}
/* the links are separated by space, not hairlines (BRIEF §0e.5); the Services group is its label over the cards */
.menu__group { display: flex; flex-direction: column; gap: var(--s-12); padding-top: var(--s-12); }
.menu__group-label { color: var(--ink-3); }
/* Services as cards (Oct 8: the panel's lower half was plain paper): the Card (paper-2 on the panel's paper, its edge
   to Roger Blue on hover, the mock growing 1.02 inside its stage), two to a line on the gutter, wrapping for a third
   service. Tablets: the ServiceTile's visual block (the mini mock on its short stage) over the name row, the motif
   beside the name; phones: the name row alone, the motif over the name, so "Submittals" keeps its H3 size in a
   132 px card at 320. The whole card is the link (the name is its text), one tab stop. */
.menu__svcs { display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--gutter); }
.menu__svc { flex: 1 1 calc(50% - var(--gutter) / 2); min-width: 0; height: auto; }
.menu__svc > .menu__svc-media { display: none; }
.menu__svc-name { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-12); padding: var(--s-16);
  font-family: var(--serif); font-weight: 600; font-size: var(--h3-fs); line-height: var(--h3-lh); color: var(--ink); }
@media (min-width: 768px) {
  .menu__svc > .menu__svc-media { display: flex; }
  .menu__svc-name { flex-direction: row; align-items: center; padding: var(--s-16) var(--s-24); }
}
/* the photo: phones full width under the button (the panel scrolls past it); tablets the right half, 1:1 beside the
   links, so the panel's paper never runs empty beside them */
.menu__row > .menu__photo { flex: none; align-self: stretch; overflow: hidden; background: var(--paper-3); }
@media (min-width: 768px) {
  .menu__row { flex-direction: row; align-items: flex-start; gap: var(--gutter); }
  .menu__col { flex: 1 1 0; min-width: 0; }
  .menu__row > .menu__photo { flex: 0 0 calc(50% - var(--gutter) / 2); align-self: flex-start; }
}
.menu__link { display: flex; align-items: center; gap: var(--s-12); min-height: var(--tap); padding: var(--s-12) 0;
  font-family: var(--serif); font-weight: 600; font-size: var(--h3-fs); line-height: var(--h3-lh); color: var(--ink); transition: color var(--t-hover); }
.menu__link:active { color: var(--blue); }
@media (max-width: 1023px) {
  .site-header .nav { display: none; }
  .menu-toggle { display: inline-flex; }
}
/* from 480 the bar keeps the filled Work With Us beside Menu (the nav's links stay in the panel): logo left, the
   button and Menu right, 24 apart */
@media (min-width: 480px) and (max-width: 1023px) {
  .site-header__bar { gap: var(--s-24); }
  .site-header .nav { display: flex; flex: 1 1 auto; justify-content: flex-end; }
  .site-header .nav__list { display: none; }
}
/* tablet: the panel's button hugs its label like every primary button (a 720 px bar with the label hard left and the
   chevron hard right read as a stretched phone layout); phones keep the thumb-wide block */
@media (min-width: 768px) and (max-width: 1023px) { .menu__col > .btn--block { align-self: flex-start; width: auto; } }
@media (min-width: 1024px) { .menu, .menu:target { display: none; } }

/* ================================ footer ================================ */
/* Footer on the six columns (BRIEF §0f.1-2): the column lines run through it in the paper-2 rule colour, down to the
   page foot. Every block starts on a line and its text sits --cell-pad in from it (+1 px after an inner line).
   The brand (wordmark + the about text) holds the left half and the link groups (.footer__nav) the right half beside
   it, so the about text never stands beside empty columns (the "odd empty space" of R2: an empty L in every page's
   last screen). 1280+: brand on columns 1-3, Services (4), Company (5), Connect (6). 1024-1279: brand on 1-3 (about
   6 lines), Services over Connect in column 4, Company across 5-6: column 6 is 139 px at 1024, too narrow for "Work
   With Us" (95 px) or "Our Approach" (99 px) clear of the margin line, and a stacked pair stands as tall as the brand.
   Tablet: the same shape on four columns: brand on 1-2, Services over Connect in 3, Company in 4. Phones: the brand,
   then the groups two to a line. The legal line under its rule, one item per column (a column holds a 100 px label
   clear of both its lines from 1024, where --cell-pad is 24: tokens.css), four to a line on tablet, two on phones.
   Inside .footer__nav a percentage is of the half, so one of the six columns is (200% + gutter) / 6.
   The Services column is a data list (src/site.json "services", R2-F4): 3-6 entries simply lengthen the column.
   No top border: the paper-2 surface already separates the footer (BRIEF §0e.5). Six fixed children per row, so the
   column positions are plain nth-child rules (layout.css · Cols is the general form). */
.site-footer { width: 100%; display: flex; flex-direction: column; align-items: center; background: var(--paper-2); }
.site-footer__in { position: relative; z-index: 0; width: 100%; max-width: var(--container); display: flex; flex-direction: column; gap: var(--s-64);
  padding: var(--s-64) var(--margin) var(--s-32); }
.site-footer .gl { background: var(--rule-2); }
.footer__top { display: flex; flex-direction: row; flex-wrap: wrap; align-items: flex-start; row-gap: var(--s-48); width: 100%; }
.footer__top > *, .footer__nav > * { min-width: 0; padding: 0 var(--cell-pad) 0 calc(var(--cell-pad) + 1px); }
.footer__top > .footer__nav { display: flex; flex-direction: row; flex-wrap: wrap; align-items: flex-start; row-gap: var(--s-32);
  flex: 1 1 0; padding: 0; }
.footer__brand { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-32); }
.footer__about { color: var(--ink-2); font-size: 16px; line-height: 24px; }
/* the about text runs across column lines (the centre line on phones, the left half's lines from 768), so its
   measure is trimmed per width band until every ragged line end (the paragraph's last line above all) lands clear of
   a line (C6: 16 px on phones, 24 from 768). Swept at every px from 320 to 1440 (the container stops growing at
   1440; 331-333 have no clean measure); a trim of the measure only, the words never change. If the about text
   (src/site.json llm) changes, re-sweep the bands. */
@media (max-width: 330px) { .footer__about { max-width: calc(100% - var(--s-32)); } }
@media (min-width: 338px) and (max-width: 463px) { .footer__about { max-width: calc(100% - var(--s-8)); } }
@media (min-width: 464px) and (max-width: 609px) { .footer__about { max-width: calc(100% - var(--s-4)); } }
@media (min-width: 610px) and (max-width: 767px) { .footer__about { max-width: calc(100% - var(--s-16)); } }
@media (min-width: 768px) { .footer__about { max-width: calc(100% - var(--s-24)); } }
@media (min-width: 991px) and (max-width: 1023px) { .footer__about { max-width: calc(100% - var(--s-8)); } }
@media (min-width: 1041px) and (max-width: 1052px) { .footer__about { max-width: calc(100% - var(--s-48) - var(--s-8)); } }
@media (min-width: 1053px) and (max-width: 1190px) { .footer__about { max-width: calc(100% - var(--s-4)); } }
@media (min-width: 1191px) and (max-width: 1246px) { .footer__about { max-width: calc(100% - var(--s-32)); } }
@media (min-width: 1247px) and (max-width: 1279px) { .footer__about { max-width: calc(100% - var(--s-16)); } }
@media (min-width: 1343px) and (max-width: 1406px) { .footer__about { max-width: calc(100% - var(--s-48) - var(--s-8)); } }
@media (min-width: 1407px) { .footer__about { max-width: calc(100% - var(--s-32)); } }
.footer__col { display: flex; flex-direction: column; gap: var(--s-8); }
.footer__h { color: var(--ink-3); }
.footer__links { display: flex; flex-direction: column; }
.footer__links > li { align-items: flex-start; }   /* each link hugs its label, so its grow underline is the label's width */
/* a link may wrap inside its column (a long service name, R2-F4); its rule then sits under the last line */
.footer__link { display: flex; align-items: center; min-height: var(--tap); padding: 10px 0; font-family: var(--sans); font-weight: 400; font-size: 16px; line-height: 24px;
  color: var(--ink); }
.footer__link.u-grow::after { top: auto; bottom: 7px; }   /* 3 px under the last line's box */
/* the legal line keeps its rule (it closes the page, BRIEF §0e.5); its text sits --clear under it */
.footer__legal { display: flex; flex-direction: row; flex-wrap: wrap; align-items: flex-start; width: 100%; padding-top: var(--clear); border-top: 1px solid var(--rule-2); }
.footer__legal > * { min-width: 0; padding: 0 var(--cell-pad) 0 calc(var(--cell-pad) + 1px); }
.footer__small { display: flex; align-items: center; min-height: var(--tap); font-size: 16px; line-height: 24px; color: var(--ink-3); white-space: nowrap;
  transition: color var(--t-hover); }
@media (hover: hover) { a.footer__small:hover { color: var(--ink); } }
a.footer__small[aria-current="page"] { color: var(--ink); }
/* desktop and tablet: the brand on the left half (columns 1-3 of six, 1-2 of four), the groups on the right half */
@media (min-width: 768px) {
  .footer__brand { flex: 0 0 calc((100% + var(--gutter)) / 2 - var(--gutter) / 2); padding-left: var(--cell-pad); }
}
@media (min-width: 1280px) {
  .footer__col--services, .footer__col--company { flex: 0 0 calc((200% + var(--gutter)) / 6); }
  .footer__col--connect { flex: 1 1 calc((200% + var(--gutter)) / 6 - var(--gutter) / 2 - 1px); }
}
/* 1024-1279: Services over Connect in column 4 (Connect wraps under it), Company across 5-6 */
@media (min-width: 1024px) and (max-width: 1279px) {
  .footer__col--services, .footer__col--connect { flex: 0 0 calc((200% + var(--gutter)) / 6); }
  .footer__col--company { flex: 1 1 calc((200% + var(--gutter)) * 2 / 6 - var(--gutter) / 2 - 1px); }
}
@media (min-width: 1024px) {
  .footer__legal > * { flex: 0 0 calc((100% + var(--gutter)) / 6); }
  .footer__legal > :first-child { flex-basis: calc((100% + var(--gutter)) / 6 - var(--gutter) / 2); padding-left: var(--cell-pad); }
  .footer__legal > :last-child { flex: 1 1 calc((100% + var(--gutter)) / 6 - var(--gutter) / 2 - 1px); }
}
/* tablet: brand on columns 1-2; Services over Connect in column 3, Company in column 4; legal four to a line */
@media (min-width: 768px) and (max-width: 1023px) {
  .site-footer__in { gap: var(--s-48); padding-top: var(--s-48); }
  .footer__col--services, .footer__col--connect { flex: 0 0 calc((200% + var(--gutter)) / 4); }
  .footer__col--company { flex: 1 1 calc((200% + var(--gutter)) / 4 - var(--gutter) / 2 - 1px); }
  .footer__legal > :nth-child(4n+1) { flex: 0 0 calc((100% + var(--gutter)) / 4 - var(--gutter) / 2); padding-left: var(--cell-pad); }
  .footer__legal > * { flex: 0 0 calc((100% + var(--gutter)) / 4); }
  .footer__legal > :nth-child(4n) { flex: 1 1 calc((100% + var(--gutter)) / 4 - var(--gutter) / 2 - 1px); }
}
/* phones: two columns */
@media (max-width: 767px) {
  .site-footer__in { gap: var(--s-48); padding-top: var(--s-48); }
  .footer__top { row-gap: var(--s-32); }
  .footer__brand { flex: 0 0 100%; padding-left: var(--cell-pad); }
  .footer__top > .footer__nav { flex: 0 0 100%; }
  .footer__nav > .footer__col--services, .footer__nav > .footer__col--connect, .footer__legal > :nth-child(2n+1) {
    flex: 0 0 calc((100% + var(--gutter)) / 2 - var(--gutter) / 2); padding-left: var(--cell-pad); }
  .footer__col--company, .footer__legal > :nth-child(2n) { flex: 1 1 calc((100% + var(--gutter)) / 2 - var(--gutter) / 2 - 1px); }
}

/* ---------- Photo: every photograph is a library master (src/assets/img/library, written by build.mjs from
   <img data-photo="<key>">). The slot sets the ratio per breakpoint; the photo fills it, cut from its centre, the
   same in every slot (masters are re-cut so the subject is the centre; manifest top_safe lists the ratios that keep
   the subject's top). No per-slot crops and no other object-position. ---------- */
.photo { display: flex; width: 100%; height: 100%; }
.photo img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }

/* ================================ sections ================================ */

/* ---------- Hero: band, display, lead, CTA on the text cell; the photo fills its own cell. No stamp (BRIEF §0, F4:
   the warranty stamp sits in the first scroll section, beside The What's H2). ---------- */
.hero__text { gap: var(--s-32); padding-top: var(--s-48); padding-bottom: var(--s-48); }
.hero__head { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-32); }
.hero__sub { max-width: 480px; color: var(--ink-2); }
.hero__photo { background: var(--paper-3); overflow: hidden; }
/* ---------- Photo slots are square (BRIEF §0d, the 1:1 crops of A/B the client liked: F7). Every photograph on every
   route is a 1:1 plate whose edges sit on bay lines, at every width: beside the text from 1024 (its own cell, top-aligned,
   never stretched to the row), full width under it below. The library masters are centred on their subject, so the
   square is always cut from the centre. ---------- */
.hero__photo, .photo-sq { aspect-ratio: 1 / 1; align-self: flex-start; }
@media (max-width: 1023px) { .row--stack-t > .hero__photo, .row--stack-t > .photo-sq { align-self: stretch; } }
@media (max-width: 767px) { .hero__text { gap: var(--s-24); padding-top: var(--s-32); padding-bottom: var(--s-32); } .hero__head { gap: var(--s-24); } }
/* Hero · Home (BRIEF §0e.2): the left half holds the text (--cell-pad inside the margin and centre lines), the right
   half the square tower, edge to edge between the centre and margin lines. From 1024 the row sits 48 under the header
   rule and the photo's top is the band line at every width, so the first screen keeps one composition.
   >= 1240 the TrustStrip panel closes the left half: its foot is the photo's foot, Sky and photo one base.
   1024-1239 the text alone is about as tall as the square, so the panel cannot fit beside it: the text column takes the
   square's height (head on the photo's top, the foot right under it, the spare height below the button) and the panel runs under both halves (an
   opaque object may span them, §0e.2), its sentence kept in the left half.
   Tablet (768-1023) keeps the split at 2+2 with the 1024-1239 rules (pages.css sizes its display to the half).
   Phones: one column in copy order: text, TrustStrip, photo (full-width square). */
.hero__foot { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-32); }
.hero--home .hero__text { padding-left: var(--cell-pad); padding-right: var(--cell-pad); }
@media (min-width: 768px) {
  .hero--home .hero__row { padding-top: var(--s-48); }
  .hero--home .hero__text { padding-top: 0; padding-bottom: 0; }
}
@media (min-width: 1024px) { .hero--home .hero__lead { justify-content: space-between; gap: var(--s-24); } }
/* 768-1239 the text column is the photo's square (2+2 at tablet, 3+3 at 1024-1239): head on the photo's top, the foot
   right under it (the stack's 32), the spare height under the button, so the text never splits in two; the TrustStrip
   panel then runs under both halves */
@media (min-width: 768px) and (max-width: 1239px) {
  .hero--home .hero__lead { gap: 0; }
  .hero--home .hero__text { aspect-ratio: 1 / 1; justify-content: flex-start; padding-bottom: var(--s-48); }
  .hero--home .hero__trust { width: 200%; }   /* the lead is exactly one half: 200 % is the row, margin line to margin line */
}
@media (min-width: 1024px) and (max-width: 1239px) { .hero--home .hero__trust .trust { width: 50%; } }
@media (max-width: 767px) { .hero__foot { gap: var(--s-24); } }
/* Hero · Page · Halves (/about, /approach; BRIEF §0e.2): the text in the left half, the square photo filling the right
   half (edges on the centre and margin lines). From 1024 the row sits 48 under the header rule, as the home plate: the
   head tops the photo and the foot (the CTA) sits on its bottom edge, so text and photo close as one block; where the
   text is the taller (1024-1100) the photo rides down to the CTA's base, as on Hero · Home, so the two always share one
   foot (an almost-aligned edge reads as an error). Below 1024 the photo is the full-width square under the text. */
@media (min-width: 1024px) {
  .hero__row--halves { padding: var(--s-48) 0; }
  .hero__row--halves > .hero__text { justify-content: space-between; padding-top: 0; padding-bottom: 0; }
  .hero__row--halves > .hero__photo { align-self: flex-end; }
}

/* ---------- TrustStrip: the proof sentence in Newsreader at the H3 role, ink on Sky (BRIEF §0, F9), sky-line lines;
   the sentence alone (no logos until real marks, D-25). The sentence is a statement line (BRIEF §0f.2): it takes the
   width it is given and balances, never forced into a half.
   TrustStrip · Line (.trust-line, R2-F13 "one Sky object in the first screen"): a full-width Sky band of its own
   (section.section--sky > .container.container--flush), the sentence on ONE line across columns 1-4 at desktop
   (it measures about 700 px at 24 px), --cell-pad from the margin line, 32 above and below; two balanced lines below
   1024. Markup:
     <section class="section section--sky" data-name="TrustStrip"><div class="container container--flush">
       (bays) <div class="row trust-line" data-inst="TrustStrip · Line" data-name="TrustStrip"><p class="trust" …>…</p></div>
     </div></section> ---------- */
.trust { font-family: var(--serif); font-weight: 500; font-size: var(--h3-fs); line-height: var(--h3-lh); color: var(--ink);
  padding: var(--s-32) var(--cell-pad); text-wrap: balance; }
@media (max-width: 1023px) { .trust { padding-top: var(--s-24); padding-bottom: var(--s-24); } }
.trust-line { width: 100%; }
.trust-line > .trust { flex: 1 1 auto; }
@media (min-width: 1024px) { .trust-line > .trust { padding-right: calc((100% + var(--gutter)) / 3 - var(--gutter) / 2 + var(--cell-pad)); } }
/* TrustStrip on a Sky panel inside a cell (Home hero): the proof sentence between the lines */
.trust-panel { display: flex; flex-direction: column; width: 100%; }
/* TrustStrip · Line + body (/about): the line in the H2 role on the content cells, the body beside it with its first
   line on the line's first line (the one rule for every pair of halves); stacked below 1024 */
.trust-split { align-items: flex-start; }
.trust-split__body { max-width: 480px; color: var(--ink); }
@media (max-width: 1023px) { .trust-split { align-items: stretch; row-gap: var(--s-24); } }

/* ---------- ServiceTile (Home "The What", Oct 8; Tidjane: "a clean responsive 2 rows with mkps visual blocks as in
   the top one but much simpler and smaller … less stacked text blocks: name, big title and subtext"). One opaque Card
   (below) per services entry: on top a Sky-2 visual block holding the service's mini product mock (visuals.css · Mini
   mock: the stage's MediaFrame at card scale, each with its own artefact), then exactly three text levels, the name
   (a label, its motif before it as Motif · XS, the system chevron after it), the title (the tile statement) and the
   subtext (the evidence as reading text, not a stat). No fourth "More about …" line (Oct 8 critique, finding 1: it
   was the stacked block Tidjane named): the card's link keeps that label for screen readers only and covers the
   card, so the whole card is one target, one tab stop, one name, and the chevron beside the name says it goes
   somewhere. Hover is the Card's (edge to Roger Blue, the mock grows 1.02 in its stage, the chevron steps 2 px).
   The pair's text matches line for line, so neither card carries a pocket of paper above its link: the titles break
   in the same place (partials/tile-*.html), two lines each at every width but 768-809, where "time on submittals."
   cannot clear the margin line in a two-column card and Submittals takes a third; the subtext keeps each figure whole
   and, in a card too narrow for both figures on one line, drops its middle dot for a line break (the evidence is its
   own inline-size container, so the switch follows the card, never the viewport: both cards switch together). Any
   slack left (that one band) sits under the shorter title, so the subtexts of a row always share their lines.
   /approach sets the same card with its own lines (partials/tile-approach-<slug>.html: title, no subtext).
   Surface (finding 10): the Sky-2 of the visual block runs on behind the text, so the card is one Sky-2 object and
   The Gain's band below stays the page's only paper-2 surface (R2-F13); the stage's foot, where the mock's frame
   is cropped, keeps a 1 px Sky-line edge so the frame reads as running off its stage, not as a paper box cut short.
   ---------- */
.card.tile { --card-bg: var(--sky-2); }   /* .card.tile: the Card (below) sets its own fill on .card */
.tile__statement { color: var(--ink); font-size: clamp(24px, 2vw, 30px); line-height: 1.2; letter-spacing: -0.01em; }
/* Use the single fraction glyph in the numeric face, without headline tracking compressing its parts. */
.tile__statement .num--fraction { font-family: var(--sans); font-variant-numeric: normal; letter-spacing: 0; white-space: nowrap; }
.tile__body { gap: var(--s-24); }
.card__media.tile__media { flex: none; background: var(--sky-2); }   /* over the Card's paper-3 media fill */
.card__media.tile__media:empty { aspect-ratio: 2 / 1; }   /* a service whose mini mock is not drawn yet keeps a Sky-2 block */
.tile__name { display: flex; flex-direction: row; align-items: center; gap: var(--s-12); color: var(--ink); }
/* the chevron beside the name: the Link/Chevron's mark without its line (Roger Blue, 7 x 12), stepping the system's
   2 px when the card is hovered (pointer only; reduced motion keeps it home, base.css) */
.tile__name > .chev-window { width: 7px; height: 12px; }
.tile__name > .chev-window > svg { flex: none; width: 7px; height: 12px; fill: var(--blue); transition: translate var(--t-hover); }
@media (hover: hover) { .tile:hover .tile__name > .chev-window > svg { translate: var(--nudge) 0; } }
.tile:active .tile__name > .chev-window > svg { translate: none; }
.tile .tile__statement { flex: 1 1 auto; align-self: stretch; container: tt / inline-size; }
.tile__br-n { display: none; }
@container tt (max-width: 8.25em) { .tile__br-n { display: inline; } .tile__br-t { display: none; } }
.tile__evidence { flex: none; align-self: stretch; container: ev / inline-size; color: var(--ink-2); }
.tile__br { display: none; }
/* 16.8 em: the longer pair of figures, "<0.5% scope gaps · 1⁄3 the manhours", is 16.2 em in the body role */
@container ev (max-width: 16.8em) { .tile__dot { display: none; } .tile__br { display: inline; } }
@media (max-width: 767px) {
  .tile__statement { font-size: clamp(22px, 6vw, 24px); line-height: 1.24; }
}
/* phones: a tighter stack (12 between the levels), with the short mini stage
   (visuals.css), so a stacked tile reads as a card in one glance */
@media (max-width: 767px) { .tile .tile__body { gap: var(--s-16); } }
/* the card's one link, out of the flow and over the whole card (its label is .sr-only), so it adds no line and no gap */
.tile .tile__link { position: absolute; inset: 0; z-index: 1; }
.tile .tile__link:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* ServiceTile rail (.rail--svc on a Rail, below): two services fit (3+3 at desktop through .rail--d2, 2-up on
   tablets) and stack on phones, where a swipe would hide the second service. Three or more turn the same cards into a
   scroll-snap rail at every width with the next card peeking: the track runs to both viewport edges (--bleed = the
   margin plus whatever lies outside the 1440 container) and its padding and scroll-padding put the resting card's
   edge on the margin line, so every snap position is on the grid and a card in motion slides out past the margins.
   Cards per view: desktop 2 (3+3, the next card in the margin), tablet one card on three of the four columns (the
   next in the fourth), phones one card 48 px short of the width. The section clips the bleed (overflow-x: clip makes
   no scroller, so sticky and vertical overflow are untouched). The nav, keys and drag are the Rail's (site.js). */
.section:has(> .container .rail--svc) { overflow-x: clip; }
@media (max-width: 767px) {
  .rail--svc:not(:has(.rail__item:nth-child(3))) .rail__track { flex-direction: column; align-self: stretch; width: 100%; gap: var(--gutter);
    overflow: visible; scroll-snap-type: none; }
  .rail--svc:not(:has(.rail__item:nth-child(3))) .rail__track > * { flex: 0 0 auto; padding: 0; }
}
.rail--svc:has(.rail__item:nth-child(3)) { --bleed: calc(var(--margin) + max(0px, (100vw - var(--container)) / 2)); }
.rail--svc:has(.rail__item:nth-child(3)) .rail__track { width: calc(100% + var(--gutter) + 2 * var(--bleed));
  padding: 0 var(--bleed); scroll-padding: 0 var(--bleed); }
@media (min-width: 768px) and (max-width: 1023px) { .rail--svc:has(.rail__item:nth-child(3)) .rail__track > * { flex-basis: 75%; } }
@media (max-width: 767px) { .rail--svc:has(.rail__item:nth-child(3)) .rail__track > * { flex-basis: calc(100% - var(--s-48)); } }

/* ---------- QuoteBand: the blue field, its frame lines in grid-on-blue. R2 (R2-F2, R2-F7): the customer's words on a
   QuoteTile beside a 1:1 licensed photo, as the CustomerCarousel's pair: the photo across columns 1-2, the tile across
   columns 3-6 with the attribution on its foot; tablet two columns each; phones the photo over the tile. Row layout:
   service.css .svc-quote (the one markup, tools/gen-service.py; the styleguide master reuses it). ---------- */
.quote-band__q { color: var(--on-blue); }
.quote-band__by, .carousel__by { color: var(--on-blue); }

/* ---------- QuoteTile (.quote-tile): a customer's words on their own tile on the Roger Blue field, Roger Blue at its
   pressed depth (#284A62, on-blue text 8.6:1). A real surface, edges on the column lines, so the field's lines stop at
   it: a quote of any length then wraps freely at any width without a line ever running into the end of one of its
   lines (C6), which a multi-column quote set straight on the field cannot promise. Text --cell-pad in on every side.
   Used by the CustomerCarousel's figure; the QuoteBand's words cell takes it too (one class on the cell). ---------- */
.quote-tile { --surface: var(--blue-press); padding: var(--cell-pad); background: var(--blue-press); color: var(--on-blue); }
.row > .cell.quote-tile { padding: var(--cell-pad); }
/* desktop: the quote set one role up (the heading sizes, in the quote's own face and weight), so a three-line quote
   fills the four-column tile beside the 1:1 photo instead of leaving some 40 % of it empty field: H1 sizes from 1280,
   H2 sizes 1024-1279. One rule for the CustomerCarousel and the QuoteBand, so both keep the look Josh called clean. */
@media (min-width: 1280px) { .quote-tile .t-quote { font-size: var(--h1-fs); line-height: var(--h1-lh); letter-spacing: var(--h1-ls); } }
@media (min-width: 1024px) and (max-width: 1279px) { .quote-tile .t-quote { font-size: var(--h2-fs); line-height: var(--h2-lh); letter-spacing: var(--h2-ls); } }

/* ---------- CustomerCarousel (BRIEF §0, F6; R2-F2): the ONE big Roger Blue field, above the closing CTA. One slide at
   a time, each a pair: a 1:1 photo and its customer quote. Prev / next + one square dot per quote; keyboard (buttons,
   Left / Right inside the carousel); swipe and drag; never autoplays.
   On the six columns: >= 1024 the photo across columns 1-2 and the quote tile across columns 3-6 (both flush, edges
   on the lines: opaque objects cover them), the quote from the tile's top, and the attribution + controls on its foot.
   Tablet: photo and tile two columns each, controls at the quote pane's lower right. Phones: photo full width, tile
   below, then controls beneath its attribution at the lower right.
   Motion (BRIEF §0f.4): with site.js the slides sit side by side in a clipped viewport and the track travels one
   slide (480 ms, enter curve); a drag moves it with the pointer and settles on release. The track is as tall as the
   tallest slide, so the field never jumps. Slides off screen are hidden (visibility, after the travel) and inert.
   Without JS every pair shows, stacked 64 apart, and the controls stay hidden. Reduced motion: the slide swaps at
   once. ---------- */
.carousel { display: flex; flex-direction: column; align-items: stretch; gap: var(--s-32); width: 100%; }
.carousel__viewport { display: flex; flex-direction: column; align-items: stretch; width: 100%; }
.carousel__track { display: flex; flex-direction: column; align-items: stretch; gap: var(--s-64); width: 100%; }
.carousel__slide { display: flex; flex-direction: column; align-items: stretch; flex: 0 0 auto; width: 100%; }   /* role=group wrapper */
.carousel__pair { flex: 1 1 auto; }
.carousel__fig { justify-content: space-between; gap: var(--s-32); }
.carousel__fig .carousel__q { max-width: 32ch; color: var(--on-blue); font-size: var(--quote-fs);
  line-height: var(--quote-lh); letter-spacing: -0.005em; }
.carousel__media { background: var(--blue-press); }
/* enhanced (site.js sets data-ready): a sideways track in a clipped viewport */
[data-carousel][data-ready] .carousel__viewport { overflow: clip; touch-action: pan-y; cursor: grab; }
[data-carousel][data-ready] .carousel__track { flex-direction: row; gap: 0; translate: calc(var(--slide, 0) * -100%) 0;
  transition: translate var(--dur-slide) var(--ease-out); }
[data-carousel][data-ready] .carousel__slide { flex: 0 0 100%; }
[data-carousel][data-ready] .carousel__slide[aria-hidden="true"] { visibility: hidden; transition: visibility 0s linear var(--dur-slide); }
[data-carousel][data-dragging] .carousel__viewport { cursor: grabbing; user-select: none; }
[data-carousel][data-dragging] .carousel__track { transition: none; translate: calc(var(--slide, 0) * -100% + var(--drag, 0px)) 0; }
[data-carousel][data-dragging] .carousel__slide[aria-hidden="true"] { visibility: visible; transition: none; }
.js [data-carousel]:not([data-ready]) .carousel__slide + .carousel__slide { display: none; }   /* no flash of every quote before site.js */
.carousel__nav { display: none; flex-direction: row; align-items: center; justify-content: flex-end; gap: var(--s-16); padding: 0 var(--cell-pad); }
.js .carousel__nav { display: flex; }
/* From tablet up, the controls sit in the quote pane's lower-right clear space. Their baseline aligns with a single
   attribution line where room allows; a narrow tablet caption gets its own bounded measure instead of crossing them. */
@media (min-width: 1024px) {
  .carousel { position: relative; }
  .carousel__nav { position: absolute; right: var(--cell-pad); bottom: calc(var(--cell-pad) - (var(--control-h) - var(--label-lh)) / 2); padding: 0; }
  .carousel__by { max-width: calc(100% - 276px); }
}
@media (min-width: 768px) and (max-width: 1023px) {
  .carousel { position: relative; }
  .carousel__nav { position: static; gap: var(--s-8); padding: var(--s-16) var(--cell-pad) 0; }
  .carousel__by { max-width: 100%; }
}
@media (max-width: 767px) {
  .carousel__fig { gap: var(--s-24); }
  .carousel__fig .carousel__q { max-width: 32ch; }
  .carousel__nav { padding: 0 var(--cell-pad); }
  .row--stack-m > .carousel__media { align-self: stretch; }
  .carousel { gap: var(--s-24); }
}
.carousel__btn { display: flex; flex: none; align-items: center; justify-content: center; width: var(--control-h); height: var(--control-h);
  border: 1px solid var(--on-blue); background: var(--blue); color: var(--on-blue); transition: background-color var(--t-hover); }
.carousel__btn svg { flex: none; width: 8px; height: 14px; fill: currentColor; transition: translate var(--t-hover); }
@media (hover: hover) {
  .carousel__btn:hover { background: var(--blue-hover); }
  .carousel__btn[data-carousel-next]:hover svg { translate: var(--nudge) 0; }
  .carousel__btn[data-carousel-prev]:hover svg { translate: calc(var(--nudge) * -1) 0; }
}
.carousel__btn:active { background: var(--blue-press); }
.carousel__btn:active svg { translate: none; }
.carousel__dots { display: flex; flex-direction: row; align-items: center; }
.carousel__dot { display: flex; flex: none; align-items: center; justify-content: center; width: var(--tap); height: var(--tap); }
.carousel__dot-m { display: block; flex: none; width: 8px; height: 8px; border: 1px solid var(--on-blue); transition: background-color var(--t-hover), scale var(--t-hover); }
@media (hover: hover) { .carousel__dot:hover .carousel__dot-m { scale: 1.25; } }
.carousel__dot[aria-current="true"] .carousel__dot-m { background: var(--on-blue); }

/* ---------- CTABand: band + line + Hire Roger under the 2 px ink rule (the one rule that carries the close, BRIEF §0e.5).
   On the six columns (BRIEF §0f.2, a Statement): >= 1024 the line across columns 1-4 (one line at H2 for the doc's
   closing lines), the button (and the secondary link) opening column 5, --cell-pad (+1 px) from its line and level with
   the line's last line of type, so the eye reads the statement and lands on the action; below 1024 the button under
   the line, hugging its label like every primary button. No bottom rule: the band ends its section (no bottom
   padding), so what follows (the next section, or the footer's paper-2 surface) closes it. ---------- */
.cta-band { align-items: flex-end; }   /* its top edge is the full-bleed section rule (layout.css), no second line */
.cta-band__lead { gap: var(--s-24); padding-top: var(--s-64); padding-bottom: var(--s-64); }
.row > .cta-band__actions { flex-direction: row; flex-wrap: wrap; align-items: center; gap: var(--s-16) var(--s-32);
  padding: 0 var(--cell-pad) var(--s-64) calc(var(--cell-pad) + 1px); }
/* a secondary action with no destination yet stays hidden; the design-state key ?state=<name> shows it (site.js) */
.cta-band__secondary { display: none; }
.state-jobs .cta-band__secondary, .cta-band--secondary .cta-band__secondary { display: inline-flex; }
/* the secondary link goes on its own line under the button at every width: set beside it, its label ran up to the
   next column line (10 at desktop, the centre line on tablets and phones) */
.row.cta-band--secondary > .cta-band__actions, .state-jobs .row.cta-band > .cta-band__actions { flex-direction: column; flex-wrap: nowrap; align-items: flex-start; }
@media (min-width: 1024px) {
  .cta-band__lead { flex: 0 0 calc((100% + var(--gutter)) * 2 / 3 - var(--gutter) / 2); }
  /* the 48 px button's foot on the 50-60 px line box's foot: centred on the last line, not hanging under it */
  .row > .cta-band__actions { padding-bottom: calc(var(--s-64) + (var(--h2-lh) - var(--control-h)) / 2); }
}
@media (max-width: 1023px) {
  .cta-band { flex-direction: column; align-items: stretch; }
  .cta-band__lead { flex: 0 0 auto; padding-top: var(--s-48); padding-bottom: var(--s-32); }
  .row > .cta-band__actions { flex: 0 0 auto; padding-left: var(--cell-pad); padding-bottom: var(--s-48); }
}
@media (max-width: 767px) {
  .cta-band__lead { padding-top: var(--s-48); padding-bottom: var(--s-24); }
}
/* Hire Roger never ends on a column line (at its natural 142 px it stopped on the 207 line at 768 and crossed the
   inner line by only 16 px at 1024): under 1024 it runs one bay, its edge a --cell-pad past the first line, like its
   label's pad from the line it starts at; at 1024-1279 it fills its two columns pad to pad, the line passing behind it
   with room on both sides, and a secondary link sits centred under it, where its label clears that line too (left-set,
   "Jobs at Roger" ended 23 px before it at 1024). From 1280 a bay is wide enough for both at their natural size.
   The widened button keeps its label and chevron packed in the centre (Button/Primary), so it is the same control at
   every width, only longer. */
@media (max-width: 767px) { .cta-band__btn { min-width: calc((100% + 2 * var(--cell-pad)) / 2); } }
@media (min-width: 768px) and (max-width: 1023px) { .cta-band__btn { min-width: calc((100% + 2 * var(--cell-pad)) / 4); } }
@media (min-width: 1024px) and (max-width: 1279px) {
  .cta-band__btn { min-width: 100%; }
  .cta-band__actions > .cta-band__secondary { align-self: center; }
}
/* the default line holds "on your next job." together (cta-band.html) so "Put Roger on" never ends on the phone's
   centre line; under 360 the held run would end "Put Roger" on that line instead, so there it wraps freely */
@media (max-width: 359px) { .cta-band__line .nowrap { white-space: normal; } }

/* ---------- ItemList: h3 + text; spacing separates the items, no hairlines (BRIEF §0e.5) ---------- */
.items { display: flex; flex-direction: column; width: 100%; }
.items__item { display: flex; flex-direction: column; gap: var(--s-8); padding: var(--cell-pad); }
/* in a flush cell that a column line opens, the line takes the cell's first pixel: the items pad 1 px more (as
   .row > .cell + .cell does), so their text lines up with the cells beside and above; stacked rows have no line */
.row > .cell + .cell--flush > .items > .items__item { padding-left: calc(var(--cell-pad) + 1px); }
@media (max-width: 1023px) { .row--stack-t > .cell + .cell--flush > .items > .items__item { padding-left: var(--cell-pad); } }
@media (max-width: 767px) { .row--stack-m > .cell + .cell--flush > .items > .items__item { padding-left: var(--cell-pad); } }
/* ItemList · Register (service pages; BRIEF §0e): the register sits in the left half (full width below 1024); space
   separates the items, no hairlines; an even list runs two to a row at 768-1023 and from 1280 (the half's two-column
   split, 48 apart), an odd one in a single column, and every list in one column at 1024-1279, where a column of the
   half would set H3s word by word. Fixed bases with 0.5 px slack, so Figma's wrap breaks where Chrome does. */
.register { position: relative; display: flex; flex-direction: column; width: 100%; }
.items--register { gap: var(--s-32); }
.items--register > .items__item { padding: 0; border-top: 0; }
@media (min-width: 768px) and (max-width: 1023px), (min-width: 1280px) {
  .register--pairs .items--register { flex-direction: row; flex-wrap: wrap; align-items: flex-start; gap: var(--s-48); }
  .register--pairs .items--register > .items__item { flex: 0 0 calc(50% - var(--s-24) - 0.5px); min-width: 0; }
}
.items__text { color: var(--ink-2); max-width: 576px; }   /* about 72 characters of 18 px Archivo */

/* ---------- KeyFacts: a register of sentences on their surface (the Sky panel), separated by space, not rules (BRIEF §0e.5) ---------- */
.facts { display: flex; flex-wrap: wrap; gap: var(--s-24) var(--s-32); width: 100%; }
.facts__item { flex: 1 1 300px; min-width: 220px; }
.facts__t { color: var(--ink); }   /* the delivery note too: grey would read disabled; its place last sets it apart */
.facts__t .num { font-weight: 600; }   /* the figure a fact turns on (gen-service.py FIG): Archivo 600 tabular, the stat face */
/* px bases, not calc(%): Figma breaks wrap rows on the basis, so three per row at 1440 (1232 px), two at 768 (640 px);
   the facts grow to share each row, as Figma's FILL children do. The 220 px min-width is the break point the importer
   falls back to when a page sets its own %-basis (the service pages' two-across register). */
@media (max-width: 1023px) { .facts__item { flex-basis: 280px; } }
@media (max-width: 767px) { .facts { flex-direction: column; flex-wrap: nowrap; } .facts__item { flex: 0 0 auto; } }
/* KeyFacts · Thirds, set in halves (/about values; BRIEF §0e.2): the first value in the left half, the other two
   stacked in the right half, first lines level; stacked below 1024 */
.facts-halves__pair { gap: var(--s-16); }
@media (max-width: 1023px) { .facts-halves { row-gap: var(--s-16); } }
/* KeyFacts · Panel (service hero): a Sky panel filling the right half from the header rule down (its edges on the
   centre and margin lines, so it covers the centre line); margin line to margin line under the pitch below 1024. The
   definition, then the facts two to a row (the half's two-column split, 48 apart; one per row on phones and at
   1024-1279, where a column would set under 200 px). Fixed bases with 0.5 px slack, no grow: a Figma wrap row cannot FILL and rounding must never wrap it. */
.facts-panel { display: flex; flex-direction: column; flex: 1 1 auto; width: 100%; padding: var(--s-64) var(--cell-pad); }
.facts-panel__in { gap: var(--s-48); }
@media (min-width: 768px) and (max-width: 1023px), (min-width: 1280px) {
  .facts-panel .facts { gap: var(--s-32) var(--s-48); }
  .facts-panel .facts__item { flex: 0 0 calc(50% - var(--s-24) - 0.5px); min-width: 0; }
}
@media (max-width: 1023px) { .facts-panel { padding: var(--s-48) var(--cell-pad); } .facts-panel__in { gap: var(--s-32); } }
@media (max-width: 767px) { .facts-panel { padding: var(--s-32) var(--cell-pad); } .facts-panel__in { gap: var(--s-24); } }

/* ---------- CompareTable: Today / AI-assisted / With Roger (BRIEF §0e, F20) ----------
   The table spans the container, margin line to margin line. >= 1280: quarters, label | Today | AI-assisted | With
   Roger, the With Roger column Sky from the head to the last row. 768-1279 (a quarter is too narrow for the states'
   sentences): each row's label on a line of its own, Today | AI-assisted as halves (the centre line between them from
   1024), With Roger the full-width Sky row under them, each state with its column label. Phones: the states stacked,
   each with its label, With Roger on Sky. Fixed bases (a Figma wrap row cannot FILL). Text pads --cell-pad from its
   lines, 1 px more after a line (the line takes the cell's first pixel).
   Children: head = Corner, Today, AI-assisted, With Roger; row = label, spacer, Today, AI-assisted, With Roger, so the
   states are always the last three. */
.compare { display: flex; flex-direction: column; width: 100%; }
.compare__row { display: flex; flex-direction: row; flex-wrap: wrap; border-top: 1px solid var(--rule); }
.compare__row--head { border-top: 0; }
.compare__row--head > .compare__cell:first-child { display: none; }   /* the corner: the labels have their own line */
.compare__cell { display: flex; flex-direction: column; gap: var(--s-8); flex: 0 0 100%; min-width: 0; padding: var(--s-24) var(--cell-pad); }
.compare__row > [role="rowheader"] { padding-bottom: 0; }
.compare__cell--roger { background: var(--sky); }   /* the Sky highlight (BRIEF §0, F15, F20) */
.compare__row--head .compare__cell--roger { border-top: 2px solid var(--blue); }
/* the Roger head cell's 2 px rule would push its label 2 px below Today / AI-assisted: the other head cells pad 2 more */
.compare__row--head .compare__cell:not(.compare__cell--roger) { padding-top: calc(var(--s-24) + 2px); }
.compare__pad { display: none; }
.compare__rowhead { font-family: var(--sans); font-weight: 600; font-size: var(--body-fs); line-height: var(--body-lh); color: var(--ink); }
/* the row label sits in the first bay at every width (it wraps inside it rather than stop on the line that closes it):
   one bay less the pads, re-expressed on the label cell (8 of 12 columns, 4 of 8, the full 4 on phones) */
.compare__rowhead { max-width: calc(50% - var(--cell-pad)); }
@media (min-width: 768px) { .compare__rowhead { max-width: calc(50% - var(--cell-pad) - var(--gutter) / 4); } }
.compare__text { color: var(--ink-2); }
.compare__cell--roger .compare__text { color: var(--ink); }
.compare__lab { display: none; color: var(--ink-3); }
/* >= 1280 (BRIEF §0e): quarters, label | Today | AI-assisted | With Roger, so the centre line falls on the boundary
   between Today and AI-assisted and passes between the columns, never through a cell's text; every cell padded, the
   row rules drawn per cell (sky-line on the Sky column) */
@media (min-width: 1280px) {
  .compare__row { flex-wrap: nowrap; border-top: 0; }
  .compare__row--head > .compare__cell:first-child { display: flex; }
  .compare__pad { display: none; }
  .compare__cell { padding: var(--s-32) var(--cell-pad); border-top: 1px solid var(--rule); }
  .compare__cell--roger { border-top-color: var(--sky-line); }
  .compare__row--head > .compare__cell { padding-bottom: var(--s-24); }
  .compare__row--head > .compare__cell:not(.compare__cell--roger) { border-top: 0; padding-top: calc(var(--s-24) + 2px); }
  .compare__row--head > .compare__cell--roger { border-top: 2px solid var(--blue); padding-top: var(--s-24); }
  .compare__row > .compare__cell:first-child, .compare__cell:last-child { flex: 0 0 calc((100% + var(--gutter)) / 4 - var(--gutter) / 2); }
  .compare__cell:nth-last-child(3), .compare__cell:nth-last-child(2) { flex: 0 0 calc((100% + var(--gutter)) / 4); }
  .compare__cell:nth-last-child(2) { padding-left: calc(var(--cell-pad) + 1px); }   /* the centre line takes its first pixel */
  .compare__rowhead { max-width: none; }
}
@media (max-width: 1279px) {
  .compare__row--head { display: none; }
  .compare__lab { display: block; }
  .compare__cell--roger:last-child { padding-top: var(--s-24); padding-bottom: var(--s-24); }
}
@media (min-width: 768px) and (max-width: 1279px) {
  .compare__cell:nth-last-child(3), .compare__cell:nth-last-child(2) { flex-basis: 50%; }
  .compare__cell:nth-last-child(2) { padding-left: calc(var(--cell-pad) + 1px); }
  .compare__cell--roger:last-child .compare__text { max-width: 576px; }   /* the full-width Sky row keeps a reading measure */
}
/* phones: each row group is one block on its surfaces, no hairline per cell (BRIEF §0e.5): the label, Today and
   AI-assisted on paper-2, 16 apart, then With Roger on Sky; a single ink rule opens each group */
@media (max-width: 767px) {
  .compare { gap: var(--s-32); }
  .compare__row { border-top: 2px solid var(--ink); background: var(--paper-2); }
  .compare__row > [role="rowheader"] { padding-top: var(--s-24); padding-bottom: 0; }
  .compare__cell { padding-top: var(--s-16); padding-bottom: 0; }
  .compare__cell:nth-last-child(2) { padding-bottom: var(--s-24); }
  .compare__cell--roger:last-child { padding-top: var(--s-24); padding-bottom: var(--s-24); }
}

/* ---------- WarrantyBlock (once per page). One arrangement everywhere: the title leads, the three promises follow,
   and the Stamp/Warranty seals them at the end, so the stamp's label never sits on its own heading. ---------- */
.warranty { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-32); }
/* the three promises are a short list: 12 apart, no hairlines (BRIEF §0e.5) */
.warranty__list { display: flex; flex-direction: column; gap: var(--s-12); width: 100%; }
.warranty__li { padding: 0; }
/* WarrantyBlock · Band (service pages): a Sky panel in the right half (full width below 1024): the title over the three
   promises, then the Stamp/Warranty M signing it at the right foot (left on phones). */
/* WarrantyBlock · Panel (/hire-roger): the three lines (each led by its IconBadge) and the seal on a Sky panel, the
   proof sentence under them at every width (one Sky object on the page, R2-F13) */
.warranty-panel { display: flex; flex-direction: column; align-items: stretch; padding: var(--s-64) 0 var(--s-16); }
.warranty-panel > .warranty { padding: 0 var(--cell-pad); }
@media (max-width: 1023px) { .warranty-panel { padding: var(--s-48) 0; } }
@media (max-width: 767px) { .warranty-panel { padding: var(--s-32) 0; } }
/* the stamp sits right (left on phones) through the band's own alignment, the terms stretch: one per-child align-self
   other than stretch has no Figma auto-layout equivalent (al-lint), so the band aligns and the terms opt out */
.warranty-band { display: flex; flex-direction: column; align-items: flex-end; gap: var(--s-48); width: 100%; padding: var(--s-48) var(--cell-pad); }
.warranty-band > :not(.stamp) { align-self: stretch; }
@media (max-width: 767px) { .warranty-band { align-items: flex-start; gap: var(--s-24); padding: var(--s-32) var(--cell-pad); } }
/* WarrantyBlock · Certificate (/approach): the page's one Sky-2 panel, an opaque certificate between the margin lines
   (BRIEF §0f.2 Cards 2+2+2): band + title at its head with the seal (Stamp/Warranty · L) at the head's right, then
   the three promises in three ruled columns on the page's lines 4 and 8 (drawn in Sky-line inside the panel, for the
   promises' height only, so the title never meets a rule), each led by its IconBadge. Text sits --cell-pad in from
   the lines (layout.css · Cols). Below 1024 the order is title, promises (icon beside the words), then the seal
   (Stamp/Warranty · M), where a certificate is signed. */
.warranty-cert { position: relative; z-index: 0; gap: var(--s-48); padding: var(--s-64) 0; }
.warranty-cert__top { align-items: flex-start; }
.warranty-cert__head { gap: var(--s-24); }
.warranty-cert__seal { align-items: flex-end; }
.warranty-cert__terms { position: relative; z-index: 0; display: flex; flex-direction: column; }
.warranty-cert__list { --cols-gap: var(--s-24); }
.warranty-cert__term { display: flex; flex-direction: row; align-items: center; gap: var(--s-24); }
@media (min-width: 1024px) { .warranty-cert__term { flex-direction: column; align-items: flex-start; } }
.warranty-cert__seal--foot { align-items: flex-start; padding: 0 var(--cell-pad); }
@media (max-width: 1023px) { .warranty-cert { gap: var(--s-32); padding: var(--s-48) 0; } }
@media (max-width: 767px) { .warranty-cert { gap: var(--s-24); padding: var(--s-32) 0; } .warranty-cert__term { gap: var(--s-16); } }

/* ---------- Steps: numbered process ---------- */
.steps { display: flex; flex-direction: column; width: 100%; counter-reset: none; }
/* the numbers and the space between steps separate them: no hairlines (BRIEF §0e.5) */
.steps__item { display: flex; flex-direction: row; gap: var(--s-32); padding: var(--cell-pad) 0; }
.steps__n { flex: 0 0 64px; font-family: var(--sans); font-weight: 600; font-size: var(--h3-fs); line-height: var(--h3-lh); color: var(--blue); font-variant-numeric: tabular-nums; }
.steps__body { display: flex; flex-direction: column; gap: var(--s-8); flex: 1 1 0; max-width: 720px; }
.steps__text { color: var(--ink-2); }
@media (max-width: 767px) { .steps__item { flex-direction: column; gap: var(--s-8); } .steps__n { flex-basis: auto; } }
/* Steps · Ledger (service pages, on the page's Sky-2 band; BRIEF §0e halves): one row per step, 48 apart: the number
   (stat role, Roger Blue) and the title in the left half, what happens in the right half, each --cell-pad from the
   lines that bound it. The title and the text drop by half the difference between their line boxes and the number's,
   so the first lines share a centre line. Below 1024: number and title, then the text under them. */
.ledger { position: relative; display: flex; flex-direction: column; width: 100%; }
.steps--ledger { gap: var(--s-48); }
.steps--ledger .steps__item { flex-direction: column; gap: var(--s-16); padding: 0 var(--cell-pad); }
@media (min-width: 768px) and (max-width: 1023px) { .steps--ledger .steps__text { padding-left: calc(var(--s-48) + var(--s-24)); } }   /* under the title */
@media (max-width: 767px) { .steps--ledger { gap: var(--s-32); } .steps--ledger .steps__item { gap: var(--s-8); } }
.steps__head { display: flex; flex-direction: row; align-items: flex-start; gap: var(--s-24); }
.steps__num { flex: 0 0 var(--s-48); color: var(--blue); }
.steps__title { flex: 1 1 0; min-width: 0; padding-top: calc((var(--stat-lh) - var(--h3-lh)) / 2); }
.steps--ledger .steps__text { max-width: 576px; }
@media (min-width: 1024px) {
  .steps--ledger .steps__item { flex-direction: row; gap: 0; padding: 0; }
  .steps--ledger .steps__head { flex: 0 0 50%; padding: 0 var(--cell-pad); }
  .steps--ledger .steps__text { flex: 1 1 0; min-width: 0; max-width: none;
    padding: calc((var(--stat-lh) - var(--body-lh)) / 2) var(--cell-pad) 0 calc(var(--cell-pad) + 1px); }   /* the centre line takes its first pixel */
}

/* ---------- StatBars (on the blue field): two Archivo 32/40 figures on one 0-100 % scale, each with its bar. A derived
   display (aria-hidden, data-derived): the sentence beside it carries the claim ---------- */
.stat-bars { display: flex; flex-direction: column; justify-content: center; gap: var(--s-16); }
.stat-bars__bar { display: flex; flex-direction: row; align-items: center; gap: var(--s-24); width: 100%; }   /* runs to the cell padding */
.stat-bars__n { flex: none; color: var(--on-blue); }
.stat-bars__track { display: flex; flex: 1 1 0; height: 12px; background: var(--grid-on-blue); }
.stat-bars__fill { display: block; flex: none; height: 12px; background: var(--on-blue); }
/* StatBars · On Sky (the articles' belief figure, on a Sky tile): the same two bars in the paper palette, the figures
   in Roger Blue as every stat on paper, the scale in Sky-line, the fill Roger Blue */
.stat-bars--sky .stat-bars__n { color: var(--blue); }
.stat-bars--sky .stat-bars__track { background: var(--sky-line); }
.stat-bars--sky .stat-bars__fill { background: var(--blue); }

/* ---------- Pricing: the terms, and the proof that the bill shrinks ---------- */
/* the terms (band, H2, body) beside the proof: the doc's own sentence in the H3 role on a Sky panel (BRIEF §0: Sky
   carries the outcome panels), its figures in Archivo through .num. No derived numeral: the sentence is the proof,
   and a display-size figure would outrank the page H1 (and Q-08 is open) */
.pricing { row-gap: var(--s-48); }
.pricing__proof { justify-content: center; padding-top: var(--s-48); padding-bottom: var(--s-48); }
@media (max-width: 767px) { .pricing__proof { padding-top: var(--s-32); padding-bottom: var(--s-32); } }
.pricing__t { max-width: 520px; color: var(--ink); }

/* ---------- FAQ: accordion on <details> (works without JS); FAQPage schema on service pages ---------- */
/* R2 (BRIEF §0f, R2-F10): each item is its own paper-2 tile, 8 apart, on the column grid: an opaque object, so the
   lines stop at it and a question or answer never meets one, and the tiles replace the hairlines between items. */
.faq { display: flex; flex-direction: column; gap: var(--s-8); width: 100%; }
.faq__item { display: flex; flex-direction: column; padding: 0 var(--cell-pad); background: var(--paper-2); }
.faq__q { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-24); min-height: var(--tap);
  padding: var(--s-24) 0; cursor: pointer; list-style: none; }
.faq__q::-webkit-details-marker { display: none; }
/* hover: the question takes Roger Blue and the chevron steps 2 px toward the answer (150 ms); the open/close itself
   is the ::details-content height transition and the caret cross-fade below */
.faq__q-t, .faq__icon svg { transition: color var(--t-hover), translate var(--t-hover); }
@media (hover: hover) {
  .faq__q:hover .faq__q-t { color: var(--blue); }
  .faq__item:not([open]) > .faq__q:hover .faq__icon svg { translate: 0 var(--nudge); }
  .faq__item[open] > .faq__q:hover .faq__icon svg { translate: 0 calc(var(--nudge) * -1); }
}
.faq__q-t { font-family: var(--serif); font-weight: 600; font-size: var(--h3-fs); line-height: var(--h3-lh); color: var(--ink); max-width: 760px; text-wrap: balance; }
/* the two carets share one 24 px slot (the up caret laid over the down one) and cross-fade over 150 ms when the item
   opens or closes: the state change reads as one control turning, with no rotate (BRIEF kill list) */
.faq__icon { position: relative; flex: none; display: flex; align-items: center; justify-content: center; width: 24px; height: var(--h3-lh); color: var(--blue); }
.faq__icon svg { width: 14px; height: 8px; fill: currentColor; }
.faq__icon-down, .faq__icon-up { display: flex; transition: opacity var(--dur-hover) var(--ease-out); }
.faq__icon-up { position: absolute; inset: 0; align-items: center; justify-content: center; opacity: 0; }
.faq__item[open] .faq__icon-up { opacity: 1; }
.faq__item[open] .faq__icon-down { opacity: 0; }
/* open: the answer's height grows from 0 (interpolate-size, base.css) over 240 ms ease-out while it fades in; close:
   it folds back over 180 ms ease-in. The answer stays laid out until the fold ends (display and content-visibility
   flip last, allow-discrete), then drops out: a closed item is not laid out, so Figma imports it at its closed height. */
.faq__a { display: flex; flex-direction: column; gap: var(--s-16); padding-bottom: var(--s-32); max-width: 640px; color: var(--ink-2);
  transition: opacity var(--dur-exit) var(--ease-in), display var(--dur-exit) allow-discrete; }
.faq__item:not([open]) > .faq__a { display: none; opacity: 0; }
.faq__item[open] > .faq__a { opacity: 1; transition: opacity var(--dur-enter) var(--ease-out), display var(--dur-enter) allow-discrete; }
@starting-style { .faq__item[open] > .faq__a { opacity: 0; } }
.faq__item::details-content { block-size: 0; overflow: clip;
  transition: block-size var(--dur-exit) var(--ease-in), content-visibility var(--dur-exit) allow-discrete; }
.faq__item[open]::details-content { block-size: auto;
  transition: block-size var(--dur-enter) var(--ease-out), content-visibility var(--dur-enter) allow-discrete; }
/* Chrome does not run the height transition on ::details-content while it is a flex item, so in a live browser with
   motion on (html.reveal-on, set by site.js; never under reduced motion or in the importer, lint and renders) the item
   lays out as a block: the same stack (no gap, children full width) with the fold animated. Everywhere else it stays
   the flex column the Figma importer reads as auto-layout. */
.reveal-on .faq__item { display: block; }
/* a design-state capture (?state=faq-open, .motion-off) shows the open answer at once; its own rule, as in base.css */
.motion-off .faq__item::details-content { transition: none !important; }

/* ---------- Related: next reads, each an H3 link with the chevron; space separates them (BRIEF §0e.5) ---------- */
.related { display: flex; flex-direction: column; gap: var(--s-24); width: 100%; }
.related__list { display: flex; flex-wrap: wrap; gap: var(--s-24) var(--s-32); }
.related__item { flex: 1 1 280px; display: flex; }
.related__link { display: inline-flex; align-items: flex-end; gap: var(--s-12); min-height: var(--tap); color: var(--ink); }
.related__link .related__t { font-family: var(--serif); font-weight: 600; font-size: var(--h3-fs); line-height: var(--h3-lh);
  max-width: 15em; text-wrap: balance; }   /* 15em: the box ends near its longest balanced line */
.related__mark { display: flex; flex: none; align-items: center; height: var(--h3-lh); }
.related__link svg { flex: none; width: 9px; height: 16px; fill: var(--blue); }
/* hover: the (possibly wrapped) title's underline fades in and the chevron steps 2 px forward */
.related__link .related__t { text-decoration-line: underline; text-decoration-color: transparent; text-decoration-thickness: 1px;
  text-underline-offset: 6px; transition: text-decoration-color var(--t-hover); }
.related__link svg { transition: translate var(--t-hover); }
@media (hover: hover) {
  .related__link:hover .related__t { text-decoration-color: currentColor; }
  .related__link:hover svg { translate: var(--nudge) 0; }
}

/* ---------- ArticleBody: the reading measure is 560 px (median 67-68 characters a line of Archivo 18/28, longest 77);
   the wide parts (Sky panel, pull quote) take 624, so a panel's text (32 in) keeps the same measure ---------- */
.article { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-24); width: 100%; max-width: 624px; }
.article > :is(.article__p, .article__h2, .article__list, .article__byline, .article-signoff) { max-width: 560px; }
.article__byline { font-family: var(--serif); font-style: italic; font-weight: 500; font-size: var(--lead-fs); line-height: var(--lead-lh); color: var(--ink-2); }
.article__p { color: var(--ink); }
.article__h2 { font-family: var(--serif); font-weight: 600; font-size: var(--h3-fs); line-height: var(--h3-lh); color: var(--ink); text-wrap: balance; }
/* a subhead opens a new movement: 48 above (gap 24 + 24), 24 below */
.article > .article__h2 { padding-top: var(--s-24); }
.article__list { display: flex; flex-direction: column; gap: var(--s-16); }
.article__li { display: flex; flex-direction: column; gap: var(--s-4); }
.article__li-t { font-weight: 600; color: var(--ink); }
.article__li-x { color: var(--ink-2); }
/* bulleted list (doc bullets): an 8 px Roger Blue square on the first line's centre, 16 from the text */
.article-li { flex-direction: row; align-items: flex-start; gap: var(--s-16); }
.article-li__m { flex: none; display: flex; align-items: center; height: var(--body-lh); }
.article-li__sq { display: block; width: 8px; height: 8px; background: var(--blue); }
/* icon list (a list of facts the site already draws with icons, BRIEF §0f.3: one icon per fact): the IconBadge leads
   the item in place of the square, the text's first line centred on the badge, 16 from it. A lead + text item (the
   Sky panel's .article__li) keeps its two lines as one stack beside the badge */
.article-li--icon { display: flex; flex-direction: row; align-items: flex-start; gap: var(--s-16); }
.article-li--icon > :not(.icon-badge) { flex: 1 1 0; min-width: 0; padding-top: calc((48px - var(--body-lh)) / 2); }
.article__li-b { display: flex; flex-direction: column; gap: var(--s-4); }
.article-panel .icon-badge { background: var(--paper); }   /* on the Sky card, as on every Sky panel */
/* phones: on the Sky card the badge heads its item and the words take the card's full measure (beside it they set
   in about 200 px, 24 characters a line) */
@media (max-width: 767px) {
  .article-panel .article-li--icon { flex-direction: column; gap: var(--s-12); }
  .article-panel .article-li--icon > :not(.icon-badge) { flex: none; width: 100%; padding-top: 0; }
}
/* pull quote: the source paragraph itself (one copy string): the band over it, the lead-in in Archivo at Lead size, the
   quotation (<q>; its curly marks are the copy's, so the element draws none) in Newsreader at Quote size */
.article-pq { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-24); width: 100%; padding: var(--s-24) 0; }
.article-pq__p { color: var(--ink); line-height: var(--lead-lh); }
.article-pq__by { font-family: var(--sans); font-weight: 400; font-size: var(--lead-fs); line-height: var(--lead-lh); letter-spacing: 0; color: var(--ink-2); }
.article-pq__q { line-height: var(--quote-lh); }
.article-pq__q::before, .article-pq__q::after { content: none; }
/* Sky panel: the list that carries the argument (Sky = Roger's side, as the CompareTable's With Roger column) */
.article-panel { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-24); width: 100%; padding: var(--s-32); background: var(--sky); }
.article-panel .article__li-x { color: var(--ink-2); }
@media (max-width: 767px) { .article-panel { padding: var(--s-24) var(--s-16); } }
/* sign-off: the names set like a signature in Newsreader, 48 under the last paragraph (space, not a hairline: 0e.5) */
.article-signoff { width: 100%; padding-top: var(--s-24); font-family: var(--serif); font-weight: 500;
  font-size: var(--h3-fs); line-height: var(--h3-lh); color: var(--ink); }

/* ---------- InsightCard parts (· Featured, · Row, · Wide): the image, the tag. Not a box: the image and a rule carry it ---------- */
.insight__img { width: 100%; aspect-ratio: 1 / 1; overflow: hidden; background: var(--paper-3); }
.insight__tag { color: var(--blue); }

/* One target per insight: the title link's ::after covers its whole card or row (the text cell is static, so the
   card/row is the containing block). "Read more" is the drawn cue (aria-hidden): one tab stop, one name. */
.insight-hit { position: relative; }
.insight-hit .insight-text { position: static; }
.insight-tlink { color: inherit; text-decoration-line: underline; text-decoration-color: transparent; text-decoration-thickness: 1px;
  text-underline-offset: 6px; transition: text-decoration-color var(--t-hover); }
.insight-tlink::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.insight-tlink:focus-visible { outline: none; }
.insight-tlink:focus-visible::after { outline: 2px solid var(--focus); outline-offset: 2px; }
.insight-hit:hover .insight-tlink { text-decoration-color: currentColor; }
/* hover (BRIEF §0f.4): the title underline fades in, Read more's rule grows and its chevron steps (base.css), and the
   photo zooms 1.02 inside its own square (components.css · Card) */

/* ---------- ItemList/Item · Route (/agents): one sitemap route, rendered by build.mjs from src/site.json ---------- */
.route-item .items__text { max-width: 640px; }
/* the URL leads each entry, over its title (order only: the build's markup stays title, description, URL); it breaks
   only between path segments (build.mjs urlHtml: <wbr> after each '/', each segment held whole), never inside a slug */
.route-item > [data-name="URL"] { order: -1; overflow-wrap: normal; }
/* Each title is a link (Roger Blue, BRIEF §0) with a resting Sky-line underline, so it reads as a link on touch too. */
.route-item h2 a { display: inline-flex; align-items: center; min-height: var(--tap); color: var(--blue);
  text-decoration-line: underline; text-decoration-color: var(--sky-line); text-decoration-thickness: 1px; text-underline-offset: 6px;
  transition: text-decoration-color var(--t-hover); }
.route-item h2 a:hover { text-decoration-color: var(--blue); }
/* The hero shows the page date once (ui `updated`, “Oct 1, 2026”); each row keeps its own route's date in the HTML
   (<time datetime>, same format) for crawlers and assistants, not repeated twelve times on screen. */
.route-item > [data-name="Updated"] { display: none; }
/* a table row: rows are ruled (rules carry meaning in a table, BRIEF 0e.5), in the paper-2 rule colour of the sheet
   the index sits on (convert-utility.css · agents__sheet; the styleguide shows it on its paper-2 section) */
.route-item + .route-item { border-top: 1px solid var(--rule-2); }
@media (min-width: 1024px) {
  /* one row per route: the URL over the title (as a search result reads), title | description beside it. Flex wrap,
     no grid: the URL line takes the whole first line of the row, the title and description share the second. */
  .route-item { flex-direction: row; flex-wrap: wrap; align-items: flex-start; gap: var(--s-8) 0; padding: var(--cell-pad) 0; }
  /* the URL line keeps to the Page column (the table's column line is bay line 6, half the row): a long URL wraps
     there instead of stopping beside the line */
  .route-item > [data-name="URL"] { flex: 0 0 100%; padding: 0 calc(50% + var(--cell-pad)) 0 var(--cell-pad); }
  .route-item > h2 { flex: 0 0 calc((100% + var(--gutter)) * 6 / 12 - var(--gutter) / 2); padding: 0 var(--cell-pad); }   /* title | description on bay line 6 */
  .route-item > .items__text { flex: 1 1 0; min-width: 0; padding: 0 var(--cell-pad) 0 calc(var(--cell-pad) + 1px); }
}

/* ---------- InsightCard · Featured / · Row (/insights): the Card component (below) composed by pages/insights.css ---------- */

/* ---------- InsightCard · Wide (/about): one opaque card on the columns, Split 2+4 (BRIEF §0f.2): the square image on
   columns 1-2 (edges on the margin line and line 2), on a paper plate across columns 3-6 the /insights card's four
   levels (R2-F12): tag, title, excerpt, then the Read more cue 32 under them, one group on the plate's foot, so the
   cue sits on the image's bottom line and the words fill the plate up from it (Oct 8 round 3: tag + title at the top
   and the cue at the foot left the plate's right half and middle blank). It opens /about's paper-2 band, so the
   plate is paper (a card on the band, as the team cards under it, R2-F13: paper-2 once). The whole card is the title
   link's target (.insight-hit), so hover grows the image 1.02 inside its frame and draws the cue. Below 1024 the
   image is the full-width square over the plate. ---------- */
.insight-wide__media { align-self: flex-start; }
.insight-wide__text { justify-content: flex-end; gap: var(--s-32); padding-top: var(--s-48); padding-bottom: var(--s-48); background: var(--paper); }
.insight-wide__t { max-width: 640px; }
.insight-wide__x { max-width: 640px; }
@media (min-width: 1024px) { .insight-wide__t { max-width: none; } }
/* the four levels fit the square from 1280 with a 32 inset (48 from 1440, where the square is 428). 1024-1279 the
   square is only 289-374 px, so the excerpt rests there (the tag, title and cue still fill the plate): the plate is
   never taller than the image beside it (a paper-2 sliver under the photo would read as an error) */
@media (min-width: 1024px) and (max-width: 1439px) { .insight-wide__text { padding-top: var(--s-32); padding-bottom: var(--s-32); } }
@media (min-width: 1024px) and (max-width: 1279px) { .insight-wide__x { display: none; } }
@media (max-width: 1023px) {
  .insight-wide__media { align-self: stretch; }
  .insight-wide__text { justify-content: flex-start; padding-top: var(--s-32); padding-bottom: var(--s-32); }
}
@media (max-width: 767px) { .insight-wide__text { gap: var(--s-24); padding-top: var(--s-24); padding-bottom: var(--s-24); } }

/* ---------- Card (BRIEF §0f.2 Cards 2+2+2 / 3+3, §0f.4 hover): an opaque surface on whole grid columns (layout.css ·
   Cols in its cards mode, or a Rail), so the inner lines run down the 24 px gutters between cards; its text sits
   --cell-pad in (32 / 24 / 16), so the first card's text keeps the page's text edge. A filled
   surface, not an outline box (BRIEF §0e.5): paper-2 on paper, paper on paper-2 / Sky / Sky-2, with an edge in its own
   fill that turns Roger Blue on hover (150 ms) while the media inside zooms 1.02 within its frame (240 ms, enter
   curve). One target per card: the title link's ::after (.card__link) covers the card, so the card is one tab stop
   and one name; any Link/Chevron inside draws its hover with the card's. Parts:
     .card__media  the frame: a 1:1 photo (.photo-sq) or a media stage; overflow clipped, so the zoom stays inside
     .card__body   meta row, title, excerpt, link (<= 4 text levels, R2-F12), --card-pad in, the link at the foot
     .card__link   the title's link; .card--hover = the drawn Hover state (/styleguide) */
.card { --card-bg: var(--paper-2); --card-pad: var(--cell-pad); position: relative; display: flex; flex-direction: column; align-items: stretch;
  width: 100%; height: 100%; background: var(--card-bg); border: 1px solid transparent; color: var(--ink);
  transition: border-color var(--t-hover); }
/* the resting edge is transparent over the card's own fill (it paints under the border): the same look as an edge in
   the fill colour, but not a drawn rule */
:is(.section--paper-2, .section--sky, .section--sky-2, .panel--sky, .panel--sky-2) .card { --card-bg: var(--paper); }
.card__media { position: relative; display: flex; flex-direction: column; overflow: hidden; background: var(--paper-3); }
.card__media :is(img, video) { transition: scale var(--dur-enter) var(--ease-out); }
/* --card-pad inside the 1 px edge, so text keeps the full clearance from the Roger Blue hover edge too (C6: the edge is
   a drawn rule while hovered); the text then sits 1 px further in than a plain cell's, which no eye can see */
.card__body { display: flex; flex-direction: column; align-items: flex-start; flex: 1 1 auto; gap: var(--s-16); padding: var(--card-pad); }
.card__foot { display: flex; flex-direction: column; align-items: flex-start; flex: 1 1 auto; justify-content: flex-end; }
/* the title link is a block of at least --tap, so a one-line title is still a 44 px target on its own (the ::after
   covering the card is the real target) */
.card__link { display: flex; align-items: center; min-height: var(--tap); color: inherit; text-decoration-line: underline; text-decoration-color: transparent; text-decoration-thickness: 1px;
  text-underline-offset: 6px; transition: text-decoration-color var(--t-hover); }
.card__link::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.card__link:focus-visible { outline: none; }
.card__link:focus-visible::after { outline: 2px solid var(--focus); outline-offset: 2px; }
@media (hover: hover) {
  .card:hover { border-color: var(--blue); }
  .card:hover .card__link { text-decoration-color: currentColor; }
}
/* the media zoom is motion: only with a hovering pointer and no reduced-motion request (the edge above is for all) */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  :is(.card, .insight-hit):hover :is(.card__media, .insight__img, .insight-wide__media) :is(img, video) { scale: 1.02; }
}
.card:active { border-color: var(--blue-press); }
.card--hover { border-color: var(--blue); }
.card--hover .card__media video { scale: 1.02; }
/* the drawn hover's 2 % zoom as geometry, not scale (Figma auto-layout cannot hold a transform): the photo set 1 % past
   each edge of its clipped frame, where scale(1.02) about its centre puts it. Out of the flow the photo no longer sizes
   its square, so the square takes the card's width, the width the loaded photo gave it */
.card--hover .card__media.photo-sq { align-self: stretch; }
.card--hover .card__media .photo { position: relative; }
.card--hover .card__media .photo img { position: absolute; left: -1%; top: -1%; width: 102%; height: 102%; max-width: none; }
.insight-hit :is(.insight__img, .insight-wide__media) img { transition: scale var(--dur-enter) var(--ease-out); }

/* ---------- Rail ([data-rail], BRIEF §0f.4 "scrolling case study carousels"): a horizontal row of cards that pages
   through on the column grid. The track is the container's width (margin line to margin line) and a native scroller
   with scroll-snap on every card, so touch gets momentum and every rest position is on the grid: the cards of
   layout.css · Cols in its cards mode (every card the same width, on whole grid columns, the inner lines down the
   gutters). Cards per view: desktop 3 (default, 2+2+2) or .rail--d2 (3+3); tablet 2 (default) or .rail--t1; phones
   one. The nav (prev / next, 48 px squares under the first card's edge) shows only with JS and only when there is
   more than one page; at either end its button dims (it cannot go further). Behaviour + markup: site.js · Rail.
   Each item holds a .card (components.css · Card). ---------- */
.rail { display: flex; flex-direction: column; align-items: center; gap: var(--s-32); width: 100%; }
/* the track is centred by the rail itself (its overflow half a gutter each side), every other part runs full width and
   sets its own content at the start: the same boxes as a per-child align-self, which Figma auto-layout cannot hold */
.rail > :not(.rail__track) { align-self: stretch; }
/* the track runs half a gutter past both margin lines (centred overflow, no negative margin), so every item is the same
   width, one grid period (2 columns + a gutter at desktop): each card sits on whole grid columns with a gutter either
   side, the outer cards' edges land on the margin lines at every snap position, and the inner lines run down the
   gutters. Cards snap one at a time (touch, trackpad, drag); the buttons and Left / Right move a page. */
.rail__track { display: flex; flex-direction: row; align-items: stretch; align-self: center; width: calc(100% + var(--gutter));
  overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; }
.rail__track > * { flex: 0 0 calc(100% / 3); min-width: 0; padding: 0 calc(var(--gutter) / 2); scroll-snap-align: start; }
@media (min-width: 1024px) { .rail--d2 .rail__track > * { flex-basis: 50%; } }
@media (min-width: 768px) and (max-width: 1023px) {
  .rail .rail__track > * { flex-basis: 50%; }
  .rail--t1 .rail__track > * { flex-basis: 100%; }
}
@media (max-width: 767px) { .rail .rail__track > * { flex-basis: 100%; } }
.js [data-rail][data-ready] .rail__track { scrollbar-width: none; }
.js [data-rail][data-ready] .rail__track::-webkit-scrollbar { display: none; }
[data-rail][data-dragging] .rail__track { scroll-snap-type: none; cursor: grabbing; user-select: none; }
.rail__nav { display: none; flex-direction: row; align-items: center; gap: var(--s-16); }
.js [data-rail][data-ready]:not([data-rail-static]) .rail__nav { display: flex; }
.rail__btn { display: flex; flex: none; align-items: center; justify-content: center; width: var(--control-h); height: var(--control-h);
  border: 1px solid var(--blue); background: var(--paper); color: var(--blue); transition: background-color var(--t-hover), opacity var(--t-hover); }
.rail__btn svg { flex: none; width: 8px; height: 14px; fill: currentColor; transition: translate var(--t-hover); }
@media (hover: hover) {
  .rail__btn:not(:disabled):hover { background: var(--sky); }
  .rail__btn[data-rail-next]:not(:disabled):hover svg { translate: var(--nudge) 0; }
  .rail__btn[data-rail-prev]:not(:disabled):hover svg { translate: calc(var(--nudge) * -1) 0; }
}
.rail__btn:not(:disabled):active { background: var(--sky); border-color: var(--blue-press); color: var(--blue-press); }
.rail__btn:disabled { opacity: 0.4; cursor: default; }
.on-blue .rail__btn { border-color: var(--on-blue); background: var(--blue); color: var(--on-blue); }
@media (hover: hover) { .on-blue .rail__btn:not(:disabled):hover { background: var(--blue-hover); } }

/* ---------- TagFilter ---------- */
.tags { display: flex; flex-wrap: wrap; gap: var(--s-8); }
/* a tag is a filled chip, not an outline box (BRIEF 0e.5): paper-2 at rest; hover a paler step (Sky-2; paper-3 on a
   paper-2 band) with no edge; the pressed tag is Sky with a 2 px Roger Blue foot rule and a semibold label, so the state
   never rests on colour alone (F7). No black slab: the first screen's one dark solid is Hire Roger (F9). The foot rule
   is always there (in the fill colour at rest), so pressing never shifts a tag. */
.tag { display: inline-flex; align-items: center; min-height: var(--tap); padding: 0 16px; border: 0; border-bottom: 2px solid var(--tag-bg, var(--paper-2));
  font-family: var(--sans); font-weight: 500; font-size: 16px; line-height: 22px; color: var(--ink); background: var(--tag-bg, var(--paper-2));
  transition: background-color var(--t-hover), border-color var(--t-hover); }
.section--paper-2 .tag { --tag-hover: var(--paper-3); }
@media (hover: hover) { .tag:hover { --tag-bg: var(--tag-hover, var(--sky-2)); } }
.tag--hover { --tag-bg: var(--tag-hover, var(--sky-2)); }   /* the drawn Tag · Hover master */
.tag:active { border-bottom-color: var(--blue); }
.tag[aria-pressed="true"], .tag--active { --tag-bg: var(--sky); border-bottom-color: var(--blue); font-weight: 600; }
@media (max-width: 403px) { .tags > .tag { flex: 0 0 calc((100% - var(--s-8)) / 2); justify-content: center; } }

/* ---------- TeamGrid (BRIEF §0f.3 team portraits): one editorial set. Each member (TeamGrid/Member) is a paper card
   on the page's paper-2 band, the WSJ-style stipple portrait flush on top (its dots are drawn on --paper, so plate and
   card are one surface), the name and role --cell-pad in under it. Every card is exactly one column of the six wide
   (the 12-col grid's two columns: 196 at 1440), the cards one gutter apart both ways, so the column lines run down the
   middle of every gutter and never behind a face or a name; every name in a row starts on the same line. Six across at
   desktop, four from 600 (the phone's centre line then runs down the middle gutter), two on small phones. Fixed bases with 0.05 px slack, no grow: a Figma wrap row cannot FILL and
   rounding must never wrap a row early. A member without a photo keeps the same card with a Sky-2 square and the
   Newsreader initial (the monogram), so the set never changes shape when a portrait arrives.
    .team__trust (/about) closes the short last row with the TrustStrip on a Sky tile: for 15 members it spans the
    three remaining desktop columns and the one remaining tablet column; on phones it takes a full row. ---------- */
.team { --team-n: 6; --team-span: 3; --team-card: calc((100% - (var(--team-n) - 1) * var(--gutter)) / var(--team-n));
  display: flex; flex-wrap: wrap; gap: var(--gutter); width: 100%; }
.team__m { display: flex; flex-direction: column; align-items: stretch; flex: 0 0 calc(var(--team-card) - 0.05px); min-width: 0; background: var(--paper); }
.team__tile { display: flex; align-items: center; justify-content: center; align-self: stretch; flex: none; aspect-ratio: 1 / 1; background: var(--sky-2); }
.team__mono { font-family: var(--serif); font-weight: 600; font-size: var(--h2-fs); line-height: var(--h2-lh); color: var(--ink-2); }
.team__txt { display: flex; flex-direction: column; min-width: 0; padding: var(--s-16) var(--cell-pad) var(--cell-pad); }
.team__name { font-family: var(--sans); font-weight: 600; font-size: var(--body-fs); line-height: var(--body-lh); color: var(--ink); }
.team__role { color: var(--ink-3); font-size: 16px; line-height: 24px; }
/* the trust tile: the line and its body as one group on the tile's middle (gap 24), so the Sky tile never holds a
   slab of air between a top line and a foot body */
.team__trust { display: flex; flex-direction: column; justify-content: center; gap: var(--s-24); min-width: 0; padding: var(--cell-pad);
  flex: 0 0 calc(var(--team-card) * var(--team-span) + (var(--team-span) - 1) * var(--gutter) - 0.05px); }
.team__trust-line { max-width: 640px; }
/* 600-1023 four across, and the trust tile closes the short last row on its two free columns, as at desktop (Oct 8
   round 3: on a row of its own it left two empty slots, 356 x 270 px of bare paper-2 at 768, over a lone Sky band).
   In that narrower tile the line takes the H3 role and the body the reading size, so it fits the members' height */
@media (min-width: 600px) and (max-width: 1023px) {
  .team { --team-n: 4; --team-span: 1; }
  .team__trust { gap: var(--s-12); padding: var(--s-24) var(--cell-pad); }
  .team__trust-line { font-size: var(--h3-fs); line-height: var(--h3-lh); letter-spacing: 0; }
  .team__trust .trust-split__body { font-size: var(--body-fs); line-height: var(--body-lh); }
}
@media (min-width: 600px) and (max-width: 699px) { .team__trust { gap: var(--s-8); padding-top: var(--s-16); padding-bottom: var(--s-16); } }
@media (max-width: 599px) { .team { --team-n: 2; --team-span: 2; } }
@media (max-width: 767px) { .team__txt { padding-top: var(--s-12); } }

/* ---------- Form fields ---------- */
.form { display: flex; flex-direction: column; gap: var(--s-24); width: 100%; max-width: 640px; }
.form > .btn { align-self: flex-start; }
/* label, control, error 8 apart as paddings (gap 0), so the error line is one opaque block with the control above it:
   a grid line behind the form passes behind both, never beside the error text (BRIEF 0d: opaque objects cover lines) */
.field { display: flex; flex-direction: column; gap: 0; width: 100%; }
.field__label { padding-bottom: var(--s-8); font-family: var(--sans); font-weight: 500; font-size: 16px; line-height: 24px; color: var(--ink); }
.field__hint { font-size: 16px; line-height: 24px; color: var(--ink-3); }
/* a filled field (BRIEF 0e.5: no outline box that reads as a placeholder): a paper-2 well with a rule-2 edge, its foot in
   ink-3 so the control keeps a 3:1 boundary (WCAG 1.4.11); hover inks the edge, focus adds the 2 px blue ring */
.input { display: block; width: 100%; height: var(--control-h); padding: 0 16px; background: var(--paper-2); border: 1px solid var(--rule-2);
  border-bottom-color: var(--ink-3); border-radius: 0; font-family: var(--sans); font-size: 16px; line-height: 24px; color: var(--ink); appearance: none; }
textarea.input { height: auto; min-height: 144px; padding: 12px 16px; resize: vertical; }
.select { position: relative; display: flex; width: 100%; }
.select .input { padding-right: 48px; }
.select > svg { position: absolute; right: 16px; top: 21px; width: 10px; height: 6px; fill: var(--ink); pointer-events: none; }
.input { transition: border-color var(--t-hover); }
.input:hover { border-color: var(--ink); }
.input:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.field__error { display: none; padding-top: var(--s-8); font-size: 16px; line-height: 24px; font-weight: 600; color: var(--ink); }
.field--error .input { border: 2px solid var(--ink); }
.field--error .field__error { display: block; }
/* label marker: the label, then “Required” (aria-hidden: the input's `required` already says it) or “Optional” in
   ink-3 at the field's right end (so it never stops beside a grid line in mid-field, BRIEF 0d), at least 12 apart,
   as two text layers; text, never colour alone */
.field__label--split { display: flex; flex-direction: row; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 0 var(--s-12); }
.field__opt { font-weight: 400; color: var(--ink-3); }
/* form alert (validation summary, send failure): a line of 600-weight ink under a 2 px ink rule */
.form__alert { display: flex; flex-direction: column; width: 100%; padding-top: var(--clear); border-top: 2px solid var(--ink); }
.form__alert-t { font-weight: 600; color: var(--ink); }
/* sent: the form gives way to the confirmation under a 2 px blue rule, then the next reads (role=status, focused) */
.form-sent { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-32); width: 100%; max-width: 640px;
  padding-top: var(--clear); border-top: 2px solid var(--blue); }
.form-sent__msg { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-16); }
/* the next reads: a row of next-step cards (/hire-roger's .sent-card, convert-utility.css) */
.form-sent__next { display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--s-24); width: 100%; }

/* ================================ Mock (Mock/Buyout, Mock/Submittals) ================================
   BRIEF §0e.4: the website shows the deliverable as a calm product mockup, not a drawn document: ONE paper card on a
   Sky-2 stage (equal padding on all four sides, no sheet pile), the wordmark with the kind + title directly under it,
   a short body of skeleton bars, and the real stamp as the hero detail. Buyout: header, a signed line (no field
   labels), the "We stake our fee on it." band; Submittals: the four dispositions, "Reviewed as noted" marked. Depth
   comes from surfaces (Sky-2 stage, paper card), not outlines (§0e.5).
   A scaled drawing: every size is N x --u, so the composition is identical wherever it is drawn. The stage takes its
   height from the card (nothing is cropped), and both cards share one geometry (two title lines reserved, equal stamp
   heights, same bar count), so a Buyout / Submittals pair lines up exactly.
   Two compositions on the mock's own width (container query), each keeping text >= 13 px (§0e.4):
   wide >= 560 px, drawn at 560 (stamp beside the title; u >= 1); narrow < 560, drawn at 1 u = 1 px (stamp under the
   title, capped at 288 so it stays a stamp, not a table). */
.mock { container-type: inline-size; display: flex; flex-direction: column; width: 100%; flex: none; }
.mock__stage { --u: calc(100cqw / 560); display: flex; flex-direction: column; width: 100%;
  padding: calc(24 * var(--u)); background: var(--sky-2); }
.mock__card { display: flex; flex-direction: column; gap: calc(32 * var(--u)); width: 100%;
  padding: calc(32 * var(--u)); background: var(--paper); color: var(--ink); }
.mock__top { display: flex; flex-direction: row; align-items: flex-start; gap: calc(16 * var(--u)); }
.mock__head { display: flex; flex-direction: column; align-items: flex-start; gap: calc(20 * var(--u)); flex: 1 1 0; min-width: 0; }
.mock__wm { display: block; flex: none; width: calc(104 * var(--u)); height: calc(21 * var(--u)); fill: var(--ink); }
/* kind + title right under the wordmark; two title lines are reserved so "Scopes of Work" (one line) and
   "Submittals / with Comments" (two) keep one card geometry */
.mock__tb { display: flex; flex-direction: column; gap: calc(8 * var(--u)); min-height: calc(88 * var(--u)); }
.mock__kind { font-family: var(--narrow); font-weight: 600; font-size: calc(13 * var(--u)); line-height: calc(16 * var(--u));
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-3); }
/* if the kind must wrap (a 320 phone), the middle dot travels with "cover sheet" */
.mock__nw { white-space: nowrap; }
.mock__title { font-family: var(--serif); font-weight: 600; font-size: calc(28 * var(--u)); line-height: calc(32 * var(--u));
  letter-spacing: -0.01em; white-space: nowrap; }
/* the stamp: the document-stamp family drawn in mock units; both inner frames are 192 x 148 u, so the pair aligns */
.mock .dstamp { --px: var(--u); }
.mock__stamp .dstamp__in { width: calc(192 * var(--u)); }
.mock--submittals .mock__stamp .dstamp__in { width: calc(256 * var(--u)); }
.mock__stamp .dstamp__lab, .mock__stamp .dstamp__opt-l { letter-spacing: 0.08em; }
.mock__stamp .dstamp__opt-l--on { letter-spacing: 0.04em; }
.mock__stamp .dstamp__band { height: calc(36 * var(--u)); }
/* Buyout: signed, not a blank form: one blue stroke resting on the entry line (32 + 80 + 36 = the Submittals 148) */
.mock__sign { display: flex; flex-direction: column; justify-content: flex-end; gap: calc(8 * var(--u));
  height: calc(80 * var(--u)); padding: 0 calc(12 * var(--u)) calc(16 * var(--u)); }
.mock__ink { display: block; flex: none; width: 56%; height: calc(4 * var(--u)); background: var(--blue); }
/* Submittals: the marked disposition is a Sky highlight row; the inner rule already closes the list (no 2 px foot) */
.mock__stamp .dstamp__list { gap: 0; padding: calc(10 * var(--u)) 0; border-bottom: 0; }
.mock__stamp .dstamp__opt { gap: calc(8 * var(--u)); height: calc(24 * var(--u)); padding: 0 calc(12 * var(--u)); }
.mock__stamp .mock__opt-on { background: var(--sky); }
/* the body: a heading bar, then the lines 24 under it (Submittals: each comment carries its blue disposition tick) */
.mock__body { display: flex; flex-direction: column; gap: calc(24 * var(--u)); }
.mock__lines { display: flex; flex-direction: column; gap: calc(12 * var(--u)); }
.mock__line { display: flex; align-items: center; gap: calc(12 * var(--u)); }
.mock__bar { display: block; height: calc(8 * var(--u)); background: var(--paper-3); }
.mock__bar--h { width: 40%; height: calc(12 * var(--u)); background: var(--sky-line); }
.mock__bar--a { width: 100%; } .mock__bar--b { width: 84%; } .mock__bar--c { width: 60%; }
.mock__tick { display: block; flex: none; width: calc(8 * var(--u)); height: calc(8 * var(--u)); background: var(--blue); }
@container (max-width: 559px) {
  .mock__stage { --u: 1px; padding: calc(16 * var(--u)); }
  .mock__card { gap: calc(24 * var(--u)); padding: calc(24 * var(--u)); }
  .mock__top { flex-direction: column; align-items: flex-start; gap: calc(24 * var(--u)); }
  .mock__head { flex: none; gap: calc(16 * var(--u)); }
  .mock__wm { width: calc(88 * var(--u)); height: calc(18 * var(--u)); }
  .mock__tb { min-height: calc(80 * var(--u)); }
  .mock__title { font-size: calc(24 * var(--u)); line-height: calc(28 * var(--u)); }
  /* the stamp fills the card up to 288; both inner frames 28 + 108 u */
  .mock__stamp { align-self: stretch; max-width: calc(288 * var(--u)); }
  .mock__stamp .dstamp__in { flex: 1 1 0; width: auto; }
  .mock__stamp .dstamp__head { height: calc(28 * var(--u)); }
  .mock__stamp .dstamp__band { height: calc(32 * var(--u)); }
  .mock__sign { height: calc(76 * var(--u)); padding-bottom: calc(14 * var(--u)); }
  .mock__stamp .dstamp__opt { height: calc(22 * var(--u)); }
  .mock__body { gap: calc(16 * var(--u)); }
  .mock__lines { gap: calc(8 * var(--u)); }
  /* whole lines only: the narrow card keeps the heading and two lines */
  .mock__line--c { display: none; }
}
/* < 300 px (a 320 phone): tighter stage and card so the stamp's words still fit at 13 px */
@container (max-width: 299px) {
  .mock__stage { padding: calc(12 * var(--u)); }
  .mock__card { padding: calc(16 * var(--u)); }
  /* the kind may take two lines here: both cards reserve them */
  .mock__tb { min-height: calc(96 * var(--u)); }
  .mock__stamp .dstamp__head, .mock__stamp .dstamp__band, .mock__stamp .dstamp__opt, .mock__sign { padding-left: calc(8 * var(--u)); padding-right: calc(8 * var(--u)); }
  .mock__stamp .dstamp__lab, .mock__stamp .dstamp__opt-l, .mock__kind { letter-spacing: 0.06em; }
  .mock__stamp .dstamp__band-l, .mock__stamp .dstamp__opt-l--on { letter-spacing: 0.02em; }
}
