/* Respecteer bewegingsvoorkeur. Dit is het vangnet; latere fases voegen
   gerichte uitzonderingen toe waar beweging betekenis draagt. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Newsreader: zelfgehost, gesubset variabel font (wght 200-800, opsz 6-72),
   vervangt Instrument Serif en EB Garamond volledig - zie fonts/Newsreader-OFL.txt
   voor de licentie (SIL Open Font License 1.1). Twee families uit dezelfde
   twee bestanden: "Newsreader Display" met font-display: block voor de
   intro-letteranimatie (een fontwissel halverwege die animatie is direct
   zichtbaar), "Newsreader" met font-display: swap voor de rest van de site.
   Beide wijzen naar hetzelfde bestand, dus dit kost geen extra netwerkverzoek. */
@font-face {
  font-family: "Newsreader Display";
  src: url("../fonts/newsreader-var.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: block;
}
@font-face {
  font-family: "Newsreader Display";
  src: url("../fonts/newsreader-italic-var.woff2") format("woff2");
  font-weight: 200 800;
  font-style: italic;
  font-display: block;
}
@font-face {
  font-family: "Newsreader";
  src: url("../fonts/newsreader-var.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Newsreader";
  src: url("../fonts/newsreader-italic-var.woff2") format("woff2");
  font-weight: 200 800;
  font-style: italic;
  font-display: swap;
}

:root {
  /* Kleur */
  --blauw:              #1A3BAA;
  --goud:               #C49A20;
  --ivoor:              #F5F0E8;
  --wit:                #FFFFFF;
  --surface:            var(--ivoor);
  --surface-alt:        #F0EEE9;
  --surface-dark:       #1A1A18;
  /* Puur zwart, bewust naast --surface-dark (#1A1A18): uitsluitend voor
     de galerij en de galerijmodal (lightbox), niet site-breed toegepast.
     --surface-dark blijft de gewone donkere achtergrond elders. */
  --zwart:              #000000;
  /* Middentoon van het merkblauw (--accent #041790), niet bijna-zwart: op
     verzoek van Mees na het teruglezen van lebarnhotel.com, die een kaki
     middentoon (#77694F, relatieve luminantie ~0,146) voor dezelfde rol
     gebruikt. #22409C houdt dezelfde blauwe tint als --accent (huismerk uit
     logo-clean-header.svg) maar met een relatieve luminantie van 0,064 -
     duidelijk lichter en levendiger dan het oude #0E1B3A (0,012), en ruim
     binnen WCAG AA: 8,74:1 tegen --text-on-dark (#FAF9F6) en 9,20:1 tegen
     zuiver wit. Zie ook de duplicaat-hexwaarde in de <style>-anti-flits-
     snippet in <head> van index.html, die hierin meeloopt. */
  --surface-blueprint:  var(--blauw);
  --text:               #1A1A18;
  --text-muted:         #69665F;
  --text-on-dark:       #FAF9F6;
  --text-on-dark-muted: #9C9A94;
  --rule:               #E3E0D9;
  --rule-strong:        #8A8780;
  /* Haarlijn op een donker vlak. --rule is op ivoor afgestemd en verdwijnt
     daar volledig. Ivoor met alpha en geen zuiver wit, zodat de lijn
     dezelfde kleurtemperatuur houdt als de rest van de site. */
  --hair-on-dark:       rgba(245, 240, 232, .24);
  --accent:             #041790;
  --accent-hover:       #02105F;
  --accent-on-dark:     #7C90EA;
  --on-accent:          #FFFFFF;
  --error:              #B23B3B;
  --error-dark:         #8c2f2f;
  /* Alle fotografie op de site in zwart-wit, op verzoek van de
     opdrachtgever. Eén token zodat de behandeling op één plek staat en
     omkeerbaar blijft (CSS-filter, geen herschreven bronbeelden). Zie de
     toepassing bij de img-basisregel hieronder. Geldt niet voor de
     gevelschets in hero/preloader (eigen invert/screen-behandeling), het
     logo of de favicon. */
  --filter-foto: grayscale(1);

  /* Eén lettertype sitebreed. Bediening gebruikt dezelfde Newsreader als
     de inhoud, maar onderscheidt zich rustig met gewicht 600 en compacte
     tracking in plaats van een tweede, contrasterende letterfamilie. */
  --font-display: "Newsreader Display", Georgia, "Times New Roman", serif;
  --font-body:    "Newsreader", Georgia, "Times New Roman", serif;
  --font-mono:    "Newsreader", Georgia, serif;
  --font-action:  "Newsreader", Georgia, "Times New Roman", serif;
  --font-action-weight: 600;
  --font-action-tracking: .025em;

  /* --display-1 (clamp(3rem, 12vw, 11rem)) is op 24 augustus 2026 verwijderd.
     Hij droeg de subpaginakoppen en liep daar tot 176px, wat de bekende
     overflow op ruimtes.html veroorzaakte. Het hero-wordmark, de enige plek
     waar zo'n maat wel hoort, gebruikt --hero-wordmark-size en heeft hem
     nooit gebruikt. Paginakoppen staan nu op --page-title hieronder. */
  --display-2: clamp(2.25rem, 7vw, 5.5rem);
  --h2:        clamp(2rem, 4.4vw, 3.5rem);
  --h3:        clamp(1.25rem, 2.2vw, 1.75rem);
  --body:      clamp(1rem, 1.05vw, 1.125rem);
  --small:     0.8125rem;
  --meta:      0.75rem;

  --leading-display: 0.88;
  --leading-tight:   1.2;
  --leading-body:    1.6;
  --tracking-display: -0.022em;
  --tracking-meta:    0.08em;

  /* Ruimte */
  --space-1: 0.25rem; --space-2: 0.5rem;  --space-3: 0.75rem;
  --space-4: 1rem;    --space-6: 1.5rem;  --space-8: 2rem;
  --space-12: 3rem;   --space-16: 4rem;   --space-24: 6rem;
  --space-section: clamp(5rem, 10vw, 8.75rem);

  /* Ritme binnen de body. Toegevoegd 21 augustus 2026: de body-secties
     hardcodeerden vier verschillende ritmes (68, 48, 76/48) naast de goede
     --space-section, en rasters stonden op een losse 18px. */
  --space-section-tight: clamp(3.5rem, 6vw, 5rem);
  --space-block: clamp(2rem, 4vw, 3.5rem);
  --space-gutter: clamp(1.5rem, 3vw, 2.5rem);

  /* Gedeelde vormtaal van het contact- en vergaderformulier. De royale
     bediening en zachte hoeken horen uitsluitend bij taakgerichte invoer;
     de redactionele vlakken van de site blijven recht en vlak. */
  --form-control-h: 3.5rem;
  --form-radius-control: 0.75rem;
  --form-radius-panel: 1.5rem;
  --form-shadow: 0 1.5rem 4rem rgba(4, 23, 144, .2);
  --motion-form-feedback: 180ms;

  /* Paginakop, site-breed. Eén waarde voor iedere h1 buiten het
     hero-wordmark, want er is nog maar één kopsysteem (.section-opener).
     Loopt tot 68px in plaats van de 176px van het verdwenen --display-1:
     "representatief" mat daar 1304px en liep het scherm uit. */
  --page-title: clamp(2.5rem, 5.4vw, 4.25rem);

  --tracking-title: -0.015em;
  --tracking-sub: -0.008em;
  --leading-title: 1.02;

  --ratio-portrait: 4 / 5;
  --ratio-landscape: 3 / 2;

  --ease-enter: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-move: cubic-bezier(0.77, 0, 0.175, 1);
  --dur-press: 160ms;
  --dur-reveal: 520ms;
  --stagger: 60ms;

  --measure: 62ch;
  --container: 1280px;
  --pad-x: clamp(1rem, 4vw, 3rem);

  /* Lijn */
  --rule-w: 1px;
  --rule-w-strong: 4px;
  --radius: 0;

  /* Beweging */
  --dur-fast: 200ms; --dur-base: 400ms; --dur-slow: 800ms;
  /* Lightbox-onthulling (clip-path plus achtergrond), zie
     docs/research/04 Teardown Zorge 9 gallery.md: 1,2s op --ease-house. */
  --dur-lightbox: 1.2s;
  --ease-house: cubic-bezier(.7, 0, .3, 1);
  --ease-out:   cubic-bezier(.25, .46, .45, .94);
  /* Huistiming van lebarnhotel.com, gebruikt voor de intro-overlayfade,
     beweging van de geïntegreerde V, lockup-uitgang en navigatie-interacties.
     Losstaand van --ease-house: dat blijft de site-eigen timing voor scroll
     en overige clip-paths. */
  --ease-lebarn: cubic-bezier(.62, .31, .55, .88);

  /* Navigatie: header + schermvullende menu-overlay, naar het model van
     lebarnhotel.com/fr (devtools-metingen van Mees, augustus 2026; zie
     preview/lebarn-landing.png en preview/lebarn-menu.png). De header staat
     op alle pagina's op position: fixed en neemt dus geen documentflow-
     hoogte meer in - --header-h compenseert dat als padding-top op de
     inhoud van de vier niet-hero pagina's (.page-main) en als sticky-top
     voor .floor-nav. */
  --header-h: 4.5rem;
  /* Hamburgericoon: drie lijnen die naar een kruis draaien, herzien op
     letterlijke devtools-metingen van lebarnhotel.com (augustus 2026):
     26 × 20px, verdeeld met space-between (geen handmatige gap meer). */
  --nav-burger-w: 1.625rem;   /* 26px */
  --nav-burger-h: 1.25rem;    /* 20px */
  /* Hoofditems van de overlay, herzien op letterlijke devtools-metingen:
     letter-spacing 1.68px, item 49px hoog met 2px marge ertussen. De
     line-height staat als ratio (49/28.8) zodat hij evenredig meeschaalt
     met de clamp(). Op mobiel schaalt de font-size terug zodat
     "Vergaderruimtes" niet afbreekt. */
  --nav-overlay-item: clamp(1.375rem, 5vw, 1.8rem);
  --nav-overlay-leading: 1.701;
  --nav-overlay-tracking: 0.105rem;
  --nav-overlay-gap: 0.125rem;
  --nav-control-weight: 800;
  --nav-menu-weight: 700;
  /* Lengte van het horizontale streepje voor ieder menu-item - de enige
     decoratieve streep op de hele site buiten functionele randen en de
     onderstreping van links in lopende tekst. 52px, herzien op letterlijke
     devtools-metingen van lebarnhotel.com. */
  --nav-active-mark: 3.25rem;
  /* Dezelfde V-vorm en twee schrijfhalen als in de hero, maar royaal
     geschaald binnen de 72px navigatiebalk zodat het monogram als merk
     leesbaar blijft. De zichtbare inkt is circa 76% van deze SVG-box. */
  --nav-v-mark-h: 2.5rem;
  /* Menu-overlay: vlakke paneelfade en een subtiele, gestaffelde opkomst
     van de zes verder volledig uniforme lijstitems. */
  --motion-menu-fade: 400ms;
  --motion-menu-item-in: 500ms;
  --motion-menu-item-out: 300ms;
  --motion-menu-item-stagger: 40ms;
  --motion-menu-streak: 500ms;
  --motion-nav-link-hover: 500ms;
  --motion-language-popover: 200ms;
  /* Burgerknop: icoon en label naar kruis/"Sluiten", 0,5s op --ease-lebarn.
     Het label crossfadet apart en iets sneller. */
  --motion-burger: 500ms;
  --motion-burger-label-fade: 250ms;

  /* Hero-introtijdlijn: alle waarden staan bewust op één plek. De eerste V
     schrijft solo vanaf 200ms, beweegt vanaf 900ms naar haar letterpositie
     en de veertien resterende letters groeien vanaf 1200ms gestaffeld in.
     De laatste letter eindigt op 2405ms; de kernintro op 3500ms. */
  --motion-intro-end: 3500ms;
  --motion-intro-max: 4000ms;
  --motion-intro-skip: 300ms;
  /* De V wordt geschreven in twee halen (neerhaal, dan ophaal), met een
     mask over de echte letterform i.p.v. een zichtbare stroke - zie
     .v-stroke verderop. De oude clip-path-onthulling (--motion-v-start/
     --motion-v-duration) is vervallen. Haal 1 eindigt op 620ms, haal 2
     begint op 580ms: de overlap van 20ms is bewust, anders hapert de punt. */
  --motion-v-stroke1-start: 200ms;
  --motion-v-stroke1-duration: 420ms;
  --motion-v-stroke2-start: 580ms;
  --motion-v-stroke2-duration: 380ms;
  --motion-letter-start: 1200ms;
  --motion-letter-duration: 620ms;
  --motion-letter-stagger: 45ms;
  --motion-initial-settle-start: 900ms;
  --motion-initial-settle-duration: 600ms;
  /* Losstaand van de twee tokens hierboven: dit is de scroll-gestuurde
     uitgang van het BLIJVENDE geïntegreerde hero-wordmark, niet de eenmalige
     beweging van de eerste V. Eigen mechanisme, eigen omslagpunt
     (scrollY 50/30), eigen class (is-past-threshold) - zie .hero__lockup
     hieronder en js/main.js. */
  --motion-lockup-collapse: 500ms;
  --motion-overlay-fade-start: 2600ms;
  --motion-overlay-fade: 600ms;
  --motion-nav-start: 3000ms;
  --motion-nav-duration: 500ms;
  --motion-nav-color: 300ms;
  --motion-indicator-start: 3100ms;
  --motion-indicator-duration: 400ms;
  /* Actieve/inactieve breedte- en opacitywissel van de slide-indicator
     (0,8s) en de losse, snellere hover-opacity op een inactief streepje
     (0,3s) - beide letterlijke devtools-metingen van lebarnhotel.com. */
  --motion-indicator-active: 800ms;
  --motion-indicator-hover: 300ms;
  --motion-slide-interval: 5000ms;
  --motion-slide-crossfade: 900ms;
  --motion-slide-ken-burns: 6000ms;
  --motion-reduced-crossfade: 200ms;
  /* Galerijmodal (lightbox): gemeten Zorge 9-waarden. --motion-gallery-item-gap
     staat hier bewust bij de motiontokens en niet als los ruimtetoken -
     het is de enige plek waar deze exacte maat gebruikt wordt. */
  --motion-gallery-modal: 400ms;
  --motion-gallery-item-gap: 10px;
  /* Omtrek van de vierkante muisvolgende Bekijk-knop: de letterlijke
     Zorge 9-duur op --ease-house. */
  --motion-gallery-cursor-outline: 1600ms;
  /* Uitleg onder een foto in de modal. 260ms met --ease-letter: kort
     genoeg om als directe reactie te lezen, lang genoeg om de beweging
     te kunnen volgen. --motion-gallery-zoom (400ms) is vervallen samen
     met het uitvergroten dat deze uitleg heeft vervangen. */
  --motion-gallery-info: 260ms;
  /* Bronbalk onderin de modal. Uit is korter dan in, zodat de oude bron
     al weg is voordat de nieuwe verschijnt en de twee elkaar nooit
     overlappen. De 180ms staat ook in js/gallery.js als wachttijd voor
     de tekstwissel: verander ze samen. */
  --motion-credit-out: 180ms;
  --motion-credit-in: 220ms;
  --ease-letter: cubic-bezier(.22, 1, .36, 1);
  --ease-carousel: cubic-bezier(.4, 0, .2, 1);
  --ease-nav: ease;
  /* Easings van de twee V-halen: neerhaal iets trager op gang (symmetrisch
     in/uit), ophaal iets vlakker - beide letterlijk uitgemeten. */
  --ease-v-stroke-1: cubic-bezier(.45, 0, .55, 1);
  --ease-v-stroke-2: cubic-bezier(.35, 0, .5, 1);

  /* V-merkteken in links (.v-mark-link in components.css): een streepje dat
     bij hover en focus terugtrekt terwijl het logo zichzelf op dezelfde plek
     schrijft. Zelfde letterpad en zelfde pen als de intro, maar ingedikt: de
     introtiming van 420 plus 380ms is voor een hover te traag, want de muis
     is er dan allang weer weg.
     De intro schrijft met twee losse halen. Voor deze microinteractie is dat
     bewust teruggebracht tot EEN doorlopend pad. Reden: --ease-v-stroke-1
     eindigt op snelheid nul en --ease-v-stroke-2 begint op snelheid nul, dus
     de pen staat op de punt van de V even helemaal stil. In de intro valt dat
     weg achter de overlap van 20ms, bij een hover van 330ms leest het als een
     hapering. Met een doorlopend pad kan die sprong per definitie niet meer
     ontstaan.
     --ease-v-mark is numeriek gelijk aan --ease-carousel maar staat er los
     van: de carousel en het merkteken mogen onafhankelijk bijgesteld worden. */
  --motion-v-mark-draw: 410ms;
  --motion-v-mark-draw-start: 100ms;
  --motion-v-mark-erase: 280ms;
  --motion-v-mark-rule-out: 175ms;
  --motion-v-mark-rule-in: 228ms;
  --motion-v-mark-rule-in-start: 154ms;
  --motion-v-mark-color: 200ms;
  /* Schakelaar in het cookievenster (iOS-vorm, 15 september 2026). Kritisch
     gedempt: snel op gang, zacht tot stilstand, geen doorschieten. Een
     CSS-transitie keert bij een snelle tweede tik om vanaf de huidige stand. */
  --motion-schakelaar: 280ms;
  --ease-schakelaar: cubic-bezier(.25, .9, .3, 1);
  --ease-v-mark: cubic-bezier(.4, 0, .2, 1);
  /* Locatieaccordeon. --motion-loc-mark-cross is de enige nieuwe waarde van
     het plus-naar-V-merkteken: de tijd waarin de verticale streep krimpt en
     terugkomt. Alle andere tijden daar komen uit de --motion-v-mark-tokens
     hierboven, en de vertragingen worden er met calc() uit afgeleid. Zie
     .loc-mark__cross in components.css. */
  --motion-loc-mark-cross: 150ms;
  --motion-loc-panel-open: 320ms;
  --motion-loc-panel-close: 240ms;
  /* Horizontale tijdlijn. De grondwissel geldt tegelijk voor het toneel, de
     streekkleur van de grote V en de rail, zodat die drie als één ding
     omslaan. --motion-tijdlijn-tik is de enige hoverbeweging in de sectie. */
  --motion-tijdlijn-grond: 450ms;
  --motion-tijdlijn-tik: 200ms;
  /* Zichtbare hoogte van het merkteken, dikte van het streepje in
     viewBox-eenheden en de ruimte tussen merkteken en label. */
  --v-mark-h: 1.5rem;
  /* 2.8 en niet 5: bij --v-mark-h 1.5rem is één eenheid 0,568px, dus het
     streepje ging van 2,84px naar 1,59px. Dat ligt dicht bij de haarlijn van
     1px die de rest van de site gebruikt, terwijl het streepje zichtbaar
     blijft. Ook .loc-mark__cross hangt aan dit token, dus de plus in de
     locatieaccordeon dunt mee. */
  --v-mark-rule-weight: 2.8;
  --v-mark-gap: .9rem;

  --hero-blueprint-width: min(88vw, 68rem);
  --hero-wordmark-size: clamp(3.25rem, 8.6vw, 8.75rem);
}

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

html {
  font-size: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--body);
  font-weight: 400;
  line-height: var(--leading-body);
  /* opsz 16 is de leesmaat van de variabele as (6-72); expliciet gezet in
     plaats van enkel font-optical-sizing: auto, want de as moet meebewegen
     met de displaymaten hieronder (opsz 72/48/24/8). */
  font-variation-settings: "opsz" 16;
  -webkit-font-smoothing: antialiased;
  letter-spacing: 0.01em;
}

img,
svg {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--rule-strong);
  transition: border-color 200ms ease;
}

