
/* ─────────────────────────── Fonts ───────────────────────────
   Both faces are served from this origin: no third-party request, no IP leaves
   with the page. Latin subset only (U+0000-00FF covers the accents this site sets).
   Archivo carries prose, Geist Mono carries metadata only. */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url(../fonts/archivo-latin-var.woff2) format('woff2');
}
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(../fonts/geist-mono-latin-var.woff2) format('woff2');
}

/* ─────────────────────────── Tokens ─────────────────────────── */
:root {
  --type-display: clamp(44px, 8.4vw, 132px);
  --type-display-sub: clamp(40px, 5.6vw, 84px);
  --type-statement: clamp(34px, 5.2vw, 80px);
  --type-h2: clamp(28px, 3.4vw, 48px);
  --type-h3: clamp(22px, 2.2vw, 30px);
  --type-card: clamp(19px, 1.4vw, 22px);
  --type-lead: clamp(16px, 1.25vw, 19px);
  --type-body: clamp(15px, 1.1vw, 17px);
  --type-numeral: clamp(160px, 22vw, 320px);
  --type-numeral-step: clamp(104px, 10vw, 152px);

  /* Fixed sizes: four steps. 17 forward links, 15 chrome and small copy, 13 sentence-case
     metadata, 11 uppercase labels (capitals read larger, so 11 sits beside 13). */
  --type-fwd: 17px;
  --type-chrome: 15px;
  --type-meta: 13px;
  --type-micro: 11px;
  --type-quote: var(--type-card);
  --type-quote-lead: var(--type-h3);
  --step-gap: 18px;
  --step-gap-in: 10px;
  --inc-gap: 12px;
  --tile-pad: clamp(14px, 1.6vw, 20px);
  --role-pad: 14px;
  --grid-gap: clamp(12px, 1.6vw, 20px);
  --role-facts: 220px;
  --role-gap: 56px;
  --type-knock: clamp(96px, 18vw, 230px);
  --people-strip: clamp(150px, 19vw, 240px);

  --font: 'Archivo', 'Helvetica Neue', Helvetica, sans-serif;
  --mono: 'Geist Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  --gutter: clamp(20px, 4vw, 44px);
  --block-gap: clamp(40px, 5vw, 64px);
  --block-step: clamp(28px, 3.4vw, 44px);
  --row-pad: 26px;
  --label-gap: 14px;
  --measure: 1280px;
  --hero-gap: clamp(32px, 4vw, 64px);
  --hero-split: 2.05;
  --pane-pad: clamp(20px, 3vw, 52px);
  --pane-gap: clamp(12px, 1.4vw, 20px);
  --pane-min: clamp(280px, 30vw, 400px);
  --pane-pad-in: clamp(22px, 2.6vw, 38px);
  --section-gap: clamp(100px, 13vw, 170px);
  --nav-pad: clamp(16px, 1.8vw, 24px);
  --logo-h: 32px;
  --logo-w: 88px;
  /* The sticky bar's height: everything that clears it or runs under it reads this. */
  --nav-h: calc(2 * var(--nav-pad) + var(--logo-h));
  --nav-row-gap: 24px;
  --tap: 44px;
  --bar-w: 18px;
  --bar-h: 21px;
  --type-drawer: 32px;
  --ease: cubic-bezier(.4, 0, .15, 1);
  --ease-settle: cubic-bezier(.55, 0, .1, 1);

  --warm-1: #FAD246;
  --warm-2: #F0663A;
  --warm-3: #E63329;
  --ramp: linear-gradient(90deg, var(--warm-1), var(--warm-2), var(--warm-3));
  --ramp-short: linear-gradient(90deg, var(--warm-2), var(--warm-3));
  /* The forward glyph cut to two humps, used as a list mark. Paint the colour through it with background. */
  --squiggle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 10'%3E%3Cpath d='M1,5 C3.7,0.8 6.3,9.2 9,5 C11.7,0.8 14.3,9.2 17,5' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
  --squiggle-w: 18px;
  --squiggle-h: 10px;
  /* Rows that carry the mark round their line height to whole pixels so the mark
     sits on the same sub-pixel as the text on every row. */
  --inc-lh: round(calc(1.3 * var(--type-meta)), 1px);
  --jd-lh: round(calc(1.6 * var(--type-body)), 1px);
  --legal-lh: round(calc(1.65 * var(--type-body)), 1px);
  /* The accent as text on the paper: #D2431D is 4.6:1, where the old #E8552B was
     3.6:1 and failed under the badge and the current nav link. */
  --accent-hi: #D2431D;
  --badge-border: color-mix(in oklab, var(--accent-hi), transparent 50%);

  --ground: #FFFFFF;
  --ground-sky: #CFDADD;
  --ground-sky-2: #E6E6E4;
  --ground-sky-warm: #F2EBE7;
  --ink: #17181A;
  --ink-2: #3E4249;
  /* The grey ramp, measured against the paper and against the tint the
     placeholders and cards use. Metadata reads as metadata by face and tracking,
     not by dropping under the contrast floor.
     --ink-soft   6.5:1 on paper, 5.7:1 on the tint
     --ink-muted  5.6:1 on paper, 4.9:1 on the tint
     --ink-meta   5.2:1 on paper, 4.6:1 on the tint and the card */
  --ink-muted: #64686F;
  --ink-soft: #5A5E66;
  --ink-meta: #696D75;
  --ghost: hsl(226 6% 84%);
  --knock-ghost: 1.67;
  --ghost-row: hsl(226 6% 78%);
  --ghost-mid: hsl(226 8% 60%); /* 3.0:1 on paper: below the ink, still visible */
  --rule: hsl(226 8% 92%);
  --tint: hsl(226 8% 94%);
  --tint-2: hsl(226 8% 96.5%);
  --card: hsl(226 8% 94.5%);
  --nav-shadow: 0 12px 32px rgba(45, 50, 66, 0.09);
  --link: var(--accent-hi);
  --link-hover: #E8552B;
  /* Graduated wash, weighted to the foot of the film: near-clear at the top,
     where the sky is brightest and the dark title already clears contrast, and
     close to paper at the bottom, where the horizon darkens and the page has to
     hand over to white. The stops trace an eased S rather than a straight ramp,
     so there is no visible knee; the climb is spent across the lower two
     thirds. Alphas are final — the layers using it sit at full opacity. */
  --scrim: linear-gradient(180deg,
    rgba(255,255,255,0.14) 0%,
    rgba(255,255,255,0.15) 12%,
    rgba(255,255,255,0.17) 24%,
    rgba(255,255,255,0.21) 34%,
    rgba(255,255,255,0.27) 43%,
    rgba(255,255,255,0.35) 51%,
    rgba(255,255,255,0.45) 59%,
    rgba(255,255,255,0.56) 67%,
    rgba(255,255,255,0.66) 74%,
    rgba(255,255,255,0.75) 81%,
    rgba(255,255,255,0.83) 88%,
    rgba(255,255,255,0.89) 94%,
    rgba(255,255,255,0.92) 100%);
  --marquee-veil: rgba(255, 255, 255, 0.6);

  --footer-ground: #17181A;
  --footer-ink: #F1F2F3;
  --footer-claim: #FFFFFF; /* Stock paper white, the page's own ground */
  --footer-ink-2: #C7C9CE;
  --footer-hairline: rgba(241, 242, 243, 0.12);
  --footer-meta: var(--footer-ink-2);
}

body.negative, .negative {
  --ground: #17181A;
  --ink: #F1F2F3;
  --ink-2: #C7C9CE;
  --ink-muted: #9A9DA4;
  --ink-soft: #A8ABB2;
  --ink-meta: #9A9DA4;
  --ghost: hsl(226 6% 32%);
  --knock-ghost: .617;
  --ghost-row: hsl(226 6% 28%);
  --ghost-mid: hsl(226 8% 41%); /* 3.0:1 on the negative ground */
  --rule: hsl(226 8% 26%);
  --tint: hsl(226 8% 19%);
  --tint-2: hsl(226 8% 23%);
  --card: hsl(226 8% 21%);
  --accent-hi: var(--warm-2);
  --nav-shadow: 0 14px 34px rgba(0, 0, 0, 0.5);
  --link: var(--accent-hi);
  --link-hover: #FAD246;
  --footer-ground: #101113;
}

/* ─────────────────────────── Reset and page ─────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { background: var(--ground); }
/* An inverted page re-points the roles on the body, so the page behind it is told
   separately; without this the overscroll gutter stays white. */
html:has(body.negative) { background: #17181A; }
html, body { min-height: 100%; }
body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font);
  -webkit-font-smoothing: antialiased;
}
img, svg, video { display: block; }
h1, h2, h3, p { margin: 0; }
a { color: var(--link); text-decoration: none; }
a:hover { color: var(--link-hover); }
::selection { background: var(--warm-1); color: #17181A; }
/* The ring takes its colour from the element it surrounds, so it re-points itself
   on every surface and the inverted footer needs no rule of its own: 16:1 on ink
   targets, 13:1 in the footer, 4.6:1 at its weakest (an accent-coloured link).
   No border-radius: the outline traces the corner the component already has. */
:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
/* Gradient-filled text paints through a clipped background, so its `color` is
   transparent and currentColor draws the ring in nothing. These three are the
   focusable ones, and each takes the colour its own surface can carry:
   --accent-hi is 4.6:1 on the paper and 4.9:1 on the negative ground, while the
   footer sits on its own dark ground where --warm-1 reads 11:1. */
.nav__link--cta:focus-visible, .fwd:focus-visible { outline-color: var(--accent-hi); }
.footer__cta:focus-visible { outline-color: var(--warm-1); }

/* Small text controls reach the 24px minimum target (WCAG 2.5.8) through an
   invisible hit area, so nothing visible moves: the box the type sits in is
   unchanged, only the area that answers a tap grows. */
.nav__links .nav__link, .footer__link, .fwd, .deck__step, .footer__pause, .footer__end-link { position: relative; }
.nav__links .nav__link::after, .footer__link::after, .fwd::after, .deck__step::after, .footer__pause::after, .footer__end-link::after {
  content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 24px; min-height: 100%;
  translate: 0 -50%;
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* The skip link holds a seat in the nav row beside the logo, in the metadata
   class, and stays clipped to nothing until a keyboard calls it. It is absolute
   in both states, so the row's space-between is untouched and the nav does not
   move when it arrives. First in the DOM, so it is still the first tab stop.
   The reveal is a clip swap, so it cannot be transitioned: it arrives at once,
   which is right for a control that was asked for. */
.skip {
  position: absolute; top: 50%; left: calc(var(--logo-w) + var(--nav-row-gap));
  translate: 0 -50%; z-index: 2;
  width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
  white-space: nowrap;
  color: var(--accent-hi); /* 4.6:1 on the paper, 4.9:1 on the negative ground */
  font-family: var(--mono); font-size: var(--type-micro); font-weight: 700;
  line-height: 1; letter-spacing: .12em; text-transform: uppercase;
}
.skip:focus { width: auto; height: auto; overflow: visible; clip-path: none; }

@keyframes marquee { to { transform: translateX(-50%); } }
@keyframes drift { from { background-position: 0% 44%; } to { background-position: 100% 56%; } }
@property --knock-p { syntax: '<number>'; inherits: true; initial-value: 0; }
@keyframes knock-drift { from { --knock-p: 0; } to { --knock-p: 1; } }
/* One control stills every ambient movement: both marquees, the quote rows, the
   people strip, the drifting sky and, in script, the deck and the quote thread. */
html.is-still .marquee__track,
html.is-still .quotes__row,
html.is-still .people__track,
html.is-still .h1--knock { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
  /* Pseudo-elements are named: * alone does not reach them. */
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ─────────────────────────── Layout ─────────────────────────── */
.wrap { max-width: var(--measure); margin: 0 auto; }
.section { padding: 0 var(--gutter) var(--section-gap); }
.section--open { padding-top: clamp(44px, 5.6vw, 84px); }
.section--top { padding-top: var(--section-gap); }
main { position: relative; z-index: 1; }

/* Section heads. A label over a heading, at the one label gap; where a lead sits
   beside them, the head is a row that wraps. Six sections declared this between
   them, in four different gaps. */
.process__head, .people__head, .thread__head, .close__head {
  display: flex; flex-direction: column; gap: var(--label-gap);
}
.work__head, .values__head, .studio__head, .roles__head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px 48px; flex-wrap: wrap;
}
.work__intro, .values__intro, .studio__intro, .roles__intro {
  display: flex; flex-direction: column; gap: var(--label-gap); max-width: 640px;
}

/* ─────────────────────────── Type ─────────────────────────── */
.eyebrow {
  font-family: var(--mono); font-size: var(--type-meta); letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-muted);
}
.meta { font-family: var(--mono); font-size: var(--type-meta); letter-spacing: .08em; color: var(--ink-meta); }
.micro {
  font-family: var(--mono); font-size: var(--type-micro); letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-meta);
}
.h1 {
  font-size: var(--type-display); line-height: 1; font-weight: 800;
  letter-spacing: -.035em; max-width: 14em; text-wrap: pretty;
}
.h1--sub {
  font-size: var(--type-display-sub); line-height: 1.02;
  letter-spacing: -.032em; max-width: 15em;
}
.h2 { font-size: var(--type-h2); font-weight: 700; letter-spacing: -.02em; }
.h2--balance { text-wrap: balance; }
.lead { font-size: var(--type-lead); line-height: 1.7; color: var(--ink-muted); max-width: 32em; }
.body { font-size: var(--type-body); line-height: 1.65; color: var(--ink-soft); text-wrap: pretty; }

