/* Fives — masthead.css · "The Full Stop." · the shared masthead system.
   Hand-authored; NO .jsx sibling — safe to edit directly.

   Spec: operations/design-review/fives-masthead-commission-2026-08-09/M1-BUILD-SPEC.md
   Reference implementation: prototype-fullstop.html (same folder), Gate 2,
   bounce locked at B2 (one 14px / 280ms hop).

   Loaded by the three M1 pages only: /history, /spirit-of-fives,
   /eton-fives-vs-rugby-fives. M2/M3 pages join later.

   FAIL-OPEN. Every hidden / undrawn state below is gated on `html.fv-mh-armed`,
   which masthead.js sets as its first statement. With no JS the masthead
   renders in its final state: real periods, plates fully drawn, words visible.
   (`fv-armed` is already owned by reveal.js and is set unconditionally there —
   this system therefore carries its own `fv-mh-armed` / `fv-mh-js` flags.)

   Reduced motion: the CSS belt is at the foot of this file; the braces are the
   matchMedia guard in masthead.js. Both honour a preference flipped live. */

/* ══════════════════════════════════════════════════════════════════
   A weight-800 fallback face for the H1 — the /schools CLS lesson again
   ------------------------------------------------------------------
   tokens.css's "Schibsted Fallback" declares no font-weight, so it matches
   Arial at 400 and Chrome SYNTHESISES the bold; its 107.98% size-adjust was
   derived for the 600 display weight. This masthead sets its H1 at 800, where
   that stack measures ~4% narrow: at 768 and 1023px the fallback fitted the
   headline on one line fewer, and the whole page jumped 66px when Schibsted
   landed (CLS 0.0274). Sourcing real Arial BOLD and declaring the weight kills
   the synthesis, and the numbers below were re-derived, not hand-tweaked —
   canvas measureText at 1000px against an English-frequency weighted average
   advance, same method as tokens.css:
     Schibsted 800  asc 0.9770  desc 0.2580  xWidthAvg 0.49930
     Arial 700      asc 0.9050  desc 0.2120  xWidthAvg 0.48061
   size-adjust 0.49930/0.48061 = 103.89%; the overrides are then the real
   face's metrics divided by that adjustment.
   ══════════════════════════════════════════════════════════════════ */
@font-face {
  font-family: "Schibsted 800 Fallback";
  src: local("Arial Bold"), local("Arial-BoldMT"), local("Helvetica Bold"), local("Liberation Sans Bold");
  font-weight: 700 900;
  size-adjust: 103.89%;
  ascent-override: 94.04%;
  descent-override: 24.84%;
  line-gap-override: 0%;
}

/* ══════════════════════════════════════════════════════════════════
   The Arrival clock — one timeline, tokens only
   ══════════════════════════════════════════════════════════════════ */
.mh {
  --fv-a-words:  120ms;   /* eyebrow lands first, then 60ms apart          */
  --fv-a-depart: 520ms;   /* the flick marks the ledge; the ball leaves it */
  --fv-flight:   710ms;   /* lob 430 + the deliberate hop 280 = B2         */

  position: relative;
  max-width: 1180px;
  margin: 0 auto;
  padding: 80px 24px 0;
  box-sizing: border-box;
}
@media (min-width: 760px)  { .mh { padding: 80px 40px 0; } }
@media (min-width: 1024px) { .mh { padding: 96px 40px 0; } }

/* ══════════════════════════════════════════════════════════════════
   Grammar — one column, then copy + furniture, then copy + plate
   ══════════════════════════════════════════════════════════════════ */
/* <760: eyebrow / H1 / sub / meta / ledge and nothing else. The plate is out
   (D5) and so is the slip — carried inside the masthead it pushed first
   content to ~91vh at 390, against a pre-commission 59vh. The mobile
   first-screen budget is protected estate; the Arrival still runs on the type,
   and the ball still leaves the ledge stroke. */
.mh-grid { display: block; }
.mh-right, .mh-slip { display: none; }