@media (hover: hover) and (pointer: fine) {
  a:hover {
    border-color: var(--accent);
  }
}

button,
input,
textarea,
select {
  font: inherit;
}

/* Na de native reset: knoppen krijgen de actierol, invoer blijft klassiek. */
button {
  font-family: var(--font-action);
  font-weight: var(--font-action-weight);
  font-optical-sizing: auto;
}

/* Elk indrukbaar element krijgt korte druk-feedback. */
button:active,
.button:active {
  transform: scale(0.97);
  transition: transform 160ms var(--ease-out);
}

/* Visueel verborgen, wel voor schermlezers: gebruikt voor h1's die
   nog geen zichtbare koptekst hebben zolang de content niet herschreven is. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  transition: outline-color 150ms ease;
}

/* Geen focusring na een muisklik, wel bij toetsenbordgebruik. js/main.js zet
   data-invoer op <html> bij de eerste pointerdown of navigerende toets.
   Alleen de outline vervalt: alles wat :focus-visible verder aanstuurt, zoals
   het V-teken dat zichzelf schrijft en de streep in de menu-overlay, blijft
   staan. Zonder JavaScript ontbreekt het attribuut en geldt de ring altijd,
   wat de veilige kant is. */
html[data-invoer="muis"] :focus-visible {
  outline: 0;
}

