/* ============================================================
   SPOKE. — design concept
   Ink / paper / signal red. Archivo variable + IBM Plex Mono.
   ============================================================ */

:root {
  --ink: #0a0a0c;
  --ink-2: #131316;
  --ink-3: #1c1c21;
  --paper: #f6f5f1;
  --paper-2: #eceae4;
  --red: #ef3e31;
  --red-deep: #c22a1e;
  --white: #ffffff;
  --grey: #a3a3ab;
  --grey-2: #64646c;
  --grey-3: #8f8f98;
  --line-w: rgba(255, 255, 255, 0.14);
  --line-k: rgba(10, 10, 12, 0.16);
  --font-sans: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", monospace;
  --wrap: 1240px;
  --pad: clamp(20px, 4vw, 56px);
  --nav-h: 72px;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --glide: cubic-bezier(0.16, 1, 0.3, 1);
  --spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

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

html {
  -webkit-text-size-adjust: 100%;
}
/* native smooth anchors only where Lenis is not running (narrow screens) */
@media (max-width: 900px) {
  html { scroll-behavior: smooth; }
}
/* Lenis smooth scroll */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }

body {
  font-family: var(--font-sans);
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--white);
  background: var(--ink);
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

img, video { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; }
address { font-style: normal; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

::selection { background: var(--red); color: var(--white); }

:focus-visible {
  outline: 2px solid var(--red);
  outline-offset: 3px;
  border-radius: 2px;
}

.mono {
  font-family: var(--font-mono);
  font-size: 1.00rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.wrap { max-width: var(--wrap); margin: 0 auto; padding-inline: var(--pad); }

section[id], main[id] { scroll-margin-top: var(--nav-h); }

/* film grain over dark sections */
body::after {
  content: "";
  position: fixed;
  inset: -100px;
  z-index: 200;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.6'/%3E%3C/svg%3E");
}

/* ============================================================ NAV */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: clamp(18px, 3vw, 40px);
  transition: background 0.4s var(--ease-out), border-color 0.4s;
  border-bottom: 1px solid transparent;
}
.nav.is-solid {
  background: rgba(10, 10, 12, 0.82);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-color: var(--line-w);
}
.nav-logo img { width: 88px; height: auto; }
.nav-links {
  display: flex;
  gap: clamp(18px, 2.6vw, 36px);
}
/* DIRECTE KINDEREN, NIET ELKE <a> IN DE NAV. Deze regel was geschreven toen
   .nav-links alleen navlinks bevatte; sinds het megamenu (2026-08-29) hangen daar
   ook negen dienstlinks en een knop in, en die kregen ongevraagd uppercase,
   letter-spacing .08em, gewicht 600 en het rode ::after-onderstreepje. Gezien op
   twee opnames achter elkaar: eerst op de taglines, daarna als rood streepje
   onder de knop "All services". Het bij de BRON begrenzen is beter dan per kind
   met specificiteit vechten, want de volgende toevoeging erft het anders weer. */
.nav-links > .nav-item > a {
  position: relative;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 6px 0;
  color: rgba(255, 255, 255, 0.85);
  transition: color 0.25s;
}
.nav-links > .nav-item > a::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 2px;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform 0.35s var(--ease-out);
}
.nav-links > .nav-item > a:hover { color: var(--white); }
.nav-links > .nav-item > a:hover::after { transform: scaleX(1); transform-origin: left center; }
.nav-right { display: flex; align-items: center; gap: 14px; }

.nav-burger {
  display: none;
  width: 44px; height: 44px;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 7px;
}
.nav-burger span {
  display: block;
  width: 24px; height: 2px;
  background: var(--white);
  transition: transform 0.3s var(--ease-out), opacity 0.3s;
}
.nav-burger.is-open span:first-child { transform: translateY(4.5px) rotate(45deg); }
.nav-burger.is-open span:last-child { transform: translateY(-4.5px) rotate(-45deg); }

.mobmenu[hidden], .vmodal[hidden] { display: none; }

.mobmenu {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: var(--ink);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--pad);
  gap: 30px;
}
.mobmenu nav { display: flex; flex-direction: column; gap: 4px; }
.mobmenu a {
  font-family: var(--font-sans);
  font-stretch: 66%;
  font-weight: 800;
  font-size: clamp(2.4rem, 10vw, 3.6rem);
  line-height: 1.16;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  padding: 4px 0;
  border-bottom: 1px solid var(--line-w);
}
.mobmenu a.mobmenu-cta { color: var(--red); }
.mobmenu-foot { color: var(--grey); }

/* ============================================================ MEGAMENU (nav)

   Toegevoegd 2026-08-29 op verzoek van SPOKE ("Nav menu could serve better with
   dropdown options?"). Hun WordPress-site had dit al als `SERVICES` met negen
   items; ronde 3 heeft het niet teruggebouwd, waardoor de negen dienstpagina's
   alleen nog via de hub bereikbaar waren.

   GEEN JAVASCRIPT. Het paneel opent op :hover en :focus-within van .nav-item.
   Dat houdt CLAUDE.md §4 invariant 1 heel (de vier script-tags), het werkt met
   JavaScript uit, en de toetsenbordbediening komt er gratis bij.

   DE HELE BLOK STAAT ACHTER hover:hover EN pointer:fine (DESIGN.md §8). Op een
   aanraakscherm bestaat het paneel dus niet en gaat een tik op Services gewoon
   naar de hub; onder 901px neemt het burgermenu het over, want daar staat
   .nav-links al op display:none.

   WAAROM .nav-item DE VOLLE NAVHOOGTE VULT: zonder dat is er tussen de onderkant
   van de link en de bovenkant van het paneel een gat van circa 22px waar de muis
   doorheen moet, en daar verliest hij de hover. Het paneel is een DOM-kind van
   .nav-item, dus zolang de aanwijzer binnen die kolom of binnen het paneel zelf
   blijft, blijft :hover staan. De <a> houdt zijn eigen box, zodat het rode
   onderstreepje van .nav-links a::after op zijn plek blijft. */
.nav-links { align-self: stretch; align-items: stretch; }
.nav-item { display: flex; align-items: center; }

.megamenu { display: none; }

@media (min-width: 901px) and (hover: hover) and (pointer: fine) {
  .megamenu {
    display: block;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: rgba(10, 10, 12, 0.97);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    border-top: 1px solid var(--line-w);
    border-bottom: 1px solid var(--line-w);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-10px);
    pointer-events: none;
    transition: opacity 0.28s var(--ease-out), transform 0.28s var(--ease-out),
      visibility 0.28s;
  }
  .nav-item-mega:hover > .megamenu,
  .nav-item-mega:focus-within > .megamenu {
    opacity: 1;
    visibility: visible;
    transform: none;
    pointer-events: auto;
  }
  /* Escape sluit het paneel; script.js zet deze klasse. Specificiteit (0,3,1)
     tegen (0,2,1), dus hij wint van de twee open-toestanden hierboven. */
  .nav-item-mega.is-dismissed:hover > .megamenu,
  .nav-item-mega.is-dismissed:focus-within > .megamenu {
    opacity: 0;
    visibility: hidden;
    transform: translateY(-10px);
    pointer-events: none;
  }
}

/* Het twee-zone-raster van .news-inner (DESIGN.md §6b): rail links, de lijst
   rechts. Geen auto-fill in de lijst, want die kolom loopt op --wrap vast en
   dan worden de cellen KLEINER naarmate het scherm groeit (§6b3a). */
/* EEN KOPBAND MET DE LIJST ERONDER, EN GEEN RAIL ERNAAST. Dat is drie keer
   gemeten en de derde vorm is de aangenomene.

   Vorm 1, rail (0,92fr) naast twee kolommen: de rail was 134px en de lijst
   332px, dus 198px lege eerste kolom. Het vierde geval van §6b.
   Vorm 2, rail naast DRIE kolommen plus de knop op margin-top:auto: de kolommen
   werden gemeten even hoog (298 om 298), maar de cel zakte naar 215px op 2000,
   1440 EN 1200px venster, en daar breekt een tagline van zestig tekens over DRIE
   regels op 12,48px. Leesbaarheid ingeruild voor hoogte.
   Vorm 3, deze: de rail kostte 333px van de 1128px, en zonder die kolom passen er
   drie kolommen van 343px. Even leesbaar als twee kolommen mét rail, maar in
   drie rijen in plaats van vijf, en er is geen kolom meer die leeg kan staan.
   Negen items in drie kolommen is exact drie rijen, dus ook geen halve rij.

   Dit is de vorm van /our-work/ (volle-breedte kop, daaronder het raster). De
   waarschuwing bij §6b zesde geval geldt hier niet: die ging over `align-items:
   end` met een cijferkolom die naar de onderrand werd geduwd. Deze kopband is
   een strook van ongeveer 60px met de lijst er direct onder. */