/* 760–1179: the plate cannot be shown without pushing first content past
   ~85vh (a 460px plate leaves the copy column under 530px at 1179), so the
   right column carries the slip alone. */
@media (min-width: 760px) {
  .mh-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 260px;
    gap: 48px;
    align-items: start;
  }
  .mh-right {
    display: flex; flex-direction: column; align-items: flex-end; gap: 22px;
    margin-top: 0;
  }
  .mh-slip { display: block; }
}
/* ≥1180: the full grammar — copy ~55%, plate column right. */
@media (min-width: 1180px) {
  .mh-grid { grid-template-columns: minmax(0, 1fr) 460px; gap: 56px; }
}

/* ── eyebrow → H1 16 · H1 → sub 20 · sub → meta 24 ─────────────── */
/* NB the explicit px line-heights on every mono row below. JetBrains Mono has
   no metric-matched fallback in tokens.css (only Inter and Schibsted do), so a
   ratio line-height re-boxes by ~3px the moment the mono face swaps in — which
   measured as a 0.00075 layout shift at 390px, most of this masthead's CLS. */
.mh-eyebrow {
  font-family: var(--fives-mono);
  font-size: 12px;
  line-height: 16px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fives-primary);
  margin: 0 0 16px;
}
.mh-h1 {
  font-family: "Schibsted Grotesk", "Schibsted 800 Fallback", "Schibsted Fallback",
               "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-weight: 800;
  font-size: 44px;
  line-height: 1.02;
  letter-spacing: -1.2px;
  color: var(--fives-text);
  margin: 0 0 20px;
  text-wrap: balance;
}
@media (min-width: 760px)  { .mh-h1 { font-size: 64px; letter-spacing: -1.8px; } }
@media (min-width: 1180px) { .mh-h1 { font-size: 88px; letter-spacing: -2.5px; } }
/* /eton-fives-vs-rugby-fives only — the longest H1 in the commission wraps to
   three lines at 88px in its own column. 80px is the spec's permitted step. */
@media (min-width: 1180px) { .mh--vs .mh-h1 { font-size: 80px; letter-spacing: -2.2px; } }

.mh-sub {
  font-size: 18px;
  line-height: 1.5;
  color: var(--fives-muted);
  margin: 0 0 24px;
  max-width: 26em;
  text-wrap: pretty;
}
@media (min-width: 760px)  { .mh-sub { font-size: 22px; max-width: 24em; } }
@media (min-width: 1180px) { .mh-sub { font-size: 26px; line-height: 1.38; max-width: 20em; } }

.mh-meta {
  list-style: none;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 0 12px;
  margin: 0; padding: 0;
  font-size: 14px;
  color: var(--fives-muted);
  font-variant-numeric: tabular-nums;
}
.mh-meta li + li::before { content: "\00B7"; margin-right: 12px; color: var(--fives-line); }

/* ══════════════════════════════════════════════════════════════════
   Chalk primitives — one hand, pathLength 100, never stretched
   ══════════════════════════════════════════════════════════════════ */
@keyframes mh-draw { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }
@keyframes mh-enter { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }
@keyframes mh-show { to { opacity: 1; } }
@keyframes mh-hide { to { opacity: 0; } }

.mh-ck { display: block; overflow: visible; color: var(--fives-muted); max-width: 100%; min-width: 0; pointer-events: none; }
.mh-ck-p {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 0;                 /* fail-open: drawn unless JS arms it */
  vector-effect: non-scaling-stroke;
}
html.fv-mh-armed .mh-ck-p { stroke-dashoffset: 100; }
/* a chalk mark outside the Arrival's scope — painted, never drawn */
.mh-ck-p.mh-no-draw,
html.fv-mh-armed .mh-ck-p.mh-no-draw { stroke-dashoffset: 0; animation: none; }
.mh-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ── the word beats — fail-open, armed by JS ───────────────────── */
html.fv-mh-armed .mh-beat { opacity: 0; }
.mh-beat.is-in {
  animation: mh-enter var(--fives-t-gentle) var(--fives-e-rebound) both;
  animation-delay: calc(var(--fv-a-words) + var(--fv-i, 0) * 60ms);
}