.container {
  width: min(100% - (var(--pad-x) * 2), var(--container));
  margin-inline: auto;
}

/* Vangnet tegen horizontale overloop. De echte oplossing is --page-title
   op de subpaginakoppen hieronder; dit is de verzekering. clip in plaats van
   hidden: hidden maakt van het element een scrollcontainer en breekt
   position: sticky en scroll-anchoring elders op de pagina. */
html,
body {
  overflow-x: clip;
}

.section {
  padding-block: var(--space-section);
}

/* Reveal bij het scrollen, aangestuurd door js/reveal.js. Gescopet op
   html.js: zonder JavaScript wordt .is-revealed nooit gezet en zou de body
   permanent onzichtbaar blijven. Alleen transform en opacity, dus geen
   layout of paint per frame. --i is een inline style per kind en zorgt voor
   de trapsgewijze opkomst binnen een raster. */
.js .reveal {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity var(--dur-reveal) var(--ease-enter),
    transform var(--dur-reveal) var(--ease-enter);
  transition-delay: calc(var(--i, 0) * var(--stagger));
}

.js .reveal.is-revealed {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .js .reveal {
    transform: none;
    transition: opacity 200ms ease;
    transition-delay: 0ms;
  }

  .v-mark-link:active {
    transform: none;
  }
}

/* ============================================================
   Kopsysteem: één component voor iedere sectie- en paginakop.

   Twaalf kolommen, opener in kolom 1 tot en met 7, kolom 8 tot en met 12
   bewust leeg. Die lege helft is de vormgeving en geen gat: er komt geen
   tweede kolom, geen citaat en geen beeld naast een kop.

   Sinds 14 september 2026 zonder haarlijn erboven en zonder grijs label
   (eyebrow) boven de kop, op de hele site, op verzoek van de eigenaar: de
   kop staat alleen. Zet ze niet terug zonder nieuwe opdracht. .eyebrow
   bestaat nog voor de verdiepingsaanduiding bij de kamers op ruimtes.html;
   dat is informatie over de ruimte en geen koplabel.

   Verving .section-heading (max-width: 540px), dat de kop in een vaste
   pixelbreedte zette en dus op ieder scherm een andere verhouding tot de
   container had. ============================================ */
