/* BlackWerks landing page.
   Structure, spacing, component anatomy and interaction come from the
   landing-canvas-2026-08-27 extraction (page 1440 / content 1280 / margin 80 /
   gutter 24 / band 96; the pill radius 999; stepped motion; alternating
   section surfaces; hairline-divided square grids; a persistent margin field).
   COLOUR AND TYPE COME FROM BRAND-CORE, NOT FROM THE CANVAS. Where the two
   disagree BRAND-CORE wins: nine palette values, the pt-anchored scale, and
   the ground-dependent accent in two cuts. */

:root {
  /* palette - BRAND-CORE 5, nine values, no tenth */
  --ink: #161719;
  --ink-raised: #1F2124;
  --grey-1: #6A6E71;
  --grey-2: #8E9295;
  --grey-3: #B9BDC0;
  --grey-4: #CDCFD1;
  --paper: #FFFFFF;
  --orange: #FF5B2E;
  --orange-deep: #C42A00;

  /* type - BRAND-CORE 3.3, points are the invariant, px = pt x 96 / 72 */
  --display-face: "Saira Condensed", "Arial Narrow", "Helvetica Neue", sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --display-track: 0.0966em;          /* re-derived off Saira per BRAND-CORE 3.1 */
  --display-track-lc: 0em;            /* a full sentence is not letterspaced */
  --measure-prose: 68ch;
  --measure-lede: 46ch;

  --d1: 96px;                         /* derived - see .hero, below */
  --d2: 45.3px;                       /* display-2, 34pt */
  --d3: 26.7px;                       /* display-3, 20pt */
  --fig: 40px;                        /* display-thin ordinals */
  --fig-xl: 124px;                    /* display-thin, the one hard number on the page */
  --subhead: 16px;                    /* 12pt */
  --body: 13.3px;                     /* 10pt */
  --caption: 12px;                    /* 9pt */
  --apparatus: 10px;                  /* 7.5pt, caps, +0.13em */

  /* motion - one step function for the whole page */
  --step: steps(12, end);
  --t-state: 240ms;
  --t-enter: 600ms;
  --rise: max(48px, 40%);

  /* geometry - the canvas extraction */
  --maxw: 1440px;
  --pad: 80px;
  --gutter: 24px;
  --band: 96px;
  --aside: 411px;
  --head-h: 72px;
  --r-pill: 999px;

  --cap: 16px;
  /* DERIVED, not chosen. The bled components start one gutter inside the page
     padding, so the field sits exactly halfway between the trim and that rule and
     its clearance is symmetric at every width. It was 33px on an 80px pad and 22px
     on a 56px one, which put the plus marks 3px off the rules at 1024 - a collision
     the old check could not see, because it measured the field against the TEXT
     column, which is 24px further in. */
  --field-in: calc((var(--pad) - var(--gutter)) / 2);
}

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

/* 320px is a floor, not a preference: one display word is wider than the
   content box there, and a clip on one root alone still lets the other scroll. */
html, body { overflow-x: clip; }
html { -webkit-text-size-adjust: 100%; color-scheme: dark; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--mono);
  font-weight: 400;
  font-size: var(--body);
  line-height: 1.62;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, figure, dl, dd { margin: 0; font-size: inherit; font-weight: inherit; }
ol, ul { margin: 0; padding: 0; list-style: none; }
svg { display: block; }
img { display: block; max-width: 100%; }

/* ── surfaces ──────────────────────────────────────────────────────────
   Two grounds, not four. BRAND-CORE has ink and paper; the canvas's second
   dark tier #262A2D and second light tier #EDEEF0 are outside the nine, and
   #1F2124 as a BAND ground measures 1.11:1 against ink - a rendering step,
   not a band. So the rhythm is ink / paper, and #1F2124 keeps the role
   BRAND-CORE gives it: the raised plate and the hairline inside a dark band.

   Every colour below is a palette hex. The accent is declared here and only
   here, once per ground, which is what makes the ground-dependent rule
   mechanically checkable. */
.s-ink {
  --bg: var(--ink);
  --fg: var(--paper);
  --fg-3: var(--grey-3);
  --fg-4: var(--grey-2);
  /* The hairline is derived, not chosen. #1F2124 measures 1.11:1 on ink where the
     paper side's #CDCFD1 measures 1.56:1, so the same rule was a third as present
     on half the page and the ledger stopped reading as a ledger on a dim screen.
     grey-1 mixed 40% into ink lands at 1.571:1 - parity with the paper side, and
     still every value in the mix is one of BRAND-CORE's nine. */
  --line: color-mix(in srgb, var(--grey-1) 40%, var(--ink));
  --plate: var(--ink-raised);         /* BRAND-CORE's own role for it: the raised plate */
  --on-plate: var(--paper);
  --accent: var(--orange);
  --on-accent: var(--ink);
  background: var(--bg);
  color: var(--fg);
}

.s-paper {
  --bg: var(--paper);
  --fg: var(--ink);
  --fg-3: var(--grey-1);
  --fg-4: var(--grey-1);
  --line: var(--grey-4);
  --plate: var(--grey-4);
  --on-plate: var(--ink);
  --accent: var(--orange-deep);
  --on-accent: var(--paper);
  background: var(--bg);
  color: var(--fg);
}

.surf { position: relative; }

/* ── type ──────────────────────────────────────────────────────────────
   RULE: caps for names and labels, sentence case for full sentences. The
   display classes default to sentence case because almost every headline on
   this page is a sentence; .caps is the opt-in for a name or a label. */
.d1, .d2, .d3 {
  font-family: var(--display-face);
  font-weight: 900;
  letter-spacing: var(--display-track-lc);
  color: var(--fg);
  text-wrap: balance;
  overflow-wrap: anywhere;
}

.d1 { font-size: var(--d1); line-height: 1.00; }
.d2 { font-size: var(--d2); line-height: 1.05; }
.d3 { font-size: var(--d3); line-height: 1.10; }

.caps { text-transform: uppercase; letter-spacing: var(--display-track); }