.megamenu-inner {
  max-width: var(--wrap);
  margin-inline: auto;
  padding: clamp(34px, 3.6vw, 52px) var(--pad);
}
.megamenu-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(24px, 4vw, 60px);
  margin-bottom: clamp(24px, 2.4vw, 34px);
}
/* .kicker staat standaard op --grey-2 = 3,37:1 op ink, onder de 4,5:1 van §8.
   Elke donkere sectie van deze site zet hem daarom op --grey (7,9:1); dit paneel
   is er daar een van. */
.megamenu .kicker { color: var(--grey); margin-bottom: 12px; }
.megamenu-title {
  font-family: var(--font-sans);
  font-stretch: 63%;
  font-weight: 840;
  font-size: clamp(1.85rem, 2.5vw, 2.6rem);
  line-height: 0.98;
  letter-spacing: 0.002em;
  text-transform: uppercase;
  color: var(--white);
}
/* .megamenu .megamenu-all en niet .megamenu-all alleen. DIT BLOK STAAT BIJ DE
   NAV, dus VOOR de buttons-sectie: `.btn` staat op r.448 en deze knop op r.302,
   en bij gelijke specificiteit wint de LATERE regel. Zonder de extra klasse
   verloor font-size hier van `.btn { font-size: 0.95rem }` en werd de knop
   15,2px in plaats van 13,6px. De plek van je blok in het bestand bepaalt dus
   mede wie wint; leun daar niet op. */
.megamenu .megamenu-all { flex: 0 0 auto; padding: 11px 22px; min-height: 44px; font-size: 0.85rem; }

/* TWEE KOLOMMEN OP ELKE DESKTOPBREEDTE, en nooit auto-fill: deze kolom loopt op
   --wrap vast, dus auto-fill zou de cellen KLEINER maken naarmate het scherm
   groeit (§6b3a, de fout die het YIELD-contactblad kostte).

   ER STONDEN EERST DRIE KOLOMMEN, en dat was de verkeerde afruil. Drie maakt van
   negen items exact drie rijen en bracht het paneel van 670px naar 404px, maar
   het kostte de LEESBAARHEID: gemeten was de cel dan 215px breed op 2000, 1440
   EN 1200px venster (de wrap loopt immers vast), en een tagline van zestig
   tekens brak daar over DRIE regels op 12,48px. Niet het contrast was het
   probleem (7,90:1) maar de regellengte, ongeveer 35 tekens. Met twee kolommen
   is de cel 341px en past een tagline op twee regels van rond de vijftig tekens.
   Het paneel wordt daar hoger van; dat is de goede kant van de ruil, want het
   staat op een venster van 900px nog ruim binnen beeld. */
/* DRIE KOLOMMEN VANAF 1100px, TWEE DAARONDER, en nooit auto-fill: deze kolom
   loopt op --wrap vast, dus auto-fill zou de cellen KLEINER maken naarmate het
   scherm groeit (§6b3a, de fout die het YIELD-contactblad kostte). De grens ligt
   op 1100 en niet lager omdat de cel daaronder onder de 290px zakt en een
   tagline dan weer over drie regels breekt. */
.megamenu-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(32px, 3.4vw, 60px);
}
@media (min-width: 1100px) {
  .megamenu-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Deze link erft NIETS meer van .nav-links a: die regel is begrensd tot directe
   kinderen, zie de kop van dit bestand bij .nav-links > .nav-item > a. */
.megamenu-link {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  column-gap: 12px;
  align-items: baseline;
  padding: 13px 0;
  border-bottom: 1px solid var(--line-w);
  transition: transform 0.3s var(--ease-out);
}
.megamenu-num { color: var(--grey); transition: color 0.25s; }
.megamenu-name {
  font-family: var(--font-sans);
  font-stretch: 66%;
  font-weight: 780;
  font-size: 1.05rem;
  line-height: 1.12;
  letter-spacing: 0.002em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.88);
  transition: color 0.25s;
}
/* 0,85rem en niet 0,78: dit is een lopende ZIN en geen mono-label. De kleinste
   tekst van de site is .mono op 0,72rem, maar dat zijn korte kapitalen met veel
   letterspatiering. Een zin van drie regels op 12,48px was te klein; Koen zag het
   op zijn eigen scherm. */
.megamenu-tag {
  grid-column: 2;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--grey);
  margin-top: 4px;
}
.megamenu-link:hover { transform: translateX(6px); }
.megamenu-link:hover .megamenu-name { color: var(--white); }
.megamenu-link:hover .megamenu-num { color: var(--red); }

/* ---------------------------------------------------------- mobiele accordeon

   Services klapt in het burgermenu open met <details>, dus zonder script. De
   summary krijgt BEWUST geen :hover-behandeling: hij is geen link, en toets 5
   van check-links.mjs zou zo'n belofte terecht als onvervuld melden. */
.mobmenu-group { border-bottom: 1px solid var(--line-w); }
.mobmenu-summary {
  font-family: var(--font-sans);
  font-stretch: 66%;
  font-weight: 800;
  font-size: clamp(2.4rem, 10vw, 3.6rem);
  line-height: 1.16;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  padding: 4px 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  cursor: pointer;
  list-style: none;
}
.mobmenu-summary::-webkit-details-marker { display: none; }
/* Een chevron uit twee randen, geen tekstteken in de markup en dus ook geen
   streepje dat check-copy zou moeten wegen. BEWUST GEEN plus die naar een kruis
   draait: het burgermenu draagt op datzelfde scherm al een kruis als sluitknop,
   en twee kruizen boven elkaar zeggen twee verschillende dingen. Gezien op de
   opname van 390x659. */
.mobmenu-summary::after {
  content: "";
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  margin-bottom: 6px;
  border-right: 2px solid var(--red);
  border-bottom: 2px solid var(--red);
  transform: rotate(45deg);
  transition: transform 0.3s var(--ease-out), margin-bottom 0.3s var(--ease-out);
}
.mobmenu-group[open] > .mobmenu-summary::after {
  transform: rotate(225deg);
  margin-bottom: -6px;
}
.mobmenu-sub { display: flex; flex-direction: column; padding: 4px 0 14px; }
.mobmenu-sub a {
  font-size: clamp(1.02rem, 4.4vw, 1.32rem);
  font-stretch: 66%;
  font-weight: 700;
  line-height: 1.2;
  padding: 12px 0;
  min-height: 44px;
  display: flex;
  align-items: center;
  border-bottom: 0;
  color: rgba(255, 255, 255, 0.82);
}

/* Met de accordeon open past het menu niet meer in elke schermhoogte. Deze twee
   regels laten het scrollen zonder de gesloten stand te verplaatsen: bij inhoud
   die past, doet auto-marge exact hetzelfde als justify-content: center; bij
   inhoud die niet past, klapt gecentreerde flex de bovenkant af en auto-marge
   niet. */
.mobmenu { justify-content: flex-start; overflow-y: auto; }
.mobmenu > *:first-child { margin-top: auto; }
.mobmenu > *:last-child { margin-bottom: auto; }

/* HET MENU RESERVEERT RUIMTE VOOR DE NAV DIE EROVERHEEN LIGT. Bestaande schuld,
   blootgelegd door de accordeon: .nav staat op z-index 100 en .mobmenu op 90, dus
   de navbalk dekt de bovenste 72px van het open menu af. Gemeten op 375x553
   (iPhone SE) stond het eerste item op y=44..96 en lag het dus half achter de
   nav; op 390x659 en 390x844 viel het net vrij. Met de accordeon open is de
   inhoud 974px in 659px en moet er sowieso gescrold worden, en dan is die
   overlap niet meer incidenteel.
   Gereserveerd wordt PRECIES de navhoogte en geen marge erbij: met var(--pad)
   erbovenop werd de gesloten stand op een iPhone SE 578px in 553px en moest hij
   scrollen waar hij eerst net paste. Nu 558px, dus vijf pixels. */
.mobmenu { padding-top: var(--nav-h); }

/* ============================================================ BUTTONS */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 15px 28px;
  min-height: 48px;
  border-radius: 999px;
  font-weight: 650;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  transition: background 0.25s, color 0.25s, border-color 0.25s, transform 0.25s var(--spring);
  will-change: transform;
}
.btn-nav { padding: 11px 22px; min-height: 44px; font-size: 0.85rem; }
.btn-red { background: var(--red); color: var(--white); }
.btn-red:hover { background: var(--red-deep); }
.btn-ghost {
  border: 1px solid var(--line-w);
  color: var(--white);
}
.btn-ghost:hover { border-color: var(--white); }
.btn-ink { background: var(--ink); color: var(--white); }
.btn-ink:hover { background: #000; }
.btn-ghost-ink {
  border: 1.5px solid rgba(10, 10, 12, 0.55);
  color: var(--ink);
}
.btn-ghost-ink:hover { border-color: var(--ink); background: rgba(10,10,12,0.06); }
.btn-meta { font-size: 0.94rem; opacity: 0.65; }
.play-dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 0 0 rgba(239, 62, 49, 0.5);
  animation: pulse 2.2s infinite;
}
@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(239, 62, 49, 0.5); }
  70% { box-shadow: 0 0 0 9px rgba(239, 62, 49, 0); }
  100% { box-shadow: 0 0 0 0 rgba(239, 62, 49, 0); }
}