/* ══════════════════════════════════════════════════════════════════
   The plate — explicit box, so the draw-in shifts nothing (zero CLS)
   Stroke order IS the choreography: structure and features down by 440ms,
   the accent strike line LAST at 500ms, the flick at 520 = departure.
   ══════════════════════════════════════════════════════════════════ */
.mh-plate { display: none; color: var(--fives-muted); }
@media (min-width: 1180px) {
  .mh-plate { display: block; width: var(--mh-plate-w, 460px); height: var(--mh-plate-h, 420px); }
}
.mh-plate .mh-ck-p.is-drawn {
  animation: mh-draw 230ms var(--fives-e-draw) both;
  animation-delay: calc(var(--fv-i, 0) * 15ms);          /* i max 14 → done 440ms */
}
.mh-plate .mh-ck-p.mh-strike.is-drawn {
  animation-duration: var(--fives-t-standard);
  animation-delay: 280ms;                                /* lands at 500ms */
}
.mh-plate .mh-ck-p.mh-flick.is-drawn {
  animation-duration: 130ms;
  animation-delay: var(--fv-a-depart);
}
.mh-plate .mh-strike, .mh-plate .mh-flick { stroke: var(--fives-accent); }
.mh-plate .is-fine { opacity: .85; }
/* a drawn-in-chalk trace that is never animated: it carries meaning in the
   settled, reduced-motion and no-JS states alike, so it is not a .mh-ck-p */
.mh-trace {
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-dasharray: 1.5 5.5; stroke-linecap: round; opacity: .55;
  vector-effect: non-scaling-stroke;
}

/* /spirit-of-fives · the ball at rest on the step. It exists only while the
   Arrival is armed — the drawing's final state is the empty step, which is
   also what a no-JS visit and a reduced-motion visit see. */
.mh-rest { fill: var(--fives-accent); opacity: 0; }
html.fv-mh-armed .mh-rest { opacity: 1; }
.is-arriving .mh-rest { animation: mh-hide 1ms linear var(--fv-a-depart) forwards; }

