/* Catalogus-pagina. Alleen wat de pagina zelf nodig heeft; daaronder staan de
   acht families, elk met hun eigen voorvoegsel. */

/* Sprongnavigatie naar de families. */
.familienav{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:2rem}
.familienav a{font-size:.875rem;text-decoration:none;color:var(--deeg);
  padding:.5rem .875rem;border:1px solid var(--lijn);border-radius:8px;
  background:var(--grond-op);transition:border-color .2s ease,color .2s ease}
.familienav a:hover{border-color:var(--goud);color:var(--goud-licht)}
.familienav a:focus-visible{outline:2px solid var(--goud-licht);outline-offset:3px}

/* Kop boven elke familie. Krijgt bewust meer lucht dan een gewoon effectblok,
   zodat je bij het scrollen voelt dat je een nieuw hoofdstuk binnengaat. */
.familie{width:var(--breed);margin:0 auto;padding:clamp(4rem,11vh,7rem) 0 0}
.familiekop{border-top:2px solid rgba(201,162,39,.45);padding-top:1.5rem;max-width:60ch}
.familietel{font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;color:var(--goud)}
.familiekop h2{font-size:clamp(1.875rem,5vw,3rem);margin:.5rem 0 .75rem}
.familiekop p{color:var(--deeg-zacht);font-size:1rem;max-width:56ch;text-wrap:pretty}

/* De effectblokken gebruiken h3 in plaats van h2, omdat de familie de h2 is.
   lab.css stylet alleen .uitleg h2, dus dit haalt de kop terug op maat. */
.uitleg h3{font-family:var(--kop);font-weight:400;line-height:1.12;
  font-size:clamp(1.375rem,3vw,1.875rem);margin:.5rem 0 1rem;color:var(--deeg)}

/* ===== familie sc · Scroll ===== */
/* ===========================================================================
   FAMILIE SCROLL  (voorvoegsel: sc)
   Effecten die hun waarde uit de scrollpositie halen.

   Drie afspraken die in dit hele bestand terugkomen:

   1. De JS zet alleen een getal in een eigen custom property. Hoe ver iets
      beweegt en welke kant op staat hier in de CSS. Zo kun je de smaak
      bijstellen zonder de JS aan te raken, en weet de JS niets van vormgeving.

   2. Beginstanden (opacity 0, verschoven) staan achter html.js. Zonder JS is
      alles gewoon zichtbaar, dus een mislukte scriptlading maakt de pagina
      niet leeg.

   3. Bij prefers-reduced-motion zetten we die getallen met !important terug
      op 0. Een author-!important wint van een inline style, dus de JS kan er
      dan niet meer tussen komen, ook niet als de systeemvoorkeur midden in
      de sessie omgaat.
   =========================================================================== */


/* ---------------------------------------------------------------------------
   0. GEDEELD BINNEN DEZE FAMILIE
   --------------------------------------------------------------------------- */

/* Alle tekstkleuren hieronder staan op een dekkend paneel (--grond-op #15110E).
   --deeg #F3EBDD haalt daarop ongeveer 17:1, --deeg-zacht #C9BFAF ongeveer 9:1
   en --goud-licht #E8CE73 ongeveer 12:1. Nooit op een foto zonder dekkend vlak,
   want dan is contrast niet te garanderen. */
.sc-label {
  margin: 0 0 .4rem;
  font-family: var(--tekst);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--goud-licht);
}

.sc-titel {
  margin: 0 0 .5rem;
  font-family: var(--kop);
  font-weight: 400;
  font-size: clamp(1.1rem, 2.4vw, 1.55rem);
  line-height: 1.22;
  color: var(--deeg);
}

.sc-tekst {
  margin: 0;
  font-family: var(--tekst);
  font-size: .95rem;
  line-height: 1.62;
  color: var(--deeg-zacht);
}

.sc-hint {
  margin: .9rem 0 0;
  font-family: var(--tekst);
  font-size: .8rem;
  line-height: 1.5;
  color: var(--deeg-zacht);
}

/* Eén focusring voor de hele familie. :focus komt eerst zodat browsers zonder
   :focus-visible ook een ring krijgen; daarna nemen we hem weg waar dat mag. */
.sc-lees-vak:focus,
.sc-strook-kijk:focus,
.sc-plak-knop:focus,
.sc-plak-stap:focus {
  outline: 2px solid var(--goud-licht);
  outline-offset: 3px;
}

.sc-lees-vak:focus:not(:focus-visible),
.sc-strook-kijk:focus:not(:focus-visible),
.sc-plak-knop:focus:not(:focus-visible),
.sc-plak-stap:focus:not(:focus-visible) {
  outline: none;
}

.sc-lees-vak:focus-visible,
.sc-strook-kijk:focus-visible,
.sc-plak-knop:focus-visible {
  outline: 2px solid var(--goud-licht);
  outline-offset: 3px;
}

/* De stap krijgt meer offset omdat de ring anders tegen de tekst aan plakt. */
.sc-plak-stap:focus-visible {
  outline: 2px solid var(--goud-licht);
  outline-offset: 6px;
  border-radius: 6px;
}


/* ---------------------------------------------------------------------------
   1. PARALLAX MET MEERDERE LAGEN
   Drie lagen die per scrollstap een andere afstand afleggen. Het raam heeft
   zelf overflow:hidden, want de lagen zijn expres groter dan het raam: anders
   zie je aan de randen de achtergrond doorschemeren zodra iets verschuift.
   --------------------------------------------------------------------------- */

.sc-par {
  /* -1 = het paneel staat onder in beeld, 0 = midden, 1 = boven in beeld.
     Zonder JS blijft dit 0 en staat dus alles in de ruststand. */
  --sc-par-p: 0;
  width: 100%;
  max-width: 46rem;
  /* .doek centreert zijn kinderen; wij willen de volle breedte gebruiken. */
  justify-self: stretch;
  margin-inline: auto;
}

.sc-par-raam {
  position: relative;
  overflow: hidden;
  height: clamp(250px, 42vh, 390px);
  border: 1px solid var(--lijn);
  border-radius: 12px;
  background: var(--grond-op);
  /* Eigen stapelcontext, zodat de z-index van de lagen niets buiten dit raam raakt. */
  isolation: isolate;
}

/* De verste laag. Hij is 118% x 132% en staat gecentreerd, zodat er aan alle
   kanten reserve is om te schuiven zonder dat er een rand vrijkomt. */
.sc-par-ver {
  --sc-par-d: 34px;
  position: absolute;
  left: 50%;
  top: 50%;
  width: 118%;
  height: 132%;
  object-fit: cover;
  object-position: center 55%;
  transform: translate3d(-50%, calc(-50% + var(--sc-par-p) * var(--sc-par-d)), 0);
  will-change: transform;
}

/* Vaste sluier: geen beweging, maar wel nodig om de foto naar het donkere
   thema te trekken. Dit is puur decoratie, dus een span met aria-hidden. */
.sc-par-waas {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(to top, rgba(13, 11, 9, .92) 0%, rgba(13, 11, 9, .45) 46%, rgba(13, 11, 9, .18) 100%);
  pointer-events: none;
}

/* Middenlaag: de tekstkaart. Dekkend, want contrast op een foto is niet te
   berekenen. Hij loopt de andere kant op en iets sneller dan de foto. */
.sc-par-kaart {
  --sc-par-d: -22px;
  position: absolute;
  z-index: 2;
  left: clamp(.85rem, 3%, 1.6rem);
  bottom: clamp(.85rem, 3%, 1.6rem);
  max-width: min(22rem, 76%);
  padding: .85rem 1rem .95rem;
  border: 1px solid var(--lijn);
  border-left: 2px solid var(--goud);
  border-radius: 10px;
  background: var(--grond-op);
  transform: translate3d(0, calc(var(--sc-par-p) * var(--sc-par-d)), 0);
  will-change: transform;
}

.sc-par-kaart .sc-tekst {
  font-size: .875rem;
}

/* Voorste laag: het snelst, dus de sterkste diepte-indruk. Het is een
   uitgeknipt vel met transparante achtergrond, daarom geen kader eromheen. */
.sc-par-dicht {
  --sc-par-d: -58px;
  position: absolute;
  z-index: 3;
  right: clamp(.5rem, 3%, 1.4rem);
  top: clamp(.5rem, 6%, 1.6rem);
  width: clamp(86px, 24%, 156px);
  height: auto;
  filter: drop-shadow(0 12px 22px rgba(0, 0, 0, .55));
  transform: translate3d(0, calc(var(--sc-par-p) * var(--sc-par-d)), 0) rotate(-6deg);
  will-change: transform;
}

/* Op een smal scherm neemt de tekstkaart anders bijna het hele raam in
   beslag en zie je van de diepte niets meer terug. */
@media (max-width: 480px) {
  .sc-par-raam {
    height: clamp(270px, 44vh, 360px);
  }

  .sc-par-kaart {
    max-width: min(20rem, 84%);
    padding: .7rem .8rem .8rem;
  }

  .sc-par-kaart .sc-label {
    font-size: .66rem;
  }

  .sc-par-kaart .sc-tekst {
    font-size: .8rem;
    line-height: 1.5;
  }
}


/* ---------------------------------------------------------------------------
   2. LEESVOORTGANG
   De balk hangt aan een eigen scrollvak in plaats van aan het venster. Op een
   catalogus met meerdere families zou een vaste balk bovenaan het scherm van
   iedereen zijn; zo blijft het effect binnen de eigen demo en is het meteen
   te zien zonder de hele pagina door te scrollen.
   --------------------------------------------------------------------------- */

.sc-lees {
  width: 100%;
  max-width: 44rem;
  justify-self: stretch;
  margin-inline: auto;
}

.sc-lees-vak {
  --sc-lees-p: 0;
  max-height: clamp(250px, 44vh, 380px);
  overflow-y: auto;
  /* Geen scrollketting: aan het einde van het artikel gaat de pagina niet
     ineens door met scrollen. Dat voelt anders als een haperende muis. */
  overscroll-behavior: contain;
  border: 1px solid var(--lijn);
  border-radius: 14px;
  background: var(--grond-op);
  scrollbar-width: thin;
  scrollbar-color: var(--goud) rgba(243, 235, 221, .08);
}

/* De kop moet in het scrollvak zitten, niet erboven: sticky werkt alleen
   binnen de scroller waarin het element staat. */
.sc-lees-kop {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem .9rem;
  padding: .8rem 1.1rem .75rem;
  /* Dekkend, anders lees je de doorlopende tekst dwars door de kop heen. */
  background: var(--grond-op);
  border-bottom: 1px solid var(--lijn);
}

.sc-lees-titel {
  margin: 0;
  flex: 1 1 12rem;
  font-family: var(--kop);
  font-weight: 400;
  font-size: 1rem;
  color: var(--deeg);
}

.sc-lees-meter {
  display: flex;
  align-items: center;
  gap: .55rem;
  flex: 1 1 8rem;
}

/* Zonder JS zou de balk voor altijd op 0% blijven staan. Een meter die liegt
   is erger dan geen meter, dus dan tonen we hem niet. */
html:not(.js) .sc-lees-meter {
  display: none;
}

.sc-lees-baan {
  position: relative;
  flex: 1 1 auto;
  height: 5px;
  overflow: hidden;
  border-radius: 999px;
  background: rgba(243, 235, 221, .14);
}

/* scaleX in plaats van width: dat scheelt een layout-berekening per frame. */
.sc-lees-vul {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--goud) 0%, var(--goud-licht) 100%);
  transform: scaleX(var(--sc-lees-p));
  transform-origin: left center;
  will-change: transform;
}

.sc-lees-cijfer {
  min-width: 3.2ch;
  font-family: var(--tekst);
  font-size: .78rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--deeg-zacht);
}

.sc-lees-tekst {
  padding: 1.1rem 1.1rem 1.6rem;
}

.sc-lees-tekst h4 {
  margin: 0;
  font-family: var(--kop);
  font-weight: 400;
  font-size: 1.05rem;
  color: var(--deeg);
}

.sc-lees-tekst p {
  margin: 0;
  font-family: var(--tekst);
  font-size: .92rem;
  line-height: 1.65;
  color: var(--deeg-zacht);
}

/* De tussenruimte staat expres na de regels hierboven en met een selector
   van gelijke of hogere specificiteit. Een `> * + *` met alleen de klasse
   erin verliest het namelijk van `.sc-lees-tekst p`, en dan plakken alle
   alinea's tegen elkaar aan. */
.sc-lees-tekst p + p,
.sc-lees-tekst h4 + p {
  margin-top: .9rem;
}

.sc-lees-tekst p + h4 {
  margin-top: 1.5rem;
}


/* ---------------------------------------------------------------------------
   3. BINNENKOMEN MET VERTRAGING
   De vertraging zit in transition-delay, niet in een setTimeout-reeks in JS.
   De IntersectionObserver zet één klasse; de rest doet de CSS. Dat blijft ook
   kloppen als iemand halverwege de rij binnenscrollt.
   --------------------------------------------------------------------------- */

.sc-trap {
  width: 100%;
  justify-self: stretch;
}

.sc-trap-rij {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 110px), 1fr));
  gap: .85rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.sc-trap-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .55rem;
  padding: .9rem .6rem 1rem;
  border: 1px solid var(--lijn);
  border-radius: 12px;
  background: var(--grond-op);
  text-align: center;
}

/* Beginstand alleen als er JS is. Anders staat de rij er gewoon. */
html.js .sc-trap-item {
  opacity: 0;
  transform: translate3d(0, 20px, 0);
  transition: opacity .55s ease, transform .55s cubic-bezier(.22, .7, .3, 1);
  /* De index staat als --sc-trap-i in de markup, zodat de volgorde klopt
     voordat er ook maar één regel JS gedraaid heeft. */
  transition-delay: calc(var(--sc-trap-i, 0) * 110ms);
}

html.js .sc-trap-item.sc-in {
  opacity: 1;
  transform: none;
}

.sc-trap-foto {
  width: 100%;
  height: auto;
  max-width: 110px;
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, .5));
}

.sc-trap-naam {
  margin: 0;
  font-family: var(--tekst);
  font-size: .82rem;
  line-height: 1.35;
  color: var(--deeg-zacht);
}


/* ---------------------------------------------------------------------------
   4. HORIZONTAAL SCHUIVENDE STROOK
   Twee modi, expres gescheiden gehouden:
   A. met JS en zonder bewegingsvoorkeur: de baan schuift met transform en het
      venster staat op overflow:hidden. Er is dan geen tweede scroller die met
      de gebruiker kan gaan vechten.
   B. zonder JS of met prefers-reduced-motion: overflow-x:auto, de gebruiker
      veegt of pijltjes-scrollt zelf. Nooit een strook die half blijft hangen.
   De pagina zelf gaat in geen van beide gevallen zijwaarts: al het extra
   brede zit binnen .sc-strook-kijk.
   --------------------------------------------------------------------------- */

.sc-strook {
  --sc-strook-p: 0;
  --sc-strook-loop: 0px;
  position: relative;
  /* Zonder deze twee regels rekent de browser de minimale breedte van dit
     blok uit over de volle baan met kaarten. Staat de sectie eromheen op
     grid of flex, dan groeit hij daardoor mee tot ver buiten een telefoon
     en scrolt de hele pagina zijwaarts. min-width:auto is de standaard voor
     grid- en flexkinderen, dus die moet hier expliciet uit. */
  min-width: 0;
  max-width: 100%;
  /* De extra hoogte is de scrollafstand die het zijwaartse schuiven kost.
     260vh betekent ongeveer anderhalf scherm aan beweging. */
  height: 260vh;
}

.sc-strook-plak {
  position: sticky;
  top: 0;
  height: 100vh;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1rem;
  /* Bewust geen overflow:hidden hier: dat zou de focusring van het venster
     eronder afknippen. Het klemmen gebeurt in .sc-strook-kijk. */
}

/* svh houdt rekening met de wegschuivende adresbalk op telefoons. */
@supports (height: 100svh) {
  .sc-strook-plak {
    height: 100svh;
  }
}

/* Bewust width:100% met een maximum en niet var(--breed): de sectie eromheen
   is zelf al --breed breed, en dan zou je de marge er een tweede keer
   afhalen. Zo staat de strook netjes in lijn met de tekst erboven. */
.sc-strook-kop,
.sc-strook-onder {
  width: 100%;
  max-width: 1180px;
  margin-inline: auto;
}

.sc-strook-kijk {
  width: 100%;
  max-width: 1180px;
  min-width: 0;
  margin-inline: auto;
  overflow-x: auto;
  overflow-y: hidden;
  padding-block: .4rem;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
  scrollbar-color: var(--goud) rgba(243, 235, 221, .08);
}

/* Modus A. Vanaf hier stuurt de scrollpositie van de pagina de baan aan. */
html.js .sc-strook-kijk {
  overflow: hidden;
}

.sc-strook-baan {
  display: flex;
  gap: clamp(.7rem, 2vw, 1.1rem);
  margin: 0;
  padding: 0;
  list-style: none;
  /* Eén lengte en twee getallen in een calc, dat is geldig: 0.5 * 800px * -1. */
  transform: translate3d(calc(var(--sc-strook-p) * var(--sc-strook-loop) * -1), 0, 0);
}

html.js .sc-strook-baan {
  will-change: transform;
}

.sc-strook-kaart {
  flex: 0 0 auto;
  /* Iets staand formaat: in een paneel van een volledig scherm hoog vullen
     liggende kaartjes de hoogte niet en oogt de strook leeg. */
  width: clamp(228px, 24vw, 300px);
  scroll-snap-align: center;
  margin: 0;
  border: 1px solid var(--lijn);
  border-radius: 14px;
  background: var(--grond-op);
  overflow: hidden;
}