/* ============================================================ HERO */
.hero { position: relative; background: var(--ink); }
.hero-pin {
  position: relative;
  height: 100svh;
  overflow: hidden;
}
.hero-media {
  position: absolute;
  inset: 0;
}
.hero-media video,
.hero-media img.hero-poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero-scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(100deg, rgba(10,10,12,0.82) 0%, rgba(10,10,12,0.52) 45%, rgba(10,10,12,0.12) 80%),
    linear-gradient(180deg, rgba(10,10,12,0.5) 0%, rgba(10,10,12,0.12) 45%, rgba(10,10,12,0.66) 100%);
  opacity: 1;
}
/* cinema mode: scrim starts transparent, phase B hidden — JS drives it */
body.cinema .hero-scrim { opacity: 0; }
body.cinema .hero-phase-b { opacity: 0; visibility: hidden; }
/* Dezelfde twee regels, maar al bij de EERSTE VERF. `body.cinema` wordt gezet
   door script.js (r.236) en dat gebeurt pas als de drie CDN-scripts binnen
   zijn; tot dat moment staat de vorm op het scherm die voor mobiel en voor
   bezoekers zonder JavaScript bedoeld is, namelijk de headline over de volle
   poster. Lokaal gemeten op een koude cache: 146ms zichtbaar, 305ms op een
   trage lijn, en 206 tot 357ms op de live site.
   De vier condities hieronder zijn letterlijk waar script.js op test (r.9-13
   en r.234), en de browser kent ze alle vier zonder ook maar een script.
   WAT CSS NIET KAN WETEN is of gsap werkelijk van de CDN is gekomen. Daarvoor
   zet script.js `html.no-cinema` zodra window.gsap ontbreekt, en dan gaan deze
   twee regels weer uit; zonder dat vangnet levert een falend CDN een hero
   zonder tekst op. Een browser die `scripting` nog niet kent laat de query
   niet matchen en houdt dus gewoon het oude gedrag. */
@media (min-width: 901px) and (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  @supports (mask-size: 10px) or (-webkit-mask-size: 10px) {
    html:not(.no-cinema) .hero-scrim { opacity: 0; }
    html:not(.no-cinema) .hero-phase-b { opacity: 0; visibility: hidden; }
    /* En de wordmark-mask zelf, want anders staat er bij de eerste verf een
       volledig beeldvullende poster die daarna in het wordmark klapt. Dat is
       de tweede helft van wat Koen zag: "eerst de headline met de volle video
       en dan pas het SPOKE-wordmark".
       DEZE VIER WAARDEN ZIJN maskFor(0) UIT script.js (r.191-225), uitgerekend:
       progress 0 geeft w = min(vw * 0.76, 1400) en een anker op vw * 0.5 en
       vh * 0.44, met fx = fy = 0.5, dus x = 50vw - w/2 en
       y = 44vh - w * (36/110) / 2. Nagemeten op 1440px: JavaScript zet
       maskW 1094,4 en deze regel doet hetzelfde. Loopt maskFor(0) ooit uiteen
       met deze regel, dan zie je een sprongetje op het moment dat GSAP het
       overneemt; een inline stijl wint altijd van deze regel.
       Dit mag alleen omdat de SVG wordt PRELOAD (index.astro): een mask-image
       die nog niet geladen is maskeert zijn element volledig weg. */
    html:not(.no-cinema) .hero-media {
      -webkit-mask-image: url("assets/logos/spoke-white.svg");
      mask-image: url("assets/logos/spoke-white.svg");
      -webkit-mask-repeat: no-repeat;
      mask-repeat: no-repeat;
      -webkit-mask-size: min(76vw, 1400px) auto;
      mask-size: min(76vw, 1400px) auto;
      -webkit-mask-position: calc(50vw - min(76vw, 1400px) / 2) calc(44vh - min(76vw, 1400px) * 0.1636364);
      mask-position: calc(50vw - min(76vw, 1400px) / 2) calc(44vh - min(76vw, 1400px) * 0.1636364);
    }
  }
}

.hero-phase-a {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}
.hero-kicker {
  text-shadow: 0 1px 18px rgba(0, 0, 0, 0.55);
  position: absolute;
  top: calc(var(--nav-h) + 26px);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 90vw;
  text-align: center;
  color: rgba(255, 255, 255, 0.9);
}
.hero-cities {
  position: absolute;
  left: clamp(18px, 3vw, 40px);
  bottom: 30px;
  color: var(--grey);
}
.hero-cities .x { color: var(--red); }
/* DE HINT DRAAGT DE BEHANDELING VAN .hero-kicker HIERBOVEN, en dat is
   hergebruik en geen nieuwe vorm: dezelfde rgba-wit en dezelfde text-shadow,
   twintig regels verderop in ditzelfde blok. Reden: op --grey was hij tot op
   de pixel gelijk aan .hero-cities (PERTH x LONDON) op dezelfde regel, zelfde
   maat, zelfde kleur, zelfde gewicht, en dan leest hij als een derde
   decoratief label in plaats van als de enige aanwijzing dat er onder deze
   hero nog een pagina staat. Contrast op --ink gaat van 7,9:1 naar 15,9:1, en
   de hierarchie wordt: kicker en hint wit, decor grijs. */
.hero-hint {
  position: absolute;
  bottom: 30px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  color: rgba(255, 255, 255, 0.9);
  text-shadow: 0 1px 18px rgba(0, 0, 0, 0.55);
}
.hint-line {
  width: 1px;
  height: 44px;
  background: linear-gradient(var(--red), transparent);
  animation: hintdrop 1.8s var(--ease-out) infinite;
  transform-origin: top center;
}
/* DE OPACITY STAAT OP 0% EN 45% EXPLICIET OP 1, en dat is de reparatie. Een
   eigenschap die maar in EEN keyframe wordt genoemd, interpoleert vanaf de
   impliciete 0%-waarde over de HELE cyclus, en met --ease-out (front-loaded)
   is die dan bijna meteen weg. Gemeten op de live site over een cyclus van
   1,8s, 32 monsters: 19 stonden op opacity <= 0,05 terwijl het streepje al op
   volle 44px stond, en de piek van 0,92 viel op het moment dat het pas 4px
   was. Zichtbaar was het 8 van de 32, dus 25% van de tijd: het enige bewegende
   element in de hero stond driekwart van zijn eigen cyclus uit. Nu zakt het
   streepje eerst dekkend uit en vervaagt het daarna. */
@keyframes hintdrop {
  0% { transform: scaleY(0); opacity: 1; }
  45% { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1); opacity: 0; }
}

.hero-phase-b {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  padding-inline: var(--pad);
  max-width: calc(var(--wrap) + 2 * var(--pad));
  margin: 0 auto;
}
.hero-title {
  font-stretch: 63%;
  font-weight: 840;
  text-transform: uppercase;
  font-size: clamp(2.7rem, 7.6vw, 6.6rem);
  line-height: 0.98;
  letter-spacing: 0.002em;
}
.slot {
  display: inline-block;
  position: relative;
  color: var(--red);
  white-space: nowrap;
}
.slot::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0.02em;
  width: 100%;
  height: 0.055em;
  background: var(--red);
}
.slot-word { display: inline-block; }
.hero-sub {
  max-width: 560px;
  margin-top: 26px;
  color: rgba(255, 255, 255, 0.82);
  font-size: 1.02rem;
}
.hero-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 34px;
}

/* ============================================================ TICKER */
.ticker {
  position: relative;
  z-index: 5;
  display: flex;
  align-items: stretch;
  background: var(--red);
  color: var(--white);
  border-block: 1px solid rgba(0, 0, 0, 0.25);
}
.ticker-label {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  padding: 14px 20px;
  background: var(--ink);
  white-space: nowrap;
}
.ticker-viewport { overflow: hidden; flex: 1; display: flex; align-items: center; }
.ticker-track {
  display: flex;
  white-space: nowrap;
  will-change: transform;
  animation: tick 60s linear infinite;
}
.ticker:hover .ticker-track { animation-play-state: paused; }
.ticker-group { display: inline-flex; }
.ticker-group span {
  font-family: var(--font-mono);
  font-size: 1.06rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding-block: 15px;
}
.ticker-group span::after {
  content: "";
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--ink);
  margin-inline: 26px;
  vertical-align: middle;
}
@keyframes tick {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* ============================================================ SECTION FURNITURE */
.kicker {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--grey-2);
  margin-bottom: clamp(22px, 3.5vw, 40px);
}
.dot {
  flex: 0 0 auto;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--red);
}
.dot-ink { background: var(--ink); }