/* tiny mono-caps plate labels (the /versus plate) */
.mh-plate text {
  font-family: var(--fives-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  fill: var(--fives-muted);
  stroke: none;
}

/* ── the full-content-width ledge stroke that closes the masthead ── */
.mh-ledge { width: 100%; height: 14px; color: var(--fives-accent); margin: 34px 0 56px; }
.mh-ledge .mh-ck-p.is-drawn { animation: mh-draw var(--fives-t-standard) var(--fives-e-draw) both; }

/* ══════════════════════════════════════════════════════════════════
   The ball — the signature
   Two transform-only wrappers, ONE animation each, and that animation
   carries the whole story (lob, contact, hop). Every stop is a sample of an
   exact analytic curve and the timing function is `linear`, so there is no
   easing anywhere and no phase seam to jitter on. Absolutely positioned over
   the period's measured ink box: zero layout shift, and the period stays in
   the text for a screen reader.
   ══════════════════════════════════════════════════════════════════ */
/* Held out of the render tree until it has been measured onto the period.
   An absolutely-positioned element still reports a layout shift when its own
   box jumps, so the un-placed 10×10 host at 0,0 was worth 0.00026 of CLS on
   its first placement. `display:none` reports nothing. */
.mh-ball { display: none; position: absolute; left: 0; top: 0; width: 10px; height: 10px; pointer-events: none; z-index: 2; }
.mh-bx, .mh-by, .mh-disc { display: block; width: 100%; height: 100%; }
.mh-bx, .mh-by { will-change: transform; }
.mh-disc { border-radius: 50%; background: var(--fives-accent); }
html.fv-mh-armed .mh-disc { opacity: 0; }   /* it exists only from the ledge onward */

/* fail-open: with no JS the period is a real period and there is no stray disc. */
html.fv-mh-js .mh-dot { color: transparent; }
html.fv-mh-js .mh-ball.is-placed { display: block; }
/* The baseline marker is a zero-box inline-block, so its top IS the baseline.
   It lives INSIDE the period's span under `white-space: nowrap`: an atomic
   inline is a line-break opportunity, and left as a sibling of the span it got
   pushed onto a line of its own at 768px — which put the ball a full line low
   and added a phantom fourth line to the H1. */
.mh-dot { white-space: nowrap; }
.mh-base { display: inline-block; width: 0; height: 0; vertical-align: baseline; }

.is-arriving .mh-bx { animation: mh-ball-x var(--fv-flight) linear var(--fv-a-depart) both; }
.is-arriving .mh-by { animation: mh-ball-y var(--fv-flight) linear var(--fv-a-depart) both; }
/* not a fade: a 1ms step at the instant of departure, so the ball appears at
   the ledge surface rather than materialising in mid-air */
.is-arriving .mh-disc { animation: mh-show 1ms linear var(--fv-a-depart) forwards; }

/* ══════════════════════════════════════════════════════════════════
   The slip — furniture, not a second meta (reading times stay left)
   ══════════════════════════════════════════════════════════════════ */
.mh-slip { width: 260px; max-width: 100%; padding: 0; background: none; border: 0; }
.mh-slip-edge { width: 120px; height: 12px; color: var(--fives-muted); margin: 0 0 12px; }
.mh-slip-edge .mh-ck-p.is-drawn { animation: mh-draw var(--fives-t-standard) var(--fives-e-draw) both; }
.mh-slip-lbl {
  font-family: var(--fives-mono);
  font-size: 11.5px; line-height: 17px; font-weight: 500; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--fives-muted);
  margin: 0 0 10px;
}
.mh-slip-hr { height: 1px; background: var(--fives-line); margin: 0 0 8px; }
.mh-slip-next {
  position: relative; display: inline-flex; align-items: center;
  font-size: 14px; font-weight: 600; color: var(--fives-accent);
  text-decoration: none; padding: 11px 0; min-height: 44px;
  transition: color var(--fives-t-quick) ease;
}
.mh-slip-next:hover { color: var(--fives-accent-700); }
.mh-slip-next .mh-ck { position: absolute; left: 0; bottom: 6px; width: 100%; color: var(--fives-accent); }
.mh-slip-next .mh-ck-p { stroke-dashoffset: 100; }
.mh-slip-next:hover .mh-ck-p,
.mh-slip-next:focus-visible .mh-ck-p { animation: mh-draw var(--fives-t-standard) var(--fives-e-draw) both; }
.mh-slip-next:focus-visible {
  outline: 2px solid var(--fives-primary);
  outline-offset: 3px;
  border-radius: var(--fives-r-sm);
}

/* ══════════════════════════════════════════════════════════════════
   The margin pull quote (/spirit-of-fives, A-31) — chalk-ruled, static.
   No new machinery: it rides the shared .fv-reveal on scroll.
   ══════════════════════════════════════════════════════════════════ */
.mh-margin-rule { width: 120px; height: 12px; color: var(--fives-accent); margin: 0 0 12px; }