.sc-strook-foto {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

/* De vellen zijn uitgeknipt en staan op transparant. Cover zou ze bijsnijden,
   dus die krijgen contain met wat lucht eromheen. */
.sc-strook-foto--vel {
  object-fit: contain;
  padding: .9rem;
  background: rgba(243, 235, 221, .04);
}

.sc-strook-bij {
  display: block;
  padding: .7rem .85rem .85rem;
  font-family: var(--tekst);
  font-size: .82rem;
  line-height: 1.4;
  color: var(--deeg-zacht);
  border-top: 1px solid var(--lijn);
}

/* Alleen op een echte muis; op een aanraakscherm blijft een hover-stand
   plakken na het tikken en dat leest als een defect. */
@media (hover: hover) and (pointer: fine) {
  .sc-strook-kaart {
    transition: border-color .3s ease;
  }

  .sc-strook-kaart:hover {
    border-color: var(--goud);
  }
}


/* ---------------------------------------------------------------------------
   5. VASTPLAKKENDE SECTIE MET WISSELENDE INHOUD
   Staat bewust buiten .doek: die heeft overflow:hidden en dan werkt sticky
   niet meer. Het paneelrandje bouwen we hier dus zelf op.
   --------------------------------------------------------------------------- */

.sc-plak {
  display: grid;
  gap: 1.25rem;
  align-items: start;
  /* Zelfde reden als bij de strook: nooit door de eigen inhoud breder
     worden dan de sectie waarin dit blok staat. */
  min-width: 0;
  max-width: 100%;
  padding: clamp(1rem, 3vw, 1.75rem);
  border: 1px solid var(--lijn);
  border-radius: 16px;
  background: var(--grond-op);
}

@media (min-width: 860px) {
  .sc-plak {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: clamp(1.5rem, 4vw, 3rem);
  }
}

.sc-plak-media {
  position: sticky;
  /* Klein beetje ruimte boven, zodat de rand van het beeld niet tegen de
     bovenkant van het scherm plakt. */
  top: .75rem;
  z-index: 1;
}

@media (min-width: 860px) {
  .sc-plak-media {
    top: clamp(1rem, 12vh, 6rem);
  }
}

.sc-plak-stapel {
  position: relative;
  overflow: hidden;
  height: clamp(190px, 30vh, 260px);
  border: 1px solid var(--lijn);
  border-radius: 14px;
  background: var(--grond);
}

@media (min-width: 860px) {
  .sc-plak-stapel {
    height: clamp(280px, 48vh, 420px);
  }
}

.sc-plak-laag {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Zonder JS liggen de lagen gewoon op elkaar en zie je de bovenste. Wel zo
   eerlijk: er verdwijnt geen inhoud, er wisselt alleen niets. */
html.js .sc-plak-laag {
  opacity: 0;
  transition: opacity .45s ease;
}

html.js .sc-plak-laag.sc-aan {
  opacity: 1;
}

.sc-plak-nav {
  display: flex;
  flex-wrap: wrap;
  gap: .45rem;
  margin: .8rem 0 0;
  padding: 0;
  list-style: none;
}

.sc-plak-knop {
  min-height: 44px;
  padding: .5rem .85rem;
  font-family: var(--tekst);
  font-size: .8rem;
  color: var(--deeg-zacht);
  background: transparent;
  border: 1px solid var(--lijn);
  border-radius: 999px;
  cursor: pointer;
}

/* De actieve knop krijgt kleur én een vulling. Alleen kleurverschil zou voor
   kleurenblinde bezoekers te weinig zijn. */
.sc-plak-knop[aria-current="step"] {
  color: var(--grond);
  background: var(--goud);
  border-color: var(--goud);
  font-weight: 600;
}

@media (hover: hover) and (pointer: fine) {
  .sc-plak-knop:hover {
    border-color: var(--goud-licht);
    color: var(--deeg);
  }

  .sc-plak-knop[aria-current="step"]:hover {
    color: var(--grond);
  }
}

.sc-plak-stappen {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Deze hoogte is de scrollafstand per stap. Te kort en de wissel voelt
   nerveus, te lang en de bezoeker denkt dat de pagina vastzit. */
.sc-plak-stap {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: min(56vh, 460px);
  padding-left: 1rem;
  border-left: 2px solid var(--lijn);
}

/* De niet-actieve stappen worden niet gedimd: tekst op 45% opacity haalt het
   contrastminimum niet. Het accent zit in de rand, niet in de leesbaarheid. */
html.js .sc-plak-stap {
  transition: border-left-color .35s ease;
}

html.js .sc-plak-stap.sc-aan {
  border-left-color: var(--goud);
}

.sc-plak-nr {
  display: inline-block;
  margin-bottom: .4rem;
  font-family: var(--tekst);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--goud-licht);
}


/* ---------------------------------------------------------------------------
   6. SCROLL-GESTUURDE CSS-ANIMATIE
   Dit blok heeft geen JS nodig: de browser koppelt de animatie zelf aan de
   scrollpositie. Belangrijk detail: view() zoekt de dichtstbijzijnde scroller.
   Binnen .doek (overflow:hidden) is dat .doek zelf, en die scrollt nooit, dus
   dan zou de animatie op de beginstand blijven staan. Vandaar een eigen
   paneel met overflow zichtbaar.
   --------------------------------------------------------------------------- */

.sc-tijd {
  min-width: 0;
  max-width: 100%;
  padding: clamp(1.25rem, 3vw, 2rem);
  border: 1px solid var(--lijn);
  border-radius: 16px;
  background: var(--grond-op);
}

.sc-tijd-status {
  display: flex;
  align-items: center;
  gap: .55rem;
  margin: 0 0 1.1rem;
  padding: .55rem .8rem;
  font-family: var(--tekst);
  font-size: .82rem;
  line-height: 1.45;
  color: var(--deeg-zacht);
  border: 1px solid var(--lijn);
  border-radius: 10px;
  background: rgba(243, 235, 221, .04);
}

/* De terugvalmelding is de standaard. Kent de browser het wel, dan wisselen
   we van tekst. Puur CSS, dus ook zonder JS klopt wat er staat. */
.sc-tijd-ja {
  display: none;
}

.sc-tijd-rij {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  gap: 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.sc-tijd-kaart {
  overflow: hidden;
  border: 1px solid var(--lijn);
  border-radius: 14px;
  background: var(--grond);
}

.sc-tijd-foto {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

.sc-tijd-body {
  padding: .85rem 1rem 1.1rem;
}

.sc-tijd-lijn {
  display: block;
  height: 3px;
  margin: 0 0 1rem;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--goud) 0%, var(--goud-licht) 100%);
  transform-origin: left center;
}

@supports (animation-timeline: view()) {
  .sc-tijd-ja {
    display: inline;
  }

  .sc-tijd-nee {
    display: none;
  }

  /* both zorgt dat de kaart voor de reeks op de beginstand staat en erna op
     de eindstand blijft. Zonder both klapt hij terug zodra je verder scrolt. */
  .sc-tijd-kaart {
    animation: sc-tijd-opkomst linear both;
    animation-timeline: view();
    animation-range: entry 6% cover 38%;
  }

  /* De trapsgewijze volgorde zit hier in het bereik, niet in een delay:
     een scrollgestuurde animatie heeft geen tijd, alleen afstand. */
  .sc-tijd-kaart:nth-child(2) {
    animation-range: entry 13% cover 45%;
  }

  .sc-tijd-kaart:nth-child(3) {
    animation-range: entry 20% cover 52%;
  }

  .sc-tijd-lijn {
    animation: sc-tijd-vullen linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 45%;
  }
}

@keyframes sc-tijd-opkomst {
  from {
    opacity: 0;
    transform: translate3d(0, 28px, 0) scale(.97);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes sc-tijd-vullen {
  from {
    transform: scaleX(0);
  }

  to {
    transform: scaleX(1);
  }
}


/* ---------------------------------------------------------------------------
   7. RUSTSTAND BIJ prefers-reduced-motion
   Regel: alles wat verplaatst of van doorzichtigheid verandert gaat uit, en
   niets blijft in een halve stand hangen.

   Twee dingen blijven bewust wel werken, omdat het geen beweging is maar een
   stand van zaken: de leesmeter (een balk die op 0% blijft staan liegt tegen
   de bezoeker) en het wisselen van de vastgeplakte afbeelding (dat gebeurt
   dan zonder overgang, dus zonder animatie).
   --------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {

  /* !important van een stylesheet wint van een inline style, dus hiermee
     staat de parallax stil, ook als de JS nog een waarde had gezet. */
  .sc-par {
    --sc-par-p: 0 !important;
  }

  .sc-par-ver,
  .sc-par-kaart,
  .sc-par-dicht {
    will-change: auto;
  }

  .sc-par-dicht {
    transform: rotate(-6deg);
  }

  html.js .sc-trap-item {
    opacity: 1;
    transform: none;
    transition: none;
    transition-delay: 0s;
  }

  .sc-lees-vul {
    will-change: auto;
  }

  /* De strook wordt een gewone, met de hand te verschuiven rij. De 260vh
     extra hoogte zou anders een lege scrolltunnel worden. */
  .sc-strook {
    --sc-strook-p: 0 !important;
    --sc-strook-loop: 0px !important;
    height: auto;
  }

  .sc-strook-plak {
    position: static;
    height: auto;
    padding-block: .5rem;
  }

  html.js .sc-strook-kijk {
    overflow-x: auto;
    overflow-y: hidden;
  }

  html.js .sc-strook-baan {
    will-change: auto;
  }

  html.js .sc-plak-laag {
    transition: none;
  }

  html.js .sc-plak-stap {
    transition: none;
  }

  .sc-tijd-kaart,
  .sc-tijd-lijn {
    animation: none !important;
    opacity: 1;
    transform: none;
  }
}


/* ===== familie tx · Tekst ===== */
/* ==========================================================================
   tx: familie "Tekst"
   Zes typografie-effecten voor de proeftuin van Ensar Yufka.
   Alles begint met tx- zodat niets botst met de andere families.
   Kleuren, fonts en .doek komen uit lab.css en worden hier niet opnieuw
   gedefinieerd.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Gedeelde onderdelen van deze familie
   -------------------------------------------------------------------------- */

/* Elk effect hakt zijn tekst visueel in stukken (losse letters, half getypte
   zinnen). Die stukken staan op aria-hidden. Deze span houdt de zin als een
   geheel over voor een schermlezer.
   Waarom deze span en geen aria-label op de <p>: een alinea heeft
   role=paragraph, en daarvoor geldt "name from author: prohibited". Een
   aria-label is daar dus ongeldig en wordt genegeerd, waarmee de zin
   helemaal zou wegvallen. Deze span staat gewoon in de leesvolgorde en doet
   het echte werk; de gesplitste letters ernaast staan op aria-hidden. Er
   blijft dus altijd precies één leesbare zin over. */
.tx-alleen-sr{
  position:absolute;
  width:1px;height:1px;
  margin:-1px;padding:0;
  overflow:hidden;
  clip:rect(0 0 0 0);
  clip-path:inset(50%);
  white-space:nowrap;
  border:0;
}

/* Eén blok per demo. Grid in plaats van margins, zodat de afstand tussen
   tekst en knop overal gelijk is, ook als er een regel bijkomt. */
.tx-blok{
  display:grid;
  gap:1.15rem;
  justify-items:center;
  text-align:center;
  width:100%;
  max-width:34rem;
}

/* De band mag over de volle breedte van het paneel lopen. */
.tx-blok-breed{max-width:none;justify-self:stretch}

/* Knoppen staan standaard uit. Zonder werkende JS doet zo'n knop niets, en
   een knop die niets doet is erger dan geen knop. Pas als het script de demo
   echt heeft opgestart zet het tx-aan op de demo en verschijnt de knop. */
.tx-knop{
  display:none;
  font-family:var(--tekst);
  font-size:.9rem;
  line-height:1;
  color:var(--deeg);
  background:transparent;
  border:1px solid var(--lijn);
  border-radius:999px;
  padding:.75rem 1.2rem;
  min-height:44px;          /* comfortabel raakvlak op een telefoon */
  cursor:pointer;
  transition:border-color .2s ease,color .2s ease;
}
.tx-aan .tx-knop{
  display:inline-flex;
  align-items:center;
  justify-content:center;
}
/* Hover-kleur alleen waar echt een muis is; op een touchscreen blijft die
   status anders hangen na een tik. */
@media (hover:hover) and (pointer:fine){
  .tx-knop:hover{border-color:var(--goud);color:var(--goud-licht)}
}
/* :focus eerst voor browsers zonder :focus-visible, daarna weer weghalen waar
   :focus-visible wel bestaat. Zo houdt iedereen een zichtbare focusring. */
.tx-knop:focus{outline:2px solid var(--goud-licht);outline-offset:3px}
.tx-knop:focus:not(:focus-visible){outline:none}
.tx-knop:focus-visible{outline:2px solid var(--goud-licht);outline-offset:3px}

/* Uitleg onder een demo. #C9BFAF op #15110E haalt ruim 9:1. */
.tx-hint{
  font-family:var(--tekst);
  font-size:.9rem;
  line-height:1.55;
  color:var(--deeg-zacht);
  margin:0;
  max-width:36ch;
}

.tx-onder{
  font-family:var(--tekst);
  font-size:.85rem;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--deeg-zacht);
  margin:0;
}

/* --------------------------------------------------------------------------
   1. Letters die na elkaar opkomen
   -------------------------------------------------------------------------- */

.tx-letters{
  font-family:var(--kop);
  font-size:clamp(1.5rem,5.6vw,3rem);
  line-height:1.18;
  color:var(--deeg);
  margin:0;
  max-width:20ch;
}

/* Een woord blijft één blok, anders breekt de regel middenin een woord af
   zodra de letters losse inline-blocks worden. */
.tx-tekstwoord{display:inline-block;white-space:nowrap}
.tx-letter{display:inline-block}

/* Beginstand alleen als JS de letters echt heeft gesplitst (tx-is-klaar).
   Zonder JS bestaat die klasse niet en staat de zin er gewoon. */
html.js .tx-letters.tx-is-klaar .tx-letter{
  opacity:0;
  transform:translateY(.42em) rotate(1.5deg);
  transition:opacity .5s ease,transform .55s cubic-bezier(.22,.68,.3,1);
  /* De trap zit in de vertraging, niet in aparte keyframes: één regel CSS
     bedient elke lengte zin. */
  transition-delay:calc(var(--tx-i,0) * 34ms);
}
html.js .tx-letters.tx-is-klaar.tx-toon .tx-letter{
  opacity:1;
  transform:none;
}

/* --------------------------------------------------------------------------
   2. Letterdikte die meeloopt met scroll of muis
   -------------------------------------------------------------------------- */

.tx-dik{
  font-family:var(--tekst);
  font-size:clamp(1.7rem,6.4vw,3.4rem);
  line-height:1.15;
  letter-spacing:-.01em;
  color:var(--deeg);
  font-weight:500;
  margin:0;
  max-width:16ch;
}
.tx-dik-letter{
  display:inline-block;
  /* font-weight is de bodem: werkt overal, ook zonder variabele as. */
  font-weight:var(--tx-w,500);
}

/* Heeft het geladen font een echte gewichts-as, dan sturen we die direct aan.
   Dat geeft tussenstanden als 437 in plaats van sprongen van 100. */
.tx-varja .tx-dik-letter{
  font-variation-settings:"wght" var(--tx-w,500);
}

/* Geen variabele as (Cardo heeft er geen, en Inter komt niet altijd als
   variabel bestand binnen). font-weight springt dan van 400 naar 700, wat
   hakkelt. Een haarfijne omtrek in dezelfde kleur vult dat gat op: de letter
   wordt geleidelijk voller terwijl het gewicht springt.
   600 stappen * 0.000034em = ruim 0.02em omtrek bij het zwaarste punt. */
.tx-varnee .tx-dik-letter{
  -webkit-text-stroke:calc((var(--tx-w,500) - 300) * 0.000034em) currentColor;
}

/* --------------------------------------------------------------------------
   3. Omtrektekst die zich vult
   -------------------------------------------------------------------------- */

.tx-omtrek{
  --tx-vul:100%;            /* zonder JS staat het woord gewoon gevuld */
  position:relative;
  display:inline-block;
  font-family:var(--kop);
  font-weight:700;
  font-size:clamp(3.1rem,16vw,8rem);
  line-height:1;
  letter-spacing:.06em;
  margin:0 -.06em 0 0;      /* laatste letterafstand wegpoetsen, anders staat
                               het woord optisch net links van het midden */
  text-align:center;
  color:var(--goud);        /* leesbare basis: 7,7:1 op #15110E */
}
html.js .tx-omtrek{--tx-vul:0%}

.tx-omtrek-basis{display:block}

/* De vulling staat standaard uit, zodat een browser zonder text-stroke geen
   dubbel woord toont. */
.tx-omtrek-vul{display:none}

@supports (-webkit-text-stroke:2px currentColor){
  .tx-omtrek-basis{
    color:transparent;
    -webkit-text-stroke:2px var(--goud);
  }
  .tx-omtrek-vul{
    display:block;
    position:absolute;
    inset:0;
    color:var(--goud-licht);  /* 12:1 op #15110E */
    /* inset vanaf rechts: bij 0% is niets zichtbaar, bij 100% alles. */
    clip-path:inset(0 calc(100% - var(--tx-vul)) 0 0);
  }
}

/* In hoog-contrastmodus bestaan eigen kleuren niet meer; dan gewoon
   systeemtekst tonen in plaats van een onzichtbaar woord. */
@media (forced-colors:active){
  .tx-omtrek-basis{color:CanvasText;-webkit-text-stroke:0}
  .tx-omtrek-vul{display:none}
}

/* --------------------------------------------------------------------------
   4. Doorlopende band
   -------------------------------------------------------------------------- */

.tx-band{
  --tx-band-duur:38s;
  --tx-band-gat:2.5rem;
  /* GEKOPPELDE WAARDE. Deze uitloop moet exact gelijk zijn aan de inline
     padding van .doek in lab.css (daar staat nu padding:2rem): de band trekt
     zichzelf met een even grote negatieve marge tot de rand van het paneel.
     Wordt die padding in lab.css ooit iets anders, bijvoorbeeld een clamp(),
     dan moet deze waarde diezelfde vorm krijgen. Blijft hij op 2rem staan,
     dan loopt de band niet meer tot de rand of steekt hij er juist buiten.
     De twee horen bij elkaar; verander ze nooit apart. */
  --tx-band-bleed:2rem;
  position:relative;
  overflow:hidden;          /* houdt de band binnen het paneel; de pagina
                               scrollt hierdoor nooit horizontaal */
  width:auto;
  justify-self:stretch;     /* .tx-blok centreert zijn kinderen; de band moet
                               juist de volle breedte pakken */
  margin-inline:calc(var(--tx-band-bleed) * -1);
  padding-block:.65rem;
  border-block:1px solid var(--lijn);
}

.tx-band-spoor{
  display:flex;
  width:max-content;
  animation:tx-schuif var(--tx-band-duur) linear infinite;
}

/* Twee identieke helften; op -50% staat de tweede precies waar de eerste
   begon, dus de sprong terug is onzichtbaar. */
@keyframes tx-schuif{
  from{transform:translate3d(0,0,0)}
  to{transform:translate3d(-50%,0,0)}
}

.tx-band-groep,
.tx-band-kopie{display:flex;flex:none}

.tx-band-item{
  display:inline-flex;
  align-items:center;
  flex:none;
  margin-inline-end:var(--tx-band-gat);
  font-family:var(--tekst);
  font-size:clamp(.95rem,2.3vw,1.3rem);
  letter-spacing:.05em;
  white-space:nowrap;
  color:var(--deeg-zacht);
}

/* Scheidingsteken als vorm, niet als leesteken: een schermlezer hoeft geen
   losse punten voor te lezen. */
.tx-band-punt{
  flex:none;
  width:.34rem;height:.34rem;
  margin-inline-end:var(--tx-band-gat);
  border-radius:50%;
  background:var(--goud);
  align-self:center;
}

/* Muis erop of toetsenbordfocus erin pauzeert ook; dat scheelt het jagen op
   een bewegend woord. */
@media (hover:hover) and (pointer:fine){
  .tx-band:hover .tx-band-spoor{animation-play-state:paused}
}
.tx-band:focus-within .tx-band-spoor{animation-play-state:paused}
.tx-band[data-tx-pauze="ja"] .tx-band-spoor{animation-play-state:paused}

/* --------------------------------------------------------------------------
   5. Typemachine
   -------------------------------------------------------------------------- */

.tx-typ-regel{
  font-family:var(--tekst);
  font-size:clamp(1.05rem,3.4vw,1.75rem);
  line-height:1.4;
  color:var(--deeg);
  margin:0;
  max-width:26ch;
  text-align:left;
  /* Ruimte voor twee regels vrijhouden: de zinnen verschillen in lengte, en
     zonder deze bodem springt het paneel bij elke wisseling. */
  min-height:2.8em;
}

.tx-typ-vast{color:var(--deeg-zacht)}

.tx-typ-cursor{
  display:inline-block;
  width:.09em;
  height:1.05em;
  margin-inline-start:.06em;
  vertical-align:-.16em;
  background:var(--goud-licht);
  animation:tx-knipper 1.05s steps(1,end) infinite;
}
/* steps(1,end) geeft een echte aan/uit-cursor in plaats van een zachte
   pulsering; dat leest als een tekstcursor. */
@keyframes tx-knipper{
  0%,50%{opacity:1}
  50.01%,100%{opacity:0}
}
/* Staat het typen stil, dan staat ook de cursor stil (WCAG 2.2.2: knipperen
   moet te stoppen zijn). */
.tx-typ-stil .tx-typ-cursor{animation:none;opacity:1}

/* --------------------------------------------------------------------------
   6. Grote tekst met een foto erdoorheen
   -------------------------------------------------------------------------- */

.tx-foto-kop{
  font-family:var(--kop);
  font-weight:700;
  font-size:clamp(3rem,14.5vw,7.5rem);
  line-height:.94;
  letter-spacing:.02em;
  margin:0;
  color:var(--deeg);        /* leesbare basis als de foto niet in de tekst kan */
}

/* Pas als de browser een achtergrond op tekstvorm kan knippen, vervangen we
   de vlakke kleur door de foto. Andersom (eerst transparant maken en dan
   hopen) levert onzichtbare tekst op. */
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .tx-foto-kop{
    /* Bovenste laag is een lichte sluier over de foto. Nagerekend: zelfs een
       zwarte pixel wordt hierdoor #86817A (243*.55, 235*.55, 221*.55) en
       haalt daarmee 4,86:1 op #15110E, dus boven de 4,5:1 die tekst nodig
       heeft. Zonder die sluier zakken donkere delen van de foto naar 2,7:1
       en is het woord niet meer te lezen. rgba staat hier voluit omdat een
       kleurvariabele geen doorzichtigheid kan meekrijgen. */
    background-image:
      linear-gradient(rgba(243,235,221,.55),rgba(243,235,221,.55)),
      url("/assets/img/vel-2.webp");
    background-repeat:no-repeat;
    background-size:cover;
    background-position:50% calc(50% + var(--tx-foto-y,0px));
    -webkit-background-clip:text;
    background-clip:text;
    color:transparent;
    -webkit-text-fill-color:transparent;
  }
}

@media (forced-colors:active){
  .tx-foto-kop{
    background-image:none;
    color:CanvasText;
    -webkit-text-fill-color:CanvasText;
  }
}

/* --------------------------------------------------------------------------
   Minder beweging: alles staat stil, alles blijft leesbaar.
   Geen enkele opacity:0 of transform blijft hier hangen.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  .tx-knop{transition:none}

  html.js .tx-letters.tx-is-klaar .tx-letter{
    opacity:1;
    transform:none;
    transition:none;
  }

  html.js .tx-omtrek{--tx-vul:100%}

  /* De band schuift niet meer maar valt uiteen in gewone regels, zodat elk
     woord leesbaar in beeld staat in plaats van half buiten het paneel.
     Het spoor moet hier een gewoon blok worden: als flexregel houdt de groep
     zijn max-content breedte vast en breekt er niets af. */
  .tx-band{margin-inline:0}
  .tx-band-spoor{animation:none;display:block;width:auto;max-width:100%}
  .tx-band-groep{
    display:flex;
    flex-wrap:wrap;
    justify-content:center;
    width:auto;
    gap:.4rem 1.6rem;       /* gap in plaats van marges: geen losse ruimte
                               achter het laatste woord van een regel, dus het
                               midden blijft echt het midden */
  }
  .tx-band-item{margin-inline-end:0}
  .tx-band-punt{display:none}   /* een bolletje aan het begin van een nieuwe
                                   regel leest als een opsomming, dat is het niet */
  .tx-band-kopie{display:none}

  .tx-typ-cursor{animation:none;opacity:1}

  /* Er beweegt niets meer, dus pauzeren en herhalen hebben geen betekenis. */
  .tx-aan .tx-band-knop,
  .tx-aan .tx-typ-knop,
  .tx-aan .tx-herhaal-knop{display:none}
}


/* ===== familie bd · Beeld ===== */
/* =====================================================================
   Familie Beeld  (voorvoegsel: bd)
   Effecten op foto's voor de proeftuin van Ensar Yufka.

   Kleuren, lettertypes, .doek en .proef komen uit lab.css en worden hier
   bewust NIET opnieuw gezet. Alles hieronder begint met bd, zodat deze
   familie nooit in de weg zit van de andere bouwers op dezelfde pagina.
   ===================================================================== */

/* Padding en randen tellen bij deze familie mee in de breedte. Zonder deze
   regel schieten kaders net over hun kolom heen zodra er een rand bij komt,
   en dan krijg je alsnog een horizontale schuifbalk op een kleine telefoon. */
[class^="bd-"],
[class*=" bd-"],
[class^="bd-"]::before,
[class^="bd-"]::after,
[class*=" bd-"]::before,
[class*=" bd-"]::after { box-sizing: border-box; }

/* Eén plek om aan te draaien. Alle effecten gebruiken dezelfde ronding en
   dezelfde vertraging, anders oogt de pagina als zes losse dingen. */
.bd-kb,
.bd-veeg,
.bd-vn,
.bd-duo,
.bd-korrel,
.bd-scherp {
  --bd-hoek: 14px;
  --bd-veer: cubic-bezier(.22, .61, .36, 1);
  width: 100%;
  margin: 0;
}

/* .doek centreert zijn inhoud met place-items:center, waardoor een kind niet
   vanzelf de volle breedte pakt. Vandaar de width:100% hierboven. */

/* ---------------------------------------------------------------------
   Gedeelde onderdelen: knop, voetregel, etiket op een foto
   --------------------------------------------------------------------- */

.bd-knop {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  min-height: 44px;              /* comfortabel raakdoel op een telefoon */
  padding: .55rem 1.1rem;
  font-family: var(--tekst);
  font-size: .875rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--deeg);            /* #F3EBDD op #0D0B09 haalt ruim 15:1 */
  background: var(--grond);
  border: 1px solid var(--lijn);
  border-radius: 999px;
  cursor: pointer;
  transition: color .2s ease, border-color .2s ease;
}

/* Aanwijzen hoort bij een muis. Op een aanraakscherm blijft :hover na een tik
   plakken, en dan zou een knop verkleurd blijven staan terwijl er niets meer
   wordt aangewezen. Vandaar deze media-vraag; :focus en :focus-visible staan
   er bewust buiten, zodat toetsenbord en aanraken gewoon blijven werken. */
@media (hover: hover) and (pointer: fine) {
  .bd-knop:hover { color: var(--goud-licht); border-color: var(--goud); }
}

/* Eerst de brede :focus-regel, daarna pas :focus-visible. Zo houdt ook een
   browser zonder :focus-visible een zichtbare ring, in plaats van niets. */
.bd-knop:focus { outline: 2px solid var(--goud-licht); outline-offset: 3px; }
.bd-knop:focus:not(:focus-visible) { outline: none; }
.bd-knop:focus-visible { outline: 2px solid var(--goud-licht); outline-offset: 3px; }

/* Een knop die zonder JavaScript niets doet, tonen we ook niet. Beter geen
   knop dan een dode knop. */
.bd-knop[data-bd-js] { display: none; }
html.js .bd-knop[data-bd-js] { display: inline-flex; }

.bd-voet {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .75rem 1.25rem;
  margin: .9rem 0 0;
  font-family: var(--tekst);
  font-size: .9rem;
  line-height: 1.55;
  color: var(--deeg-zacht);      /* #C9BFAF op het donkere paneel: ruim 9:1 */
}

.bd-voet__tekst { margin: 0; max-width: 46ch; }

/* Etiket dat over een foto ligt. De achtergrond is vrijwel dicht, want tekst
   direct op een foto haalt nooit betrouwbaar 4,5:1. Op #0D0B09 met 92 procent
   dekking blijft #F3EBDD ook boven een spierwit stuk foto rond 14:1 staan. */
.bd-etiket {
  position: absolute;
  z-index: 2;
  display: inline-block;
  padding: .4rem .75rem;
  font-family: var(--tekst);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--deeg);
  background: rgba(13, 11, 9, .92);
  border: 1px solid var(--lijn);
  border-radius: 999px;
  pointer-events: none;          /* mag klikken en slepen nooit blokkeren */
}

/* =====================================================================
   1. Ken Burns: langzaam inzoomen en verschuiven
   ===================================================================== */

.bd-kb__raam {
  position: relative;
  overflow: hidden;
  border-radius: var(--bd-hoek);
  aspect-ratio: 16 / 9;
  background: var(--grond);
}

.bd-kb__foto {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* De bakker staat rechts op de foto. Met 62 procent blijft hij in beeld,
     ook als het kader op een telefoon veel smaller wordt. */
  object-position: 62% 50%;
}

/* Beginstand achter html.js: zonder JavaScript is er geen knop om de beweging
   mee te stoppen, dus dan laten we de foto gewoon stilstaan. */
html.js .bd-kb__foto {
  animation: bd-kb-drift 26s var(--bd-veer) infinite alternate both;
  will-change: transform;
}

/* Nooit verder inzoomen dan ongeveer 1.2, anders wordt de foto zichtbaar
   zachter. De verschuiving blijft binnen de overhang van de schaal, zodat er
   op geen enkel moment een rand naast de foto verschijnt. */
@keyframes bd-kb-drift {
  from { transform: scale(1.08) translate3d(2.2%, .8%, 0); }
  to   { transform: scale(1.2) translate3d(-2.6%, -1.4%, 0); }
}

/* Twee redenen om te pauzeren: de bezoeker vraagt erom, of het blok staat
   buiten beeld. In dat laatste geval scheelt stilstaan tekenwerk en accu.
   De html.js ervoor is geen sier: de regel hierboven zet animation als
   verkorte schrijfwijze, en die zou zonder dit steuntje zwaarder wegen en de
   pauze stilzwijgend overrulen. */
html.js .bd-kb--pauze .bd-kb__foto,
html.js .bd-kb--rust .bd-kb__foto { animation-play-state: paused; }

.bd-kb__band { left: .9rem; bottom: .9rem; }

/* =====================================================================
   2. Onthullen met een veeg (clip-path)
   ===================================================================== */

.bd-veeg__raam {
  position: relative;
  overflow: hidden;
  border-radius: var(--bd-hoek);
  aspect-ratio: 3 / 2;
  background: var(--grond);
}

.bd-veeg__foto {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Terugval eerst: kan de browser geen clip-path, dan komt de foto gewoon
   invloeiend binnen. Nooit een beginstand die kan blijven hangen. */
html.js .bd-veeg__foto {
  opacity: 0;
  transition: opacity .8s ease;
}
html.js .bd-veeg--zicht .bd-veeg__foto { opacity: 1; }

@supports (clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)) {
  html.js .bd-veeg__foto {
    opacity: 1;
    /* Vier punten in de verborgen stand, vier punten in de eindstand: alleen
       dan kan de browser er netjes tussenin rekenen. */
    clip-path: polygon(0% 0%, 0% 0%, -34% 100%, -34% 100%);
    transform: scale(1.06);
    transition: clip-path 1.05s var(--bd-veer), transform 1.5s var(--bd-veer);
  }
  html.js .bd-veeg--zicht .bd-veeg__foto {
    clip-path: polygon(0% 0%, 134% 0%, 100% 100%, 0% 100%);
    transform: scale(1);
  }
}

/* Het gouden randje dat voor de veeg uit loopt. Het element is even breed als
   het kader, dus een verschuiving in procenten rekent met de kaderbreedte en
   loopt daardoor exact met de snijlijn mee. De schuinte van 27 graden hoort
   bij de beeldverhouding 3/2 en bij de 34 procent uit de polygon hierboven. */
html.js .bd-veeg__raam::after {
  content: "";
  position: absolute;
  top: -30%;
  bottom: -30%;
  left: 0;
  right: 0;
  background: linear-gradient(90deg,
      rgba(232, 206, 115, 0) 0%,
      rgba(232, 206, 115, .85) 50%,
      rgba(232, 206, 115, 0) 100%) no-repeat;
  background-size: 3rem 100%;
  background-position: -1.5rem 0;   /* midden van het streepje op x = 0 */
  transform: translateX(-17%) skewX(-27deg);
  transition: transform 1.05s var(--bd-veer);
  pointer-events: none;
}
html.js .bd-veeg--zicht .bd-veeg__raam::after { transform: translateX(117%) skewX(-27deg); }

/* Opnieuw afspelen: overgangen heel even uit, terugzetten, en dan pas weer
   aan. Zonder deze tussenstap loopt het effect achterstevoren terug. */
html.js .bd-veeg.bd-veeg--vast .bd-veeg__foto,
html.js .bd-veeg.bd-veeg--vast .bd-veeg__raam::after { transition: none; }

/* =====================================================================
   3. Voor en na met een schuif
   ===================================================================== */

.bd-vn {
  --bd-vn-x: 50;                 /* stand in procenten, zonder eenheid */
  --bd-vn-greep: 2.25rem;
  display: grid;
  justify-items: center;
  gap: .9rem;
}

.bd-vn__raam {
  position: relative;
  width: 100%;
  /* De vellen zijn 620 bij 620 pixels. Verder dan 34rem oprekken zou de foto
     opblazen, en juist bij een vergelijking valt onscherpte meteen op. */
  max-width: 34rem;
}

.bd-vn__beeld {
  position: relative;
  overflow: hidden;
  border-radius: var(--bd-hoek);
  aspect-ratio: 1 / 1;
  background: radial-gradient(120% 120% at 50% 32%, #221A14 0%, #0D0B09 72%);
  /* Horizontaal slepen bedient de schuif, verticaal blijft scrollen. Zonder
     deze regel kiest de browser zelf en werkt slepen op een telefoon niet. */
  touch-action: pan-y;
}

.bd-vn__foto {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: 7%;
  user-select: none;
  -webkit-user-drag: none;       /* anders pakt de browser de foto op bij slepen */
}

/* De bovenste foto wordt links afgesneden op precies dezelfde plek als de
   greep van de schuif, zodat lijn, greep en snijrand altijd samenvallen. */
.bd-vn__laag {
  position: absolute;
  inset: 0;
  clip-path: inset(0 calc((100 - var(--bd-vn-x)) * 1%) 0 0);
}

.bd-vn__lijn {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--bd-vn-x) * 1%);
  width: 2px;
  margin-left: -1px;
  background: var(--goud-licht);
  box-shadow: 0 0 12px rgba(13, 11, 9, .8);
  pointer-events: none;
}

/* Beide etiketten hangen even hoog; de modifier bepaalt alleen de zijkant.
   De opmaak zelf komt van .bd-etiket. */
.bd-vn__merk { top: .8rem; }
.bd-vn__merk--links  { left: .8rem; }
.bd-vn__merk--rechts { right: .8rem; }