.ramp {
  background: var(--ramp);
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
  padding-right: .06em; margin-right: -.06em;
}

.rev { color: var(--ink); }
html.js .rev { color: var(--ghost); transition: color 1s var(--ease); }
html.js .rev.is-on { color: var(--ink); }

@property --rev { syntax: '<number>'; inherits: false; initial-value: 0; }
html.js .rev.ramp {
  --rev: 0; transition: --rev 1s var(--ease);
  -webkit-text-fill-color: transparent;
  background-image: linear-gradient(90deg,
    color-mix(in oklab, var(--ghost), var(--warm-1) calc(var(--rev) * 100%)),
    color-mix(in oklab, var(--ghost), var(--warm-2) calc(var(--rev) * 100%)),
    color-mix(in oklab, var(--ghost), var(--warm-3) calc(var(--rev) * 100%)));
}
html.js .rev.ramp.is-on { --rev: 1; }
/* Each word carries its own ramp, so the full sweep would land deep red five times over.
   Showing the first 59% keeps the sentence in the yellow-to-orange half. */
.sentence .ramp { background-size: 170% 100%; background-position: 0% 50%; }

.defs { position: absolute; left: -9999px; width: 0; height: 0; }

/* Optical logo scale. Marks are normalised by perceived weight, not by the box the
   artwork came in. Two things pull against the box: density, where coverage of the
   ink box runs from .55 for the solid NHS lozenge to .19 for the Virgin roundel;
   and enclosure, where the part that carries the identity is only a fraction of
   the artwork. Measured, the lettering is .66 of the box for Microsoft but .34 for
   both BT and NatWest and .36 for Virgin, so matching their outer bounds to the
   set leaves their names half the size of everyone else's.
   Those three are set between box parity and letter parity: letter parity alone
   wants about 1.9x and turns the BT ring and the NatWest symbol into the largest
   objects in the row, so the enclosure is allowed to carry some of the presence
   and the lettering does the rest. How far along that line each one sits was read
   at both the 28px and 20px bases: BT and Virgin land at about three quarters of
   the way to letter parity, NatWest at about a third. NatWest sits lower because
   its symbol is a solid geometric block, which holds far more weight at a given
   height than the open BT ring or the open Virgin loop, so less of the correction
   has to come from scale.
   Components set --mark-base; the factor does the balancing. Microsoft and
   PeopleCert are the reference and sit at the base. */
/* The width/height attributes on each mark are its viewBox numbers, not its
   rendered size: the files are authored at width="100%", so they carry a ratio but
   no intrinsic size and the browser reads that ratio off the attributes. Rounded
   per-component pairs stretch the artwork; the viewBox pair states it exactly and
   this rule owns the size. */
.mark {
  --k: 1;
  width: auto; height: calc(var(--mark-base, 28px) * var(--k));
  max-width: 100%; object-fit: contain;
}
.mark--nhs { --k: .84; }
.mark--arup { --k: .88; }
.mark--mclaren { --k: .54; }
.mark--natwest { --k: 1.34; }
.mark--bt { --k: 1.68; }
.mark--virgin { --k: 1.61; }
/* Sized against the set above by aspect and ink density: the dense ASDA mark
   steps down, the light, open marks (Oxford, BHF, Lloyds) step up. */
.mark--asda { --k: .86; }
.mark--mitie { --k: .98; }
.mark--softcat { --k: 1.08; }
.mark--somerset-council { --k: 1.12; }
.mark--lloyds { --k: 1.26; }
.mark--british-heart-foundation { --k: 1.3; }
.mark--university-of-oxford { --k: 1.45; }

/* ─────────────────────────── Nav ─────────────────────────── */
/* The bar is sticky, so every anchor target clears its height plus a label gap. */
html { scroll-padding-top: calc(var(--nav-h) + 24px); }
.nav {
  position: sticky; top: 0; z-index: 60;
  padding: var(--nav-pad) var(--gutter);
  background: var(--ground);
  transition: box-shadow .5s ease;
}
.nav.is-scrolled { box-shadow: var(--nav-shadow); }
.nav__rule { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; pointer-events: none; }
.nav__progress {
  height: 100%; width: 0; border-radius: 999px;
  background: var(--ramp); transition: width .1s linear;
}
.nav__inner {
  position: relative; /* the skip link is absolute against this row */
  max-width: var(--measure); margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between; gap: var(--nav-row-gap);
}
.nav__logo { display: flex; align-items: center; }
.nav__logo img { height: var(--logo-h); width: var(--logo-w); }
.nav__links { display: flex; align-items: center; gap: clamp(16px, 2.2vw, 28px); }
/* Below the collapse point the bar keeps the logo and this toggle: three lines
   that fold into a cross. The control IS its 44px target, with the lines centred
   in it, so the site's one focus ring traces what the finger and the pointer can
   actually hit — nothing is live outside it. Negative margins pull the target's
   slack back out of the bar's height and off the gutter, so the lines still line
   up with the logo. The lines are the control's only label on screen; the word is
   there for anything that reads rather than looks. */
.nav__menu {
  display: none; place-items: center;
  width: var(--tap); height: var(--tap); padding: 0;
  margin: calc((var(--logo-h) - var(--tap)) / 2) calc((var(--bar-w) - var(--tap)) / 2) calc((var(--logo-h) - var(--tap)) / 2) 0;
  background: transparent; border: 0; cursor: pointer;
  color: var(--ink); transition: color .2s;
}
/* Hover-only ink on the toggle: a touch leaves :hover stuck on the button, so
   the cross would sit orange after the menu closed. Hover pointers only. */
@media (hover: hover) { .nav__menu:hover { color: var(--accent-hi); } }
/* The lines are the list mark. Each path is the squiggle from --squiggle with
   only its y moved, drawn at the size the lists draw it: 16 wide, stroke 1.8,
   which is the one number here that cannot be aliased to its source because it
   lives inside that data URI. Open: every line interpolates to the same flat
   path on the centre line and the outer two turn about it, so the closing is
   one shape becoming another rather than parts being moved. Both ends of the
   tween are the same three commands, which is what lets the browser do it. */
.nav__bars { display: block; width: var(--bar-w); height: var(--bar-h); }
.nav__bars path {
  fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round;
  transform-box: view-box; transform-origin: 9px 10.5px;
  transition: d .3s var(--ease), transform .3s var(--ease), opacity .2s linear;
}
.nav.is-open .nav__bars path { d: path("M1,10.5 C3.7,10.5 6.3,10.5 9,10.5 C11.7,10.5 14.3,10.5 17,10.5"); }
.nav.is-open .nav__bars path:first-child { transform: rotate(45deg); }
.nav.is-open .nav__bars path:nth-child(2) { opacity: 0; }
.nav.is-open .nav__bars path:last-child { transform: rotate(-45deg); }
.nav__link {
  font-size: var(--type-chrome); font-weight: 500; color: var(--ink-2);
  transition: opacity .3s ease, color .2s;
}
.nav__links .nav__link:focus-visible { opacity: 1; }
@media (hover: hover) {
  .nav__links:hover .nav__link { opacity: .34; }
  .nav__links .nav__link:hover { opacity: 1; }
  .nav__links a.nav__link:not(.nav__link--cta):hover { color: var(--accent-hi); }
}
.nav__link.is-current { color: var(--accent-hi); font-weight: 700; }
.nav__link--studio {
  display: inline-flex; align-items: center; gap: 7px;
  white-space: nowrap; color: var(--ink); font-weight: 600;
}
.nav__link--cta {
  font-weight: 700; white-space: nowrap;
  background: var(--ramp-short);
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
  padding-right: .06em; margin-right: -.06em;
}
.badge {
  font-family: var(--mono); font-size: var(--type-micro); letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-hi); border: 1px solid var(--badge-border);
  padding: 1px 5px; border-radius: 3px;
}

/* ─────────────────────────── Frames ─────────────────────────── */
.frame { position: relative; width: 100%; }
.frame__media { position: absolute; inset: 0; overflow: hidden; }
/* Photography fills its frame at the frame's ratio, so the crop is decided by
   the aspect-ratio class rather than by the file's own dimensions. */
.frame__media > img { width: 100%; height: 100%; object-fit: cover; }
.frame--21x9 { aspect-ratio: 21 / 9; }
.frame--16x9 { aspect-ratio: 16 / 9; }
.frame--4x3 { aspect-ratio: 4 / 3; }
.frame--1x1 { aspect-ratio: 1; }
.ph {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  background: repeating-linear-gradient(135deg, var(--tint) 0 9px, var(--tint-2) 9px 18px);
}
.ph__label {
  font-family: var(--mono); font-size: var(--type-meta); letter-spacing: .08em;
  color: var(--ink-meta); text-align: center;
}

/* ─────────────────────────── Forward links ─────────────────────────── */
/* One motion for every CTA. On hover or focus the squiggle straightens, then draws
   in equally from both ends to the same short line. The direction class only says
   where that line settles: .fwd stays level (another page or site), .fwd--down
   drops (further down this page), .fwd--up rises (back up it). Leaving runs in
   reverse: back to length first, then the wave returns. A .pane wakes the .fwd
   inside it. */
.fwd {
  --fwd-keep: .36;   /* share of the line left once drawn in */
  --fwd-shift: 0px;  /* where it settles, in glyph units */
  font-size: var(--type-fwd); font-weight: 700; white-space: nowrap;
  background: var(--ramp-short); background-size: 170% 100%; background-position: 0% 50%;
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
  padding-right: .06em; margin-right: -.06em;
  transition: background-position .5s var(--ease);
}
/* is-open follows aria-expanded a beat late, while is-moving has the button faded,
   so a .fwd--more line is never seen changing direction. */
.fwd--down, .fwd--more { --fwd-shift: 4.5px; }
.fwd--up, .fwd--more.is-open { --fwd-shift: -4.5px; }
.fwd__glyph {
  display: inline-block; width: 1.5em; height: .7em;
  margin-left: .34em; vertical-align: .02em; overflow: visible;
}
/* The wave's points travel to y = 7, so the straightened frame is an exactly
   straight line; squashing the group instead left a sub-pixel ripple that read as
   shimmer. The draw-in scales about the line's centre (15, 7), so both ends move
   in by the same amount. The gradient is in user space (#railWarm) so a flat path,
   with no height, still paints. */