/* ══════════════════════════════════════════════════════════════════
   Reduced motion — the CSS belt (the braces are in masthead.js).
   tokens.css already flattens durations globally; these rules put the
   masthead's own gated states into their FINAL state, not their armed one.
   `html.fv-rm` mirrors every rule so a preference flipped mid-session lands
   without waiting for a media re-evaluation of a cached stylesheet.
   ══════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  html.fv-mh-armed .mh-beat { opacity: 1 !important; transform: none !important; }
  .mh-beat.is-in { animation: none !important; }
  .mh-ck-p, html.fv-mh-armed .mh-ck-p { animation: none !important; stroke-dashoffset: 0 !important; }
  .mh-slip-next .mh-ck-p { stroke-dashoffset: 100 !important; }
  .mh-slip-next:hover .mh-ck-p,
  .mh-slip-next:focus-visible .mh-ck-p { stroke-dashoffset: 0 !important; }
  .mh-bx, .mh-by { animation: none !important; transform: none !important; }
  .mh-disc, html.fv-mh-armed .mh-disc { animation: none !important; opacity: 1 !important; }
  .mh-rest, html.fv-mh-armed .mh-rest { animation: none !important; opacity: 0 !important; }
}
html.fv-rm.fv-mh-armed .mh-beat { opacity: 1 !important; transform: none !important; }
html.fv-rm .mh-beat.is-in { animation: none !important; }
html.fv-rm .mh-ck-p { animation: none !important; stroke-dashoffset: 0 !important; }
html.fv-rm .mh-slip-next .mh-ck-p { stroke-dashoffset: 100 !important; }
html.fv-rm .mh-slip-next:hover .mh-ck-p,
html.fv-rm .mh-slip-next:focus-visible .mh-ck-p { stroke-dashoffset: 0 !important; }
html.fv-rm .mh-bx, html.fv-rm .mh-by { animation: none !important; transform: none !important; }
html.fv-rm .mh-disc { animation: none !important; opacity: 1 !important; }
html.fv-rm .mh-rest { animation: none !important; opacity: 0 !important; }

/* ══════════════════════════════════════════════════════════════════
   THE BAKED FLIGHT · B2 · generated, do not hand-edit.
   y = the two measured anchors (--fv-y0 departure height, --fv-ya apex)
   blended per stop; x = --fv-x0 decayed with a slight decel; the 14px/280ms
   hop is baked into the tail of the same curve. ~75 analytic samples,
   `linear`, no seams. Ported verbatim from the Gate-2 prototype.
   ══════════════════════════════════════════════════════════════════ */