.fig-xl {
  font-family: var(--display-face);
  font-weight: 300;
  font-size: var(--fig-xl);
  line-height: 0.86;
  letter-spacing: -0.02em;
  color: var(--fg);
}

.fig {
  font-family: var(--display-face);
  font-weight: 300;
  font-size: var(--fig);
  line-height: 1.00;
  letter-spacing: -0.01em;
  color: var(--fg);
}

.ap {
  font-family: var(--mono);
  font-weight: 500;
  font-size: var(--apparatus);
  line-height: 1.40;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-4);
}

.rule-out { display: flex; align-items: center; gap: 12px; }
.rule-pill { border: 1px solid var(--line); border-radius: var(--r-pill); padding: 5px 12px; }

/* THE TERMINAL DOT IS NOT AN ACCENT SITE. The device is drawn twice on the page, so
   putting the accent in it spends two of the page's saturated sites on a rule's full
   stop. Counted at 1440 the accent ran to eight sites, against the four round one
   credited as "the only place the accent carries meaning instead of decoration"; two
   of the four new ones were these dots. The reference draws the same terminal dot in
   a neutral, sampled #797672. --fg-4 is #8E9295 on ink, the same register. */
.rule-out::after {
  content: "";
  flex: 0 0 auto;
  width: 96px;
  height: 6px;
  background:
    linear-gradient(var(--fg-4) 0 0) no-repeat left center / 88px 1px,
    radial-gradient(circle, var(--fg-4) 0 3px, transparent 3px) no-repeat right center / 6px 6px;
}

.sub { font-size: var(--subhead); line-height: 1.45; color: var(--fg-3); text-wrap: pretty; }
/* NO text-wrap:pretty HERE, AND IT IS NOT AN OVERSIGHT. It was on .tx at 9bb61ac,
   removed at 8384152 because round one's craft critic priced the trade - "buying a
   tidy last line at the cost of an 85px notch halfway down a five-line paragraph" -
   and round two put it back under a comment presenting it as new. Re-measured: it
   buys nothing, because the shear it claims to prevent is already gone. Ink rows cut
   at the fold are 0 with it and 0 without, at 1440, 1280, 1024 and 768; the hero
   height did that. And it costs the rag - THE PATTERN sets 407/327.2/359.1/351.1/119.7
   with it against 407/367/351/375/64 without, doubling the notch from 40px to 79.8px,
   within 5px of the value that got it removed the first time. Removed again.

   AND REMOVING IT PAGE-WIDE WAS THE SAME MISTAKE POINTING THE OTHER WAY. A page-wide
   lever pulled for one paragraph's defect: the WHO standing line was authored while
   pretty was in force and depends on it, and losing it turned that line's last line at
   390 from 96px into a 72px stub, which is a CHECK 22 failure. So neither state of the
   page-wide declaration is right, because the two paragraphs want opposite things. It
   is scoped per use below, and each scope carries the measurement that earns it. */
.tx  { color: var(--fg-3); max-width: var(--measure-prose); }

/* The five-line standfirst wants EVEN lines, not a tidy last one: pretty gave it
   407/327.2/359.1/351.1/119.7, a 79.8px notch, and plain gave it 407/367/351/375/64,
   a 63.8px stub on the first screen. balance is the declaration that asks for the
   thing actually wanted here, and it is the only one of the three that closes the
   notch and the stub together. */
.sec-head-r .tx { text-wrap: balance; }

/* BOTH PARAGRAPHS ON THIS SELECTOR WANT balance, AND THAT WAS CHECKED, NOT ASSUMED.
   The objection was real: balance was breaking "...and what it leaves" / "behind, ..."
   in the DELIVERY note, splitting a phrase that the rail's own column header repeats
   110px below. An opt-out was written for it - and two things came out of measuring it
   rather than reading it. The rule as first written was `.sec-head-note`, (0,1,0)
   against this selector's (0,2,0), so it lost the cascade and computed text-wrap-style
   still read `balance` at all five widths: a declaration present in the file and doing
   nothing. Once it was specific enough to apply, `auto` set the note 542.6/63.8 at 768,
   a 63.8px stub under the page's own floor.
   And by then the objection had dissolved anyway: the recut copy no longer contains
   "leaves behind", so there is no echoed phrase left to break. balance sets it
   319.2/287.3, breaking at its comma, with no stub at any width. The opt-out is gone. */

/* THE PAGE-WIDE REMOVAL HAD A BLAST RADIUS NOBODY ENUMERATED. Round two carried
   text-wrap:pretty on .tx; round three took it off page-wide and re-scoped it to two
   elements, and every OTHER .tx lost it silently. At 390 that took the rail's HANDOVER
   last line from 71.8px to 39.9px - worse than the orphan round two was criticised for
   - and opened a second on EXIT. Swept across all five widths with each of the three
   states forced, counting every multi-line block on the page under the 80px floor:

       390    shipped 4 blocks (worst 39.9)   pretty 3 (worst 71.8)   balance 1 (worst 71.8)
      1024    1 / 1 / 1 (unchanged, .pair-item, which these selectors do not reach)
      1440 / 1280 / 768    0 / 0 / 0 in every state

   balance is the only state that clears the rail and the client rows, and it costs
   nothing at any other width. CHECK 22's floor is extended to these elements so this
   cannot silently regress again. */
.step .tx, .wrow-v { text-wrap: balance; }

/* The last two blocks under the floor, swept the same way. .lrow-k ended on 71.8px at
   390 and .pair-item .tx on 63.8px at 1024, and both were pre-existing - neither was
   round three's doing, and nothing gated them because the floor reached one element.
   With these two the whole page clears 80px at all five widths: worst last line 144 /
   144 / 96 / 144 / 95.8. No element needs exempting, so CHECK 23 carries no exemptions. */
.lrow-k, .pair-item .tx { text-wrap: balance; }

/* The standing line under the name is two lines at 390 and one everywhere else, so
   what it needs is the last-line rule, not the balancing one. Scoped to the one
   element that wants it. CHECK 22 holds the floor at 80px. */