.section-title,
.statement-title,
.contact-title {
  font-stretch: 63%;
  font-weight: 840;
  text-transform: uppercase;
  line-height: 0.96;
  letter-spacing: 0.002em;
}
.section-title { font-size: clamp(2.6rem, 6.4vw, 5.6rem); }
/* HET MASKER KNIPT OP BEIDE ASSEN TERWIJL ALLEEN DE VERTICALE NODIG IS, en dat
   kostte twee soorten stille tekstverlies. `overflow: hidden` hoort bij de
   reveal (script.js r.371-378 zet elke regel op yPercent 115 en schuift hem
   terug), maar CSS kan niet op één as knippen: zet je één as op hidden, dan
   wordt de andere auto.
   Gemeten over 482 maskers op 152 pagina's, vóór deze regel:
     - HORIZONTAAL drie afgekapte koppen, alle drie pas vanaf 1440px, steeds een
       LANG WOORD in een kolom die bij `--wrap` vastloopt terwijl de `clamp()`
       op vw doorgroeit (§6b3a). "Current opportunities." in de careers-rail
       verloor 38px, twee artikelkoppen 5 tot 10px: een halve letter die je stil
       kwijt bent. De careers-kop is met een kleinere maat opgelost, want 38px is
       geen knipmarge maar een verkeerde maat.
     - VERTICAAL het accent op de E van "CLICHÉS", op één artikelpagina. Met
       `line-height: 0.96` is de regelbox kleiner dan de em-box, dus de inkt van
       een diakriet ligt erbuiten: 5,4px boven de regelbox tegen 3,6px padding.
   `overflow: clip` plus `overflow-clip-margin` doet precies wat hier nodig is:
   het KNIPGEBIED wordt 0,3em ruimer in alle richtingen en de LAYOUT verandert
   niet, want een clip-marge zit buiten het boxmodel. Voor de reveal maakt het
   niets uit: yPercent 115 zet de regel een volle regelhoogte naar beneden, ruim
   voorbij 0,3em.
   EERST GEPROBEERD EN VERWORPEN: padding met een even grote negatieve marge.
   Dat leek layout-neutraal en is het niet, want een negatieve `margin-top`
   COLLAPST met de marge van het masker erboven, dus elke meerregelige kop werd
   0,1em per regel krapper. Gemeten: shot-diff 8858px desktop en 9080px mobiel,
   allebei over de tolerantie. Met deze vorm 0px.
   0,3em is een TOLERANTIE en geen vrijbrief: overhangt een kop meer, dan is de
   maat fout en niet het masker. Toets 2 van check-render.mjs bewaakt dat. */
.line-mask {
  display: block;
  overflow: clip;
  overflow-clip-margin: 0.3em;
  padding-block: 0.04em;
}
.line { display: inline-block; }
.accent { color: var(--red); }

/* ============================================================ STATEMENT */
.statement {
  background: var(--paper);
  color: var(--ink);
  padding-block: clamp(90px, 10vw, 140px);
  min-height: 100svh;
  display: grid;
  align-content: center;
}
.statement .kicker { color: var(--grey-2); }
.statement-topline {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  margin-bottom: clamp(22px, 3.5vw, 40px);
}
.statement-topline .kicker { margin-bottom: 0; }
.statement-instrument {
  color: var(--grey-2);
  font-variant-numeric: tabular-nums;
}
.statement-instrument .isep { color: var(--red); margin-inline: 10px; }
@media (max-width: 700px) { .statement-instrument { display: none; } }
.statement-title { font-size: clamp(3rem, 8.6vw, 8rem); }
/* the focus (blur) reveal must not be clipped by the line masks */
.statement-title .line-mask { overflow: visible; }
.statement-cols {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(30px, 6vw, 90px);
  margin-top: clamp(40px, 6vw, 80px);
  align-items: start;
}
.statement-copy {
  font-size: clamp(1.12rem, 1.6vw, 1.35rem);
  line-height: 1.62;
  color: #2a2a2e;
  max-width: 62ch;
}
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.chips li {
  border: 1px solid var(--line-k);
  border-radius: 999px;
  padding: 7px 14px;
  color: var(--ink);
}
.minifacts {
  margin-top: 30px;
  border-top: 1px solid var(--line-k);
}
.minifacts div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 20px;
  padding-block: 12px;
  border-bottom: 1px solid var(--line-k);
}
.minifacts dt { color: var(--grey-2); }
.minifacts dd { font-weight: 650; }

/* ============================================================ SERVICES */
.services {
  position: relative;
  background: var(--ink);
  padding-block: clamp(90px, 12vw, 170px);
}
.services .kicker { color: var(--grey); }
/* full-bleed stage: the hovered service's image fills the whole section */
.svc-stage { position: absolute; inset: 0; overflow: hidden; }
.svc-stage-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  filter: grayscale(1) contrast(1.06) brightness(0.62);
}
.svc-stage-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(10, 10, 12, 0.94) 0%,
    rgba(10, 10, 12, 0.55) 42%,
    rgba(10, 10, 12, 0.72) 78%,
    rgba(10, 10, 12, 0.95) 100%);
}
.services > .wrap, .services-grid { position: relative; z-index: 1; }
.services-grid {
  display: block;
  margin-top: clamp(36px, 5vw, 64px);
}
.svc-list { border-top: 1px solid var(--line-w); }
.svc-row {
  position: relative;
  display: grid;
  grid-template-columns: 58px minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: 8px;
  padding: 20px 14px 22px 10px;
  border-bottom: 1px solid var(--line-w);
  cursor: default;
  overflow: hidden;
}
.svc-row { --fill-y: top; }
.svc-row::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--red);
  transform: scaleY(0);
  /* JS sets --fill-y to the edge the cursor enters/leaves from, so the fill
     always travels in the direction of the cursor (both up and down). */
  transform-origin: center var(--fill-y);
  transition: transform 0.38s var(--ease-out);
  z-index: 0;
}
.svc-row:hover::before,
.svc-row.is-hot::before { transform: scaleY(1); }
.svc-row > * { position: relative; z-index: 1; }
.svc-num {
  grid-row: 1 / 3;
  color: var(--red);
  padding-top: 8px;
  transition: color 0.3s;
}
.svc-name {
  font-stretch: 64%;
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(1.55rem, 3.1vw, 2.5rem);
  line-height: 1.04;
  transition: transform 0.38s var(--ease-out);
}
.svc-tag {
  color: var(--grey);
  font-size: 0.94rem;
  margin-top: 6px;
  transition: color 0.3s;
  max-width: 46ch;
}
/* RONDE 7: de hele rij is klikbaar naar zijn dienstpagina. De link is een LEEG
   <a> dat de rij overdekt, en staat bewust NAAST de naam en niet erin:
   script.js r.493-509 wist de inhoud van elke .svc-name om de letters los te
   kunnen wegen, dus een <a> binnen de h3 verdwijnt zodra JS draait (gemeten: 9
   links in de HTML, 0 in de browser). Absoluut gepositioneerd, dus geen
   grid-item en geen enkel effect op de layout: shot-diff 471px desktop / 452px
   mobiel, exact de basislijn. De z-index ligt boven de rode ::before-vulling
   (z-index 0) en boven de inhoud (z-index 1 via `.svc-row > *`). */
.svc-link { position: absolute; inset: 0; z-index: 2; }
.svc-row:has(.svc-link) { cursor: pointer; }
/* De huisregel `:focus-visible` zet de ring op `outline-offset: 3px`, dus BUITEN
   het element. Deze link dekt de rij exact en `.svc-row` heeft `overflow:
   hidden`, dus die ring werd volledig weggeknipt: op de opname was er met
   toetsenbordfocus niets te zien, terwijl DESIGN.md §8 een zichtbare
   :focus-visible eist. Naar binnen halen lost het op zonder de huisregel te
   raken; outline neemt geen ruimte in, dus de layout verandert niet. */
.svc-link:focus-visible { outline-offset: -6px; }

.svc-row:hover .svc-num,
.svc-row.is-hot .svc-num { color: var(--ink); }
.svc-row:hover .svc-name,
.svc-row.is-hot .svc-name { transform: translateX(10px); }
.svc-row:hover .svc-tag,
.svc-row.is-hot .svc-tag { color: rgba(255, 255, 255, 0.92); }

.svc-thumb { display: none; }
/* wide: the list is the whole stage, tag moves beside the name */
@media (min-width: 1021px) {
  .svc-row {
    grid-template-columns: 64px minmax(0, 1.42fr) minmax(0, 0.58fr);
    grid-template-rows: auto;
    column-gap: 30px;
    align-items: center;
    padding-block: 28px;
  }
  .svc-num { grid-row: auto; padding-top: 0; }
  .svc-name { font-size: clamp(2rem, 3.5vw, 3.25rem); font-weight: 620; }
  .svc-name .wl { white-space: nowrap; }
  .svc-tag { margin-top: 0; justify-self: end; text-align: right; max-width: 34ch; }
}
@media (max-width: 1020px) {
  .svc-stage { display: none; }
  .svc-row { grid-template-columns: 44px minmax(0, 1fr) 92px; column-gap: 14px; }
  .svc-thumb {
    display: block;
    grid-row: 1 / 3;
    grid-column: 3;
    width: 92px;
    height: 69px;
    object-fit: cover;
    border-radius: 8px;
    align-self: center;
    outline: 1px solid var(--line-w);
  }
}

