/* bartknijnenberg.com - standalone homepage
   Donker canvas, een licht blok. DM Sans voor tekst, JetBrains Mono voor systeemdata. */

/* ---------- fonts, self-hosted (BK-20) ----------
   Variabele woff2-slices van Google Fonts, gedownload naar src/fonts/ en
   geserveerd vanaf /assets/fonts/. Eén bestand per subset dekt alle gewichten
   (DM Sans 400-700, JetBrains Mono 400-500). latin-ext staat erbij met een
   unicode-range: de browser haalt hem alleen op als er echt zo'n teken op de
   pagina staat. Geen requests meer naar Google-servers voor fonts.          */
@font-face {
  font-family: "DM Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/assets/fonts/dm-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "DM Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/assets/fonts/dm-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url('/assets/fonts/jetbrains-mono-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400 500;
  font-display: swap;
  src: url('/assets/fonts/jetbrains-mono-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --bg: #0A0B0D;
  --surface: #121418;
  --surface-2: #171A1F;
  --line: rgba(255, 255, 255, .09);
  --line-strong: rgba(255, 255, 255, .22);
  --text: #E8EAED;
  --muted: #98A0AC;
  --dim: #7A8290; /* BK-21: een tint lichter dan #6C7480, tegen wegvallende systeemtekst */
  --accent: #2563EB;
  --accent-hover: #1D4ED8;
  --accent-text: #7FA9F7;
  --accent-soft: #5B8DEF;
  --live: #34D399;
  --soon: #FBBF24;
  --danger: #FCA5A5;
  --light: #F5F3EF;
  --light-text: #1A1A1A;
  --light-muted: #4A5058;
  /* De lichtste grijstint die op #F5F3EF nog 4.5:1 haalt. Alles daarboven
     (zoals de oude #8A8578) zakt naar 3.3:1 en zakt door de AA-ondergrens. */
  --light-dim: #5B6470;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  /* De leeskolom. In een proportionele letter is 1ch (de breedte van de nul)
     breder dan de gemiddelde letter: 68ch leverde ~93 tekens per regel op,
     ruim boven de 80 waar het oog de weg terug kwijtraakt. 58ch landt op ~78. */
  --kolom: 58ch;
  --wrap: 1180px;
  --gutter: 5vw;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img { max-width: 100%; display: block; }

a { color: var(--accent-text); text-decoration: none; transition: color .18s ease; }
a:hover { color: #fff; }

:focus-visible {
  outline: 3px solid #60A5FA;
  outline-offset: 3px;
  border-radius: 4px;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: #fff;
  color: #0A0B0D;
  padding: 12px 18px;
  z-index: 100;
  font-weight: 600;
}
.skip:focus { left: 12px; top: 12px; color: #0A0B0D; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gutter); }

/* ---------- header ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: #0D0D0D;
  border-bottom: 1px solid var(--line);
}
.site-header .wrap {
  display: flex;
  align-items: center;
  gap: 24px;
  min-height: 66px;
}
.brand {
  font-weight: 700;
  font-size: 17px;
  letter-spacing: -.02em;
  color: #fff;
  white-space: nowrap;
}
.brand:hover { color: #fff; }
.site-nav {
  display: flex;
  gap: 20px;
  margin-left: auto;
  align-items: center;
  flex-wrap: nowrap;
}
.site-nav a {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: #CBD2DC;
  white-space: nowrap;
  position: relative;
  padding: 6px 0;
}
.site-nav a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: #3B82F6;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .2s ease;
}
.site-nav a:hover { color: #fff; }
.site-nav a:hover::after { transform: scaleX(1); }
.lang {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .03em;
  color: var(--dim);
  padding-left: 6px;
  border-left: 1px solid var(--line);
}
.lang a { color: var(--dim); }
.lang a:hover, .lang [aria-current] { color: #fff; }
.lang [aria-current] { font-weight: 700; }
/* BK-21: tap-target van de taalwissel >= 44x44px. De negatieve marge houdt de
   balk visueel even compact; alleen het raakvlak groeit. */
.lang a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  margin: -11px -10px;
}

/* ---------- typografie ---------- */
h1, h2, h3 { color: #fff; margin: 0; letter-spacing: -.025em; line-height: 1.12; }
h1 { font-size: clamp(40px, 7vw, 78px); font-weight: 700; line-height: 1.04; letter-spacing: -.035em; max-width: 16ch; margin-bottom: 22px; }
h2 { font-size: clamp(30px, 4.2vw, 46px); font-weight: 700; margin-bottom: 14px; }
h3 { font-size: 20px; font-weight: 700; line-height: 1.25; margin-bottom: 8px; }
p { margin: 0 0 16px; color: var(--muted); max-width: var(--kolom); }
.kicker {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--dim);
  margin: 0 0 22px;
  max-width: none;
}

/* De regel onder de merknaam in de hero is een zin, geen label. Uppercase met
   .14em tracking is prima voor drie woorden en onleesbaar voor 62 tekens: je
   herkent woorden aan hun vorm (stok- en staartletters) en die haalt uppercase
   weg. Vandaar een eigen klasse in gewone zinsvorm. */
.signatuur {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: .01em;
  color: var(--dim);
  margin: 0 0 22px;
  max-width: none;
}

section { padding: clamp(64px, 9vw, 120px) 0; border-top: 1px solid var(--line); }

/* ---------- knoppen ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 15px 28px;
  border-radius: 8px;
  font-size: 17px;
  font-weight: 600;
  white-space: nowrap;
  flex: 0 0 auto;
  transition: background .2s ease, transform .2s ease, border-color .2s ease;
}
.btn-primary { background: var(--accent); color: #fff; border: 2px solid var(--accent); }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: #fff; transform: translateY(-2px); }
.btn-ghost { color: var(--text); border: 2px solid var(--line-strong); }
.btn-ghost:hover { border-color: #fff; color: #fff; transform: translateY(-2px); }
.actions { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 32px; }

/* ---------- hero ---------- */
.hero { padding-top: clamp(56px, 9vh, 92px); padding-bottom: clamp(48px, 6vw, 72px); border-top: 0; }
.hero .lead { font-size: clamp(17px, 2vw, 21px); line-height: 1.6; max-width: 56ch; }

/* ---------- de hero op een laptop ----------

   Het kantoortje is het bewijsstuk van deze pagina en op een laptop stond het
   eronder. Gemeten 3 september 2026: de onderste rij robots eindigde op 1031
   pixels in een venster van 900 hoog, en op 1026 in een venster van 800. Je
   moest dus scrollen naar het enige stuk van de pagina dat beweegt, en de
   inspectieronde speelde zich af terwijl je nog naar de kop keek.

   De regel die dat oplost: op een scherm dat korter is dan waar dit voor
   ontworpen is krimpt eerst de witruimte en pas daarna de inhoud. Geen kleinere
   kop, geen kortere zin, geen weggelaten knop. Alles hieronder is marge of
   padding; er wordt niets aan de tekst zelf veranderd.

   Uitkomst gemeten: op 1440x900 en 1512x945 staat de hele scene boven de vouw.
   Op 1280x800 zie je er twee derde van en is een kleine scroll genoeg. Verder
   inkorten zou aan de kop of de zin moeten komen, en dat is de verkeerde ruil:
   dan win je een robot en verlies je de belofte waar hij bij hoort.           */
@media (min-width: 900px) and (max-height: 1000px) {
  body.home .hero { padding-top: clamp(28px, 4vh, 56px); }
  body.home .signatuur { margin-bottom: 12px; }
  body.home .hero h1 { margin-bottom: 14px; }
  body.home .hero .actions { margin-top: 20px; }
  body.home .rig { margin-top: clamp(20px, 2.6vh, 36px); }
  body.home .rig-top { padding-top: 11px; padding-bottom: 11px; }
  body.home .rig-body { padding-top: 10px; }
}

/* ---------- nachtploeg ---------- */
.rig {
  margin-top: clamp(44px, 6vw, 72px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  /* Geen overflow: hidden. De nu-marker en de stationslabels steken bewust
     buiten de baan uit; met een clip verdwijnt de bovenkant van de marker
     precies op het moment dat hij iets zegt. De kinderen raken de rand
     nergens, dus er valt niets af te ronden. */
}
.rig-top {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 26px;
  align-items: baseline;
  padding: 18px 22px;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 13px;
  color: var(--dim);
}
.rig-top b { color: var(--text); font-weight: 500; }
/* Het statusbolletje. Vroeger pulseerde dit met een vaste 2.4s-hartslag, wat
   decoratie is: de puls betekende niets en een pulserend bolletje is bovendien
   het meest herkenbare "er gebeurt iets"-cliché.

   Nu draagt hij tijd. Rond de bol staat een ring die in zestig seconden
   volloopt; app.js zet --seconde elke tik op de echte Amsterdamse seconde.
   Kijk je een halve minuut naar de pagina, dan zie je de wijzer bewegen en
   weet je dat de klok links ervan echt loopt en geen plaatje is.

   @property is nodig omdat een niet-geregistreerde custom property een string
   is: die interpoleert niet. We animeren hier weliswaar niet met keyframes maar
   met een waarde per seconde, en de registratie geeft ook de type-check en de
   initiële waarde die de conic-gradient nodig heeft. */
@property --seconde {
  syntax: "<angle>";
  /* inherits: true is hier geen detail. app.js zet de waarde op .dot, maar de
     ring die hem gebruikt is ::after. Met inherits: false erft het
     pseudo-element de waarde niet en blijft de ring op 0deg staan: hij ziet er
     dan uit alsof hij werkt en beweegt nooit. */
  inherits: true;
  initial-value: 0deg;
}

.dot {
  display: inline-block;
  position: relative;
  width: 8px; height: 8px;
  border-radius: 50%;
  margin-right: 8px;
  vertical-align: 1px;
  background: var(--dim);
}
.dot.on { background: var(--live); }
.dot.on::after {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  padding: 1px;
  background: conic-gradient(var(--live) var(--seconde), rgba(255, 255, 255, .10) 0);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
}
/* Geen transition: de ring tikt per seconde, zoals een secondewijzer. Vloeiend
   interpoleren zou bij de overgang van 354deg naar 0deg een seconde lang
   achteruit lopen, en dat is precies het tegenovergestelde van wat hij zegt.
   Bij reduced motion verandert er niets aan deze regel: één sprong per seconde
   is een statuswissel, geen animatie, en de stand blijft informatie. */
.rig-body { padding: 18px 22px 22px; }
.rig-note { font-family: var(--mono); font-size: 12px; color: var(--dim); margin: 18px 0 0; max-width: 64ch; }

/* ---------------------------------------------------------------
   Het kantoortje: de nachtploeg als levende scene (kantoor.mjs).
   Alles is decoratief (aria-hidden); het verhaal staat in .rig-note.
   De regisseur in app.js verplaatst robots door left/bottom/width te
   zetten; de transitie hieronder maakt daar een wandeling van. Zonder
   JavaScript of met reduced motion staat de momentopname stil en
   klopt hij nog steeds.
   --------------------------------------------------------------- */
.kantoor {
  position: relative;
  aspect-ratio: 1000 / 290;
  overflow: hidden;
  contain: layout style;
}
.kantoor svg { display: block; width: 100%; height: auto; overflow: visible; }
.kantoor > span { position: absolute; }

/* wand en vloer */
.k-vloer { left: 0; right: 0; border-top: 1px solid var(--line); }
.k-kozijn { fill: #0D1016; stroke: var(--line-strong); stroke-width: 1.5; }
.k-roede { stroke: var(--line-strong); stroke-width: 1; }
.k-maan { fill: #E8EAED; opacity: .82; }
.k-ster { fill: #E8EAED; opacity: .55; animation: k-twinkel 3.8s ease-in-out infinite; }
.k-ster:nth-of-type(3) { animation-delay: 1.3s; }
.k-zon { fill: var(--soon); display: none; }
.kantoor.dag .k-zon { display: block; }
.kantoor.dag .k-nachtlucht { display: none; }
.k-lijn { fill: none; stroke: var(--accent-soft); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.k-punt { fill: var(--accent-soft); }
.k-funnel rect { fill: var(--accent-soft); }
.k-funnel rect:nth-child(2) { opacity: .85; }
.k-funnel rect:nth-child(3) { opacity: .7; }
.k-funnel rect:nth-child(4) { opacity: .55; }
.k-funnel rect:nth-child(5) { opacity: .42; }
.k-funnel rect:nth-child(6) { opacity: .3; }
.k-poster-txt { font: 500 5px var(--mono); fill: var(--dim); text-anchor: middle; letter-spacing: .08em; }
.k-wijzerplaat { fill: var(--surface-2); stroke: var(--line-strong); stroke-width: 1.5; }
.k-as { fill: var(--muted); }
.k-uurwijzer, .k-minuutwijzer {
  stroke: var(--text);
  stroke-width: 2.5;
  stroke-linecap: round;
  transform-box: view-box;
  transform-origin: 50% 50%;
}
.k-uurwijzer { transform: rotate(var(--uur, 75deg)); }
.k-minuutwijzer { stroke-width: 1.6; transform: rotate(var(--minuut, 180deg)); }

/* meubilair */
.m-blad { fill: #262B33; }
.m-poot, .m-voet { fill: #1D2127; }
.m-scherm { fill: #10131A; stroke: rgba(255, 255, 255, .16); stroke-width: 1; }
.m-scherm.aan { animation: k-flikker 2.6s ease-in-out infinite; animation-delay: var(--d, 0s); }
@keyframes k-flikker { 0%, 100% { fill: #152441; } 55% { fill: #1B2E55; } }
/* Er wordt echt getikt: op een bezet bureau groeien drie regels er stukje
   bij beetje bij, en als het scherm vol is begint het opnieuw. steps() geeft
   het schokkerige ritme van tekst die per teken verschijnt. */
.m-toetsen {
  fill: #333A44;
  stroke: rgba(255, 255, 255, .18);
  stroke-width: .5;
  transform-box: fill-box;
  transform-origin: center;
}
/* Het toetsenbord veert mee met de armen: zelfde duur, tegenfase, zodat het
   leest als aanslag en niet als een los trillend blokje. */
.m-scherm.aan ~ .m-toetsen { animation: m-aanslag .26s linear infinite alternate; }
@keyframes m-aanslag { from { transform: translateY(0); } to { transform: translateY(.35px); } }
.m-muis { fill: #333A44; stroke: rgba(255, 255, 255, .18); stroke-width: .5; }
.m-regel {
  fill: var(--accent-soft);
  opacity: 0;
  transform-box: fill-box;
  transform-origin: left center;
}
.m-scherm.aan ~ .m-regel { animation: m-typ 4.2s steps(7, end) infinite; }
.m-scherm.aan ~ .m-r2 { animation-delay: 1.3s; }
.m-scherm.aan ~ .m-r3 { animation-delay: 2.6s; }
@keyframes m-typ {
  0% { opacity: 0; transform: scaleX(0); }
  1% { opacity: .85; transform: scaleX(0); }
  26% { opacity: .85; transform: scaleX(1); }
  92% { opacity: .85; transform: scaleX(1); }
  100% { opacity: 0; transform: scaleX(1); }
}
.k-machine { fill: #262B33; stroke: rgba(255, 255, 255, .12); stroke-width: 1; }
.k-strip { fill: var(--accent); }
.k-display { fill: #152441; }
.k-uitloop { fill: #1D2127; }
.k-beker { fill: #E8EAED; }
.k-damp { fill: none; stroke: var(--dim); stroke-width: 1.2; stroke-linecap: round; animation: k-stoom 2.2s ease-out infinite; }
.k-pot { fill: #262B33; }
.k-blad-1 { fill: #2E5F4E; }
.k-blad-2 { fill: #3B7A63; }
.k-lamp-arm { stroke: #3A4149; stroke-width: 2.5; stroke-linecap: round; }
.k-lamp-kap { fill: #3A4149; }
.k-gloed { fill: var(--soon); opacity: .16; animation: k-gloei 4s ease-in-out infinite; }
.kantoor.dag .k-gloed { opacity: 0; animation: none; }
.k-naambord { fill: #1D2127; stroke: rgba(255, 255, 255, .2); stroke-width: 1; }
.k-naam { font: 600 6.5px var(--mono); fill: var(--text); text-anchor: middle; letter-spacing: .1em; }
.k-post { fill: #D7DBE0; }
.k-postbak { fill: #1D2127; stroke: rgba(255, 255, 255, .2); stroke-width: 1; }

/* de GO-stempel bij een aflevering */
.k-go {
  position: absolute;
  top: -34%;
  right: 6%;
  font: 700 12px var(--mono);
  font-style: normal;
  color: #06251A;
  background: var(--live);
  padding: 2px 7px;
  border-radius: 999px;
  display: none;
}
/* Een stempel klapt neer en stopt hard: van groot (boven het papier) naar
   zijn plek, met een steile uitloop. Geen overshoot; echte stempels veren
   niet na. */
.k-bart.stempel .k-go { display: block; animation: k-stempel .32s cubic-bezier(.16, 1, .3, 1); }
@keyframes k-stempel { from { transform: scale(1.8) rotate(-14deg); opacity: 0; } 45% { opacity: 1; } to { transform: scale(1) rotate(-4deg); } }

/* ---------- de robots ---------- */
.bot {
  /* Bewust left/bottom/width en geen transform: de scene rekent in procenten
     van de containerbreedte en een robot wisselt van maat tussen de rijen.
     Een translate in px zou bij elke resize herrekend moeten worden voor een
     handvol elementjes van 30px; de layout-kost blijft binnen de contain van
     .kantoor. */
  transition-property: left, bottom, width;
  transition-timing-function: ease-in-out;
  transition-duration: 0s;
}
.bot svg { transform: scaleX(var(--f, 1)); transform-origin: 50% 50%; }
.b-kop, .b-lijf { fill: #232830; stroke: rgba(255, 255, 255, .22); stroke-width: 1; }
.b-ant { stroke: #3A4149; stroke-width: 1.6; stroke-linecap: round; }
.b-tip { fill: var(--soon); animation: k-tip 2.8s linear infinite; animation-delay: var(--d, 0s); }
.b-oog { fill: var(--accent-text); animation: k-knipper 4.7s linear infinite; animation-delay: var(--d, 0s); }
.b-arm { fill: #3A4149; }
.b-wiel { fill: #3A4149; stroke: rgba(255, 255, 255, .18); stroke-width: 1; }
/* De code op de borst spiegelt mee met de kijkrichting; dezelfde factor er
   nog een keer overheen zet hem weer rechtop. */
.b-code {
  font: 500 8px var(--mono);
  fill: var(--muted);
  text-anchor: middle;
  letter-spacing: .04em;
  transform: scaleX(var(--f, 1));
  transform-box: fill-box;
  transform-origin: center;
}
.b-doc rect { fill: #E8EAED; }
.b-doc line { stroke: #7A8290; stroke-width: .9; }
.b-doc, .b-cup { display: none; }
.bot.draagt .b-doc { display: block; }
.bot.koffie .b-cup { display: block; }
.b-cup rect { fill: #E8EAED; }
.b-cup .b-damp { animation-duration: 1.9s; }

/* activiteiten */

/* Werken is hier een echte houding, geen trillend blokje. Beide armen komen
   naar voren en landen op het toetsenbord (de bureauhoogte is daarop gerekend,
   zie kantoor.mjs), de verre arm schuift mee naar de bureaukant zodat het
   leest als twee handen naast elkaar, en het hoofd kantelt naar het scherm.
   De tikbeweging is een paar graden heen en weer, in tegenfase. */
.bot.werkt .b-arm-l, .bot.werkt .b-arm-r { transform-box: fill-box; transform-origin: 50% 6%; }
.bot.werkt .b-arm-r { animation: k-tik-nabij .26s ease-in-out infinite alternate; }
/* De verre arm staat een tint donkerder en een graad of tien anders, anders
   vallen beide armen samen tot één dikke streep. */
.bot.werkt .b-arm-l { animation: k-tik-ver .26s ease-in-out -.13s infinite alternate; fill: #2C323A; }
.bot.werkt .b-hoofd { transform: rotate(5deg); transform-box: fill-box; transform-origin: 50% 100%; }
@keyframes k-tik-nabij { from { transform: rotate(-58deg); } to { transform: rotate(-50deg); } }
@keyframes k-tik-ver {
  from { transform: translateX(19px) translateY(-1.5px) rotate(-40deg); }
  to { transform: translateX(19px) translateY(-1.5px) rotate(-32deg); }
}
.bot.loopt .b-al { animation: k-stap .3s ease-in-out infinite alternate; }
.bot.dut .b-oog { animation: none; opacity: .15; }
.bot.dut .b-tip { animation: none; opacity: .3; }

/* ---------- de zweep van de manager ----------

   De Chief of Staff loopt rond met een zweep en knalt er af en toe mee. Hij
   raakt niemand: de zweep gaat over zijn hoofd de lucht in, en de robots
   antwoorden erop in de tekstballonnen. De grap landt op hem.

   De rustzweep hangt altijd aan zijn hand, ook in de statische momentopname
   zonder JavaScript. De knalzweep en de knalstreepjes bestaan alleen tijdens
   `.knalt`, dat de regisseur er een halve seconde op zet.

   `overflow: visible` op zijn svg omdat de boog net buiten de viewBox van 36
   bij 46 komt. Alleen bij hem, niet bij de andere zeventien.                 */
.b-zweep { display: none; }
.bot[data-code="CS"] .b-zweep { display: block; }
.bot[data-code="CS"] svg { overflow: visible; }
/* De streng loopt taps toe: dik bij de greep, dun bij de punt. Dat en de
   losse greep zijn samen het verschil tussen een zweep en een kabel. */
.b-zweep path { fill: none; stroke: var(--muted); stroke-linecap: round; }
.b-zweep .z1 { stroke-width: 1.45; }
.b-zweep .z2 { stroke-width: .95; }
.b-zweep .z3 { stroke-width: .55; }
.b-zweep-greep {
  stroke: var(--text);
  stroke-width: 3.4;
  stroke-linecap: round;
}
.b-zweep-knal, .b-knal { display: none; }
.b-knal line { stroke: var(--text); stroke-width: 1.3; stroke-linecap: round; }

.bot.knalt .b-zweep-rust { display: none; }
.bot.knalt .b-zweep-knal {
  display: block;
  transform-box: view-box;
  transform-origin: 34.6px 28.2px;
  animation: k-zweep .58s cubic-bezier(.2, .9, .3, 1) both;
}
.bot.knalt .b-knal {
  display: block;
  transform-box: view-box;
  transform-origin: 7.4px 5.8px;
  animation: k-knal .3s ease-out .1s both;
}
/* De zwiep gaat op 1,6 keer de robot uit. In het kantoortje is een robot
   ongeveer dertig pixels breed, en op die maat is een zweep die binnen zijn
   eigen omtrek blijft een krul. Buiten zijn omtrek is het een zweep. Kan
   omdat zijn svg op overflow: visible staat.

   En hij blijft even staan. Op de eerste versie stond de boog 150 ms op zijn
   hoogste punt en dat is korter dan de tijd die je nodig hebt om je blik te
   verplaatsen: je zag de nasleep, nooit de slag. Nu is de hele beweging 580 ms
   waarvan de helft stilstand op het hoogste punt. Dat is nog steeds binnen het
   bewegingsbudget van deze site (500 ms per animatie geldt voor wat bij het
   laden vuurt; dit vuurt op eigen initiatief, verderop in de pagina). */
@keyframes k-zweep {
  0% { transform: rotate(-38deg) scale(.8); opacity: 0; }
  26% { transform: rotate(6deg) scale(1.6); opacity: 1; }
  76% { transform: rotate(-2deg) scale(1.58); opacity: 1; }
  100% { transform: rotate(0deg) scale(1.3); opacity: 0; }
}
@keyframes k-knal {
  from { transform: scale(.4); opacity: 0; }
  35% { transform: scale(2); opacity: 1; }
  70% { transform: scale(2.4); opacity: .9; }
  to { transform: scale(2.8); opacity: 0; }
}

/* ---------- schrikken van de knal ----------

   De reactie was tot nu toe alleen sneller tikken, en dat begint pas nadat je
   al niet meer kijkt. Dit is het moment ervoor: een korte schrik, even opkijken,
   en dan pas het tempo.

   Drie dingen tegelijk, allemaal binnen een derde seconde:
   - het lijf maakt een sprongetje en kantelt een graad of vier;
   - de handen gaan van het toetsenbord (de tik-animatie staat even stil);
   - wie aan een bureau zit kijkt op van zijn scherm en gaat daarna weer terug.

   De schrik loopt als een golf door de rij: robots die verder van de knal staan
   krijgen een vertraging naar afstand, gezet door de regisseur. Iedereen op
   hetzelfde moment laten springen leest als een storing in plaats van als een
   reactie.

   De transform staat op het lijf (`.b-al`) en niet op de span eromheen: die
   span verplaatst zich met left/bottom en is van die twee de enige die de
   scene positioneert. */
.bot.schrikt .b-al {
  transform-box: view-box;
  transform-origin: 50% 100%;
  animation: k-schrik .34s cubic-bezier(.2, .9, .3, 1);
}
@keyframes k-schrik {
  0% { transform: translateY(0) rotate(0deg); }
  35% { transform: translateY(-3.2px) rotate(-6.5deg); }
  65% { transform: translateY(-.8px) rotate(2.5deg); }
  100% { transform: translateY(0) rotate(0deg); }
}

/* handen van het toetsenbord tijdens de schrik */
.bot.schrikt.werkt .b-arm-l,
.bot.schrikt.werkt .b-arm-r { animation-play-state: paused; }

/* en even opkijken van het scherm: de vaste kanteling van .werkt wordt
   overschreven door deze animatie en staat er daarna weer */
.bot.schrikt.werkt .b-hoofd { animation: k-opkijken .5s ease-out; }
@keyframes k-opkijken {
  0% { transform: rotate(5deg); }
  30% { transform: rotate(-7deg); }
  100% { transform: rotate(5deg); }
}

/* Wie net de zweep heeft gehad tikt een paar seconden sneller. Dat is de hele
   pointe: het effect is zichtbaar en het is tijdelijk, precies zoals opjagen
   werkt. De duur staat op de animatie die er al is, dus er komt geen tweede
   animatie bij die met de eerste vecht. */
.bot.werkt.opgejaagd .b-arm-r,
.bot.werkt.opgejaagd .b-arm-l { animation-duration: .1s; }
.k-bureau.opgejaagd .m-scherm.aan ~ .m-toetsen { animation-duration: .1s; }
.k-bureau.opgejaagd .m-scherm.aan ~ .m-regel { animation-duration: 1.5s; }

/* dienst: dit is het enige statuslicht dat iets betekent, dus het mag gloeien */
.bot.dienst .b-tip { fill: var(--live); animation: k-dienst 1.6s ease-in-out infinite; }
.bot.dienst .b-code { fill: var(--live); }

/* Tekstballonnen. De inhoud komt uit de regisseur (app.js), die hele
   gesprekken tussen robots afspeelt: een ballon tegelijk, om en om. De
   staart wijst standaard naar linksonder; bij robots aan de rechterrand
   klapt de ballon om zodat hij binnen de scene blijft. */
.b-bubbel {
  position: absolute;
  bottom: 92%;
  left: 58%;
  font: 500 clamp(7px, 1.1vw, 11px)/1.5 var(--mono);
  font-style: normal;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  border-radius: 8px 8px 8px 2px;
  padding: 0 5px;
  white-space: nowrap;
  display: none;
}
.bot.zegt .b-bubbel { display: block; animation: k-zweef 1.6s ease-in-out infinite alternate; }
.bot.bubbel-links .b-bubbel { left: auto; right: 42%; border-radius: 8px 8px 2px 8px; }

@keyframes k-stap { from { transform: translateY(0); } to { transform: translateY(-1.8px); } }
@keyframes k-knipper { 0%, 91% { opacity: 1; } 95% { opacity: .1; } 100% { opacity: 1; } }
@keyframes k-tip { 0%, 78% { opacity: 1; } 88% { opacity: .25; } 100% { opacity: 1; } }
@keyframes k-dienst { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
@keyframes k-twinkel { 0%, 100% { opacity: .55; } 50% { opacity: .15; } }
@keyframes k-stoom { 0% { opacity: 0; transform: translateY(2px); } 35% { opacity: .7; } 100% { opacity: 0; transform: translateY(-4px); } }
@keyframes k-gloei { 0%, 100% { opacity: .16; } 50% { opacity: .09; } }
@keyframes k-zweef { from { transform: translateY(0); } to { transform: translateY(-2px); } }

/* ---------- logowall ---------- */
.logos { padding: clamp(38px, 5vw, 56px) 0; border-top: 1px solid var(--line); }
.logos p { font-family: var(--mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--dim); margin: 0 0 22px; }
.logos ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 14px 42px; align-items: center; }
.logos li { font-size: clamp(15px, 1.7vw, 20px); font-weight: 700; letter-spacing: -.01em; color: #7E8794; }

/* ---------- kaarten ---------- */
.grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: 44px; }
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 26px 24px;
  display: flex;
  flex-direction: column;
  transition: border-color .2s ease, transform .2s ease;
}
/* Hover-differentiatie. Tot nu toe deed alles met een rand hetzelfde: 3px
   omhoog. Zes soorten kaarten met identiek gedrag betekent dat de beweging
   niets meer zegt. Nu antwoordt elk kaarttype in zijn eigen taal.

   Productkaart = een systeem dat draait. Zijn merkstreep loopt vol. */
.card:hover { border-color: var(--line-strong); }
.card-merk i { transition: opacity .25s ease, background .25s ease; }
.card:hover .card-merk i {
  opacity: 1;
  background: linear-gradient(90deg, var(--merk), transparent 100%);
}
.card p { font-size: 15px; margin-bottom: 14px; }
.pill {
  display: inline-flex;
  align-items: center;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 5px 11px;
  border-radius: 999px;
  margin: 0 0 16px;
  align-self: flex-start;
}
.pill.live { background: rgba(52, 211, 153, .12); color: #6EE7B7; border: 1px solid rgba(52, 211, 153, .32); }
.pill.soon { background: rgba(251, 191, 36, .10); color: #FCD34D; border: 1px solid rgba(251, 191, 36, .30); }

/* Merkteken bovenaan de productkaart: icoon links, accentlijn in de
   merkkleur ernaartoe. De lijn vult de restbreedte, dus dit werkt op
   1280px net zo goed als op 375px. */
.card-merk { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
.card-merk svg { width: 32px; height: 32px; flex: 0 0 auto; display: block; }
.card-merk i { display: block; flex: 1; height: 2px; border-radius: 1px; background: linear-gradient(90deg, var(--merk), transparent 88%); opacity: .55; }
.proof { font-size: 14px; color: var(--dim); border-left: 2px solid rgba(255, 255, 255, .14); padding-left: 14px; }
.card .out { margin-top: auto; font-family: var(--mono); font-size: 13px; letter-spacing: .03em; }
.card .out.none { color: var(--dim); }

/* ---------- hoe het werkt ---------- */
.grid4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-top: 44px; }
.step { border-top: 2px solid rgba(255, 255, 255, .16); padding-top: 18px; }
.step .num { font-family: var(--mono); font-size: 12px; color: var(--dim); letter-spacing: .1em; display: block; margin-bottom: 10px; }
.step h3 { font-size: 17px; }
.step p { font-size: 15px; margin: 0; }

.roster { margin-top: 52px; padding-top: 34px; border-top: 1px solid var(--line); }
.roster > p { font-family: var(--mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--dim); margin: 0 0 20px; }
.codes { display: flex; flex-wrap: wrap; gap: 10px; list-style: none; margin: 0; padding: 0; }
/* Het kaartje is sinds concept 05 een knop: hij opent het dossier van die
   specialist onder het kantoortje. De <li> houdt de vorm en de positie (de
   rondlopende rand van .nu hangt eraan), de knop draagt het uiterlijk. */
.codes li {
  display: flex;
  position: relative;
  border-radius: 8px;
}
.codes li > button {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 9px 13px;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color .18s ease, background .18s ease;
}
.codes li > button:hover { border-color: var(--line-strong); }
.codes li > button.open {
  border-color: var(--accent-line, var(--accent-soft));
  background: var(--surface-2);
}
.codes i {
  font-family: var(--mono);
  font-style: normal;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
  color: #0A0B0D;
  background: #7E8794;
  border-radius: 4px;
  padding: 3px 6px;
  line-height: 1;
}
.codes span { font-size: 13px; color: var(--muted); white-space: nowrap; }
.codes time { font-family: var(--mono); font-size: 12px; color: var(--dim); }

/* ---------- incident log ---------- */
.log { margin-top: 40px; border: 1px solid var(--line); border-radius: 14px; overflow: hidden; background: var(--surface); }
.log-row {
  display: grid;
  grid-template-columns: 112px 96px 1fr;
  gap: 18px;
  padding: 18px 22px;
  border-top: 1px solid var(--line);
  align-items: start;
}
.log-row:first-child { border-top: 0; }
.log-row .when { font-family: var(--mono); font-size: 12px; color: var(--dim); letter-spacing: .04em; padding-top: 2px; }
.tag {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 4px 8px;
  border-radius: 4px;
  text-align: center;
  white-space: nowrap;
  justify-self: start;
  margin-top: 2px;
}
.tag.f { background: rgba(248, 113, 113, .12); color: var(--danger); border: 1px solid rgba(248, 113, 113, .28); }
.tag.w { background: rgba(251, 191, 36, .10); color: #FCD34D; border: 1px solid rgba(251, 191, 36, .28); }
.log-row p { margin: 0; font-size: 15px; max-width: var(--kolom); }

/* ---------- de nachtstrip ----------

   Veertien staafjes naast de cyclusregel. De regel ernaast zegt wat er
   vannacht gebeurde; deze strip zegt of het een gewoonte is. Hoogte is het
   aantal cycli van die nacht, het rode deel onderin is wat er niet afliep, en
   een nacht zonder cyclus is een streepje op de vloer.

   Die lege streepjes zijn het punt. Een strip zonder gaten zou betekenen dat
   er nooit een nacht is overgeslagen, en dat is niet waar. Op een pagina die
   claimt dat je ziet wat er echt gebeurt, is een eerlijk gat meer waard dan
   een vol vakje.

   Geen canvas, geen bibliotheek: veertien elementjes met een hoogte-variabele.
   De tooltip doet de browser via `title`, en de hele strip draagt dezelfde
   uitkomst in woorden als aria-label.                                        */
.strip-blok { display: inline-flex; align-items: center; gap: 9px; }
.strip-label { color: var(--dim); }
.strip {
  display: inline-flex;
  align-items: flex-end;
  gap: 2px;
  height: 16px;
}
.strip i {
  display: block;
  width: 4px;
  border-radius: 1px;
  min-height: 1px;
  height: calc(var(--h, 0) * 16px);
  background: linear-gradient(to top,
    var(--danger) var(--stop, 0%), var(--accent-soft) var(--stop, 0%));
}
.strip i.leeg { height: 2px; background: var(--line-strong); }

/* De wijzerplaat naast de digitale tijd. Zelfde variabelen als de wandklok in
   het kantoortje eronder, want er is maar één tijd; die staan daarom op .rig
   en niet meer op de scene. */
.rig-klok { display: inline-flex; align-items: center; gap: 8px; }
.wijzerplaat { width: 15px; height: 15px; display: block; flex: none; }

/* ---------- de opstart ----------

   Bij het laden gaan de schermen één voor één aan, in de volgorde waarin de
   ploeg 's nachts aan de beurt is (--boot is de index in die volgorde), en de
   klok loopt in dezelfde beweging naar de echte tijd. Achttien bureaus in
   ongeveer zeven tienden van een seconde, daarna nooit meer.

   Dit is het ene geregisseerde moment dat DESIGN.md toestaat, en het draagt
   staat: aan het eind van de beweging staat het kantoor precies zoals het er
   op dit moment echt voor staat.

   De flikkering die daarna doorloopt begint bewust pas ná de opstart, anders
   vechten twee animaties om dezelfde `fill`.                                 */
@keyframes k-boot { from { fill: #10131A; } to { fill: #152441; } }

.m-scherm.aan {
  animation:
    k-boot .3s ease-out backwards,
    k-flikker 2.6s ease-in-out infinite;
  animation-delay:
    calc(var(--boot, 0) * 38ms),
    calc(var(--boot, 0) * 38ms + 300ms);
}

/* De wijzers lopen mee: van twaalf uur naar nu, in dezelfde beweging als de
   schermen. `@property` maakt de hoek een echte waarde in plaats van een
   string, anders klapt hij halverwege om in plaats van te draaien. De
   keyframe heeft alleen een `from`: het eindpunt is de waarde die er op dat
   moment op staat, en dat is de echte tijd. */
@property --uur { syntax: "<angle>"; inherits: true; initial-value: 0deg; }
@property --minuut { syntax: "<angle>"; inherits: true; initial-value: 0deg; }
@keyframes k-klok-op { from { --uur: 0deg; --minuut: 0deg; } }
/* De animatie zelf staat verderop bij de entree, in dezelfde declaratie als
   `entree`. Een losse `.rig { animation: ... }` haalt het niet: `.hero .rig`
   is specifieker en overschrijft de hele shorthand, inclusief deze. Twee
   animaties op één element horen ook in één declaratie thuis. */

/* De uitkomst van de nacht in de kopregel (concept 01). `.rig-stop` is wat er
   niet afliep, `.oud` is wat app.js ervan maakt zodra het cijfer ouder is dan
   36 uur: dan staat er geen cijfer meer maar de datum waarop het stil viel. */
.rig-stop { color: var(--danger); }
.rig-cyclus.oud { color: var(--dim); }

/* ---------- het spookcijfer ----------

   Eén groot getal per sectie, weggezakt in de achtergrond. Drie producten,
   achttien specialisten, vier incidenten: het cijfer waar de sectie over
   gaat, in de systeemletter, op de dekking van een watermerk.

   Waarom dit geen versiering is: het middenstuk van de pagina was tekst in
   vakken, van boven tot onder, en daardoor las het als documentatie in plaats
   van als een pagina met een ritme. Dit geeft elke sectie een eigen gewicht
   zonder er een illustratie bij te verzinnen die niets betekent.

   Het beweegt niet, het is aria-hidden en het staat achter de inhoud. Op
   smalle schermen valt het weg: daar is de kolom de hele breedte en zou het
   onder de tekst gaan liggen.                                                */
section:has(> .spook) { position: relative; overflow: hidden; }
.spook {
  display: none;
  position: absolute;
  top: 0;
  right: 2.5vw;
  transform: translateY(-24%);
  font-family: var(--mono);
  font-weight: 700;
  font-size: clamp(180px, 26vw, 420px);
  line-height: .8;
  letter-spacing: -.04em;
  color: color-mix(in srgb, var(--text) 5%, transparent);
  pointer-events: none;
  user-select: none;
}
@media (min-width: 1100px) { .spook { display: block; } }

/* ---------- de nacht als tijdas (concept 04) ----------

   De pagina is een nacht. Bovenaan is het 22:30 en begint de ploeg, onderaan
   is het 07:00 en ligt de briefing klaar. Twee dingen dragen dat:

   1. De grond warmt op. `--nacht` loopt van 0 naar 1 terwijl je scrollt
      (gezet door app.js) en stuurt een vaste, warme laag achter de inhoud.
      Het is bewust klein: van #0A0B0D naar iets dat er nauwelijks anders
      uitziet als je het naast elkaar legt, maar wel voelbaar is als je er
      een minuut doorheen scrollt. Dat is het verschil tussen sfeer en effect.

   2. Het licht komt en blijft. Vanaf het lichte blok blijft de pagina licht
      tot en met de afsluiter. Vroeger sprong hij daarna terug het donker in,
      waardoor het lichte blok een onderbreking was in plaats van een
      aankomst.

   Waarom dit mag van DESIGN.md: beweging draagt staat. Scrollen ís hier door
   de nacht bewegen, hetzelfde argument als de tijdlijn op /ai-team. Met
   reduced motion blijft de grond onbewogen (.stil-nacht) en blijft alleen de
   betekenis staan: het licht aan het eind, en de tijd in de rail.          */

html.stil-nacht body.home::after { opacity: 0; }

body.home::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* Warm licht dat van onderen opkomt, zoals een ochtend dat doet. */
  background: radial-gradient(120% 80% at 50% 120%, #241a12 0%, #140f0c 45%, transparent 75%);
  opacity: calc(var(--nacht, 0) * .85);
}

/* De rail met de tijd. Alleen waar hij naast de leeskolom past: bij smallere
   vensters zou hij over de inhoud vallen en dan is het geen tweede lezing
   meer maar een obstakel. */
.tijdrail { display: none; }

@media (min-width: 1400px) {
  .tijdrail {
    display: block;
    position: fixed;
    left: 30px;
    top: 120px;
    bottom: 80px;
    width: 58px;
    z-index: 40;
    pointer-events: none;
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: .08em;
    color: var(--dim);
    font-variant-numeric: tabular-nums;
  }
  .tijdrail-lijn {
    position: absolute;
    left: 4px;
    top: 0;
    bottom: 0;
    width: 1px;
    /* Eén grijs dat zowel op het nachtcanvas als op het crème van de ochtend
       zichtbaar blijft, want de rail staat vast en de grond schuift eronder
       door. */
    background: rgba(140, 148, 160, .38);
  }
  /* De gevulde kant is hoe ver de nacht is, de stip is waar je nu bent. */
  .tijdrail-lijn i {
    position: absolute;
    left: 0;
    top: 0;
    width: 1px;
    background: var(--accent-soft);
    height: calc(var(--nacht, 0) * 100%);
    /* Zodra het ochtend is heeft de rail zijn werk gedaan en trekt hij zich
       vrijwel helemaal terug. Een blauwe streep die over het lichte blok
       blijft staan leest als iets dat vergeten is uit te gaan; het label
       07:00 vertelt op dat punt genoeg. */
    opacity: calc(1.1 - var(--nacht, 0) * .98);
  }
  .tijdrail-lijn i::after {
    content: "";
    position: absolute;
    left: -3px;
    bottom: -3px;
    width: 7px;
    height: 7px;
    border-radius: 999px;
    background: var(--accent-soft);
  }
  .tijdrail-nu {
    position: absolute;
    left: 16px;
    top: calc(var(--nacht, 0) * 100%);
    transform: translateY(-50%);
    font-weight: 400;
    color: var(--accent-text);
  }
  .tijdrail-eind {
    position: absolute;
    left: 16px;
    bottom: 0;
    transform: translateY(50%);
    opacity: calc(1 - var(--nacht, 0));
  }
}

/* ---------- de ochtend ----------
   Eén klasse die de tokens omdraait. Alles in deze secties gebruikt dezelfde
   variabelen als de rest van de site, dus de hele wereld kantelt mee zonder
   dat er per component een lichte variant bij komt. Wat hier wél expliciet
   moet: de plekken waar in het donker #fff als hover-kleur staat, want wit
   op crème is niets.                                                        */
.ochtend {
  --bg: var(--light);
  --surface: #FFFFFF;
  --surface-2: #EDEAE3;
  --line: rgba(0, 0, 0, .12);
  --line-strong: rgba(0, 0, 0, .30);
  --text: var(--light-text);
  --muted: var(--light-muted);
  --dim: var(--light-dim);
  --accent-text: #1D4ED8;
  --accent-soft: #2563EB;
  background: var(--bg);
  color: var(--text);
}
.ochtend h1, .ochtend h2, .ochtend h3 { color: #0A0B0D; }
.ochtend a:hover { color: var(--accent); }
.ochtend .btn-ghost:hover { border-color: var(--accent); color: var(--accent); }
.ochtend + .ochtend { border-top: 0; }
/* De afsluiter deelt de grond met de rest van de ochtend. Wit-op-crème zou
   er een tweede blok van maken, terwijl het één aankomst is. */
.end.ochtend { background: var(--bg); }

/* ---------- licht blok ---------- */
.light { background: var(--light); color: var(--light-text); border-top: 0; }
.light h2, .light h3 { color: #0A0B0D; }
.light p { color: var(--light-muted); }
.light .kicker { color: var(--light-dim); }
.light-inner { display: grid; grid-template-columns: 1.25fr .75fr; gap: 56px; align-items: center; }
/* `height: auto` is hier het hele punt en geen detail. Het img-element draagt
   width="1080" height="1350" voor layout-stabiliteit, en die attributen werken
   als presentational hint: zonder een CSS-height wint `height: 1350px` en werd
   de foto uitgerekt tot 374 bij 1350. `object-fit: cover` maakte daar dan een
   sterk ingezoomde uitsnede van, waarbij zijn hoofd bovenlangs werd afgesneden.
   Met height:auto volgt de hoogte de breedte en staat de hele foto er weer op. */
.light img {
  width: 100%;
  height: auto;
  aspect-ratio: 4/5;
  object-fit: cover;
  object-position: 50% 42%;
  border-radius: 14px;
  background: #E7E4DE;
}
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px; margin-top: 38px; padding-top: 32px; border-top: 1px solid rgba(0, 0, 0, .12); }
.stats b { display: block; font-size: clamp(28px, 3.6vw, 42px); font-weight: 700; letter-spacing: -.03em; color: #0A0B0D; line-height: 1; }
.stats span { display: block; font-size: 14px; color: #5B6470; margin-top: 8px; line-height: 1.4; }

/* ---------- werk met mij ---------- */
.mini { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin-top: 38px; }
.mini > div { border: 1px solid var(--line); border-radius: 12px; padding: 22px; }
.mini h3 { font-size: 17px; }
.mini p { font-size: 14px; margin: 0; }

/* ---------- slot ---------- */
.end { text-align: center; background: var(--surface); }
.end h2, .end p { margin-left: auto; margin-right: auto; }
.end h2 { max-width: 20ch; }
.end .actions { justify-content: center; }

/* ---------- footer ---------- */
.site-footer { background: #0D0D0D; border-top: 1px solid var(--line); padding: 44px 0 56px; }
.site-footer .wrap { display: flex; flex-wrap: wrap; gap: 18px 40px; align-items: baseline; }
.site-footer p, .site-footer a { font-size: 14px; color: #A7AEBB; margin: 0; }
.site-footer a:hover { color: #fff; }
.site-footer nav { display: flex; gap: 22px; flex-wrap: wrap; margin-left: auto; }

/* ---------- responsive ---------- */
@media (max-width: 980px) {
  .grid3, .grid4, .mini { grid-template-columns: 1fr 1fr; }
  .light-inner { grid-template-columns: 1fr; gap: 34px; }
  /* geen hamburger: de nav schuift onder de merknaam, horizontaal scrollbaar */
  .site-header .wrap { flex-wrap: wrap; gap: 0 16px; padding-top: 12px; }
  .site-nav {
    order: 3;
    width: 100%;
    margin-left: 0;
    overflow-x: auto;
    gap: 18px;
    padding: 4px 0 12px;
    scrollbar-width: none;
    /* BK-21: fade-hint rechts, zodat je ziet dat de balk verder scrollt.
       Puur CSS-mask, geen extra element en geen JS. */
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 40px), transparent);
    mask-image: linear-gradient(90deg, #000 calc(100% - 40px), transparent);
  }
  .site-nav::-webkit-scrollbar { display: none; }
  .lang { margin-left: auto; border-left: 0; }
}
@media (max-width: 640px) {
  .grid3, .grid4, .mini { grid-template-columns: 1fr; }
  .stats { grid-template-columns: 1fr; gap: 20px; }
  .log-row { grid-template-columns: 1fr; gap: 8px; }
  .actions .btn { width: 100%; }
  .stop b { display: none; }
  .rig-top { font-size: 12px; }
  .site-footer nav { margin-left: 0; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ---------------------------------------------------------------
   Overgang tussen pagina's (view transitions)

   Deze site is 26 losse HTML-bestanden. Zonder dit voelt elke klik als
   een witte flits en een nieuwe start: je verliest het idee dat je in
   één systeem rondloopt. Met deze regels blijft de kop staan waar hij
   staat en wisselt alleen de inhoud. Dat is de goedkoopste manier om
   een statische site als één oppervlak te laten voelen.

   Het is progressive enhancement in de zuiverste vorm: browsers die
   `@view-transition` niet kennen (Firefox, oudere Safari) negeren dit
   blok volledig en navigeren zoals altijd. Er is geen JavaScript bij
   betrokken en geen fallback nodig.
   --------------------------------------------------------------- */
@view-transition { navigation: auto; }

/* Kop en voet zijn dezelfde elementen op elke pagina, dus krijgen ze een
   eigen naam. Daarmee worden ze bij de overgang behandeld als hetzelfde
   ding dat blijft staan, niet als iets dat verdwijnt en opnieuw verschijnt. */
.site-header { view-transition-name: kop; }
.site-footer { view-transition-name: voet; }
main { view-transition-name: inhoud; }

::view-transition-old(kop),
::view-transition-new(kop),
::view-transition-old(voet),
::view-transition-new(voet) {
  animation: none;
}

/* De inhoud wisselt met een korte overvloeiing plus 6px opkomst. Uitgaand
   sneller dan inkomend, want vertrek hoeft geen aandacht en aankomst wel. */
::view-transition-old(inhoud) {
  animation: vt-weg .16s cubic-bezier(.4, 0, 1, 1) both;
}
::view-transition-new(inhoud) {
  animation: vt-aan .3s cubic-bezier(.16, 1, .3, 1) both;
}
@keyframes vt-weg { to { opacity: 0; } }
@keyframes vt-aan { from { opacity: 0; transform: translateY(var(--vt-opkomst, 6px)); } }

/* Bij reduced motion blijft de overgang bestaan, maar zonder verplaatsing en
   korter. Een overvloeiing is continuïteit, geen beweging: hem helemaal
   weghalen zou de gebruiker juist de oriëntatie kosten die hij nodig heeft. */
@media (prefers-reduced-motion: reduce) {
  :root { --vt-opkomst: 0px; }
  ::view-transition-old(inhoud) { animation-duration: .1s; }
  ::view-transition-new(inhoud) { animation-duration: .12s; }
}

/* ---------------------------------------------------------------
   Inhoudspagina's (/about, /nl/over)

   De homepage is een landingspagina met een klok en een tijdlijn. Alles
   daarna is leestekst, en die heeft andere regels nodig: een smallere
   kolom, want een regel van 90 tekens leest niemand uit, en meer lucht
   tussen de blokken zodat je ziet waar een onderwerp ophoudt.
   --------------------------------------------------------------- */
.hero-pg { padding-bottom: 2rem; }
.hero-pg h1 { max-width: 20ch; }

.pg-sectie { padding: 3.5rem 0; border-top: 1px solid var(--lijn, rgba(255,255,255,.09)); }
.pg-sectie h2 { margin: .4rem 0 1.2rem; max-width: 24ch; }
/* 68 tekens per regel. Breder leest slechter, ook op een groot scherm. */
.pg-sectie p { max-width: var(--kolom); }
.pg-sectie > .wrap > p + p { margin-top: 1rem; }

/* ---------- het dossier onder het kantoortje (concept 05) ----------

   Neemt de plek van de uitleg in zodra je een specialist aanklikt. Geen
   overlay en geen dialoogvenster: je blijft naar hetzelfde kantoor kijken,
   er staat alleen andere tekst onder. Daarom heeft dit blok ook exact de
   typografie van .rig-note en niet die van een kaart.                       */

.kantoor.klikbaar .bot { cursor: pointer; }

.dossier {
  margin: 18px 0 0;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--surface-2);
  padding: 14px 16px;
}

.dossier-kop {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 12px;
  margin-bottom: 10px;
}
.dossier-code {
  font-family: var(--mono);
  font-style: normal;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
  color: #0A0B0D;
  background: var(--accent-soft);
  border-radius: 4px;
  padding: 3px 6px;
  line-height: 1;
}
.dossier-kop b { font-size: 15px; color: var(--text); font-weight: 700; }
.dossier-dienst {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--dim);
  letter-spacing: .04em;
  display: inline-flex;
  gap: 6px;
}
.dossier-dicht {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  color: var(--muted);
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px 11px;
  cursor: pointer;
}
.dossier-dicht:hover { color: var(--text); border-color: var(--line-strong); }

.dossier-taak { margin: 0 0 10px; font-size: 15px; color: var(--muted); max-width: var(--kolom); }

.dossier-nacht {
  margin: 0;
  font-size: 14px;
  color: var(--text);
  max-width: var(--kolom);
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.dossier-nacht.stil { color: var(--dim); font-family: var(--mono); font-size: 12px; }
.dossier-label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
}

.dossier-links {
  margin: 12px 0 0;
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .04em;
}

/* ---------- de nachtlog (concept 02) ----------

   Een logboek, geen artikel. Datum en cijfers in de systeemletter, want die
   komen uit de runlogs; het onderwerp in de leesletter, want dat schreef een
   specialist. Dezelfde taakverdeling als in de rest van de site.

   Geen kaarten met schaduw: een nacht is een blok met een datumregel erboven
   en regels eronder, zoals een logboek eruitziet.                            */

.nacht {
  padding: 20px 0;
  border-top: 1px solid var(--line);
  max-width: none;
}
.nacht:first-of-type { border-top: 0; }

.nacht h3 {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 16px;
  margin: 0 0 12px;
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--text);
}

.nacht-cijfers {
  font-size: 12px;
  color: var(--dim);
  letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
}

.nacht-rijen { list-style: none; margin: 0; padding: 0; max-width: none; }
.nacht-rijen li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 14px;
  padding: 8px 0;
  border-bottom: 1px dashed var(--line);
}
.nacht-rijen li:last-child { border-bottom: 0; }

.nacht-rol {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--accent-text);
  letter-spacing: .04em;
  min-width: 12rem;
}
.nacht-onderwerp { color: var(--muted); font-size: 15px; flex: 1 1 14rem; }
.nacht-prio {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  color: var(--dim);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 1px 7px;
}
.nacht-verdict {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  border-radius: 999px;
  padding: 2px 9px;
  white-space: nowrap;
}
.nacht-verdict.ok { color: var(--live); border: 1px solid var(--live); }
.nacht-verdict.terug { color: var(--danger); border: 1px solid var(--danger); }

/* De regel die iemand bewust voor publicatie schreef krijgt een eigen regel
   onder de rij, in de leesletter: dat is de enige echte zin op deze pagina. */
.nacht-zin {
  flex: 1 1 100%;
  margin: 4px 0 0;
  color: var(--text);
  font-size: 15px;
  max-width: var(--kolom);
}

.nacht-stil { color: var(--dim); font-size: 14px; margin: 0; max-width: none; }

@media (max-width: 640px) {
  .nacht-rol { min-width: 0; }
}

/* ---------- de merkpoort (concept 03) ----------

   Het gereedschap op /brand-gate en /nl/merkpoort. Twee kolommen: links wat
   jij schrijft in de leesletter, rechts wat de machine ervan vindt in de
   systeemletter. Dat onderscheid is de hele typografische stellingname van
   deze site en het is hier letterlijk de scheidslijn tussen de twee vakken.

   De uitslag zit in een blok met een eigen rand in plaats van in een kaart met
   schaduw: dit is een terminal-uitvoer, geen notificatie.                    */

.poort {
  display: grid;
  gap: 18px;
  margin-top: 1.8rem;
  max-width: none;
}

@media (min-width: 860px) {
  /* Twee kolommen, en een derde rij eronder voor de robot: die hoort onder het
     tekstvak te staan en niet ernaast, zodat hij naar het oordeel kijkt. */
  .poort { grid-template-columns: 1fr 1fr; gap: 22px; align-items: start; }
}

.poort label {
  display: block;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
  margin-bottom: 8px;
}

.poort textarea {
  width: 100%;
  min-height: 210px;
  resize: vertical;
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.6;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  padding: 14px 16px;
}

.poort-privacy {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--dim);
  margin: 10px 0 0;
  max-width: none;
}

.poort-uit {
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--surface);
  padding: 16px 18px;
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.7;
  min-height: 210px;
}

.poort-kop {
  display: flex;
  align-items: center;
  gap: 12px;
  justify-content: space-between;
  padding-bottom: 12px;
  margin-bottom: 12px;
  border-bottom: 1px solid var(--line);
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
}

.poort-verdict {
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  letter-spacing: .08em;
  white-space: nowrap;
}
.poort-verdict.door { color: var(--live); border: 1px solid var(--live); }
.poort-verdict.terug { color: var(--danger); border: 1px solid var(--danger); }

.poort-meting {
  margin: 0 0 12px;
  color: var(--dim);
  font-size: 12px;
  max-width: none;
  font-variant-numeric: tabular-nums;
}

.poort-lijst { list-style: none; margin: 0; padding: 0; max-width: none; }
.poort-lijst li {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  padding: 9px 0;
  border-bottom: 1px solid var(--line);
}
.poort-lijst li:last-child { border-bottom: 0; }
.poort-regel { color: var(--danger); white-space: nowrap; }
.poort-detail { color: var(--muted); flex: 1 1 12rem; }
.poort-detail em { display: block; color: var(--dim); font-style: normal; font-size: 12px; }

.poort-schoon { color: var(--live); margin: 0; max-width: none; }
.poort-leeg, .poort-geenjs { color: var(--dim); margin: 0; max-width: none; }
.poort-noot {
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  color: var(--dim);
  font-size: 12px;
  line-height: 1.65;
  max-width: none;
}

.pg-sub { margin-top: 1.8rem; max-width: var(--kolom); }
.pg-sub h3 { margin: 0 0 .35rem; font-size: 1.05rem; }
.pg-sub p { margin: 0; }

.pg-lijst { margin: 1.2rem 0 0; padding-left: 1.1rem; max-width: var(--kolom); }
.pg-lijst li { margin-bottom: .5rem; }

.pg-tabel { width: 100%; max-width: var(--kolom); margin-top: 1.4rem; border-collapse: collapse; }
.pg-tabel th, .pg-tabel td {
  text-align: left; padding: .7rem .9rem; vertical-align: top;
  border-bottom: 1px solid var(--lijn, rgba(255,255,255,.09));
}
.pg-tabel th { font-family: var(--mono, monospace); font-size: .78rem; text-transform: uppercase; letter-spacing: .06em; }
/* Kleur in plaats van opacity. Opacity dimt de tekst én laat de achtergrond
   erdoorheen komen, waardoor het feitelijke contrast onder de 4.5:1 zakt terwijl
   de gedeclareerde kleur nog prima lijkt. Een expliciete tint is meetbaar. */
.pg-tabel td:first-child { color: var(--dim); }

/* Een tabel is het eerste dat breekt op een telefoon. Laat hem in zijn
   eigen vak schuiven in plaats van de hele pagina mee te trekken. */
@media (max-width: 640px) {
  .pg-tabel { display: block; overflow-x: auto; }
}

/* De ploeg: rol plus starttijd. De code ervoor is dezelfde afkorting als op
   de tijdlijn van de homepage, zodat je de twee aan elkaar kunt knopen. */
.pg-team td:first-child { color: var(--muted); }
/* Met de wat-kolom erbij mag de ploegtabel breder zijn dan de leeskolom:
   drie kolommen in 68ch wordt dringen. De rolkolom blijft op één regel. */
/* Niet 100%: de wat-kolom kreeg dan 729px en dat is ~100 tekens per regel.
   max-width op de cel zelf helpt niet, want het tabel-layoutalgoritme negeert
   dat; de tabel zelf smaller maken wel. */
.pg-team-breed { max-width: 880px; }
.pg-team-breed td:first-child { white-space: nowrap; }
.pg-team-breed td:nth-child(2) { color: var(--muted); }
/* De wat-kolom is lopende tekst en niet een cel met twee woorden: zonder
   maximum loopt hij op een breed scherm door tot 93 tekens per regel. */
.pg-team-breed td:last-child { max-width: 46ch; }
.pg-code {
  display: inline-block; min-width: 2.4em; margin-right: .6em;
  font-family: var(--mono, monospace); font-style: normal;
  font-size: .78rem; color: var(--dim);
}
.pg-team time { font-family: var(--mono, monospace); font-size: .82rem; color: var(--dim); }
.pg-knop { margin-top: 1.6rem; }

/* ---------------------------------------------------------------
   Sterka-showcase (BK-7). Drie productbeelden onder de productkaarten:
   het enige bewijs voor een product zonder publieke site.
   --------------------------------------------------------------- */
.showcase { margin-top: 30px; }
.showcase-note {
  font-family: var(--mono);
  /* Was 11px uppercase als label. Nu het een gewone zin is, is het ook gewone
     lopende tekst, en 11px lopende tekst leest niemand. */
  font-size: 13px;
  letter-spacing: .02em;
  color: var(--dim);
  margin: 0 0 14px;
  max-width: var(--kolom);
}
.showcase-rij { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.showcase figure {
  margin: 0;
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  background: var(--surface);
  transition: border-color .2s ease;
}
.showcase figure:hover { border-color: var(--line-strong); }
.showcase img { width: 100%; height: auto; display: block; }
.showcase figcaption {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .02em;
  color: var(--dim);
  padding: 10px 14px;
  border-top: 1px solid var(--line);
}
@media (max-width: 980px) { .showcase-rij { grid-template-columns: 1fr; } }

/* ---------------------------------------------------------------
   Paginafoto in de hero (BK-8, /about en /nl/over). Subtiel kader:
   een dunne rand plus een verschoven echo-lijn, geen ronde bubbel.
   --------------------------------------------------------------- */
.hero-met-foto .wrap {
  display: grid;
  grid-template-columns: 1.15fr .85fr;
  gap: 52px;
  align-items: center;
}
.pg-foto { margin: 0; position: relative; max-width: 400px; justify-self: end; }
.pg-foto img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 12px;
  border: 1px solid var(--line-strong);
}
.pg-foto::after {
  content: "";
  position: absolute;
  inset: 14px -14px -14px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  z-index: -1;
}
@media (max-width: 980px) {
  .hero-met-foto .wrap { grid-template-columns: 1fr; gap: 36px; }
  .pg-foto { justify-self: start; max-width: 360px; }
}

/* ---------------------------------------------------------------
   Nachtdienst-tijdlijn (BK-9, /ai-team). Tijd-as links, content rechts,
   op elk formaat. De poorten (03:45 beeld, 04:00 merk) zijn ruiten in
   plaats van rondjes. app.js zet 's nachts .nu en .klaar (BK-10);
   zonder JS is dit een statisch, volledig leesbaar schema.
   --------------------------------------------------------------- */
.tl-status {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .02em;
  color: var(--dim);
  margin: 2rem 0 0;
  max-width: none;
}
.tl-status b { color: var(--live); font-weight: 500; }
.tl {
  list-style: none;
  margin: 1.2rem 0 0;
  padding: 0;
  position: relative;
  max-width: 720px;
}
.tl::before {
  content: "";
  position: absolute;
  left: 72px;
  top: 8px;
  bottom: 8px;
  width: 2px;
  background: rgba(255, 255, 255, .12);
}
/* De tijdlijn vult mee met je scrollpositie.

   Dit is de enige plek op de site waar scroll-gedreven beweging inhoudelijk
   klopt: naar beneden scrollen door dit schema ís door de nacht bewegen, van
   22:30 naar 07:00. De lijn die meegroeit vertelt je waar in die nacht je nu
   kijkt. Op elke andere sectie zou dezelfde techniek decoratie zijn.

   Geen JavaScript, geen scroll-listener: `animation-timeline: view()` laat de
   browser dit op de compositor doen. We animeren scaleY en niet height, want
   height is een layout-eigenschap en die kost een reflow per frame.

   Dubbele poort: `@supports` voor browsers die de techniek niet kennen
   (Firefox staat nog achter een vlag) en `no-preference` zodat er bij reduced
   motion helemaal geen bewegend element ontstaat. In beide gevallen blijft de
   statische lijn uit `.tl::before` staan en is het schema volledig leesbaar. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .tl-baan { view-timeline-name: --tl; }

    .tl::after {
      content: "";
      position: absolute;
      left: 72px;
      top: 8px;
      bottom: 8px;
      width: 2px;
      background: var(--accent-soft);
      transform: scaleY(0);
      transform-origin: top;
      /* Losse longhands, geen `animation:`-shorthand. De shorthand zet
         animation-duration op 0s, en met een scroll-tijdlijn betekent 0s dat
         de animatie in één keer klaar is: de lijn blijft dan op scaleY(0)
         staan en er beweegt nooit iets. `auto` laat de duur samenvallen met
         het bereik hieronder. */
      animation-name: tl-vullen;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: --tl;
      /* `cover` en niet `entry`/`exit`: deze lijst is met achttien stappen
         hoger dan het scherm, en dan zijn de entry- en exit-fases zo lang dat
         de lijn pas vult als je er al voorbij bent. `cover` loopt van het
         eerste tot het laatste contact met het scherm, dus de vulling volgt
         precies de scroll door het schema. */
      animation-range: cover 20% cover 78%;
    }
    @keyframes tl-vullen { to { transform: scaleY(1); } }
  }
}

.tl-stap {
  position: relative;
  display: grid;
  grid-template-columns: 52px 22px 1fr;
  column-gap: 10px;
  padding: 0 0 1.5rem;
}
.tl-stap:last-child { padding-bottom: 0; }
.tl-stap time {
  font-family: var(--mono);
  font-size: .8rem;
  color: var(--dim);
  text-align: right;
  padding-top: .15em;
  white-space: nowrap;
}
.tl-punt { position: relative; }
.tl-punt::before {
  content: "";
  position: absolute;
  left: 50%;
  top: .42em;
  width: 9px;
  height: 9px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: #2A2F37;
  border: 1px solid rgba(255, 255, 255, .3);
}
.tl-body h3 { font-size: 17px; margin-bottom: 4px; }
.tl-body p { font-size: 14px; color: var(--dim); margin: 0; max-width: 50ch; }
.tl-poortlabel {
  font-family: var(--mono);
  font-style: normal;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #FCD34D;
  border: 1px solid rgba(251, 191, 36, .35);
  border-radius: 4px;
  padding: 2px 7px;
  margin-left: 8px;
  vertical-align: 2px;
  white-space: nowrap;
}
.tl-poort .tl-punt::before {
  border-radius: 2px;
  transform: translateX(-50%) rotate(45deg);
  border-color: rgba(251, 191, 36, .6);
}
.tl-eind time, .tl-eind .tl-body h3 { color: var(--text); }
.tl-eind .tl-punt::before { border-color: rgba(255, 255, 255, .55); }
/* live stand, gezet door app.js */
.tl-stap.klaar .tl-punt::before { background: var(--accent-soft); border-color: var(--accent-soft); }
.tl-stap.klaar time { color: var(--muted); }
.tl-stap.nu .tl-punt::before {
  background: var(--live);
  border-color: var(--live);
  box-shadow: 0 0 0 4px rgba(52, 211, 153, .18);
}
.tl-stap.nu time, .tl-stap.nu .tl-body h3 { color: #fff; }
.tl-stap.nu .tl-body p { color: var(--muted); }
.tl.af .tl-stap .tl-punt::before { background: var(--accent-soft); border-color: var(--accent-soft); }
.tl.af .tl-poort .tl-punt::before { border-color: rgba(251, 191, 36, .6); }

/* roosterkaartjes op de homepage, zelfde klok (BK-10) */
.codes li.klaar i { background: var(--accent-soft); }
.codes li.nu > button { border-color: rgba(52, 211, 153, .5); }
.codes li.nu i { background: var(--live); }
.codes li.nu span { color: #fff; }

/* De agent die op dit moment draait krijgt een rondlopende rand. Beweging
   betekent hier iets: dit gebeurt nu, terwijl je kijkt. Daarom staat hij
   uitsluitend op .nu en niet op de kaartjes eromheen, en daarom is dit het
   enige bewegende element in deze sectie.

   @property maakt de hoek animeerbaar. Zonder registratie is een custom
   property een string: die interpoleert niet en klapt halverwege om. Alle drie
   de velden zijn verplicht, anders wordt de regel genegeerd zonder melding. */
@property --loophoek {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
@keyframes loop-rand { to { --loophoek: 360deg; } }

/* Hetzelfde randje op de badge in de kopregel: dat motief betekent op deze
   site "dit gebeurt nu", en dus hoort het overal waar dat aan de orde is en
   nergens anders. Op een verdict van vannacht zou het liegen. */
#nu-wrap {
  position: relative;
  border-radius: 999px;
  padding: 2px 10px;
  background: color-mix(in srgb, var(--live) 8%, transparent);
}
#nu-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(from var(--loophoek),
    transparent 0turn, var(--live) .12turn, transparent .3turn, transparent 1turn);
  animation: loop-rand 3s linear infinite;
  pointer-events: none;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
}

.codes li.nu::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(from var(--loophoek),
    transparent 0turn, var(--live) .12turn, transparent .3turn, transparent 1turn);
  animation: loop-rand 3s linear infinite;
  pointer-events: none;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
}

/* ---------------------------------------------------------------
   De entree (vervangt de scroll-reveal uit BK-11)

   Wat hier stond, was een reveal op elke sectie: alles kwam 8px omhoog
   gefadet zodra het in beeld kwam. Twaalf secties met exact dezelfde
   beweging betekent dat geen van die bewegingen nog iets zegt, en het
   maakte de pagina tijdens het scrollen leger dan hij is.

   Ervoor in de plaats staat één geregisseerde entree, alleen bovenaan,
   alleen bij het laden: de merkregel, de kop, de belofte, de knoppen en
   dan de console, in die volgorde. Dat is de volgorde waarin je het ook
   zou willen lezen, en de console komt als laatste omdat dat het bewijs
   is waar de rest naartoe werkt.

   Belangrijk: opacity 0 staat uitsluitend ín de keyframe, met
   animation-fill-mode backwards. Valt de animatie weg (reduced motion
   zet `animation: none`), dan staat alles gewoon op zijn natuurlijke
   waarde. Er is geen enkele toestand waarin de hero onzichtbaar blijft
   hangen, ook niet zonder JS: dit is puur CSS.
   --------------------------------------------------------------- */
@keyframes entree {
  from { opacity: 0; transform: translateY(10px); }
}

.hero .signatuur,
.hero h1,
.hero .lead,
.hero .actions,
/* De console erft de entree en krijgt de klok-opstart erbij: op het moment dat
   de balk verschijnt lopen de wijzers naar de echte tijd en gaan de schermen
   in pipeline-volgorde aan. Eén moment, drie dingen die hetzelfde zeggen.
   Beide animaties in één declaratie, want een tweede regel met alleen
   `animation` zou deze in zijn geheel overschrijven. */
.hero .rig {
  animation:
    entree .62s cubic-bezier(.16, 1, .3, 1) backwards,
    k-klok-op .9s cubic-bezier(.16, 1, .3, 1) backwards;
}
.hero .signatuur { animation-delay: 0s; }
.hero h1         { animation-delay: .06s; }
.hero .lead      { animation-delay: .13s; }
.hero .actions   { animation-delay: .2s; }
.hero .rig       { animation-delay: .3s, .3s; }

/* ---------------------------------------------------------------
   Blog (BK-15 + BK-16). Zelfde donkere idioom, maar dit is leestekst
   van 1.300+ woorden: smallere kolom (68ch), meer wit tussen koppen,
   en tabellen die in hun eigen vak scrollen in plaats van de pagina
   open te duwen op 375px.
   --------------------------------------------------------------- */
.post-hero { border-top: 0; padding-bottom: 0; }
.post-hero h1 { max-width: 26ch; font-size: clamp(32px, 5.2vw, 56px); }
.post-hero .kicker a { color: var(--dim); }
.post-hero .kicker a:hover { color: #fff; }

.post-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: baseline;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--dim);
  margin: 0;
  max-width: none;
}
.post-meta time { color: var(--muted); }

/* TL;DR: de samenvatting vóór het artikel, in het systeemdata-idioom */
/* Geen gekleurde balk op de linkerrand meer. Een dikke accentrand op één zijde
   van een afgerond blok is het meest herkenbare AI-UI-cliché dat er is, en het
   accent hoort hier bij het label, niet bij de doos. De doos is gewoon een doos;
   het streepje voor het label zegt waar je bent. */
.tldr {
  margin: 30px 0 0;
  max-width: var(--kolom);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 18px 22px;
}
.tldr-label {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--accent-text);
  margin: 0 0 8px;
}
.tldr-label::before {
  content: "";
  width: 18px;
  height: 1px;
  background: var(--accent-text);
  flex: 0 0 auto;
}
.tldr p:last-child { margin: 0; font-size: 15px; line-height: 1.6; }

/* het artikel zelf */
.post-body { border-top: 0; padding-top: clamp(36px, 5vw, 56px); }
.post-body h2 { font-size: clamp(24px, 3.2vw, 34px); margin: 2.6rem 0 .9rem; max-width: 30ch; }
.post-body h3 { margin: 1.9rem 0 .5rem; }
.post-body p { max-width: var(--kolom); }
.post-body ul, .post-body ol { max-width: var(--kolom); margin: 0 0 16px; padding-left: 1.25rem; color: var(--muted); }
.post-body li { margin-bottom: .5rem; }
.post-body li::marker { color: var(--dim); }
.post-body hr { border: 0; border-top: 1px solid var(--line); max-width: var(--kolom); margin: 2.6rem 0; }
.post-body a { text-decoration: underline; text-decoration-color: rgba(127, 169, 247, .4); text-underline-offset: 3px; }
.post-body a:hover { text-decoration-color: currentColor; }

/* Contextuele links in lopende tekst (BK-56). Sinds die wijziging staan er
   links middenin alinea's op de inhoudspagina's en op de homepage, en daar
   was kleur alleen te weinig voor: het accent haalt tegen de broodtekst
   ongeveer 1,1:1, dus wie die tint niet onderscheidt ziet de link niet.
   Zelfde vorm als in een blogpost, een gedempte onderstreping die bij hover
   dichtloopt, maar met color-mix op currentColor in plaats van een vast
   blauw: op het lichte blok is het accent donkerblauw en zou een vaste
   lichtblauwe lijn daar verdwijnen. Knoppen, leeskaarten en de kicker
   houden hun eigen vorm. */
main p:not(.kicker) a:not(.btn),
main .pg-lijst li a {
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
  text-underline-offset: 3px;
}
main p:not(.kicker) a:not(.btn):hover,
main .pg-lijst li a:hover { text-decoration-color: currentColor; }

/* tabellen: eigen scrollvak, want een 6-koloms tabel past nooit op 375px */
.tabel-scroll { overflow-x: auto; margin: 1.4rem 0; max-width: 100%; }
.post-body table { border-collapse: collapse; min-width: 560px; width: 100%; }
.post-body th, .post-body td {
  text-align: left;
  padding: .65rem .9rem;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
  color: var(--muted);
}
.post-body th {
  font-family: var(--mono);
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--dim);
}
.post-body td strong { color: var(--text); }

/* Eén pull-quote per post: de sterkste zin, groot. De blauwe balk links is
   vervangen door een haarlijn boven de zin: dezelfde onderbreking van de
   leesstroom, zonder de gekleurde tab die elke AI-gegenereerde pagina heeft.
   De nadruk komt van de korpsgrootte en het wit eromheen, niet van kleur. */
.pull {
  margin: 2.8rem 0;
  padding: 1.4rem 0 .2rem;
  border-top: 1px solid var(--line-strong);
  max-width: 56ch;
}
.pull p {
  margin: 0;
  font-size: clamp(20px, 2.6vw, 27px);
  line-height: 1.35;
  font-weight: 600;
  letter-spacing: -.02em;
  color: #fff;
  max-width: none;
}

/* Diagrammen in de leesstroom (BK-19): statische inline-SVG's, kleuren via
   de site-tokens zodat het beeld hetzelfde idioom volgt als de systeemdata.
   Geen JS, geen animatie. De brede tijdband scrollt in zijn eigen vak, net
   als de tabellen, zodat hij op 375px leesbaar blijft in plaats van te
   krimpen tot 5px-letters. */
.diagram { margin: 2.4rem 0; max-width: var(--kolom); }
/* Een brede figuur hoort niet in de leeskolom van 58ch te passen: dan scrolt
   hij ook op een groot scherm terwijl er ruimte zat is. */
.diagram-breed { max-width: 100%; }
.diagram-vak {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 18px;
  overflow-x: auto;
}
/* min-width houdt de tekst in de figuur leesbaar op 375px (het vak scrollt
   dan, net als de tabellen); max-width voorkomt dat een smal diagram op
   desktop opblaast tot 25px-letters. */
.diagram-vak svg { display: block; width: 100%; height: auto; min-width: 360px; max-width: 560px; margin: 0 auto; }
.diagram-breed .diagram-vak svg { min-width: 720px; max-width: none; }
.diagram figcaption {
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.55;
  color: var(--dim);
  margin-top: 10px;
  padding: 0 2px;
  /* Zonder maximum loopt het onderschrift van een brede figuur over de volle
     kolombreedte door, en dat is 112 tekens per regel. In een monospace is 1ch
     exact één teken, dus dit maximum is letterlijk 64 tekens per regel. */
  max-width: 64ch;
}
/* Let op de `.diagram-vak`-prefix op de tekstklassen hieronder. Zonder die
   prefix wint `.diagram-vak text` (0,1,1) van `.dg-wit` (0,1,0) en krijgt élke
   tekst in élk diagram dezelfde grijstint. Dat was tussen BK-19 en 2026-08-10
   het geval: de hiërarchie stond in de SVG maar was op het scherm onzichtbaar,
   en op het accentblok zakte de tekst bovendien door de contrastgrens. */
.diagram-vak text { fill: var(--muted); font-family: var(--sans); }
.diagram-vak .dg-naam { fill: var(--text); font-weight: 600; }
.diagram-vak .dg-tijd { fill: var(--dim); font-family: var(--mono); }
.diagram-vak .dg-wit { fill: #fff; font-weight: 600; }
/* Tekst op het accentvlak. #98A0AC op #2563EB haalt 2.3:1; deze tint haalt
   ruim 7:1 en houdt het blok toch rustiger dan puur wit. */
.diagram-vak .dg-op-accent { fill: #DCE6FF; }
.dg-as { stroke: var(--line-strong); stroke-width: 2; }
.dg-lijn { stroke: var(--line); stroke-width: 1; }
.dg-punt { fill: var(--accent); }
.dg-eind { fill: var(--live); }
.dg-poort { fill: var(--surface-2); stroke: var(--soon); stroke-width: 2; }
.dg-pijl { stroke: var(--dim); stroke-width: 1.5; fill: none; }
.dg-pijlkop { fill: var(--dim); }
.dg-vak { fill: var(--surface-2); stroke: var(--line-strong); }
/* De kringloop op de homepage kent een nachtstand. app.js zet .nu en .klaar op
   de fase-groepen, precies zoals bij de tijdlijn en het rooster; de figuur is
   daarmee hetzelfde stuk informatie als de klok erboven, alleen dan getekend.
   Zonder JS gebeurt hier niets en klopt de figuur nog steeds. */
.dg-fase rect { transition: stroke .3s ease, fill .3s ease; }
/* `:not(.dg-poort)` zodat een poort geel blijft als hij klaar is: geel betekent
   op deze site "hier kan werk worden tegengehouden", en dat is ook overdag waar.
   Groen (nu) wint wel van geel, want dat is een stand van dit moment. */
.dg-fase.klaar rect:not(.dg-poort) { stroke: var(--accent-soft); }
.dg-fase.nu rect { stroke: var(--live); stroke-width: 2; }
.dg-fase.nu .dg-wit { fill: var(--live); }
@media (prefers-reduced-motion: reduce) {
  .dg-fase rect { transition: none; }
}
.dg-vak-accent { fill: var(--accent); }
.dg-balk { fill: var(--accent); }
.dg-balk-dim { fill: var(--line-strong); }

/* ---------- de robot op de tijdlijn (BK-86) ----------

   Op /ai-team vult de tijdlijn zich met je scrollpositie. Deze robot loopt op
   de kop van die vulling mee, dus hij staat altijd precies bij het station dat
   op dit moment aan de beurt is in jouw scroll. Zelfde tijdlijn, zelfde
   bereik: ze kunnen niet uit elkaar lopen.

   `--tl-h` is de hoogte van de baan in pixels, één keer gemeten door app.js en
   opnieuw bij een resize. Zonder die meting zou dit een percentage van de eigen
   hoogte zijn, en dat is achttien pixels in plaats van veertienhonderd.       */

.tl-baan { position: relative; }
.tl-bot {
  display: none;
  position: absolute;
  left: 64px;
  top: 0;
  width: 18px;
  height: 18px;
  z-index: 2;
}
.tl-bot svg { width: 100%; height: 100%; display: block; }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .tl-bot {
      display: block;
      animation-name: tl-rijdt;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: --tl;
      animation-range: cover 20% cover 78%;
    }
  }
}
@keyframes tl-rijdt {
  from { transform: translateY(0); }
  to { transform: translateY(calc(var(--tl-h, 0px) - 18px)); }
}

/* ---------- de robot bij de vouwlijst (BK-86) ----------

   Eén robot in de kantlijn van de FAQ, bij de vraag die openstaat. Verhuist
   mee als je een andere opent, verdwijnt als je alles dichtklapt. `--y` is het
   midden van de geopende kop, gezet door app.js.

   Alleen op schermen die de kantlijn hebben: op smal loopt hij over de tekst,
   en dan is hij een obstakel in plaats van een aanwijzer.                    */

.pg-vouwlijst { position: relative; }
.vouw-bot {
  display: none;
  position: absolute;
  left: -34px;
  top: 0;
  width: 20px;
  height: 20px;
  opacity: 0;
  transform: translateY(calc(var(--y, 0px) - 10px));
}
.vouw-bot svg { width: 100%; height: 100%; display: block; }

@media (min-width: 1080px) {
  .vouw-bot { display: block; }
  .pg-vouwlijst.vouw-actief .vouw-bot { opacity: 1; }
}

@media (min-width: 1080px) and (prefers-reduced-motion: no-preference) {
  .vouw-bot { transition: transform .3s cubic-bezier(.22, 1, .36, 1), opacity .2s ease; }
}

/* ---------- de robot bij de poort (BK-86) ----------

   Naast het oordeel op /brand-gate staat de merkpoort zelf, in de vorm van een
   robot. Hij knikt als je tekst erdoor komt en houdt een stopbord op als hij
   zakt. Dat is dezelfde uitkomst als de tekst ernaast, maar dan zonder lezen:
   je ziet aan de overkant van het scherm of het goed zit.

   Waarom dit langs de bewegingsregel komt: het is de uitkomst van wat jij net
   getypt hebt, dus de beweging draagt een toestand. Hij beweegt ook alleen op
   het moment dat die toestand verandert, niet doorlopend.                    */

.poort-bot {
  display: none;
  width: 34px;
  height: 34px;
  align-self: end;
  justify-self: start;
}
@media (min-width: 860px) {
  .poort-bot { display: block; }
}
.poort-bot svg { width: 100%; height: 100%; display: block; }
.pb-oog { fill: var(--dim); transition: fill .2s ease; }
.pb-bord { opacity: 0; }
.pb-bord rect { fill: var(--danger); }
.pb-bord .pb-streep { fill: #0A0B0D; }

/* schoon: hij kijkt op en knikt één keer */
.poort.oordeel-door .pb-oog { fill: var(--live); }
.poort.oordeel-terug .pb-oog { fill: var(--danger); }

@media (prefers-reduced-motion: no-preference) {
  .poort.oordeel-door .poort-bot { animation: pb-knik .5s cubic-bezier(.22, 1, .36, 1); }
  .poort.oordeel-terug .pb-bord { animation: pb-bord-op .3s cubic-bezier(.22, 1, .36, 1) forwards; }
}
/* Zonder beweging blijft de betekenis staan: groene of rode ogen, en bij een
   afkeuring het bord gewoon zichtbaar zonder dat het omhoog komt. */
@media (prefers-reduced-motion: reduce) {
  .poort.oordeel-terug .pb-bord { opacity: 1; }
}

@keyframes pb-knik {
  40% { transform: translateY(-4px) rotate(-4deg); }
  70% { transform: translateY(0) rotate(2deg); }
}
@keyframes pb-bord-op {
  from { opacity: 0; transform: translateY(6px) rotate(-12deg); }
  to { opacity: 1; transform: none; }
}

/* ---------- de bouwmodus (BK-85) ----------

   Op verzoek: de pagina valt uit elkaar en wordt in vier seconden opnieuw
   neergezet, met per fase de specialist die aan de beurt is. Dit is de enige
   beweging op de site die langer duurt dan het budget van 800ms, en dat mag,
   want de bezoeker heeft er zelf op geklikt. Het budget geldt voor beweging die
   je overkomt, niet voor beweging waar je om vraagt. Zie DESIGN.md.

   Twee dingen houden hem veilig. Er wordt geen DOM weggehaald: de blokken
   krijgen alleen een klasse en een index, en elke animatie eindigt op de stand
   waar het blok zonder de show ook staat (`animation-fill-mode: both`). En de
   knop verschijnt niet bij reduced motion, want een bouwshow zonder beweging is
   geen show.

   `--b` is de vertraging in milliseconden, per blok gezet door app.js.        */

.bouwknop {
  display: none;
  margin: 0;
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .06em;
  color: var(--dim);
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 7px 15px;
  cursor: pointer;
  transition: color .18s ease, border-color .18s ease;
}
.bouwknop:hover { color: var(--text); border-color: var(--line-strong); }

/* De voet is een flexrij met de links rechts uitgelijnd. Zonder eigen regel
   werd de knop ertussen gedrukt, en met flex-basis op de knop zelf werd hij
   over de volle breedte uitgerekt: flex-basis stuurt de hoofdas, niet de
   uitlijning. Vandaar een rij eromheen die de breedte pakt, met de knop erin
   op zijn eigen maat. */
.bouwrij { flex-basis: 100%; margin-top: 4px; }

@media (prefers-reduced-motion: no-preference) {
  .bouwknop:not([hidden]) { display: inline-block; }

  /* de blokken worden neergezet */
  html.bouwt #main > * {
    animation: bouw-blok .5s cubic-bezier(.22, 1, .36, 1) both;
    animation-delay: calc(var(--b, 0) * 1ms);
  }

  /* de laag met het label en de robot die het werk doet */
  .bouwlaag {
    position: fixed;
    inset: 0;
    z-index: 60;
    pointer-events: none;
  }
  .bouwlabel {
    position: absolute;
    left: 50%;
    top: 84px;
    transform: translateX(-50%);
    font-family: var(--mono);
    font-size: 12px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--accent-text);
    background: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    padding: 6px 14px;
    white-space: nowrap;
  }
  /* de laatste fase is de GO, en die is groen zoals overal op deze site */
  .bouwlabel.go { color: var(--live); border-color: var(--live); }

  /* De robot loopt in vier seconden vier keer heen en weer over de vloer van
     het scherm: hij zet neer, loopt terug, zet de volgende neer. */
  .bouwbot {
    position: absolute;
    bottom: 26px;
    left: 0;
    width: 22px;
    height: 22px;
    animation: bouw-loopt 4.6s cubic-bezier(.45, 0, .55, 1) both;
  }
  .bouwbot svg { width: 100%; height: 100%; display: block; }
}

@keyframes bouw-blok {
  from { opacity: 0; transform: translateY(16px) scale(.985); }
}
@keyframes bouw-loopt {
  0%   { transform: translateX(-4vw); }
  22%  { transform: translateX(74vw); }
  38%  { transform: translateX(18vw); }
  56%  { transform: translateX(82vw); }
  72%  { transform: translateX(30vw); }
  88%  { transform: translateX(88vw); }
  100% { transform: translateX(106vw); }
}

/* ---------- de aankomst van de ploeg ----------

   Drie robotjes lopen bij het laden over de onderrand van de kopbalk. Achter de
   eerste aan vult zich een accentlijn en verschijnen de navigatie-items, alsof
   ze de balk komen neerzetten. Zevenhonderdzestig milliseconden, en daarna zijn
   ze weg.

   Wat er expliciet NIET gebeurt: wachten. De kop, de tekst en de knoppen staan
   er vanaf het eerste frame; dit speelt zich af op de chroom eromheen. Een
   bezoeker die meteen doorleest merkt er niets van, en een bezoeker die kijkt
   ziet de ploeg langslopen. Dat is het verschil tussen een aankomst en een
   laadscherm.

   De animaties hangen aan `html.aankomst`, die `app.js` alleen zet als
   sessionStorage nog niets weet. Zonder JavaScript, bij reduced motion en bij
   het tweede bezoek in dezelfde sessie gebeurt er dus niets, en mist er ook
   niets: de balk staat er gewoon.                                            */

.ploeg { display: none; }

@media (prefers-reduced-motion: no-preference) {
  html.aankomst .ploeg {
    display: block;
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 22px;
    overflow: hidden;
    pointer-events: none;
  }

  /* de rail die ze leggen: een accentlijn die met de eerste robot meegroeit en
     daarna wegtrekt in de gewone onderrand van de balk */
  html.aankomst .ploeg::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 2px;
    background: var(--accent-line);
    transform-origin: left;
    animation: ploeg-rail .76s cubic-bezier(.22, 1, .36, 1) both;
  }

  html.aankomst .ploeg i {
    position: absolute;
    bottom: 1px;
    left: 0;
    width: 18px;
    height: 18px;
    animation: ploeg-loopt .62s linear both;
    animation-delay: calc(var(--p, 0) * 70ms);
  }
  html.aankomst .ploeg svg { width: 100%; height: 100%; display: block; }
  .pl-lijf { fill: var(--muted); }
  .pl-oog { fill: var(--accent-text); }

  /* de navigatie komt achter ze aan tevoorschijn */
  html.aankomst .site-nav a {
    animation: ploeg-navkomt .28s ease-out both;
    animation-delay: calc(120ms + var(--i, 0) * 42ms);
  }
}

@keyframes ploeg-rail {
  from { transform: scaleX(0); opacity: 1; }
  70% { transform: scaleX(1); opacity: 1; }
  to { transform: scaleX(1); opacity: 0; }
}
/* -6% tot 106%: ze komen van buiten beeld en lopen er ook weer uit, zodat er
   geen robot uit het niets verschijnt of halverwege oplost. */
@keyframes ploeg-loopt {
  from { transform: translateX(-6vw); }
  to { transform: translateX(106vw); }
}
@keyframes ploeg-navkomt { from { opacity: 0; transform: translateY(-4px); } }

/* ---------- leesvoortgang onder de kopbalk ----------

   Een haarlijn die meeloopt met hoe ver je in de pagina bent. Alleen op pagina's
   die lang genoeg zijn om te verdwalen: blogposts, /ai-team, de nachtlog en de
   FAQ dragen daarvoor `body.lees`. De homepage niet: die heeft de tijdrail al,
   en twee voortgangsmeters op één scherm is er een te veel.

   Het is letterlijk de scrollpositie, dus het draagt staat. `scroll(root block)`
   is de hele tijdlijn: geen JavaScript, geen scroll-listener, en de animatie
   loopt op transform en dus op de compositor.                                */

body.lees .site-header::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: var(--accent-line);
  transform: scaleX(0);
  transform-origin: left;
}

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    body.lees .site-header::after {
      animation-name: lees-voortgang;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: scroll(root block);
    }
  }
}
@keyframes lees-voortgang { to { transform: scaleX(1); } }

/* ---------- panelen die opengaan in plaats van openspringen ----------

   De FAQ-vouwlijst en het dossierpaneel onder het kantoortje klappen open met
   een sprong, want een hoogte naar `auto` was tot voor kort niet te animeren.
   `interpolate-size: allow-keywords` maakt dat wel mogelijk: het vertelt de
   browser dat hij tussen een lengte en een intrinsieke maat mag interpoleren.

   Chromium kan dit sinds versie 129, Firefox en Safari nog niet (stand
   september 2026). De terugval is precies het gedrag van vandaag: het paneel
   gaat open, alleen zonder de beweging. Daarom staat hier geen enkele
   fallback-truc met max-height of een gemeten pixelwaarde: die kosten code en
   liegen over de hoogte, en dit kost zes regels en is over een jaar de norm.

   Beide zitten achter een no-preference-media: dit is een toestandswissel en
   die mag beweging dragen, maar niet als iemand daarom vraagt.             */

@supports (interpolate-size: allow-keywords) {
  @media (prefers-reduced-motion: no-preference) {
    :root { interpolate-size: allow-keywords; }

    /* De FAQ. `::details-content` is het pseudo-element om de inhoud van een
       <details> heen; zonder dat pseudo is er niets om een hoogte aan te geven,
       want de inhoud is een anonieme box. `content-visibility` moet mee in de
       transitie met `allow-discrete`, anders knipt de browser de inhoud weg op
       het moment dat de hoogte nog aan het lopen is. */
    .pg-vouw::details-content {
      block-size: 0;
      overflow: clip;
      transition: block-size .22s cubic-bezier(.16, 1, .3, 1),
                  content-visibility .22s allow-discrete;
    }
    .pg-vouw[open]::details-content { block-size: auto; }

    /* Het dossier onder het kantoortje. Dat gaat van `hidden` naar zichtbaar,
       dus de beginstand komt uit `@starting-style`: die geldt precies één frame
       lang, op het moment dat het element voor het eerst gerenderd wordt.
       De padding gaat mee, anders begint het paneel op een hoogte van twee keer
       de padding en springt het alsnog. */
    .dossier {
      overflow: clip;
      transition: height .22s cubic-bezier(.16, 1, .3, 1),
                  padding-block .22s cubic-bezier(.16, 1, .3, 1);
      height: auto;
    }
    @starting-style {
      .dossier { height: 0; padding-block: 0; }
    }
  }
}

/* ---------- diagrammen die zichzelf tekenen ----------

   Een diagram ís een volgorde, en tot nu toe stond het er in één klap. Nu
   bouwt het zich op terwijl het in beeld scrolt, in de volgorde waarin je het
   hoort te lezen: eerst de vakken en de staven, dan tekenen de pijlen zich,
   dan landen de pijlkoppen, en de tekst komt als laatste. Het raster en de as
   blijven staan, want dat is de bladzijde en niet het argument.

   Waarom dit mag onder de bewegingsregel: het is dezelfde redenering als de
   tijdlijn op /ai-team. Scrollen ís hier door de figuur bewegen, en aan het
   eind staat er precies de tekening die er zonder deze laag ook stond.

   Hoe de choreografie werkt: één benoemde tijdlijn op de figuur
   (`view-timeline-name: --fig`) en per groep een eigen venster daarin met
   `animation-range`. Dus geen delays, want een delay loopt op de klok en deze
   beweging loopt op jouw scroll. Scrol je terug, dan loopt de tekening terug;
   dat is geen herhaling maar dezelfde beweging in omgekeerde richting.

   De basisstand hieronder is de EINDstand. Alles wat de figuur verbergt staat
   uitsluitend in de keyframes, achter de guard. In een browser zonder steun
   (Firefox stable, september 2026) en bij reduced motion staat de figuur er dus
   gewoon, compleet, zoals nu.

   Kosten: de staven en vakken lopen op transform en opacity, dus die zitten op
   de compositor. De pijlen lopen op `stroke-dashoffset` en dat is paint, geen
   compositor, hoe vaak het ergens anders ook anders staat. Daarom tekenen ze
   in een kort venster en nooit meer dan één figuur tegelijk in beeld.        */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    .diagram { view-timeline-name: --fig; }

    /* Gedeelde instellingen. Losse longhands en geen `animation:`-shorthand:
       de shorthand zet animation-duration op 0s, en met een scroll-tijdlijn
       betekent 0s dat de animatie meteen klaar is. Dan beweegt er nooit iets
       en zoek je een half uur naar een typefout die er niet is. */
    .diagram-vak rect.dg-vak,
    .diagram-vak g.dg-vak > rect,
    .diagram-vak rect.dg-vak-accent,
    .diagram-vak .dg-poort,
    .diagram-vak rect.dg-balk,
    .diagram-vak rect.dg-balk-dim,
    .diagram-vak g.dg-balk > rect,
    .diagram-vak g.dg-balk-dim > rect,
    .diagram-vak .dg-pijl,
    .diagram-vak .dg-pijlkop,
    .diagram-vak .dg-punt,
    .diagram-vak .dg-eind,
    .diagram-vak .dg-bot,
    .diagram-vak text {
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: --fig;
    }

    /* 1. de vakken en de poort komen op */
    .diagram-vak rect.dg-vak,
    .diagram-vak g.dg-vak > rect,
    .diagram-vak rect.dg-vak-accent,
    .diagram-vak .dg-poort {
      transform-box: fill-box;
      transform-origin: center;
      animation-name: fig-opkomen;
      animation-range: cover 12% cover 30%;
    }

    /* 2. de staven groeien uit hun nulpunt. Horizontaal vanaf links, en de drie
       verticale (`data-op`, gezet in diagram.mjs) vanaf de vloer. */
    .diagram-vak rect.dg-balk,
    .diagram-vak rect.dg-balk-dim,
    .diagram-vak g.dg-balk > rect,
    .diagram-vak g.dg-balk-dim > rect {
      transform-box: fill-box;
      transform-origin: left center;
      animation-name: fig-groeien;
      animation-range: cover 12% cover 32%;
    }
    .diagram-vak rect[data-op] {
      transform-origin: center bottom;
      animation-name: fig-groeien-op;
    }

    /* 3. de pijlen tekenen zich. pathLength="1" staat er bij het bouwen op
       (diagram.mjs), dus 1 is hier de hele lengte van elk pad. */
    .diagram-vak .dg-pijl {
      stroke-dasharray: 1;
      animation-name: fig-tekenen;
      animation-range: cover 22% cover 40%;
    }

    /* 4. de koppen en de punten landen ná de lijn die naar ze toe loopt */
    .diagram-vak .dg-pijlkop,
    .diagram-vak .dg-punt,
    .diagram-vak .dg-eind {
      animation-name: fig-verschijnen;
      animation-range: cover 36% cover 46%;
    }

    /* 5. de tekst als laatste: eerst de tekening, dan het bijschrift erin */
    .diagram-vak text {
      animation-name: fig-verschijnen;
      animation-range: cover 40% cover 52%;
    }

    /* 6. het robotje rijdt mee met de lijn die hij tekent.

       De route staat als `offset-path` op het element zelf, gezet bij het
       bouwen met dezelfde padgegevens als de pijl (diagram.mjs). Twee animaties
       op één element, met eigen bereiken: rijden loopt gelijk op met het
       tekenen, en zichtbaar-zijn begint iets eerder en eindigt iets later, zodat
       hij komt aanrijden en wegrijdt in plaats van te knipperen.

       `offset-rotate: 0deg` houdt hem rechtop in de bochten. Een robot die
       kantelt in een hoek van negentig graden staat even op zijn kop, en dat is
       grappig op de verkeerde manier. */
    .diagram-vak .dg-bot {
      /* transform-box moet fill-box zijn, anders is de referentiedoos van dit
         SVG-element de hele viewBox en betekent offset-anchor 50% 100% niet
         "onderkant van het robotje" maar het midden-onder van de hele figuur.
         Gemeten voor de fix: de robot stond 250 eenheden naast zijn route. */
      transform-box: fill-box;
      offset-rotate: 0deg;
      offset-anchor: 50% 100%;
      animation-name: fig-rijden, fig-bot-tonen;
      animation-range: cover 22% cover 40%, cover 20% cover 43%;
    }
  }
}

/* Basisstand: geen robot. Hij hoort alleen te bestaan terwijl de lijn getekend
   wordt, dus zonder steun voor scroll-tijdlijnen en bij reduced motion is hij
   er niet. Dat is hier de eindstand: als het tekenen klaar is, is hij weg. */
.diagram-vak .dg-bot { opacity: 0; offset-distance: 100%; }
/* Het lijf in --muted en niet in --dim: op tien procent van de figuurbreedte
   moet hij zich van het raster onderscheiden, anders zie je wel dat er iets
   beweegt maar niet wat. */
.dg-bot-lijf { fill: var(--muted); }
.dg-bot-oog { fill: var(--accent-text); }

@keyframes fig-rijden { from { offset-distance: 0%; } to { offset-distance: 100%; } }
@keyframes fig-bot-tonen { 0%, 100% { opacity: 0; } 14%, 82% { opacity: 1; } }

@keyframes fig-opkomen { from { opacity: 0; transform: scale(.96); } }
@keyframes fig-groeien { from { transform: scaleX(0); } }
@keyframes fig-groeien-op { from { transform: scaleY(0); } }
@keyframes fig-verschijnen { from { opacity: 0; } }
@keyframes fig-tekenen { from { stroke-dashoffset: 1; } }

/* Reduced motion zet een view-tijdlijn niet vanzelf uit: de duur naar nul
   collapsen helpt niet, want het element blijft de scrollpositie volgen. De
   animaties hierboven staan al binnen een no-preference-media, maar deze reset
   staat er als vangnet voor alles wat er later bij komt. Zie DESIGN.md, vraag 4. */
@media (prefers-reduced-motion: reduce) {
  .diagram-vak * { animation-timeline: auto !important; animation-name: none !important; }
}

/* auteursblok onderaan het artikel */
.post-auteur {
  display: flex;
  gap: 22px;
  align-items: flex-start;
  margin-top: 3.4rem;
  max-width: var(--kolom);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 24px;
}
.post-auteur img {
  width: 88px;
  height: 110px;
  object-fit: cover;
  object-position: 50% 30%;
  border-radius: 10px;
  flex: 0 0 auto;
}
.post-auteur-kop {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--dim);
  margin: 0 0 10px;
}
.post-auteur p { font-size: 14px; margin-bottom: 10px; }
.post-auteur p:last-child { margin-bottom: 0; }
@media (max-width: 640px) {
  .post-auteur { flex-direction: column; }
}

/* Related: twee kaartjes met echte gerelateerde posts uit de RELATED-matrix
   in paginas/blog.mjs (BK-24). */
.rel-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 34px; }
.rel-slot {
  display: flex;
  flex-direction: column;
  gap: 10px;
  border: 1px dashed var(--line-strong);
  border-radius: 14px;
  padding: 24px;
  color: var(--text);
  transition: border-color .2s ease, transform .2s ease;
}
/* Leeskaart = een plek waar je heen gaat. De wijzer schuift die kant op. */
.rel-slot:hover { border-color: #fff; color: var(--text); }
.rel-slot i { transition: transform .22s cubic-bezier(.16, 1, .3, 1); }
.rel-slot:hover i { transform: translateX(5px); }
.rel-slot span {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--dim);
}
.rel-slot b { font-size: 17px; font-weight: 700; color: #fff; letter-spacing: -.01em; }
.rel-slot i { font-style: normal; font-family: var(--mono); font-size: 13px; color: var(--accent-text); }
/* Verder lezen onder een inhoudspagina: drie doorstapjes in plaats van de
   twee related-kaarten onder een blogpost. Auto-fit in plaats van een vast
   aantal kolommen, zodat een pagina met vier doorstapjes niet hoeft te
   kiezen tussen een weesje op de tweede rij en een eigen regel CSS. */
.rel-grid-drie { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }

@media (max-width: 640px) {
  .rel-grid { grid-template-columns: 1fr; }
  .rel-grid-drie { grid-template-columns: 1fr; }
}

/* de blogindex */
.blog-lijst { list-style: none; margin: 0; padding: 0; display: grid; gap: 18px; }
.blog-item {
  display: block;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 26px 24px;
  color: var(--text);
  transition: border-color .2s ease, transform .2s ease;
}
.blog-item:hover { border-color: var(--line-strong); color: var(--text); }
.blog-item h2 { font-size: clamp(20px, 2.4vw, 25px); margin: 10px 0 6px; max-width: 30ch; }
.blog-item > p:not(.post-meta) { margin: 0 0 14px; font-size: 15px; }
.blog-meer {
  display: inline-block;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: .03em;
  color: var(--accent-text);
  transition: color .18s ease, transform .22s cubic-bezier(.16, 1, .3, 1);
}
.blog-item:hover .blog-meer { color: #fff; transform: translateX(5px); }


/* Onderaan site/src/styles.css toevoegen (na het bestaande .blog-item-blok).
   Bewust achteraan: .pg-sub-kaart overschrijft .pg-sub op gelijke
   specificiteit, dus het moet later in het bestand staan. */

/* ---------------------------------------------------------------
   Uitklapbare sub-blokken (secties met `subUitklap: true`).

   Waarom <details> en geen toggle in app.js: bij <details> staat het
   antwoord in de server-HTML, ook dichtgeklapt. Een crawler die de kale
   HTML leest krijgt daardoor precies de tekst die het FAQPage-schema
   belooft, en dat is de enige reden dat een FAQ hier dicht mag staan.
   Toetsenbordbediening, Enter/Space, "zoeken op pagina" en het openklappen
   bij een deeplink krijgen we er native bij; met JS was dat allemaal
   nabouwwerk geweest.
   --------------------------------------------------------------- */
.pg-vouwlijst {
  margin-top: 1.6rem;
  max-width: var(--kolom);
  counter-reset: vouw;
  display: grid;
  gap: 10px;
}

.pg-vouw {
  counter-increment: vouw;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  transition: border-color .18s ease;
}
.pg-vouw:hover { border-color: var(--line-strong); }

/* De driehoek van de browser weg: hij staat links, hij is nauwelijks te
   positioneren en hij ziet er in elke engine anders uit. `list-style` dekt
   Firefox en het huidige Chrome, de -webkit-regel de Safari's die nog een
   marker-pseudo hebben. */
.pg-vouw > summary {
  list-style: none;
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 15px 18px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.pg-vouw > summary::-webkit-details-marker { display: none; }

/* Het volgnummer. Telwerk is systeemdata, dus de monoletter; 12px omdat 11px
   de ondergrens is en dit geen label maar een leesbare index is. Vaste breedte
   zodat de antwoordkolom eronder uitlijnt. */
.pg-vouw > summary::before {
  content: counter(vouw, decimal-leading-zero);
  flex: none;
  width: 1.5rem;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
  transition: color .18s ease;
}

.pg-vouw > summary h3 {
  margin: 0;
  flex: 1;
  font-size: 1.05rem;
  line-height: 1.45;
  color: var(--text);
}

/* De chevron: twee randen van 2px, gedraaid. Naar beneden als het item dicht
   is, naar boven als het open staat, dus de draai is de stand en geen
   versiering. currentColor, zodat er geen kleur bij komt. */
.pg-vouw > summary::after {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  margin-top: .3em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transform-origin: 60% 60%;
  color: var(--dim);
  transition: transform .2s ease, color .18s ease;
}
.pg-vouw[open] > summary::after { transform: rotate(-135deg); }
.pg-vouw > summary:hover::after,
.pg-vouw[open] > summary::after { color: var(--accent-text); }

/* Open: sterkere rand plus een accentlijn aan de kop. Eén item openen is een
   stand, en dit is de plek waar die stand zichtbaar wordt. */
.pg-vouw[open] {
  border-color: var(--line-strong);
  box-shadow: inset 2px 0 0 var(--accent-soft);
}
.pg-vouw[open] > summary::before { color: var(--accent-text); }

/* De focusring van de site staat 3px buiten het element; op een summary die de
   volle kaartbreedte vult loopt hij dan over de rand van de kaart heen. Naar
   binnen halen houdt hem heel en even goed zichtbaar. Kleur en dikte blijven
   van de globale :focus-visible. */
.pg-vouw > summary:focus-visible {
  outline-offset: -3px;
  border-radius: 10px;
}

/* Het antwoord lijnt uit onder de vraag, niet onder de index: 18px padding +
   1.5rem nummer + 12px gap. */
.pg-vouw-antwoord { padding: 0 18px 16px 54px; }
.pg-vouw-antwoord p { margin: 0; max-width: none; color: var(--muted); }
.pg-vouw-antwoord p + p { margin-top: .8rem; }

/* Openklappen mag bewegen, want die beweging is de standwissel zelf. Dubbele
   poort, zoals bij de tijdlijn: `@supports` omdat `interpolate-size` nog lang
   niet overal zit (zonder de regel klapt het item direct open, wat prima is),
   en `no-preference` zodat er bij reduced motion niets te animeren valt. */
@supports (interpolate-size: allow-keywords) {
  @media (prefers-reduced-motion: no-preference) {
    .pg-vouwlijst { interpolate-size: allow-keywords; }
    .pg-vouw::details-content {
      block-size: 0;
      overflow: clip;
      transition: block-size .26s ease, content-visibility .26s allow-discrete;
    }
    .pg-vouw[open]::details-content { block-size: auto; }
  }
}

/* Expliciet, naast de globale regel bovenaan: geen overgang, wel alle
   betekenis. De chevron wijst nog steeds omhoog als het item openstaat, het
   nummer kleurt nog steeds mee, alleen zonder tussenstappen. */
@media (prefers-reduced-motion: reduce) {
  .pg-vouw,
  .pg-vouw > summary::before,
  .pg-vouw > summary::after { transition: none; }
}

/* Op een telefoon is de inspringing zonde van de leesbreedte. */
@media (max-width: 560px) {
  .pg-vouw > summary { padding: 13px 14px; gap: 10px; }
  .pg-vouw-antwoord { padding: 0 14px 14px; }
}

/* ---------------------------------------------------------------
   Sub-blokken als kaarten (secties met `subKaart: true`). Vier verwante
   blokken onder elkaar lezen als één tekstmuur; naast elkaar zijn het
   vier dingen die je los kunt oppakken. Geen icoontegel boven de kop en
   geen kaart-in-kaart: een rand, een vlak, de kop.
   --------------------------------------------------------------- */
.pg-subraster {
  display: grid;
  gap: 14px;
  margin-top: 1.8rem;
  max-width: 880px;
}
@media (min-width: 760px) {
  .pg-subraster { grid-template-columns: 1fr 1fr; }
}
.pg-sub-kaart {
  margin-top: 0;
  max-width: none;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  padding: 18px 20px;
}
.pg-sub-kaart h3 { margin: 0 0 .5rem; }
.pg-sub-kaart p { color: var(--muted); }

/* ---------- juridische pagina's (BK-76) ----------
   Een privacyverklaring en algemene voorwaarden zijn lange documenten die
   iemand doorzoekt en niet doorleest: hij wil weten welke gegevens je bewaart,
   of hoe lang de opzegtermijn is. Dat vraagt om koppen die je scannend kunt
   volgen en om regels die niet te breed worden.

   De koppen komen uit de markdown en zijn één niveau opgeschoven (zie
   renderTekst), zodat het document netjes onder de sectiekop hangt in plaats
   van ermee te vechten. Maten uit de ladder in DESIGN.md: h3 op de
   blogHeading-rol, h4 op `kop`, de rest op `tekst`. */
.juridisch { max-width: 68ch; }
.juridisch h3 {
  font-size: clamp(20px, 2.4vw, 25px);
  margin: 2.4rem 0 .6rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--lijn, rgba(255,255,255,.09));
}
.juridisch h3:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.juridisch h4 { font-size: 20px; margin: 1.6rem 0 .4rem; }
.juridisch h5 { font-size: 17px; margin: 1.2rem 0 .3rem; color: var(--muted); }
.juridisch p { margin: 0 0 1rem; }
.juridisch ul, .juridisch ol { margin: 0 0 1rem; padding-left: 1.3rem; }
.juridisch li { margin-bottom: .35rem; }
.juridisch hr { border: 0; border-top: 1px solid var(--lijn, rgba(255,255,255,.09)); margin: 2rem 0; }

/* De verwerkers- en cookietabellen: smal genoeg om te lezen, en op een telefoon
   scrollen ze in hun eigen kader in plaats van de pagina mee te sleuren. */
.juridisch table { border-collapse: collapse; width: 100%; font-size: 15px; }
.juridisch th, .juridisch td {
  text-align: left;
  padding: 10px 14px 10px 0;
  border-bottom: 1px solid var(--lijn, rgba(255,255,255,.09));
  vertical-align: top;
}
.juridisch th { color: var(--muted); font-weight: 600; }

/* `code` staat hier voor cookienamen (cc_consent, _ga). Geen achtergrondvlak:
   in lopende juridische tekst leest een gemarkeerd blokje als een knop. */
.juridisch code { font-family: var(--mono); font-size: .92em; color: var(--muted); }