.who-who .tx { text-wrap: pretty; }
.cap { font-size: var(--caption); line-height: 1.55; color: var(--fg-4); text-wrap: pretty; }

a { color: var(--accent); text-decoration: none; position: relative; }

/* THIS RULE OCCUPIES THE ::after SLOT ON EVERY ANCHOR ON THE PAGE, AND IT IS A TRAP.
   Anything else that hangs decoration off an anchor's ::after inherits position:absolute
   and scaleX(0) from here and silently paints NOTHING, while every CSS-level reading -
   computed `content`, the cascade, the devtools panel - reports it present. It cost a
   round: a footer separator was hung off ::after on alternating items, and the one that
   landed on the tel: anchor vanished. Only a pixel scan found it. Swept 2026-08-30: the
   page currently carries three rules that can land on an anchor and all three are
   deliberate - .btn::after kills it, .head-link::after and .meta a::after only nudge
   its offset. The live hazard is .rule-out::after, which draws the hairline and dot
   device: it sits on <p> today and would paint nothing the day it moves to a link.
   If you need a mark on an anchor, use ::before, or reset position/transform here. */
a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -3px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--t-state) var(--step);
}

a:hover::after, a:focus-visible::after { transform: scaleX(1); }
a:focus-visible { outline: 1px solid currentColor; outline-offset: 4px; }

::selection { background: var(--orange); color: var(--ink); }

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ── layout ────────────────────────────────────────────────────────────
   The canvas caps content at 1280 with an 80px margin under content-box,
   which resolves to 1440 at exactly one width and clips below it. Same
   geometry, expressed in border-box: identical 1280 column at 1440, correct
   at every width under it. */
.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--pad);
  position: relative;
  z-index: 1;
}

.band { padding-top: var(--band); padding-bottom: var(--band); }

.sec-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--aside);
  gap: var(--gutter);
  align-items: start;
}

.sec-head-l { display: flex; flex-direction: column; gap: 20px; max-width: 880px; }
.sec-head-r { display: flex; flex-direction: column; gap: 20px; }

/* ── the margin field ──────────────────────────────────────────────────
   A dotted column half a gutter inside the bled rules - 28px in from each page
   edge at 1440 and 1280, 16px at 1024, derived by --field-in above rather than
   chosen, which is why this comment names the rule and not a number. It carried
   a literal 33px until the derivation replaced it. A cross sits on the section
   seams. Out of flow, pointer-inert, and it never enters the content column
   - which is asserted in the browser, not trusted. --line is the same
   hairline value the bands use, so the field cannot become louder than the
   rules it sits beside. */
.field { position: absolute; inset: 0; pointer-events: none; z-index: 0; overflow: hidden; }

.field-col {
  position: absolute;
  top: 0; bottom: 0;
  width: 1px;
  background-image: repeating-linear-gradient(var(--line) 0 1.5px, transparent 1.5px 44px);
}

.field-col.l { left: var(--field-in); }
.field-col.r { right: var(--field-in); }

.plus { position: absolute; width: 13px; height: 13px; color: var(--line); }
.plus::before, .plus::after { content: ""; position: absolute; background: currentColor; }
.plus::before { left: 0; right: 0; top: 6px; height: 1px; }
.plus::after { top: 0; bottom: 0; left: 6px; width: 1px; }
.plus.tl { left: calc(var(--field-in) - 6px); top: 24px; }
.plus.tr { right: calc(var(--field-in) - 6px); top: 24px; }
.plus.bl { left: calc(var(--field-in) - 6px); bottom: 24px; }
.plus.br { right: calc(var(--field-in) - 6px); bottom: 24px; }

/* ── photography ──────────────────────────────────────────────────────
   Monochrome, and it stays monochrome: no duotone, no tint layer, no blend
   mode. The orange is the only colour on the page and that is the reason.
   One grade for every photograph on either ground. */
.ph { position: relative; overflow: hidden; background: var(--ink); }

.ph img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: contrast(1.10) brightness(0.95);
  transition: transform var(--t-enter) var(--step);
}

.plate { aspect-ratio: 16 / 9; box-shadow: 0 0 0 1px var(--line); }

/* ── controls ─────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: 44px;
  padding: 0 22px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--mono);
  font-weight: 500;
  font-size: var(--caption);
  letter-spacing: 0.02em;
  transition: transform var(--t-state) var(--step);
}

.btn::after { display: none; }
.btn:hover { transform: translateY(-1px); }
.btn:focus-visible { outline: 1px solid var(--fg); outline-offset: 4px; }

/* ── header ───────────────────────────────────────────────────────────── */
.head-shell {
  position: sticky;
  top: 0;
  z-index: 30;
  border-bottom: 1px solid var(--line);
}

.head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  height: var(--head-h);
}

/* THE GAP IS 1.700 x CAP, INK TO INK - BRAND-CORE 2 and 2.2. It was var(--cap), the
   1.000 that Rady replaced on 2026-08-27 off the eight-rung gap ladder, and it shipped
   through five fix rounds because nothing measured it. The multiplier hangs off --cap
   rather than off a pixel value so the relationship survives a type-size change: at the
   767 breakpoint --cap drops to 14 and the gap follows to 23.8 on its own.

   IT REALLY IS INK TO INK. The negative margins below pull .mark in by its 40-unit
   viewBox margin and .wm in by the B's 50-unit sidebearing, so each flex item's MARGIN
   BOX is its ink box and the flex gap is the ink gap. BRAND-CORE 2.3 records two shipped
   builds that measured to the glyph origin instead and were 7% out. */
.lockup { display: flex; align-items: center; gap: calc(var(--cap) * 1.7); }

.mark {
  --box-h: calc(var(--cap) * 2 * 850 / 770);
  --box-w: calc(var(--cap) * 2 * 1260 / 770);
  --trim: calc(var(--cap) * 2 * 40 / 770);
  width: var(--box-w);
  height: var(--box-h);
  margin: calc(var(--trim) * -1);
  fill: var(--fg);
}