/* ============================================================ STORY */
.story { background: var(--ink-2); }
.story-pin {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: clamp(80px, 9vw, 120px);
  overflow: hidden;
}
.story .kicker { color: var(--grey); }
.story-head { width: 100%; }
.story-title { margin-bottom: clamp(30px, 4.5vw, 56px); }
.story-stage {
  width: 100%;
  display: grid;
  grid-template-columns: 120px minmax(0, 1fr);
  gap: clamp(20px, 3vw, 44px);
  align-items: stretch;
  /* text column aligns with the page grid, media bleeds to the right edge */
  padding-left: max(var(--pad), calc((100vw - var(--wrap)) / 2 + var(--pad)));
}
@media (max-width: 1020px) {
  .story-stage { padding-inline: var(--pad); }
}
.story-rail {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  gap: 18px;
  position: relative;
  padding-left: 16px;
}
.rail-step {
  color: var(--grey-3);
  transition: color 0.3s;
  white-space: nowrap;
}
.rail-step.is-on { color: var(--red); }
.rail-bar {
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 2px;
  background: var(--line-w);
}
.rail-fill {
  display: block;
  width: 100%;
  height: 0%;
  background: var(--red);
  transition: height 0.2s linear;
}
/* the story literally travels down the rail */
.rail-dot {
  position: absolute;
  left: 50%;
  top: 0%;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--red);
  transform: translate(-50%, -50%);
  box-shadow: 0 0 0 4px rgba(239, 62, 49, 0.22), 0 0 14px rgba(239, 62, 49, 0.5);
  transition: top 0.2s linear;
}
.story-beats {
  position: relative;
  min-height: clamp(420px, 56vh, 560px);
}
.beat {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.2fr);
  gap: clamp(24px, 4vw, 60px);
  align-items: center;
  opacity: 0;
  visibility: hidden;
}
@media (min-width: 1021px) {
  .beat-media {
    align-self: stretch;
    aspect-ratio: auto;
    height: 100%;
  }
}
.beat-media .beat-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.5s var(--ease-out);
  filter: grayscale(1) contrast(1.06);
}
.beat-media .beat-video.is-live { opacity: 1; }
.beat.is-active { opacity: 1; visibility: visible; }
.beat-num {
  color: var(--red);
  display: block;
  margin-bottom: 14px;
}
.beat-title {
  font-stretch: 64%;
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(2.1rem, 4vw, 3.4rem);
  line-height: 1;
  margin-bottom: 16px;
}
.beat-text p { color: rgba(255, 255, 255, 0.85); max-width: 40ch; font-size: 1.08rem; }
.beat-media {
  position: relative;
  border-radius: 10px;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  outline: 1px solid var(--line-w);
}
.beat-media .rec {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 9px;
  font-size: 0.84rem;
  letter-spacing: 0.2em;
  color: #fff;
  background: rgba(10, 10, 12, 0.5);
  border-radius: 4px;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.beat-media .rec-dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 0 0 rgba(239, 62, 49, 0.6);
  animation: pulse 2s infinite;
}
/* desktop: media bleeds full-height to the right edge, no frame */
@media (min-width: 1021px) {
  .beat-media { border-radius: 0; outline: none; }
  .beat-media .rec { right: max(24px, calc((100vw - var(--wrap)) / 2 + 24px)); }
}
.beat-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) contrast(1.06);
}
.stats {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(20px, 3vw, 44px);
  margin-top: 26px;
}
.stats dt { color: var(--grey); margin-bottom: 4px; }
.stats dd {
  font-stretch: 63%;
  font-weight: 840;
  font-size: clamp(2.2rem, 4.6vw, 3.8rem);
  line-height: 1;
  color: var(--white);
}
.stats dd .count { color: var(--red); }
/* odometer digit reels */
.odo { display: inline-flex; align-items: flex-start; line-height: 1; }
.odo-col {
  display: inline-block;
  height: 1em;
  overflow: hidden;
  vertical-align: top;
}
.odo-reel { display: flex; flex-direction: column; will-change: transform; }
.odo-cell {
  height: 1em;
  display: flex;
  align-items: center;
  justify-content: center;
  font-variant-numeric: tabular-nums;
}
.odo-sep { display: inline-block; }

/* ============================================================ RESULTS BAND */
.results {
  background: var(--ink);
  border-top: 1px solid var(--line-w);
  padding-block: clamp(52px, 6vw, 88px);
}
.results .kicker { color: var(--grey); }
.results-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(26px, 4vw, 60px);
  margin-top: clamp(18px, 3vw, 32px);
}
.result { display: grid; gap: 10px; align-content: start; }
.result-fig {
  font-stretch: 63%;
  font-weight: 840;
  font-size: clamp(2.7rem, 5.4vw, 4.7rem);
  line-height: 0.9;
  color: var(--white);
}
.result-lab {
  display: grid;
  gap: 3px;
  color: rgba(255, 255, 255, 0.92);
  font-weight: 650;
  font-size: 1.02rem;
  padding-top: 6px;
  border-top: 1px solid var(--line-w);
}
/* --grey, niet --grey-2: op ink haalt #64646c 3,37:1 en dat is onder de 4,5:1
   van DESIGN.md §8. --grey haalt 7,9:1. Zelfde huisregel als .results .kicker
   hierboven en als de contact-hero-kicker uit ronde 2. */
.result-lab .mono { color: var(--grey); font-weight: 500; }

/* ============================================================ IN THE NEWS */
.newswire {
  background: var(--ink);
  border-top: 1px solid var(--line-w);
  padding-block: clamp(80px, 11vw, 150px);
}
.newswire .kicker { color: var(--grey); }
.news-inner {
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 2fr);
  gap: clamp(30px, 5vw, 76px);
  align-items: start;
}
.news-rail {
  position: sticky;
  top: calc(var(--nav-h) + 26px);
  align-self: start;
}
.news-rail .section-title {
  font-size: clamp(2.3rem, 4.2vw, 3.7rem);
  margin-bottom: 22px;
}
.news-note {
  color: var(--grey);
  max-width: 32ch;
  font-size: 0.98rem;
  margin-bottom: 22px;
}
.news-live {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--grey-3);
  font-variant-numeric: tabular-nums;
  padding: 8px 14px;
  border: 1px solid var(--line-w);
  border-radius: 999px;
}
.live-dot {
  flex: 0 0 auto;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 0 0 rgba(239, 62, 49, 0.6);
  animation: pulse 2s infinite;
}
.news-list { border-top: 1px solid var(--line-w); }
.news-row {
  position: relative;
  display: grid;
  grid-template-columns: 120px minmax(0, 1fr) minmax(146px, 210px);
  grid-template-areas:
    "date body pub"
    "date body tag";
  gap: 6px clamp(16px, 2.4vw, 40px);
  align-items: start;
  padding: clamp(22px, 2.6vw, 32px) 12px;
  border-bottom: 1px solid var(--line-w);
  overflow: hidden;
  transition: background 0.35s var(--ease-out);
}
.news-row > * { position: relative; z-index: 1; }
/* cursor spotlight (JS feeds --mx / --my) */
.news-row::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(240px circle at var(--mx, 50%) var(--my, 50%), rgba(255, 255, 255, 0.06), transparent 62%);
  opacity: 0;
  transition: opacity 0.3s;
}
.news-row:hover::before { opacity: 1; }
/* signal-red marker travels down the row edge on hover */
.news-row::after {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 2px;
  background: var(--red);
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform 0.35s var(--ease-out);
  z-index: 1;
}
.news-row:hover::after { transform: scaleY(1); }
.news-row:hover { background: rgba(255, 255, 255, 0.016); }
.news-date { grid-area: date; color: var(--grey-3); padding-top: 7px; }
.news-body { grid-area: body; }
/* client logo: muted white mark on the dark wire, real colour on hover */
.news-logo {
  display: block;
  height: 26px;
  width: auto;
  max-width: 158px;
  object-fit: contain;
  object-position: left center;
  margin-bottom: 13px;
  filter: brightness(0) invert(1);
  opacity: 0.5;
  transition: opacity 0.35s var(--ease-out), filter 0.35s var(--ease-out);
}
/* stay white on the dark feed (real-colour reveal would sink the dark logos,
   e.g. Rox/MinersAI, into the background); brighten on hover instead */