@keyframes mh-ball-y{0%{transform:translateY(var(--fv-y0))}1.389%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.9225))}2.778%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.8481))}4.167%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.7768))}5.556%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.7087))}6.944%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.6437))}8.333%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.5818))}9.722%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.5231))}11.111%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.4674))}12.5%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.4149))}13.889%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.3655))}15.278%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.3193))}16.667%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.2762))}18.056%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.2362))}19.444%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.1993))}20.833%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.1656))}22.222%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.135))}23.611%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.1075))}25%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.0831))}26.389%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.0619))}27.778%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.0438))}29.167%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.0288))}30.556%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.0169))}31.944%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.0082))}33.333%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.0026))}34.722%{transform:translateY(calc(var(--fv-ya) + (var(--fv-y0) - var(--fv-ya)) * 0.0001))}35.127%{transform:translateY(var(--fv-ya))}36.111%{transform:translateY(calc(var(--fv-ya) * 0.9985))}37.5%{transform:translateY(calc(var(--fv-ya) * 0.9913))}38.889%{transform:translateY(calc(var(--fv-ya) * 0.9781))}40.278%{transform:translateY(calc(var(--fv-ya) * 0.959))}41.667%{transform:translateY(calc(var(--fv-ya) * 0.9339))}43.056%{transform:translateY(calc(var(--fv-ya) * 0.9028))}44.444%{transform:translateY(calc(var(--fv-ya) * 0.8658))}45.833%{transform:translateY(calc(var(--fv-ya) * 0.8228))}47.222%{transform:translateY(calc(var(--fv-ya) * 0.7739))}48.611%{transform:translateY(calc(var(--fv-ya) * 0.719))}50%{transform:translateY(calc(var(--fv-ya) * 0.6581))}51.389%{transform:translateY(calc(var(--fv-ya) * 0.5913))}52.778%{transform:translateY(calc(var(--fv-ya) * 0.5185))}54.167%{transform:translateY(calc(var(--fv-ya) * 0.4397))}55.556%{transform:translateY(calc(var(--fv-ya) * 0.355))}56.944%{transform:translateY(calc(var(--fv-ya) * 0.2643))}58.333%{transform:translateY(calc(var(--fv-ya) * 0.1677))}59.722%{transform:translateY(calc(var(--fv-ya) * 0.065))}60.563%{transform:translateY(0px)}61.111%{transform:translateY(-0.767px)}62.5%{transform:translateY(-2.615px)}63.889%{transform:translateY(-4.324px)}65.278%{transform:translateY(-5.894px)}66.667%{transform:translateY(-7.325px)}68.056%{transform:translateY(-8.618px)}69.444%{transform:translateY(-9.771px)}70.833%{transform:translateY(-10.786px)}72.222%{transform:translateY(-11.661px)}73.611%{transform:translateY(-12.398px)}75%{transform:translateY(-12.996px)}76.389%{transform:translateY(-13.454px)}77.778%{transform:translateY(-13.774px)}79.167%{transform:translateY(-13.955px)}80.556%{transform:translateY(-13.997px)}81.944%{transform:translateY(-13.9px)}83.333%{transform:translateY(-13.665px)}84.722%{transform:translateY(-13.29px)}86.111%{transform:translateY(-12.776px)}87.5%{transform:translateY(-12.124px)}88.889%{transform:translateY(-11.332px)}90.278%{transform:translateY(-10.402px)}91.667%{transform:translateY(-9.333px)}93.056%{transform:translateY(-8.125px)}94.444%{transform:translateY(-6.778px)}95.833%{transform:translateY(-5.292px)}97.222%{transform:translateY(-3.667px)}98.611%{transform:translateY(-1.903px)}100%{transform:translateY(0px)}}
@keyframes mh-ball-x{0%{transform:translateX(var(--fv-x0))}1.389%{transform:translateX(calc(var(--fv-x0) * 0.9737))}2.778%{transform:translateX(calc(var(--fv-x0) * 0.9474))}4.167%{transform:translateX(calc(var(--fv-x0) * 0.9213))}5.556%{transform:translateX(calc(var(--fv-x0) * 0.8953))}6.944%{transform:translateX(calc(var(--fv-x0) * 0.8693))}8.333%{transform:translateX(calc(var(--fv-x0) * 0.8435))}9.722%{transform:translateX(calc(var(--fv-x0) * 0.8177))}11.111%{transform:translateX(calc(var(--fv-x0) * 0.7921))}12.5%{transform:translateX(calc(var(--fv-x0) * 0.7666))}13.889%{transform:translateX(calc(var(--fv-x0) * 0.7411))}15.278%{transform:translateX(calc(var(--fv-x0) * 0.7158))}16.667%{transform:translateX(calc(var(--fv-x0) * 0.6906))}18.056%{transform:translateX(calc(var(--fv-x0) * 0.6656))}19.444%{transform:translateX(calc(var(--fv-x0) * 0.6406))}20.833%{transform:translateX(calc(var(--fv-x0) * 0.6158))}22.222%{transform:translateX(calc(var(--fv-x0) * 0.5911))}23.611%{transform:translateX(calc(var(--fv-x0) * 0.5666))}25%{transform:translateX(calc(var(--fv-x0) * 0.5421))}26.389%{transform:translateX(calc(var(--fv-x0) * 0.5179))}27.778%{transform:translateX(calc(var(--fv-x0) * 0.4937))}29.167%{transform:translateX(calc(var(--fv-x0) * 0.4698))}30.556%{transform:translateX(calc(var(--fv-x0) * 0.4459))}31.944%{transform:translateX(calc(var(--fv-x0) * 0.4223))}33.333%{transform:translateX(calc(var(--fv-x0) * 0.3988))}34.722%{transform:translateX(calc(var(--fv-x0) * 0.3755))}35.127%{transform:translateX(calc(var(--fv-x0) * 0.3688))}36.111%{transform:translateX(calc(var(--fv-x0) * 0.3524))}37.5%{transform:translateX(calc(var(--fv-x0) * 0.3295))}38.889%{transform:translateX(calc(var(--fv-x0) * 0.3068))}40.278%{transform:translateX(calc(var(--fv-x0) * 0.2843))}41.667%{transform:translateX(calc(var(--fv-x0) * 0.262))}43.056%{transform:translateX(calc(var(--fv-x0) * 0.24))}44.444%{transform:translateX(calc(var(--fv-x0) * 0.2182))}45.833%{transform:translateX(calc(var(--fv-x0) * 0.1967))}47.222%{transform:translateX(calc(var(--fv-x0) * 0.1756))}48.611%{transform:translateX(calc(var(--fv-x0) * 0.1547))}50%{transform:translateX(calc(var(--fv-x0) * 0.1342))}51.389%{transform:translateX(calc(var(--fv-x0) * 0.1141))}52.778%{transform:translateX(calc(var(--fv-x0) * 0.0945))}54.167%{transform:translateX(calc(var(--fv-x0) * 0.0754))}55.556%{transform:translateX(calc(var(--fv-x0) * 0.0569))}56.944%{transform:translateX(calc(var(--fv-x0) * 0.0392))}58.333%{transform:translateX(calc(var(--fv-x0) * 0.0224))}59.722%{transform:translateX(calc(var(--fv-x0) * 0.0073))}60.563%{transform:translateX(0px)}100%{transform:translateX(0px)}}