.wm {
  --box-h: calc(var(--cap) * 1000 / 714);
  --box-w: calc(var(--cap) * 6540 / 714);
  width: var(--box-w);
  height: var(--box-h);
  margin: calc(var(--cap) * -158.5 / 714) calc(var(--cap) * -22 / 714)
          calc(var(--cap) * -127.5 / 714) calc(var(--cap) * -50 / 714);
  fill: var(--fg);
}

.head-link { padding: 13px 0; color: var(--accent); }   /* 14 line + 26 = 40; the row is 72 tall */
.head-link::after { bottom: 3px; }

/* ── hero ─────────────────────────────────────────────────────────────
   Full-bleed photograph running under the sticky header. The type sits in
   the frame's dark third, and the scrim under it resolves to a PINNED
   ground: a component carrying a pinned tone needs a pinned ground, so the
   scrim is ink at full opacity where the words are, and the contrast is
   computed against the composite rather than eyeballed. */
/* NO min-height. The hero is exactly as tall as its own type block, which is
   what makes --d1 a derived value rather than a chosen one: grow the headline and
   the hero grows, and the section under it drops toward the fold.

   The rule it is derived against, restated for this layout - the shipped page's
   72px came off a manifest row that had to clear the fold, and there is no such
   row here. What the rule protects is the same: the first screen delivers the
   whole statement AND says there is more. Measured at four viewports, the binding
   one is 1280 at a real 710px of content, where the ceiling is 112px. 96px is
   taken instead, because it is the largest value at which the next section's label
   is COMPLETE there rather than a sliver - and because this criterion has failed
   twice before on an exact coincidence, so the ceiling is not the value. */
.hero {
  position: relative;
  /* One viewport, less the room the next section's label needs to be COMPLETE at
     the fold: 144px from the hero's bottom edge to that label's ink, plus its own
     14px line, plus slack. Stated as a viewport calculation rather than a fixed
     number because the rule is about the viewport - a fixed hero satisfies the
     fold on one screen and breaks it on the next one down. Below 768 it is off:
     100vh on a phone is the address-bar-hidden viewport, not the visible one.

     224, AND THE VALUE MOVED 200 -> 224 -> 216 -> 224 BECAUSE THE FIRST THREE WERE
     PRICED ON ONE SIDE ONLY. Ink rows cut by the fold, which is the only thing a
     reader can see, and first-screen clearance in px:

              @200      @208      @216      @224
       1440   1 cut     8px       16px      24px
       1280   1 cut     8px       16px      24px
       1024   12 cut    cut       5px       6px    (at 768; 13px at 800/900/1080)

     "The minimum that clears every width" picked 216 off the top two rows. It is the
     wrong reading, because the row that binds is the third: 216 clears 1024 by 5px,
     which is a 62% cut in margin against 224's 13px, and it buys 8px of photograph on
     a 684px hero - 1.2%, on a frame that fills the screen. Nobody perceives the 8px;
     5px is one font-fallback metric shift from a cut, on a statistic this page has got
     wrong twice, at the one width in the matrix that is a physical device rather than
     an interpolation. Asymmetric harm, so buy the margin.

     The 1024x700 cut is NOT this rule's: it is identical at 200, 208, 216 and 224,
     because the hero's own content is 551.17px there and 100vh minus anything asks for
     less, so the hero stops tracking the fold. Pre-existing and unreachable from here.
     Below 1024 min-height is 0 and this does not bind. */
  min-height: calc(100vh - 224px);
  margin-top: calc(var(--head-h) * -1);
  display: flex;
  overflow: hidden;
}

.hero-photo { position: absolute; inset: 0; z-index: 0; }
/* On a phone the window is taller than it is wide, so cover crops horizontally and
   centre puts the standing sheet off-frame. The sheet's ink sits between 50% and
   83% of the frame's width [measured on hero.jpg], so the crop is anchored on it.
   No effect on wide windows, where the crop is vertical. */
.hero-photo img { object-position: 68% center; }

.hero-photo::before,
.hero-photo::after { content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none; }

/* THE SCRIM IS A CORNER, NOT A COVER. The type sits bottom-left and the subject runs
   centre-right, so two ramps that cover the whole frame dim the picture to buy contrast
   the type never needed: the asset is already near-black behind the headline (its left
   34% reads sRGB 0..37 under the page grade), and the scrim was pushed until the
   headline priced 18.4:1 against a floor of 4.5. These two multiply instead - a
   horizontal ramp and a vertical one, each reaching zero before the sheet - so the
   product is a bottom-left vignette and the subject is carried ungraded. What the
   remaining opacity buys is the grey lede, which needs its ground under L 0.072 for
   4.5:1 and is the binding constraint on this screen, not the headline. */
.hero-photo::before {
  background: linear-gradient(to right,
    rgba(22, 23, 25, 0.55) 0%, rgba(22, 23, 25, 0.42) 30%,
    rgba(22, 23, 25, 0.10) 58%, rgba(22, 23, 25, 0) 78%);
}

.hero-photo::after {
  background: linear-gradient(to top,
    rgba(22, 23, 25, 0.55) 0%, rgba(22, 23, 25, 0.34) 26%,
    rgba(22, 23, 25, 0.08) 55%, rgba(22, 23, 25, 0) 75%);
}


.hero-inner {
  position: relative;
  z-index: 2;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-top: calc(var(--head-h) + 88px);
  padding-bottom: 88px;
}

.hero-eyebrow { margin-bottom: 28px; font-size: var(--caption); color: var(--fg-3); }

/* The tail is a fixed 96px plus a 12px gap, so on a 350px column it leaves 242px for
   a label that needs 333 and the pill wrapped to two lines inside a capsule drawn for
   one - the first component a phone visitor sees. Below 430 the tail is not drawn and
   the label gets the whole column. */
@media (max-width: 430px) {
  .hero-eyebrow.rule-out::after { display: none; }
}
.hero-lede { margin-top: 28px; max-width: var(--measure-lede); }
.hero-action { margin-top: 40px; }