.fwd__glyph path {
  transform-box: view-box; transform-origin: 15px 7px;
  transition: scale .38s var(--ease), translate .38s var(--ease), d .45s var(--ease) .14s;
}
.fwd:focus-visible, .pane:focus-visible .fwd { background-position: 100% 50%; }
.fwd:focus-visible .fwd__glyph path, .pane:focus-visible .fwd__glyph path {
  d: path('M1,7 C4.2,7 8.2,7 11.4,7 C14.6,7 18.6,7 21.8,7 C23.9,7 26.5,7 29,7');
  scale: var(--fwd-keep) 1; translate: 0 var(--fwd-shift);
  transition: d .45s var(--ease), scale .6s var(--ease-settle) .2s, translate .6s var(--ease-settle) .2s;
}
/* Hover only where hover is real, so a tap never leaves the line drawn in. */
@media (hover: hover) {
  .fwd:not(.fwd--inert):hover, .pane:hover .fwd { background-position: 100% 50%; }
  .fwd:not(.fwd--inert):hover .fwd__glyph path, .pane:hover .fwd__glyph path {
    d: path('M1,7 C4.2,7 8.2,7 11.4,7 C14.6,7 18.6,7 21.8,7 C23.9,7 26.5,7 29,7');
    scale: var(--fwd-keep) 1; translate: 0 var(--fwd-shift);
    transition: d .45s var(--ease), scale .6s var(--ease-settle) .2s, translate .6s var(--ease-settle) .2s;
  }
}
/* Where d cannot animate (Safari), the wave squashes flat instead; the draw-in
   and the settle still run on the path. */
@supports not (d: path('M0,0')) {
  .fwd__glyph g { transform-origin: 0 7px; transition: transform .45s var(--ease) .14s; }
  .fwd:focus-visible .fwd__glyph g, .pane:focus-visible .fwd__glyph g { transform: scaleY(.045); transition-delay: 0s; }
  @media (hover: hover) {
    .fwd:not(.fwd--inert):hover .fwd__glyph g, .pane:hover .fwd__glyph g { transform: scaleY(.045); transition-delay: 0s; }
  }
}
/* Held: a pressed CTA keeps the drawn-in pose its hover and focus reach, until
   the script hands it back. Pair with .fwd--inert so nothing else answers. */
.fwd--held { background-position: 100% 50%; }
.fwd--held .fwd__glyph path {
  d: path('M1,7 C4.2,7 8.2,7 11.4,7 C14.6,7 18.6,7 21.8,7 C23.9,7 26.5,7 29,7');
  scale: var(--fwd-keep) 1; translate: 0 var(--fwd-shift);
  transition: d .45s var(--ease), scale .6s var(--ease-settle) .2s, translate .6s var(--ease-settle) .2s;
}
@supports not (d: path('M0,0')) {
  .fwd--held .fwd__glyph g { transform: scaleY(.045); transition-delay: 0s; }
}
/* In-place action: a button that opens the rest of a post where it stands. The line
   settles towards where the copy goes: down to open, up once open. */
.fwd--more {
  display: inline-flex; align-items: center; min-height: 44px;
  padding: 0 .06em 0 0; border: 0; font-family: inherit; cursor: pointer;
  transition: background-position .5s var(--ease), opacity .2s var(--ease);
}
.fwd--more[hidden] { display: none; }
.fwd--more.is-moving { opacity: 0; transition-duration: .5s, .12s; }
.fwd--end { align-self: flex-end; }
.row-end { display: flex; justify-content: flex-end; }

/* ─────────────────────────── Hero: home ─────────────────────────── */
.hero {
  position: relative; overflow: hidden;
  min-height: 100vh;
  min-height: 100svh;
  margin-top: calc(-1 * var(--nav-h));
  display: flex; flex-direction: column;
  padding: clamp(40px, 6vh, 80px) var(--gutter) 0;
}
/* ─────────────────────────── Sky: the film behind the hero and the footer ─────────────────────────── */
/* One player per box, sized to the box and cropped to fill it, so there is
   nothing to clip and no stack to keep in order. The loop is the element's,
   and script sets no style here — only the is-ready class once a frame is
   genuinely on screen, which both skies fade in on. The box carries the
   footer's filter and mask. */
.sky { position: absolute; inset: 0; }
.sky video { width: 100%; height: 100%; object-fit: cover; }
/* The hero's static frame is the same picture as the film's first frame and is
   preloaded, so it is held as the box's own ground and the player simply fades
   over it. Opacity composites inside the box, so nothing is sampled beyond the
   left and right edges; a film that never decodes leaves the still frame in
   place rather than an entrance state. The hidden step is script's to undo, so
   it is scoped to js: without it the player is plainly visible. */
.hero__sky .sky { background: url(../images/sky.webp) center / cover no-repeat; }
html.js .hero__sky .sky video { opacity: 0; transition: opacity .3s var(--ease); }
html.js .hero__sky .sky.is-ready video { opacity: 1; }

.hero__sky { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
/* The wash that holds the dark title off the weather, now graduated rather than
   flat: see --scrim. The title band sits in the middle of the climb, where the
   veil is heavier than the old flat .5, so the 5.2:1 minimum recorded against
   --ink on this film is a floor, not a target. It is permanent. */
.hero__scrim { position: absolute; inset: 0; background: var(--scrim); z-index: 1; }
.hero__spacer { flex: 1; }
.hero__centre {
  position: absolute; left: 0; right: 0;
  top: var(--nav-h); bottom: 96px;
  display: flex; align-items: center;
  padding: 0 var(--gutter); z-index: 1; pointer-events: none;
}
.hero__centre .wrap { width: 100%; pointer-events: auto; }
.hero__cue-row { display: flex; justify-content: center; padding: 0 0 16px; position: relative; z-index: 1; }
.cue {
  background: transparent; border: 0; padding: 6px; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  /* Hidden is also out of the tab order, so an invisible cue is never focused. */
  opacity: 0; visibility: hidden;
  transition: opacity 1s var(--ease), visibility 0s linear 1s;
}
.cue.is-in { opacity: 1; visibility: visible; transition-delay: 0s; }
.cue.is-out { opacity: 0; visibility: hidden; transition-delay: 0s, 1s; }
.cue__label {
  font-family: var(--mono); font-size: var(--type-micro); font-weight: 600; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink); padding-bottom: 6px;
}
.cue__bar {
  width: 3px; height: 44px; border-radius: 999px;
  background: linear-gradient(180deg, var(--warm-1), var(--warm-2), var(--warm-3));
  transition: height .5s var(--ease);
}
.cue:hover .cue__bar, .cue:focus-visible .cue__bar { height: 64px; }
.cue.is-out .cue__bar { height: 0; transition-duration: 1s; }

.marquee {
  position: relative; z-index: 1;
  margin: 0 calc(-1 * var(--gutter));
  padding: 30px 0; overflow: hidden;
}
/* The logo bar and the offices strip are one band: the same veil and blur. */
.marquee, .panes__strip {
  background: var(--marquee-veil);
  -webkit-backdrop-filter: blur(16px) saturate(1.1);
  backdrop-filter: blur(16px) saturate(1.1);
}
.marquee__track {
  --mark-base: 28px;
  display: flex; align-items: center; width: max-content;
  animation: marquee 173s linear infinite; will-change: transform;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__track img {
  width: auto; max-width: 200px; object-fit: contain; flex: none;
  margin-right: 64px; opacity: .55; transition: opacity .3s;
}
.marquee__track img:hover { opacity: 1; }

/* ─────────────────────────── Hero: subpages ─────────────────────────── */
.hero-sub {
  display: grid; grid-template-columns: 1.05fr 1fr;
  gap: var(--hero-gap); align-items: center;
}
.hero-sub__copy, .hero-knock { display: flex; flex-direction: column; gap: clamp(20px, 2.2vw, 30px); }

/* ─────────────────────────── Say hello: the two panes ─────────────────────────── */
/* The band runs under the nav so the sky reaches the top edge, and it holds the
   full screen like the home hero: the panes on the optical centre, and the
   offices on a static strip at the foot, built like the home marquee. */
.panes {
  position: relative; overflow: hidden;
  container-type: inline-size;
  min-height: 100vh;
  min-height: 100svh;
  margin-top: calc(-1 * var(--nav-h));
  display: flex; flex-direction: column;
  padding: calc(var(--nav-h) + var(--pane-pad)) var(--gutter) 0;
}
.panes__centre { flex: 1; display: flex; align-items: center; padding-bottom: var(--pane-pad); }
/* Matched to the hero wash, graduated the same way. The pane grounds barely
   move with it: the veil dominates the mix behind them, and it is heavier at the
   foot of the panes than the flat 50% it replaces, so the paper pane's metadata
   holds the 5.1:1 recorded below. */
.panes__scrim { position: absolute; inset: 0; background: var(--scrim); z-index: 1; }
.panes__row {
  position: relative; z-index: 1; width: 100%;
  max-width: var(--measure); margin: 0 auto;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--pane-gap);
}
/* Two panes hold their pair down to 700px of band, where each is 344px wide and
   the display line still sets on two lines. Below that they stack. */
@container (max-width: 700px) { .panes__row { grid-template-columns: minmax(0, 1fr); } }
/* The panes are frames, so the geometry is the values tile's: cutPath in site.js
   puts a small 45 degree cut on the top-right and bottom-left at the default
   0.14 of the short side. The glass is the clipped layer; the copy sits above it
   in normal flow, so the pane grows with its content and nothing is ever cut. */
/* Each pane carries its own veil and its own tint of the sky behind it, at rest
   and under the pointer, so the glass states this once and neither tone is
   written twice. The paper pane thins its veil and doubles its blur; the ink
   pane holds almost no veil and takes its darkness from the backdrop instead,
   then thickens by the same step the paper pane opens by. The film's play disc
   is cut from the same paper glass and shares these rules. */
.pane {
  position: relative; isolation: isolate;
  display: flex; min-height: var(--pane-min);
  color: var(--ink);
}
.pane, .film__play, .sky-toggle {
  --veil: 50%;
  --veil-on: 26%;
  --glass-blur: 22px;
  --glass-blur-on: 44px;
  --glass-tint: saturate(118%);
  --glass-tint-on: saturate(118%);
}
.pane__glass, .film__disc, .sky-toggle::before {
  background: color-mix(in oklab, var(--ground) var(--veil), transparent);
  /* A clip discards a box shadow, so the lift is a filter, which follows the cut. */
  filter: drop-shadow(0 18px 34px rgba(45, 50, 66, .18));
  -webkit-backdrop-filter: blur(var(--glass-blur)) var(--glass-tint);
  backdrop-filter: blur(var(--glass-blur)) var(--glass-tint);
  transition: backdrop-filter .5s var(--ease), background-color .5s var(--ease);
}
.pane__inner {
  position: relative; z-index: 1; flex: 1;
  display: flex; flex-direction: column; justify-content: space-between;
  gap: clamp(20px, 2vw, 30px); padding: var(--pane-pad-in);
}
/* Metadata on the paper pane sits on the sky through a 50% veil, an effective
   ground of about #E3E7E9, where --ink-meta measured 4.1:1 and failed. Aliased
   up to --ink-soft, it is 5.1:1 at the top of the pane, where that number was
   taken. Re-measured across the whole pane band with the wash at .5, against
   the darkest row of this film, --ink-soft is 4.1:1: under the floor at the
   foot of the pane, and 4.3:1 at the wash's old .62. Recovering it means the
   veil up or these inks at full --ink, not the wash, which is matched to the
   hero's by instruction. Under the pointer both are 11.2:1. The ink pane
   needs no correction: 6.9:1. */
.pane:not(.pane--dark) { --ink-meta: var(--ink-soft); --ink-muted: var(--ink-soft); }
/* The ink pane is a tinted window rather than a sheet: a 10% veil over the sky
   darkened to .40, so what reads is the weather. Measured at the top of the
   pane, where the sky is brightest and the eyebrow sits, the resting ground is
   about #6E7176: the body copy is 2.6:1 and the warm forward label 1.5:1, both
   under the 4.5:1 floor, and 6.6:1 and 3.4:1 under the pointer. This pane is
   legible on hover and not at rest. It ships pending a decision on which way to
   recover it: this pane's copy set in full paper, or the veil taken back up. */