/* ══════════════════════════════════════════════════════════════════
   The plateless departure (/laws) — the masthead's own ledge stroke is
   the founding mark, and the flick marks the strike on it. Absolutely
   positioned and held out of the render tree until masthead.js has
   measured the ledge, so it costs no layout.
   ══════════════════════════════════════════════════════════════════ */
.mh-ledge-flick { display: none; position: absolute; left: 0; top: 0; width: 20px; height: 20px; color: var(--fives-accent); z-index: 2; }
html.fv-mh-js .mh-ledge-flick.is-placed { display: block; }
/* the full shorthand, not just duration/delay: outside a plate there is no
   base rule supplying the animation-NAME, so a duration-only override left the
   flick sitting at its armed dashoffset — i.e. permanently invisible. */
.mh-ledge-flick .mh-ck-p.is-drawn { animation: mh-draw 130ms var(--fives-e-draw) var(--fv-a-depart) both; }

/* ══════════════════════════════════════════════════════════════════
   The marginalia rail (/laws) — the page reading along.
   Ported from the Gate-2 prototype §5: the swap is `settle`, not rebound
   (this is reading context arriving, not an entrance), out 140 / in 260,
   4px rise, complete inside one gentle.
   ══════════════════════════════════════════════════════════════════ */
@keyframes mh-note-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes mh-note-in  { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }
.mh-note-lbl {
  font-family: var(--fives-mono);
  font-size: 11px; line-height: 16px; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--fives-muted);
  margin: 0 0 8px;
}
.mh-note-body {
  font-size: 14.5px; line-height: 1.5; color: var(--fives-text);
  font-style: italic; margin: 0;
}
.mh-note-body.is-out { animation: mh-note-out 140ms var(--fives-e-settle) both; }
.mh-note-body.is-in  { animation: mh-note-in  260ms var(--fives-e-settle) both; }
@media (prefers-reduced-motion: reduce) {
  .mh-note-body.is-out, .mh-note-body.is-in { animation: none !important; opacity: 1 !important; transform: none !important; }
}
html.fv-rm .mh-note-body.is-out,
html.fv-rm .mh-note-body.is-in { animation: none !important; opacity: 1 !important; transform: none !important; }

/* ══════════════════════════════════════════════════════════════════
   Optional hero body copy, between the sub and the meta row
   (/set-piece-play's three-sentence lede).
   ══════════════════════════════════════════════════════════════════ */
.mh-body {
  font-size: 16px; line-height: 1.6; color: var(--fives-text);
  margin: 0 0 24px; max-width: 34em; text-wrap: pretty;
}
.mh-body em { font-style: italic; color: var(--fives-text); }
@media (min-width: 1180px) { .mh-body { font-size: 17px; max-width: 30em; } }

/* the serve's dotted flight (/set-piece-play) — drawn art, never the ball.
   Not a .mh-ck-p: it carries meaning in the settled, RM and no-JS states, and
   its dash pattern must not be overwritten by the draw system. */
.mh-serve-arc {
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-dasharray: 2 6; stroke-linecap: round; opacity: .5;
  vector-effect: non-scaling-stroke;
}