/* ── 01 the ledger ────────────────────────────────────────────────────
   A hairline-divided table that bleeds one gutter, so the container rule
   sits outside the text column and the type lands back on the page margin. */
.ledger { margin: 56px -24px 0; border-top: 1px solid var(--line); }

.lrow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 8ch;
  gap: var(--gutter);
  align-items: baseline;
  padding: 18px 24px;
  border-bottom: 1px solid var(--line);
}

.lrow-v { text-align: right; font-size: var(--caption); color: var(--fg-4); }
.lrow-built .lrow-v { color: var(--accent); }
.lrow-built .lrow-k { color: var(--fg); }

/* ── 02 the two-up ───────────────────────────────────────────────── */
.pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 40px var(--gutter);
  margin-top: 56px;
}

.pair-item { display: flex; flex-direction: column; gap: 16px; }
.pair-item .tx { max-width: none; }

/* ── 04 the delivery rail ─────────────────────────────────────────────── */
.rail { margin: 0 -24px; border-top: 1px solid var(--line); }

/* The pane is --aside wide, the same column the section heads use, because 300px
   could not hold its own content: step 02's two chips measure 151.6 and 166px with
   an 8px gap, which is 325.6px into 300, so they wrapped and that one row grew 32px.
   Measured pitch was 130 / 162 / 130 / 130 - the same component stacking in one row
   and not the next - and the 768 layout, where the pane is 568px, showed it whole.
   A component must not be more broken on a wider screen than on a narrower one. */
.step {
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr) var(--aside);
  gap: var(--gutter);
  align-items: baseline;
  padding: 28px 24px;
  border-bottom: 1px solid var(--line);
}

.step-t { display: grid; grid-template-columns: 176px minmax(0, 1fr); gap: var(--gutter); align-items: baseline; }
.step-n { color: var(--fg-4); }
.step .tx { max-width: none; }

/* The right 30% of every row was empty: the index shipped without the pane it was
   indexing. What the pane carries is what that step leaves in the building - and the
   label naming that column is printed once, at the head of the rail, rather than four
   times down it, where it was the only string on the page that repeated verbatim. */
.rail-h {
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr) var(--aside);
  gap: var(--gutter);
  margin: 56px -24px 0;
  padding: 0 24px 12px;
}

.rail-h .pane-k { grid-column: 3; }
.pane { display: flex; }
.pane-k { color: var(--fg-4); }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }

.chip {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--plate);
  color: var(--on-plate);
  font-family: var(--mono);
  font-weight: 500;
  font-size: var(--apparatus);
  letter-spacing: 0.12em;
}

/* ── the full-bleed photographic band ─────────────────────────────────── */
.bleed-band { position: relative; height: 460px; overflow: hidden; }
.bleed-band > .ph { position: absolute; inset: 0; }

/* ── 05 who: the one break in the section mould ───────────────────────
   FIVE section heads now, not six - the count in this comment outlived the section
   that was cut from under it. Four of the five open at the page margin. This one
   starts on column four instead, and what follows it is a key/value band rather than
   a component, so one opening is not the others.

   THE INDENT IS A GRID COLUMN, NOT A NUMBER AND NOT A FRACTION. It was var(--aside),
   a fixed 411px, which happens to land on column five at 1440 and is the same 411px
   at 1280: the head started at x515 at BOTH widths, and at 1024 the same offset
   wrapped the headline. A third of the content column was tried next and is no better
   where it matters: the headline's natural width is 582.3px at every width down to
   1024, so at 1024 a third-column indent starts it at x376 and leaves 320px of empty
   column left against 9.7px right - 33:1, worse than the 4:1 it replaced.

   Twelve columns of the page's own grid, with the head on column four, is the only
   value that holds at all three. Measured left/right empty split, ink to margin:
   1440 326/371.7 = 0.88, 1280 286/251.7 = 1.14, 1024 234/95.7 = 2.45, and one line at
   every width. Below 1024 the break is off and the head returns to the margin. */
.sec-head-flip { grid-template-columns: repeat(12, minmax(0, 1fr)); }
.sec-head-flip .sec-head-l { grid-column: 4 / -1; }

/* THE NAME IS THE HEADLINE. The page argues seniority in four places - "Senior-led,
   end to end", "No juniors on your work", "Senior hands", "a named owner" - and none
   of them is checkable without a person attached. Round two put the name in, and put
   it in mono at subhead size UNDER a 45px condensed line of positioning, so the one
   falsifiable fact in the section was set two steps beneath the abstraction it exists
   to support. The name takes the display slot and the abstraction becomes the
   standfirst; nothing else about the ruling moves. Still type, never a face, one line
   of standing, no link and no credentials. .who-name carries no size of its own - .d2
   does that - and stays as the hook CHECK 22 measures the painted glyphs on. */
.who-who { display: flex; flex-direction: column; gap: 6px; }

