/* ============================================================
   Ink Fusion v28 — components
   ============================================================ */

/* ---- corner-bracket button (component 1) ---- */
.btn {
  position: relative; font-family: var(--f-body); font-weight: 700; font-size: 15px;
  padding: 16px 30px; border: 2.5px solid var(--ink); display: inline-flex; align-items: center; gap: 10px;
  cursor: pointer; background: var(--paper); color: var(--ink);
  /* Was `transition: 0.15s` - the `all` shorthand, so the hover background and
     colour swap FADED in, which reads as loading rather than pressing. Scoped
     to the two properties that actually move, on the token layer. */
  transition: transform var(--dur-state) var(--ease-state), box-shadow var(--dur-state) var(--ease-state);
}
.btn.fill { background: var(--cyan); box-shadow: 6px 6px 0 var(--ink); }
/* --mag-on-paper, not --mag: the label is 15px bold, which WCAG counts as NORMAL
   text and requires 4.5:1. The brand magenta gives 3.45:1 on paper and 4.44:1 on
   ink, so neither label colour rescues it and the background itself had to move.
   See tokens.css. This is the primary call to action on every page. */
/* ── HOVER / PRESS / FOCUS ──────────────────────────────────────────────────
   Every affordance on the marketing pages was :hover-only — measured, .btn had
   ten hover rules and zero :active and zero :focus-visible. On a phone that is
   no acknowledgement at all between touch-down and navigation, and on a keyboard
   it is no visible focus.

   Hover is gated on a FINE POINTER because on touch :hover latches after a tap
   and stays until the next tap elsewhere, leaving a button looking pressed.
   :active is the deeper press of the SAME gesture, not a new idiom — the
   pattern already ships at pages.css .preset:active and account.css .acct-btn. */
@media (pointer: fine) {
  .btn.fill:hover { transform: translate(3px, 3px); box-shadow: 3px 3px 0 var(--ink); background: var(--mag-on-paper); color: var(--paper); }
}
.btn.fill:active { transform: translate(5px, 5px); box-shadow: 1px 1px 0 var(--ink); }
.btn.ghost { box-shadow: 6px 6px 0 var(--yel); }
@media (pointer: fine) {
  .btn.ghost:hover { transform: translate(3px, 3px); box-shadow: 3px 3px 0 var(--yel); }
}
.btn.ghost:active { transform: translate(5px, 5px); box-shadow: 1px 1px 0 var(--yel); }
.btn:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }
.btn .cb { position: absolute; width: 11px; height: 11px; opacity: 0; transition: opacity var(--dur-state) var(--ease-state), transform var(--dur-swap) var(--ease-state); }
.btn .cb::before, .btn .cb::after { content: ""; position: absolute; background: currentColor; }
.btn .cb::before { width: 2px; height: 100%; }
.btn .cb::after { height: 2px; width: 100%; }
.btn .cb.tl { top: 5px; left: 5px; }
.btn .cb.tr { top: 5px; right: 5px; }
.btn .cb.tr::before { right: 0; } .btn .cb.tr::after { right: 0; }
.btn .cb.bl { bottom: 5px; left: 5px; }
.btn .cb.bl::before { bottom: 0; } .btn .cb.bl::after { bottom: 0; }
.btn .cb.br { bottom: 5px; right: 5px; }
.btn .cb.br::before { bottom: 0; right: 0; } .btn .cb.br::after { bottom: 0; right: 0; }
.btn:hover .cb { opacity: 1; }
.btn:hover .cb.tl { transform: translate(-2px, -2px); }
.btn:hover .cb.tr { transform: translate(2px, -2px); }
.btn:hover .cb.bl { transform: translate(-2px, 2px); }
.btn:hover .cb.br { transform: translate(2px, 2px); }

/* ---- print-pass preloader (component 14) ---- */
/* Reveal is CSS-driven (auto-hide keyframe) so it does NOT depend on the JS module:
   on some mobile browsers the module's timer is deferred until first scroll, which
   left the overlay stuck. The bars are CSS animations that DO run, so a CSS auto-hide
   runs reliably too. The JS `.done` path still works as an enhancement (hard-removes). */
/*
   CAPPED AT 900 ms — 2026-08-10. It was 2.3s: the K bar finished at 1.65s and
   the CSS preReveal did not even start until 1.75s.

   The whole print-pass sequence is compressed rather than merely cut off, so it
   still reads as a completed pass: bars finish at 0.62s, the reveal runs
   0.62→0.90s. The cap is CSS-side because the CSS animation is what actually
   governs the timing here — the JS is an enhancement and the fallback below it.

   Why it matters more than it used to: this overlay costs nothing in front of a
   page with no imagery, but it is about to sit in front of the six workshop
   photographs, which are the only real evidence the site has. Do not spend
   2.3 seconds hiding the thing that earns the trust.

   The logo CMYK bars at layout.css:139 are timed to land as this lifts — the
   two are coupled and must move together. They did, in this commit. */