/* De schuif ligt over de foto en steekt aan beide kanten een halve greep uit.
   Daardoor staat het midden van de greep bij stand 0 precies op de linkerrand
   en bij stand 100 precies op de rechterrand van het beeld. */
.bd-vn__regel {
  position: absolute;
  z-index: 3;
  top: 50%;
  left: calc(var(--bd-vn-greep) / -2);
  width: calc(100% + var(--bd-vn-greep));
  height: 3rem;
  margin: 0;
  padding: 0;
  border: 0;
  transform: translateY(-50%);
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  cursor: ew-resize;
  touch-action: pan-y;
}

.bd-vn__regel::-webkit-slider-runnable-track { height: 3rem; background: transparent; border: 0; }
.bd-vn__regel::-moz-range-track { height: 3rem; background: transparent; border: 0; }

.bd-vn__regel::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box;
  width: var(--bd-vn-greep);
  height: var(--bd-vn-greep);
  margin-top: .375rem;           /* (3rem - 2.25rem) / 2, houdt de greep gecentreerd */
  border-radius: 50%;
  /* Twee pijltjes in de greep. Een kale bol zegt niet dat je hem kunt slepen. */
  background: var(--goud) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%230D0B09'%3E%3Cpath d='M10 6 4 12l6 6z'/%3E%3Cpath d='M14 6l6 6-6 6z'/%3E%3C/svg%3E") center / 1.25rem 1.25rem no-repeat;
  border: 2px solid var(--grond);
  box-shadow: 0 2px 12px rgba(0, 0, 0, .6);
  cursor: ew-resize;
}
.bd-vn__regel::-moz-range-thumb {
  box-sizing: border-box;
  width: var(--bd-vn-greep);
  height: var(--bd-vn-greep);
  border-radius: 50%;
  /* Twee pijltjes in de greep. Een kale bol zegt niet dat je hem kunt slepen. */
  background: var(--goud) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%230D0B09'%3E%3Cpath d='M10 6 4 12l6 6z'/%3E%3Cpath d='M14 6l6 6-6 6z'/%3E%3C/svg%3E") center / 1.25rem 1.25rem no-repeat;
  border: 2px solid var(--grond);
  box-shadow: 0 2px 12px rgba(0, 0, 0, .6);
  cursor: ew-resize;
}

/* Een ring om de greep zelf is niet te tekenen, dus zetten we hem om het hele
   beeld. Dat is bij toetsenbordgebruik veel beter te zien. */
.bd-vn__regel:focus { outline: none; }
.bd-vn__regel:focus + .bd-vn__beeld {
  outline: 2px solid var(--goud-licht);
  outline-offset: 3px;
}

/* =====================================================================
   4. Duotone die bij aanwijzen naar echte kleuren gaat
   ===================================================================== */

.bd-duo {
  display: grid;
  gap: 1rem;
  /* min(100%, 12rem) voorkomt dat de kolom breder wil zijn dan het paneel op
     een scherm van 360 pixels. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}

.bd-duo__kaart {
  display: grid;
  gap: .6rem;
  margin: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  background: none;
  border: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.bd-duo__kaart:focus { outline: 2px solid var(--goud-licht); outline-offset: 3px; border-radius: 4px; }
.bd-duo__kaart:focus:not(:focus-visible) { outline: none; }
.bd-duo__kaart:focus-visible { outline: 2px solid var(--goud-licht); outline-offset: 3px; }

.bd-duo__raam {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--bd-hoek);
  aspect-ratio: 4 / 3;
  background: #0D0B09;
  /* Eigen laagje, anders mengt de kleurlaag met alles wat eronder ligt. */
  isolation: isolate;
}

.bd-duo__foto {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Iets donkerder en contrastrijker dan de echte foto. Daardoor is de sprong
     naar kleur duidelijk te zien zonder dat de kleurlaag schreeuwerig hoeft. */
  filter: grayscale(1) contrast(1.16) brightness(.84);
  transition: filter .55s ease, transform .8s var(--bd-veer);
}

/* De mengstand blijft staan en alleen de dekking beweegt. Een mengstand kun je
   namelijk niet vloeiend laten overlopen; dekking wel.
   Let op de verzadiging van deze drie kleuren: bij mix-blend-mode:color neemt
   de foto de verzadiging van deze laag letterlijk over. Met het volle goud
   #C9A227 wordt licht deeg citroengeel. Rond 35 tot 45 procent verzadiging
   blijft het brons en blijft het duur ogen. */
.bd-duo__waas {
  position: absolute;
  inset: 0;
  background: linear-gradient(150deg, #3C4870 0%, #8A6737 54%, #B8904E 100%);
  mix-blend-mode: color;
  opacity: 1;
  transition: opacity .55s ease;
  pointer-events: none;
}

/* Terugval voor browsers zonder mengstanden: dan doen we de tweekleur met een
   gewoon filter. Minder mooi, maar het blijft een duidelijke kleurwissel. */
@supports not (mix-blend-mode: color) {
  .bd-duo__waas { display: none; }
  .bd-duo__foto { filter: grayscale(1) sepia(.6) saturate(1.8) contrast(1.05) brightness(.92); }
}

/* Aanraken met een vinger (dan telt :active), het toetsenbord via
   :focus-visible, en met JavaScript een vaste stand. */
.bd-duo__kaart:active .bd-duo__foto,
.bd-duo__kaart:focus-visible .bd-duo__foto,
.bd-duo__kaart--kleur .bd-duo__foto { filter: none; transform: scale(1.03); }

.bd-duo__kaart:active .bd-duo__waas,
.bd-duo__kaart:focus-visible .bd-duo__waas,
.bd-duo__kaart--kleur .bd-duo__waas { opacity: 0; }

/* Aanwijzen met de muis staat apart, want alleen een echte aanwijzer gaat er
   ook weer vanaf. Op een aanraakscherm blijft :hover na een tik plakken: de
   tweede tik haalt bd-duo__kaart--kleur weg en zet aria-pressed op false,
   maar de blijvende :hover hield filter:none overeind. De foto stond dan in
   kleur terwijl de knop meldde dat hij niet was ingedrukt. */
@media (hover: hover) and (pointer: fine) {
  .bd-duo__kaart:hover .bd-duo__foto { filter: none; transform: scale(1.03); }
  .bd-duo__kaart:hover .bd-duo__waas { opacity: 0; }
}

.bd-duo__voet {
  display: grid;
  gap: .15rem;
}

.bd-duo__naam {
  display: block;
  font-family: var(--tekst);
  font-size: .95rem;
  font-weight: 600;
  color: var(--deeg);
}

.bd-duo__hint {
  display: block;
  font-family: var(--tekst);
  font-size: .8rem;
  color: var(--deeg-zacht);
}

/* =====================================================================
   5. Korrel en ruis over een foto
   ===================================================================== */

.bd-korrel__raam {
  position: relative;
  overflow: hidden;
  border-radius: var(--bd-hoek);
  aspect-ratio: 16 / 9;
  background: var(--grond);
}

.bd-korrel__foto {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Uitsnede op het vlakke, lichte deel van de foto. Juist daar zie je wat
     korrel doet: een egaal vlak wordt levendig in plaats van digitaal glad. */
  object-position: 26% 50%;
}

/* De ruis is een SVG in dit stijlbestand zelf. Geen extra verzoek, geen extra
   bestand om kwijt te raken, en hij schaalt niet mee met de foto. */
.bd-korrel__ruis {
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180' viewBox='0 0 180 180'%3E%3Cfilter id='bdruis' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23bdruis)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
  background-repeat: repeat;
  opacity: var(--bd-korrel-kracht, .38);
  mix-blend-mode: overlay;
  transition: opacity .35s ease;
  pointer-events: none;
}

/* Zonder mengstanden legt de ruis een grijze sluier over de foto. Dan liever
   veel zachter, zodat het niet vies wordt. */
@supports not (mix-blend-mode: overlay) {
  .bd-korrel__ruis { opacity: .16; }
}

/* Een randje donkerte houdt het oog in het midden en verbergt meteen de
   overgang van foto naar paneel. */
.bd-korrel__vignet {
  position: absolute;
  inset: 0;
  background: radial-gradient(118% 92% at 50% 45%, rgba(13, 11, 9, 0) 42%, rgba(13, 11, 9, .6) 100%);
  pointer-events: none;
}

.bd-korrel--kaal .bd-korrel__ruis { opacity: 0; }
.bd-korrel__band { left: .9rem; bottom: .9rem; }

/* =====================================================================
   6. Foto die scherp wordt bij het laden
   ===================================================================== */

.bd-scherp__rij {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 8.5rem), 1fr));
}

.bd-scherp__tegel {
  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--bd-hoek);
  aspect-ratio: 1 / 1;
  /* Vaste maat en een eigen ondergrond: zo verspringt er niets terwijl de
     foto nog onderweg is. */
  background: linear-gradient(140deg, #1C1611 0%, #100D0B 100%);
}

.bd-scherp__foto {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: 8%;
}

/* Beginstand achter html.js: zonder JavaScript komt de foto gewoon scherp
   binnen en blijft er niets wazig hangen. */
html.js .bd-scherp__foto {
  filter: blur(16px) saturate(.65);
  transform: scale(.92);
  opacity: .4;
  transition: filter .9s ease, transform 1.1s var(--bd-veer), opacity .7s ease;
}
html.js .bd-scherp--klaar .bd-scherp__foto {
  filter: blur(0) saturate(1);
  transform: none;
  opacity: 1;
}

/* Kleine vertraging per tegel. Alles tegelijk scherp worden leest als een
   schokje, na elkaar leest als iets dat binnenkomt. */
html.js .bd-scherp__tegel:nth-child(2) .bd-scherp__foto { transition-delay: .14s; }
html.js .bd-scherp__tegel:nth-child(3) .bd-scherp__foto { transition-delay: .28s; }

html.js .bd-scherp.bd-scherp--vast .bd-scherp__foto { transition: none; }

/* =====================================================================
   Stilstand: geen beweging, wel alles zichtbaar en leesbaar
   ===================================================================== */

@media (prefers-reduced-motion: reduce) {

  /* 1. Ken Burns staat stil op de gewone uitsnede. Er valt niets te pauzeren,
        dus verdwijnt ook de pauzeknop. */
  html.js .bd-kb__foto { animation: none; transform: none; will-change: auto; }
  html.js .bd-knop[data-bd-kb-knop] { display: none; }

  /* 2. De veeg vervalt: de foto staat er meteen, volledig en scherp. Deze
        selector telt bewust een klasse extra, anders wint de eindstand uit
        het @supports-blok hierboven en blijft er een clip-path staan. */
  html.js .bd-veeg.bd-veeg--zicht .bd-veeg__foto,
  html.js .bd-veeg .bd-veeg__foto {
    opacity: 1;
    clip-path: none;
    transform: none;
    transition: none;
  }
  html.js .bd-veeg__raam::after { display: none; }
  html.js .bd-knop[data-bd-veeg-knop] { display: none; }

  /* 3. De schuif blijft gewoon werken: dat is bediening, geen animatie. */

  /* 4. De kleurwissel blijft, maar zonder overgang en zonder schaalsprong. */
  .bd-duo__foto { transition: none; }
  .bd-duo__waas { transition: none; }
  .bd-duo__kaart:hover .bd-duo__foto,
  .bd-duo__kaart:active .bd-duo__foto,
  .bd-duo__kaart:focus-visible .bd-duo__foto,
  .bd-duo__kaart--kleur .bd-duo__foto { transform: none; }

  /* 5. Korrel is stil van zichzelf; alleen de overgang gaat eruit. */
  .bd-korrel__ruis { transition: none; }

  /* 6. Foto's staan direct scherp, niets blijft wazig of klein hangen. Ook
        hier een klasse extra, zodat de eindstand deze regel niet verslaat. */
  html.js .bd-scherp.bd-scherp--klaar .bd-scherp__foto,
  html.js .bd-scherp .bd-scherp__foto {
    filter: none;
    transform: none;
    opacity: 1;
    transition: none;
  }
  html.js .bd-knop[data-bd-scherp-knop] { display: none; }

  .bd-knop { transition: none; }
}


/* ===== familie pt · Aanwijzer ===== */
/* Familie "Aanwijzer" — zes effecten die aan de muis hangen.
   Uitgangspunt: de muis is een extraatje, geen voorwaarde. Elk effect heeft
   daarom drie toestanden die alle drie af zijn: met muis, zonder muis, en met
   "minder beweging" aan. In alle drie staat exact dezelfde tekst even leesbaar
   op het scherm. Beginstanden zijn zo gekozen dat de stand zonder JavaScript
   toevallig ook de goede eindstand is; alleen de stip heeft html.js nodig,
   want die bestaat helemaal niet zonder script.

   Alle draaiingen, verschuivingen en doorzichtigheden lopen via eigen
   variabelen (--pt-...). JavaScript schrijft alleen die variabelen en raakt
   nooit een echte eigenschap aan; zo blijft de CSS de baas over hoe iets
   eruitziet en het script alleen over waar de muis is. */

/* Alle knoppen in deze familie erven de paginaletter en houden hun eigen
   focusring, want een demo zonder zichtbare focus is geen demo maar een val. */
.pt-kantel__knop,
.pt-rand__knop,
.pt-rimpel,
.pt-stip__knop,
.pt-glans,
.pt-dock__knop {
  font-family: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.pt-kantel__knop:focus-visible,
.pt-rand__knop:focus-visible,
.pt-rimpel:focus-visible,
.pt-stip__knop:focus-visible,
.pt-glans:focus-visible,
.pt-dock__knop:focus-visible {
  outline: 2px solid var(--goud-licht);
  outline-offset: 3px;
}
/* Vangnet voor smalle kolommen. Een kaart van honderd pixels breed en een kop
   van een woord dat daar niet in past: dan breekt het woord af in plaats van
   dat het over de rand van de kaart heen loopt. */
.pt-kantel__kop, .pt-rand__kop, .pt-rand__knop, .pt-glans__kop, .pt-stip__kop {
  overflow-wrap: break-word;
}


/* ── 1. Kaart die kantelt onder de muis ───────────────────────────────────
   Het perspectief staat op de wikkel en niet op de kaart zelf. Zet je het op
   de kaart, dan verschuift het vluchtpunt mee met de kaart en klopt de
   ruimtelijke indruk niet meer zodra hij draait.
   De lichtreflectie zit binnen het beeldvlak, dus niet over het tekstvlak.
   Dat is bewust: een lichtvlek die over letters trekt haalt het contrast in
   een halve seconde onder de norm, en dat is precies wanneer iemand leest. */
.pt-kantel-veld {
  /* De variabelen staan op de wikkel en niet op de kaart, omdat het script ze
     ook op de wikkel schrijft. De wikkel draait namelijk zelf niet mee: zou de
     muis worden gevolgd op de kaart, dan verschuift de kaart onder de muis
     vandaan, valt de muis er even af, springt de kaart terug, en trilt het
     geheel. De waarden erven vanzelf door naar binnen. */
  --pt-k-x: 0deg;      /* kanteling om de horizontale as */
  --pt-k-y: 0deg;      /* kanteling om de verticale as   */
  --pt-k-gx: 50%;      /* plek van de lichtvlek          */
  --pt-k-gy: 50%;
  --pt-k-glans: 0;
  width: min(100%, 320px);
  perspective: 900px;
  perspective-origin: 50% 50%;
}
.pt-kantel { display: block; }
.pt-kantel__vlak {
  position: relative;
  border-radius: 14px;
  background: var(--grond);
  border: 1px solid var(--lijn);
  box-shadow: 0 20px 48px rgba(0, 0, 0, .55);
  transform: rotateX(var(--pt-k-x)) rotateY(var(--pt-k-y));
  transform-style: preserve-3d;
  transition: transform .18s ease-out, box-shadow .3s ease;
}
.pt-kantel__beeld {
  position: relative;
  overflow: hidden;                 /* rondt de foto af zonder de kaart plat te slaan */
  border-radius: 13px 13px 0 0;
}
.pt-kantel__foto {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: center 42%;
  filter: brightness(.86);
}
.pt-kantel__glans {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: var(--pt-k-glans);
  transition: opacity .3s ease;
  /* Bewust een ellips op maat van het vlak en niet de standaardgrootte. Zonder
     maat rekent de browser tot de verste hoek, en dan is de vlek zo groot dat
     de halve foto wegtrekt in de mist; dit blijft een reflectie. */
  background: radial-gradient(ellipse 44% 54% at var(--pt-k-gx) var(--pt-k-gy),
    rgba(255, 248, 225, .26), rgba(255, 248, 225, .06) 40%, rgba(255, 248, 225, 0) 72%);
  mix-blend-mode: screen;
}
/* Kent de browser geen mengmodus, dan wordt het een gewone lichte vlek.
   Iets minder mooi, nergens stuk. */
@supports not (mix-blend-mode: screen) {
  .pt-kantel__glans {
    background: radial-gradient(ellipse 44% 54% at var(--pt-k-gx) var(--pt-k-gy),
      rgba(255, 248, 225, .18), rgba(255, 248, 225, 0) 70%);
  }
}
.pt-kantel__tekst {
  padding: 1.125rem 1.25rem 1.375rem;
  border-radius: 0 0 13px 13px;
  transform-style: preserve-3d;
}
/* De inhoud staat een paar millimeter voor de kaart. Dat maakt het kantelen
   leesbaar als diepte in plaats van als scheefstand. */
.pt-kantel__tekst > * {
  transform: translateZ(20px);
}
.pt-kantel__label {
  margin: 0 0 .25rem;
  font-size: .75rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--goud);
}
.pt-kantel__kop {
  margin: 0 0 .5rem;
  font-family: var(--kop);
  font-size: 1.375rem;
  line-height: 1.15;
  color: var(--deeg);
}
.pt-kantel__regel {
  margin: 0 0 1rem;
  font-size: .875rem;
  color: var(--deeg-zacht);
}
.pt-kantel__knop {
  display: inline-block;
  padding: .55rem 1.1rem;
  font-size: .875rem;
  font-weight: 600;
  color: var(--deeg);
  background: transparent;
  border: 1px solid rgba(243, 235, 221, .45);
  border-radius: 999px;
  transition: border-color .2s ease, color .2s ease;
}
/* Hover alleen daar waar een muis hangt. Op een aanraakscherm blijft :hover na
   het tikken plakken, en dan is die plakkende rand de enige reactie die iemand
   op zijn tik krijgt. */
@media (hover: hover) and (pointer: fine) {
  .pt-kantel__knop:hover {
    border-color: var(--goud-licht);
    color: var(--goud-licht);
  }
}
/* Indrukken geeft altijd antwoord, ook zonder muis. De tap-highlight staat uit,
   dus dit is de terugkoppeling. Goud (#E8CE73) op de opgelichte vulling over de
   kaart (#0D0B09) haalt 9,2:1. */
.pt-kantel__knop:active {
  border-color: var(--goud-licht);
  color: var(--goud-licht);
  background: rgba(232, 206, 115, .16);
}
/* Een extra laag aanmaken kost geheugen. Doe dat alleen op apparaten waar het
   effect ook echt gaat draaien. */
@media (hover: hover) and (pointer: fine) {
  .pt-kantel__vlak { will-change: transform; }
  .pt-kantel-veld:hover .pt-kantel__vlak { box-shadow: 0 26px 60px rgba(0, 0, 0, .62); }
}
/* Zonder muis blijft de kaart recht staan, maar krijgt hij een vaste lichtval
   linksboven. Anders ziet een telefoonbezoeker een dode, vlakke kaart die op
   de andere schermen wel leeft. */
@media (hover: none) {
  .pt-kantel-veld { --pt-k-gx: 26%; --pt-k-gy: 14%; --pt-k-glans: .55; }
}


/* ── 2. Rand die oplicht waar de muis is ──────────────────────────────────
   De gloed zit in een ::before die precies over de rand van de kaart valt en
   dan met twee maskers wordt uitgehold: de buitenkant blijft staan, de
   binnenkant wordt weggeknipt. Zo licht alleen de lijn op en niet het vlak,
   want een lichtvlek achter de tekst kost contrast. */
/* De ondergrens van 110px is met opzet laag: op een telefoon staan er dan twee
   kaarten naast elkaar in plaats van drie onder elkaar. Drie kaarten op volle
   breedte maken van dit ene voorbeeld een scherm van meer dan duizend pixels
   hoog, en dan scrolt iemand er alleen maar langs. */
.pt-rand-rij {
  width: 100%;
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 110px), 1fr));
}
.pt-rand {
  --pt-r-x: 50%;
  --pt-r-y: 0%;
  --pt-r-aan: 0;
  position: relative;
  background: var(--grond);
  border: 1px solid var(--lijn);
  border-radius: 14px;
  padding: .75rem .75rem 1rem;
  transition: border-color .25s ease;
}
.pt-rand::before {
  content: "";
  position: absolute;
  inset: -1px;                       /* valt exact over de 1px rand van de kaart */
  border-radius: inherit;
  padding: 1px;                      /* dit wordt de dikte van de oplichtende lijn */
  pointer-events: none;
  background: radial-gradient(170px circle at var(--pt-r-x) var(--pt-r-y),
    var(--goud-licht), rgba(201, 162, 39, .45) 42%, rgba(201, 162, 39, 0) 72%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  opacity: var(--pt-r-aan);
  transition: opacity .25s ease;
}
/* Toetsenbord telt hier net zo goed als een muis: wie tabt, ziet dezelfde
   kaart oplichten. De regel staat na de basisregel en is specifieker, dus hij
   wint van de variabele die het script schrijft. */
.pt-rand:focus-within::before { opacity: 1; }

/* Terugval voor browsers die twee maskers niet kunnen samenvoegen. Daar wordt
   het geen wandelende gloed maar een rand die netjes van kleur wisselt. Geen
   gloed achter de tekst, dus het contrast blijft overeind. */
@supports not ((mask-composite: exclude) or (-webkit-mask-composite: xor)) {
  .pt-rand::before { display: none; }
  .pt-rand:focus-within { border-color: rgba(232, 206, 115, .7); }
  @media (hover: hover) {
    .pt-rand:hover { border-color: rgba(232, 206, 115, .7); }
  }
}
/* Vierkant, want de bronbestanden zijn vierkant. Een liggende uitsnede snijdt
   de boven- en onderkant van een rond vel eraf en dan lijkt het plat afgekapt. */
.pt-rand__foto {
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 9px;
  filter: brightness(.9);
}
.pt-rand__kop {
  margin: .75rem 0 .25rem;
  font-family: var(--kop);
  font-size: 1.0625rem;
  line-height: 1.2;
  color: var(--deeg);
  text-wrap: balance;
}
.pt-rand__regel {
  margin: 0 0 .75rem;
  font-size: .8125rem;
  color: var(--deeg-zacht);
}
/* Een onderstreepte knop is nog steeds een knop, dus hij moet ook met een duim
   te raken zijn. Zonder minimumhoogte werd dit vlak zo'n 28px, en in dit raster
   staan op een telefoon drie van deze kaarten naast elkaar. De inline-flex houdt
   de tekst in het midden van die 44px in plaats van hem bovenaan te plakken, en
   de zijkanten krijgen wat ruimte zodat er ook horizontaal iets te raken valt. */
.pt-rand__knop {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: .375rem .625rem;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--goud-licht);
  background: none;
  border: 0;
  border-bottom: 1px solid rgba(232, 206, 115, .45);
}
@media (hover: hover) and (pointer: fine) {
  .pt-rand__knop:hover { border-bottom-color: var(--goud-licht); }
}
/* Terugkoppeling bij het indrukken, want de tap-highlight staat uit. Goud
   (#E8CE73) op de opgelichte vulling over de kaart (#0D0B09) haalt 9,7:1. */
.pt-rand__knop:active {
  border-bottom-color: var(--goud-licht);
  background: rgba(232, 206, 115, .14);
}
/* Zonder muis is er geen plek om naartoe te lichten. Dan krijgt elke kaart een
   vaste gloed aan de bovenrand: rustig, en het laat zien dat de rand meedoet. */
@media (hover: none) {
  .pt-rand { --pt-r-aan: .6; --pt-r-x: 50%; --pt-r-y: 0%; }
}


/* ── 3. Rimpel vanaf de plek van de klik ──────────────────────────────────
   De rimpel is een los element dat het script op de raakplek neerzet en dat
   zichzelf opruimt zodra de animatie klaar is. Dat is bewust geen pseudo-laag:
   twee klikken kort na elkaar moeten twee rimpels geven, en een pseudo-laag
   kan er maar een tegelijk. */
.pt-rimpel-veld {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: .75rem;
  max-width: 30rem;
  text-align: center;
}
.pt-rimpel {
  position: relative;
  overflow: hidden;                  /* houdt de rimpel binnen de knopvorm */
  isolation: isolate;
  padding: .85rem 1.6rem;
  font-size: .9375rem;
  font-weight: 600;
  border-radius: 999px;
  border: 1px solid transparent;
  transition: background-color .2s ease, color .2s ease, transform .12s ease;
}
.pt-rimpel--vol {
  background: var(--goud-licht);
  color: var(--grond);
  border-color: var(--goud-licht);
}
.pt-rimpel--lijn {
  background: transparent;
  color: var(--deeg);
  border-color: rgba(243, 235, 221, .45);
}
.pt-rimpel:active { transform: translateY(1px); }
.pt-rimpel__tekst { position: relative; z-index: 2; }
.pt-rimpel__golf {
  position: absolute;
  z-index: 1;
  border-radius: 50%;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(0);
  animation: pt-golf .62s cubic-bezier(.22, .61, .36, 1) forwards;
  will-change: transform, opacity;
}
.pt-rimpel--vol .pt-rimpel__golf { background: rgba(13, 11, 9, .3); }
.pt-rimpel--lijn .pt-rimpel__golf { background: rgba(232, 206, 115, .3); }
@keyframes pt-golf {
  from { transform: translate(-50%, -50%) scale(0); opacity: .95; }
  to   { transform: translate(-50%, -50%) scale(1); opacity: 0; }
}
/* De stille variant. Bij "minder beweging" maakt het script geen rimpel maar
   zet het deze klasse er kort op: de knop wisselt van kleur en staat verder
   volkomen stil. Wie klikt krijgt nog steeds antwoord. */
.pt-rimpel--vol.pt-rimpel--raak { background: var(--goud); border-color: var(--goud); }
.pt-rimpel--lijn.pt-rimpel--raak {
  background: rgba(232, 206, 115, .16);
  border-color: var(--goud-licht);
}
.pt-rimpel__hint {
  flex: 1 0 100%;
  margin: .5rem 0 0;
  font-size: .875rem;
  color: var(--deeg-zacht);
}