.facts {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin: 56px -24px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.fact {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 28px 24px;
  border-right: 1px solid var(--line);
}

.fact:last-child { border-right: 0; }
.fact-k { color: var(--fg-4); }
.fact-v { color: var(--fg); font-size: var(--subhead); line-height: 1.35;
          text-wrap: balance; }   /* four short cells; balance beats a one-word tail */

/* ── 06 work ──────────────────────────────────────────────────────────
   The page held one hard number and set it as prose in 15px mono. It is a number
   now, at the display-thin step the scale defines and the page never invoked, on a
   pinned ink plate inside the paper band - which is also the page's one transition
   that is not the binary ink/paper flip.

   The row of three bordered client cells is gone. It read as a tab switcher with no
   selected state, and two of the three names had nothing behind them. Three named
   rows, each carrying its own line, and the figure attributed to the client whose
   figure it is. */
/* ANCHORING BOTH ENDS JUST MOVED THE HOLE INTO THE MIDDLE. The first attempt pinned
   the figure at a fixed 320px on the left, so the content stopped at x967 in a plate
   running to x1384 and every pixel the plate gained between 1280 and 1440 went into a
   trailing void - 34% of the page's one hero component. The second attempt pinned the
   words to the left margin and the number to the bled right edge; measured, that
   reproduced the same defect one step in. Text box ended x623 and the figure began
   x1146: a 523.9px gap, 39.5% of the plate, against 363.9px at 1280. The plate gains
   160px between those widths and ALL 160 landed in the gap. Two anchored ends with a
   fixed 68ch measure between them cannot do anything else with the growth.

   THE THIRD ANSWER IS NOT A THIRD ARRANGEMENT. It is the geometry the page already
   uses for exactly this shape, one row below: .wrow is `260px minmax(0,1fr)`, a label
   column and a value column that absorbs the width. The proof is the same shape - a
   figure and the sentence it proves - so it takes the same grid. The figure reads
   first and sits on the left where the eye enters, the sentence starts at x364, which
   is the SAME x the three client sentences beneath it start at, and the separation
   between them is one gutter at every width because it is a gutter, not a remainder.

   ONE MEASURE, ON THE BLOCK. The lede was capped at 46ch of 16px type and the hedge
   under it at 68ch of 13.3px, so the LARGER type broke and the smaller one did not,
   and their right edges disagreed by 101px. The cap stays on the parent, where both
   children share it. */
.proof {
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  gap: 40px var(--gutter);
  align-items: center;
  margin: 56px -24px 0;
  padding: 40px 24px;
}

/* 14px of gap beside a 124px numeral is 11% of its cap height, so "MINUTES" sat in
   the 5's own optical shadow and read as a collision rather than a unit. The page
   already owns a horizontal separation value; the figure uses it. Ink to ink: 14px
   before, one gutter after. */
.proof-fig { grid-column: 1; grid-row: 1;
             display: flex; align-items: baseline; gap: var(--gutter); }
.proof-u { color: var(--fg-3); }
/* ONE MEASURE MEANS ONE COUNT OF CHARACTERS, NOT ONE COUNT OF PIXELS. 68ch was right
   until the lede grew one word: "Vehicle" took it to 604.8px against a 542.6px cap and
   the larger type broke while the smaller one under it did not. Widening the pixel cap
   to 78ch fixed the symptom and kept the mechanism, because ch resolves against THIS
   element's 13.3px while the sentence it binds is set at 16px - so 78ch was 78
   characters of the note and 64.84 of the lede, and the lede sat 8.03px inside it. A
   0.84-character margin is not a measure, it is a coincidence waiting to expire.

   The cap is now stated in the type it has to fit. This element carries the lede's own
   size so ch means the LEDE's character, --measure-prose keeps its 68, and the note
   declares its own size back. Measured: box 652.80px, lede 614.40px, headroom 38.40px =
   4.0 characters, against 0.84 before.

   What this does NOT do, and the earlier comment here claimed it did: make one number
   mean the same to both children. 652.80px is 68 characters of the 16px lede and 81.8
   of the 13.3px note. One pixel cap over two type sizes is always two character counts.
   What changed is WHICH child the count is stated in - the one that binds it, and the
   one that broke last time. The note has slack by construction because it is smaller
   type carrying a shorter sentence; if that ever inverts, this cap is the wrong one. */
.proof-t { grid-column: 2; grid-row: 1;
           display: flex; flex-direction: column; gap: 12px;
           font-size: var(--subhead);
           max-width: var(--measure-prose); }
.proof-t .proof-note { font-size: var(--body); }
.proof-t .sub { color: var(--fg); }

/* The hedge is what makes the figure safe to forward, so it is not the faintest
   thing on the page any more. */
.proof-note { color: var(--fg-3); }

.work { margin: 40px -24px 0; border-top: 1px solid var(--line); }

.wrow {
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  gap: var(--gutter);
  align-items: baseline;
  padding: 24px;
  border-bottom: 1px solid var(--line);
}

.wrow-k {
  font-family: var(--display-face);
  font-weight: 900;
  font-size: 17px;
  letter-spacing: var(--display-track);
  text-transform: uppercase;
  color: var(--fg);
}

.wrow-v { color: var(--fg-3); }

/* ── 07 close ─────────────────────────────────────────────────────────── */
/* Content-sized, the closing band came out 383px tall at 1440 - a 3.76:1 window on
   a 1.78:1 frame, which is 53% of the photograph thrown away. 560 takes it to
   2.57:1. Nothing here is fold-constrained; it is the last thing on the page. */
.close { position: relative; min-height: 560px; display: flex; overflow: hidden; }

.close-photo { position: absolute; inset: 0; z-index: 0; }

/* THE SUBJECT IS HARD LEFT AND THE DEFAULT CROP THREW IT AWAY. Same reasoning as the
   hero's 68%, opposite end of the frame: door.jpg's above-60 pixels run x2.8% to x30.1%
   with a median at 14.5%, so a centred cover crop on a phone - which keeps only the
   middle 47.6% of the width - kept a sliver of it and dropped the rest. Measured share
   of the rendered plate above level 60 at 390: 0.10% centred, against 23.41% at 1440.
   The last image before the ask, and on a phone it was 240px of near-black.
   Swept rather than guessed: 50% 0.10 / 35% 3.10 / 25% 5.65 / 15% 9.16 / 0% 14.98 at
   390, monotonic, and the same ordering at 768. Unconditional because it is inert where
   there is no horizontal overflow: at 1440 and 1280 the box is wider than 16:9 so the
   image is scaled by width and cropped vertically, and this value does nothing.
   Framing, not grading - the asset is untouched. */
.close-photo img { object-position: 0% center; }

.close-photo::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(to left,
    var(--ink) 0%, var(--ink) 40%, rgba(22, 23, 25, 0.88) 54%,
    rgba(22, 23, 25, 0.5) 70%, rgba(22, 23, 25, 0.18) 86%, rgba(22, 23, 25, 0.06) 100%);
}

.close-inner {
  position: relative;
  z-index: 2;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  gap: 28px;
  padding-top: var(--band);
  padding-bottom: var(--band);
  text-align: right;
}