/* ══════════════════════════════════════════════════════════════════
   M3 · THE COMPRESSED VARIANT (/courts, /clubs)
   These pages are tools. The masthead's job is to be excellent BRIEFLY
   and hand over to the map/filters as fast as possible, so this variant
   deliberately does less: eyebrow at nav+48 rather than +96, tighter
   gaps (12/16/20), the 64px directory tier, no plate, no slip, and a
   shorter run-out to first content (40px, not 56). The right half at
   ≥1180 carries live utility instead of furniture.
   ══════════════════════════════════════════════════════════════════ */
.mh--compact { padding: 32px 24px 0; }
@media (min-width: 760px)  { .mh--compact { padding: 48px 40px 0; } }
@media (min-width: 1024px) { .mh--compact { padding: 48px 40px 0; } }

.mh--compact .mh-eyebrow { margin: 0 0 12px; }
.mh--compact .mh-h1      { margin: 0 0 16px; }
.mh--compact .mh-sub     { margin: 0 0 20px; }
/* Below 760 the spec's floor is the MOBILE BUDGET, not the desktop gaps:
   /courts at 46% is the estate's model and must not grow past +4vh, so the
   phone gets a tighter set and a much shorter run-out under the ledge. */
@media (max-width: 759px) {
  .mh--compact { padding-top: 16px; }
  .mh--compact .mh-eyebrow { margin: 0 0 8px; }
  .mh--compact .mh-h1      { margin: 0 0 10px; }
  .mh--compact .mh-sub     { margin: 0 0 14px; }
  .mh--compact .mh-ledge   { margin: 6px 0 8px; }
  .mh--compact .mh-body    { line-height: 1.45; }
}

/* the directory tier — one line at every width from 760 up */
@media (min-width: 760px)  { .mh--compact .mh-h1 { font-size: 64px; letter-spacing: -1.8px; } }
@media (min-width: 1180px) { .mh--compact .mh-h1 { font-size: 64px; letter-spacing: -1.8px; } }

/* the sub on these pages is the live-stats line, so it keeps display type */
.mh--compact .mh-sub {
  font-family: var(--fives-display);
  font-weight: 500;
  letter-spacing: -0.6px;
  line-height: 1.2;
  font-size: 22px;
  max-width: 26em;
}
@media (min-width: 760px)  { .mh--compact .mh-sub { font-size: 28px; } }
@media (min-width: 1180px) { .mh--compact .mh-sub { font-size: 32px; line-height: 1.18; } }

/* the lede — one written paragraph at every width (A4: no clamp anywhere) */
.mh--compact .mh-body { font-size: 16px; line-height: 1.6; margin: 0; max-width: 39.11em; }
/* wider measure on the desktop tool pages: it drops the lede a line, which is
   the difference between the filter bar landing above and below the fold */
@media (min-width: 1180px) { .mh--compact .mh-body { font-size: 16px; max-width: 42em; } }

/* the run-out: the ledge closes the masthead and hands over quickly */
.mh--compact .mh-ledge { margin: 8px 0 40px; }

/* ── the right half: live utility, not furniture (≥1180 only) ── */
.mh--compact .mh-right { display: none; }
@media (min-width: 1180px) {
  .mh--compact .mh-grid { grid-template-columns: minmax(0, 1fr) 300px; gap: 56px; align-items: center; }
  .mh--compact .mh-right { display: flex; align-items: flex-end; margin-top: 0; }
}
.mh-chips { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; }
.mh-chip {
  display: flex; align-items: baseline; gap: 6px;
  padding: 8px 12px;
  background: color-mix(in srgb, var(--fives-bg) 97%, transparent);
  border: 1px solid var(--fives-line);
  border-radius: var(--fives-r-md);
}
.mh-chip-n {
  font-family: var(--fives-mono);
  font-size: 18px; line-height: 1; font-weight: 500;
  color: var(--fives-primary-700);
  font-variant-numeric: tabular-nums;
}
.mh-chip-l { font-family: var(--fives-body); font-size: 12px; line-height: 1; color: var(--fives-muted); }