/* ── 4. Stip die de muis met vertraging volgt ─────────────────────────────
   Belangrijk: de echte muisaanwijzer blijft gewoon staan. De stip is een open
   ring, dus je ziet de pijl er dwars doorheen. Een eigen aanwijzer die de
   echte verbergt kost precisie, en dat merk je pas als je iets kleins moet
   raken; dat is het niet waard.
   De ring hangt in het demovlak en niet in de pagina, zodat hij nooit over
   andere onderdelen van de pagina heen loopt. */
.pt-stip__veld {
  position: relative;
  width: 100%;
  max-width: 42ch;
  min-height: clamp(200px, 28vh, 260px);
}
.pt-stip__kop {
  margin: 0 0 .5rem;
  font-family: var(--kop);
  font-size: 1.25rem;
  line-height: 1.2;
  color: var(--deeg);
}
.pt-stip__regel {
  margin: 0 0 1rem;
  font-size: .875rem;
  color: var(--deeg-zacht);
}
.pt-stip__rij {
  display: flex;
  flex-wrap: wrap;
  gap: .625rem;
  margin: 0;
}
.pt-stip__knop {
  padding: .55rem 1rem;
  font-size: .875rem;
  font-weight: 600;
  color: var(--deeg);
  background: var(--grond);
  border: 1px solid rgba(243, 235, 221, .45);
  border-radius: 999px;
  transition: border-color .2s ease, color .2s ease;
}
@media (hover: hover) and (pointer: fine) {
  .pt-stip__knop:hover { border-color: var(--goud-licht); color: var(--goud-licht); }
}
/* Op dit vlak volgt er zonder muis niets, dus is het indrukken hier de enige
   reactie die overblijft. Goud (#E8CE73) op de opgelichte vulling over het doek
   (#15110E) haalt 8,5:1. */
.pt-stip__knop:active {
  border-color: var(--goud-licht);
  color: var(--goud-licht);
  background: rgba(232, 206, 115, .16);
}
.pt-stip__punt { display: none; }
/* De ring bestaat alleen als er een script draait en als er een echte muis is.
   Op alle andere apparaten wordt hij niet eens aangemaakt. */
@media (hover: hover) and (pointer: fine) {
  html.js .pt-stip__punt {
    display: block;
    position: absolute;
    left: 0;
    top: 0;
    z-index: 3;
    width: 20px;
    height: 20px;
    margin: -10px 0 0 -10px;         /* zet het midden van de ring op de coördinaat */
    border: 2px solid rgba(232, 206, 115, .85);
    /* De lijn wordt teruggedeeld door de vergroting, zodat de ring boven een
       knop wel groter wordt maar niet dikker. Een lijn van bijna vijf pixels
       snijdt dwars door het opschrift dat je juist wilt lezen. De 2px hierboven
       blijft staan als terugval voor wie deze deling niet aankan. */
    border-width: calc(2px / var(--pt-s-schaal, 1));
    border-radius: 50%;
    pointer-events: none;            /* mag nooit een klik onderscheppen */
    opacity: var(--pt-s-op, 0);
    transform: translate3d(var(--pt-s-x, 0px), var(--pt-s-y, 0px), 0) scale(var(--pt-s-schaal, 1));
    transition: opacity .25s ease, background-color .2s ease, border-color .2s ease;
    will-change: transform;
  }
  /* De vertraging zit in het script, niet in een transition. Een transition op
     transform zou boven op de vertraging nog een tweede vertraging leggen en
     dan voelt de stip stroperig in plaats van soepel. */
  html.js .pt-stip__punt--groot {
    background: rgba(232, 206, 115, .16);
    border-color: var(--goud-licht);
  }
}
/* Drie eerlijke mededelingen in plaats van een leeg vlak. Elke reden om het
   effect uit te zetten krijgt zijn eigen regel, zodat de bezoeker weet dat er
   niets stuk is. */
.pt-stip__uit {
  display: none;
  margin: 1rem 0 0;
  font-size: .875rem;
  color: rgba(201, 191, 175, .8);
}
@media (hover: none), (pointer: coarse) {
  .pt-stip__uit--muis { display: block; }
}
html:not(.js) .pt-stip__uit--script { display: block; }


/* ── 5. Glansstreep die over de knop trekt ────────────────────────────────
   De streep loopt onder de tekst door en niet eroverheen. Dat is het hele
   verschil tussen een knop die even oplicht en een knop waarvan het opschrift
   een halve seconde onleesbaar is. */
.pt-glans-veld {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
  width: min(100%, 340px);
}
.pt-glans {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  border: 1px solid transparent;
  text-align: left;
}
.pt-glans::after {
  content: "";
  position: absolute;
  z-index: 1;                        /* onder de tekst, boven de achtergrond */
  top: -60%;
  bottom: -60%;
  left: -40%;
  width: 34%;
  pointer-events: none;
  transform: translateX(-170%) rotate(18deg);
  background: linear-gradient(90deg,
    rgba(255, 248, 225, 0) 0%, rgba(255, 248, 225, .45) 50%, rgba(255, 248, 225, 0) 100%);
  filter: blur(2px);
}
/* Alleen de tekstlagen gaan boven de streep. De foto blijft eronder, want daar
   moet de streep juist overheen trekken: dat is de reflectie. Het tekstvlak is
   dekkend, dus de streep verdwijnt netjes achter de letters in plaats van
   eroverheen te vegen. */
.pt-glans__tekst,
.pt-glans__vak { position: relative; z-index: 2; }
@keyframes pt-veeg {
  from { transform: translateX(-170%) rotate(18deg); }
  to   { transform: translateX(540%)  rotate(18deg); }
}
/* Een animatie in plaats van een transition, want een transition draait bij
   het weghalen van de muis netjes terug en dan trekt de streep achterwaarts
   over de knop. Dat ziet er verkeerd uit. */
@media (hover: hover) and (pointer: fine) {
  .pt-glans:hover::after { animation: pt-veeg .85s ease-out; }
}
.pt-glans:focus-visible::after { animation: pt-veeg .85s ease-out; }
/* Op een aanraakscherm blijft :hover na het tikken hangen. Daarom hangt de
   streep daar aan het indrukken zelf. */
@media (hover: none) {
  .pt-glans:active::after { animation: pt-veeg .85s ease-out; }
}
.pt-glans--knop {
  width: 100%;
  padding: .95rem 1.5rem;
  font-size: 1rem;
  font-weight: 600;
  text-align: center;
  color: var(--grond);
  background: var(--goud-licht);
  border-color: var(--goud-licht);
  border-radius: 999px;
}
.pt-glans--kaart {
  display: block;
  width: 100%;
  padding: 0;
  background: var(--grond);
  border-color: var(--lijn);
  border-radius: 14px;
}
.pt-glans__foto {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  filter: brightness(.88);
}
.pt-glans__vak {
  display: block;
  padding: 1rem 1.125rem 1.25rem;
  background: var(--grond);          /* dekkend, zodat het contrast van de tekst vaststaat */
}
.pt-glans__kop {
  display: block;
  margin-bottom: .25rem;
  font-family: var(--kop);
  font-size: 1.1875rem;
  line-height: 1.2;
  color: var(--deeg);
}
.pt-glans__regel {
  display: block;
  font-size: .875rem;
  line-height: 1.5;
  color: var(--deeg-zacht);
}


/* ── 6. Rij die uitwijkt voor de aanwijzer ────────────────────────────────
   Alles gebeurt met transform. Zou ik de breedte van de tegels aanpassen, dan
   herberekent de browser bij elke muisbeweging de hele rij en gaat de pagina
   schokken. Met transform verandert er niets aan de opmaak, dus er komt ook
   nooit een horizontale schuifbalk bij.
   De tegels groeien vanaf hun onderrand omhoog, dus alle ruimte die het effect
   nodig heeft zit bovenin. Het bijschrift staat eronder en staat er altijd:
   een naam die alleen bij hover verschijnt is op een aanraakscherm nergens te
   vinden, en dan heet elke tegel dus niets. */
.pt-dock-veld {
  width: 100%;
  display: grid;
  justify-items: center;
  gap: .5rem;
}
.pt-dock {
  display: flex;
  flex-wrap: wrap;                   /* op smalle schermen breekt de rij af in plaats van eruit te lopen */
  align-items: flex-start;
  justify-content: center;
  column-gap: clamp(.375rem, 1.5vw, .75rem);
  row-gap: 1.5rem;
  list-style: none;
  margin: 0;
  padding: 3rem .25rem .5rem;        /* alleen bovenin ruimte: daar komt de tegel op */
}
/* Vaste breedte op het lijstitem, niet op de knop. Een lang bijschrift mag de
   rij niet oprekken, want dan verspringt de onderlinge afstand per tegel en
   klopt de berekening van de buren niet meer. */
/* De drie waarden staan op het lijstitem, niet op de knop, zodat het bijschrift
   ze erft en dezelfde kant op schuift als zijn tegel. Blijft het bijschrift
   staan terwijl de tegel uitwijkt, dan hoort de naam zichtbaar bij de buurman. */
.pt-dock__item {
  --pt-d-s: 1;
  --pt-d-x: 0px;
  --pt-d-y: 0px;
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: none;
  width: clamp(52px, 13vw, 76px);
}
.pt-dock__knop {
  display: block;
  width: 100%;
  padding: 0;
  overflow: hidden;
  background: var(--grond);
  border: 1px solid var(--lijn);
  border-radius: 14px;
  transform: translate3d(var(--pt-d-x), var(--pt-d-y), 0) scale(var(--pt-d-s));
  transform-origin: 50% 100%;        /* groeit omhoog, dus het bijschrift blijft staan */
  transition: border-color .25s ease;
}
.pt-dock__knop:hover { border-color: rgba(232, 206, 115, .55); }
.pt-dock__foto {
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
  filter: brightness(.92);
}
/* Dit bijschrift is de naam van de knop: de knop wijst er met aria-labelledby
   naar. Dus geen aria-hidden en geen tweede, verborgen kopie van dezelfde tekst. */
.pt-dock__naam {
  margin-top: .5rem;
  font-size: .6875rem;
  line-height: 1.3;
  text-align: center;
  color: var(--deeg-zacht);
  overflow-wrap: break-word;
  transform: translateX(var(--pt-d-x));   /* schuift mee, groeit niet mee */
  transition: color .25s ease;
}
.pt-dock__knop:hover + .pt-dock__naam,
.pt-dock__knop:focus-visible + .pt-dock__naam { color: var(--goud-licht); }
@media (hover: hover) and (pointer: fine) {
  .pt-dock__knop { will-change: transform; }
}
/* Zonder muis is er niets om voor uit te wijken. Dan groeit alleen de tegel
   die je aanraakt, precies zolang je hem vasthoudt. */
@media (hover: none) {
  .pt-dock__knop { transition: transform .18s ease-out, border-color .25s ease; }
  .pt-dock__item:active { --pt-d-s: 1.14; --pt-d-y: -6px; }
}
.pt-dock__hint {
  margin: 0;
  max-width: 34ch;
  text-align: center;
  font-size: .875rem;
  color: var(--deeg-zacht);
}


/* ── Minder beweging ──────────────────────────────────────────────────────
   lab.css zet alle animaties en transitions al uit met !important. Wat hier
   staat is het restje: de standen die anders op hun laatste waarde zouden
   blijven staan, en de versieringen die zonder beweging niets meer toevoegen.
   Geen enkel stuk tekst raakt hierdoor kwijt of onleesbaar; het script zet
   zichzelf bij deze instelling ook stil. */
@media (prefers-reduced-motion: reduce) {
  .pt-kantel-veld { perspective: none; }
  .pt-kantel__vlak { transform: none; }
  .pt-kantel__tekst > * { transform: none; }
  .pt-rimpel__golf { display: none; }
  .pt-rimpel:active { transform: none; }
  html.js .pt-stip__punt { display: none; }
  .pt-stip__uit--beweging { display: block; }
  .pt-glans::after { display: none; }
  .pt-glans:hover, .pt-glans:focus-visible { filter: brightness(1.12); }
  .pt-dock__knop { transform: none; }
  .pt-dock__naam { transform: none; }
  .pt-dock__item:active { --pt-d-s: 1; --pt-d-y: 0px; }
}


/* ===== familie dp · Diepte en 3D ===== */
/* ==========================================================================
   dp - Diepte en 3D
   Vijf effecten met CSS 3D transforms. Geen WebGL, geen bibliotheek.

   Drie regels die in dit hele bestand terugkomen, met de reden erbij:

   1. Perspectief staat bewust HOOG (900px tot 1400px). Bij een lage
      perspectiefwaarde wordt een vlak dat naar voren komt fors uitvergroot,
      en juist die uitvergroting maakt tekst wazig. Met 1400px blijft de
      vergroting onder de 1,1x en blijft tekst scherp.
   2. Nergens wordt gecentreerd met translate(-50%, -50%). Dat levert
      halve pixels op, en halve pixels zijn de meest voorkomende oorzaak van
      vage letters op een 3D-vlak. Overal grid of flex gebruikt.
   3. will-change staat alleen op vlakken ZONDER tekst. Op een element met
      will-change bevriest de browser de rasterschaal, wat prima is voor een
      foto maar tekst permanent zacht maakt.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Gedeelde onderdelen van deze familie
   -------------------------------------------------------------------------- */

.dp-knop {
  font: inherit;
  font-family: var(--tekst);
  font-size: .95rem;
  line-height: 1.2;
  color: var(--goud-licht);
  background: transparent;
  border: 1px solid var(--goud);
  border-radius: 999px;
  padding: .6rem 1.15rem;
  cursor: pointer;
  /* Minimaal 44px hoog zodat de knop op een telefoon met een duim raak te
     tikken is zonder dat je moet mikken. */
  min-height: 44px;
  transition: background-color .2s ease, color .2s ease;
}

@media (hover: hover) and (pointer: fine) {
  .dp-knop:hover {
    background: var(--goud-licht);
    color: var(--grond);
  }
}

.dp-knop:active {
  background: var(--goud-licht);
  color: var(--grond);
}

.dp-bediening {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  justify-content: center;
}

/* Eén focusring voor de hele familie. Ook op gedraaide vlakken blijft die
   zichtbaar, omdat de outline mee kantelt met het vlak. */
.dp-knop:focus-visible,
.dp-flow-knop:focus-visible,
.dp-flow-baan:focus-visible {
  outline: 2px solid var(--goud-licht);
  outline-offset: 3px;
}

/* Oudere browsers zonder :focus-visible krijgen gewoon :focus, anders zou
   daar helemaal geen ring staan. */
@supports not selector(:focus-visible) {
  .dp-knop:focus,
  .dp-flow-knop:focus,
  .dp-flow-baan:focus {
    outline: 2px solid var(--goud-licht);
    outline-offset: 3px;
  }
}

.dp-onderschrift {
  margin: 0;
  max-width: 46ch;
  text-align: center;
  font-family: var(--tekst);
  font-size: .875rem;
  line-height: 1.55;
  /* #C9BFAF op het donkere paneel haalt ruim 10:1, dus veilig onder AA. */
  color: var(--deeg-zacht);
}

/* Alleen voor schermlezers. Wordt gebruikt om het omdraaien van de kaart
   aan te kondigen, want een visuele draai zegt een schermlezer niets. */
.dp-alleen-lezer {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Over elke foto met tekst erop ligt dezelfde sluier: rgba(13,11,9,.82).
   Bij die dekking komt zelfs een spierwit stukje foto niet lichter uit dan
   #393735, en daarop houdt --deeg nog 10,0:1 en --deeg-zacht nog 6,5:1.
   Dat is nagerekend op het slechtst denkbare beeldpunt, niet geschat. */

/* --------------------------------------------------------------------------
   1. Kaart die omdraait
   -------------------------------------------------------------------------- */

.dp-kaartveld {
  display: grid;
  justify-items: center;
  gap: 1rem;
  width: 100%;
  position: relative;
}

/* De kaart is gewone inhoud en géén knop. In een button telt alles binnenin
   als versiering: de koppen en de tekst op de kaart verdwijnen dan uit de
   voorleesboom en er blijft alleen het knoplabel over. Het omdraaien doet de
   losse knop .dp-kaart-knop hieronder. */
.dp-kaart {
  display: block;
  width: min(300px, 100%);
  border-radius: 14px;
}

/* Zonder JavaScript staan beide kanten gewoon onder elkaar en valt er niets
   om te draaien; dan horen de knop en de uitleg erover er ook niet te staan.
   inline-block en niet flex, zodat de knop precies zo oogt als de andere
   knoppen van deze familie. */
.dp-kaart-knop,
.dp-kaart-uitleg { display: none; }
html.js .dp-kaart-knop { display: inline-block; }
html.js .dp-kaart-uitleg { display: block; }

/* Zonder JavaScript is er niets om mee te draaien. Dan staan beide kanten
   gewoon onder elkaar, allebei leesbaar. Dat is de basis; het 3D-gedrag
   hieronder is de uitbreiding. */
.dp-kaart-spil {
  display: grid;
  gap: .75rem;
  width: 100%;
}

.dp-vlak {
  position: relative;
  display: grid;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid var(--lijn);
  background: var(--grond-op);
  min-height: 220px;
}

/* Eén expliciete cel waar de foto en de tekstbalk allebei in vallen. Zonder
   die vaste cel bepaalt de langste tekst de hoogte en gaat de foto meerekken. */
.dp-vlak-voor {
  grid-template: 1fr / 1fr;
}

.dp-vlak-foto {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  grid-area: 1 / 1;
}

.dp-vlak-band {
  grid-area: 1 / 1;
  align-self: end;
  display: grid;
  gap: .2rem;
  padding: .85rem 1rem;
  background: rgba(13, 11, 9, .82);
}

.dp-vlak-kop {
  font-family: var(--kop);
  font-size: 1.2rem;
  line-height: 1.15;
  color: var(--deeg);
}

.dp-vlak-regel {
  font-family: var(--tekst);
  font-size: .875rem;
  line-height: 1.5;
  color: var(--deeg-zacht);
}

.dp-achter-inhoud {
  grid-area: 1 / 1;
  display: grid;
  gap: .6rem;
  align-content: center;
  justify-items: start;
  padding: 1.25rem;
  text-align: left;
}

.dp-achter-merk {
  display: block;
  width: 116px;
  height: auto;
}

/* Met JavaScript: de twee kanten schuiven op elkaar en de kaart kan draaien. */
html.js .dp-kaart {
  perspective: 1200px;
  aspect-ratio: 3 / 4;
}

html.js .dp-kaart-spil {
  display: block;
  position: relative;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform .7s cubic-bezier(.2, .75, .25, 1);
}

html.js .dp-vlak {
  position: absolute;
  inset: 0;
  min-height: 0;
  /* Zonder dit zie je de achterkant spiegelbeeldig door de voorkant heen
     zodra de kaart voorbij een kwartslag komt. */
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}

html.js .dp-vlak-achter {
  transform: rotateY(180deg);
}

html.js .dp-kaart[data-dp-om="true"] .dp-kaart-spil {
  transform: rotateY(180deg);
}

@supports not (aspect-ratio: 1 / 1) {
  html.js .dp-kaart { height: 400px; }
}

/* --------------------------------------------------------------------------
   2. Gestapelde lagen die op scroll uit elkaar schuiven
   -------------------------------------------------------------------------- */

/* Deze demo zit bewust NIET in .doek: dat paneel heeft overflow:hidden en
   daarmee werkt position:sticky er niet, want een clippende voorouder wordt
   zelf de scrollcontainer. Vandaar een eigen omhulsel. */
.dp-baan {
  position: relative;
  /* De hoogte is de speelruimte waarover de lagen uit elkaar gaan. */
  min-height: 210vh;
  --dp-uit: 1;
}

.dp-baan-plek {
  position: sticky;
  top: clamp(1rem, 8vh, 4rem);
  display: grid;
  gap: 1rem;
  justify-items: center;
}

.dp-toneel {
  position: relative;
  width: 100%;
  display: grid;
  place-items: center;
  padding: clamp(1rem, 4vw, 2rem);
  border: 1px solid var(--lijn);
  border-radius: 16px;
  background: var(--grond-op);
  /* Vangt de lagen op die naar buiten waaieren, zodat de pagina zelf nooit
     horizontaal gaat schuiven. */
  overflow: hidden;
  min-height: clamp(300px, 56vh, 480px);
  perspective: 1100px;
  perspective-origin: 50% 45%;
}

.dp-stapel {
  position: relative;
  width: clamp(150px, 40vw, 230px);
  aspect-ratio: 1 / 1;
  transform-style: preserve-3d;
}

.dp-laag {
  position: absolute;
  inset: 0;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--lijn);
  box-shadow: 0 18px 40px rgba(0, 0, 0, .45);
  /* Eén formule voor alle vier de lagen; alleen de drie variabelen
     verschillen. Bij --dp-uit:0 liggen ze precies op elkaar. */
  transform: translate3d(
    calc(var(--dp-lx, 0%) * var(--dp-uit, 1)),
    calc(var(--dp-ly, 0%) * var(--dp-uit, 1)),
    calc(var(--dp-lz, 0px) * var(--dp-uit, 1))
  );
  /* Hier mag will-change wel: op deze lagen staat alleen een foto en geen
     letter, dus een bevroren rasterschaal kost niets aan leesbaarheid. */
  will-change: transform;
}

.dp-laag img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.dp-laag-1 { --dp-lx: -42%; --dp-ly: -30%; --dp-lz: -140px; }
.dp-laag-2 { --dp-lx: -16%; --dp-ly:  -8%; --dp-lz:  -50px; }
.dp-laag-3 { --dp-lx:  16%; --dp-ly:   8%; --dp-lz:   40px; }
.dp-laag-4 { --dp-lx:  42%; --dp-ly:  28%; --dp-lz:  120px; }

/* Het bijschrift staat expres BUITEN de 3D-stapel. Tekst die meedraait of
   mee naar voren komt wordt door de browser opnieuw geschaald en dus vaag;
   op z=0 blijft hij haarscherp. */
.dp-toneel-bijschrift {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  margin: 0;
  padding: .7rem 1rem;
  text-align: center;
  font-family: var(--kop);
  font-size: 1.05rem;
  color: var(--deeg);
  background: rgba(13, 11, 9, .82);
}

.dp-baan-voet {
  display: grid;
  gap: .6rem;
  justify-items: center;
  width: 100%;
}

.dp-meter {
  width: min(280px, 100%);
  height: 3px;
  border-radius: 999px;
  background: var(--lijn);
  overflow: hidden;
}

/* De balk leest dezelfde variabele als de lagen, dus er is geen tweede
   stukje JavaScript nodig om hem bij te houden. */
.dp-meter-vul {
  display: block;
  height: 100%;
  background: var(--goud);
  transform: scaleX(var(--dp-uit, 1));
  transform-origin: left center;
}

/* --------------------------------------------------------------------------
   3. Coverflow
   -------------------------------------------------------------------------- */

.dp-flow {
  display: grid;
  gap: 1rem;
  width: 100%;
  --dp-flow-b: clamp(140px, 44vw, 230px);
  --dp-flow-hoek: 46deg;
  --dp-flow-diep: 110px;
}

.dp-flow-baan {
  overflow-x: auto;
  overflow-y: hidden;
  /* Doorlopen tot de rand van het paneel, precies de 2rem binnenmarge van
     .doek terug. Blijft de rij binnen die marge, dan zie je op een telefoon
     alleen de middelste foto en is er van de waaier niets te merken. Het
     paneel knipt zelf af, dus de pagina kan hier niet van gaan schuiven,
     ook niet als die marge ooit anders wordt. */
  margin-inline: -2rem;
  scroll-snap-type: x mandatory;
  /* Voorkomt dat een veegbeweging aan het einde van de rij de browser laat
     terugnavigeren. Op een telefoon is dat anders zo gebeurd. */
  overscroll-behavior-x: contain;
  /* Lucht voor de schaduwen en voor de focusring, die anders tegen de rand
     van de scroller wordt afgeknipt. */
  padding-block: 1.5rem;
  scrollbar-width: thin;
  scrollbar-color: var(--goud) transparent;
}

/* Deze ene ring ligt binnenlangs in plaats van eromheen. De rij loopt tot de
   rand van het paneel, en dat paneel knipt af, dus een ring aan de buitenkant
   zou links en rechts wegvallen. Een ring die je half ziet is geen ring. */
.dp-flow-baan:focus-visible { outline-offset: -4px; }

@supports not selector(:focus-visible) {
  .dp-flow-baan:focus { outline-offset: -4px; }
}

.dp-flow-baan::-webkit-scrollbar { height: 8px; }
.dp-flow-baan::-webkit-scrollbar-track { background: transparent; }
.dp-flow-baan::-webkit-scrollbar-thumb {
  background: var(--goud);
  border-radius: 999px;
}

.dp-flow-rij {
  display: flex;
  gap: clamp(.6rem, 2vw, 1.1rem);
  margin: 0;
  list-style: none;
  /* Halve schermbreedte lucht aan weerskanten, anders kunnen de eerste en de
     laatste foto het midden nooit bereiken. De procenten rekenen tegen de
     scroller, dus dit klopt op elke schermmaat. */
  padding: 0 calc(50% - var(--dp-flow-b) / 2);
  /* Zonder max-content houdt de rij de breedte van de scroller aan, puilen de
     foto's er rechts gewoon uit en telt de rechterpadding niet mee in de
     schuifbare lengte. De laatste foto komt dan een halve schermbreedte
     tekort. Dit regeltje is de eigenlijke oplossing, niet de padding. */
  width: max-content;
}

/* Elk item krijgt zijn EIGEN perspectief. Perspectief op de scroller zelf
   laat het verdwijnpunt met de scrollpositie meelopen in Chrome, waardoor
   de rij scheef gaat staan. Per item is dat probleem er niet. */
.dp-flow-item {
  flex: 0 0 var(--dp-flow-b);
  perspective: 900px;
  scroll-snap-align: center;
  list-style: none;
}

.dp-flow-knop {
  display: grid;
  width: 100%;
  padding: 0;
  cursor: pointer;
  border: 1px solid var(--lijn);
  border-radius: 14px;
  overflow: hidden;
  background: var(--grond-op);
  box-shadow: 0 16px 34px rgba(0, 0, 0, .45);
  /* Geen transition: de stand komt per frame uit de scrollpositie. Een
     overgang zou daar juist een naijlend, plakkerig gevoel van maken. */
  transform: translateZ(calc(var(--dp-a, 0) * var(--dp-flow-diep) * -1))
             rotateY(calc(var(--dp-d, 0) * var(--dp-flow-hoek)));
}

.dp-flow-knop[aria-current="true"] {
  border-color: var(--goud);
  box-shadow: 0 0 0 1px var(--goud), 0 22px 44px rgba(0, 0, 0, .55);
}

.dp-flow-beeld {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
}