.close-lede { max-width: var(--measure-lede); }

/* ── footer ───────────────────────────────────────────────────────────── */
.bleed { overflow: hidden; }

.wm-big {
  /* Ink runs 50..6518 in a 0..6540 box. At 110% the crop took 311 units off the B
     and 283 off the S and the name read BI ACKWERK S, which is a misspelling, not a
     bleed. The scale is derived instead: the INK spans the page exactly, less four
     units at each edge so the terminals reach the trim rather than stopping a pixel
     short. Four units is 1px at 1440 - a bleed, not a crop. */
  --w: calc(100% * 6540 / 6460);
  width: var(--w);
  margin-left: calc(var(--w) * -54 / 6540);
  fill: var(--fg);
  /* The entrance rise is 40% of this element's own height, and .bleed clips: at the
     default rise it cut 63px off a 173px letterform, every foot with it. The ink
     clears the box bottom by 127.5/1000 of the box, so the rise stays under that. */
  --rise: 24px;
}

.foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding-top: 40px;
  padding-bottom: 56px;
}

.mark-foot { --cap: 14px; fill: var(--fg); }
/* Items are flex children and the separator belongs to the item that FOLLOWS it, so
   a wrap can never end a line on a dangling middot. The tap padding used to inflate
   the line boxes to 40px of leading on 10px type, which read as three paragraphs on
   a phone; the height is on the box now, not on the leading. */
.meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
}

.meta-i { display: inline-flex; align-items: center; min-height: 30px; }
.meta a.meta-i { min-height: 44px; }
.meta-i + .meta-i::before { content: "·"; padding: 0 8px; color: var(--fg-4); }
.meta-plain { text-transform: none; }   /* an email is neither a sentence nor a name */
.meta a::after { bottom: 8px; }

/* ── motion ───────────────────────────────────────────────────────────
   The reveal DISPLACES, it never hides. Nothing on this page needs
   JavaScript to be readable, printable, screenshot or scraped. */
.js [data-enter] {
  transform: translateY(var(--rise));
  transition: transform var(--t-enter) var(--step);
  transition-delay: calc(var(--i, 0) * 80ms);
}

.js [data-enter].in { transform: translateY(0); }

/* ── responsive ───────────────────────────────────────────────────────── */
@media (max-width: 1180px) {
  :root { --pad: 56px; --band: 80px; --aside: 340px; }

  /* The pane went from 300 to 340 so its chips would fit, which took 40px off the
     step description and pushed three of the four rows to a third line while the
     fourth stayed on two - the pitch measured 139.6 / 118.1 / 139.6 / 139.6. The
     label column is the slack: "HANDOVER" is the longest and sets 91px here. */
  .step-t { grid-template-columns: 132px minmax(0, 1fr); }

}

/* THE 1024 FOLD, SCOPED TO 1024 AND NOT ONE PIXEL WIDER. This trim was written for a
   defect measured only at 1024x768 and was left firing across the whole 768-1180
   range. At 768 it cost 8px of hero (523.2 -> 515.2) and bought nothing - 0 ink rows
   cut either way - so it is fenced at the bottom now as well as the top. With the
   216 above it, the next headline's second line inked 715-779 against a 768 fold
   before and clears it whole after. */
@media (min-width: 1024px) and (max-width: 1180px) {
  .hero-inner { padding-top: calc(var(--head-h) + 80px); }
}