.section-opener {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--space-gutter);
  margin-bottom: 40px;
}

.section-opener > * {
  grid-column: 1 / span 7;
}

/* De intro-alinea onder de opener deelt dezelfde kolom en dezelfde maat.
   Staat bewust binnen .section-opener en niet erbuiten: zo erft zij de
   kolomindeling zonder dat elke sectie zijn eigen raster hoeft te herhalen.
   40px onder de kop, en de opener zelf houdt 40px tot wat erna komt. */
.section-opener__intro {
  max-width: var(--measure);
  margin: 40px 0 0;
}

@media (max-width: 759px) {
  .section-opener {
    grid-template-columns: minmax(0, 1fr);
    margin-bottom: 28px;
  }

  .section-opener > * {
    grid-column: 1;
  }

  .section-opener__intro {
    margin-top: 28px;
  }
}

/* Geen eigen padding: de tussenruimte komt van de gap van .about-grid.
   Padding hier zou bij de tekening naast zich dubbel tellen. */
.section-copy {
  padding: 0;
}

h1,
h2,
h3,
p {
  margin-top: 0;
}

/* lang="nl" staat op <html>, dus hyphens: auto breekt Nederlandse woorden
   op de juiste plek. Samen met break-word is een kop daarmee nooit meer
   breder dan zijn kolom. */
h1,
h2,
h3 {
  overflow-wrap: break-word;
  hyphens: auto;
  text-wrap: balance;
}

/* Paginakop. Koningsblauw op ivoor haalt 8,2:1 en mag dus letters dragen,
   niet alleen vlakken. Gewicht 600 in plaats van 800: op deze maat leest 800
   als een banner en 600 als een kop. */
h1 {
  margin-bottom: 0;
  color: var(--blauw);
  font-family: var(--font-display);
  font-size: var(--page-title);
  font-weight: 600;
  font-variation-settings: "opsz" 72;
  line-height: 1.02;
  letter-spacing: -0.022em;
}

/* Sectiekop. De afstand tot de tekst eronder komt van .section-opener en
   niet van een marge op de kop zelf, zodat een kop zonder opener geen
   losse ruimte meesleept. letter-spacing staat expliciet: zonder dit zou
   h2 het 0.01em van body erven, dat voor lopende tekst bedoeld is. */
h2 {
  margin-bottom: 0;
  color: var(--blauw);
  font-family: var(--font-display);
  font-size: var(--h2);
  font-weight: 600;
  font-variation-settings: "opsz" 48;
  line-height: 1.04;
  letter-spacing: -0.02em;
}

/* Een kop draagt één stijl. De <em> blijft in de HTML staan, want die is
   semantisch, maar hij krijgt geen eigen vorm meer: kleur, gewicht én
   letterstand zijn gelijk aan de rest van de kop. Eerder stond hier cursief,
   en daarvoor --text-muted; beide gaven twee stijlen binnen één kop. Op
   verzoek van de eigenaar, 4 september 2026. */
h1 em,
h2 em,
h3 em {
  color: inherit;
  font-style: normal;
  font-weight: inherit;
}

h3 {
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--h3);
  font-weight: 600;
  font-variation-settings: "opsz" 24;
  line-height: var(--leading-tight);
}

p {
  max-width: var(--measure);
  color: var(--text-muted);
}

/* Utility voor maten, jaartallen, aantallen en bijschriften. */
.meta {
  font-family: var(--font-mono);
  font-size: var(--meta);
  font-weight: 500;
  font-variation-settings: "opsz" 8;
  letter-spacing: var(--tracking-meta);
  color: var(--text-muted);
  text-transform: uppercase;
}

/* ============================================================
   Filmische hero-intro en carousel

   De intro is een los overlay (.intro-overlay, position: fixed, z-index:
   100) boven de blijvende hero-DOM. De overlay draagt een eigen kopie van
   het geïntegreerde wordmark, met de merk-V als eerste letter; zodra hij
   wegfadet blijft de identieke hero-kopie eronder exact staan terwijl de
   foto zichtbaar wordt. Zonder JavaScript is dit blok meteen in zijn
   volledige eindtoestand zichtbaar: .intro-overlay staat dan permanent op
   visibility: hidden, er is dus geen risico op een blijvend blauw scherm.
   Alleen de expliciete .intro-pending/.intro-running toestanden op <html>
   zetten de overlay terug naar zijn startpunt. */
html.is-loading,
html.is-loading body {
  overflow: hidden;
}

/* .intro-overlay is verborgen tenzij JavaScript de intro-toestanden zet
   (geen JS, herladen na de intro of reduced motion tonen hem dus nooit). */
.intro-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--blauw);
  opacity: 1;
  visibility: hidden;
  pointer-events: none;
}

.intro-pending .intro-overlay,
.intro-running .intro-overlay {
  visibility: visible;
}

.intro-running .intro-overlay {
  animation: intro-overlay-fade var(--motion-overlay-fade) var(--ease-lebarn)
    var(--motion-overlay-fade-start) both;
}

@keyframes intro-overlay-fade {
  from { opacity: 1; }
  to   { opacity: 0; }
}

.hero {
  position: relative;
  min-height: 100svh;
  height: 100svh;
  overflow: hidden;
  isolation: isolate;
  background: var(--blauw);
  color: var(--wit);
}

.hero__slides,
.hero__slide,
.hero__slide-media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.hero__slides {
  z-index: 0;
}

.hero__slide {
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--blauw);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--motion-slide-crossfade) var(--ease-carousel);
}

.hero__slide.is-active {
  z-index: 1;
  opacity: 1;
  pointer-events: auto;
}

/* Een niet-breedbeeldbron kan alleen volledig zichtbaar blijven met
   rustige zij-/letterboxruimte. Donker in plaats van merkblauw voorkomt
   een kleurzweem naast de originele foto. */
.hero__slide[data-slide-fit="contain"] {
  background: var(--surface-dark);
}

.hero__slide-media {
  display: grid;
  place-items: center;
  object-fit: cover;
  transform: scale(1.06);
  transform-origin: center;
  will-change: transform;
}

.hero__slide.is-active .hero__slide-media {
  transform: scale(1);
}

/* Houd de eerste Ken Burns-startpositie vast tot de intro de pagina vrijgeeft. */
.is-loading .hero__slide.is-active .hero__slide-media {
  transform: scale(1.06);
}

/* De blauwdruk en iedere contain-slide staan volledig stil: geen Ken Burns
   en dus ook geen tijdelijke afsnijding door scale(1.06). Alleen de
   crossfade van .hero__slide zelf regelt daar de overgang. */
.hero__slide[data-slide-index="0"] .hero__slide-media,
.hero__slide[data-slide-index="0"].is-active .hero__slide-media,
.is-loading .hero__slide[data-slide-index="0"].is-active .hero__slide-media,
.hero__slide[data-slide-fit="contain"] .hero__slide-media,
.hero__slide[data-slide-fit="contain"].is-active .hero__slide-media,
.is-loading .hero__slide[data-slide-fit="contain"].is-active .hero__slide-media {
  transform: none;
  will-change: auto;
}

.hero__slide-media > img,
.hero__slide-media > picture,
.hero__slide-media > picture > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero__slide[data-slide-fit="contain"] .hero__slide-media > img {
  position: absolute;
  inset: 0;
  object-fit: contain;
}

.hero__blueprint {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: var(--hero-blueprint-width);
  max-width: calc(100vw - 2rem);
  height: min(72svh, 51rem);
  mix-blend-mode: screen;
}

.hero__blueprint picture,
.hero__blueprint img,
.hero__blueprint svg {
  width: 100%;
  max-width: 100%;
  max-height: 100%;
  min-height: 0;
  object-fit: contain;
}