.dp-flow-beeld img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* De sluier ligt alleen over de foto en niet over het bijschrift. Zou ik de
   hele knop dimmen, dan zakt ook het contrast van de tekst onder de norm. */
.dp-flow-beeld::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(13, 11, 9, calc(var(--dp-a, 0) * .55));
  pointer-events: none;
}

.dp-flow-naam {
  display: block;
  padding: .7rem .9rem;
  text-align: center;
  font-family: var(--tekst);
  font-size: .9rem;
  color: var(--deeg);
  background: var(--grond-op);
}

@supports not (aspect-ratio: 1 / 1) {
  .dp-flow-beeld { height: 165px; }
  .dp-stapel { height: clamp(150px, 40vw, 230px); }
}

/* --------------------------------------------------------------------------
   4. Paneel dat openvouwt als een boek
   -------------------------------------------------------------------------- */

.dp-vouw {
  display: grid;
  gap: 1.25rem;
  justify-items: center;
  width: 100%;
}

.dp-boek-scene {
  width: min(420px, 100%);
  perspective: 1400px;
}

/* Twee rijen van precies 1fr. Daardoor zijn de bovenste en de onderste helft
   gegarandeerd even hoog en valt de klep na een halve slag exact op de
   onderste helft. Met aspect-ratio of automatische hoogte klopt dat net niet. */
.dp-boek {
  display: grid;
  grid-template-rows: 1fr 1fr;
  gap: 0;
  height: 360px;
  transform-style: preserve-3d;
  transform: translateY(var(--dp-boek-y, 0px));
  transition: transform .8s cubic-bezier(.25, .8, .3, 1);
}

@media (min-width: 640px) {
  .dp-boek { height: 440px; }
}

.dp-boek-boven {
  position: relative;
  /* De gedeelde rand: de onderrand van de bovenste helft is tegelijk de
     bovenrand van de onderste helft. Daar scharniert het om. */
  transform-origin: center bottom;
  transform-style: preserve-3d;
  transition: transform .8s cubic-bezier(.25, .8, .3, 1);
}

.dp-boekvlak {
  position: absolute;
  inset: 0;
  display: grid;
  border-radius: 14px 14px 0 0;
  overflow: hidden;
  border: 1px solid var(--lijn);
  background: var(--grond-op);
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}

/* Die ene pixel voorkomt dat de kaft en de onderste helft in exact hetzelfde
   vlak liggen. Zonder die pixel gaan ze flikkeren zodra je scrolt. */
.dp-boek-voor {
  transform: translateZ(1px);
  align-content: center;
  gap: .5rem;
  padding: 1.1rem;
}

/* Twee halve slagen om een X-as achter elkaar (deze en die van de klep) zijn
   samen een zuivere verschuiving. De kaft komt dus rechtop en niet
   spiegelbeeldig op de onderste helft terecht, en de tekst blijft leesbaar. */
.dp-boek-kaft {
  transform: rotateX(180deg) translateZ(1px);
  border-radius: 0 0 14px 14px;
}

.dp-boek-kaft img {
  grid-area: 1 / 1;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.dp-boek-band {
  grid-area: 1 / 1;
  align-self: end;
  display: grid;
  gap: .2rem;
  padding: .85rem 1rem;
  background: rgba(13, 11, 9, .82);
}

.dp-boek-onder {
  display: grid;
  align-content: center;
  gap: .5rem;
  padding: 1.1rem;
  border: 1px solid var(--lijn);
  border-top: 0;
  border-radius: 0 0 14px 14px;
  background: var(--grond-op);
  overflow: auto;
}

.dp-boek-kop {
  margin: 0;
  font-family: var(--kop);
  font-size: 1.15rem;
  line-height: 1.2;
  color: var(--deeg);
}

.dp-boek-regel {
  margin: 0;
  font-family: var(--tekst);
  font-size: .9rem;
  line-height: 1.55;
  color: var(--deeg-zacht);
}

/* Dicht is een toevoeging bovenop de open basis, niet andersom. Zonder
   JavaScript staat het paneel dus open en is alle tekst gewoon te lezen. */
html.js .dp-boek[data-dp-open="false"] .dp-boek-boven {
  transform: rotateX(180deg);
}

/* Als de klep dichtligt ligt de kaft in de ONDERSTE helft en is de bovenste
   helft leeg. Het geheel een kwart hoogte OMHOOG schuiven zet het gesloten
   paneel weer midden in zijn vak. Omlaag zou het buiten het vak duwen, want
   een transform verplaatst wel het beeld maar niet de ruimte die het inneemt,
   en dan komt de kaft over de knop eronder te liggen.
   Een vast pixelgetal en geen percentage: een percentage van een oneven
   hoogte geeft halve pixels en dus vage letters op de kaft. */
html.js .dp-boek[data-dp-open="false"] { --dp-boek-y: -90px; }

@media (min-width: 640px) {
  html.js .dp-boek[data-dp-open="false"] { --dp-boek-y: -110px; }
}

/* --------------------------------------------------------------------------
   5. Prisma met foto's
   -------------------------------------------------------------------------- */

.dp-prisma-veld {
  display: grid;
  gap: 1.25rem;
  justify-items: center;
  width: 100%;
  --dp-vlakbreed: clamp(140px, 38vw, 220px);
  --dp-vlakhalf: calc(var(--dp-vlakbreed) / 2);
}

.dp-prisma-scene {
  width: 100%;
  display: grid;
  place-items: center;
  /* Op zijn breedst meet het prisma ongeveer 1,45 keer een zijde. Die ruimte
     reserveren we, anders springt de omgeving mee bij het draaien. */
  min-height: calc(var(--dp-vlakbreed) * 1.2);
  perspective: 1400px;
}

/* Twee lagen: de motor draait automatisch, het lijf houdt de handmatige
   stand vast. Zo vechten de animatie en de knoppen niet om dezelfde
   transform-eigenschap. */
.dp-prisma-motor {
  transform-style: preserve-3d;
  animation: dp-draai 24s linear infinite;
}

.dp-prisma-lijf {
  position: relative;
  width: var(--dp-vlakbreed);
  height: var(--dp-vlakbreed);
  transform-style: preserve-3d;
  transform: rotateY(var(--dp-hoek, 0deg));
  transition: transform .6s cubic-bezier(.2, .75, .25, 1);
}

.dp-zij {
  position: absolute;
  inset: 0;
  margin: 0;
  display: grid;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--lijn);
  background: var(--grond-op);
  /* Bij een vierzijdig prisma is de afstand van het hart tot een vlak precies
     de helft van de breedte; dan sluiten de randen naadloos aan. */
  transform: rotateY(var(--dp-draaihoek, 0deg)) translateZ(var(--dp-vlakhalf));
  /* Anders zie je de twee achterste vlakken spiegelbeeldig door de voorste
     heen schemeren. */
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}

.dp-zij-1 { --dp-draaihoek: 0deg; }
.dp-zij-2 { --dp-draaihoek: 90deg; }
.dp-zij-3 { --dp-draaihoek: 180deg; }
.dp-zij-4 { --dp-draaihoek: 270deg; }

.dp-zij img {
  grid-area: 1 / 1;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.dp-zij figcaption {
  grid-area: 1 / 1;
  align-self: end;
  padding: .5rem .7rem;
  text-align: center;
  font-family: var(--tekst);
  font-size: .8rem;
  color: var(--deeg);
  background: rgba(13, 11, 9, .82);
}

@keyframes dp-draai {
  from { transform: rotateY(0deg); }
  to   { transform: rotateY(360deg); }
}

/* Op een muisapparaat is stilstaan bij hover vanzelfsprekend. Op een
   aanraakscherm bestaat hover niet, en daar doet de pauzeknop het werk. */
@media (hover: hover) and (pointer: fine) {
  .dp-prisma-veld:hover .dp-prisma-motor { animation-play-state: paused; }
}

/* Bewust GEEN pauze op :focus-within. De enige elementen die hier focus
   kunnen krijgen zijn de knoppen eronder, dus dan zou de knop "Draaien
   hervatten" zichzelf meteen weer stilzetten en leek hij kapot. Wie met het
   toetsenbord werkt heeft de pauzeknop, en dat is precies het middel dat de
   toegankelijkheidsrichtlijn voor bewegende inhoud vraagt. */
.dp-prisma-veld[data-dp-pauze="true"] .dp-prisma-motor { animation-play-state: paused; }

/* Zodra iemand zelf stapt nemen de knoppen het over. De animatie gaat er
   dan helemaal af, zodat de vlakken recht voor je uit komen te staan. */
.dp-prisma-veld[data-dp-hand="true"] .dp-prisma-motor { animation: none; }

/* --------------------------------------------------------------------------
   Terugval voor browsers zonder echte 3D
   -------------------------------------------------------------------------- */

/* Zonder preserve-3d klapt alles plat en zou je stapels onleesbare vlakken
   over elkaar krijgen. Dan liever een eerlijke platte weergave. */
@supports not (transform-style: preserve-3d) {
  html.js .dp-kaart { aspect-ratio: auto; height: auto; perspective: none; }
  html.js .dp-kaart-spil { display: grid; height: auto; transform: none; }
  html.js .dp-vlak { position: relative; inset: auto; min-height: 220px; transform: none; }
  /* Beide kanten staan hier onder elkaar in beeld. Er valt dus niets om te
     draaien: de knop gaat weg en de halve slag blijft eruit, anders zou de
     hele stapel plat spiegelen en stond alle tekst achterstevoren. */
  html.js .dp-kaart-knop,
  html.js .dp-kaart-uitleg { display: none; }
  html.js .dp-kaart[data-dp-om="true"] .dp-kaart-spil { transform: none; }

  .dp-laag { transform: none; position: relative; margin-bottom: .5rem; }
  .dp-stapel { display: grid; gap: .5rem; height: auto; aspect-ratio: auto; }
  .dp-baan { min-height: 0; }
  .dp-baan-plek { position: static; }
  .dp-toneel-bijschrift { position: static; }

  .dp-flow-knop { transform: none; }
  .dp-flow-beeld::after { background: none; }

  html.js .dp-boek { height: auto; grid-template-rows: auto auto; transform: none; }
  html.js .dp-boek-boven { transform: none !important; height: 200px; }
  .dp-boek-kaft { display: none; }

  .dp-prisma-lijf { display: grid; grid-template-columns: repeat(2, 1fr); gap: .5rem; width: 100%; height: auto; transform: none; }
  .dp-zij { position: relative; inset: auto; transform: none; aspect-ratio: 1 / 1; }
  .dp-prisma-motor { animation: none; }
  .dp-prisma-scene { min-height: 0; perspective: none; }
}

/* --------------------------------------------------------------------------
   Minder beweging
   -------------------------------------------------------------------------- */

/* Alles staat stil. Belangrijker nog: niets blijft daardoor onzichtbaar of
   halverwege hangen. De lagen staan uit elkaar, het boek gaat open en het
   prisma toont gewoon een vlak dat je met de knoppen verder kunt draaien. */
@media (prefers-reduced-motion: reduce) {
  html.js .dp-kaart-spil { transition: none; }

  .dp-baan { min-height: 0; --dp-uit: 1; }
  .dp-baan-plek { position: static; }
  .dp-laag { will-change: auto; }

  .dp-flow { --dp-flow-hoek: 0deg; --dp-flow-diep: 0px; }
  .dp-flow-knop { transform: none; }
  .dp-flow-beeld::after { background: none; }
  .dp-flow-baan { scroll-behavior: auto; }

  .dp-boek,
  .dp-boek-boven { transition: none; }

  .dp-prisma-motor { animation: none; }
  .dp-prisma-lijf { transition: none; }
  /* De pauzeknop slaat nergens meer op als er niets draait. */
  .dp-prisma-pauze { display: none; }
}


/* ===== familie mi · Micro-interacties ===== */
/* =============================================================================
   mi  |  Micro-interacties
   Alles is voorvoegsel-gebonden (mi-) zodat de families elkaar niet overschrijven.
   lab.css levert al: --grond --grond-op --deeg --deeg-zacht --goud --goud-licht
   --lijn --kop --tekst --breed en de klassen .proef .uitleg .nr .kost .doek enz.
   Die worden hier bewust NIET opnieuw gedefinieerd.
   ============================================================================= */

:root{
  /* Alleen wat lab.css niet heeft. Elke waarde is nagerekend op contrast,
     niet op gevoel; de ratio's staan erbij zodat een latere wijziging
     zichtbaar maakt wat je stukmaakt. */
  --mi-uit:#9A9084;               /* uitgeschakelde tekst: 5,28:1 op het knopvlak */
  --mi-goed:#86D19A;              /* 10,37:1 op --grond-op */
  --mi-fout:#FFA294;              /* 9,70:1 op --grond-op */
  --mi-druk:#B08D1E;              /* ingedrukte knop, donkere tekst haalt 6,24:1 */
  --mi-toastvlak:#1D1815;         /* eigen vlak, zodat de toastkleuren berekenbaar blijven */
  --mi-spoor:#2A2420;             /* uit-stand van de schakelaar */

  /* Randen van bedienbare onderdelen moeten 3:1 halen (WCAG 1.4.11).
     --lijn (.14) haalt dat niet en is daarom alleen voor decoratieve scheidingen. */
  --mi-rand:rgba(243,235,221,.45);  /* 4,00:1 op --grond en op --grond-op */

  /* Twee snelheden, meer heb je niet nodig. Kort voor reactie op een klik,
     iets langer voor iets dat van formaat verandert. */
  --mi-vlot:.2s cubic-bezier(.2,.7,.3,1);
  --mi-traag:.34s cubic-bezier(.2,.7,.3,1);
  --mi-rond:12px;
}

/* Eigen laag binnen .doek. Geen eigen achtergrond: .doek is al donker en beide
   mogelijke ondergronden (--grond en --grond-op) zijn doorgerekend. */
.mi-vlak{
  inline-size:100%;
  max-inline-size:52rem;
  margin-inline:auto;
  box-sizing:border-box;
}

/* Tekst die alleen voor schermlezers is. Niet display:none, want dan
   verdwijnt hij ook uit de voorleesvolgorde. */
.mi-vh{
  position:absolute;
  inline-size:1px;block-size:1px;
  padding:0;margin:-1px;
  overflow:hidden;
  clip-path:inset(50%);
  white-space:nowrap;
  border:0;
}

/* Eén focusring voor de hele familie. Losse ringen per onderdeel lopen uit de
   pas zodra iemand er één aanpast. */
.mi-knop:focus-visible,
.mi-schakel__vak:focus-visible,
.mi-veld__in:focus-visible,
.mi-vouw__kop:focus-visible,
.mi-toast__sluit:focus-visible{
  outline:2px solid var(--goud-licht);
  outline-offset:3px;
}
/* Terugval voor browsers zonder :focus-visible: liever een ring te veel dan geen. */
@supports not selector(:focus-visible){
  .mi-knop:focus,
  .mi-schakel__vak:focus,
  .mi-veld__in:focus,
  .mi-vouw__kop:focus,
  .mi-toast__sluit:focus{
    outline:2px solid var(--goud-licht);
    outline-offset:3px;
  }
}

/* -----------------------------------------------------------------------------
   1. Knop met alle toestanden
   -------------------------------------------------------------------------- */

.mi-knop{
  display:inline-flex;align-items:center;justify-content:center;gap:.5rem;
  box-sizing:border-box;
  /* 2,9rem is bij standaard lettergrootte 46px: ruim boven het minimum voor
     een duim, en het schaalt mee als iemand groter zet. */
  min-block-size:2.9rem;
  padding:.7rem 1.15rem;
  font:600 .95rem/1.2 var(--tekst);
  color:var(--grond);
  background:var(--goud);
  border:1px solid var(--goud);
  border-radius:999px;
  cursor:pointer;
  text-align:center;
  -webkit-tap-highlight-color:transparent;
  transition:background var(--mi-vlot),border-color var(--mi-vlot),
             color var(--mi-vlot),transform var(--mi-vlot),box-shadow var(--mi-vlot);
}

/* Hover alleen waar er echt een muis is. Op aanraakschermen blijft de
   hover-stand anders plakken na een tik. */
@media (hover:hover) and (pointer:fine){
  .mi-knop:hover:not(:disabled):not([data-mi-stand="bezig"]){
    background:var(--goud-licht);
    border-color:var(--goud-licht);
    box-shadow:0 6px 18px rgba(232,206,115,.16);
  }
}
/* Losse klasse zodat de toestand ook stil naast de andere te tonen is. */
.mi-knop--hover{
  background:var(--goud-licht);
  border-color:var(--goud-licht);
  box-shadow:0 6px 18px rgba(232,206,115,.16);
}

.mi-knop:active:not(:disabled):not([data-mi-stand="bezig"]),
.mi-knop--druk{
  background:var(--mi-druk);
  border-color:var(--mi-druk);
  transform:translateY(1px);
  box-shadow:inset 0 2px 6px rgba(0,0,0,.4);
}

/* Statische focus-toestand voor de etalage. Zelfde ring als de echte. */
.mi-knop--focus{
  outline:2px solid var(--goud-licht);
  outline-offset:3px;
}

.mi-knop--bezig,
.mi-knop[data-mi-stand="bezig"]{
  background:transparent;
  color:var(--deeg);              /* 15,86:1 op het paneel */
  border-color:var(--mi-rand);
  cursor:progress;
}

.mi-knop--gelukt,
.mi-knop[data-mi-stand="gelukt"]{
  background:transparent;
  color:var(--mi-goed);           /* 10,37:1 */
  border-color:rgba(134,209,154,.55);
}

/* Uitgeschakeld mag gedempt lijken maar moet leesbaar blijven: 5,28:1.
   De veelgebruikte opacity:.4 op goud zakt naar ongeveer 2:1 en is dan
   voor slechtziende bezoekers gokwerk. */
.mi-knop:disabled,
.mi-knop--uit{
  background:rgba(243,235,221,.06);
  color:var(--mi-uit);
  border-color:rgba(243,235,221,.18);
  cursor:not-allowed;
  transform:none;
  box-shadow:none;
}

/* De zes etalage-exemplaren zijn spans met aria-hidden: ze tonen een toestand
   en doen niets. Zonder deze regel erven ze cursor:pointer van .mi-knop en
   staan ze naast de echte knop met precies dezelfde uitnodiging om te klikken.
   Attribuutselector (0,2,0) wint van .mi-knop--bezig en .mi-knop--gelukt, dus
   ook die twee wijzen niet meer naar progress of pointer. De echte
   uitgeschakelde knop is een <button> zonder aria-hidden en houdt not-allowed. */
.mi-knop[aria-hidden="true"]{cursor:default}

/* Tweede knopsoort: rand in plaats van vlak. */
.mi-knop--rand{
  background:transparent;
  color:var(--deeg);
  border-color:var(--mi-rand);
}
.mi-knop.mi-knop--rand:active{
  background:rgba(243,235,221,.12);
  border-color:var(--goud-licht);
  transform:translateY(1px);
  box-shadow:none;
}
@media (hover:hover) and (pointer:fine){
  .mi-knop.mi-knop--rand:hover:not(:disabled){
    background:rgba(243,235,221,.08);
    border-color:var(--goud-licht);
    color:var(--deeg);
    box-shadow:none;
  }
}

/* Draaiend ringetje. currentColor, dus hij kleurt automatisch mee met de
   toestand waarin de knop staat. */
.mi-ring{
  inline-size:1.05rem;block-size:1.05rem;
  flex:0 0 auto;
  border:2px solid currentColor;
  border-block-start-color:transparent;
  border-radius:50%;
  animation:mi-draai .85s linear infinite;
}
@keyframes mi-draai{to{transform:rotate(360deg)}}

/* Het vinkje tekent zichzelf. Eindstand is dashoffset 0, dus als de animatie
   uitstaat staat het vinkje er gewoon: nooit een half getekend streepje. */
.mi-vink{inline-size:1.05rem;block-size:1.05rem;flex:0 0 auto}
.mi-vink path{
  fill:none;stroke:currentColor;stroke-width:2.6;
  stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:26;stroke-dashoffset:0;
  animation:mi-tik .4s ease-out;
}
@keyframes mi-tik{from{stroke-dashoffset:26}}

.mi-kruis{inline-size:1.05rem;block-size:1.05rem;flex:0 0 auto}
.mi-kruis path{
  fill:none;stroke:currentColor;stroke-width:2.6;
  stroke-linecap:round;
}

/* De etalage mag de volle breedte van het paneel pakken, anders passen de
   zeven toestanden niet naast elkaar en verlies je juist het vergelijk. */
.mi-knopvlak{max-inline-size:none}
/* min() in de minmax voorkomt dat de kolom breder wordt dan het scherm.
   Zonder die guard schuift de pagina op 360px horizontaal weg. */
.mi-toestanden{
  display:grid;
  /* 7rem is de grootste maat waarbij er op 360px nog twee kolommen naast
     elkaar passen (.doek houdt daar 248px over). Breder en de zeven
     toestanden vallen onder elkaar en is het vergelijk weg. */
  grid-template-columns:repeat(auto-fit,minmax(min(7rem,100%),1fr));
  gap:1rem .9rem;
  list-style:none;
  margin:0;padding:0;
}
.mi-toestand{display:flex;flex-direction:column;align-items:stretch;gap:.5rem;min-inline-size:0}
.mi-toestand__naam{
  font:600 .72rem/1.3 var(--tekst);
  letter-spacing:.12em;text-transform:uppercase;
  color:var(--deeg-zacht);        /* 10,33:1 */
}
.mi-toestanden .mi-knop{inline-size:100%;padding-inline:.8rem;font-size:.9rem}

.mi-knopvlak__proef{
  display:flex;flex-wrap:wrap;align-items:center;gap:.9rem;
  margin-block-start:1.6rem;padding-block-start:1.4rem;
  border-block-start:1px solid var(--lijn);
}
.mi-knopvlak__proef .mi-toestand__naam{flex:1 0 100%}
.mi-knopvlak__mel{
  margin:0;font:400 .88rem/1.5 var(--tekst);color:var(--deeg-zacht);
  min-block-size:1.35rem;          /* gereserveerd, anders springt de regel eronder */
}

/* -----------------------------------------------------------------------------
   2. Schakelaar op een echte checkbox
   -------------------------------------------------------------------------- */

.mi-schakels{display:grid;gap:.75rem;margin:0;padding:0;list-style:none}

.mi-schakel{
  display:flex;align-items:center;gap:.9rem;
  box-sizing:border-box;
  padding:.75rem .95rem;
  background:var(--grond);
  border:1px solid var(--lijn);
  border-radius:var(--mi-rond);
  cursor:pointer;
  font:400 1rem/1.4 var(--tekst);
  color:var(--deeg);              /* 16,59:1 */
}

/* De input zelf is het zichtbare spoor. Zo landt de focusring op het element
   dat de toetsaanslag ook echt verwerkt, en niet op een nagebouwd blokje. */
.mi-schakel__vak{
  -webkit-appearance:none;appearance:none;
  box-sizing:border-box;
  flex:0 0 auto;
  position:relative;
  inline-size:3.25rem;block-size:1.85rem;
  margin:0;
  background:var(--mi-spoor);
  border:1px solid var(--mi-rand);   /* 4,00:1, dus de rand is echt te zien */
  border-radius:999px;
  cursor:pointer;
  transition:background var(--mi-vlot),border-color var(--mi-vlot);
}
.mi-schakel__vak::after{
  content:"";
  position:absolute;
  inset-block-start:50%;inset-inline-start:2px;
  inline-size:1.25rem;block-size:1.25rem;
  border-radius:50%;
  background:var(--deeg-zacht);      /* 8,42:1 op het spoor */
  transform:translateY(-50%);
  transition:transform var(--mi-vlot),background var(--mi-vlot);
}
.mi-schakel__vak:checked{background:var(--goud);border-color:var(--goud-licht)}
.mi-schakel__vak:checked::after{
  background:var(--grond);
  /* 3,25rem spoor - 1,25rem knop - 6px (2x rand + 2x speling) */
  transform:translate(calc(3.25rem - 1.25rem - 6px),-50%);
}
/* Als appearance:none niet zou werken, laat de browser dan zijn eigen
   vinkvakje tekenen in plaats van een leeg gat. */
@supports not (appearance:none){
  .mi-schakel__vak{
    inline-size:auto;block-size:auto;
    background:none;border:0;
    -webkit-appearance:checkbox;appearance:checkbox;
  }
  .mi-schakel__vak::after{display:none}
}

.mi-schakel__naam{flex:1 1 auto;min-inline-size:0}
/* De stand staat ook in woorden. Kleur en positie alleen is te weinig:
   dat leest niemand die het verschil tussen grijs en goud niet ziet. */
.mi-schakel__stand{
  flex:0 0 auto;
  font:600 .78rem/1.3 var(--tekst);
  letter-spacing:.1em;text-transform:uppercase;
  color:var(--deeg-zacht);
}
.mi-schakel__vak:checked ~ .mi-schakel__stand{color:var(--goud-licht)}
.mi-schakel__uit{display:inline}
.mi-schakel__aan{display:none}
.mi-schakel__vak:checked ~ .mi-schakel__stand .mi-schakel__uit{display:none}
.mi-schakel__vak:checked ~ .mi-schakel__stand .mi-schakel__aan{display:inline}

.mi-schakel__hulp{
  margin:.15rem 0 0;
  font:400 .88rem/1.55 var(--tekst);
  color:var(--deeg-zacht);
}

/* -----------------------------------------------------------------------------
   3. Skelet dat overgaat in inhoud
   -------------------------------------------------------------------------- */

.mi-laad{display:grid;gap:1.1rem;justify-items:center}
.mi-laad__kop{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:.9rem;inline-size:100%;
}
.mi-laad__mel{margin:0;font:400 .88rem/1.5 var(--tekst);color:var(--deeg-zacht)}

/* Skelet en kaart wisselen elkaar niet af, ze liggen op elkaar. De hoogte
   komt dus altijd van de echte kaart, en het omschakelen kan per definitie
   geen sprong geven, op welke schermbreedte dan ook. Bones die je met de
   hand op maat probeert te brengen kloppen altijd maar bij één breedte. */
.mi-laad__kaart{position:relative;inline-size:100%;max-inline-size:20rem}
.mi-laad__skelet,
.mi-laad__echt{
  box-sizing:border-box;
  display:grid;gap:.7rem;justify-items:start;align-content:start;
  padding:1rem;
  background:var(--grond);
  border:1px solid var(--lijn);
  border-radius:var(--mi-rond);
}

/* Zonder JS is er niets om op te wachten: dan meteen de echte kaart. */
.mi-laad__skelet{display:none}
html.js .mi-laad[data-mi-stand="laden"] .mi-laad__skelet{
  display:grid;
  position:absolute;inset:0;
}
/* visibility en niet display: zo houdt de doos zijn hoogte vast, en slaan
   schermlezers de halve kaart over zolang aria-busy aan staat. */
html.js .mi-laad[data-mi-stand="laden"] .mi-laad__echt{visibility:hidden}

.mi-bot{
  background:rgba(243,235,221,.10);
  border-radius:8px;
  position:relative;overflow:hidden;
}
/* De glans loopt over het bot heen in plaats van dat het bot knippert.
   Knipperen trekt de aandacht naar iets wat nog niets betekent. */
.mi-bot::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,transparent,rgba(243,235,221,.12),transparent);
  transform:translateX(-100%);
  animation:mi-glans 1.5s ease-in-out infinite;
}
@keyframes mi-glans{to{transform:translateX(100%)}}
/* De maten volgen de regelhoogtes van de echte kaart, zodat het skelet
   dezelfde ritmiek heeft en niet als los blokkenpatroon oogt. */