.news-row:hover .news-logo { opacity: 1; }
/* RONDE 7: een rij met een `url` is klikbaar naar de publicatie. Dezelfde vorm
   als .svc-link: een leeg <a> dat de rij overdekt, want script.js r.1091 draait
   scramble() op .news-client en die zoekt de laatste directe TEKSTNODE. Een <a>
   om de naam heen zou dat effect stil uitschakelen.
   De rij heeft hierboven al position: relative en de spotlight (--mx/--my) hangt
   aan een pointermove op de rij zelf, die door bubbling gewoon blijft vuren.
   De huisregel :focus-visible zet de ring op outline-offset 3px, en .news-row
   heeft overflow: hidden; naar binnen halen houdt hem zichtbaar.

   DE HOVER-BEHANDELING HANGT BEWUST NIET AAN `is-linked`. Dat is een halve nacht
   wel zo geweest, met als redenering "een hover zonder bestemming is een valse
   belofte". Koen zag het resultaat en keurde het af: zonder die behandeling is de
   newswire dood, en er kwam niets voor in de plaats omdat de adressen bij SPOKE
   vandaan moeten komen (vraag 27). De regel was te absoluut. Op een SCANLIJST is
   oplichten ook leesondersteuning, niet alleen een klikbelofte: het is precies
   wat .about-row doet, waar de leespositie hem stuurt in plaats van de muis.
   Alleen `cursor: pointer` hangt aan `is-linked`, want dat IS een klikbelofte. */
.news-link { position: absolute; inset: 0; z-index: 2; }
.news-row.is-linked { cursor: pointer; }
.news-link:focus-visible { outline-offset: -6px; }
.news-client {
  font-stretch: 64%;
  font-weight: 780;
  text-transform: uppercase;
  font-size: clamp(1.5rem, 2.9vw, 2.35rem);
  line-height: 1.02;
  transition: color 0.3s, transform 0.4s var(--ease-out);
}
.news-code {
  font-family: var(--font-mono);
  font-size: 0.42em;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--red);
  vertical-align: middle;
  margin-left: 10px;
  white-space: nowrap;
}
.news-head {
  color: var(--grey);
  max-width: 54ch;
  margin-top: 9px;
  font-size: 1rem;
  transition: color 0.3s;
}
.news-row:hover .news-client { color: var(--white); transform: translateX(7px); }
.news-row:hover .news-head { color: rgba(255, 255, 255, 0.9); }
.news-pub { grid-area: pub; justify-self: end; text-align: right; color: rgba(255, 255, 255, 0.72); }
.news-tag {
  grid-area: tag;
  align-self: end;
  justify-self: end;
  /* --grey, niet --grey-2: 3,37:1 op ink haalt de 4,5:1 van §8 niet. */
  color: var(--grey);
  padding: 3px 10px;
  border: 1px solid var(--line-w);
  border-radius: 999px;
}
.news-seen {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 18px;
  margin-top: clamp(34px, 4.5vw, 56px);
  padding-top: clamp(20px, 3vw, 30px);
  border-top: 1px solid var(--line-w);
}
.seen-label { color: var(--red); }
.seen-list { color: var(--grey); letter-spacing: 0.1em; } /* was --grey-2: 3,37:1 op ink */

/* ============================================================ CASES / ARCHIVE */
.casework {
  background: var(--paper);
  color: var(--ink);
  padding-block: clamp(90px, 12vw, 150px);
}
.casework .kicker { color: var(--grey-2); }
.casework-head { margin-bottom: clamp(36px, 5vw, 60px); }
.case-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(22px, 2.6vw, 38px);
}
.case-lead { grid-column: span 2; }
.case-media {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: 10px;
  overflow: hidden;
  outline: 1px solid var(--line-k);
  transition: box-shadow 0.5s var(--ease-out);
}
.case-lead .case-media { aspect-ratio: 16 / 10; }
.case-media img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  filter: grayscale(1) contrast(1.05);
  transition: filter 0.6s var(--ease-out), transform 0.6s var(--ease-out);
}
/* newspaper halftone that resolves into the real photo on hover */
.case-scan {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: radial-gradient(rgba(10, 10, 12, 0.62) 30%, transparent 32%);
  background-size: 5px 5px;
  mix-blend-mode: multiply;
  opacity: 0.55;
  transition: opacity 0.6s var(--ease-out);
}
.case:hover .case-media { box-shadow: 0 20px 46px rgba(10, 10, 12, 0.17); }
.case:hover .case-media img { filter: grayscale(0) contrast(1); transform: scale(1.03); }
.case:hover .case-scan { opacity: 0; }
.case-meta { padding-top: 16px; }
/* --red-deep op paper (5,28:1), niet --red (3,56:1). Zelfde meting en zelfde
   ingreep als .hub-num op de services-hub; op ink mag --red wel (5,09:1). */
.case-eng { display: block; color: var(--red-deep); margin-bottom: 8px; }
/* Het jaar naast het engagement-label (punt 7). Het label blijft het accent en
   het jaar is bijschrift, dus --grey-2 (5,38:1 op paper, DESIGN.md §8). Het
   scheidingsteken houdt de rode behandeling van het label, net als .work-svcs
   .sep op /our-work/ en .news-lead-meta .sep op /news/. */
.case-eng .case-when { white-space: nowrap; }
.case-eng .case-year { color: var(--grey-2); }
.case-client {
  font-stretch: 64%;
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(1.5rem, 2.4vw, 2.2rem);
  line-height: 1;
}
.case-lead .case-client { font-size: clamp(2rem, 3.4vw, 3rem); }
.case-result { color: #2a2a2e; margin-top: 8px; max-width: 46ch; font-size: 0.98rem; }
@media (min-width: 861px) {
  .case-lead {
    display: grid;
    grid-template-columns: 1.55fr 1fr;
    gap: clamp(22px, 3vw, 48px);
    align-items: center;
  }
  .case-lead .case-meta { padding-top: 0; }
}

/* ---- results / news / cases responsive ---- */
@media (max-width: 900px) {
  .news-inner { grid-template-columns: 1fr; gap: clamp(26px, 6vw, 40px); }
  .news-rail { position: static; }
}
@media (max-width: 860px) {
  .results-grid { grid-template-columns: 1fr 1fr; gap: clamp(26px, 5vw, 44px); }
  .case-grid { grid-template-columns: 1fr 1fr; }
  .case-lead { grid-column: span 2; }
}
@media (max-width: 720px) {
  .news-row {
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "date pub"
      "body body"
      "tag  tag";
    gap: 10px 14px;
    padding-inline: 8px;
  }
  .news-pub { align-self: center; }
  .news-tag { justify-self: start; align-self: start; margin-top: 4px; }
}
@media (max-width: 560px) {
  .results-grid { grid-template-columns: 1fr; }
  .case-grid { grid-template-columns: 1fr; }
  .case-lead, .case { grid-column: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .live-dot { animation: none !important; }
}

/* ============================================================ YIELD */
.yield {
  background: var(--ink);
  padding-block: clamp(70px, 9vw, 120px) clamp(90px, 12vw, 170px);
  border-top: 1px solid rgba(255, 255, 255, 0.2);
}
.yield .kicker { color: var(--grey); }
.yield-brand {
  display: grid;
  grid-template-columns: minmax(180px, 300px) minmax(0, 1fr);
  gap: clamp(26px, 5vw, 70px);
  align-items: center;
  margin-bottom: clamp(36px, 5vw, 60px);
}
.yield-logo-wrap { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; }
.yield-logo { width: 100%; max-width: 300px; height: auto; }
.yield-copy {
  font-size: clamp(1.05rem, 1.5vw, 1.25rem);
  color: rgba(255, 255, 255, 0.85);
  max-width: 56ch;
}
.filmstrip {
  display: flex;
  gap: 18px;
  overflow-x: auto;
  padding-inline: max(var(--pad), calc((100vw - var(--wrap)) / 2 + var(--pad)));
  scroll-padding-inline: max(var(--pad), calc((100vw - var(--wrap)) / 2 + var(--pad)));
  padding-bottom: 8px;
  scroll-snap-type: x proximity;
  cursor: grab;
  /* the custom .strip-progress bar replaces the native scrollbar */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.filmstrip.dragging { cursor: grabbing; scroll-snap-type: none; }
.filmstrip::-webkit-scrollbar { display: none; }
.film-card {
  flex: 0 0 clamp(288px, 32vw, 440px);
  scroll-snap-align: start;
}
.film-thumb {
  position: relative;
  display: block;
  width: 100%;
  border-radius: 10px;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  outline: 1px solid var(--line-w);
}
.film-thumb img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.5s var(--ease-out), filter 0.4s;
  filter: grayscale(0.35);
}
.film-card:hover .film-thumb img { transform: scale(1.06); filter: grayscale(0); }
.film-play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 54px; height: 54px;
  border-radius: 50%;
  background: rgba(10, 10, 12, 0.72);
  outline: 1px solid rgba(255,255,255,0.3);
  transition: background 0.3s, transform 0.3s var(--spring);
}
.film-play::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 0; height: 0;
  border-left: 13px solid var(--white);
  border-top: 8px solid transparent;
  border-bottom: 8px solid transparent;
  transform: translateX(2px);
}
.film-card:hover .film-play { background: var(--red); transform: scale(1.1); }
.film-thumb::after {
  content: "Watch";
  position: absolute;
  left: 14px; bottom: 14px;
  font-family: var(--font-mono);
  font-size: 0.90rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--white);
  background: var(--red);
  padding: 5px 10px;
  border-radius: 4px;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.35s var(--ease-out), transform 0.35s var(--ease-out);
}
.film-card:hover .film-thumb::after { opacity: 1; transform: translateY(0); }
.film-meta { color: var(--grey); margin: 14px 0 5px; font-size: 0.84rem; }
.film-title {
  font-stretch: 66%;
  font-weight: 750;
  text-transform: uppercase;
  font-size: clamp(1.25rem, 1.75vw, 1.62rem);
  text-wrap: pretty;
  line-height: 1.12;
}
.film-more {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed var(--line-w);
  border-radius: 10px;
  aspect-ratio: 16 / 12;
  align-self: flex-start;
  transition: border-color 0.3s, background 0.3s;
}
.film-more:hover { border-color: var(--red); background: rgba(239, 62, 49, 0.06); }
.film-more-inner { text-align: center; display: grid; gap: 8px; color: var(--grey); }
.film-more-inner .big {
  font-stretch: 63%;
  font-weight: 840;
  font-size: 2.6rem;
  color: var(--white);
  line-height: 1;
}