.pane--dark {
  --veil: 10%;
  --glass-tint: brightness(.4) saturate(130%);
  --glass-tint-on: brightness(.36) saturate(118%);
}
.pane--dark .pane__glass { filter: drop-shadow(0 18px 34px rgba(45, 50, 66, .26)); }
/* The pane is a link: without this, a:hover would turn its heading the link colour. */
.pane:hover, .pane:focus-visible { color: var(--ink); }
.pane__say { display: flex; flex-direction: column; gap: 14px; }
.pane .h2 { line-height: 1.05; }
.pane__say .body { max-width: 26em; }
/* The link sits right, so the bottom-left cut stays empty. */
.pane__foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 12px 16px; }
/* A thinner veil puts the paper pane's copy on the sky itself, where the soft
   inks fail, so the ink comes up with the veil: both roles point at --ink and
   nothing else is restated. The ink pane keeps every colour it has. */
.pane:not(.pane--dark):focus-visible {
  --ink-soft: var(--ink); --ink-muted: var(--ink); --ink-meta: var(--ink);
}
.pane .eyebrow, .pane .body { transition: color .5s var(--ease); }
.pane:focus-visible .pane__glass, .film__play:focus-visible .film__disc, .sky-toggle:focus-visible::before {
  background: color-mix(in oklab, var(--ground) var(--veil-on), transparent);
  -webkit-backdrop-filter: blur(var(--glass-blur-on)) var(--glass-tint-on);
  backdrop-filter: blur(var(--glass-blur-on)) var(--glass-tint-on);
}
@media (hover: hover) {
  .pane:not(.pane--dark):hover {
    --ink-soft: var(--ink); --ink-muted: var(--ink); --ink-meta: var(--ink);
  }
  .pane:hover .pane__glass, .film__play:hover .film__disc, .sky-toggle:hover::before {
    background: color-mix(in oklab, var(--ground) var(--veil-on), transparent);
    -webkit-backdrop-filter: blur(var(--glass-blur-on)) var(--glass-tint-on);
    backdrop-filter: blur(var(--glass-blur-on)) var(--glass-tint-on);
  }
}

/* The offices strip is the home marquee's construction, standing still: the same
   30px band, the same veil and blur, run out to both edges. */
.panes__strip {
  position: relative; z-index: 1;
  margin: 0 calc(-1 * var(--gutter));
  padding: 30px var(--gutter);
}
.where {
  max-width: var(--measure); margin: 0 auto;
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;
  gap: 8px clamp(16px, 2vw, 32px);
  min-height: 36px;
}
.where__row {
  font-style: normal; font-size: var(--type-meta); line-height: 1.5;
  color: var(--ink-soft); white-space: nowrap;
  /* Read from the painted strip at 2x: the line box centres, but cap top to
     baseline sat 1.75px high because the only ink below the baseline is the two
     descenders in “Kingdom”. 1.75px at 13px is .135em. */
  transform: translateY(.135em);
}
.where__org { margin-right: 3px; color: var(--ink); }
/* The pair holds one row, and the strip the logo bar's 96px (30px, a 36px row,
   30px), while the band is wide enough for both offices at 13px. Below that they stack from the left and
   the strip grows; below the pane collapse they give up the single line rather
   than run out of the band. */
@container (max-width: 1080px) { .where { justify-content: flex-start; } }
@container (max-width: 700px) { .where__row { white-space: normal; text-wrap: pretty; } }

/* ─────────────────────────── Hero: pause control ─────────────────────────── */
/* Set after the panes, whose glass rules it joins and then trims. The hero's pause control, the footer control's twin: a 32px disc of the panes'
   paper glass, flat, with the icon of the action in ink. The glass and its hover
   are the panes' own rules. The 44px button is the target and the disc its
   ::before; the disc's edge sits on the gutter, 10px above the logos or offices. */
.sky-toggle {
  position: relative; z-index: 1; display: grid; place-items: center;
  width: var(--tap); height: var(--tap); margin-right: -6px;
  padding: 0; border: 0; border-radius: 50%; background: none;
  color: var(--ink); cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.sky-toggle::before { content: ''; width: 32px; height: 32px; border-radius: 50%; filter: none; }
.sky-toggle::before, .sky-toggle svg { grid-area: 1 / 1; }
.sky-toggle:focus-visible { outline-offset: -4px; }
/* Positioned so the icon paints over the glass's backdrop layer. */
.sky-toggle svg {
  position: relative; width: 14px; height: 14px; fill: currentColor;
  transition: opacity .2s var(--ease), transform .35s var(--ease);
}
.sky-toggle path { stroke: currentColor; stroke-width: 1; stroke-linejoin: round; }
.sky-toggle__play, html.is-still .sky-toggle__pause { opacity: 0; transform: scale(.7); }
html.is-still .sky-toggle__play { opacity: 1; transform: none; }
.hero__cue-row > .sky-toggle { position: absolute; right: 0; bottom: 4px; }
/* Say hello: its own line between the panes and the offices, pulled up into the
   panes' foot padding; where that is shorter than the target, the strip moves down. */
.panes > .sky-toggle { align-self: flex-end; margin-top: calc(4px - var(--pane-pad)); margin-bottom: 4px; }
@media (forced-colors: active) { .sky-toggle::before { border: 1px solid ButtonText; } }

/* ─────────────────────────── Hero: knockout ─────────────────────────── */
.h1--knock {
  line-height: .93; letter-spacing: -.042em; max-width: 13em;
  --knock-pic: url(../images/sky.webp);
  background-image: var(--knock-pic);
  background-size: 150% auto; background-position: 0% 46%;
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
  padding-right: .06em; margin-right: -.06em;
  animation: drift 48s ease-in-out infinite alternate;
}
html.js .h1--knock.is-perword {
  background-image: none;
  animation-name: knock-drift;
}
html.js .h1--knock.is-perword .rev {
  background-image: var(--knock-pic);
  background-size: var(--knock-w) var(--knock-h);
  background-position:
    calc(var(--knock-dx) * var(--knock-p) - var(--wx))
    calc(var(--knock-y0) + var(--knock-dy) * var(--knock-p) - var(--wy));
  -webkit-background-clip: text; background-clip: text;
  padding-right: .06em; margin-right: -.06em;
}
html.js .h1--knock:not(.is-perword) {
  filter: grayscale(1) contrast(0) brightness(var(--knock-ghost));
  transition: filter 1s var(--ease);
}
html.js .h1--knock:not(.is-perword):not(:has(.rev:not(.is-on))) { filter: none; }
html.js .h1--knock .rev {
  -webkit-text-fill-color: transparent;
  filter: grayscale(1) contrast(0) brightness(var(--knock-ghost));
  transition: filter 1s var(--ease);
}
html.js .h1--knock .rev.is-on { filter: none; }

/* ─────────────────────────── The method, as one sentence ─────────────────────────── */
.sentence {
  font-size: var(--type-statement); line-height: 1.08; font-weight: 800;
  letter-spacing: -.03em; max-width: 17em; text-wrap: pretty;
}
.sentence__n {
  font-family: var(--mono); font-size: var(--type-meta); font-weight: 400; letter-spacing: .08em;
  color: var(--ink-meta); vertical-align: top; margin-right: .12em; position: relative; top: .35em;
}

/* ─────────────────────────── Process steps ─────────────────────────── */
.process { display: flex; flex-direction: column; gap: clamp(40px, 5vw, 64px); }
.process__body { display: flex; flex-direction: column; gap: clamp(28px, 3vw, 40px); }
.rail { position: relative; height: 25px; margin: -11px 0; }
.rail svg { width: 100%; height: 100%; overflow: visible; }
.rail__track { stroke: var(--rule); }
.steps {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(24px, 3vw, 40px) clamp(20px, 2.4vw, 32px);
}
.step { display: flex; flex-direction: column; }
.step .frame { position: relative; z-index: 1; }
.step__n {
  position: relative; z-index: 0;
  margin: -.20em 0 0 -.04em;
  font-size: var(--type-numeral-step); line-height: .82;
  font-weight: 800; letter-spacing: -.06em;
}
.step__title {
  margin-top: var(--step-gap); font-size: var(--type-card); font-weight: 700;
  letter-spacing: -.01em; line-height: 1.25; color: var(--ink); text-wrap: pretty;
}
.step__body { margin-top: var(--step-gap-in); }

/* Tile 00 is the numbered format, emptied: a ghost numeral and an includes list
   of gaps, sitting last in the grid. The whole tile is the link. */
.step--open { color: var(--ink); }
.step__n--open { color: var(--ghost); }
.inc--open .inc__item::before { background: var(--ghost); }

/* The squiggle list mark, one recipe for the three lists that carry it. Each row
   names its own whole-pixel line height as --mark-lh; the mark fills that line and
   is centred in it on a whole pixel. The colour is the list's own. */
.inc__item::before, .jd__item::before, .legal__list li::before {
  content: ""; align-self: start; width: var(--squiggle-w); height: var(--mark-lh);
  mask: var(--squiggle) 0 round(calc((var(--mark-lh) - var(--squiggle-h)) / 2), 1px) / var(--squiggle-w) var(--squiggle-h) no-repeat;
  -webkit-mask: var(--squiggle) 0 round(calc((var(--mark-lh) - var(--squiggle-h)) / 2), 1px) / var(--squiggle-w) var(--squiggle-h) no-repeat;
}
.inc { list-style: none; margin: var(--step-gap) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--inc-gap); transform: translateY(var(--snap, 0px)); }
.inc__item {
  --t: calc(var(--i, 0) / max(1, var(--n, 1) - 1));
  --mark-lh: var(--inc-lh);
  display: grid; grid-template-columns: 22px minmax(0, 1fr); column-gap: 10px; align-items: baseline;
}
.inc__item::before {
  background: color-mix(in srgb,
    color-mix(in srgb, var(--warm-1), var(--warm-2) calc(min(1, var(--t) * 2) * 100%)),
    var(--warm-3) calc(max(0, var(--t) * 2 - 1) * 100%));
}
.inc__label {
  font-family: var(--mono); font-size: var(--type-meta); letter-spacing: .04em;
  line-height: var(--inc-lh); color: var(--ink-meta); text-wrap: pretty;
}
html.js .inc__label { color: var(--ghost); transition: color 1s var(--ease); }
html.js .inc__item.is-on .inc__label { color: var(--ink-meta); }

/* ─────────────────────────── Testimonials ─────────────────────────── */
/* The film's palette read as one move of light: the cooler grey-blue of its upper
   band settling through neutral into warm off-white at the foot of the section. No
   image, no blur, no layer to composite. The cards carry their own ground, so no
   quote's contrast depends on this. */
.quotes {
  margin: 0 0 var(--section-gap); padding: clamp(56px, 7vw, 88px) 0; overflow: hidden;
  background: linear-gradient(180deg,
    var(--ground-sky) 0%, var(--ground-sky-2) 56%, var(--ground-sky-warm) 100%);
}
.quotes__head {
  max-width: var(--measure); margin: 0 auto clamp(32px, 3.8vw, 48px);
  padding: 0 var(--gutter); display: flex; flex-direction: column; gap: var(--label-gap);
}
/* Reading pace, not display pace: at ~45px a second a card takes about nine
   seconds to pass a given point, so a quote can be read without hovering. The
   two rows carry eight cards each, so they share one duration and travel at the
   same speed. */
.quotes__row { display: flex; width: max-content; animation: marquee 69s linear infinite; will-change: transform; }
.quotes__row--back { animation-direction: reverse; animation-duration: 69s; margin-top: 18px; }
.quotes:hover .quotes__row { animation-play-state: paused; }
/* The card grows with the copy: the quote text now sits on the card step of the
   ramp and the highlights on the h3 step, so the tile takes the same two steps
   up and the longest quote still sets four lines and no more. */