/* <source> levert alleen responsive bronmetadata en mag geen CSS-griditem
   worden. Zonder deze expliciete uitsluiting reserveert WebKit/Chromium op
   hoge viewports een lege eerste gridrij en schuift het echte img omlaag. */
.hero__slide-media > source {
  display: none;
}

.hero__blueprint img {
  filter: invert(1);
}

.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(circle at 50% 48%, rgba(26, 59, 170, .35), rgba(26, 59, 170, 0) 68%),
    linear-gradient(to bottom, rgba(26, 59, 170, .25), rgba(26, 59, 170, 0) 48%);
}

/* Neutrale variant voor slides met echte fotografie. De blauwe scrim
   hierboven hoort bij het blauwe plaatshoudervlak; over een foto legt hij
   een zichtbare blauwe waas. "header 2 foto.jpg" is bovendien zwart-wit
   (gemeten gemiddelde RGB 193/193/193, blauwoverschot 0), dus elke blauwzweem
   in die hero kwam volledig van deze laag en niet van het beeld.
   Helemaal weglaten kan niet: het beeld is licht en het witte wordmark komt
   daar op ongeveer 1,5:1 uit. Deze variant houdt dus dezelfde geometrie en
   dekking aan, maar in neutraal zwart, zodat het beeld grijs blijft en de
   naam leesbaar. Bij de kleurenfoto verandert deze laag uitsluitend de
   helderheid; de oorspronkelijke kleurkanalen blijven intact. */
.hero__scrim--neutraal {
  background:
    radial-gradient(circle at 50% 48%, rgba(0, 0, 0, .35), rgba(0, 0, 0, 0) 68%),
    linear-gradient(to bottom, rgba(0, 0, 0, .25), rgba(0, 0, 0, 0) 48%);
}

.hero__placeholder {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  margin: 0;
  background: var(--blauw);
  color: rgba(255, 255, 255, .4);
  font-family: var(--font-mono);
  font-size: clamp(.6875rem, 1vw, .8125rem);
  letter-spacing: .12em;
  text-transform: uppercase;
}

/* .hero__brand centreert uitsluitend. De overlay- en hero-kopie gebruiken
   exact dezelfde geometrie: wanneer het blauwe overlay wegfadet blijft de
   complete hero-kopie eronder daardoor optisch op dezelfde plek staan. Er
   is bewust geen tweede hero-entree. Alleen .hero__lockup mag na
   de intro nog reageren op scroll, zie .hero.is-past-threshold hieronder. */