.filmstrip.has-more,
.team-strip.has-more {
  -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 calc(100% - 90px), transparent 100%);
  mask-image: linear-gradient(90deg, #000 0%, #000 calc(100% - 90px), transparent 100%);
}
.strip-controls {
  display: flex;
  align-items: center;
  gap: clamp(16px, 3vw, 34px);
  margin-top: 12px;
}
.strip-progress {
  position: relative;
  flex: 1;
  min-width: 0;
  height: 18px;
  display: flex;
  align-items: center;
  cursor: pointer;
  touch-action: none;
}
.strip-progress::before {
  content: "";
  position: absolute;
  left: 0; right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 3px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.14);
}
.strip-progress:hover::before { background: rgba(255, 255, 255, 0.22); }
.strip-progress-bar {
  position: relative;
  z-index: 1;
  height: 3px;
  width: 22%;
  background: var(--red);
  border-radius: 3px;
}
.strip-progress.dragging .strip-progress-bar { height: 5px; }
.strip-progress:focus-visible { outline: 2px solid var(--red); outline-offset: 4px; border-radius: 3px; }
.strip-arrows { display: flex; gap: 10px; flex: 0 0 auto; }
.strip-arrow {
  width: 46px; height: 46px;
  border-radius: 50%;
  border: 1px solid var(--line-w);
  color: var(--white);
  display: grid;
  place-items: center;
  transition: background 0.25s, border-color 0.25s, transform 0.25s var(--spring), opacity 0.25s;
}
.strip-arrow svg { width: 20px; height: 20px; }
.strip-arrow:hover:not(:disabled) { background: var(--red); border-color: var(--red); transform: translateY(-2px); }
.strip-arrow:disabled { opacity: 0.3; cursor: default; }

/* ============================================================ CLIENTS */
.clients {
  background: var(--paper);
  color: var(--ink);
  padding-block: clamp(90px, 12vw, 160px);
}
.logo-marquee {
  margin-top: clamp(40px, 5vw, 64px);
  display: grid;
  gap: clamp(30px, 4vw, 50px);
  overflow: hidden;
  padding-block: clamp(30px, 4vw, 48px);
  border-block: 1px solid var(--line-k);
}
.logo-track {
  display: flex;
  width: max-content;
  animation: tick 38s linear infinite;
}
.logo-track.reverse { animation-direction: reverse; }
.logo-marquee:hover .logo-track { animation-play-state: paused; }
.logo-set {
  display: flex;
  align-items: center;
  gap: clamp(56px, 6.5vw, 104px);
  padding-right: clamp(56px, 6.5vw, 104px);
}
.logo-set img {
  height: 76px;
  width: auto;
  max-width: 300px;
  object-fit: contain;
  filter: grayscale(1) opacity(0.78) contrast(1.05);
  transition: filter 0.3s, transform 0.3s var(--ease-out);
}
.logo-set img:hover { filter: grayscale(0) opacity(1); transform: scale(1.04); }

/* ============================================================ PEOPLE */
.people {
  background: var(--paper);
  color: var(--ink);
  padding-block: 0 clamp(90px, 12vw, 150px);
}
.people .kicker { color: var(--grey-2); }
.team-strip {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  margin-top: clamp(36px, 5vw, 60px);
  padding-inline: max(var(--pad), calc((100vw - var(--wrap)) / 2 + var(--pad)));
  scroll-padding-inline: max(var(--pad), calc((100vw - var(--wrap)) / 2 + var(--pad)));
  padding-bottom: 22px;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: var(--red) transparent;
}
.team-strip::-webkit-scrollbar { height: 4px; }
.team-strip::-webkit-scrollbar-thumb { background: var(--red); border-radius: 4px; }
.team-strip::-webkit-scrollbar-track { background: rgba(10,10,12,0.08); }
.team-card {
  flex: 0 0 clamp(200px, 21vw, 250px);
  scroll-snap-align: start;
}
.team-photo {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: 10px;
  overflow: hidden;
  transition: transform 0.45s var(--ease-out), box-shadow 0.45s;
}
.team-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* brand duotone (ink shadows -> red highlights) by default on desktop; the real
   colour photo resolves on hover. Touch devices (no hover) always show colour. */
.team-card:hover .team-photo {
  transform: translateY(-6px);
  box-shadow: 0 18px 40px rgba(10, 10, 12, 0.18);
}
@media (hover: hover) and (pointer: fine) {
  .team-real {
    filter: grayscale(1) contrast(1.04);
    transition: filter 0.55s var(--ease-out);
  }
  .team-card:hover .team-real { filter: none; }
}
.team-card figcaption {
  padding-top: 14px;
  display: grid;
  gap: 5px;
}
.team-card strong {
  font-weight: 700;
  font-size: 1.02rem;
  position: relative;
  width: fit-content;
}
/* red caption bar wipes in under the name on hover */
.team-card strong::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -4px;
  width: 100%;
  height: 2px;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.4s var(--ease-out);
}
.team-card:hover strong::after { transform: scaleX(1); }
.team-card .mono { color: var(--grey-2); font-size: 0.92rem; }
/* Ronde 4: negen van de tien kaarten zijn een link naar hun eigen pagina.
   `display: block` is het enige dat nodig is - de reset op r.55 zet `a` al op
   color: inherit en text-decoration: none, dus de kaart ziet er ongewijzigd uit
   (bewezen: shot-diff 0px). Zonder die regel is de <a> een inline doos om twee
   blokken heen en verschuift de opmaak. De focusring komt van de globale
   :focus-visible op r.62. */
.team-card a { display: block; }

/* ============================================================ OFFICES / DEPARTURE BOARD */
.offices {
  background: var(--ink);
  color: var(--white);
  padding-block: clamp(80px, 11vw, 150px);
}
.offices-inner { display: grid; gap: clamp(34px, 5vw, 58px); }
.offices-head .kicker { color: var(--grey); }