.quote {
  position: relative; flex: none; margin-right: 18px;
  width: clamp(320px, 26vw, 408px); height: clamp(252px, 21vw, 288px);
}
.quote .frame__media { background: var(--card); }
.quote__inner {
  width: 100%; height: 100%; padding: 28px 30px;
  display: flex; flex-direction: column; justify-content: space-between; gap: 16px;
}
.quote__text { font-size: var(--type-quote); line-height: 1.5; font-weight: 500; color: var(--ink-2); text-wrap: pretty; }
/* The emphasis carries the wash the Our work thread uses: warm-1 at 70%, one
   character of space inside each end, cloned across line breaks, and the
   sentence's closing full stop set inside it. The leading is the copy's own 1.5
   opened only by the wash's vertical padding, so blocks on consecutive lines
   clear each other and the quote still reads as one paragraph. The ramp cards
   already have the warm highlight as their ground, so their emphasis keeps
   weight alone. */
.quote__text b { font-weight: 700; color: var(--ink); }
.quote:not(.quote--lead) .quote__text b {
  line-height: 1.6; padding: .14em 1ch;
  background-color: color-mix(in oklab, var(--warm-1), transparent 30%);
  box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
/* The attribution is the client mark alone, set flush with the copy's right
   edge. The marks carry different proportions, so the row aligns them on their
   optical centre and lets each keep its own height off the shared base. */
.quote__by { display: flex; justify-content: flex-end; align-items: center; gap: 10px; }
.quote__by .mark { --mark-base: 24px; opacity: .55; transition: opacity .3s; }
.quote:hover .quote__by .mark { opacity: 1; }
/* The tile takes the same ramp the highlighted copy takes, and the same slice of
   it: the copy runs stretch the ramp to 170% and start it at 0%, so they show
   the first 59% of the walk and stop in the orange, short of #E63329. Stretched
   the same way the tile ends where they end. Ink on the visible range is 12.6:1
   at #FAD246 falling to 5.5:1, and 90deg keeps the pale end left where the meta
   lines sit. */
.quote--lead .frame__media {
  background: var(--ramp); background-size: 170% 100%; background-position: 0% 50%;
}
.quote--lead .quote__text, .quote--lead .quote__text b { color: #17181A; }
.quote--lead .quote__text {
  font-size: var(--type-quote-lead); line-height: 1.25; font-weight: 700; letter-spacing: -.02em;
}
/* On the warm ramp the muted step has less room to read, so the marks start
   closer to full ink and land on it under the pointer. */
.quote--lead .quote__by .mark { opacity: .72; }

/* ─────────────────────────── Our work ─────────────────────────── */
.work { display: flex; flex-direction: column; gap: clamp(32px, 4vw, 52px); }
/* The film selector: centred under the player. On a wide player the films are dots
   and the one playing has grown into its title; pointing at another dot grows it
   into its name. Every pip is the pill's full height in every state, so only its
   width and fill move, and it opens to its own measured width (--w, set by
   site.js) so opening and closing run the same distance on the same curve and a
   move from one dot to the next stays balanced. The items touch, so the row has
   no dead gaps; the 2px between pills is padding inside each item.
   Below 480px of player the roles split, because pointing cannot make a dot speak
   on touch: the current title stands still in the ink chip between two step
   arrows, and the dots drop beneath it as a position rail, the current one
   lengthened into a bar. Every dot and arrow is a --tap target around a smaller
   mark; a swipe on the film steps too. */
.sel { --pill: 36px; container-type: inline-size; }
.sel__wide { display: flex; justify-content: center; align-items: center; }
.sel__phone { display: none; }
@container (max-width: 479px) {
  .sel__wide { display: none; }
  .sel__phone { display: flex; flex-direction: column; align-items: center; gap: 2px; }
}
.sel__item {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--tap); min-height: var(--tap); padding: 0 1px;
  border: 0; background: none; cursor: pointer; font: inherit;
}
.sel__pip {
  position: relative; flex: none; display: inline-flex; align-items: center; justify-content: center; overflow: hidden;
  height: var(--pill); width: 10px; padding: 0; border-radius: 999px; background: transparent;
  font-size: var(--type-chrome); font-weight: 600; letter-spacing: -.005em; white-space: nowrap; color: transparent;
  transition: width .5s var(--ease), background-color .3s var(--ease), color .2s linear;
}
.sel__pip::before {
  content: ""; position: absolute; left: 50%; top: 50%; width: 10px; height: 10px; margin: -5px 0 0 -5px;
  border-radius: 50%; background: var(--ghost-mid); transition: opacity .2s linear;
}
.sel__item.is-on .sel__pip { width: var(--w, 10px); background: var(--ink-2); color: var(--ground); }
.sel__item.is-on { cursor: default; }
.sel__item.is-on .sel__pip::before,
.sel__item:not(.is-on):focus-visible .sel__pip::before { opacity: 0; }
.sel__item:not(.is-on):focus-visible .sel__pip { width: var(--w, 10px); background: var(--tint); color: var(--ink); }
@media (hover: hover) {
  .sel__item:not(.is-on):hover .sel__pip { width: var(--w, 10px); background: var(--tint); color: var(--ink); }
  .sel__item:not(.is-on):hover .sel__pip::before { opacity: 0; }
}
.sel__bar { display: grid; grid-template-columns: var(--tap) auto var(--tap); align-items: center; }
.sel__now {
  display: inline-flex; align-items: center; height: var(--pill); margin: 0 6px; padding: 0 16px; border-radius: 999px;
  background: var(--ink-2); color: var(--ground);
  font-size: var(--type-chrome); font-weight: 600; letter-spacing: -.005em; white-space: nowrap;
}
.sel__now span { text-box: trim-both cap alphabetic; }
.sel__step {
  position: relative; display: grid; place-items: center; width: var(--tap); height: var(--tap);
  padding: 0; border: 0; background: none; cursor: pointer; color: var(--ink-soft);
}
.sel__step::before {
  content: ""; position: absolute; left: 50%; top: 50%; width: var(--pill); height: var(--pill); translate: -50% -50%;
  border-radius: 50%; background: transparent; transition: background-color .3s var(--ease);
}
.sel__step svg { position: relative; width: 10px; height: 6px; }
.sel__step--prev svg { rotate: 90deg; }
.sel__step--next svg { rotate: -90deg; }
.sel__step:active { color: var(--ink); }
.sel__step:active::before { background: var(--tint); }
.sel__rail { display: flex; justify-content: center; }
.sel__dot { display: grid; place-items: center; width: var(--tap); min-height: var(--tap); padding: 0; border: 0; background: none; cursor: pointer; }
.sel__dot i { display: block; width: 8px; height: 8px; border-radius: 999px; background: var(--ghost-mid); transition: width .5s var(--ease), background-color .3s var(--ease); }
.sel__dot.is-on i { width: 22px; background: var(--ink-2); }
/* The film is click to load: the poster and a play button are the site's own, and
   nothing is requested from YouTube until the button is pressed. The player then
   fills the same clipped media box, so the frame's geometry and ring are untouched.
   The box is ink under the poster, so a slow or failed poster reads as a dark
   screen rather than a gap. */
.film { container-type: inline-size; background: var(--ink); }
.film__poster, .film > iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }
.film__poster { object-fit: cover; }
/* The play control is the whole picture, so the target is never smaller than the
   film; the mark is a disc of the Say Hello pane's paper glass with PLAY cut clean
   through it, so the word is the film itself, sharp inside the blur. Hover and
   focus make the pane's move: the veil clears as the blur deepens. The word is set
   as the forward links are, Archivo 700 at --type-fwd, and steps down to
   --type-chrome in a narrow player; the disc is sized in the word's em, so the
   balance holds at every size. The cut is an alpha mask the script draws in the
   loaded face (site.js, cutDisc); until it lands the disc is not shown, and where
   forced colours strip the glass, the word is drawn as plain text instead.
   The disc is rounded to whole pixels and the script snaps its position to the
   device pixel grid, so the mask is drawn one-to-one; the mask is its only edge. */
.film__play {
  position: absolute; inset: 0; display: grid; place-items: center;
  padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: clamp(var(--type-chrome), 2.4cqi, var(--type-fwd)); font-weight: 700;
}
.film__play[hidden] { display: none; }
.film__note { margin: 0; text-align: center; text-wrap: pretty; }
.film__play:focus-visible { outline-color: var(--accent-hi); outline-offset: -6px; }
.film__disc {
  display: grid; place-items: center; width: 5.2em; width: round(5.2em, 2px); aspect-ratio: 1;
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
}
html.js .film__disc:not(.is-cut) { visibility: hidden; }
.film__word { letter-spacing: .06em; text-transform: uppercase; color: transparent; }
@media (forced-colors: active) {
  .film__disc { -webkit-mask: none; mask: none; border: 2px solid ButtonText; border-radius: 50%; visibility: visible; }
  .film__word { color: ButtonText; }
}
.cases {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: clamp(30px, 4vw, 56px) clamp(22px, 2.6vw, 34px);
}
.cases figure { margin: 0; display: flex; flex-direction: column; gap: 16px; }
.cases figure.case--lead { grid-column: span 2; gap: 18px; }
.case__cap { display: flex; flex-direction: column; gap: 6px; }
.case__cap[hidden] { display: none; }
.case--lead .case__cap { gap: 8px; max-width: 38em; }
.case--stagger .case__cap { padding-left: var(--cut-end, 0px); }
.case__head { font-size: var(--type-card); font-weight: 600; letter-spacing: -.015em; color: var(--ink); text-wrap: pretty; }
.case--lead .case__head { font-size: var(--type-h3); line-height: 1.25; font-weight: 700; letter-spacing: -.015em; }
.case__desc { margin: 0; font-size: var(--type-lead); line-height: 1.5; color: var(--ink-soft); text-wrap: pretty; max-width: 34em; }

.nums { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(24px, 3.4vw, 64px); }
.num { display: flex; flex-direction: column; gap: 16px; container-type: inline-size; }
/* Scales with the column so the widest figure ("£500M+") stays inside it; all
   three columns are equal, so the numerals stay the same size as each other. */