@media (max-width: 1023px) {
  :root { --d1: 60px; --d2: 36px; --d3: 22px; --fig: 32px; --fig-xl: 96px; }

  /* A landscape photograph in a portrait window is all crop and no subject. Below
     1024 the viewport can be taller than it is wide, so the hero stops being sized
     by the viewport and is sized by its own type again. Measured at 768x1024: the
     viewport-derived hero was 824px tall on a 768px column, a 0.93:1 window on a
     1.78:1 frame, and the standing sheet was entirely outside it. */
  .hero { min-height: 0; }

  .sec-head, .sec-head-flip { grid-template-columns: minmax(0, 1fr); gap: 40px; }
  .sec-head-flip .sec-head-l { grid-column: 1; }
  /* The 560px cap belongs to a paragraph measure, and it was on the wrapper: it
     capped the photograph too, so every section picture stopped 96px short of the
     margin its own body copy reached. The measure lives on .tx, where it started. */
  .sec-head-r .tx { max-width: 560px; }
  .pair { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .step, .rail-h { grid-template-columns: 64px minmax(0, 1fr); row-gap: 14px; }
  .rail-h .pane-k { grid-column: 2; }
  .step-t { grid-column: 2; grid-template-columns: minmax(0, 1fr); row-gap: 8px; }
  .pane { grid-column: 2; }
  /* TERMS wraps and the other three do not, so the two rows measured 103 and 124
     and the divider visibly missed the midpoint of a 2x2. */
  .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: 1fr; }
  .fact:nth-child(2n) { border-right: 0; }
  .fact:nth-child(-n+2) { border-bottom: 1px solid var(--line); }
  .proof { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .proof-fig, .proof-t { grid-column: 1; grid-row: auto; justify-self: start; }
  .bleed-band { height: 380px; }
  .hero-photo::before {
    background: linear-gradient(to right,
      rgba(22, 23, 25, 0.52) 0%, rgba(22, 23, 25, 0.40) 40%,
      rgba(22, 23, 25, 0.10) 70%, rgba(22, 23, 25, 0) 88%);
  }
  .close-photo::before {
    background: linear-gradient(to left,
      var(--ink) 0%, var(--ink) 52%, rgba(22, 23, 25, 0.9) 68%,
      rgba(22, 23, 25, 0.5) 84%, rgba(22, 23, 25, 0.14) 100%);
  }
}

@media (max-width: 899px) {
  .field { display: none; }
}

@media (max-width: 767px) {
  :root {
    --pad: 20px;
    --band: 56px;
    --d1: 44px;
    --d2: 29px;
    --d3: 20px;
    --fig: 28px;
    --fig-xl: 76px;
    --subhead: 15px;
    --cap: 14px;
    /* A phone block is full-width and tall, so 40% of it is a long way to
       travel and the stagger compounds it. 16px is the smallest multiple of
       the 8px base that still moves on every one of the twelve steps at 1x. */
    --rise: 16px;
  }

  .js [data-enter] { transition-delay: 0s; }

  /* Content-sized, these two bands leave the photograph a 130px strip and it stops
     being a photograph. A floor gives it a band worth looking at and puts the type
     under it on solid ink; the fold is not at risk on a phone, which scrolls. */
  .hero { min-height: 540px; }
  .hero-inner { padding-top: calc(var(--head-h) + 56px); padding-bottom: 56px; }
  /* The only ramp on the page that is doing real work: object-position anchors the
     phone crop on the sheet, so here the type IS over the subject and the floor is
     bought rather than assumed. It comes down, not off. */
  .hero-photo::before {
    background: linear-gradient(to top,
      rgba(22, 23, 25, 0.80) 0%, rgba(22, 23, 25, 0.70) 42%,
      rgba(22, 23, 25, 0.34) 68%, rgba(22, 23, 25, 0.08) 100%);
  }
  .hero-photo::after { background: none; }

  .close { min-height: 460px; }
  .close-inner { align-items: flex-start; justify-content: flex-end; text-align: left; }
  .close-photo::before {
    background: linear-gradient(to top,
      var(--ink) 0%, var(--ink) 46%, rgba(22, 23, 25, 0.8) 66%, rgba(22, 23, 25, 0.3) 100%);
  }

  .ledger, .rail, .rail-h, .facts, .proof, .work { margin-left: -12px; margin-right: -12px; }
  .lrow, .step, .rail-h, .fact, .proof, .wrow { padding-left: 12px; padding-right: 12px; }
  .facts { grid-template-columns: minmax(0, 1fr); }
  .fact { border-right: 0; border-bottom: 1px solid var(--line); }
  .fact:last-child { border-bottom: 0; }
  .wrow { grid-template-columns: minmax(0, 1fr); row-gap: 8px; }
  /* 262px of pane cannot hold two chips of 152 and 166, but it can hold two of 101
     and 116 - so one row wrapped and the next did not, with nothing explaining which.
     One per line settles that. THE CLAIM THAT USED TO SIT HERE WAS FALSE: it said row
     height became a function of how much each step leaves behind. Measured, it did
     not - steps 02 and 03 both carry two chips and came out 208.1 and 229.6, and
     steps 01 and 04 both carry one chip and came out 176.1 and 197.6. The 21.5px in
     each pair is the description wrapping to a third line, not the chips. So the
     rail sets its own rows equal here, the same way .facts does, and the pitch stops
     encoding a difference the reader cannot decode. */
  .chips { flex-direction: column; align-items: flex-start; }
  .rail { display: grid; grid-auto-rows: 1fr; }
  .bleed-band { height: 260px; }

  .foot { flex-direction: column; align-items: flex-start; gap: 24px; }

  /* Four items stacked is four paragraphs, not a contact line, and the baselines ran
     37 / 44 / 37 because only the middle gap carried two link paddings. Two per row
     fixes the leading, and the separator sits BETWEEN two items on the same line so it
     can never dangle at the end of one.

     THE SEPARATOR IS NOT DRAWN HERE AT ALL, AND THAT IS THE FIX. Hung off the even
     items it began at column two's edge, and a grid column is placed by the LONGER of
     the two items in it - so measured in pixels the middot sat 10px after "ARABIA" in
     row one and 39.2px after "7176" in row two, detached from the phone number.

     Three ways to move it were measured and all three cost more than the mark is
     worth. Trailing the odd item deletes it: item three is the tel: anchor and a::after
     is the link underline, so the separator inherits position:absolute and scaleX(0)
     and never paints - the pixel scan found no ink at all in row two, gap 52.2px.
     Right-aligning the odd items evens the mark but breaks the left margin by 29.2px,
     and the page margin is a stronger line than a middot. Forcing flex rows instead of
     grid columns wraps item two onto a line of its own, three rows not two.

     What a separator does in a wrapping line is tell you where one item ends. In a
     fixed two-by-two the columns already do that, so the mark is redundant and it goes.
     Above 768 the row is a single wrapping line and it keeps its middots. */
  .meta {
    display: grid;
    grid-template-columns: max-content max-content;
    justify-content: start;
    align-items: center;
    row-gap: 0;
    column-gap: var(--gutter);   /* the mark was carrying this; without it they abut at 1px */
  }

  .meta-i + .meta-i::before { content: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
    transition-delay: 0s !important;
  }
  .js [data-enter] { transform: translateY(0); }
}

/* ── print ────────────────────────────────────────────────────────────
   A dark page prints as a blank sheet unless it is redrawn for paper.
   Nothing here relies on the browser printing backgrounds, which is off by
   default, so the photographs and the scrims go and the type inverts. */
@media print {
  body { background: var(--paper); color: var(--ink); }

  .s-ink, .s-paper {
    --bg: var(--paper);
    --fg: var(--ink);
      --fg-3: var(--ink);
    --fg-4: var(--ink);
    --line: var(--ink);
    --accent: var(--ink);
    --on-accent: var(--paper);
    background: var(--paper);
    color: var(--ink);
  }

  .mark, .wm, .wm-big, .mark-foot { fill: var(--ink); }
  .btn { border-color: var(--ink); color: var(--ink); background: none; }
  .field, .bleed, .hero-photo, .close-photo, .bleed-band, .plate { display: none; }
  .close-inner { align-items: flex-start; text-align: left; }
  .lrow, .step, .fact, .wrow { break-inside: avoid; }
  /* Round one printed "LEAVES BEHIND" inside every row, so it could not be separated
     from what it labels. Printing it once, at the head of the rail, made a page break
     between the header and row 01 possible for the first time. */
  .rail-h { break-after: avoid; break-inside: avoid; }
  .js [data-enter] { transform: translateY(0); }
  a::after { display: none; }
}