.board {
  perspective: 900px;
  border-top: 1px solid var(--line-w);
}
.board-head,
.board-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto auto;
  gap: clamp(12px, 3vw, 48px);
  align-items: center;
  padding-block: clamp(11px, 1.3vw, 17px);
  border-bottom: 1px solid var(--line-w);
}
.board-head {
  color: var(--grey-3);
  font-size: 0.62rem;
}
.board-head span { justify-self: end; }
.board-head span:first-child { justify-self: start; }
.board-row .bd-code,
.board-row .bd-time,
.board-row .bd-tag { justify-self: end; }
.flap { display: inline-flex; gap: 3px; }
.flap-cell {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 0.92em;
  height: 1.42em;
  padding: 0 0.05em;
  background: #17171b;
  color: #f2efe8;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: clamp(0.92rem, 1.45vw, 1.3rem);
  line-height: 1;
  border-radius: 3px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.05);
  transform-origin: center center;
  backface-visibility: hidden;
  position: relative;
}
.flap-cell::after {
  content: "";
  position: absolute;
  left: 2px; right: 2px;
  top: 50%;
  height: 1px;
  background: rgba(0, 0, 0, 0.55);
  transform: translateY(-0.5px);
}
.bd-dest .flap-cell { background: #202027; font-size: clamp(1rem, 1.7vw, 1.55rem); }
.board-row.is-home .bd-dest .flap-cell,
.board-row.is-home .bd-time .flap-cell { color: var(--red); }
.board-row.is-home .bd-dest .flap-cell { background: #241312; }

.office-cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(24px, 5vw, 70px);
  padding-top: clamp(8px, 2vw, 24px);
}
.office-card { display: grid; gap: 12px; align-content: start; }
.office-label { display: flex; align-items: center; gap: 10px; color: var(--grey); }
.office-city {
  font-stretch: 63%;
  font-weight: 840;
  text-transform: uppercase;
  font-size: clamp(2.4rem, 4.5vw, 3.6rem);
  line-height: 0.95;
}
.office-card address { line-height: 1.55; font-size: 1.02rem; color: rgba(255,255,255,0.9); }
.office-tel { letter-spacing: 0.08em; color: rgba(255, 255, 255, 0.85); }
a.office-tel:hover { color: var(--red); }
/* Zie .result-lab .mono: --grey-2 op ink is 3,37:1, --grey is 7,9:1. */
.office-note { color: var(--grey); }
/* live analogue office clocks */
.office-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
}
.office-headings { display: grid; gap: 12px; align-content: start; }
.office-clock {
  flex: 0 0 auto;
  width: clamp(98px, 11vw, 150px);
  height: clamp(98px, 11vw, 150px);
  overflow: visible;
}
.clock-face { fill: none; stroke: rgba(255, 255, 255, 0.2); stroke-width: 1.4; }
.clock-ticks line { stroke: rgba(255, 255, 255, 0.36); stroke-width: 1.4; }
.clock-ticks line.major { stroke: rgba(255, 255, 255, 0.72); stroke-width: 2.1; }
.clock-hand { stroke-linecap: round; }
.clock-hour { stroke: #fff; stroke-width: 3.4; }
.clock-min { stroke: #fff; stroke-width: 2.2; }
.clock-sec { stroke: var(--red); stroke-width: 1.4; }
.clock-cap { fill: var(--red); }
.office-tz { color: var(--grey-3); letter-spacing: 0.16em; }
@media (max-width: 640px) {
  .board-head span:nth-child(4),
  .board-row .bd-tag { display: none; }
  .board-head,
  .board-row { grid-template-columns: minmax(0, 1fr) auto auto; }
  .office-cards { grid-template-columns: 1fr; }
}

/* ============================================================ CONTACT */
.contact {
  position: relative;
  background: var(--red);
  color: var(--ink);
  padding-block: clamp(52px, 6vw, 84px);
  text-align: left;
}
.contact-title {
  display: grid;
  gap: clamp(8px, 1.4vw, 20px);
  color: var(--ink);
}
.ct-speak {
  font-stretch: 63%;
  font-weight: 840;
  text-transform: uppercase;
  font-size: clamp(2.4rem, 7vw, 6rem);
  line-height: 0.95;
  letter-spacing: 0.002em;
}
/* closing bookend: the showreel plays inside the SPOKE. wordmark again */
.ct-wordmark {
  display: block;
  width: min(94%, 940px);
  aspect-ratio: 110 / 36;
  background: url("assets/logos/spoke-white.svg") left center / contain no-repeat;
}
.ct-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  -webkit-mask: url("assets/logos/spoke-white.svg") left center / contain no-repeat;
  mask: url("assets/logos/spoke-white.svg") left center / contain no-repeat;
  opacity: 0;
  transition: opacity 0.7s var(--ease-out);
}
.ct-video.is-live { opacity: 1; }
.contact-row {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-top: clamp(34px, 5vw, 56px);
}
.contact-foot { margin-top: 26px; color: rgba(10, 10, 12, 0.88); }

/* ============================================================ FOOTER */
.footer {
  background: var(--ink);
  color: var(--white);
  padding-top: clamp(60px, 7vw, 90px);
}
.footer-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1.2fr) auto;
  gap: clamp(30px, 5vw, 60px);
  padding-bottom: 50px;
}
.footer-brand img { width: 96px; }
.footer-tag { margin-top: 16px; color: var(--grey); }
.footer-nav { display: grid; gap: 10px; align-content: start; }
.footer-nav a {
  width: fit-content;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.8);
  transition: color 0.25s;
}
.footer-nav a:hover { color: var(--red); }
.footer-addr { display: grid; gap: 16px; color: var(--grey); text-transform: none; letter-spacing: 0.04em; }
.footer-social { display: flex; gap: 14px; align-items: start; }
.footer-social a {
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  border: 1px solid var(--line-w);
  border-radius: 50%;
  transition: border-color 0.3s, background 0.3s, transform 0.3s var(--spring);
}
.footer-social a:hover { border-color: var(--red); background: var(--red); transform: translateY(-3px); }
.footer-base {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px;
  padding-block: 22px;
  border-top: 1px solid var(--line-w);
  color: var(--grey-3);
}
.footer-note { text-transform: none; letter-spacing: 0.03em; }

/* ============================================================ VIDEO MODAL */
.vmodal {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: rgba(5, 5, 6, 0.92);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 4vw, 60px);
}
.vmodal-frame {
  width: min(1080px, 100%);
  aspect-ratio: 16 / 9;
  border-radius: 10px;
  overflow: hidden;
  outline: 1px solid var(--line-w);
  background: #000;
}
.vmodal-frame iframe { width: 100%; height: 100%; border: 0; display: block; }
.vmodal-close {
  position: absolute;
  top: 20px;
  right: 24px;
  width: 48px; height: 48px;
  border-radius: 50%;
  border: 1px solid var(--line-w);
  color: var(--white);
  font-size: 1.1rem;
  display: grid;
  place-items: center;
  transition: background 0.25s, border-color 0.25s;
}
.vmodal-close:hover { background: var(--red); border-color: var(--red); }

/* ============================================================ RESPONSIVE */
@media (max-width: 1020px) {
  .services-grid { grid-template-columns: 1fr; }
  .svc-preview { display: none; }
  .story-stage { grid-template-columns: 1fr; }
  .story-rail {
    flex-direction: row;
    padding-left: 0;
    padding-bottom: 14px;
    gap: 16px;
    flex-wrap: wrap;
  }
  .rail-bar { display: none; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 900px) {
  .nav-links { display: none; }
  .btn-nav { display: none; }
  .nav-burger { display: flex; }
  .statement-cols { grid-template-columns: 1fr; }
  .yield-brand { grid-template-columns: 1fr; }
  .yield-logo { max-width: 210px; }
  .office-cards { grid-template-columns: 1fr; }
  .office { min-height: 52svh; }
}

/* DE HERO-INHOUD PAST OP EEN TELEFOON NIET IN EEN SCHERMHOOGTE, en phase A is
   het eerste dat daardoor botst. Die laag hoort bij de cinema-zoom, en de
   timeline die haar wegfadet draait alleen vanaf 901px (script.js r.234): op
   mobiel blijft zij dus staan terwijl de animatie waar zij bij hoort er niet is.
   .hero-cities stond om die reden al op display:none (hieronder).
   Gemeten op de gebouwde site, zichtbare hoogte (niet de schermmaat):
     iPhone 13/14  659px  kicker 23px door de kop
     iPhone mini   629px  kicker 35px door de kop, hint 2px achter de knoppen
     iPhone SE     553px  kicker 73px door de kop, hint 40px achter de knoppen
   De kantelhoogte is 704/705px, binair bepaald op 390px breed. Elke iPhone op
   de Pro Max na zit eronder, elk Android-toestel erboven: iOS-browsers hebben
   een balk boven EN onder. Dat is waarom dit op Android en desktop niet te zien
   was, en waarom het geen WebKit-bug is (Chromium meet exact dezelfde getallen).
   De 704px hoort bij de HUIDIGE kop en subtekst; wordt die copy langer, dan
   schuift de grens mee. Er is bewust GEEN poort op gezet: controleer dit met een
   opname op een telefoonformaat als de hero-copy verandert. */
@media (max-width: 900px) and (max-height: 704px) {
  .hero-phase-a { display: none; }
}

@media (max-width: 700px) {
  body { font-size: 1rem; }
  .hero-title { font-size: clamp(2.35rem, 11.5vw, 3.4rem); }
  .hero-sub { font-size: 0.98rem; }
  .hero-cities { display: none; }
  .beat {
    grid-template-columns: 1fr;
    align-content: start;
    gap: 18px;
  }
  .beat-media { aspect-ratio: 16 / 10; order: -1; }
  .story-beats { min-height: 74svh; }
  .stats { gap: 18px; }
  .contact-row .btn { width: 100%; justify-content: center; }
  .footer-grid { grid-template-columns: 1fr; gap: 34px; }
  .ticker-label { display: none; }
}

/* ============================================================ MOBILE STORY (unpinned) */
@media (max-width: 1020px) {
  .story-pin { min-height: 0; }
  .story-beats { min-height: 0; }
  .beat {
    position: relative;
    inset: auto;
    opacity: 1;
    visibility: visible;
    margin-bottom: clamp(44px, 8vw, 70px);
  }
  .beat:last-child { margin-bottom: 0; }
  .story-rail { display: none; }
}

/* ============================================================ REDUCED MOTION */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001s !important;
  }
  .ticker-track, .logo-track { animation: none !important; transform: none !important; }
  .play-dot, .hint-line, .rec-dot { animation: none !important; }
}