.mi-bot--beeld{inline-size:100%;aspect-ratio:1;border-radius:10px}
.mi-bot--titel{block-size:1.5rem;inline-size:70%}
.mi-bot--regel{block-size:.9rem;inline-size:100%}
.mi-bot--kort{inline-size:55%}
.mi-bot--meta{block-size:.75rem;inline-size:40%}

.mi-laad__foto{
  inline-size:100%;block-size:auto;aspect-ratio:1;
  object-fit:cover;border-radius:10px;display:block;
}
.mi-laad__titel{margin:0;font:400 1.15rem/1.3 var(--kop);color:var(--deeg)}
.mi-laad__tekst{margin:0;font:400 .92rem/1.55 var(--tekst);color:var(--deeg-zacht)}
.mi-laad__meta{margin:0;font:400 .8rem/1.4 var(--tekst);color:var(--mi-uit)}
/* Invaden in plaats van een beginstand-klasse: als de animatie uitstaat,
   blijft de kaart gewoon staan waar hij hoort. Aan de klaar-stand gehangen,
   zodat hij bij Opnieuw laden opnieuw speelt. */
html.js .mi-laad[data-mi-stand="klaar"] .mi-laad__echt{animation:mi-inbeeld .28s ease-out}
@keyframes mi-inbeeld{from{opacity:0;transform:translateY(6px)}}

/* -----------------------------------------------------------------------------
   4. Toast
   -------------------------------------------------------------------------- */

/* Eigen wrapper, geen .doek-kind met position:sticky: .doek staat op
   overflow:hidden en dan werkt sticky sowieso niet. Absoluut mag wel. */
.mi-toastvlak{
  position:relative;
  inline-size:100%;max-inline-size:52rem;margin-inline:auto;
  /* Hoog genoeg dat de volle stapel de knoppen erboven niet bedekt en niet
     buiten .doek valt, ook op 360px waar de knoppen op twee regels staan en
     de meldingen hoger zijn. Nagemeten, niet geschat. */
  min-block-size:27rem;
  box-sizing:border-box;
}
@media (min-width:40rem){
  .mi-toastvlak{min-block-size:20rem}
}
.mi-toast__knoppen{display:flex;flex-wrap:wrap;gap:.7rem}

.mi-toast__lijst{
  position:absolute;
  inset-block-end:0;inset-inline:0;
  display:flex;flex-direction:column;gap:.6rem;
  margin:0;padding:0;list-style:none;
  pointer-events:none;              /* de lege ruimte ernaast blijft klikbaar */
}
@media (min-width:40rem){
  .mi-toast__lijst{inset-inline-start:auto;max-inline-size:23rem}
}
/* De vaste voorbeeldmelding bestaat voor het geval JavaScript uitblijft.
   Draait het wel, dan is hij overbodig en zou hij alleen maar een plek in de
   stapel innemen die een echte melding nodig heeft. */
html.js .mi-toast[data-mi-toastvast]{display:none}

.mi-toast{
  pointer-events:auto;
  position:relative;overflow:hidden;
  display:flex;align-items:flex-start;gap:.7rem;
  box-sizing:border-box;
  padding:.85rem .9rem;
  background:var(--mi-toastvlak);   /* eigen vlak zodat de kleuren narekenbaar zijn */
  border:1px solid var(--lijn);
  border-inline-start:3px solid var(--deeg-zacht);
  border-radius:var(--mi-rond);
  box-shadow:0 10px 26px rgba(0,0,0,.45);
  animation:mi-toast-in .26s cubic-bezier(.2,.7,.3,1);
}
@keyframes mi-toast-in{from{opacity:0;transform:translateY(10px)}}
.mi-toast--goed{border-inline-start-color:var(--mi-goed)}
.mi-toast--fout{border-inline-start-color:var(--mi-fout)}
.mi-toast__ico{flex:0 0 auto;display:flex;margin-block-start:.15rem}
.mi-toast--goed .mi-toast__ico{color:var(--mi-goed)}   /* 9,72:1 op het toastvlak */
.mi-toast--fout .mi-toast__ico{color:var(--mi-fout)}   /* 9,09:1 */
.mi-toast__body{flex:1 1 auto;min-inline-size:0;display:grid;gap:.15rem}
.mi-toast__titel{font:600 .92rem/1.35 var(--tekst);color:var(--deeg)}
.mi-toast__regel{font:400 .88rem/1.5 var(--tekst);color:var(--deeg-zacht);overflow-wrap:anywhere}
.mi-toast__sluit{
  flex:0 0 auto;
  display:inline-flex;align-items:center;justify-content:center;
  inline-size:2.4rem;block-size:2.4rem;
  margin:-.35rem -.25rem 0 0;
  color:var(--deeg-zacht);
  background:transparent;border:0;border-radius:8px;
  cursor:pointer;
  transition:background var(--mi-vlot),color var(--mi-vlot);
}
@media (hover:hover) and (pointer:fine){
  .mi-toast__sluit:hover{background:rgba(243,235,221,.1);color:var(--deeg)}
}
/* De balk is de klok. Hij pauzeert bij hover en bij focus binnen de melding,
   zodat lezen of het sluitknopje pakken niet tegen een afteller vecht. */
.mi-toast__balk{
  position:absolute;inset-block-end:0;inset-inline-start:0;
  block-size:2px;inline-size:100%;
  background:var(--goud);
  transform-origin:left center;
  animation:mi-toast-krimp var(--mi-toast-duur,5s) linear forwards;
}
/* Pauzeren bij hover alleen waar een echte muis is. Op een aanraakscherm
   blijft :hover na een tik hangen en zou de melding nooit meer weggaan. */
@media (hover:hover) and (pointer:fine){
  .mi-toast:hover .mi-toast__balk{animation-play-state:paused}
}
.mi-toast:focus-within .mi-toast__balk{animation-play-state:paused}
@keyframes mi-toast-krimp{from{transform:scaleX(1)}to{transform:scaleX(0)}}

/* Wegvallen is een uitgaande animatie op een element dat daarna verdwijnt.
   Blijft de animatie uit, dan haalt JS de melding meteen weg; er blijft
   dus nooit een onzichtbaar blok ruimte innemen. */
.mi-toast--weg{animation:mi-toast-uit .2s ease-in forwards;pointer-events:none}
@keyframes mi-toast-uit{to{opacity:0;transform:translateY(8px)}}

/* -----------------------------------------------------------------------------
   5. Uitklapbaar blok
   -------------------------------------------------------------------------- */

.mi-vouw{display:grid;gap:.6rem}
.mi-vouw__blok{
  background:var(--grond);
  border:1px solid var(--lijn);
  border-radius:var(--mi-rond);
  overflow:hidden;
}
.mi-vouw__kop{
  display:flex;align-items:center;gap:.9rem;
  padding:.95rem 1.05rem;
  min-block-size:2.9rem;
  box-sizing:border-box;
  font:600 1rem/1.4 var(--tekst);
  color:var(--deeg);
  cursor:pointer;
  list-style:none;                  /* weg met het standaard driehoekje */
  transition:background var(--mi-vlot);
}
.mi-vouw__kop::-webkit-details-marker{display:none}
@media (hover:hover) and (pointer:fine){
  .mi-vouw__kop:hover{background:rgba(243,235,221,.06)}
}
.mi-vouw__pijl{
  flex:0 0 auto;margin-inline-start:auto;
  inline-size:.55rem;block-size:.55rem;
  border-inline-end:2px solid var(--goud);
  border-block-end:2px solid var(--goud);
  transform:rotate(45deg);
  transition:transform var(--mi-traag);
}
/* Tijdens het dichtklappen staat [open] er nog even op; zonder de :not()
   zou de pijl pas aan het eind terugdraaien en dat oogt als een hapering. */
.mi-vouw__blok[open]:not([data-mi-sluiten]) .mi-vouw__pijl{transform:rotate(-135deg)}

/* 0fr -> 1fr is de enige manier om naar "de hoogte die de inhoud nodig heeft"
   te animeren zonder die hoogte eerst in JS uit te rekenen. */
.mi-vouw__rek{
  display:grid;
  grid-template-rows:0fr;
  transition:grid-template-rows var(--mi-traag);
}
.mi-vouw__blok[open] .mi-vouw__rek{grid-template-rows:1fr}
.mi-vouw__blok[open][data-mi-sluiten] .mi-vouw__rek{grid-template-rows:0fr}
.mi-vouw__binnen{overflow:hidden;min-block-size:0}
.mi-vouw__tekst{
  margin:0;padding:0 1.05rem 1.05rem;
  font:400 .95rem/1.65 var(--tekst);
  color:var(--deeg-zacht);          /* 10,81:1 op --grond */
}
/* Zou een browser 0fr niet kunnen animeren, dan valt hij terug op direct
   open of dicht. Lelijker, maar nooit onleesbaar. */
@supports not (grid-template-rows:0fr){
  .mi-vouw__rek{display:block}
  .mi-vouw__binnen{overflow:visible}
}

/* -----------------------------------------------------------------------------
   6. Veld met inline controle
   -------------------------------------------------------------------------- */

.mi-veld{display:grid;gap:.45rem;max-inline-size:26rem}
.mi-veld__label{font:600 .9rem/1.4 var(--tekst);color:var(--deeg)}
.mi-veld__doos{position:relative;display:flex;align-items:center}
.mi-veld__in{
  inline-size:100%;box-sizing:border-box;
  min-block-size:2.9rem;
  padding:.7rem 3rem .7rem .95rem;   /* rechts ruimte voor het toestandsicoon */
  font:400 1rem/1.4 var(--tekst);
  color:var(--deeg);                 /* 16,59:1 */
  background:var(--grond);
  border:1px solid var(--mi-rand);
  border-radius:var(--mi-rond);
  transition:border-color var(--mi-vlot),box-shadow var(--mi-vlot);
}
.mi-veld__in::placeholder{color:var(--mi-uit);opacity:1}  /* 6,26:1, geen mist */
.mi-veld__ico{
  position:absolute;inset-inline-end:.85rem;
  display:flex;align-items:center;
  pointer-events:none;
}
/* Alle drie de iconen staan in de HTML en worden per toestand getoond.
   Scheelt innerHTML-geknutsel in JS en houdt de markup voorspelbaar. */
.mi-veld__bezig,.mi-veld__goed,.mi-veld__fout{display:none}
.mi-veld[data-mi-stand="bezig"] .mi-veld__bezig{display:block;color:var(--deeg-zacht)}
.mi-veld[data-mi-stand="goed"] .mi-veld__goed{display:block;color:var(--mi-goed)}
.mi-veld[data-mi-stand="fout"] .mi-veld__fout{display:block;color:var(--mi-fout)}
.mi-veld[data-mi-stand="goed"] .mi-veld__in{border-color:var(--mi-goed)}
.mi-veld[data-mi-stand="fout"] .mi-veld__in{border-color:var(--mi-fout)}

.mi-veld__hulp{margin:0;font:400 .85rem/1.55 var(--tekst);color:var(--deeg-zacht)}
/* Hoogte vooraf vastgezet: anders duwt de foutmelding bij het verschijnen
   alles eronder omlaag en mist iemand de knop die hij aanklikte. */
.mi-veld__mel{
  margin:0;min-block-size:1.4rem;
  font:600 .88rem/1.55 var(--tekst);
  color:var(--deeg-zacht);
}
.mi-veld[data-mi-stand="goed"] .mi-veld__mel{color:var(--mi-goed)}
.mi-veld[data-mi-stand="fout"] .mi-veld__mel{color:var(--mi-fout)}

/* -----------------------------------------------------------------------------
   7. Kopieerknop
   -------------------------------------------------------------------------- */

.mi-kopie{display:grid;gap:.7rem}
.mi-kopie__rij{
  display:flex;flex-wrap:wrap;align-items:center;gap:.8rem;
  padding:.85rem .95rem;
  box-sizing:border-box;
  background:var(--grond);
  border:1px solid var(--lijn);
  border-radius:var(--mi-rond);
}
.mi-kopie__tekst{flex:1 1 12rem;min-inline-size:0;display:grid;gap:.2rem}
.mi-kopie__label{
  font:600 .72rem/1.3 var(--tekst);
  letter-spacing:.12em;text-transform:uppercase;
  color:var(--deeg-zacht);
}
/* anywhere in plaats van een scrollbalk: een lange regel mag afbreken,
   de pagina mag nooit horizontaal gaan schuiven. */
.mi-kopie__waarde{
  font:400 .95rem/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  color:var(--deeg);
  overflow-wrap:anywhere;
}
/* Breed genoeg voor het langste woord dat erin komt te staan. Anders groeit
   de knop bij Gekopieerd en verspringt de regel ernaast op het moment dat
   je hem net hebt aangeklikt. */
.mi-kopie__knop{flex:0 0 auto;min-inline-size:9rem}
.mi-kopie__ico{display:flex;flex:0 0 auto}
.mi-kopie__knop .mi-vink,
.mi-kopie__knop .mi-kopie__vel{transition:none}
.mi-kopie__vel{inline-size:1.05rem;block-size:1.05rem;flex:0 0 auto}
.mi-kopie__vel path{fill:none;stroke:currentColor;stroke-width:1.7;stroke-linejoin:round}
.mi-kopie__knop[data-mi-stand="gelukt"]{color:var(--mi-goed);border-color:rgba(134,209,154,.55)}
.mi-kopie__mel{
  margin:0;min-block-size:1.4rem;
  font:400 .85rem/1.55 var(--tekst);
  color:var(--deeg-zacht);
}

/* -----------------------------------------------------------------------------
   Rustige stand
   Alles staat stil. Belangrijk: elke animatie hierboven eindigt op de
   normale stand, dus wegvallen van de animatie laat nooit iets onzichtbaar
   of verschoven achter.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  .mi-knop,
  .mi-ring,
  .mi-vink path,
  .mi-bot::after,
  .mi-laad__echt,
  .mi-toast,
  .mi-toast__balk,
  .mi-schakel__vak,
  .mi-schakel__vak::after,
  .mi-vouw__kop,
  .mi-vouw__pijl,
  .mi-vouw__rek,
  .mi-veld__in,
  .mi-toast__sluit{
    transition:none !important;
    animation:none !important;
  }
  .mi-knop:active,.mi-knop--druk{transform:none}
  /* De aftelbalk zou stil op vol blijven staan en dan liegt hij over de
     resterende tijd. Weg ermee; het sluitknopje blijft. */
  .mi-toast__balk{display:none}
}


/* ===== familie sv · SVG en lijnen ===== */
/* ==========================================================================
   sv  |  Familie "SVG en lijnen"  |  proeftuin Ensar Yufka
   --------------------------------------------------------------------------
   Zes vector-effecten. Alles wat hier staat begint met sv, zodat deze familie
   nooit botst met de zeven andere bouwers op dezelfde pagina.

   Twee uitgangspunten die overal terugkomen:
   1. Zonder JavaScript is alles al af. Beginstanden die iets verbergen staan
      achter html.js, of worden pas door het script gezet.
   2. Bij prefers-reduced-motion valt alles stil op een eindstand die klopt,
      niet op een halve stand met opacity 0 of een verschoven positie.
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. Gedeelde bodem voor de zes panelen
   -------------------------------------------------------------------------- */

/* Het palet uit lab.css blijft leidend. Alleen dit tussenvlak zit daar niet in:
   een heel lichte waas voor tegels en pillen, zodat ze los komen van het paneel
   zonder dat er een tweede achtergrondkleur bij hoeft. */
.sv-teken,
.sv-morph,
.sv-icoon,
.sv-route,
.sv-glans,
.sv-ring {
  --sv-vlak: rgba(243, 235, 221, .035);
}

/* .doek zet place-items:center, waardoor grid-items krimpen tot hun inhoud en
   een brede SVG niet meeschaalt. Daarom hier stretch en een expliciete kolom
   van minmax(0,1fr): dat laatste voorkomt dat brede inhoud het paneel oprekt
   en zo de hele pagina horizontaal laat scrollen. Compound selector, want dan
   wint hij van .doek zonder dat we .doek zelf aanraken. */
.doek.sv-teken,
.doek.sv-morph,
.doek.sv-icoon,
.doek.sv-route,
.doek.sv-glans,
.doek.sv-ring {
  grid-template-columns: minmax(0, 1fr);
  justify-items: stretch;
  align-content: center;
  gap: 1.25rem;
  text-align: center;
}

/* Elke inline SVG schaalt mee met zijn kolom. height:auto houdt de verhouding
   uit de viewBox, zodat er nooit een vaste hoogte overheen valt. */
.sv-teken__svg,
.sv-morph__svg,
.sv-route__svg,
.sv-glans__svg,
.sv-ring__svg {
  display: block;
  width: 100%;
  height: auto;
}

/* Eén focusring voor de hele familie. Los uitgeschreven per element omdat
   :focus-visible op een input de ring op zijn eigen span moet zetten. */
.sv-icoon__knop:focus-visible,
.sv-ring__knop:focus-visible,
.sv-pauzeknop:focus-visible,
.sv-morph__radio:focus-visible + .sv-morph__tekst {
  outline: 2px solid var(--goud-licht);
  outline-offset: 3px;
}

/* Pauzeknop voor de twee effecten die uit zichzelf blijven lopen: de route en
   de glans. WCAG 2.2.2 wil dat beweging die langer dan vijf seconden duurt te
   stoppen is, en prefers-reduced-motion telt daar niet voor: dat is een
   systeeminstelling en geen bediening op de pagina zelf.

   --deeg-zacht (#C9BFAF) op dit vlak (#F3EBDD op 3,5% over --grond-op #15110E,
   samen #1D1915) haalt 9,64:1, ruim boven de 4,5:1 voor tekst. In de
   hover-stand wordt de tekst --goud-licht op rgba(201,162,39,.07): 10,96:1.
   min-height van 44px maakt het raakvlak groot genoeg voor een duim. */
.sv-pauzeknop {
  justify-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: .55rem 1.1rem;
  border: 1px solid var(--lijn);
  border-radius: 999px;
  background: var(--sv-vlak);
  font-family: var(--tekst);
  font-size: .8125rem;
  line-height: 1;
  color: var(--deeg-zacht);
  cursor: pointer;
  transition: color .25s ease, border-color .25s ease, background-color .25s ease;
}

@media (hover: hover) and (pointer: fine) {
  .sv-pauzeknop:hover {
    color: var(--goud-licht);
    border-color: rgba(201, 162, 39, .45);
    background: rgba(201, 162, 39, .07);
  }
}

/* Zonder script doet deze knop niets, en een dode knop is erger dan geen knop. */
html:not(.js) .sv-pauzeknop { display: none; }

/* Alleen voor schermlezers. clip-path in plaats van display:none, want het
   element moet focusbaar blijven. */