#pre { position: fixed; inset: 0; z-index: 10000; background: var(--paper); display: grid; place-items: center; transition: opacity 0.28s var(--ease), visibility 0.28s; animation: preReveal 0.28s var(--ease) 0.62s forwards; }
#pre.done { opacity: 0; visibility: hidden; animation: none; }
@keyframes preReveal { to { opacity: 0; visibility: hidden; pointer-events: none; } }
@media (prefers-reduced-motion: reduce) { #pre { animation: none; opacity: 0; visibility: hidden; } }
#pre .pw { font-family: var(--f-display); font-weight: 800; font-size: clamp(28px, 6vw, 64px); text-transform: uppercase; margin-bottom: 22px; letter-spacing: -0.02em; text-align: center; }
#pre .pw em { font-style: normal; color: var(--mag); }
#pre .bars { display: flex; width: min(62vw, 380px); height: 26px; border: 2px solid var(--ink); }
#pre .bars i { flex: 1; transform: scaleX(0); transform-origin: left; animation: pass 0.18s var(--ease) forwards; }
#pre .bars i:nth-child(1) { background: var(--cyan); animation-delay: 0.02s; }
#pre .bars i:nth-child(2) { background: var(--mag); animation-delay: 0.16s; }
#pre .bars i:nth-child(3) { background: var(--yel); animation-delay: 0.30s; }
#pre .bars i:nth-child(4) { background: var(--ink); animation-delay: 0.44s; } /* K ends 0.62s */
@keyframes pass { to { transform: scaleX(1); } }
#pre .lbl { font-family: var(--f-mono); font-size: 11px; letter-spacing: 0.2em; margin-top: 14px; color: #4a443a; }

/* ---- CMYK ink-wipe transition (component 8) ---- */
#wipe { position: fixed; inset: 0; z-index: 9990; display: flex; pointer-events: none; }
#wipe i { flex: 1; transform: scaleX(0); transform-origin: left; }
#wipe i:nth-child(1) { background: var(--cyan); }
#wipe i:nth-child(2) { background: var(--mag); }
#wipe i:nth-child(3) { background: var(--yel); }
#wipe i:nth-child(4) { background: var(--ink); }
#wipe.in i { animation: wipein 0.5s var(--ease) forwards; }
#wipe.out i { animation: wipeout 0.5s var(--ease) forwards; }
#wipe.in i:nth-child(2), #wipe.out i:nth-child(2) { animation-delay: 0.05s; }
#wipe.in i:nth-child(3), #wipe.out i:nth-child(3) { animation-delay: 0.1s; }
#wipe.in i:nth-child(4), #wipe.out i:nth-child(4) { animation-delay: 0.15s; }
@keyframes wipein { from { transform: scaleX(0); transform-origin: left; } to { transform: scaleX(1); transform-origin: left; } }
@keyframes wipeout { from { transform: scaleX(1); transform-origin: right; } to { transform: scaleX(0); transform-origin: right; } }

/* ---- CMYK channel-split heading (component 2) ---- */
.op {
  position: relative; display: inline-block; font-family: var(--f-display); font-weight: 800;
  text-transform: uppercase; line-height: 0.9; letter-spacing: -0.03em; font-size: clamp(38px, 8vw, 92px);
}
/* WAS `white-space: nowrap`, which silently CLIPPED the page H1 on 7 of 8 hero
   pages at every width from 561px up — `.page-hero`/`.hero` are `overflow: hidden`,
   so there is no scrollbar and no page-overflow signal, the words simply vanish.
   Measured on production 2026-09-18: /about/ lost 361-564px ("BY AUSTRALIAN P"),
   /artwork-guidelines/ 238-380px, /contact/ 72-120px, four more pages 2-12px.
   The escape hatch already existed in pages.css but was scoped to
   `@media (max-width: 560px)` — and the overflow depends on STRING LENGTH, not on
   viewport width, which is why mobile was clean and desktop was broken.
   The .c/.m/.y overprint layers are `position: absolute; inset: 0` and render
   `attr(data-t)` at the same width, so they track a wrapped line correctly —
   verified visually on /about/ at 958px before shipping. */
.op .ln { display: block; position: relative; white-space: normal; overflow-wrap: break-word; }
.op .ln .base { position: relative; z-index: 4; color: var(--ink); }
/*
 * The CMYK misregistration layers are DECORATION, and are drawn from a data
 * attribute rather than from real text.
 *
 * S2, 2026-08-27. Each layer used to carry its own copy of the heading, so a
 * three-word H1 put twelve words in the DOM: the homepage's crawlable H1 read
 * "Press-readyPress-readyPress-readyPress-ready DTFDTFDTFDTF transfers", and
 * /about/'s was one sentence repeated four times. That is what a search engine
 * indexed and what a screen reader announced.
 *
 * content: attr(data-t) keeps the effect pixel-identical while leaving exactly
 * one copy of the words in the document. `aria-hidden` on the spans covers the
 * screen readers that do announce generated content.
 *
 * The text must stay in an attribute rather than being hardcoded per layer:
 * the three layers and the real one have to say the same thing, and one source
 * is what makes that true instead of hoped for.
 */
.op .ln .c, .op .ln .m, .op .ln .y { position: absolute; inset: 0; pointer-events: none; mix-blend-mode: multiply; transition: transform 0.5s var(--ease); }
.op .ln .c::before, .op .ln .m::before, .op .ln .y::before { content: attr(data-t); }
.op .ln .c { color: var(--cyan); z-index: 1; transform: translate(var(--cx, -18px), var(--cy, -14px)); }
.op .ln .m { color: var(--mag); z-index: 2; transform: translate(var(--mx, 18px), var(--my, 14px)); }
.op .ln .y { color: var(--yel); z-index: 3; transform: translate(var(--yx, 14px), var(--yy, -14px)); }
.op.reg { --cx: -5px; --cy: -4px; --mx: 5px; --my: 4px; --yx: 4px; --yy: -4px; }
.op:hover { --cx: -11px; --cy: -9px; --mx: 11px; --my: 9px; --yx: 9px; --yy: -9px; }
/* channel-split on smaller section headings */
h2.split { position: relative; }
h2.split .c, h2.split .m, h2.split .y { position: absolute; inset: 0; pointer-events: none; mix-blend-mode: multiply; transition: transform 0.5s var(--ease); }
h2.split .c { color: var(--cyan); transform: translate(var(--cx, -4px), var(--cy, -3px)); }
h2.split .m { color: var(--mag); transform: translate(var(--mx, 4px), var(--my, 3px)); }
h2.split .y { color: var(--yel); transform: translate(var(--yx, 3px), var(--yy, -3px)); }
h2.split .base { position: relative; z-index: 4; }

/* ---- morph flip word (component 3) ---- */
.flipwrap { display: inline-block; position: relative; color: var(--mag); min-width: 5ch; }

/* ---- animated SVG registration target (component 12) ---- */
.regtarget { position: absolute; top: 64px; right: 52px; width: 74px; height: 74px; z-index: 2; }
.regtarget svg { width: 100%; height: 100%; }
.regtarget .spin { transform-origin: 50% 50%; animation: spin 9s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (max-width: 860px) { .regtarget { display: none; } }

/* ---- film card ---- */
.filmcard { position: relative; border: 2.5px solid var(--ink); background: var(--paper-2); aspect-ratio: 1 / 1.12; overflow: hidden; box-shadow: 10px 10px 0 var(--mag); }
.filmcard .halft {
  position: absolute; inset: 0; background-image: radial-gradient(var(--cyan) 30%, transparent 32%);
  background-size: 12px 12px; opacity: 0.9; mix-blend-mode: multiply;
  -webkit-mask-image: radial-gradient(circle at 50% 42%, #000, transparent 78%);
  mask-image: radial-gradient(circle at 50% 42%, #000, transparent 78%);
}
.filmcard .halft.m { background-image: radial-gradient(var(--mag) 26%, transparent 30%); background-size: 12px 12px; background-position: 4px 4px; opacity: 0.55; }
.filmcard .cmykbar { position: absolute; top: 0; left: 0; right: 0; height: 16px; display: flex; }
.filmcard .cmykbar i { flex: 1; }
.filmcard .cmykbar i:nth-child(1) { background: var(--cyan); }
.filmcard .cmykbar i:nth-child(2) { background: var(--mag); }
.filmcard .cmykbar i:nth-child(3) { background: var(--yel); }
.filmcard .cmykbar i:nth-child(4) { background: var(--ink); }
.filmcard .lbl { position: absolute; inset: 0; display: grid; place-items: center; text-align: center; padding: 16%; }
/* The film-card wordmark is DECORATION, not document structure: it was an
   <h3> sitting between the page h1 and its first h2, which is the level skip
   axe reported on the home page. Retagged to <p class="filmcard-wordmark">;
   both selectors kept so the look is unchanged. */
.filmcard .lbl h3, .filmcard .lbl .filmcard-wordmark { font-family: var(--f-display); font-weight: 800; text-transform: uppercase; font-size: clamp(30px, 5.6vw, 60px); line-height: 0.86; letter-spacing: -0.02em; }
.filmcard .lbl h3 .a, .filmcard .lbl .filmcard-wordmark .a { color: var(--cyan); }
.filmcard .lbl h3 .b, .filmcard .lbl .filmcard-wordmark .b { color: var(--mag); }
.filmcard .lbl .tag { font-family: var(--f-mono); font-size: 11px; letter-spacing: 0.18em; margin-top: 14px; background: var(--ink); color: var(--paper); padding: 5px 10px; display: inline-block; }
.filmcard .cap { position: absolute; bottom: 10px; left: 0; right: 0; text-align: center; font-family: var(--f-mono); font-size: 10.5px; letter-spacing: 0.1em; color: #4a443a; }
.filmcard .crop { position: absolute; width: 20px; height: 20px; }
.filmcard .crop::before, .filmcard .crop::after { content: ""; position: absolute; background: var(--ink); }
.filmcard .crop::before { width: 1.5px; height: 100%; left: 0; }
.filmcard .crop::after { height: 1.5px; width: 100%; top: 0; }
.filmcard .crop.c1 { top: 24px; left: 14px; }
.filmcard .crop.c2 { top: 24px; right: 14px; transform: scaleX(-1); }
.filmcard .crop.c3 { bottom: 34px; left: 14px; transform: scaleY(-1); }
.filmcard .crop.c4 { bottom: 34px; right: 14px; transform: scale(-1); }

/* ---- calibration bar ---- */
.calib { display: flex; border-block: 2.5px solid var(--ink); }
.calib i { flex: 1; height: 34px; display: flex; align-items: center; justify-content: center; font-family: var(--f-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.2em; }
.calib i:nth-child(1) { background: var(--cyan); color: var(--ink); }
.calib i:nth-child(2) { background: var(--mag-ground); color: var(--paper); }
.calib i:nth-child(3) { background: var(--yel); color: var(--ink); }
.calib i:nth-child(4) { background: var(--ink); color: var(--paper); }
.calib i:nth-child(5) { background: var(--paper); color: var(--ink); }

/* ---- colour-invert marquee (component 6) ---- */
.marq { background: var(--ink); color: var(--yel); overflow: hidden; padding: 16px 0; border-bottom: 2.5px solid var(--ink); position: relative; }
/* Pause control (WCAG 2.2.2 — moving content lasting more than 5s needs a way to
   stop it). Sits in the corner of the band rather than above it so it costs no
   vertical rhythm; 44px hit area, and it reads on the ink ground. */
.marq__pause { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); z-index: 2;
  min-width: 44px; min-height: 44px; padding: 0 12px; cursor: pointer;
  background: var(--paper); color: var(--ink); border: 2px solid var(--paper);
  font-family: var(--f-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; }
.marq__pause:focus-visible { outline: 3px solid var(--yel); outline-offset: 2px; }
.marq.is-paused .t { animation-play-state: paused; }
.marq .t { display: flex; white-space: nowrap; font-family: var(--f-display); font-weight: 800; text-transform: uppercase; font-size: clamp(22px, 3vw, 34px); letter-spacing: -0.01em; animation: scroll 26s linear infinite; }
.marq:hover .t { animation-play-state: paused; }
.marq .t span { padding: 0 26px; display: inline-flex; align-items: center; gap: 26px; transition: background-color var(--dur-state) var(--ease-state), color var(--dur-state) var(--ease-state); }
.marq .t span::after { content: "\25C6"; color: var(--cyan); }
@media (pointer: fine) { .marq .t span:hover { background: var(--cyan); color: var(--ink); } }
@keyframes scroll { to { transform: translateX(-50%); } }

/* ---- modes: spot-colour brutalist cards ---- */
.modes-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px; }
/* ── 2026-07-14 mobile audit: 3-col card grid never collapsed → stack ≤640px;
   min-width:0 lets the cards shrink instead of blowing the track past the viewport.
   Once narrowed, big display headings must wrap (break-word) and the .pcard h3, .pcard h2
   drops a step so a long word can't overflow the card and re-bleed the page. ── */
.modes-grid > *, .bento > *, .statgrid > * { min-width: 0; }
h1, h2, h3, h4, .pcard h3, .pcard h2 { overflow-wrap: break-word; }
/* Display headings are display:inline-block (for the underline effect) so they
   size to content and won't wrap — cap them so they wrap within the container. */
.hd h2, .cta h2, .op, .hero h1, .trade h3 { max-width: 100%; }
/* 3 → 2 columns at 1024px — 2026-08-10.
   The grid held three columns all the way down to 640px. MEASURED at a 958px
   viewport: each card was 265px, leaving a 205px line, while "Custom" at the
   clamp(26px, 3vw, 36px) size needs 216px — it overflowed by 11px, so the
   break-word safety net above fired MID-WORD and the card read "Custo / m DTF"
   on /pricing/ and the 404.
   Fixing the container, not the type: break-word stays the last-resort guard it
   was added to be instead of becoming load-bearing.
   1024 not 900, because 900 still leaves the 901–1024 band broken. Checked the
   worst case just above the boundary: at 1025px three columns give a 255px line
   and "Custom" needs 231px — it fits, and gets easier as the viewport grows.
   Must precede the 640px rule below, which takes it to a single column. */
@media (max-width: 1024px) { .modes-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .modes-grid { grid-template-columns: 1fr; } }
/* Stat numbers are 40px — too wide for 2 narrow cols on a phone; stack them. */
@media (max-width: 560px) { .pcard h3, .pcard h2 { font-size: 22px; } .statgrid { grid-template-columns: 1fr; } }
/* Drop the biggest display floors a step so a single word can't exceed a 280px screen. */
@media (max-width: 400px) {
  .hd h2 { font-size: clamp(22px, 8vw, 34px); }
  .cta h2 { font-size: clamp(28px, 11vw, 60px); }
  .trade h3 { font-size: clamp(18px, 6vw, 24px); }
}
.pcard { position: relative; border: 2.5px solid var(--ink); padding: 28px; min-height: 340px; display: flex; flex-direction: column; transition: transform var(--dur-state) var(--ease-state), box-shadow var(--dur-state) var(--ease-state); }
.pcard.c1 { background: var(--cyan); box-shadow: 8px 8px 0 var(--ink); }
.pcard.c2 { background: var(--mag-ground); color: var(--paper); box-shadow: 8px 8px 0 var(--ink); }
.pcard.c3 { background: var(--yel); box-shadow: 8px 8px 0 var(--ink); }
@media (pointer: fine) {
  .pcard:hover { transform: translate(-3px, -3px); box-shadow: 12px 12px 0 var(--ink); }
}
/* focus-within, not focus: the card is not focusable, its link inside is. */
.pcard:focus-within { transform: translate(-3px, -3px); box-shadow: 12px 12px 0 var(--ink); }
.pcard a:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }
.pcard .kv { font-family: var(--f-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; }
.pcard .num { font-family: var(--f-display); font-weight: 800; font-size: 16px; margin-left: auto; border: 2px solid currentColor; width: 34px; height: 34px; display: grid; place-items: center; }
.pcard .top { display: flex; align-items: center; }
.pcard h3, .pcard h2 { font-family: var(--f-display); font-weight: 800; text-transform: uppercase; font-size: clamp(26px, 3vw, 36px); line-height: 0.92; margin: 22px 0 12px; letter-spacing: -0.02em; }
.pcard p { font-size: 14.5px; margin-bottom: 20px; }
.pcard.c2 p { color: #f6e9f0; }
.pcard .go { margin-top: auto; font-family: var(--f-mono); font-weight: 700; font-size: 13px; letter-spacing: 0.06em; display: inline-flex; gap: 8px; align-items: center; transition: gap var(--dur-swap) var(--ease-state); }
@media (pointer: fine) { .pcard:hover .go { gap: 14px; } }
.pcard:focus-within .go { gap: 14px; }
.pcard .price { font-family: var(--f-display); font-weight: 800; font-size: 40px; margin-bottom: 6px; }
@media (max-width: 860px) { .modes-grid { grid-template-columns: 1fr; } }
.trade { margin-top: 26px; border: 2.5px solid var(--ink); background: var(--ink); color: var(--paper); padding: 26px 30px; display: flex; align-items: center; justify-content: space-between; gap: 20px; box-shadow: 8px 8px 0 var(--orange); flex-wrap: wrap; }
/* `color` is NOT inherited here: base.css sets an explicit `color: var(--ink)` on
   every h1-h6, which outranks the `color: var(--paper)` on .trade's dark ground.
   Measured on production 2026-09-18: ink on ink, 1.00:1, 32px/800 — "Trade
   accounts -" and ", every path" rendered INVISIBLE, leaving only the yellow <em>
   so the band read as a disjointed "UP TO 30% OFF / THE PRINT".
   textContent returns the whole string, which is why every earlier pass missed it.
   ⚠ Any future inverted block needs the same explicit re-assertion. */
.trade h3 { font-family: var(--f-display); font-weight: 800; text-transform: uppercase; font-size: clamp(22px, 2.6vw, 32px); letter-spacing: -0.02em; color: var(--paper); }
.trade h3 em { font-style: normal; color: var(--yel); }
.trade .btn { background: var(--yel); color: var(--ink); box-shadow: 5px 5px 0 var(--mag); }
.trade .btn:hover { transform: translate(2px, 2px); box-shadow: 3px 3px 0 var(--mag); }

/* ---- staggered bento (component 9) ---- */
.bento { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: minmax(124px, auto); gap: 16px; }
.bcell { position: relative; border: 2.5px solid var(--ink); padding: 20px; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; transition: transform var(--dur-state) var(--ease-state); }
.bcell:hover { transform: translate(-3px, -3px); }
.bcell .bk { font-family: var(--f-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; }
/* .bcell-val — the bento cards hold STAT VALUES ("24-48 hours", "Net-30",
   "Priority queue"), each already labelled by its own .bk eyebrow. They were
   <h3>/<h4>, which put 18 non-structural strings into the document outline and
   caused most of the remaining heading-order violations. Retagged to <p>;
   the heading selectors stay so nothing that still uses them loses its look. */
.bcell h4, .bcell h3, .bcell .bcell-val { font-family: var(--f-display); font-weight: 800; text-transform: uppercase; font-size: clamp(18px, 2vw, 26px); line-height: 0.94; letter-spacing: -0.01em; }
/* ── THE PROSE VARIANTS THE COMPONENT WAS MISSING ──────────────────────────
   Added 2026-09-18. The comment above states the contract: .bcell-val is for
   STAT VALUES. /about/ was putting 59-90 character SENTENCES through it, which
   at the measured 229px content box and ~25px caps is 16 characters per line -
   a 72-character sentence became 4.2 lines of all-caps display type. The tell
   was that two cells already carried inline font-size overrides doing this fix
   by hand, per instance.

   So this is not a taste question and needed no copy rewrite: the component had
   one slot and two jobs. Now it has both.

   .bcell-note  - body copy: sentence case, body face, readable line-height.
   .big--claim  - a STATEMENT that is still display type but longer than a stat:
                  the two "promise" cells. Replaces their inline overrides.

   MEASURED, so the limit is stated honestly: in a 4-column cell this yields
   ~22-26 characters per line over ~3 lines, against 16 characters of all-caps
   display before. It does NOT reach the 45-character comfortable floor and
   cannot at this grid width - a 42ch max-width was tried and removed because it
   resolved to 404px against a 379px text box and never engaged. Closing that
   last gap needs shorter copy or wider cells, which are content and layout
   decisions, not CSS ones. */
.bcell .bcell-note {
  font-family: var(--f-body); font-weight: 400; text-transform: none;
  font-size: 15px; line-height: 1.5; letter-spacing: 0;
}
.bcell .big.big--claim { font-size: clamp(20px, 2.3vw, 31px); line-height: 1.05; }

.bcell.b1 { grid-column: span 2; grid-row: span 2; background: var(--cyan); }
.bcell.b2 { grid-column: span 2; background: var(--paper-2); }
.bcell.b3 { background: var(--mag-ground); color: var(--paper); }
.bcell.b4 { background: var(--yel); }
.bcell.b5 { grid-column: span 2; background: var(--ink); color: var(--paper); }
.bcell.b6 { background: var(--orange); color: var(--ink); }
.bcell.b7 { background: var(--paper-2); }
.bcell .big { font-family: var(--f-display); font-weight: 800; font-size: clamp(30px, 4vw, 54px); line-height: 0.9; }
.bcell .ht { position: absolute; inset: 0; background-image: radial-gradient(rgba(21,19,16,0.2) 26%, transparent 30%); background-size: 9px 9px; opacity: 0.25; pointer-events: none; }
@media (max-width: 860px) { .bento { grid-template-columns: repeat(2, 1fr); } .bcell.b1, .bcell.b2, .bcell.b5 { grid-column: span 2; } }

/* ---- stats (component 7) ---- */
.statsec { background: var(--ink); color: var(--paper); }
.statgrid { display: grid; grid-template-columns: repeat(4, 1fr); }
.st { padding: 44px 26px; border-right: 2px solid rgba(239, 233, 219, 0.16); }
.st:last-child { border-right: 0; }
.st .n { font-family: var(--f-display); font-weight: 800; font-size: clamp(40px, 5vw, 66px); line-height: 1; font-variant-numeric: tabular-nums; }
.st:nth-child(1) .n { color: var(--cyan); }
.st:nth-child(2) .n { color: var(--mag); }
.st:nth-child(3) .n { color: var(--yel); }
.st:nth-child(4) .n { color: var(--orange); }
.st .n .u { font-size: 0.5em; }
.st .l { font-family: var(--f-mono); font-size: 12px; letter-spacing: 0.1em; margin-top: 12px; text-transform: uppercase; color: #c9c3b6; }
@media (max-width: 760px) { .statgrid { grid-template-columns: repeat(2, 1fr); } .st:nth-child(2) { border-right: 0; } }

/* ---- horizontal scroll-pin process (component 4) ---- */
.pinwrap { position: relative; height: 340vh; background: var(--paper-2); border-block: 2.5px solid var(--ink); }
.pinsticky { position: sticky; top: 0; height: 100vh; overflow: hidden; display: flex; align-items: center; }
.pintrack { display: flex; align-items: center; gap: 40px; padding: 0 48px; will-change: transform; }
.ppanel { flex: 0 0 auto; }
.ppanel.intro { width: min(80vw, 460px); }
.ppanel.intro h2 { font-family: var(--f-display); font-weight: 800; text-transform: uppercase; font-size: clamp(38px, 6vw, 72px); line-height: 0.92; letter-spacing: -0.02em; }
.ppanel.intro h2 em { font-style: normal; color: var(--mag); }
.ppanel.intro p { margin-top: 14px; color: #3a352d; max-width: 360px; }
.ppanel.intro .scrollnote { margin-top: 20px; font-family: var(--f-mono); font-size: 12px; letter-spacing: 0.14em; color: #4a443a; }
.pstep { width: min(74vw, 380px); border: 2.5px solid var(--ink); background: var(--paper); padding: 30px; box-shadow: 10px 10px 0 var(--ink); }
.pstep .chip { width: 64px; height: 64px; border: 2.5px solid var(--ink); display: grid; place-items: center; font-family: var(--f-display); font-weight: 800; font-size: 26px; }
.pstep:nth-of-type(2) .chip { background: var(--cyan); }
.pstep:nth-of-type(3) .chip { background: var(--mag-ground); color: var(--paper); }
.pstep:nth-of-type(4) .chip { background: var(--yel); }
.pstep:nth-of-type(5) .chip { background: var(--orange); color: var(--ink); }
.pstep:nth-of-type(6) .chip { background: var(--ink); color: var(--paper); }
.pstep .idx { font-family: var(--f-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.14em; margin: 18px 0 6px; }
.pstep h4, .pstep h3 { font-family: var(--f-display); font-weight: 800; text-transform: uppercase; font-size: 26px; margin-bottom: 10px; letter-spacing: -0.01em; }
.pstep p { font-size: 14.5px; color: #3a352d; }
.pinprog { position: absolute; left: 0; bottom: 0; height: 8px; width: 0; background: var(--cyan); z-index: 5; }
.pinwrap.nopin { height: auto; }
.pinwrap.nopin .pinsticky { position: static; height: auto; overflow-x: auto; padding: 60px 0; }
.pinwrap.nopin .pintrack { transform: none !important; }

/* ---- reveal list (component 5) + diagonal carousel (component 10) ---- */
.worktop { display: grid; grid-template-columns: 1fr 1fr; gap: 44px; align-items: start; }
.rlist { border-top: 2px solid var(--ink); }
.rlist .ri { border-bottom: 2px solid var(--ink); padding: 22px 6px; display: flex; justify-content: space-between; align-items: center; cursor: pointer; transition: padding var(--dur-swap) var(--ease-state), background var(--dur-state) var(--ease-state); }
/* Hover gated to a real pointer; :focus-within gives the keyboard the same
   affordance, since the reveal is now driven by a focusable button inside. */
@media (pointer: fine) {
  .rlist .ri:hover { padding-left: 18px; background: var(--yel); }
}
.rlist .ri:focus-within { padding-left: 18px; background: var(--yel); }
/* h3 AND h4: all four items are h3 now, but the h4 selector stays so an older
   cached page does not lose its type scale mid-deploy. */
.rlist .ri h3, .rlist .ri h4 { font-family: var(--f-display); font-weight: 800; text-transform: uppercase; font-size: clamp(20px, 2.6vw, 30px); letter-spacing: -0.01em; }
/* The label is a <button> for keyboard access; it must look like the heading it
   sits in, so everything the UA puts on a button is reset back to inherit. */
.rlist .ri__btn { font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit; background: none; border: 0; padding: 0; margin: 0; text-align: left; cursor: pointer; }
.rlist .ri__btn:focus-visible { outline: 3px solid var(--ink); outline-offset: 4px; }
.rlist .ri .m { font-family: var(--f-mono); font-size: 11px; letter-spacing: 0.12em; color: #4a443a; }
.rframe { position: relative; border: 2.5px solid var(--ink); aspect-ratio: 4 / 5; overflow: hidden; box-shadow: 10px 10px 0 var(--cyan); background: var(--paper-2); }
.rframe .slide { position: absolute; inset: 0; display: grid; place-items: center; opacity: 0; transition: opacity 0.01s; clip-path: inset(0 0 100% 0); }
.rframe .slide.on { opacity: 1; animation: pixreveal 0.5s steps(6) forwards; }
@keyframes pixreveal { from { clip-path: inset(0 0 100% 0); } to { clip-path: inset(0 0 0 0); } }
.rframe .slide .duo { position: absolute; inset: 0; }
.rframe .slide .duo::after { content: ""; position: absolute; inset: 0; background-image: radial-gradient(var(--ink) 32%, transparent 36%); background-size: 10px 10px; mix-blend-mode: soft-light; opacity: 0.5; }
.rframe .slide.s1 .duo { background: var(--cyan); }
.rframe .slide.s2 .duo { background: var(--mag); }
.rframe .slide.s3 .duo { background: var(--yel); }
.rframe .slide.s4 .duo { background: var(--orange); }
.rframe .slide .big { position: relative; font-family: var(--f-display); font-weight: 800; text-transform: uppercase; font-size: clamp(32px, 4vw, 52px); text-align: center; line-height: 0.9; color: var(--ink); }
.rframe .slide.s2 .big { color: var(--paper); }
.rframe .ph { position: absolute; inset: 0; display: grid; place-items: center; font-family: var(--f-mono); font-size: 12px; letter-spacing: 0.16em; color: #4a443a; }
@media (max-width: 860px) { .worktop { grid-template-columns: 1fr; } .rframe { display: none; } }

.carwrap { margin-top: 56px; overflow: hidden; transform: rotate(-2.5deg); border-block: 2.5px solid var(--ink); background: var(--ink); padding: 34px 0; }
.cartrack { display: flex; gap: 20px; padding: 0 24px; cursor: grab; width: max-content; }
.cartrack.drag { cursor: grabbing; }
.ctile { flex: 0 0 240px; aspect-ratio: 1 / 1; border: 2.5px solid var(--paper); position: relative; overflow: hidden; }
.ctile .duo { position: absolute; inset: 0; }
.ctile.k1 .duo { background: var(--cyan); }
.ctile.k2 .duo { background: var(--mag); }
.ctile.k3 .duo { background: var(--yel); }
.ctile.k4 .duo { background: var(--orange); }
.ctile.k5 .duo { background: var(--paper); }
.ctile .duo::after { content: ""; position: absolute; inset: 0; background-image: radial-gradient(rgba(21,19,16,0.5) 30%, transparent 34%); background-size: 10px 10px; opacity: 0.4; }
.ctile .lb { position: absolute; left: 12px; bottom: 12px; font-family: var(--f-mono); font-size: 10px; font-weight: 700; letter-spacing: 0.1em; background: var(--ink); color: var(--paper); padding: 4px 8px; }

/* ---- draggable sticker pile (component 13) ---- */
.stickerzone { position: relative; height: 340px; border-block: 2.5px solid var(--ink); overflow: hidden; background: var(--paper-2); }
.stickerzone .note { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); font-family: var(--f-mono); font-size: 12px; letter-spacing: 0.16em; color: #4a443a; text-transform: uppercase; z-index: 1; }
.sticker { position: absolute; font-family: var(--f-display); font-weight: 800; text-transform: uppercase; font-size: 19px; padding: 14px 20px; border: 2.5px solid var(--ink); cursor: grab; user-select: none; box-shadow: 4px 4px 0 var(--ink); touch-action: none; letter-spacing: -0.01em; }
.sticker.drag { cursor: grabbing; box-shadow: 8px 8px 0 var(--ink); z-index: 50; }
.sticker.k1 { background: var(--cyan); }
.sticker.k2 { background: var(--mag-ground); color: var(--paper); }
.sticker.k3 { background: var(--yel); }
.sticker.k4 { background: var(--orange); color: var(--ink); }
.sticker.k5 { background: var(--paper); }

/* ---- FAQ accordion ---- */
.faq { max-width: 860px; border: 2.5px solid var(--ink); }
.qa { border-bottom: 2px solid var(--ink); }
.qa:last-child { border-bottom: 0; }
.qa button { width: 100%; text-align: left; background: var(--paper); border: 0; cursor: pointer; padding: 24px 22px; display: flex; justify-content: space-between; gap: 20px; align-items: center; font-family: var(--f-display); font-weight: 700; font-size: clamp(16px, 2vw, 20px); color: var(--ink); transition: background 0.2s; }
.qa button:hover { background: var(--yel); }
.qa.open button { background: var(--cyan); }
.qa .pm { flex: 0 0 auto; width: 24px; height: 24px; position: relative; }
.qa .pm::before, .qa .pm::after { content: ""; position: absolute; background: var(--ink); left: 50%; top: 50%; transition: transform 0.3s var(--ease); }
.qa .pm::before { width: 14px; height: 2.5px; transform: translate(-50%, -50%); }
.qa .pm::after { width: 2.5px; height: 14px; transform: translate(-50%, -50%); }
.qa.open .pm::after { transform: translate(-50%, -50%) scaleY(0); }
.qa .ans { overflow: hidden; max-height: 0; transition: max-height var(--dur-view) var(--ease-state); background: var(--paper-2); }
.qa .ans p { padding: 20px 22px; font-size: 15px; color: #3a352d; max-width: 680px; }

/* ---- CTA ---- */
.cta { background: var(--mag-ground); color: var(--paper); border-block: 2.5px solid var(--ink); text-align: center; padding: 110px 0; position: relative; overflow: hidden; }
.cta .ht { position: absolute; inset: 0; background-image: radial-gradient(var(--yel) 20%, transparent 24%); background-size: 16px 16px; opacity: 0.25; }
.cta h2 { position: relative; font-family: var(--f-display); font-weight: 800; text-transform: uppercase; line-height: 0.9; font-size: clamp(42px, 8vw, 110px); letter-spacing: -0.02em; }
.cta h2 .o { -webkit-text-stroke: 2.5px var(--paper); color: transparent; }
.cta .actions { position: relative; justify-content: center; margin-top: 30px; }
.cta .btn.fill { background: var(--yel); color: var(--ink); box-shadow: 6px 6px 0 var(--ink); }
/* color, not just background: the base .btn.fill:hover sets color:var(--paper),
   so changing only the background left PAPER ON CYAN at 2.28:1 - a live WCAG AA
   failure on a primary call to action, at 15px bold, which is normal text and
   needs 4.5:1. Ink on the same cyan is 6.72:1, and it also stops the label
   flipping colour on hover, since the rest state is already ink on yellow.
   The magenta hover on the SITE-WIDE .btn.fill is a separate, harder failure:
   3.45:1 on paper and 4.44:1 on ink, so neither text colour rescues it and the
   brand magenta itself has to change. Left as an owner decision (V2). */
.cta .btn.fill:hover { background: var(--cyan); color: var(--ink); }
.cta .btn.ghost { background: transparent; color: var(--paper); border-color: var(--paper); box-shadow: 6px 6px 0 var(--ink); }

.bcell .bcell-val { margin: 0; }