.num__v {
  font-size: min(var(--type-numeral-step), 25cqi); font-weight: 800; letter-spacing: -.06em; line-height: .85;
  margin-left: -.04em; padding-right: .06em; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.num__cap { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; border-top: 3px solid var(--rule); padding-top: 14px; }
.num__line { font-size: var(--type-body); font-weight: 600; letter-spacing: -.01em; line-height: 1.35; text-wrap: pretty; }

/* ─────────────────────────── Kind words, threaded ─────────────────────────── */
.section--thread {
  --thread-gap: clamp(34px, 3.4vw, 52px);
  padding-bottom: calc(var(--section-gap) - var(--thread-gap));
}
.thread { display: flex; flex-direction: column; gap: clamp(30px, 3.6vw, 48px); }
.thread__band { position: relative; padding: var(--thread-gap) 0; }
.thread__wave {
  position: absolute; left: 50%; top: 0; transform: translateX(-50%);
  width: 120px; height: 100%; overflow: visible;
}
.thread__track { stroke: var(--rule); }
.thread__qs {
  position: relative; width: min(100%, 880px); margin: 0 auto;
  display: flex; flex-direction: column; gap: var(--thread-gap);
}
/* A full logo is four times the width of the square it replaces, which no longer
   fits beside the quote in a half-width column and cannot be cleared by sliding
   the text sideways. The mark takes its own line under the quote at the row's
   outer edge, and the travel becomes the rise the narrow layout already used, so
   one motion now serves both widths. */
.thread__q {
  position: relative; width: calc(50% - 70px); color: var(--ink);
  display: flex; flex-direction: column; gap: 12px; align-items: flex-end;
}
html.js .thread__q { color: var(--ink-muted); transition: color .5s; }
html.js .thread__q.is-current,
html.js .thread__q:hover,
html.js .thread.is-still .thread__q,
html.js.is-still .thread__q { color: var(--ink); }
.thread__q--right { align-self: flex-end; align-items: flex-start; }
/* The mark waits a row-gap low and comes up as the row becomes current, in step
   with the quote taking full ink. */
.thread__mark { --mark-base: 18px; }
html.js .thread__mark {
  opacity: 0; transform: translateY(9px);
  transition: opacity .5s var(--ease), transform .5s var(--ease);
}
/* The quote carries no ground of its own, so the row works on any surface and
   never changes height. Two weights, as the homepage quotes are set: the copy in
   regular and the phrase the client emphasised in bold. The pair is pushed to the ends
   of Archivo's range, 400 against 800, because at body size a nearer pair read as one
   weight. Weight alone carries it, so the row's own muted-to-ink transition still
   governs the colour of the whole quote.
   The emphasis also carries a wash, full line height, cloned across line breaks.
   At rest it is the neutral card tint; as the row becomes current or takes the
   pointer it warms to the highlight, on the same .5s the row's ink takes. The
   wash holds one character of space inside each end, which is also the gap the
   surrounding words read against, and the sentence’s closing punctuation sits
   inside the emphasis rather than beside it; the leading opens so runs on
   consecutive lines do not touch. */
.thread__text {
  display: block; width: 100%;
  font-size: var(--type-body); font-weight: 400; line-height: 1.4;
  letter-spacing: -.01em; text-wrap: balance; /* Short quotes: even lines, no single-word last line. */
}
.thread__text b {
  font-weight: 800; letter-spacing: -.016em; line-height: 1.9;
  padding: .14em 1ch; margin: 0;
  background-color: var(--tint);
  box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
html.js .thread__text b { transition: background-color .5s; }
html:not(.js) .thread__text b,
html.js .thread__q.is-current .thread__text b,
html.js .thread__q:hover .thread__text b,
html.js .thread.is-still .thread__text b,
html.js.is-still .thread__text b {
  background-color: color-mix(in oklab, var(--warm-1), transparent 30%);
}
.thread__q--left .thread__text { text-align: right; }
html.js .thread__q.is-current .thread__mark,
html.js .thread__q:hover .thread__mark,
html.js .thread.is-still .thread__mark,
html.js.is-still .thread__mark { opacity: 1; transform: none; }

/* ─────────────────────────── Studio ─────────────────────────── */
.studio { display: flex; flex-direction: column; gap: var(--block-gap); }
/* Beside a head rather than under it, so it takes a wider measure than .lead.
   Declared after .lead, which it overrides. */
.values__lead, .studio__lead, .roles__lead, .work__lead { flex: 1 1 320px; max-width: 34em; }

.deck { display: flex; flex-direction: column; gap: 12px; outline-offset: 6px; }
.deck__media { background: var(--card); }
.deck__slide {
  position: absolute; inset: 0;
  opacity: 0; transition: opacity .5s var(--ease);
}
.deck__slide.is-on { opacity: 1; }
.deck__slide > img { width: 100%; height: 100%; object-fit: cover; }
.deck__bar { display: flex; align-items: center; gap: 16px; padding-left: var(--cut-end, 0px); }
.deck__rail { flex: 1 1 auto; min-width: 0; position: relative; height: 25px; margin: -11px 0; }
/* The counter holds its one line; the rail is what gives way when the bar is tight. */
.deck__bar > .meta { flex: none; white-space: nowrap; }
.deck__rail svg { display: block; width: 100%; height: 100%; overflow: visible; }
.deck__track { stroke: var(--rule); }
.deck__nav { display: flex; align-items: center; gap: 8px; flex: none; }
.deck__step {
  font: inherit; letter-spacing: inherit; color: var(--ink-meta);
  background: transparent; border: 0; padding: 0; cursor: pointer;
  transition: color .2s;
}
.deck__step:hover, .deck__step:focus-visible { color: var(--ink); }
.deck__slash { color: var(--ghost-mid); }
.pkgs-block { display: flex; flex-direction: column; gap: var(--block-step); container-type: inline-size; }
.count {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(32px, 4vw, 64px); align-items: start;
}
@container (max-width: 704px) { .count { grid-template-columns: minmax(0, 1fr); } }
.count__col { display: flex; flex-direction: column; gap: clamp(24px, 3vw, 36px); }
.count__numeral {
  font-size: var(--type-numeral); line-height: .82; font-weight: 800;
  letter-spacing: -.06em; margin-left: -.04em;
}
/* The label and the list it heads sit one label gap apart, the same distance
   every other label in the page keeps from its content. */
.count__so { display: flex; flex-direction: column; gap: var(--label-gap); }
.parts { display: flex; flex-direction: column; gap: 10px; }
.part { display: flex; align-items: baseline; gap: 12px; color: var(--ink); }
html.js .part { color: var(--ghost); transition: color .3s; }
html.js .part.is-on { color: var(--ink); }
html.js .part.is-current { color: var(--accent-hi); }
.part__n { font-family: var(--mono); font-size: var(--type-micro); letter-spacing: .08em; width: 28px; flex: none; color: color-mix(in oklab, currentColor, var(--ground) 40%); }
.part__label { font-size: var(--type-body); font-weight: 500; }
.pkgs { display: flex; flex-direction: column; }
.pkgs__rule { height: 1px; background: var(--rule); }
.pkg {
  display: flex; flex-direction: column; gap: 10px; align-items: flex-start;
  padding: var(--row-pad) 0; width: 100%; text-align: left; cursor: pointer;
  background: transparent; border: 0;
  /* No rule per row: the block's own top and bottom rules hold the four options. */
  font-family: inherit; color: var(--ink);
}
html.js .pkg { color: var(--ghost-row); transition: color .5s; }
html.js .pkg.is-on { color: var(--ink); }
.pkg__name { display: flex; align-items: baseline; gap: 14px; }
.pkg__n { font-family: var(--mono); font-size: var(--type-meta); letter-spacing: .08em; color: color-mix(in oklab, currentColor, var(--ground) 40%); }
.pkg__title { font-size: var(--type-h3); font-weight: 700; letter-spacing: -.02em; line-height: 1.1; }
.pkg__desc { font-size: var(--type-body); line-height: 1.6; color: color-mix(in oklab, currentColor, var(--ground) 28%); max-width: 28em; text-wrap: pretty; }
.pkgs__cta { display: flex; justify-content: flex-end; padding-top: var(--row-pad); }

/* ─────────────────────────── Values ─────────────────────────── */
.values { display: flex; flex-direction: column; gap: clamp(40px, 5vw, 64px); }
.values__block { display: flex; flex-direction: column; gap: clamp(20px, 2.4vw, 28px); }
.lines {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 24px clamp(20px, 2.4vw, 32px);
}
.line {
  padding-top: 14px; border-top: 1px solid var(--rule);
  font-size: var(--type-card); font-weight: 600; letter-spacing: -.01em; line-height: 1.35; text-wrap: pretty;
}
.values__grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--grid-gap); align-items: start;
}
.value { aspect-ratio: 3 / 4.4; }
.value--drop { margin-top: clamp(24px, 3.75vw, 48px); }
.value__media {
  background: color-mix(in oklab, var(--stop) 16%, var(--ground));
  display: flex; flex-direction: column; justify-content: space-between;
  padding: var(--tile-pad);
}
.value__n {
  font-size: clamp(56px, 7.2vw, 92px); line-height: .8; font-weight: 800;
  letter-spacing: -.06em; margin-left: -.04em; color: var(--stop);
}
.value__text { display: flex; flex-direction: column; gap: 6px; }
.value__title { font-size: var(--type-card); font-weight: 700; letter-spacing: -.01em; line-height: 1.2; }
.value__body { font-size: var(--type-meta); line-height: 1.5; color: var(--ink-soft); text-wrap: pretty; }

/* ─────────────────────────── Our people ─────────────────────────── */
.people { display: flex; flex-direction: column; gap: clamp(28px, 3.4vw, 44px); }
.people__band { margin: clamp(40px, 5vw, 64px) calc(-1 * var(--gutter)) 0; display: flex; flex-direction: column; }
.people__wide { aspect-ratio: 3 / 1; }
.people__wide img { width: 100%; height: 100%; object-fit: cover; }
.people__strip { position: relative; isolation: isolate; height: var(--people-strip); overflow: hidden; }
.people__track {
  display: flex; height: 100%; width: max-content;
  animation: marquee 80s linear infinite; will-change: transform;
}
@media (hover: hover) { .people__strip:hover .people__track { animation-play-state: paused; } }
.people__pic { display: block; height: 100%; width: auto; flex: none; margin-right: -1px; object-fit: cover; }
.people__knock {
  position: absolute; inset: 0; background: #FFFFFF; mix-blend-mode: screen;
  display: flex; align-items: center; padding: 0 var(--gutter);
}
.people__word {
  font-size: var(--type-knock); line-height: .9; font-weight: 800;
  letter-spacing: -.05em; color: #000000; white-space: nowrap;
}
.people__band--word { container-type: inline-size; }
.people__band--word .people__strip {
  --knock-word: calc((100cqw - 2 * var(--gutter)) / 4.2);
  height: calc(var(--knock-word) * .82);
}
.people__band--word .people__word { font-size: var(--knock-word); }

/* ─────────────────────────── Roles ─────────────────────────── */
.roles { display: flex; flex-direction: column; gap: clamp(28px, 3.4vw, 44px); container-type: inline-size; }
/* The five roles are the ramp. Each takes one stop of the warm walk and the bands
   sit flush, no gaps, so the list is the gradient the site signs everything with
   — held inside the measure, in the column the rest of the page is written in.
   The stop is on the button itself: the band is the target, so there is no tile
   behind it and no grey to fill.
   The walk runs the other way to the values grid above: deepest at the top,
   palest at the foot, so the weight lands beside the heading and the list fades
   into the page rather than darkening into the section below it.
   Each band is the values tile's mix taken to the midpoint between that tile's
   16% and the numeral's full stop. The tile only has to hold two lines of type;
   the band has to read as a colour field and still let a white sheet sit inside
   it, and 16% left the sheet with no edge. --ink measures 8.2:1 on the deepest
   band and 14.4:1 on the palest, so the index runs in the ink as well and stays
   metadata by face and tracking. Under the pointer, on focus and while open the
   band steps its title in by one inset — position, not colour, since the colour
   is the content here. One property moves, on the page's easing.
   The description lands on a paper sheet inside the colour: a frame with the
   corner in px, because the sheet grows with the description it holds and the
   corner has to stay put. */
.roles__grid {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 0;
}
.role { display: flex; flex-direction: column; }
.role__h { margin: 0; width: 100%; }
.role__head {
  appearance: none; margin: 0; border: 0; font: inherit;
  text-align: left; cursor: pointer; position: relative; z-index: 1;
  width: 100%; display: grid; grid-template-columns: 4ch minmax(0, 1fr);
  align-items: center; gap: clamp(14px, 1.6vw, 22px);
  padding: clamp(15px, 1.7vw, 22px) var(--tile-pad);
  background: color-mix(in oklab, var(--stop) 58%, var(--ground)); color: var(--ink);
  transition: padding-left .5s var(--ease);
}
.role:hover .role__head,
.role:has(.role__head:focus-visible) .role__head,
.role.is-open .role__head { padding-left: calc(2 * var(--tile-pad)); }
.role__n {
  font-family: var(--mono); font-size: var(--type-meta);
  letter-spacing: .08em; color: var(--ink);
}
.role__title { font-size: var(--type-h3); font-weight: 700; letter-spacing: -.02em; line-height: 1.2; text-wrap: pretty; }
/* This is the box whose height is animated, so it carries nothing of its own:
   no padding, since with a border box an inset is a floor the movement cannot
   get under and the close ended on a step of it. While the height is moving the
   box is contained — its size is its own, so the description inside is not laid
   out again on every frame — and the scroll is told not to re-anchor to
   something inside a box that is closing. */
.role__body { background: color-mix(in oklab, var(--stop) 58%, var(--ground)); overflow-anchor: none; }
.role__body.is-animating { contain: strict; }
/* The sheet holds the inset instead, evenly on all four sides. */
.role__sheet {
  --sheet-pad: clamp(22px, 2.6vw, 36px);
  /* auto, not the 100% every .frame takes: the margin is inside the band. */
  width: auto; margin: var(--tile-pad);
}
.role__sheet > .frame__media { background: var(--ground); }
/* The cut takes the sheet's bottom left corner, where the description ends, so
   the last line goes to the other end: the apply link is set to the right, the
   way the forward links at the end of a row are. The floor is then one inset
   like every other side, instead of a hundred pixels of clearance. */