.hero__brand {
  position: absolute;
  z-index: 2;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

/* Verdwijnt rustig bij scrollen (taak D, js/main.js zet/verwijdert
   .is-past-threshold op .hero), los van de kleuromslag van de header
   hierboven en de overlay-intro. Een kleine opwaartse beweging plus fade
   houdt de overgang reversibel zonder het harde oude scale(0)-effect. */
.hero__lockup {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: min(calc(100vw - 3rem), 88rem);
  color: var(--wit);
  font-size: var(--hero-wordmark-size);
  line-height: .9;
  text-align: center;
  opacity: 1;
  transform: none;
  transform-origin: center center;
  transition: opacity var(--motion-lockup-collapse) var(--ease-lebarn),
    transform var(--motion-lockup-collapse) var(--ease-lebarn);
}

.hero.is-past-threshold .hero__lockup {
  opacity: 0;
  transform: translateY(-8%) scale(.97);
}

/* Het wordmark staat op ieder formaat bewust op twee regels: Villa boven,
   Sophialaan eronder. Dat verkort de beweging van de solo-V naar haar
   letterpositie en geeft de schrijfstagger een duidelijke leesrichting. De
   twee DOM-kopieën delen deze regel exact, zodat de overlayfade als één
   doorlopende motion leest. Gewicht 600 houdt de foto tussen de lettervormen
   zichtbaar. */
.hero__wordmark {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .06em;
  width: 100%;
  margin: 0;
  color: var(--wit);
  font-family: var(--font-display);
  font-size: 1em;
  font-weight: 600;
  font-variation-settings: "opsz" 72;
  line-height: .82;
  letter-spacing: -.01em;
  white-space: normal;
}

.hero__word {
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  white-space: nowrap;
}

.hero__letter {
  display: inline-block;
  opacity: 1;
  transform: scaleY(1);
  transform-origin: bottom center;
}

/* Het echte merkteken vervangt alleen de eerste tekstletter. De netto
   advance is .75em (.77em box minus .02em marge), vrijwel gelijk aan de
   eerdere Newsreader-V. De strakke viewBox staat in de HTML en houdt de
   zichtbare inkt optisch even hoog als de volgende letters. */
.hero__initial {
  display: inline-flex;
  flex: 0 0 .77em;
  align-self: baseline;
  align-items: flex-end;
  justify-content: center;
  width: .77em;
  height: .9em;
  margin-inline-end: -.02em;
}

.hero__initial-mark {
  display: block;
  width: .77em;
  height: .75em;
  overflow: visible;
  color: var(--wit);
  shape-rendering: geometricPrecision;
}

/* Alleen de veertien Newsreader-letters in de intro-overlay starten
   onzichtbaar en groeien in. De geïntegreerde SVG-V gebruikt haar eigen
   schrijfmasker en continuity transition; de blijvende hero-kopie staat
   altijd in de eindtoestand. */
.intro-pending .intro-overlay .hero__letter {
  transform: scaleY(0);
  will-change: transform;
}

.intro-running .intro-overlay .hero__letter {
  animation: hero-letter-grow var(--motion-letter-duration) var(--ease-letter)
    calc(var(--motion-letter-start) + (var(--letter-index, 0) * var(--motion-letter-stagger))) both;
}

.hero__word:first-child .hero__letter:nth-child(2) { --letter-index: 0; }
.hero__word:first-child .hero__letter:nth-child(3) { --letter-index: 1; }
.hero__word:first-child .hero__letter:nth-child(4) { --letter-index: 2; }
.hero__word:first-child .hero__letter:nth-child(5) { --letter-index: 3; }
.hero__word:last-child .hero__letter:nth-child(1) { --letter-index: 4; }
.hero__word:last-child .hero__letter:nth-child(2) { --letter-index: 5; }
.hero__word:last-child .hero__letter:nth-child(3) { --letter-index: 6; }
.hero__word:last-child .hero__letter:nth-child(4) { --letter-index: 7; }
.hero__word:last-child .hero__letter:nth-child(5) { --letter-index: 8; }
.hero__word:last-child .hero__letter:nth-child(6) { --letter-index: 9; }
.hero__word:last-child .hero__letter:nth-child(7) { --letter-index: 10; }
.hero__word:last-child .hero__letter:nth-child(8) { --letter-index: 11; }
.hero__word:last-child .hero__letter:nth-child(9) { --letter-index: 12; }
.hero__word:last-child .hero__letter:nth-child(10) { --letter-index: 13; }

/* De geïntegreerde intro-V start met haar zichtbare path exact in het
   viewportcentrum. JavaScript meet die path-bounds na document.fonts.ready
   en zet twee variabelen; er staat dus geen viewportcorrectie hardcoded. */
.intro-overlay .hero__initial-mark {
  transform: translate(var(--intro-v-shift-x, 0px), var(--intro-v-shift-y, 0px));
  will-change: transform;
}

.intro-running .intro-overlay .hero__initial-mark {
  animation: hero-initial-settle var(--motion-initial-settle-duration) var(--ease-lebarn)
    var(--motion-initial-settle-start) both;
}

.intro-skipping .intro-overlay .hero__initial-mark {
  animation-delay: 0ms;
  animation-duration: var(--motion-intro-skip);
}

/* De maskerlijnen die de V "schrijven". stroke-width 19 tegen een gemeten
   maximale afstand van 8,35 tussen outline en hartlijn (wiskundig minimum
   16,7): ruime marge tegen antialiasing-slivers tijdens de animatie.
   stroke-dasharray/-dashoffset op --len (met een royale fallback zodat de
   lijn ook zonder JS volledig verborgen start in plaats van te flitsen) -
   --len wordt met getTotalLength() gemeten in js/preloader.js en als
   custom property op elk .v-stroke-element gezet. */
.v-stroke {
  stroke-dasharray: var(--len, 200px);
  stroke-dashoffset: var(--len, 200px);
}

@keyframes v-draw {
  from { stroke-dashoffset: var(--len, 200px); }
  to   { stroke-dashoffset: 0; }
}

.intro-running .intro-overlay .v-stroke--1 {
  animation: v-draw var(--motion-v-stroke1-duration) var(--ease-v-stroke-1)
    var(--motion-v-stroke1-start) both;
}

.intro-running .intro-overlay .v-stroke--2 {
  animation: v-draw var(--motion-v-stroke2-duration) var(--ease-v-stroke-2)
    var(--motion-v-stroke2-start) both;
}

.intro-skipping .intro-overlay .v-stroke {
  animation-delay: 0ms;
  animation-duration: var(--motion-intro-skip);
}

/* De hero-kopie (buiten .intro-overlay) krijgt geen schrijfanimatie: in de
   hero staat alles stil. In plaats van het mask ongebruikt te laten (dat
   zou nog steeds van de --len-meting afhangen) zetten we stroke-dashoffset
   hier hard op 0: de maskerlijnen zijn dan altijd volledig getekend, ook
   als getTotalLength() om wat voor reden dan ook niet gedraaid heeft. Dit
   kan het minst omvallen, want het is onafhankelijk van JavaScript. */
.hero .hero__initial-mark .v-stroke {
  stroke-dashoffset: 0;
}

/* Slide-indicator linksonder: massieve witte streepjes, geen aparte track
   plus voortgangsvulling meer. Positie en maten zijn letterlijke
   devtools-metingen van lebarnhotel.com (augustus 2026). */
.hero__indicator {
  position: absolute;
  z-index: 5;
  left: 34px;
  bottom: 30px;
  display: flex;
  opacity: 1;
}

.intro-pending .hero__indicator {
  opacity: 0;
}

.intro-running .hero__indicator {
  animation: hero-indicator-in var(--motion-indicator-duration) ease
    var(--motion-indicator-start) both;
}

/* padding-top: 30px is het klikoppervlak dat boven de lijn doorloopt;
   padding-inline: 4px levert de 8px tussenruimte tussen de knoppen. Geen
   expliciete breedte/hoogte: de knop volgt de maat van zijn eigen streepje. */
.hero__indicator button {
  display: block;
  padding: 30px 4px 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.hero__indicator button::before {
  content: "";
  display: block;
  width: 25px;
  height: 3px;
  background: var(--wit);
  opacity: .5;
  border-radius: 0;
  transition: width var(--motion-indicator-active) var(--ease-lebarn),
    opacity var(--motion-indicator-active) var(--ease-lebarn);
}

.hero__indicator-button[aria-current="true"]::before {
  width: 45px;
  opacity: 1;
}

.hero__indicator button:focus-visible {
  outline: 2px solid var(--goud);
  outline-offset: 2px;
}

.hero__indicator button:active {
  transform: none;
}

/* Alleen toegevoegd t.o.v. de referentiesite, voor bruikbaarheid: een
   inactief streepje wordt op hover kort zichtbaarder. Eigen, kortere duur
   dan de 0,8s van de actieve wissel. */
@media (hover: hover) and (pointer: fine) {
  .hero__indicator button:hover:not([aria-current="true"])::before {
    opacity: .8;
    transition: opacity var(--motion-indicator-hover) var(--ease-lebarn);
  }
}

@keyframes hero-letter-grow {
  0% {
    transform: scaleY(0);
  }
  .001% {
    transform: scaleY(.06);
  }
  100% {
    transform: scaleY(1);
  }
}

@keyframes hero-initial-settle {
  from { transform: translate(var(--intro-v-shift-x, 0px), var(--intro-v-shift-y, 0px)); }
  to   { transform: translate(0, 0); }
}

@keyframes hero-indicator-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Doorspoelen houdt dezelfde eindstaat, maar comprimeert iedere zichtbare
   overlay- en indicatoranimatie tot de vastgelegde 300ms. De V-verplaatsing
   en twee maskerlijnen (.v-stroke) hebben eigen regels hierboven. */
.intro-skipping .intro-overlay,
.intro-skipping .intro-overlay .hero__letter,
.intro-skipping .hero__indicator {
  animation-delay: 0ms;
  animation-duration: var(--motion-intro-skip);
}

@media (max-width: 899px) {
  :root {
    --hero-blueprint-width: calc(100vw - 2rem);
    --hero-wordmark-size: clamp(2.6rem, 14vw, 5.75rem);
  }
}

@media (max-width: 767px) {
  .hero__blueprint {
    width: calc(100vw - 1.5rem);
    max-width: calc(100vw - 1.5rem);
    height: min(64svh, 34rem);
  }

  .hero__lockup {
    width: calc(100vw - 3rem);
  }
}

@media (prefers-reduced-motion: reduce) {
  html.is-loading,
  html.is-loading body {
    overflow: visible;
  }

  /* Eén regel dekt alle overlay-interne animaties (V-schrijven, letters en
     de beweging naar de eerste-letterpositie): de overlay verschijnt nooit. */
  .intro-overlay {
    display: none !important;
  }

  .hero__slide-media,
  .hero__slide.is-active .hero__slide-media {
    transform: none !important;
  }

  .hero__slide {
    transition: opacity var(--motion-reduced-crossfade) linear !important;
  }

  .hero__indicator,
  .intro-pending .hero__indicator {
    opacity: 1 !important;
    animation: none !important;
  }
}

/* Homepage sections */
.about-grid {
  display: grid;
  gap: var(--space-block);
}

/* De lijntekening staat rechtstreeks op het papier van de pagina: geen
   vlak eromheen, geen kader, geen bijschrift. Het vroegere vlak in
   --surface-alt met opacity .74 maakte van de inkt grijs en liet de fijne
   arcering van de ornamentiek wegvallen. Op --ivoor en volle dekking
   springen de halen er als tekening uit. */
.about-drawing img {
  display: block;
  width: 100%;
  height: auto;
}

/* De bovenrand van de blauwe band eronder is sinds 28 augustus 2026 een
   harde horizon over de volle schermbreedte. Daar kan de tekening op
   rusten, dus de gewone --space-section (80 tot 140px) eronder is te veel:
   de tekening zweeft dan boven de band in plaats van erop te staan. */
.about-section {
  padding-bottom: clamp(48px, 4vw, 64px);
}


.cta-section {
  padding-block: var(--space-section-tight);
  background: var(--accent);
}

.cta-section__inner {
  display: grid;
  gap: var(--space-block);
  align-items: center;
}

.cta-section h2 {
  color: var(--on-accent);
}

/* Twee gelijkwaardige .v-mark-link-acties naast elkaar. De vroegere
   .button--primary is hier weg: sinds 24 augustus 2026 is het V-merkteken de
   enige actievorm buiten de bevroren header, dus er staat geen gevuld blok
   meer naast een merkteken dat hetzelfde vraagt. Ze wikkelen onder elkaar
   zodra de regel niet meer past. */
.cta-section__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4) var(--space-12);
}

.eyebrow--light {
  color: var(--accent-on-dark);
}