.sv-verborgen {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Onderschriften. --deeg-zacht op --grond-op haalt ruim 9:1, dus veilig op
   het donkere paneel. */
.sv-teken__meter,
.sv-morph__bijschrift,
.sv-icoon__hint,
.sv-route__bijschrift,
.sv-glans__bijschrift,
.sv-ring__voet {
  margin: 0;
  font-family: var(--tekst);
  font-size: .8125rem;
  line-height: 1.5;
  color: var(--deeg-zacht);
}


/* --------------------------------------------------------------------------
   2. Pad dat zichzelf tekent op scroll
   --------------------------------------------------------------------------
   De lengte van elk pad wordt door het script gemeten met getTotalLength() en
   als --sv-teken-len op het pad zelf gezet. Hier staat alleen de terugval:
   dasharray none en offset 0 betekent "gewoon volledig getekend". Valt het
   script uit, dan zie je de complete tekening in plaats van een leeg vlak.
   -------------------------------------------------------------------------- */

.sv-teken__vlak {
  width: 100%;
  max-width: 420px;
  margin-inline: auto;
}

/* Bewust GEEN vector-effect:non-scaling-stroke hier. Dat lijkt handig voor een
   gelijke lijndikte, maar het rekent stroke-dasharray dan af in schermpixels
   terwijl het pad zelf wel meekrimpt. De streep zou op een telefoon dus niet
   meer even lang zijn als het pad, en dan tekent hij nooit netjes vol. */
.sv-teken__pad {
  fill: none;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

html.js .sv-teken__pad {
  stroke-dasharray: var(--sv-teken-len, none);
  stroke-dashoffset: var(--sv-teken-off, 0);
}

.sv-teken__pad--rol { stroke: var(--deeg); }
.sv-teken__pad--greep { stroke: var(--deeg-zacht); }
.sv-teken__pad--vel { stroke: var(--goud-licht); }
.sv-teken__pad--nerf { stroke: var(--goud); }
.sv-teken__pad--stof { stroke: var(--deeg-zacht); }

/* De meetwaarde staat er als bewijs bij: dit getal komt uit de browser, niet
   uit een schatting in de code. */
.sv-teken__cijfer {
  font-variant-numeric: tabular-nums; /* geen breedtesprong bij het tellen */
  color: var(--goud-licht);
}


/* --------------------------------------------------------------------------
   3. Vorm die overgaat in een andere vorm
   --------------------------------------------------------------------------
   Aansturing loopt via drie radio's, dus de demo werkt ook zonder JavaScript.
   Kan de browser d: path() én :has(), dan doet CSS de overgang. Kan hij dat
   niet, dan rekent het script de tussenstanden uit en schrijft het d-attribuut.
   De @supports-test hieronder en de test in sv.js zijn exact dezelfde twee
   vragen, zodat de twee routes elkaar nooit overlappen.
   -------------------------------------------------------------------------- */

.sv-morph__vlak {
  width: 100%;
  max-width: 260px;
  margin-inline: auto;
}

.sv-morph__vorm {
  fill: rgba(201, 162, 39, .17);
  stroke: var(--goud-licht);
  stroke-width: 3;
  stroke-linejoin: round;
  transition: stroke 720ms ease, fill 720ms ease;
}

/* Terugvalroute. Deze klasse zet sv.js alleen als de browser d: path() of
   :has() mist. De kleur komt dan hiervandaan, het pad rekent het script uit.
   Draait de CSS-route wel, dan wordt deze klasse nooit gezet en winnen de
   regels in het @supports-blok hieronder toch al op specificiteit. */
.sv-morph--v1 .sv-morph__vorm { stroke: var(--goud-licht); fill: rgba(201, 162, 39, .17); }
.sv-morph--v2 .sv-morph__vorm { stroke: var(--deeg); fill: rgba(243, 235, 221, .12); }
.sv-morph--v3 .sv-morph__vorm { stroke: var(--goud); fill: rgba(201, 162, 39, .22); }

/* Vaste hulpcirkel eromheen: geeft het oog een ijkpunt waardoor de morph
   duidelijker leest dan een vorm die los in het niets verandert. */
.sv-morph__hulp {
  fill: none;
  stroke: var(--lijn);
  stroke-width: 1.5;
  stroke-dasharray: 3 7;
}

.sv-morph__knoppen {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .5rem;
}

.sv-morph__knop {
  position: relative;
  display: inline-flex;
}

/* Niet display:none: de radio moet bereikbaar blijven met tab en pijltjes. */
.sv-morph__radio {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.sv-morph__tekst {
  display: inline-block;
  padding: .5rem .95rem;
  border: 1px solid var(--lijn);
  border-radius: 999px;
  background: var(--sv-vlak);
  font-family: var(--tekst);
  font-size: .8125rem;
  line-height: 1;
  color: var(--deeg-zacht);
  cursor: pointer;
  transition: color .25s ease, border-color .25s ease, background-color .25s ease;
}

.sv-morph__radio:checked + .sv-morph__tekst {
  border-color: var(--goud);
  background: rgba(201, 162, 39, .14);
  color: var(--goud-licht);
}

@media (hover: hover) and (pointer: fine) {
  .sv-morph__tekst:hover { color: var(--deeg); border-color: rgba(243, 235, 221, .3); }
}

/* CSS-route. Alleen als beide bouwstenen er zijn, anders neemt sv.js het over. */
@supports (d: path("M 0 0 L 1 1")) and selector(:has(*)) {
  .sv-morph__vorm {
    transition: d 720ms cubic-bezier(.66, .02, .28, 1),
                stroke 720ms ease,
                fill 720ms ease;
  }

  .sv-morph:has(.sv-morph__radio[value="1"]:checked) .sv-morph__vorm {
    d: path("M 110 26 C 156.4 26 194 63.6 194 110 C 194 156.4 156.4 194 110 194 C 63.6 194 26 156.4 26 110 C 26 63.6 63.6 26 110 26 Z");
    stroke: var(--goud-licht);
  }
  .sv-morph:has(.sv-morph__radio[value="2"]:checked) .sv-morph__vorm {
    d: path("M 110 24 C 137 78 165 132 192 186 C 165 186 137 186 110 186 C 83 186 55 186 28 186 C 55 132 83 78 110 24 Z");
    stroke: var(--deeg);
    fill: rgba(243, 235, 221, .12);
  }
  .sv-morph:has(.sv-morph__radio[value="3"]:checked) .sv-morph__vorm {
    d: path("M 110 26 C 133 26 152 45 152 110 C 152 175 133 194 110 194 C 87 194 68 175 68 110 C 68 45 87 26 110 26 Z");
    stroke: var(--goud);
    fill: rgba(201, 162, 39, .22);
  }
}


/* --------------------------------------------------------------------------
   4. Drie pictogrammen die kort tot leven komen
   --------------------------------------------------------------------------
   Elke tegel is een echte button, dus tab en Enter werken vanzelf. Hover zit
   achter (hover:hover) and (pointer:fine): op een telefoon zou :hover blijven
   plakken na een tik. Aanraking krijgt in plaats daarvan de klasse
   sv-icoon__knop--speel van het script, die precies dezelfde animatie start.
   -------------------------------------------------------------------------- */

.sv-icoon__rij {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .5rem;
  width: 100%;
  max-width: 420px;
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

/* min-width:0 op het item, anders kan een lang woord in de tegel de kolom
   breder duwen dan 1fr en gaat de pagina alsnog horizontaal scrollen. */
.sv-icoon__rij > li {
  display: flex;
  min-width: 0;
}

.sv-icoon__knop {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .55rem;
  width: 100%;
  padding: .9rem .35rem;
  border: 1px solid var(--lijn);
  border-radius: 14px;
  background: var(--sv-vlak);
  color: var(--deeg-zacht);
  font-family: var(--tekst);
  font-size: .78125rem;
  line-height: 1.2;
  cursor: pointer;
  transition: color .25s ease, border-color .25s ease, background-color .25s ease;
}

@media (hover: hover) and (pointer: fine) {
  .sv-icoon__knop:hover {
    color: var(--goud-licht);
    border-color: rgba(201, 162, 39, .45);
    background: rgba(201, 162, 39, .07);
  }
}

.sv-icoon__knop:focus-visible,
.sv-icoon__knop--speel {
  color: var(--goud-licht);
  border-color: rgba(201, 162, 39, .45);
}

.sv-icoon__svg {
  display: block;
  width: 40px;
  height: 40px;
  overflow: visible; /* de pijl schuift buiten zijn viewBox, anders wordt hij afgeknipt */
}

.sv-icoon__g {
  fill: none;
  stroke: currentColor;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  transform-box: view-box; /* expliciet, zodat px in transform gelijk staat aan viewBox-eenheden */
}

/* Op 360px is een tegel nog geen 80px breed en past "Openingstijden" niet op
   één regel. In de opmaak staat daarom een zacht afbreekstreepje op de plek
   waar het woord hoort te breken; zonder dat knipt de browser er willekeurig
   doorheen ("Openingstij / den"). break-word blijft als laatste redmiddel staan
   voor labels die later vervangen worden. */
.sv-icoon__naam {
  display: block;
  max-width: 100%;
  overflow-wrap: break-word;
  hyphens: auto;
  -webkit-hyphens: auto;
}

/* Pijl: schacht trekt zich terug en schiet er weer uit terwijl de hele groep
   meeveert. De dasharray staat gelijk aan de padlengte (24), dus in rust is
   de schacht gewoon volledig zichtbaar. */
.sv-icoon__schacht {
  stroke-dasharray: 24;
  stroke-dashoffset: 0;
}

@keyframes sv-icoon-pijl {
  0%   { transform: translateX(0); }
  40%  { transform: translateX(9px); }
  62%  { transform: translateX(-4px); }
  100% { transform: translateX(0); }
}

@keyframes sv-icoon-schacht {
  0%   { stroke-dashoffset: 0; }
  40%  { stroke-dashoffset: 22; }
  55%  { stroke-dashoffset: -22; }
  100% { stroke-dashoffset: 0; }
}

/* Klok: minuutwijzer maakt een hele ronde, uurwijzer schuift een stukje mee.
   transform-origin op het hart van de wijzerplaat. */
.sv-icoon__wijzer {
  transform-box: view-box;
  transform-origin: 24px 24px;
}

@keyframes sv-icoon-minuut {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

@keyframes sv-icoon-uur {
  from { transform: rotate(0deg); }
  to   { transform: rotate(60deg); }
}

/* Doos: deksel scharniert om zijn linkeronderhoek, de bak zakt heel licht in. */
.sv-icoon__deksel {
  transform-box: view-box;
  transform-origin: 8px 22px; /* linkeronderhoek van het deksel: daar zit het scharnier */
}

.sv-icoon__bak {
  transform-box: view-box;
  transform-origin: 24px 40px; /* de bak zakt in vanaf zijn bodem, niet vanaf zijn midden */
}

@keyframes sv-icoon-deksel {
  0%   { transform: rotate(0deg) translateY(0); }
  35%  { transform: rotate(-16deg) translateY(-2px); }
  70%  { transform: rotate(3deg) translateY(0); }
  100% { transform: rotate(0deg) translateY(0); }
}

@keyframes sv-icoon-bak {
  0%   { transform: scaleY(1); }
  35%  { transform: scaleY(.9); }
  70%  { transform: scaleY(1.04); }
  100% { transform: scaleY(1); }
}

/* De drie triggers staan samen, zodat muis, toetsenbord en aanraking exact
   hetzelfde te zien krijgen. */
.sv-icoon__knop:focus-visible .sv-icoon__g--pijl,
.sv-icoon__knop--speel .sv-icoon__g--pijl {
  animation: sv-icoon-pijl 700ms cubic-bezier(.5, 0, .2, 1) 1;
}
.sv-icoon__knop:focus-visible .sv-icoon__schacht,
.sv-icoon__knop--speel .sv-icoon__schacht {
  animation: sv-icoon-schacht 700ms cubic-bezier(.5, 0, .2, 1) 1;
}
.sv-icoon__knop:focus-visible .sv-icoon__wijzer--min,
.sv-icoon__knop--speel .sv-icoon__wijzer--min {
  animation: sv-icoon-minuut 900ms cubic-bezier(.55, 0, .25, 1) 1;
}
.sv-icoon__knop:focus-visible .sv-icoon__wijzer--uur,
.sv-icoon__knop--speel .sv-icoon__wijzer--uur {
  animation: sv-icoon-uur 900ms cubic-bezier(.55, 0, .25, 1) 1;
}
.sv-icoon__knop:focus-visible .sv-icoon__deksel,
.sv-icoon__knop--speel .sv-icoon__deksel {
  animation: sv-icoon-deksel 760ms cubic-bezier(.34, 1.3, .5, 1) 1;
}
.sv-icoon__knop:focus-visible .sv-icoon__bak,
.sv-icoon__knop--speel .sv-icoon__bak {
  animation: sv-icoon-bak 760ms cubic-bezier(.34, 1.3, .5, 1) 1;
}

@media (hover: hover) and (pointer: fine) {
  .sv-icoon__knop:hover .sv-icoon__g--pijl {
    animation: sv-icoon-pijl 700ms cubic-bezier(.5, 0, .2, 1) 1;
  }
  .sv-icoon__knop:hover .sv-icoon__schacht {
    animation: sv-icoon-schacht 700ms cubic-bezier(.5, 0, .2, 1) 1;
  }
  .sv-icoon__knop:hover .sv-icoon__wijzer--min {
    animation: sv-icoon-minuut 900ms cubic-bezier(.55, 0, .25, 1) 1;
  }
  .sv-icoon__knop:hover .sv-icoon__wijzer--uur {
    animation: sv-icoon-uur 900ms cubic-bezier(.55, 0, .25, 1) 1;
  }
  .sv-icoon__knop:hover .sv-icoon__deksel {
    animation: sv-icoon-deksel 760ms cubic-bezier(.34, 1.3, .5, 1) 1;
  }
  .sv-icoon__knop:hover .sv-icoon__bak {
    animation: sv-icoon-bak 760ms cubic-bezier(.34, 1.3, .5, 1) 1;
  }
}


/* --------------------------------------------------------------------------
   5. Gestippelde route met een stip die eroverheen loopt
   --------------------------------------------------------------------------
   Alles hangt aan één getal: de lengte van het pad. De waarde hieronder is
   numeriek uitgerekend over de drie bezier-segmenten (641,84 gebruikerseenheden),
   dus de animatie klopt ook zonder JavaScript. Zodra het script draait meet het
   dezelfde lijn opnieuw met getTotalLength() en overschrijft hij deze waarde,
   want elke engine rondt bezierlengtes net iets anders af.

   Techniek: stroke-dasharray "0.01 lengte" met een ronde lijnkop levert precies
   één punt op. dashoffset schuift dat punt over het pad. Geen offset-path nodig,
   dus geen ondersteuningsvraag.
   -------------------------------------------------------------------------- */

.sv-route {
  --sv-route-len: 641.84px;
  --sv-route-duur: 5s;
}

.sv-route__vlak {
  width: 100%;
  max-width: 640px;
  margin-inline: auto;
}

.sv-route__basis,
.sv-route__vol,
.sv-route__stip {
  fill: none;
  stroke-linecap: round;
}

.sv-route__basis {
  stroke: rgba(243, 235, 221, .3);
  stroke-width: 2.5;
  stroke-dasharray: 2 11; /* de gestippelde "nog te gaan" route */
}

.sv-route__vol {
  stroke: var(--goud);
  stroke-width: 3.5;
  stroke-dasharray: var(--sv-route-len);
  stroke-dashoffset: var(--sv-route-len);
  animation: sv-route-vul var(--sv-route-duur) linear infinite;
}

.sv-route__stip {
  stroke: var(--goud-licht);
  stroke-width: 12;
  stroke-dasharray: 0.01px var(--sv-route-len);
  stroke-dashoffset: 0;
  animation: sv-route-stip var(--sv-route-duur) linear infinite;
}

/* Zelfde duur en zelfde knikpunt op 82%, daarom lopen de vullijn en de stip
   exact gelijk op. Daarna staat de route tien procent van de cyclus stil: even
   rust voor het oog, en het moment waarop je ziet dat de stip is aangekomen.

   Twee dingen die niet vanzelf goed gaan:

   1. De eindstand is -0,998 keer de lengte, niet -1. Bij precies -1 begint de
      streep exact op het eindpunt van het pad en valt hij er dus nog nét buiten:
      de stip verdwijnt dan de laatste vijfde van elke ronde compleet. Die 0,2
      procent marge is ruim een halve eenheid, onzichtbaar naast een stip van
      twaalf dik, en de stip blijft gewoon staan.

   2. Zonder de opacity-stappen zou de lus met een harde knip terugspringen: aan
      het eind ligt er een volle gouden lijn met een stip aan het eind, en één
      frame later is alles weer leeg. Nu doven beide tussen 92% en 100% uit, en
      op 0% is er nog niets te zien, dus de overgang valt weg. */
@keyframes sv-route-vul {
  0%   { stroke-dashoffset: var(--sv-route-len); stroke-opacity: 1; }
  82%  { stroke-dashoffset: 0; }
  92%  { stroke-opacity: 1; }
  100% { stroke-dashoffset: 0; stroke-opacity: 0; }
}

@keyframes sv-route-stip {
  0%   { stroke-dashoffset: 0; stroke-opacity: 0; }
  5%   { stroke-opacity: 1; }
  82%  { stroke-dashoffset: calc(var(--sv-route-len) * -.998); }
  92%  { stroke-opacity: 1; }
  100% { stroke-dashoffset: calc(var(--sv-route-len) * -.998); stroke-opacity: 0; }
}

/* De knop zet alleen dit attribuut; het stilzetten gebeurt hier. Beide lijnen
   pauzeren tegelijk, zodat de stip nooit los komt te staan van de gouden lijn
   die hij trekt. */
.sv-route[data-sv-route-pauze="ja"] .sv-route__vol,
.sv-route[data-sv-route-pauze="ja"] .sv-route__stip {
  animation-play-state: paused;
}

.sv-route__anker {
  fill: var(--grond-op);
  stroke: var(--goud);
  stroke-width: 2.5;
}

.sv-route__anker--eind {
  stroke: var(--deeg);
}

.sv-route__legenda {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .35rem 1.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: var(--tekst);
  font-size: .8125rem;
  color: var(--deeg-zacht);
}

.sv-route__punt {
  display: inline-block;
  width: .55rem;
  height: .55rem;
  margin-right: .45rem;
  border-radius: 50%;
  background: var(--goud);
  vertical-align: baseline;
}

.sv-route__punt--eind {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--deeg);
}


/* --------------------------------------------------------------------------
   6. Lijn met een kleurverloop dat langs de lijn beweegt
   --------------------------------------------------------------------------
   Het verloop zelf staat stil; wat beweegt is de rechthoek waar het verloop op
   ligt. Die rechthoek wordt afgedekt door een masker in de vorm van de drie
   lijnen. Zo is de animatie een gewone CSS-transform, en dat is de enige soort
   die overal exact hetzelfde doet.

   spreadMethod="repeat" op de linearGradient herhaalt het verloop elke 300
   eenheden. Door de baan precies 300 eenheden op te schuiven sluit de lus
   naadloos: je ziet nooit een sprong.

   Onder het masker ligt een doffe kopie van dezelfde lijnen. Mocht een engine
   het masker niet tekenen, dan staat er nog steeds een nette scheidingslijn.
   -------------------------------------------------------------------------- */

.sv-glans__vlak {
  display: grid;
  gap: 1rem;
  width: 100%;
  max-width: 600px;
  margin-inline: auto;
}

.sv-glans__kop {
  margin: 0;
  font-family: var(--kop);
  font-size: clamp(1.35rem, 1.1rem + 1.2vw, 1.9rem);
  line-height: 1.15;
  color: var(--deeg);
}

.sv-glans__lijn {
  fill: none;
  stroke-linecap: round;
}

.sv-glans__bodem {
  stroke: rgba(243, 235, 221, .12);
}

.sv-glans__masker {
  stroke: #fff; /* wit in een masker betekent volledig doorlaten */
}

@keyframes sv-glans-baan {
  from { transform: translateX(0); }
  to   { transform: translateX(-300px); }
}

.sv-glans__baan {
  transform-box: view-box;
  animation: sv-glans-baan 2.6s linear infinite;
}

/* Dezelfde pauze als bij de route: de glans loopt in een lus door en moet dus
   met een knop te stoppen zijn. */
.sv-glans[data-sv-glans-pauze="ja"] .sv-glans__baan {
  animation-play-state: paused;
}


/* --------------------------------------------------------------------------
   7. Cirkel die een percentage toont
   --------------------------------------------------------------------------
   De omtrek van r=54 is 2 * pi * 54 = 339,292. Die waarde staat als
   stroke-dasharray en stroke-dashoffset gewoon in het HTML-attribuut, met de
   juiste eindstand erin. Zonder JavaScript zie je dus meteen de goede ring en
   het goede getal. Het script zet de ring eerst op nul en telt hem op naar de
   eindwaarde zodra hij in beeld komt; het rekent die omtrek zelf opnieuw uit
   met het r-attribuut, zodat er nergens een los getal in de code staat.
   -------------------------------------------------------------------------- */

.sv-ring__rij {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: .5rem;
  width: 100%;
  max-width: 460px;
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.sv-ring__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .5rem;
  min-width: 0;
}

.sv-ring__svg {
  max-width: 128px;
}

.sv-ring__spoor {
  fill: none;
  stroke: rgba(243, 235, 221, .12);
  stroke-width: 9;
}

.sv-ring__vul {
  fill: none;
  stroke: var(--goud);
  stroke-width: 9;
  stroke-linecap: round;
}

/* Alle drie de ringen noemen hun kleur zelf. De eerste stond eerder op de
   erfenis uit .sv-ring__vul, waardoor sv-ring__item--een in de opmaak stond
   zonder dat er hier iets aan hing. */
.sv-ring__item--een .sv-ring__vul { stroke: var(--goud); }
.sv-ring__item--twee .sv-ring__vul { stroke: var(--goud-licht); }
.sv-ring__item--drie .sv-ring__vul { stroke: var(--deeg-zacht); }

/* De fill is --goud-licht (#E8CE73) en die haalt op --grond-op (#15110E)
   12,08:1, ruim boven de 4,5:1 die tekst nodig heeft. Ter vergelijking: het
   donkerdere --goud van de ring zelf komt op dezelfde grond uit op 7,76:1.
   Tabular nums voorkomt dat het getal wiebelt tijdens het tellen. */
.sv-ring__getal {
  fill: var(--goud-licht);
  font-family: var(--tekst);
  font-size: 27px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.sv-ring__naam {
  margin: 0;
  font-family: var(--tekst);
  font-size: .8125rem;
  color: var(--deeg-zacht);
}

.sv-ring__knop {
  justify-self: center;
  padding: .55rem 1.1rem;
  border: 1px solid var(--lijn);
  border-radius: 999px;
  background: var(--sv-vlak);
  font-family: var(--tekst);
  font-size: .8125rem;
  line-height: 1;
  color: var(--deeg-zacht);
  cursor: pointer;
  transition: color .25s ease, border-color .25s ease, background-color .25s ease;
}

@media (hover: hover) and (pointer: fine) {
  .sv-ring__knop:hover {
    color: var(--goud-licht);
    border-color: rgba(201, 162, 39, .45);
    background: rgba(201, 162, 39, .07);
  }
}

/* Zonder script doet deze knop niets, dus dan tonen we hem ook niet. */
html:not(.js) .sv-ring__knop { display: none; }

@media (max-width: 24rem) {
  .sv-ring__getal { font-size: 30px; } /* de ring krimpt mee, het getal moet leesbaar blijven */
  .sv-icoon__svg { width: 34px; height: 34px; }
  .sv-icoon__knop { font-size: .75rem; padding: .8rem .25rem; }
  .sv-icoon__rij { gap: .4rem; }
}


/* --------------------------------------------------------------------------
   8. Rust: prefers-reduced-motion
   --------------------------------------------------------------------------
   Staat bewust achteraan, zodat deze regels van alles hierboven winnen zonder
   dat er ergens !important nodig is. Elk effect valt stil op een stand die af
   is: tekening compleet, route gevuld, ringen gevuld, verloop netjes verdeeld.
   Nergens blijft iets onzichtbaar of verschoven hangen.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {

  html.js .sv-teken__pad {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
  }

  .sv-morph__vorm { transition: none; }

  .sv-icoon__knop:hover .sv-icoon__g--pijl,
  .sv-icoon__knop:hover .sv-icoon__schacht,
  .sv-icoon__knop:hover .sv-icoon__wijzer--min,
  .sv-icoon__knop:hover .sv-icoon__wijzer--uur,
  .sv-icoon__knop:hover .sv-icoon__deksel,
  .sv-icoon__knop:hover .sv-icoon__bak,
  .sv-icoon__knop:focus-visible .sv-icoon__g--pijl,
  .sv-icoon__knop:focus-visible .sv-icoon__schacht,
  .sv-icoon__knop:focus-visible .sv-icoon__wijzer--min,
  .sv-icoon__knop:focus-visible .sv-icoon__wijzer--uur,
  .sv-icoon__knop:focus-visible .sv-icoon__deksel,
  .sv-icoon__knop:focus-visible .sv-icoon__bak,
  .sv-icoon__knop--speel .sv-icoon__g--pijl,
  .sv-icoon__knop--speel .sv-icoon__schacht,
  .sv-icoon__knop--speel .sv-icoon__wijzer--min,
  .sv-icoon__knop--speel .sv-icoon__wijzer--uur,
  .sv-icoon__knop--speel .sv-icoon__deksel,
  .sv-icoon__knop--speel .sv-icoon__bak {
    animation: none;
  }

  /* Route staat stil met de stip iets voorbij het midden: dat leest als een
     afgelegde weg in plaats van als een lijn die per ongeluk halverwege stopte. */
  .sv-route__vol {
    animation: none;
    stroke-dashoffset: calc(var(--sv-route-len) * .38);
  }
  .sv-route__stip {
    animation: none;
    stroke-dashoffset: calc(var(--sv-route-len) * -.62);
  }

  .sv-glans__baan { animation: none; }

  .sv-ring__knop { display: none; }

  /* Er beweegt niets meer, dus valt er ook niets te pauzeren. Zet iemand de
     voorkeur tijdens het bezoek weer uit, dan staan de knoppen er meteen weer:
     dit is een CSS-regel, geen keuze die het script eenmalig maakt. */
  .sv-pauzeknop { display: none; }
}


/* ===== familie ov · Overgangen en layout ===== */
/* ==========================================================================
   ov.css  -  familie "Overgangen en layout"   (voorvoegsel: ov)

   Zes effecten: weergavewissel met View Transitions, FLIP-herordening,
   een ongelijk raster dat na elkaar binnenkomt, meelopende zijnavigatie,
   een gordijn bij het openen en een kaart die uitklapt naar een paneel.

   Elke klasse, id, keyframe, data-attribuut en variabele begint met ov-,
   zodat acht losse bouwers op dezelfde pagina elkaar niet raken.
   Variabelen uit lab.css (--grond, --deeg, --goud, ...) worden hier
   alleen gebruikt, nooit opnieuw gedefinieerd.
   ========================================================================== */


/* ==========================================================================
   0.  Gedeeld gereedschap van deze familie
   ========================================================================== */

/* .doek staat op display:grid met place-items:center. Een grid-item krijgt
   daardoor max-content breedte in plaats van de volle breedte. Zonder deze
   regel krimpt elke demo in het midden in elkaar. */
.ov-vul{
  width:100%;
  min-width:0;
}

/* Dingen die pas ergens toe dienen als JS draait. Zonder JS is een knop die
   niets doet erger dan geen knop: dan liever helemaal weg. De weergavewaarde
   verschilt per element, vandaar de variabele met een veilige standaard.

   Beide regels hangen bewust aan html en niet aan de klasse alleen. Met een
   kale .ov-alleen-js wint een latere regel als .ov-knoprij{display:flex} op
   volgorde, en dan staat de knop er zonder JS alsnog. */
html:not(.js) .ov-alleen-js{ display:none; }
html.js .ov-alleen-js{ display:var(--ov-jsweergave, flex); }

/* Tekst die alleen de schermlezer nodig heeft (statusmeldingen). */
.ov-vw{
  position:absolute;
  width:1px;height:1px;
  margin:-1px;padding:0;
  overflow:hidden;
  clip:rect(0 0 0 0);
  clip-path:inset(50%);
  white-space:nowrap;
  border:0;
}

/* Eén focusring voor de hele familie. Op een donkere ondergrond verdwijnt de
   standaardring van de browser bijna, daarom het lichte goud met afstand. */
.ov-knop:focus-visible,
.ov-kaart:focus-visible,
.ov-kaart-sluit:focus-visible,
.ov-zij-link:focus-visible,
.ov-zij-terug:focus-visible{
  outline:2px solid var(--goud-licht);
  outline-offset:3px;
  border-radius:6px;
}

/* --- tekststijlen binnen de demo's ------------------------------------- */
.ov-oog{
  font-family:var(--tekst);
  font-size:.72rem;
  letter-spacing:.16em;
  text-transform:uppercase;
  /* #C9A227 op #15110E haalt 7,7:1, ruim boven de 4,5:1 die nodig is. */
  color:var(--goud);
  margin:0 0 .4rem;
}
.ov-kop{
  font-family:var(--kop);
  font-weight:400;
  font-size:clamp(1.1rem,2.2vw,1.55rem);
  line-height:1.22;
  color:var(--deeg);
  margin:0 0 .5rem;
}
.ov-tekst{
  font-family:var(--tekst);
  font-size:.95rem;
  line-height:1.62;
  color:var(--deeg-zacht);
  margin:0;
  /* Voorbij ongeveer 62 tekens per regel raakt het oog de volgende regel
     kwijt; dat is bij lange tekstblokken het verschil tussen lezen en scannen. */
  max-width:62ch;
}
/* Twee alinea's zonder tussenruimte lezen als één blok. Alleen ruimte tussen
   opeenvolgende alinea's, niet eronder, zodat een grid-gap niet verdubbelt. */
.ov-tekst + .ov-tekst{ margin-top:.7rem; }
.ov-mini{
  font-family:var(--tekst);
  font-size:.8rem;
  line-height:1.5;
  color:var(--deeg-zacht);
  margin:0;
}

/* --- knoppen ------------------------------------------------------------ */
.ov-knoprij{
  display:flex;
  flex-wrap:wrap;
  gap:.5rem;
  align-items:center;
}
.ov-knop{
  font-family:var(--tekst);
  font-size:.85rem;
  line-height:1.2;
  padding:.55rem .95rem;
  border:1px solid var(--lijn);
  border-radius:999px;
  background:transparent;
  color:var(--deeg-zacht);
  cursor:pointer;
  transition:background-color .18s ease, color .18s ease, border-color .18s ease;
}
/* De ingedrukte stand is goud met bijna zwarte tekst: 8,1:1, goed leesbaar
   en meteen duidelijk welke weergave aan staat. */
.ov-knop[aria-pressed="true"]{
  background:var(--goud);
  border-color:var(--goud);
  color:var(--grond);
  font-weight:600;
}
/* Hover alleen voor een echte muis. Op een aanraakscherm blijft een
   hover-stand anders plakken na het tikken. */
@media (hover:hover) and (pointer:fine){
  .ov-knop:hover{
    border-color:var(--goud);
    color:var(--deeg);
  }
  .ov-knop[aria-pressed="true"]:hover{
    background:var(--goud-licht);
    border-color:var(--goud-licht);
    color:var(--grond);
  }
}

/* Eén curve voor de hele familie, zodat de zes effecten als familie aanvoelen
   en niet als zes losse trucjes. :where() houdt de specificiteit op nul, zodat
   een latere regel er altijd overheen kan. */
:where(.ov-raster, .ov-kaart-veld){
  --ov-curve:cubic-bezier(.2,.8,.25,1);
}


/* ==========================================================================
   1.  Weergavewissel met View Transitions  (.ov-vt-*)
   ========================================================================== */

.ov-vt{
  display:grid;
  gap:1rem;
}

.ov-vt-bediening{
  justify-content:flex-start;
}

.ov-vt-lijst{
  list-style:none;
  margin:0;
  padding:0;
  display:grid;
  /* min() voorkomt dat een track breder wordt dan het scherm op 360px,
     dat is de enige manier waarop dit raster horizontaal zou kunnen scrollen. */
  grid-template-columns:repeat(auto-fit, minmax(min(9.5rem,100%), 1fr));
  gap:.75rem;
}

/* De lijstweergave is één kolom met de foto links. Dat is de hele
   layoutverandering; alle beweging komt van de browser, niet van CSS. */
.ov-vt-lijst--lijst{
  grid-template-columns:minmax(0,1fr);
}

/* De li zelf als grid, anders rekt de kaart niet mee met de hoogste buur en
   krijg je een rafelige onderrand in de rasterweergave. */
.ov-vt-lijst > li{ display:grid; min-width:0; }

.ov-vt-kaart{
  display:grid;
  grid-template-columns:minmax(0,1fr);
  gap:.6rem;
  background:var(--grond);
  border:1px solid var(--lijn);
  border-radius:12px;
  padding:.6rem;
  height:100%;
  align-content:start;
}
.ov-vt-lijst--lijst .ov-vt-kaart{
  grid-template-columns:4.75rem minmax(0,1fr);
  align-items:center;
  align-content:center;
  gap:.9rem;
  padding:.6rem .8rem;
}

.ov-vt-foto{
  display:block;
  width:100%;
  height:auto;
  aspect-ratio:4/3;
  object-fit:cover;
  border-radius:8px;
  background:var(--grond-op);
}
.ov-vt-lijst--lijst .ov-vt-foto{
  aspect-ratio:1/1;
}

.ov-vt-naam{
  font-family:var(--kop);
  font-weight:400;
  font-size:1rem;
  color:var(--deeg);
  margin:0 0 .15rem;
  line-height:1.25;
}
.ov-vt-bij{
  font-family:var(--tekst);
  font-size:.8rem;
  color:var(--deeg-zacht);
  margin:0;
}

/* Terugval voor browsers zonder View Transitions: een korte, rustige fade
   op de lijst zelf. Beter iets bescheidens dan een harde sprong. */
@keyframes ov-vt-zacht{
  from{ opacity:.35; }
  to  { opacity:1; }
}
.ov-vt-lijst--wissel{
  animation:ov-vt-zacht 220ms ease-out both;
}

/* De namen staan alleen achter html.js. Zonder JS wisselt er niets, dan is
   een view-transition-name overbodig gewicht. */
@supports (view-transition-name: none){
  html.js .ov-vt-lijst > li[data-ov-vt="1"]{ view-transition-name:ov-vt-k1; }
  html.js .ov-vt-lijst > li[data-ov-vt="2"]{ view-transition-name:ov-vt-k2; }
  html.js .ov-vt-lijst > li[data-ov-vt="3"]{ view-transition-name:ov-vt-k3; }
  html.js .ov-vt-lijst > li[data-ov-vt="4"]{ view-transition-name:ov-vt-k4; }
  html.js .ov-vt-lijst > li[data-ov-vt="5"]{ view-transition-name:ov-vt-k5; }

  /* De foto krijgt een eigen naam. Zonder dat wordt de foto meegerekt in de
     kaart-overgang en zie je hem uitgesmeerd; met een eigen naam schuift en
     bijsnijdt hij netjes mee. */
  html.js .ov-vt-lijst > li[data-ov-vt="1"] .ov-vt-foto{ view-transition-name:ov-vt-f1; }
  html.js .ov-vt-lijst > li[data-ov-vt="2"] .ov-vt-foto{ view-transition-name:ov-vt-f2; }
  html.js .ov-vt-lijst > li[data-ov-vt="3"] .ov-vt-foto{ view-transition-name:ov-vt-f3; }
  html.js .ov-vt-lijst > li[data-ov-vt="4"] .ov-vt-foto{ view-transition-name:ov-vt-f4; }
  html.js .ov-vt-lijst > li[data-ov-vt="5"] .ov-vt-foto{ view-transition-name:ov-vt-f5; }
}

/* Alleen onze eigen groepen aansturen, nooit ::view-transition-group(*):
   die pseudo-elementen staan op documentniveau en zouden het werk van de
   andere zeven bouwers overrulen. */
::view-transition-group(ov-vt-k1),
::view-transition-group(ov-vt-k2),
::view-transition-group(ov-vt-k3),
::view-transition-group(ov-vt-k4),
::view-transition-group(ov-vt-k5),
::view-transition-group(ov-vt-f1),
::view-transition-group(ov-vt-f2),
::view-transition-group(ov-vt-f3),
::view-transition-group(ov-vt-f4),
::view-transition-group(ov-vt-f5){
  animation-duration:340ms;
  animation-timing-function:cubic-bezier(.2,.8,.25,1);
}
::view-transition-old(ov-vt-k1),::view-transition-new(ov-vt-k1),
::view-transition-old(ov-vt-k2),::view-transition-new(ov-vt-k2),
::view-transition-old(ov-vt-k3),::view-transition-new(ov-vt-k3),
::view-transition-old(ov-vt-k4),::view-transition-new(ov-vt-k4),
::view-transition-old(ov-vt-k5),::view-transition-new(ov-vt-k5){
  animation-duration:220ms;
}
/* De foto's mogen niet vervormen tijdens de morph: bijsnijden in plaats van
   uitrekken, precies zoals object-fit:cover op de echte afbeelding. */
::view-transition-old(ov-vt-f1),::view-transition-new(ov-vt-f1),
::view-transition-old(ov-vt-f2),::view-transition-new(ov-vt-f2),
::view-transition-old(ov-vt-f3),::view-transition-new(ov-vt-f3),
::view-transition-old(ov-vt-f4),::view-transition-new(ov-vt-f4),
::view-transition-old(ov-vt-f5),::view-transition-new(ov-vt-f5){
  width:100%;
  height:100%;
  object-fit:cover;
  animation-duration:340ms;
}


/* ==========================================================================
   2.  FLIP: van volgorde wisselen en er vloeiend naartoe schuiven (.ov-flip-*)
   ========================================================================== */

.ov-flip{
  display:grid;
  gap:1rem;
}

.ov-flip-lijst{
  list-style:none;
  margin:0;
  padding:0;
  display:grid;
  gap:.55rem;
}

.ov-flip-item{
  display:grid;
  grid-template-columns:2.25rem 3.25rem minmax(0,1fr);
  align-items:center;
  gap:.85rem;
  background:var(--grond);
  border:1px solid var(--lijn);
  border-radius:12px;
  padding:.55rem .8rem;
}

/* will-change kost geheugen zolang het aanstaat, dus alleen tijdens de
   schuifbeweging. JS zet deze klasse erop en haalt hem er weer af. */
.ov-flip-item--beweegt{
  will-change:transform;
}

.ov-flip-rang{
  font-family:var(--kop);
  font-size:1.05rem;
  color:var(--goud);
  text-align:center;
  font-variant-numeric:tabular-nums;
}

.ov-flip-foto{
  display:block;
  width:3.25rem;
  height:3.25rem;
  object-fit:cover;
  border-radius:8px;
  background:var(--grond-op);
}

.ov-flip-naam{
  font-family:var(--kop);
  font-weight:400;
  font-size:1rem;
  color:var(--deeg);
  margin:0 0 .1rem;
  line-height:1.25;
}
.ov-flip-bij{
  font-family:var(--tekst);
  font-size:.78rem;
  color:var(--deeg-zacht);
  margin:0;
}

@media (max-width:420px){
  .ov-flip-item{
    grid-template-columns:1.75rem 2.5rem minmax(0,1fr);
    gap:.6rem;
  }
  .ov-flip-foto{ width:2.5rem; height:2.5rem; }
}


/* ==========================================================================
   3.  Ongelijk raster dat na elkaar binnenkomt  (.ov-raster / .ov-tegel)
   ========================================================================== */

.ov-raster{
  display:grid;
  /* Mobiel: twee kolommen. Zes gelijke blokken onder elkaar zou het effect
     onzichtbaar maken, twee kolommen houdt het ritme overeind. */
  grid-template-columns:repeat(2, minmax(0,1fr));
  grid-auto-rows:minmax(6.5rem, auto);
  gap:.7rem;
  list-style:none;
  margin:0;
  padding:0;
}

.ov-tegel{
  position:relative;
  overflow:hidden;
  border-radius:12px;
  border:1px solid var(--lijn);
  background:var(--grond);
  min-width:0;
}

/* Beginstand alleen achter html.js: zonder JS ziet iemand alles gewoon staan
   in plaats van een leeg vlak dat nooit meer opent. */
html.js .ov-tegel{
  opacity:0;
  transform:translateY(16px) scale(.985);
  transition:opacity 520ms var(--ov-curve), transform 520ms var(--ov-curve);
  /* Elke tegel wacht op zijn beurt. De vertraging staat in de markup als
     --ov-i, zodat de volgorde van het raster de volgorde van de opbouw is. */
  transition-delay:calc(var(--ov-i, 0) * 75ms);
}
html.js .ov-raster--aan .ov-tegel{
  opacity:1;
  transform:none;
}

/* Absoluut in plaats van height:100%: een procenthoogte hangt af van een
   definitieve hoogte op de ouder, en die is bij grid-auto-rows niet altijd
   op tijd bekend. Zo vult de foto de tegel altijd. */
.ov-tegel-foto{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  object-fit:cover;
}

/* De tekst staat op het donkere deel van het verloop, niet op de foto zelf.
   Alleen de onderkant narekenen is te makkelijk: de letters staan hoger dan de
   onderrand, en bij een titel van twee regels nog hoger. Daarom houdt de sluier
   zijn dekking vast tot voorbij de bovenkant van de letters en valt hij pas in
   de laatste strook weg, waar alleen nog padding zit.

   Nagerekend met --deeg #F3EBDD op de lichtste foto (gemeten #F0E8DA), hoogte
   gemeten vanaf de onderkant van dit blokje:
     29%: 14,96:1 · 50%: 14,60:1 · 62% (bovenkant letters, één regel): 14,39:1
     · 70%: 14,24:1 · 79% (bovenkant letters, twee regels): 12,47:1
   Onder 4,5:1 zakt het pas vanaf 85,7% hoogte. Daar komt geen letter: de
   padding-top van .85rem duwt de bovenste regel altijd onder die grens. */
.ov-tegel-bijschrift{
  position:absolute;
  inset:auto 0 0 0;
  padding:.85rem .9rem .8rem;
  background:linear-gradient(to top,
              rgba(9,7,6,.94) 0%,
              rgba(9,7,6,.90) 78%,
              rgba(9,7,6,.28) 92%,
              rgba(9,7,6,0) 100%);
}
.ov-tegel-titel{
  font-family:var(--kop);
  font-weight:400;
  font-size:clamp(.95rem,1.6vw,1.15rem);
  color:var(--deeg);
  margin:0;
  line-height:1.2;
}

/* Tegels zonder foto: rustig vlak met een gouden randje aan de linkerkant. */
.ov-tegel--woord{
  display:grid;
  align-content:center;
  gap:.35rem;
  padding:1rem 1.1rem;
  background:var(--grond-op);
  border-left:3px solid var(--goud);
}

/* --- de spans: hier ontstaat het ongelijke ritme ----------------------- */
.ov-tegel[data-ov-tegel="1"]{ grid-column:span 2; grid-row:span 2; }
.ov-tegel[data-ov-tegel="2"]{ grid-column:span 2; }
.ov-tegel[data-ov-tegel="3"]{ grid-column:span 1; }
.ov-tegel[data-ov-tegel="4"]{ grid-column:span 1; }
.ov-tegel[data-ov-tegel="5"]{ grid-column:span 2; }
.ov-tegel[data-ov-tegel="6"]{ grid-column:span 2; }

@media (min-width:640px){
  .ov-raster{
    grid-template-columns:repeat(4, minmax(0,1fr));
    grid-auto-rows:minmax(7.5rem, auto);
    gap:.8rem;
  }
  .ov-tegel[data-ov-tegel="1"]{ grid-column:span 2; grid-row:span 2; }
  .ov-tegel[data-ov-tegel="2"]{ grid-column:span 2; }
  .ov-tegel[data-ov-tegel="3"]{ grid-column:span 2; }
  .ov-tegel[data-ov-tegel="4"]{ grid-column:span 1; }
  .ov-tegel[data-ov-tegel="5"]{ grid-column:span 1; }
  .ov-tegel[data-ov-tegel="6"]{ grid-column:span 2; }
}

@media (min-width:960px){
  .ov-raster{
    grid-template-columns:repeat(6, minmax(0,1fr));
    grid-auto-rows:minmax(8.5rem, auto);
  }
  /* Zes kolommen geeft ruimte voor 3+3, 3+2+1 en 2+4: nergens drie gelijke
     kolommen onder elkaar, en toch een rustig raster. */
  .ov-tegel[data-ov-tegel="1"]{ grid-column:span 3; grid-row:span 2; }
  .ov-tegel[data-ov-tegel="2"]{ grid-column:span 3; }
  .ov-tegel[data-ov-tegel="3"]{ grid-column:span 2; }
  .ov-tegel[data-ov-tegel="4"]{ grid-column:span 1; }
  .ov-tegel[data-ov-tegel="5"]{ grid-column:span 2; }
  .ov-tegel[data-ov-tegel="6"]{ grid-column:span 4; }
}


/* ==========================================================================
   4.  Meelopende zijnavigatie  (.ov-zij-*)

   Bewust GEEN .doek: die staat op overflow:hidden en daarmee werkt
   position:sticky niet meer. Dit is een eigen paneel zonder overflow.
   ========================================================================== */

.ov-zij-veld{
  display:grid;
  grid-template-columns:minmax(0,1fr);
  gap:clamp(1rem,3vw,2rem);
  background:var(--grond-op);
  border:1px solid var(--lijn);
  border-radius:16px;
  padding:clamp(1rem,3vw,1.75rem);
  /* Uitdrukkelijk zichtbaar: één overflow:hidden in de keten en de sticky
     kolom valt stil zonder dat de browser klaagt. */
  overflow:visible;
}

.ov-zij-nav{
  position:sticky;
  top:0;
  z-index:2;
  /* In een grid moet het item zelf niet uitrekken, anders heeft sticky
     geen ruimte om binnen te schuiven. */
  align-self:start;
  background:var(--grond-op);
  /* De balk loopt op smalle schermen door tot de rand van het paneel; anders
     schuift de tekst zichtbaar langs de zijkanten van de balk omhoog. */
  margin-inline:calc(clamp(1rem,3vw,1.75rem) * -1);
  padding:.6rem clamp(1rem,3vw,1.75rem);
  border-bottom:1px solid var(--lijn);
}

.ov-zij-titel{
  font-family:var(--tekst);
  font-size:.72rem;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--goud);
  margin:0 0 .55rem;
}

.ov-zij-lijst{
  list-style:none;
  margin:0;
  padding:0;
  display:flex;
  gap:.4rem;
  /* Op smalle schermen is de balk breder dan het scherm. Die breedte blijft
     binnen dit eigen kadertje, de pagina zelf scrollt nooit horizontaal. */
  overflow-x:auto;
  overscroll-behavior-x:contain;
  scrollbar-width:thin;
  padding-bottom:.35rem;
}
.ov-zij-lijst > li{ flex:0 0 auto; }

.ov-zij-link{
  display:block;
  font-family:var(--tekst);
  font-size:.85rem;
  line-height:1.2;
  padding:.5rem .8rem;
  border:1px solid var(--lijn);
  border-radius:999px;
  color:var(--deeg-zacht);
  text-decoration:none;
  white-space:nowrap;
  transition:color .18s ease, border-color .18s ease, background-color .18s ease;
}
/* De actieve stand mag niet alleen kleur zijn: er staat ook een gouden rand
   en een dikker lettergewicht, zodat het bij kleurenblindheid leesbaar blijft. */
.ov-zij-link[aria-current="true"]{
  color:var(--grond);
  background:var(--goud);
  border-color:var(--goud);
  font-weight:600;
}
@media (hover:hover) and (pointer:fine){
  .ov-zij-link:hover{ color:var(--deeg); border-color:var(--goud); }
  .ov-zij-link[aria-current="true"]:hover{ color:var(--grond); background:var(--goud-licht); }
}

.ov-zij-inhoud{ min-width:0; }

.ov-zij-sectie{
  /* Genoeg hoogte om het meelopen te kunnen zien; minder en er valt niets
     te volgen. Dit is meteen de prijs van dit effect: extra paginahoogte. */
  min-height:clamp(13rem, 30vh, 18rem);
  padding-block:1.25rem;
  border-top:1px solid var(--lijn);
  /* Anders schuift de sticky balk over de kop heen na een sprong. */
  scroll-margin-top:5.5rem;
}
.ov-zij-sectie:first-child{ border-top:0; padding-top:.25rem; }

.ov-zij-terug{
  display:inline-block;
  margin-top:1.25rem;
  font-family:var(--tekst);
  font-size:.8rem;
  color:var(--goud);
  text-decoration:underline;
  text-underline-offset:3px;
}

@media (min-width:820px){
  .ov-zij-veld{
    grid-template-columns:minmax(0,13.5rem) minmax(0,1fr);
  }
  .ov-zij-nav{
    top:clamp(.75rem, 3vh, 1.5rem);
    margin-inline:0;
    padding:0;
    background:transparent;
    border-bottom:0;
  }
  .ov-zij-lijst{
    display:grid;
    gap:.35rem;
    overflow-x:visible;
    padding-bottom:0;
  }
  .ov-zij-link{
    border:0;
    border-left:2px solid var(--lijn);
    border-radius:0 6px 6px 0;
    padding:.5rem .75rem;
    white-space:normal;
  }
  .ov-zij-link[aria-current="true"]{
    background:transparent;
    color:var(--goud-licht);
    border-left-color:var(--goud);
  }
  @media (hover:hover) and (pointer:fine){
    .ov-zij-link[aria-current="true"]:hover{ background:transparent; color:var(--goud-licht); }
  }
  .ov-zij-sectie{ scroll-margin-top:2rem; }
}


/* ==========================================================================
   5.  Gordijn bij het openen  (.ov-gordijn-*)

   Harde grens: 380ms beweging, na 420ms is de laag hoe dan ook weg.
   De inhoud staat er al vanaf frame één; het gordijn ligt er alleen even
   overheen en heeft pointer-events:none, dus klikken kan altijd.
   ========================================================================== */

.ov-gordijn-veld{
  position:relative;
  overflow:hidden;
  border-radius:16px;
  border:1px solid var(--lijn);
  background:var(--grond-op);
  isolation:isolate;
}

.ov-gordijn-inhoud{
  display:grid;
  gap:0;
}
@media (min-width:760px){
  .ov-gordijn-inhoud{
    grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);
    align-items:stretch;
  }
}

.ov-gordijn-foto{
  display:block;
  width:100%;
  height:100%;
  min-height:12rem;
  max-height:22rem;
  object-fit:cover;
  /* De bakker staat in de bovenhelft van de foto; hoger uitsnijden zou zijn
     handen afsnijden. */
  object-position:50% 42%;
}

.ov-gordijn-tekst{
  display:grid;
  align-content:center;
  gap:.6rem;
  padding:clamp(1.1rem,3vw,1.9rem);
}

.ov-gordijn{
  position:absolute;
  inset:0;
  z-index:2;
  display:none;
  /* Nooit een klik onderscheppen. Dit is de belangrijkste regel van dit
     hele effect: een gordijn dat de knop eronder blokkeert is een bug. */
  pointer-events:none;
}
html.js .ov-gordijn--speelt{ display:block; }

.ov-gordijn-helft{
  position:absolute;
  left:0;
  right:0;
  height:50.5%;
  background:linear-gradient(180deg, #14100D 0%, #0B0908 100%);
  /* Vaste vaart, en de eindstand blijft staan tot JS de laag weghaalt.
     Zo is er geen enkel frame waarin het gordijn terugvalt. */
  animation-duration:380ms;
  animation-timing-function:cubic-bezier(.65,0,.25,1);
  animation-fill-mode:forwards;
}
.ov-gordijn-helft--boven{
  top:0;
  border-bottom:1px solid var(--goud);
  animation-name:ov-gordijn-op;
}
.ov-gordijn-helft--onder{
  bottom:0;
  border-top:1px solid var(--goud);
  animation-name:ov-gordijn-neer;
}

@keyframes ov-gordijn-op{
  from{ transform:translateY(0); }
  to  { transform:translateY(-101%); }
}
@keyframes ov-gordijn-neer{
  from{ transform:translateY(0); }
  to  { transform:translateY(101%); }
}


/* ==========================================================================
   6.  Kaart die uitklapt naar een paneel op dezelfde plek  (.ov-kaart-*)
   ========================================================================== */

.ov-kaart-veld{
  position:relative;
  background:var(--grond-op);
  border:1px solid var(--lijn);
  border-radius:16px;
  padding:clamp(1rem,3vw,1.6rem);
}
/* Alleen met JS heeft het veld een vaste minimumhoogte nodig, want dan ligt
   het paneel er absoluut overheen. Zonder JS staat alles gewoon onder elkaar. */
html.js .ov-kaart-veld{
  min-height:clamp(21rem, 46vh, 28rem);
}

.ov-kaart-raster{
  list-style:none;
  margin:0;
  padding:0;
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(min(10.5rem,100%), 1fr));
  gap:.85rem;
}
.ov-kaart-raster > li{ display:grid; min-width:0; }

.ov-kaart{
  display:grid;
  gap:.55rem;
  align-content:start;
  background:var(--grond);
  border:1px solid var(--lijn);
  border-radius:12px;
  padding:.6rem;
  color:var(--deeg);
  text-decoration:none;
  height:100%;
  transition:border-color .2s ease, transform .2s var(--ov-curve);
}
@media (hover:hover) and (pointer:fine){
  .ov-kaart:hover{ border-color:var(--goud); transform:translateY(-2px); }
}

.ov-kaart-foto{
  display:block;
  width:100%;
  height:auto;
  aspect-ratio:4/3;
  object-fit:cover;
  border-radius:8px;
  background:var(--grond-op);
}
.ov-kaart-titel{
  font-family:var(--kop);
  font-weight:400;
  font-size:1.02rem;
  line-height:1.25;
  margin:0;
  color:var(--deeg);
}
.ov-kaart-bij{
  font-family:var(--tekst);
  font-size:.78rem;
  color:var(--deeg-zacht);
  margin:0;
}
.ov-kaart-meer{
  font-family:var(--tekst);
  font-size:.78rem;
  color:var(--goud);
  margin:.1rem 0 0;
}

/* Zonder JS staan de panelen gewoon onder de kaarten. De kaart is een anker
   naar het paneel eronder, dus alle inhoud blijft bereikbaar. */
.ov-kaart-lagen{
  display:grid;
  gap:1rem;
  margin-top:1.25rem;
}
.ov-kaart-paneel{
  background:var(--grond);
  border:1px solid var(--lijn);
  border-radius:14px;
  padding:clamp(1rem,3vw,1.5rem);
  scroll-margin-top:2rem;
}

html.js .ov-kaart-lagen{
  position:absolute;
  inset:0;
  display:block;
  margin:0;
  pointer-events:none;
}
html.js .ov-kaart-paneel{
  position:absolute;
  inset:0;
  overflow:auto;
  visibility:hidden;
  opacity:0;
  border-radius:16px;
  /* Verticaal gecentreerd, anders plakt korte inhoud bovenin een hoog vlak.
     "safe" is hier het hele punt: past de inhoud niet, dan valt het terug op
     bovenaan uitlijnen in plaats van de bovenkant weg te snijden. */
  display:grid;
  align-content:safe center;
}
html.js .ov-kaart-paneel--open{
  visibility:visible;
  opacity:1;
  pointer-events:auto;
  z-index:3;
}

.ov-kaart-paneel-binnen{
  display:grid;
  gap:1rem;
}
@media (min-width:680px){
  .ov-kaart-paneel-binnen{
    grid-template-columns:minmax(0,15rem) minmax(0,1fr);
    align-items:start;
  }
}

.ov-kaart-paneel-foto{
  display:block;
  width:100%;
  height:auto;
  aspect-ratio:4/3;
  object-fit:cover;
  border-radius:10px;
  background:var(--grond-op);
}

.ov-kaart-paneel-tekst{
  display:grid;
  gap:.6rem;
  align-content:start;
}

.ov-kaart-sluit{
  font-family:var(--tekst);
  font-size:.85rem;
  line-height:1.2;
  align-items:center;
  gap:.4rem;
  padding:.5rem .9rem;
  border:1px solid var(--lijn);
  border-radius:999px;
  background:transparent;
  color:var(--deeg-zacht);
  cursor:pointer;
  justify-self:start;
  transition:border-color .18s ease, color .18s ease;
}
@media (hover:hover) and (pointer:fine){
  .ov-kaart-sluit:hover{ border-color:var(--goud); color:var(--deeg); }
}

/* De binnenkant komt iets later dan het paneel zelf: eerst het vlak, dan de
   inhoud. Dat leest als één beweging in plaats van twee. */
html.js .ov-kaart-paneel--open .ov-kaart-paneel-binnen{
  animation:ov-kaart-in 300ms var(--ov-curve) 90ms both;
}
@keyframes ov-kaart-in{
  from{ opacity:0; transform:translateY(8px); }
  to  { opacity:1; transform:none; }
}

/* Terugval voor browsers die clip-path met round niet aankunnen: het paneel
   vervaagt dan gewoon in beeld. Geen halve morph, wel een nette overgang. */
@supports not (clip-path: inset(0px round 16px)){
  html.js .ov-kaart-paneel{
    transition:opacity 220ms ease, visibility 0s linear 220ms;
  }
  html.js .ov-kaart-paneel--open{
    transition:opacity 220ms ease, visibility 0s;
  }
}


/* ==========================================================================
   7.  Minder beweging: alles staat stil, niets blijft onzichtbaar hangen
   ========================================================================== */

@media (prefers-reduced-motion: reduce){

  /* Beginstanden opheffen. Dit is de kern: wie minder beweging kiest krijgt
     de eindstand meteen, niet een leeg vlak dat op een animatie wacht. */
  html.js .ov-tegel,
  html.js .ov-raster--aan .ov-tegel{
    opacity:1 !important;
    transform:none !important;
    transition:none !important;
    transition-delay:0s !important;
  }

  html.js .ov-kaart-paneel--open .ov-kaart-paneel-binnen{
    animation:none !important;
    opacity:1;
    transform:none;
  }

  /* Het gordijn wordt door JS al overgeslagen; dit is de tweede grendel voor
     het geval de laag toch in de markup blijft staan. */
  .ov-gordijn{ display:none !important; }
  .ov-gordijn-helft{ animation:none !important; }

  .ov-vt-lijst--wissel{ animation:none !important; }

  .ov-knop,
  .ov-kaart,
  .ov-kaart-sluit,
  .ov-zij-link{
    transition:none !important;
  }
  .ov-kaart:hover{ transform:none !important; }

  /* Ook de browser-eigen overgang stilzetten, voor het geval een oudere
     versie startViewTransition toch aanroept. */
  ::view-transition-group(ov-vt-k1),::view-transition-group(ov-vt-k2),
  ::view-transition-group(ov-vt-k3),::view-transition-group(ov-vt-k4),
  ::view-transition-group(ov-vt-k5),
  ::view-transition-group(ov-vt-f1),::view-transition-group(ov-vt-f2),
  ::view-transition-group(ov-vt-f3),::view-transition-group(ov-vt-f4),
  ::view-transition-group(ov-vt-f5),
  ::view-transition-old(ov-vt-k1),::view-transition-new(ov-vt-k1),
  ::view-transition-old(ov-vt-k2),::view-transition-new(ov-vt-k2),
  ::view-transition-old(ov-vt-k3),::view-transition-new(ov-vt-k3),
  ::view-transition-old(ov-vt-k4),::view-transition-new(ov-vt-k4),
  ::view-transition-old(ov-vt-k5),::view-transition-new(ov-vt-k5),
  ::view-transition-old(ov-vt-f1),::view-transition-new(ov-vt-f1),
  ::view-transition-old(ov-vt-f2),::view-transition-new(ov-vt-f2),
  ::view-transition-old(ov-vt-f3),::view-transition-new(ov-vt-f3),
  ::view-transition-old(ov-vt-f4),::view-transition-new(ov-vt-f4),
  ::view-transition-old(ov-vt-f5),::view-transition-new(ov-vt-f5){
    animation:none !important;
  }
}