.role__sheet-in {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: var(--role-facts) minmax(0, 1fr);
  column-gap: var(--role-gap); align-items: start;
  padding: var(--sheet-pad);
}
.role__facts {
  margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 4px;
  font-family: var(--mono); font-size: var(--type-meta); letter-spacing: .08em; line-height: 1.5; color: var(--ink);
}
.jd { display: flex; flex-direction: column; gap: 14px; max-width: 76ch; }
.jd__h {
  margin: 0; font-family: var(--mono); font-size: var(--type-micro); font-weight: 400; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-meta);
}
.jd__h:not(:first-child) { margin-top: 20px; }
.jd__p { font-size: var(--type-body); line-height: 1.7; color: var(--ink-2); text-wrap: pretty; }
.jd__list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 9px; transform: translateY(var(--snap, 0px)); }
.jd__item {
  --mark-lh: var(--jd-lh);
  display: grid; grid-template-columns: 22px minmax(0, 1fr); column-gap: 10px; align-items: baseline;
  font-size: var(--type-body); line-height: var(--jd-lh); color: var(--ink-2); text-wrap: pretty;
}
.jd__item::before { background: var(--ink-2); }
.jd__apply { display: flex; flex-direction: column; gap: 14px; margin-top: 20px; }
.jd__wave { display: block; width: 100%; height: 14px; overflow: visible; margin-bottom: 14px; }
.jd__track { stroke: var(--rule); }
.jd__apply .fwd { align-self: flex-end; }
@container (max-width: 900px) {
  .role__sheet-in { grid-template-columns: minmax(0, 1fr); row-gap: 18px; }
  .role__facts { flex-direction: row; flex-wrap: wrap; gap: 4px 18px; }
}

/* ─────────────────────────── What’s new: the feed ─────────────────────────── */
.section--feed { overflow: clip; }
.feed {
  --feed-crop: min(10vw, 128px);
  display: flex; flex-direction: column; gap: clamp(56px, 7vw, 96px);
  overflow: clip;
}
.feed__new { display: flex; flex-direction: column; gap: clamp(48px, 6vw, 80px); }
.feed__lead {
  display: grid;
  grid-template-columns:
    calc((100% - var(--feed-crop) - var(--hero-gap)) / var(--hero-split) + var(--feed-crop))
    minmax(0, 1fr);
  gap: clamp(28px, 3.4vw, 48px); align-items: center;
  margin-left: calc(-1 * var(--feed-crop));
}
.feed__lead .ph { padding-left: var(--feed-crop); }
.feed__post { display: flex; flex-direction: column; gap: 16px; align-items: flex-start; }
.feed__title {
  font-size: var(--type-h2); line-height: 1.06; font-weight: 800;
  letter-spacing: -.032em; text-wrap: balance;
}
.feed__extract { max-width: 46ch; }
.feed__old {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 2.4vw, 32px); align-items: start;
}
.feed__card { display: flex; flex-direction: column; gap: 12px; }
.feed__card-text { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.feed__card-title {
  font-size: var(--type-card); line-height: 1.25; font-weight: 700;
  letter-spacing: -.015em; text-wrap: pretty;
}
.feed__card .fwd { font-size: var(--type-chrome); }
.feed__card-note { max-width: 42ch; }
/* The rest of a longer post. The script folds it so that the closed posts in a row end
   on one line, and sets --veil to how much of the fade the copy still hidden calls for:
   none once the last line shows. The fade is the fold's last three lines, 4.95em of the
   body's own em. Without the script the post stands open. */
.feed__card-more {
  position: relative; align-self: stretch; overflow: hidden;
  display: flex; flex-direction: column; gap: 8px; align-items: flex-start;
  font-size: var(--type-body); line-height: 1.65;
}
.feed__card-more::after {
  content: ''; position: absolute; inset: auto 0 0; height: 4.95em; pointer-events: none;
  opacity: var(--veil, 0);
  background: linear-gradient(color-mix(in srgb, var(--ground) 15%, transparent), color-mix(in srgb, var(--ground) 80%, transparent) 55%, var(--ground));
}

/* ─────────────────────────── What's new: the stack ─────────────────────────── */
.stack {
  --sheet: clamp(18px, 2vw, 26px);
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(28px, 3.4vw, 48px); align-items: center;
}
/* Stacks with the rest of the page (.steps, .cases, .nums), not at its own width. */
@media (max-width: 860px) { .stack { grid-template-columns: minmax(0, 1fr); } }
.stack__pile { position: relative; aspect-ratio: 1; }
.stack__sheet { position: absolute; top: 0; left: 0; width: calc(100% - 2 * var(--sheet)); }
.stack__sheet--2 { transform: translate(var(--sheet), var(--sheet)); }
.stack__sheet--3 { transform: translate(calc(2 * var(--sheet)), calc(2 * var(--sheet))); }

/* ─────────────────────────── Legal pages ─────────────────────────── */
.legal-head { display: flex; flex-direction: column; gap: clamp(18px, 2vw, 28px); align-items: flex-start; }
/* The lead runs the full column here, where the page is one column of prose. */
.legal-head .lead { max-width: none; }
.legal {
  --legal-toc: 216px;
  display: grid; grid-template-columns: var(--legal-toc) minmax(0, 1fr);
  gap: var(--hero-gap);
}
.legal__toc { position: sticky; top: calc(var(--nav-h) + 24px); align-self: start; }
.legal__toc-label { display: block; padding-bottom: var(--label-gap); }
.legal__toc-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column;
}
.legal__toc-list a {
  /* An entry that wraps needs leading of its own; the padding gives back what the
     leading adds, so a one-line row keeps its height. */
  display: block;
  padding: 4px 0; line-height: 1.35;
  font-size: var(--type-chrome); color: var(--ink-muted);
  transition: color .3s;
}
/* Where you are is an ink step, not a mark: the entry you are reading carries the
   full ink, the rest sit at the metadata grey. 5.6:1 idle, 16.4:1 current. */
.legal__toc-list a[aria-current] { color: var(--ink); font-weight: 600; }
/* The weight step widens the label, so every entry is measured at the bold width
   and the list never rewraps as the ink moves. */
.legal__toc-list li::before {
  content: attr(data-label); display: block; height: 0; overflow: hidden;
  font-size: var(--type-chrome); font-weight: 600; visibility: hidden;
}
.legal__toc-list a:hover, .legal__toc-list a:focus-visible { color: var(--accent-hi); }
.legal__body { display: flex; flex-direction: column; gap: var(--block-step); }
.legal__item {
  display: flex; flex-direction: column; gap: 12px;
}
.legal__title { font-size: var(--type-h3); font-weight: 700; letter-spacing: -.015em; text-wrap: balance; }
.legal__list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 10px;
  transform: translateY(var(--snap, 0px));
}
.legal__list li { --mark-lh: var(--legal-lh); display: grid; grid-template-columns: 22px minmax(0, 1fr); column-gap: 10px; line-height: var(--legal-lh); }
/* The label and its sentence are one run of text in the second column: two grid
   items there would drop the sentence into the 22px mark column. */
.legal__list li > * { grid-column: 2; }
.legal__list li::before { grid-column: 1; grid-row: 1; background: var(--ink-soft); }
.legal__list strong { color: var(--ink-2); font-weight: 600; }
.legal__contact {
  display: flex; flex-direction: column; gap: 4px;
  font-size: var(--type-body); line-height: 1.65; color: var(--ink-soft);
}
/* The section spans the page, so its width is the width the grid has to collapse
   on: the two gutters plus the narrowest readable pair of columns. */
.section--legal { container-type: inline-size; }
@container (max-width: 800px) {
  .legal { grid-template-columns: minmax(0, 1fr); }
  .legal__toc { position: static; }
}

/* ─────────────────────────── Close ─────────────────────────── */
.close { display: flex; flex-direction: column; gap: clamp(28px, 3.4vw, 48px); align-items: flex-start; }
.close__head .h2 { max-width: 22em; text-wrap: pretty; }
.close__mail {
  display: block; line-height: 1;
  /* One line, never wrapped. The address measures 8.9 em-widths of Archivo 800 at
     this tracking, so the display size is capped by the measure it has to fit. */
  font-size: min(var(--type-display), calc((100vw - 2 * var(--gutter)) / 8.9));
  font-weight: 800; letter-spacing: -.035em; white-space: nowrap;
  transition: opacity .3s;
}
.close__mail:hover { opacity: .8; }
.close__foot { display: flex; align-items: baseline; gap: 16px 40px; flex-wrap: wrap; }

/* ─────────────────────────── Footer ─────────────────────────── */
.footer {
  position: relative; overflow: clip;
  background: var(--footer-ground); padding: 0 var(--gutter) 28px;
}
.footer__ramp { height: 3px; margin: 0 calc(-1 * var(--gutter)); background: var(--ramp); }
.footer__inner { max-width: var(--measure); margin: 0 auto; display: flex; flex-direction: column; container-type: inline-size; }
.footer__top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px 32px; flex-wrap: wrap; padding: 30px 0;
}
.footer__logo { display: flex; align-items: center; }
.footer__top img { height: 30px; width: 82px; }
.footer__where {
  display: flex; align-items: baseline; gap: 6px 14px; flex-wrap: wrap;
  font-family: var(--mono); font-size: var(--type-meta); letter-spacing: .08em;
  color: var(--footer-meta);
}
.footer__place { white-space: nowrap; }
/* Under 340px neither place fits on one line beside the other, so the names wrap. */
@media (max-width: 340px) { .footer__place { white-space: normal; } }
.footer__sep { padding-inline: .7em; }
.footer__hairline { height: 1px; background: var(--footer-hairline); }
.footer__mid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 28px clamp(40px, 6vw, 96px); padding: clamp(56px, 7vw, 96px) 0;
}
@container (max-width: 660px) { .footer__mid { grid-template-columns: minmax(0, 1fr); } }
.footer__claim {
  font-size: var(--type-h2); font-weight: 700; letter-spacing: -.02em;
  line-height: 1.12; color: var(--footer-claim); text-wrap: balance; max-width: 14em;
}
.footer__about {
  font-size: var(--type-lead); line-height: 1.7;
  color: var(--footer-ink-2); max-width: 30em; align-self: end;
}
.footer__nav {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px clamp(20px, 3vw, 40px); flex-wrap: wrap; padding: 26px 0;
}
.footer__links { display: flex; align-items: center; gap: clamp(18px, 2.4vw, 32px); flex-wrap: wrap; }
.footer__link {
  font-size: var(--type-chrome); font-weight: 500; color: var(--footer-ink-2);
  transition: opacity .3s ease, color .2s;
}
.footer__links:hover .footer__link { opacity: .34; }
.footer__links .footer__link:hover, .footer__links .footer__link:focus-visible { opacity: 1; }
.footer__links .footer__link:hover { color: var(--warm-1); }
.footer__cta {
  font-size: var(--type-chrome); font-weight: 700; white-space: nowrap;
  background: linear-gradient(90deg, var(--warm-1), var(--warm-2));
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
  padding-right: .06em; margin-right: -.06em;
}
.footer__end { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; padding-top: 22px; }
.footer__end span { font-family: var(--mono); font-size: var(--type-meta); letter-spacing: .08em; line-height: 1.5; color: var(--footer-meta); }

/* ─────────────────────────── Footer: the night sky ─────────────────────────── */
/* Monochrome by filter, not by a second export: the film's colour competes with
   the warm ramp above it here, and the footer only ever wanted its movement.
   Contrast and brightness are tuned to the greyscale result. */