/* ============================================================
   Blauwe band: de uitgang van de homepage naar de ruimtes.

   Was tot 28 augustus 2026 .cta-section--band, een vlak binnen de
   container met marge eromheen. Vier dingen klopten daar niet: het las als
   een banner die op de pagina was gelegd in plaats van als onderdeel
   ervan, er zat ruim tweehonderd pixels dood midden in, de twee links
   waren even zwaar zodat er geen eerste actie was, en de kop stond op de
   gewone sectiemaat terwijl het blok bijna een half scherm besloeg.

   De indruk komt nu uit drie dingen: volle breedte, de schaal van de kop
   en de strakke snit. Niet uit decoratie. Er staat dan ook geen verloop,
   geen kader, geen ronde hoek, geen schaduw, geen beeld en geen icoon in.

   Het blauw blijft --accent (#041790) en gaat NIET naar --blauw (#1A3BAA).
   Gemeten op 28 augustus 2026 met de WCAG-contrastformule:

     goud #C49A20   op --accent  5,22:1   op --blauw  3,55:1
     ivoor 72%      op --accent  6,70:1   op --blauw  5,02:1
     ivoor vol      op --accent 12,10:1   op --blauw  8,20:1

   De eyebrow en de secundaire actie staan op 12px en de primaire op 14px;
   dat telt als gewone tekst en vraagt dus 4,5:1. Op --blauw haalt goud dat
   niet, en een tweede goudtint verzinnen mag niet. --accent is daarmee de
   enige van de twee projectblauwen waar het goud van de eyebrow en de
   primaire actie AA haalt. ============================ */
.cta-band {
  /* Blokelementen zijn dit al; expliciet zodat leesbaar is dat de volle
     breedte bedoeld is. Bewust geen 100vw en geen
     margin-left: calc(50% - 50vw): die tellen op desktop de scrollbar mee
     en zijn precies waar horizontale overflow ontstaat. */
  width: 100%;
  /* Geen min-height en geen aspect-ratio. Elke vaste maat is precies de
     reden dat de vorige versie een gat in het midden had; de hoogte volgt
     hier uit de inhoud. */
  padding-block: clamp(88px, 9vw, 128px);
  /* Marge en geen padding: het blauw moet tot de onderrand doorlopen.
     Houdt het normale sectieritme tussen de band en de galerij eronder. */
  margin-bottom: var(--space-section);
  background: var(--accent);
}

.cta-band__inner {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--space-gutter);
}

/* De haarlijn loopt over de volle containerbreedte, net als bij iedere
   andere opener op de site; de kop zelf blijft in kolom 1 tot en met 7 via
   .section-opener > *. Kolom 8 tot en met 12 blijft leeg, en dat is de
   vormgeving en geen gat. */
.cta-band__inner > .section-opener {
  grid-column: 1 / -1;
  /* Intro naar acties: 44px, tegen de 40px van de gewone opener. */
  margin-bottom: 44px;
}

/* Paginakopschaal in plaats van sectiekopschaal. Dit is de enige plek op
   de homepage buiten de hero waar dat gebeurt: het blok is groot genoeg om
   die maat te dragen en het is de belangrijkste uitgang van de pagina.
   Rol deze maat niet uit naar andere secties, dan is het effect meteen
   weg. De cursief blijft in dezelfde kleur; een lichtere <em> is elders in
   dit project juist verwijderd. */
.cta-band h2 {
  color: var(--text-on-dark);
  font-size: var(--page-title);
  font-variation-settings: "opsz" 72;
  line-height: 1.02;
  letter-spacing: -0.022em;
}

.cta-band .section-opener__intro {
  /* 54 tekens in plaats van de gebruikelijke 62 van --measure. Lichte
     tekst op een donker vlak bloeit optisch uit, waardoor dezelfde regel
     zwaarder leest dan op ivoor; een kortere regel compenseert dat.
     Vol ivoor en geen lagere alpha: de hiërarchie komt hier uit schaal,
     niet uit contrast, en 12,1:1 geef je niet weg. */
  max-width: 54ch;
  margin-top: 32px;
  color: var(--text-on-dark);
  font-size: 1.0625rem;
  font-variation-settings: "opsz" 16;
  line-height: 1.6;
}

/* Twee acties die niet gelijkwaardig zijn. De hiërarchie komt uit drie
   dingen tegelijk: volgorde (primair eerst, dus ook eerst in de
   tabvolgorde), maat (14 tegen 12px) en kleur (goud tegen ivoor op 72
   procent). Er komt geen tweede knopvorm bij: allebei zijn het gewoon een
   .v-mark-link. De kleuren staan in components.css, direct onder het
   .on-dark-blok van dat component, want daar moeten ze in bronvolgorde
   achter staan om te winnen. */
.cta-band__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  grid-column: 1 / span 7;
  gap: 20px 48px;
}

@media (max-width: 759px) {
  .cta-band {
    padding-block: 72px;
  }

  .cta-band__inner {
    grid-template-columns: minmax(0, 1fr);
  }

  .cta-band__inner > .section-opener {
    grid-column: 1;
    margin-bottom: 32px;
  }

  .cta-band__actions {
    grid-column: 1;
  }

  .cta-band .section-opener__intro {
    margin-top: 24px;
  }

  /* Alleen de ondergrens van de kop zakt, van 2,5rem naar 1,75rem; de rest
     van de clamp blijft die van --page-title en boven 760px verandert er
     dus niets (op 759px komt beide uit op 41px).

     Dit is de maat en niet de kop. Gemeten op 28 augustus 2026 met
     tools/_kop.js: op 320px valt op de gewone ondergrens van 40px altijd
     een los woord op een derde regel, en dat gebeurt bij elke formulering
     van vijf of zes woorden. Ook het voorgestelde alternatief "Vergaderen
     in een monumentaal pand" en een nog kortere variant breken daar in
     drie regels. Pas onder 28px past de kop op twee regels, en dan doet
     iedere formulering het. De woorden van de eigenaar blijven daarom
     staan. */
  .cta-band h2 {
    font-size: clamp(1.75rem, 5.4vw, 4.25rem);
  }
}

/* Onder 600px onder elkaar, primair boven. Wrappen zou hier hetzelfde
   doen, maar niet gegarandeerd: twee korte labels passen op 599px nog net
   naast elkaar en staan dan met 48px ertussen tegen de rand aan. */