.footer__sky {
  filter: grayscale(1) contrast(1.45) brightness(.33);
  mask-image: linear-gradient(180deg, #000 0 46%, transparent 82%);
  opacity: 0; transition: opacity 1s var(--ease);
}
.footer__sky.is-ready { opacity: 1; }
.footer__ramp, .footer__inner { position: relative; z-index: 1; }
/* The stillness control and the end links are the same small underlined mono
   link; only the button needs its native chrome removing. */
.footer__pause, .footer__end-link {
  font-family: var(--mono); font-size: var(--type-meta); letter-spacing: .08em;
  color: var(--footer-ink-2); border-bottom: 1px solid currentColor; line-height: 1.5;
  transition: color .2s;
}
.footer__pause { background: none; border-width: 0 0 1px; padding: 0; cursor: pointer; }
.footer__pause:hover, .footer__pause:focus-visible,
.footer__end-link:hover, .footer__end-link:focus-visible,
.footer__end-link[aria-current] { color: var(--footer-ink); }
.footer__end-right { display: flex; align-items: center; gap: clamp(14px, 1.6vw, 22px); }
.footer__social { display: block; padding: 6px; margin: -6px; transition: opacity .2s; }
.footer__social img { width: 18px; height: 18px; }
.footer__social:hover, .footer__social:focus-visible { opacity: .62; }
html:not(.js) .footer__pause, html:not(.js) .sky-toggle { display: none; }
@media (prefers-reduced-motion: reduce) {
  .footer__pause, .sky-toggle { display: none; }
}

/* ─────────────────────────── Responsive ─────────────────────────── */
@media (max-width: 980px) {
  .hero-sub { grid-template-columns: 1fr; }
  .hero-sub__frame { aspect-ratio: 16 / 9; max-height: 46vh; order: -1; }
  .feed__lead {
    grid-template-columns: minmax(0, 1fr);
    --feed-crop: calc((100vw - var(--gutter)) / 4);
    margin-left: calc(-1 * (var(--gutter) + var(--feed-crop)));
  }
  .feed { overflow: visible; }
  .feed__lead .feed__post { padding-left: calc(var(--gutter) + var(--feed-crop)); }
  .lines { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .values__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
  .value { aspect-ratio: 3 / 3.8; }
  .value__n { font-size: clamp(64px, 12vw, 92px); }
}
@media (max-width: 860px) {
  .step__n { font-size: clamp(150px, 42vw, 340px); }
  /* The bar keeps the logo and the toggle; the links take the screen. The panel
     is fixed to the viewport, so it covers the page rather than moving it, and
     it fades in as one sheet with the links rising behind it. The bar's own two
     controls are lifted over the panel, which is why they are positioned.
     Without script the links stay in the bar and wrap, so nothing on this page
     depends on a control that could not work. */
  html.js .nav__menu { display: grid; }
  .nav__links { flex-wrap: wrap; gap: 12px 20px; }
  html.js .nav__logo, html.js .nav__menu { position: relative; z-index: 1; }
  html.js .nav__links {
    position: fixed; inset: 0; z-index: 0;
    flex-direction: column; align-items: stretch; justify-content: center; gap: 0;
    padding: var(--nav-h) var(--gutter) var(--gutter);
    background: var(--ground); overflow-y: auto;
    opacity: 0; visibility: hidden;
    transition: opacity .3s var(--ease), visibility 0s linear .3s;
  }
  html.js .nav.is-open .nav__links { opacity: 1; visibility: visible; transition-delay: 0s; }
  html.js .nav__links:hover .nav__link { opacity: 1; }
  html.js .nav__links .nav__link {
    padding: 9px 0; font-size: var(--type-drawer); font-weight: 700;
    letter-spacing: -.02em; color: var(--ink);
    transform: translateY(14px); transition: transform .5s var(--ease);
  }
  /* Same active state as the bar: the current page is the accent. The drawer
     already sets every link bold, so here the colour carries it alone.
     --accent-hi is 4.6:1 on the paper drawer and, as --warm-2 on the negative
     page, 4.9:1 on its ground. */
  html.js .nav__links .nav__link.is-current { color: var(--accent-hi); }
  html.js .nav.is-open .nav__link { transform: none; }
  html.js .nav.is-open .nav__link:nth-child(1) { transition-delay: .04s; }
  html.js .nav.is-open .nav__link:nth-child(2) { transition-delay: .09s; }
  html.js .nav.is-open .nav__link:nth-child(3) { transition-delay: .14s; }
  html.js .nav.is-open .nav__link:nth-child(4) { transition-delay: .19s; }
  html.js .nav.is-open .nav__link:nth-child(5) { transition-delay: .24s; }
  html.js .nav.is-open .nav__link:nth-child(6) { transition-delay: .29s; }
  html.js .nav__links .nav__link--studio { justify-content: space-between; }
  html.js .nav__links .nav__link--cta {
    margin-top: 20px; padding: 14px 0; text-align: center; font-size: var(--type-fwd);
    letter-spacing: 0; border-top: 1px solid var(--rule);
  }
  /* The page under a full-screen panel does not scroll. Clipped rather than
     hidden: hidden on an ancestor kills the bar’s own sticking. */
  html.is-nav-open, html.is-nav-open body { overflow: clip; }
  .steps, .cases, .nums { grid-template-columns: 1fr; }
  .feed__old { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .inc { gap: calc(var(--inc-gap) * 1.2); }
  .cases figure.case--lead { grid-column: span 1; }
  /* One column instead of two. The squiggle stays and changes job: it runs down
     the left of the stack as its spine rather than between two sides, and the
     rows hang off it. Its width is the amplitude the script draws to, so the
     path stays inside the track it is given. */
  .section--thread { padding-bottom: var(--section-gap); }
  .thread__band {
    --spine: 40px; --spine-gap: 18px; --thread-edge: clamp(14px, 3.4vw, 22px);
    padding: var(--thread-edge) 0 var(--thread-edge) calc(var(--spine) + var(--spine-gap));
  }
  .thread__wave { left: 0; transform: none; width: var(--spine); }
  .thread__qs { width: 100%; gap: clamp(26px, 6vw, 34px); }
  /* No room for two sides: the rows come onto one left edge and the mark keeps
     the place it has at the wide size, under its own quote. */
  .thread__q, .thread__q--right {
    width: 100%; align-self: stretch; align-items: flex-start; gap: 9px;
  }
  /* The measure is the one the wide layout reads at, so the line count holds
     from 860 down rather than running the full width of the band. */
  .thread__text { max-width: 22em; }
  .thread__q--left .thread__text { text-align: left; }
}
/* Between 601 and 1024 five columns are too narrow to read, and five is prime, so
   no partial collapse leaves a full row. The step becomes a row instead: the frame
   in a fixed track, the numeral and the copy beside it. Below 601 it stacks. */
@media (max-width: 1024px) and (min-width: 601px) {
  .steps { grid-template-columns: minmax(0, 1fr); gap: clamp(28px, 3.4vw, 40px); }
  .step {
    display: grid; grid-template-columns: clamp(160px, 26%, 260px) minmax(0, 1fr);
    column-gap: clamp(20px, 3vw, 32px); align-items: start;
  }
  /* The rows here are implicit, so the span is counted, not -1: -1 resolves
     against the explicit grid and would collapse the frame into row one. */
  .step > .frame { grid-column: 1; grid-row: 1 / span 4; }
  .step__n { grid-column: 2; font-size: var(--type-numeral-step); margin-top: 0; }
  .step__title, .step__body, .inc { grid-column: 2; }
}
@media (max-width: 560px) {
  /* Stacked hero: sized to the longest word, "creativity" (about 5.1em), so it wraps by word, never mid-word. */
  .hero .h1 { font-size: clamp(44px, 15.5vw, 84px); line-height: .96; }
  .values__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .value--drop { margin-top: 24px; }
  .lines { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .feed__old { grid-template-columns: minmax(0, 1fr); }
  .feed__card { display: grid; grid-template-columns: 104px minmax(0, 1fr); gap: 16px; align-items: start; }
}

/* ─────────────────── Forced colours ─────────────────── */
/* Gradient and image-filled text paints through a clipped background, and forced
   colours paint no background image: without this the words vanish. Reverting
   colour hands each one back to the mode's own ink, or link ink for the links. */
@media (forced-colors: active) {
  .ramp, .nav__link--cta, .fwd, .footer__cta,
  html.js .rev.ramp, html.js .h1--knock, html.js .h1--knock .rev {
    background-image: none; filter: none;
    color: revert; -webkit-text-fill-color: currentColor;
  }
  /* The lead quote tile paints the ramp as a surface rather than as text; forced
     colours drop it, so the ink on it has to be the mode's own. */
  .quote--lead .quote__text, .quote--lead .quote__text b { color: revert; }
  .feed__card-more::after { display: none; }
}

/* ─────────────────────────── Print ─────────────────────────── */
@media print {
  .nav, .hero__sky, .cue, .hero__cue-row, .marquee, .people__band, .footer__ramp, .footer__sky, .footer__pause, .sky-toggle, .footer__social { display: none; }
  /* Paper drops the band: the tint does not print, so the inset that held the
     title off the colour goes with it. */
  .role__head { background: none; color: var(--ink); padding: var(--role-pad) 0; }
  .role__n { color: var(--ink); }
  .role__body { background: none; }
  .role__sheet-in { padding: 0 0 var(--role-pad); }
  html, body { background: #FFFFFF; }
  /* The two quote rows are a loop on screen: on paper they wrap, once, and the
     second copy of each card goes. */
  .quotes__row { width: auto; flex-wrap: wrap; }
  .quotes__row--back, .quote[aria-hidden="true"] { display: none; }
  .quote { margin: 0 18px 18px 0; break-inside: avoid; }
  .hero { min-height: 0; margin-top: 0; padding-top: 0; }
  .hero__centre { position: static; padding: 0; }
  /* The glass and the sky do not print, so the panes go back to paper and the
     band stops reaching under the nav. */
  .panes { margin-top: 0; padding-top: 0; min-height: 0; }
  .panes__strip {
    margin: 0; padding: 18px 0 0; background: none;
    -webkit-backdrop-filter: none; backdrop-filter: none;
    border-top: 1px solid var(--rule);
  }
  .panes__scrim { display: none; }
  .pane {
    border: 1px solid var(--rule);
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }
  .pane__glass { display: none; }
  .pane--dark { --ink: #17181A; --ink-soft: #5A5E66; --ink-meta: #696D75; }
  html.js .rev, html.js .inc__label, html.js .part, html.js .pkg, html.js .thread__q { color: var(--ink); }
  html.js .thread__mark { opacity: 1; transform: none; }
  /* The caption names the film; the selector has nothing to do on paper. */
  .sel { display: none; }
  .thread__q, .thread__q--right { width: 100%; align-items: flex-start; }
  .thread__q--left .thread__text { text-align: left; }
  html.js .h1--knock, html.js .h1--knock .rev {
    background-image: none; filter: none;
    -webkit-text-fill-color: var(--ink); color: var(--ink); animation: none;
  }
  body.negative {
    --ground: #FFFFFF; --ink: #17181A; --ink-2: #3E4249; --ink-muted: #6E727A;
    --ink-soft: #5A5E66; --ink-meta: #8A8E96; --rule: hsl(226 8% 92%);
    --tint: hsl(226 8% 94%); --tint-2: hsl(226 8% 96.5%); --card: hsl(226 8% 94.5%);
  }
  .footer {
    --footer-ground: #FFFFFF; --footer-ink: #17181A; --footer-ink-2: #3E4249;
    --footer-hairline: hsl(226 8% 92%);
    --footer-meta: color-mix(in oklab, var(--footer-ink), var(--footer-ground) 55%);
  }
  .footer__claim { color: var(--footer-ink); }
  .feed__lead { grid-template-columns: minmax(0, 1fr); margin-left: 0; }
  .feed__lead .feed__post { padding-left: 0; }
  .feed__lead .ph { padding-left: 0; }
  .feed__old { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* Paper has no button to press: every post prints in full. */
  .fwd--more { display: none; }
  .feed__card-more { max-height: none !important; }
  .feed__card-more::after { display: none; }
  /* The contents list is a way to move around the page: on paper it does nothing. */
  .legal__toc { display: none; }
  .legal { grid-template-columns: minmax(0, 1fr); }
  .legal__item { break-inside: avoid; }
  .section { padding-bottom: clamp(40px, 6vw, 64px); }
}