@media (max-width: 599px) {
  .cta-band__actions {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* Verschijnen: de bestaande observer uit js/reveal.js, met een eigen duur
   en verplaatsing. De vijf regels dragen elk .reveal met een --i van 0 tot
   4, dus de stagger komt uit --stagger (60ms), gelijk aan de tijdlijn en
   de ruimtebanden. De band zelf draagt bewust GEEN .reveal: een vlak dat
   over de volle breedte infadet leest als een laadfout.

   De .is-revealed-regel hieronder is niet overbodig. .js .reveal.is-revealed
   staat eerder in dit bestand op dezelfde specificiteit (0,3,0) als de
   regel hierboven; zonder deze zwaardere regel zou de begintransform de
   eindtoestand overschrijven en zou er nooit iets verschijnen. Om precies
   dezelfde reden staat het reduced-motion-blok hier en niet bovenaan. */
.js .cta-band .reveal {
  transform: translateY(10px);
  transition-duration: 320ms;
  transition-timing-function: var(--ease-lebarn);
}

.js .cta-band .reveal.is-revealed {
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .js .cta-band .reveal {
    transform: none;
    transition: opacity 200ms ease;
    transition-delay: 0ms;
  }
}

/* Header staat op alle pagina's op position: fixed (zie components.css) en
   neemt dus geen documentflow-hoogte meer in. Op de vier niet-hero pagina's
   compenseert .page-main dat met padding-top, zodat de inhoud niet onder de
   header begint. De homepage regelt dit zelf via .hero (100svh, de header
   ligt daar bewust overheen). */
.page-main {
  padding-top: var(--header-h);
}

/* Basic content page scaffolding */
.page-hero {
  padding-block: var(--space-section);
  background: var(--surface);
}

/* Geen max-width meer op de wrapper: de paginakop staat sinds 24 augustus
   2026 in .section-opener en haalt zijn breedte daar uit de zeven van de
   twaalf kolommen. Een vaste 680px erbovenop zou die kolomindeling weer
   dichtdrukken. .page-hero h1 heeft eveneens geen eigen regel meer nodig,
   want de basis-h1 draagt het kopsysteem al. */
.page-hero__inner > p {
  max-width: var(--measure);
}

/* Contactgegevens blijven redactioneel en vlak; de taak zelf krijgt rechts
   een duidelijk koningsblauw werkvlak. */
.content-card {
  padding-block: var(--space-block) 0;
  border-top: var(--rule-w) solid var(--rule);
}

.content-card h3 {
  color: var(--blauw);
}

.content-card p:last-child {
  margin-bottom: 0;
}

.contact-layout {
  display: grid;
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
}

.contact-form-panel {
  padding: clamp(1.5rem, 4vw, 3rem);
  border-radius: var(--form-radius-panel);
  background: var(--blauw);
  box-shadow: var(--form-shadow);
  color: var(--text-on-dark);
  scroll-margin-top: calc(var(--header-h) + var(--space-4));
}

.contact-form-panel__head {
  max-width: 38rem;
  margin-bottom: var(--space-8);
}

.contact-form-panel .eyebrow {
  color: color-mix(in srgb, var(--text-on-dark) 72%, transparent);
}

.contact-form-panel__title {
  margin: 0 0 var(--space-3);
  color: var(--text-on-dark);
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 3vw, 2.5rem);
  font-weight: 600;
  font-variation-settings: "opsz" 40;
  letter-spacing: -.018em;
  line-height: 1.05;
}

.contact-form-panel__intro {
  margin: 0;
  color: color-mix(in srgb, var(--text-on-dark) 76%, transparent);
  font-size: var(--small);
  line-height: 1.55;
}

.contact-form {
  display: grid;
  gap: var(--space-8);
}

.contact-form__group {
  display: grid;
  gap: var(--space-4);
  min-width: 0;
  margin: 0;
  padding: var(--space-6) 0 0;
  border: 0;
  border-top: 1px solid var(--hair-on-dark);
}

.contact-form__group legend {
  padding: 0;
  color: var(--text-on-dark);
  font-size: 1rem;
  font-weight: 600;
  font-variation-settings: "opsz" 16;
  letter-spacing: .01em;
}

.contact-form label {
  display: grid;
  gap: var(--space-2);
  min-width: 0;
  color: var(--text-on-dark);
  font-family: var(--font-body);
  font-size: .875rem;
  font-weight: 600;
  font-variation-settings: "opsz" 14;
  letter-spacing: .01em;
  line-height: 1.35;
}

.contact-form label > span:not(.field-error) {
  color: color-mix(in srgb, var(--text-on-dark) 68%, transparent);
  font-weight: 400;
}

.contact-form input,
.contact-form textarea,
.contact-form select {
  width: 100%;
  border: 1px solid rgba(255, 255, 255, .36);
  border-radius: var(--form-radius-control);
  background: rgba(255, 255, 255, .96);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 400;
  font-variation-settings: "opsz" 16;
  letter-spacing: 0;
  transition:
    background-color var(--motion-form-feedback) var(--ease-out),
    border-color var(--motion-form-feedback) var(--ease-out),
    box-shadow var(--motion-form-feedback) var(--ease-out);
}

.contact-form input,
.contact-form select {
  min-height: var(--form-control-h);
  padding: 0 var(--space-4);
}

.contact-form textarea {
  resize: vertical;
  min-height: 9rem;
  padding: var(--space-4);
  line-height: 1.5;
}

.contact-form input::placeholder,
.contact-form textarea::placeholder {
  color: color-mix(in srgb, var(--text-muted) 62%, transparent);
}

@media (hover: hover) and (pointer: fine) {
  .contact-form input:hover,
  .contact-form textarea:hover,
  .contact-form select:hover {
    border-color: rgba(255, 255, 255, .78);
    background: var(--wit);
  }
}

.contact-form input:focus-visible,
.contact-form textarea:focus-visible,
.contact-form select:focus-visible {
  border-color: var(--goud);
  outline: 0;
  background: var(--wit);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--goud) 34%, transparent);
}

.contact-form .field-row {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 640px) {
  .contact-form .field-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Foutmelding onder het veld waar zij bij hoort, gekoppeld met
   aria-describedby. Rood plus tekst, nooit rood alleen. */
.field-error {
  color: #FFD8D8;
  font-family: var(--font-body);
  font-size: var(--small);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
}

.contact-form [aria-invalid="true"] {
  border-color: var(--error);
  box-shadow: 0 0 0 3px rgba(255, 216, 216, .28);
}

.contact-form__status {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--form-radius-control);
  background: rgba(255, 255, 255, .1);
  color: var(--text-on-dark);
  font-size: var(--small);
}

.contact-form > .v-mark-link {
  justify-self: start;
}

/* Lokveld voor bots. Bewust niet display: none en niet visibility: hidden:
   een deel van de bots vult alleen velden die werkelijk gerenderd zijn, dus
   het veld moet in de renderboom blijven staan. Buiten beeld geschoven en
   buiten de tabvolgorde gehouden via tabindex in de HTML. */
.contact-form__pot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Bevestiging na een geslaagde inzending, getoond op de pagina zelf door
   js/contact-form.js. Zonder JavaScript blijft dit blok verborgen en doet
   het formulier een gewone POST. */
.contact-form__success {
  padding-top: var(--space-6);
  border-top: 1px solid var(--hair-on-dark);
}

.contact-form__success h3 {
  margin: 0 0 var(--space-2);
  color: var(--text-on-dark);
}

.contact-form__success p {
  margin: 0 0 var(--space-6);
  color: color-mix(in srgb, var(--text-on-dark) 76%, transparent);
  font-family: var(--font-body);
  font-size: var(--small);
}

.contact-form__success:focus-visible {
  outline: 2px solid var(--goud);
  outline-offset: 4px;
}

@media (max-width: 639px) {
  .contact-form-panel {
    padding: var(--space-6) var(--space-4);
    border-radius: 1.125rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .contact-form input,
  .contact-form textarea,
  .contact-form select {
    transition-duration: 0ms;
  }
}

@media (prefers-contrast: more) {
  .contact-form input,
  .contact-form textarea,
  .contact-form select {
    border-color: var(--wit);
    background: var(--wit);
  }
}

/* Twee ankerlinks onder de paginakop van ruimtes.html, naar de twee
   segmenten. Staan buiten .section-opener omdat het acties zijn en geen
   inleiding, maar houden dezelfde afstand aan. */
.page-hero__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-12);
  margin-top: 40px;
}

@media (min-width: 768px) {
  .about-grid {
    /* minmax(0, 1fr) en niet 1fr: een grid-item heeft standaard
       min-width: auto, waardoor een lange regel de kolom breder maakt dan
       de fractie en het raster buiten de container duwt. */
    /* Acht om vier en niet de helft om de helft: de tekst is drie korte
       zinnen plus een link, de tekening is het onderwerp van de sectie. */
    grid-template-columns: minmax(0, 8fr) minmax(0, 4fr);
    /* De tekst is korter dan de tekening hoog is; bovenaan uitlijnen laat
       de rechterkolom als een losse alinea in de leegte hangen. */
    align-items: center;
  }

}

/* Pas wanneer beide kolommen werkelijk ademruimte hebben komen adres en
   formulier naast elkaar. Op tabletbreedte stapelen ze, zodat het blauwe
   werkvlak geen smalle desktopkaart wordt. */
@media (min-width: 900px) {
  .contact-layout {
    grid-template-columns: minmax(0, 3.25fr) minmax(0, 8.75fr);
  }
}
