/* ==========================================================================
   DE ENE PUBLIEKE STYLESHEET.

   ZONE 2 (CLAUDE.md sectie 4). Dit bestand is het publieke kanaal: de CMS-site
   en alles wat daarin wordt uitgeleverd, de webshop inbegrepen. Het laadt de
   kernstylesheet van het ERP NIET, en dat is sinds 29-08-2026 de hele zaak:
   zolang het dat wel deed, droeg elke website die op ons CMS draait de huisstijl
   van ons ERP, en dan is het CMS niet los te verkopen.

   EEN STYLESHEET, OOK VOOR DE WINKEL. De webshop is een EXTENSIE van het CMS en
   krijgt er geen eigen (Alper, 29-08-2026). Twee publieke stylesheets naast
   elkaar is hoe een site er bij de derde module uitziet als drie sites. Dat de
   winkelregels daarmee in de CMS-module wonen is een spanning en geen
   vergetelheid; de uitweg is ring 2 uit todo/cms.md - herbruikbare BLOKKEN
   binnen een sjabloon - en hij staat daar als punt.

   DE KLEURREGEL IS NIET VERDWENEN, HIJ IS VERHUISD. Geen hex, geen rgb en geen
   rauwe oklch in dit bestand. De waarden komen uit RIJEN in `cms_theme_token` en
   worden door /scripts/siteTheme.js als --site-* op <html> gezet. Wat hieronder
   staat is de BRUG: de namen waar de regels mee werken, gevuld uit het thema.

   De terugval is met opzet een SYSTEEMKLEUR (Canvas, CanvasText, GrayText,
   LinkText) en geen hex. Systeemkleuren volgen de licht/donker-stand van de
   bezoeker vanzelf, ze zijn geen huisstijl van wie dan ook, en ze houden de
   regel overeind dat er in dit bestand geen enkele losse kleurwaarde staat.
   Staat er geen thema in de tabel, dan is de site saai en leesbaar - beter dan
   wit op wit.
   ========================================================================== */

:root {
  /* DE STAND VAN HET TOESTEL WINT HIER NIET, EN DAT IS EEN BESLUIT (D25, B841).
     Hier stond `light dark`, terwijl `cms_theme_token` maar EEN palet levert en
     dat een LICHT palet is. Die twee samen zijn de verboden derde vorm uit
     DESIGNREGELS.md D25: de browser tekent zijn eigen onderdelen donker en elke
     systeemkleur slaat om, terwijl het thema blijft staan. Wat Alper op zijn
     telefoon zag was precies dat - een zwarte knop met de donkere themaletter
     erop, en een monogram dat wegviel tegen zijn eigen verloop.

     `light` is dus geen versobering maar de enige stand die vandaag KLOPT met
     wat er in de tabel staat. Zodra de donkere set wordt AFGELEID (D37, B841 -
     lichtheid omklappen in oklch, met correctie per kleur) mag hier weer
     `light dark` staan, en dan is dat ook waar. */
  color-scheme: light;

  --bg: var(--site-bg, Canvas);
  --bg-diep: var(--site-bg-diep, Canvas);
  --panel: var(--site-vlak, Canvas);
  /* DEZELFDE KLEUR, MAAR ONAANTASTBAAR. Onderaan dit bestand kantelt `--panel`
     onder 600 px naar de paginakleur (D38); wat over de pagina heen ZWEEFT mag
     daar niet in meegaan en pakt dit token. Het is geen tweede kleur - het is
     hetzelfde themavlak, buiten het bereik van die kanteling. */
  --panel-vast: var(--site-vlak, Canvas);
  --ink: var(--site-ink, CanvasText);
  --muted: var(--site-muted, GrayText);
  --line: var(--site-lijn, GrayText);
  --brand: var(--site-merk, LinkText);
  --brand-ink: var(--site-merk-ink, Canvas);
  --color-accent: var(--site-accent, LinkText);
  --goud: var(--site-goud, LinkText);
  --actie: var(--site-actie, LinkText);
  /* EEN BESPARING IS GEEN WAARSCHUWING. `--actie` is de KNOPkleur en bij het
     standaardthema rood; een korting stond daar tot 08-09-2026 in en las als
     een fout. Eigen token, dus een klant kan hem meebrengen zoals elke andere
     kleur - en de terugval is `CanvasText` en geen groene hex, want een half
     thema is precies de mengeling die D26 verbiedt. */
  --besparing: var(--site-besparing, CanvasText);

  --radius: var(--site-radius, 12px);
  --radius-pil: var(--site-radius-pil, 999px);
  --breedte: var(--site-breedte, 1180px);
  --tracking: var(--site-tracking, 0.12em);

  font-family: var(--site-letter-ui, system-ui, sans-serif);
  font-size: var(--site-maat, 16px);
}

/* Niets in beeld voordat het thema er is; anders flitst de pagina eerst in de
   systeemkleuren voorbij. siteTheme.js zet dit attribuut, ook als het ophalen
   mislukt - een kapotte route mag geen witte pagina opleveren. */
html:not([data-thema-geladen]) body { visibility: hidden; }

/* D1: DE PAGINA SCHUIFT NOOIT ZIJWAARTS - HET VANGNET (Alper, 30-09-2026:
   *"ik wil nooit dat de pagina volledig scrolled naar rechts ... Een tabel mag
   wel scrollen, maar alleen indien echt nodig"*). Drie lagen, en dit is de
   onderste: steekt er tóch iets uit, dan knipt de wortel het af in plaats van
   de hele pagina te laten schuiven. `clip` en niet `hidden`: `hidden` maakt een
   scrollcontainer en dan werkt `position: sticky` niet meer. Staat hier en in
   `app.css` allebei, want zone 2 laadt de kernstylesheet niet.
   Dit VERBERGT een fout, het repareert hem niet - daarom meet
   `scripts/breedteproef.sh` het element dat uitsteekt, ook onder dit vangnet,
   en keurt de reviewronde een tak af die er een toevoegt (V4). Bewaakt door
   `core/tests/designregels.h` (D1, het vangnet staat er). */
html, body { overflow-x: clip; }
img, video, iframe, canvas { max-inline-size: 100%; }
select, input, textarea { max-inline-size: 100%; }

body {
  background: var(--bg);
  color: var(--ink);
  margin: 0;
}

.cms {
  /* De kern zet op body een verloop met vaste kleuren. Hier niet: één rustig
     vlak, want een leespagina hoort geen achtergrond te hebben die met de tekst
     concurreert. */
  background: var(--bg);

  /* Alles wat dit blad aan maten gebruikt, op één plek. */
  --cms-zij: 17rem;
  --cms-lees: 46rem;
  --cms-radius: 0.75rem;
  --cms-kop-hoog: 3.5rem;
  /* WAAR IETS OP PLAKT. In de kennisbank scrolt het venster en ligt de
     werkplekbalk erover, dus plakt een kolom onder die balk. Op de publieke
     site scrolt het vlak `.cms-rol` ONDER de kop (D54), en daarin plakt alles
     op nul - zie het scrollvlak hieronder. */
  --cms-plak: var(--cms-kop-hoog);
}

/* Toetsenbordgebruikers eerst: de sprong naar de inhoud is de enige manier om
   een menu van dertig pagina's over te slaan. */
.cms-skip {
  position: absolute;
  inset-inline-start: -100vw;
  z-index: 10;
  padding: 0.6rem 1rem;
  background: var(--panel);
  color: var(--ink);
  border-radius: var(--cms-radius);
}
.cms-skip:focus-visible { inset-inline-start: 1rem; top: 0.75rem; }

/* --- Bovenbalk ---------------------------------------------------------- */

.cms-kop {
  position: sticky;
  top: 0;
  z-index: 5;
  height: var(--cms-kop-hoog);
  border-block-end: 1px solid var(--line);
  background: color-mix(in oklch, var(--panel) 85%, transparent);
  backdrop-filter: blur(12px);
}

/* HET WIKI-KANAAL HEEFT AL EEN BOVENBALK - de werkplekschil van
   `werkplekSchil.js`, die cmsWiki.html sinds WIKIBALK-1 inlaadt (CLAUDE.md
   sectie 4: de wiki is zone 1, de werkplek, en houdt de ERP-huisstijl). Deze
   `.cms-kop` is de kop van de PUBLIEKE site (zone 2) en blijft daar sticky op
   top 0 staan; voor de wiki stapelt hij bovenop de werkplekbalk - twee sticky
   koppen op nul die elkaar overschrijven zodra je scrolt. Vandaar verbergen en
   niet verwijderen: de markup erboven bedient beide kanalen.
   `--cms-kop-hoog` blijft op 3.5rem staan (gelijk aan `.topbar`'s
   `min-height: 56px` in app.css) - de boom (`.cms-zijkant`) en de
   `scroll-margin` hieronder rekenen met die maat, en die rekensom moet nu
   kloppen voor de werkplekbalk in plaats van voor deze verborgen kop. */
body.cms[data-kanaal="WIKI"] .cms-kop {
  display: none;
}

.cms-kop-binnen {
  height: 100%;
  max-width: 84rem;
  margin-inline: auto;
  padding-inline: 1.25rem;
  display: flex;
  align-items: center;
  gap: 1.5rem;
}

.cms-merk {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--ink);
  text-decoration: none;
  font-weight: 700;
  letter-spacing: -0.02em;
}

/* DE MERKNAAM EN DE ONDERTITEL komen uit het thema (`site-naam`,
   `site-ondertitel`) en staan niet in de schil - zie de kop van
   cmsSite.js. Ze worden onder elkaar gezet zodat een lange ondertitel de
   navigatie ernaast niet wegduwt. */
.cms-merk-woorden {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}

.cms-merk-sub {
  font-weight: 400;
  font-size: 0.75rem;
  letter-spacing: 0;
  color: var(--muted);
}

/* GEEN MERKTEKEN IN DE KOP (Alper, 16-10-2026: "een oud placeholder icoon").
   Hier stond een merkteken: een afgerond vierkant met een rondje in twee
   lagen merkkleur - van niemand: niet ons logo en niet dat van de klant. Tot
   het thema een logotoken heeft (LOGOKOP-2) staat in de kop alleen de naam.
   Bewaakt door modules/cms/tests/sitekop_zonder_plaatshouderteken.h. */

.cms-kanalen {
  display: flex;
  gap: 0.25rem;
  padding: 0.2rem;
  border-radius: 999px;
  background: color-mix(in oklch, var(--ink) 6%, transparent);
}

/* ZONDER DEZE REGEL DOET `hidden` NIETS. De `display: flex` hierboven is een
   AUTEURSregel en wint van de `[hidden] { display: none }` uit de
   UA-stylesheet, ongeacht specificiteit - dus de balk blijft staan terwijl het
   attribuut er keurig op staat. Dat is MAREE-8, en de reden dat
   `tests/hidden_wordt_niet_overstemd.h` de hele klasse bewaakt in plaats van
   het geval. `cmsSite.js` schakelt de balk hiermee. */
.cms-kanalen[hidden] { display: none; }

.cms-kanaal {
  font: inherit;
  font-size: 0.8125rem;
  padding: 0.3rem 0.85rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: color 120ms, background-color 120ms;
}
.cms-kanaal:hover { color: var(--ink); }
.cms-kanaal.is-actief {
  background: var(--panel);
  color: var(--ink);
  box-shadow: 0 1px 2px color-mix(in oklch, var(--ink) 12%, transparent);
}

.cms-wie {
  margin-inline-start: auto;
  display: flex;
  align-items: center;
  gap: 1rem;
  font-size: 0.8125rem;
}
.cms-rollen {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
/* De omgevingsbadge. Accentkleur, want hij hoort op te vallen: het verschil
   tussen acceptatie en productie is anders alleen het poortnummer. Op
   productie wordt hij niet getoond - zie cmsSite.js. */
.cms-omgeving {
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  /* De TEKST is `--ink`: accent op een accentvlak haalde 2,43:1 (axe,
     color-contrast), en het accent is klantdata dus nooit te garanderen. Rand
     en vlak houden het accent, dus de badge valt nog steeds op. */
  color: var(--ink);
  border: 1px solid color-mix(in oklch, var(--color-accent) 45%, transparent);
  background: color-mix(in oklch, var(--color-accent) 12%, transparent);
}

/* De weergaveknop. Het pictogram is een vorm en geen bestand: een cirkel die
   in de donkere stand een maansikkel wordt doordat er een schijf overheen
   schuift, en in de systeemstand half gevuld blijft. */
.cms-thema {
  inline-size: 1.9rem;
  block-size: 1.9rem;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: color 120ms, border-color 120ms;
}
.cms-thema:hover { color: var(--ink); border-color: color-mix(in oklch, var(--ink) 35%, transparent); }
.cms-thema::before {
  content: "";
  inline-size: 0.85rem;
  block-size: 0.85rem;
  border-radius: 50%;
  border: 1.5px solid currentColor;
}
/* Licht: gevuld rondje, als een zon. */
.cms-thema[data-thema="LICHT"]::before { background: currentColor; }
/* Donker: een schijf eroverheen maakt er een sikkel van. */
.cms-thema[data-thema="DONKER"]::before {
  background: currentColor;
  box-shadow: inset -0.28rem -0.1rem 0 0 var(--panel);
  border-color: transparent;
}
/* Systeem: half. De derde stand hoort ook zichtbaar te zijn, anders lijkt de
   knop twee standen te hebben en is "volg het systeem" onvindbaar. */
.cms-thema[data-thema="SYSTEEM"]::before {
  background: linear-gradient(90deg, currentColor 50%, transparent 50%);
}

.cms-werkplek,
.cms-inloggen { color: var(--brand); text-decoration: none; white-space: nowrap; }
.cms-werkplek:hover,
.cms-inloggen:hover { text-decoration: underline; }
.cms-werkplek[hidden],
.cms-inloggen[hidden] { display: none; }

/* --- Het scrollvlak (D54) ----------------------------------------------
   Alper, 16-10-2026: *"die [scrollbar] gaat over de hele pagina, inclusief
   menu. Als ik switch van contact -> kwetsbaarheden melden verschuift het menu
   omdat er een scrollbar bij komt."* Daarom scrolt op de publieke site het
   VENSTER niet meer: de body is een vast kader van `100dvh` (niet `vh`: dat is
   op een telefoon de hoogte zonder adresbalk) met de kop in de eerste rij en
   het vlak `.cms-rol` in de tweede. De scrollbalk begint dus onder de kop, en
   `scrollbar-gutter: stable` houdt zijn goot ook op een korte pagina vast, zodat
   een wissel niets opzij schuift. Alleen het SITE-kanaal: de kennisbank hangt
   aan de werkplekbalk en volgt de werkplek (zie de TICKET in de overdracht van
   site-scrollvlak). Bewaakt door modules/cms/tests/scrollbalk_hoort_bij_het_vlak.h. */
body.cms[data-kanaal="SITE"] {
  --cms-plak: 0px;
  height: 100dvh;
  overflow: hidden;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  /* Eén kolom die nooit breder wordt dan het venster: zonder dit trok een menu
     dat niet afbreekt (D57) de kolom op tot zijn eigen breedte. */
  grid-template-columns: minmax(0, 1fr);
}

.cms-rol {
  /* `clip` naast `auto` wordt `hidden`: het D1-vangnet, nu op het vlak. */
  overflow-x: clip;
  overflow-y: auto;
  scrollbar-gutter: stable;
}
/* Het vlak krijgt focus alleen om scrolltoetsen te ontvangen (cmsSite.js);
   een rand om de hele pagina zegt dan niets. */
.cms-rol:focus { outline: none; }

/* Op papier is er geen venster: alles weer onder elkaar, anders print alleen
   wat er in beeld staat. */
@media print {
  body.cms[data-kanaal="SITE"] {
    height: auto;
    overflow: visible;
    display: block;
  }
  .cms-rol { overflow: visible; }
}

/* --- De indeling -------------------------------------------------------- */

.cms-blad {
  max-width: 84rem;
  margin-inline: auto;
  padding-inline: 1.25rem;
  display: grid;
  grid-template-columns: var(--cms-zij) minmax(0, 1fr);
  gap: 3rem;
  align-items: start;
}

.cms-zijkant {
  position: sticky;
  top: calc(var(--cms-plak) + 1.5rem);
  padding-block: 2rem;
  max-block-size: calc(100dvh - var(--cms-kop-hoog) - 2rem);
  overflow-y: auto;
}

.cms-zijkop {
  margin: 0 0 0.75rem;
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}

.cms-boom, .cms-boom ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* De inspringing van een tak. Eén regel, want de boom is recursief. */
.cms-boom ul {
  margin-inline-start: 0.7rem;
  padding-inline-start: 0.7rem;
  border-inline-start: 1px solid var(--line);
}

.cms-boom a {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.5rem;
  margin-block: 1px;
  border-radius: 0.4rem;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.875rem;
  line-height: 1.35;
  transition: background-color 120ms, color 120ms;
}
.cms-boom a:hover {
  background: color-mix(in oklch, var(--ink) 5%, transparent);
  color: var(--ink);
}
.cms-boom a[aria-current="page"] {
  background: color-mix(in oklch, var(--brand) 12%, transparent);
  color: var(--brand);
  font-weight: 600;
}

/* Het slotje in het menu. Puur een label: de tekst van die pagina is niet
   verstuurd, dus er valt hier niets te verbergen. */
.cms-boom a[data-slot="true"]::after {
  content: "";
  inline-size: 0.55rem;
  block-size: 0.7rem;
  margin-inline-start: auto;
  border: 1.5px solid currentColor;
  border-block-start-width: 0;
  border-radius: 0 0 0.15rem 0.15rem;
  box-shadow: inset 0 0.18rem 0 -0.1rem currentColor;
  opacity: 0.55;
}

.cms-leeg {
  font-size: 0.8125rem;
  color: var(--muted);
  line-height: 1.5;
}

/* --- De inhoud ---------------------------------------------------------- */

.cms-inhoud {
  padding-block: 2rem 5rem;
  min-inline-size: 0;
}

.cms-kruimels {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-block-end: 1.25rem;
  font-size: 0.8125rem;
  color: var(--muted);
}
.cms-kruimels a { color: var(--muted); text-decoration: none; }
.cms-kruimels a:hover { color: var(--ink); }
.cms-kruimels span[aria-hidden] { opacity: 0.5; }

.cms-artikel { max-inline-size: var(--cms-lees); }

.cms-artikelkop h1 {
  margin: 0;
  font-size: clamp(1.9rem, 1.3rem + 2vw, 2.75rem);
  line-height: 1.1;
  letter-spacing: -0.03em;
  /* Voorkomt een laatste regel met één woord erop. */
  text-wrap: balance;
}

.cms-teaser {
  margin: 0.75rem 0 0;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--muted);
  text-wrap: pretty;
}

/* --- De tekst zelf ------------------------------------------------------ */

.cms-tekst {
  margin-block-start: 2.5rem;
  font-size: 1rem;
  line-height: 1.75;
  /* EEN LANG WOORD BREEKT AF (D1, 14-09-2026). Een pad of productcode zonder
     afbreekpunt rekte de pagina op een telefoon op tot een zijwaartse scroll
     (393 px op 390); zie modules/cms/tests/tekst_breekt_lange_woorden.h. */
  overflow-wrap: break-word;
}
.cms-tekst > :first-child { margin-block-start: 0; }

.cms-tekst h1, .cms-tekst h2, .cms-tekst h3 {
  margin-block: 2.5rem 0.75rem;
  line-height: 1.25;
  letter-spacing: -0.02em;
  text-wrap: balance;
  /* De eerste kop van een artikel herhaalt vaak de titel; die zetten we niet
     nog een keer groot neer. */
  scroll-margin-block-start: calc(var(--cms-plak) + 1rem);
}
.cms-tekst h1 { font-size: 1.6rem; }
.cms-tekst h2 { font-size: 1.3rem; }
.cms-tekst h3 { font-size: 1.0625rem; color: var(--muted); }

.cms-tekst p { margin-block: 0 1.15rem; text-wrap: pretty; }
.cms-tekst ul, .cms-tekst ol { margin-block: 0 1.15rem; padding-inline-start: 1.3rem; }
.cms-tekst li { margin-block-end: 0.4rem; }

.cms-tekst a { color: var(--brand); text-underline-offset: 0.2em; }

.cms-tekst strong { color: var(--ink); font-weight: 650; }

.cms-tekst code {
  /* Een pad als core/network/tests/x.h heeft geen woordgrens die een browser herkent. */
  overflow-wrap: anywhere;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.875em;
  padding: 0.1em 0.35em;
  border-radius: 0.3rem;
  background: color-mix(in oklch, var(--ink) 8%, transparent);
}

.cms-tekst blockquote {
  margin: 1.5rem 0;
  padding: 0.9rem 1.25rem;
  border-inline-start: 3px solid var(--brand);
  border-radius: 0 var(--cms-radius) var(--cms-radius) 0;
  background: color-mix(in oklch, var(--brand) 7%, transparent);
  color: var(--ink);
}
.cms-tekst blockquote p:last-child { margin-block-end: 0; }

/* Sectie en knop uit de blokken (BLOKKEN.md sectie 7): twee vaste klassen, geen
   vrij invulbare. Alleen tokens, dus het thema van de site kleurt ze mee. */
.cms-sectie {
  margin-block: 0 2rem;
  padding: 1.5rem 1.25rem;
  border-radius: var(--cms-radius);
  background: color-mix(in oklch, var(--brand) 6%, transparent);
}
.cms-sectie > :last-child { margin-block-end: 0; }
.cms-tekst a.cms-knop {
  display: inline-block;
  padding: 0.7rem 1.4rem;
  border-radius: var(--cms-radius);
  background: var(--brand);
  color: var(--brand-ink);
  font-weight: 600;
  text-decoration: none;
}

/* Een tabel mag de leeskolom nooit oprekken; hij schuift in zijn eigen doos, en
   alleen als hij niet past (D39). `contain: inline-size`: zie `.table-wrap` in
   `app.css` - zonder die regel rekt de tabel zijn ouders op in plaats van te
   schuiven. */
.cms-tabelwikkel {
  contain: inline-size;
  max-inline-size: 100%;
  overscroll-behavior-inline: contain;
  overflow-x: auto;
  margin-block: 0 1.5rem;
  border: 1px solid var(--line);
  border-radius: var(--cms-radius);
}
/* MAAR NIET IN EEN TABEL. Daar schuift de doos hierboven, en `anywhere` brak een
   veldnaam per letter over drie regels (gemeten 14-09-2026, 390 px). */
.cms-tabelwikkel code {
  overflow-wrap: normal;
  white-space: nowrap;
}
.cms-tekst table {
  inline-size: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}
.cms-tekst th, .cms-tekst td {
  padding: 0.6rem 0.9rem;
  text-align: start;
  border-block-end: 1px solid var(--line);
}
.cms-tekst th {
  background: color-mix(in oklch, var(--ink) 4%, transparent);
  font-weight: 600;
  font-size: 0.8125rem;
}
.cms-tekst tbody tr:last-child td { border-block-end: 0; }

/* --- De op-slot-stand --------------------------------------------------- */

.cms-slot {
  display: flex;
  gap: 1.1rem;
  margin-block-start: 2.5rem;
  padding: 1.5rem;
  border: 1px solid color-mix(in oklch, var(--color-accent) 35%, var(--line));
  border-radius: var(--cms-radius);
  background: color-mix(in oklch, var(--color-accent) 8%, transparent);
}
/* EN HIJ MOET OOK WEG KUNNEN. `cmsSite.js` stuurt dit paneel met
   `el.slot.hidden`, en de schil draagt het attribuut al bij het laden. Zonder
   deze regel deed dat niets: `[hidden] { display: none }` komt uit de
   UA-stylesheet, en de `display: flex` hierboven is een AUTEURSregel - die wint
   ongeacht specificiteit. Het paneel "Deze pagina is op slot" stond daardoor
   onder ELKE gewone pagina. Zie modules/cms/tests/hidden_wordt_niet_overstemd.h. */
.cms-slot[hidden] { display: none; }
.cms-slot h2 { margin: 0 0 0.5rem; font-size: 1.0625rem; }
.cms-slot p { margin: 0 0 1rem; color: var(--muted); line-height: 1.6; }
.cms-slot-noot { font-size: 0.875rem; }

.cms-slot-teken {
  flex: 0 0 auto;
  inline-size: 1.1rem;
  block-size: 1.4rem;
  margin-block-start: 0.15rem;
  border: 2px solid var(--color-accent);
  border-block-start-width: 0;
  border-radius: 0 0 0.2rem 0.2rem;
  box-shadow: inset 0 0.35rem 0 -0.2rem var(--color-accent);
}

.cms-knop {
  font: inherit;
  font-size: 0.875rem;
  padding: 0.45rem 1rem;
  border: 1px solid var(--line);
  border-radius: 0.45rem;
  background: var(--panel);
  color: var(--ink);
  cursor: pointer;
}
.cms-knop:hover { border-color: var(--color-accent); }

.cms-fout {
  padding: 1rem 1.25rem;
  border: 1px solid var(--line);
  border-radius: var(--cms-radius);
  background: var(--panel);
  color: var(--muted);
}

/* --- Voet --------------------------------------------------------------- */

/* DE VOET VOLGT DE INDELING VAN BOL EN AMAZON (29-09-2026): kolommen met een
   kop en links, daaronder een smalle balk met de juridische links, het
   copyright en de versie. De kolommen breken vanzelf af op een smal scherm. */
.cms-voet {
  max-width: 84rem;
  margin-inline: auto;
  padding: 2.5rem 1.25rem 3rem;
  font-size: 0.8125rem;
  color: var(--muted);
  border-block-start: 1px solid var(--line);
}
.cms-voetkolommen {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 16rem));
  justify-content: start;
  gap: 2rem;
  padding-block-end: 2rem;
}
.cms-voetkolommen[hidden],
.cms-voetlinks[hidden] {
  display: none;
}
.cms-voetkolom h2 {
  margin: 0 0 0.75rem;
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--ink);
}
.cms-voetkolom ul,
.cms-voetlinks ul {
  margin: 0;
  padding: 0;
  list-style: none;
}
.cms-voetkolom li + li {
  margin-block-start: 0.5rem;
}
.cms-voetkolom a,
.cms-voetlinks a {
  color: inherit;
  text-decoration: none;
}
.cms-voetkolom a:hover,
.cms-voetlinks a:hover {
  color: var(--ink);
  text-decoration: underline;
}
.cms-voetbalk {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1.5rem;
}
.cms-voetkolommen:not([hidden]) + .cms-voetbalk {
  padding-block-start: 1.25rem;
  border-block-start: 1px solid var(--line);
}
.cms-voetlinks ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.25rem;
}
.cms-voet code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.9em;
}

/* HET MERKTEKEN IN DE VOETREGEL (D42).
   Het teken hoort AAN de regel vast en niet als derde los blok in de voet,
   dus mark en tekst zitten samen in een eigen doos met een krappere tussenmaat
   dan de `gap` van de voet zelf. */
.cms-voet-powered {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

/* EEN MASKER EN GEEN `<img>`, en dat is de enige vorm die hier werkt.
   Het monobestand is getekend in `currentColor`; via een `<img>` levert dat
   ZWART op, want zo'n beeld krijgt zijn eigen documentboom en daar is geen
   kleur om te erven. Een masker leest alleen het alfakanaal, en de kleur komt
   van `currentColor` hieronder. Het teken is met opzet een SILHOUET en niet het
   merk met zijn dekkingen: op deze maat lopen die samen tot een veeg. Zie
   core/network/gateway_web/merk/MERK.md. Het teken heeft daarmee per constructie exact het
   contrast van de voetregel zelf - en dat is wat D42 eist, in beide standen.

   `../merk/` en niet `/merk/`: relatief aan deze stylesheet, zodat het pad ook
   klopt waar de schil onder een ander voorvoegsel wordt uitgeleverd. Zie de
   gelijkluidende regel in de kernstylesheet.

   DE TERUGVAL IS ONTWORPEN (D32): zonder maskerondersteuning verschijnt er
   GEEN teken. Anders zou `background: currentColor` een dicht vierkantje in de
   voet zetten, en dat is erger dan geen merk - de regel "Powered by StoreBOS"
   staat er dan nog steeds voluit. */
.cms-voet-merk { display: none; }

@supports (mask-image: url("")) {
  .cms-voet-merk {
    display: block;
    inline-size: 1.5em;
    block-size: 1.5em;
    flex: 0 0 auto;
    background: currentColor;
    mask-image: url("../merk/storebos-merk-mono.svg");
    mask-size: contain;
    mask-position: center;
    mask-repeat: no-repeat;
  }
}

/* --- Smal scherm --------------------------------------------------------- */

@media (max-width: 960px) {
  .cms-blad {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }
  .cms-zijkant {
    position: static;
    max-block-size: none;
    padding-block: 1.25rem 0;
    border-block-end: 1px solid var(--line);
  }
  .cms-wie .cms-rollen { display: none; }
}

/* Wie beweging heeft uitgezet, krijgt geen overgangen. */
@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.01ms !important; }
}

/* ==========================================================================
   DE VERKOOPPAGINA: hero, kaarten, carrousel en het menu in de kop.

   WAAROM (28-09-2026). Alper, over onze eigen site: "Hoe kan ik een state of
   the art tech solution verkopen als de site er zo duf uit ziet." Een site op
   dit CMS had één vorm - een zijmenu en een artikel - en daarmee las elke
   pagina als documentatie. De blokken kennen sinds CMSHERO-1 drie
   sectievarianten (`modules/cms/tests/sectie_varianten.h`); hier krijgen ze
   hun opmaak. HIER en niet in een eigen bestand: de publieke zone heeft één
   stylesheet (`modules/cms/tests/public_zone.h`). Een klantsite zonder hero,
   kaarten of carrousel merkt er niets van.

   ALLES UIT DE TOKENS, dus de hero van een bloemist is roze en de onze blauw.
   Het maskerverloop van het raster gebruikt `--ink` alleen om zijn dekking.
   ========================================================================== */

/* --- Het menu in de kop -------------------------------------------------
   `site-indeling: kop` in het thema (cmsSite.js zet het als data-indeling op
   <body>). Een verkoopsite heeft vijf pagina's en geen boom; een zijmenu maakt
   er een handleiding van. De boom blijft dezelfde markup, alleen liggend. */
body.cms[data-indeling="kop"] .cms-blad {
  display: block;
  max-width: none;
  padding-inline: 0;
}
body.cms[data-indeling="kop"] .cms-zijkant {
  position: sticky;
  top: var(--cms-plak);
  z-index: 4;
  max-block-size: none;
  overflow: visible;
  padding-block: 0;
  border-block-end: 1px solid var(--line);
  background: color-mix(in oklch, var(--panel) 85%, transparent);
  backdrop-filter: blur(12px);
}
body.cms[data-indeling="kop"] .cms-zijkop,
body.cms[data-indeling="kop"] .cms-kruimels { display: none; }
body.cms[data-indeling="kop"] .cms-boom {
  max-width: var(--breedte);
  margin-inline: auto;
  padding: 0.4rem 1.25rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.5rem;
}
body.cms[data-indeling="kop"] .cms-boom[hidden],
body.cms[data-indeling="kop"] .cms-boom ul { display: none; }
body.cms[data-indeling="kop"] .cms-boom a { font-size: 0.9375rem; padding: 0.4rem 0.75rem; }
body.cms[data-indeling="kop"] .cms-inhoud {
  max-width: var(--breedte);
  margin-inline: auto;
  padding: 2rem 1.25rem 5rem;
}
/* DE VOET VOLGT DEZELFDE BREEDTE ALS `.cms-inhoud` HIERBOVEN (VOETKOLOM-3,
   01-10-2026). Zonder dit bleef `.cms-voet` op zijn vaste 84rem staan, breder
   dan `var(--breedte)` van de inhoud - op de huissite begon de voet zo'n
   80px verder naar links dan de tekst erboven. */
body.cms[data-indeling="kop"] .cms-voet {
  max-width: var(--breedte);
}
body.cms[data-indeling="kop"] .cms-artikel { max-inline-size: none; }
body.cms[data-indeling="kop"] .cms-artikel > .cms-artikelkop,
body.cms[data-indeling="kop"] .cms-tekst > :not(.cms-sectie) {
  max-inline-size: var(--cms-lees);
  margin-inline: auto;
}

/* EEN PAGINA MET EEN HERO HEEFT ZIJN TITEL AL. De artikelkop staat er nog
   voor crawlers en schermlezers die de schil zonder JS lezen - de server vult
   hem - maar in beeld herhaalt hij alleen wat de hero groter zegt. */
.cms-artikel:has(.cms-sectie-hero) .cms-artikelkop { display: none; }
.cms-artikel:has(.cms-sectie-hero) .cms-tekst { margin-block-start: 0; }

/* --- Hero ---------------------------------------------------------------- */

.cms-tekst .cms-sectie-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  margin-block-end: 3rem;
  padding: clamp(3rem, 2rem + 6vw, 7rem) clamp(1.25rem, 5vw, 4rem);
  border-radius: calc(var(--cms-radius) * 2);
  background:
    radial-gradient(60% 80% at 15% 10%, color-mix(in oklch, var(--brand) 55%, transparent), transparent 70%),
    radial-gradient(50% 70% at 90% 90%, color-mix(in oklch, var(--color-accent) 45%, transparent), transparent 70%),
    color-mix(in oklch, var(--ink) 92%, var(--brand));
  color: var(--panel-vast);
  text-align: center;
}
/* Het raster dat traag onder de tekst door schuift: het "technologie"-beeld
   zonder een foto van een laptop. Twee lineaire verlopen, geen afbeelding, dus
   geen extra verzoek en geen externe bron. */
.cms-tekst .cms-sectie-hero::before {
  content: "";
  position: absolute;
  inset: -50%;
  z-index: -1;
  background-image:
    linear-gradient(color-mix(in oklch, var(--panel-vast) 9%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in oklch, var(--panel-vast) 9%, transparent) 1px, transparent 1px);
  background-size: 3rem 3rem;
  transform: perspective(40rem) rotateX(55deg);
  mask-image: radial-gradient(closest-side, var(--ink), transparent);
  animation: landing-raster 24s linear infinite;
}
/* De gloed die rondzweeft, een tweede laag zodat het nooit stilstaat. */
.cms-tekst .cms-sectie-hero::after {
  content: "";
  position: absolute;
  inline-size: 28rem;
  block-size: 28rem;
  inset-block-start: -10rem;
  inset-inline-start: 50%;
  z-index: -1;
  border-radius: 50%;
  background: radial-gradient(closest-side, color-mix(in oklch, var(--brand) 60%, transparent), transparent);
  filter: blur(2rem);
  animation: landing-gloed 16s ease-in-out infinite alternate;
}
@keyframes landing-raster {
  to { background-position: 0 3rem, 3rem 0; }
}
@keyframes landing-gloed {
  from { transform: translate(-80%, 0); }
  to { transform: translate(-20%, 8rem); }
}

/* HET LOGO IN DE HERO. De algemene beeldregel verderop (`article img`) is voor
   een FOTO in een tekstkolom: hij snijdt bij (`cover`), rondt hoeken af en kapt
   op 26rem. Geen van die drie hoort bij een logo - een merk wordt niet
   bijgesneden en heeft geen hoeken. Vandaar dat deze regel ze alle drie
   terugdraait en de maat op de BREEDTE zet, zodat de verhouding van het bestand
   leidend blijft. */
.cms-tekst .cms-sectie-hero img {
  inline-size: min(24rem, 80%);
  block-size: auto;
  max-block-size: none;
  margin: 0 auto 2.25rem;
  border-radius: 0;
  object-fit: contain;
}

.cms-tekst .cms-sectie-hero h1 {
  margin: 0 auto 1rem;
  max-inline-size: 18ch;
  font-size: clamp(2.2rem, 1.4rem + 4vw, 4.25rem);
  line-height: 1.05;
  letter-spacing: -0.04em;
  color: inherit;
}
.cms-tekst .cms-sectie-hero p {
  margin-inline: auto;
  max-inline-size: 36rem;
  font-size: clamp(1.05rem, 1rem + 0.4vw, 1.3rem);
  color: color-mix(in oklch, var(--panel-vast) 80%, transparent);
}
.cms-tekst .cms-sectie-hero a.cms-knop {
  margin-block-start: 0.75rem;
  padding: 0.9rem 2rem;
  border-radius: var(--radius-pil);
  font-size: 1.0625rem;
  background: var(--actie);
  box-shadow: 0 0.75rem 2rem -0.75rem var(--actie);
  transition: transform 160ms ease;
}
.cms-tekst .cms-sectie-hero a.cms-knop:hover { transform: translateY(-2px); }

/* --- Kaarten --------------------------------------------------------------- */

.cms-tekst .cms-sectie-kaarten {
  margin-block-end: 3rem;
  padding: 0;
  background: none;
  text-align: center;
}
.cms-kaarten {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: 1rem;
  margin-block-start: 1.5rem;
  text-align: start;
}
.cms-kaart {
  padding: 1.5rem;
  border: 1px solid var(--line);
  border-radius: calc(var(--cms-radius) * 1.5);
  background: var(--panel);
  transition: transform 160ms ease, border-color 160ms ease;
}
.cms-kaart:hover {
  transform: translateY(-3px);
  border-color: color-mix(in oklch, var(--brand) 50%, var(--line));
}
.cms-tekst .cms-kaart h3 {
  margin-block: 0 0.5rem;
  font-size: 1.125rem;
  color: var(--ink);
}
.cms-tekst .cms-kaart h3::before {
  content: "";
  display: block;
  inline-size: 2.25rem;
  block-size: 2.25rem;
  margin-block-end: 0.9rem;
  border-radius: 0.6rem;
  background: linear-gradient(135deg, var(--brand), var(--color-accent));
}
.cms-kaart > :last-child { margin-block-end: 0; }

/* --- Definities (cms-geen-definitielijst-blok) -----------------------------
   Een vraag/antwoord- of tag/toelichting-lijst: de koptekst (`<dt>`) draagt de
   nadruk, de toelichting (`<dd>`) staat kleiner eronder - geen tabel met drie
   gelijkwaardige kolommen. */
.cms-tekst .cms-sectie-definities {
  padding: 0;
  background: none;
}
.cms-definities {
  display: grid;
  gap: 1.25rem;
  margin: 0;
}
.cms-tekst .cms-definities dt {
  font-weight: 600;
  color: var(--ink);
}
.cms-tekst .cms-definities dd {
  margin-inline-start: 0;
  margin-block-start: 0.35rem;
  color: var(--muted);
  font-size: 0.95rem;
}
.cms-definities dd > :last-child { margin-block-end: 0; }

/* --- Carrousel ------------------------------------------------------------
   ALLE DIA'S IN DEZELFDE RASTERCEL, dus ze liggen op elkaar en de hoogste
   bepaalt de maat. Geen zijwaarts schuivend vak (D39): er schuift niets, er
   vervaagt er een en er komt er een op. Zonder JavaScript staat de eerste er
   (`siteCarrousel.js` zet `is-actief`; zonder die klasse is de eerste zichtbaar). */
.cms-tekst .cms-sectie-carrousel {
  margin-block-end: 3rem;
  padding: 0;
  background: none;
}
.cms-dias { display: grid; }
.cms-dia {
  grid-area: 1 / 1;
  opacity: 0;
  transform: scale(0.98);
  transition: opacity 700ms ease, transform 700ms ease;
}
.cms-dias:not(:has(.is-actief)) > .cms-dia:first-child,
.cms-dia.is-actief {
  opacity: 1;
  transform: none;
}
.cms-tekst .cms-dia p { margin: 0; }
.cms-dia img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  border: 1px solid var(--line);
  border-radius: calc(var(--cms-radius) * 1.5);
  box-shadow: 0 1.5rem 3rem -1.5rem color-mix(in oklch, var(--ink) 45%, transparent);
}
/* De pijlen liggen in DEZELFDE rastercel als de dia's, tegen de linker- en
   rechterrand en verticaal in het midden: over het beeld heen, zonder omhulsel.
   Een rond glazen knopje (paneel half doorzichtig plus blur) zodat hij op een
   licht en een donker beeld allebei leesbaar is. Met een muis komen ze pas
   tevoorschijn als je over de carrousel gaat; op een touchscherm staan ze er
   altijd, want daar is geen hover. */
.cms-dias { touch-action: pan-y; }
.cms-dia-pijl {
  grid-area: 1 / 1;
  align-self: center;
  z-index: 1;
  display: grid;
  place-items: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  margin-inline: 0.85rem;
  padding: 0;
  border: 1px solid color-mix(in oklch, var(--ink) 12%, transparent);
  border-radius: 50%;
  background: color-mix(in oklch, var(--panel) 72%, transparent);
  -webkit-backdrop-filter: blur(10px) saturate(1.4);
  backdrop-filter: blur(10px) saturate(1.4);
  color: var(--ink);
  box-shadow: 0 0.6rem 1.4rem -0.6rem color-mix(in oklch, var(--ink) 55%, transparent);
  cursor: pointer;
  transition: opacity 280ms ease, transform 280ms cubic-bezier(0.2, 0.8, 0.2, 1),
    background-color 200ms ease, color 200ms ease, border-color 200ms ease;
}
.cms-dia-pijl-vorig { justify-self: start; }
.cms-dia-pijl-volgend { justify-self: end; }
.cms-dia-pijl svg {
  inline-size: 1.3rem;
  block-size: 1.3rem;
  transition: transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.cms-dia-pijl-vorig svg { scale: -1 1; }
.cms-dia-pijl:hover {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-ink);
  transform: scale(1.08);
}
.cms-dia-pijl-volgend:hover svg { transform: translateX(2px); }
.cms-dia-pijl-vorig:hover svg { transform: translateX(-2px); }
.cms-dia-pijl:active { transform: scale(0.94); }
.cms-dia-pijl:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 3px;
}
@media (hover: hover) and (pointer: fine) {
  .cms-dia-pijl { opacity: 0; }
  .cms-dia-pijl-vorig { transform: translateX(-0.5rem); }
  .cms-dia-pijl-volgend { transform: translateX(0.5rem); }
  .cms-sectie-carrousel:hover .cms-dia-pijl,
  .cms-sectie-carrousel:focus-within .cms-dia-pijl {
    opacity: 1;
    transform: none;
  }
  .cms-sectie-carrousel .cms-dia-pijl:hover { transform: scale(1.08); }
}
@media (max-width: 600px) {
  .cms-dia-pijl { inline-size: 2.25rem; block-size: 2.25rem; margin-inline: 0.5rem; }
  .cms-dia-pijl svg { inline-size: 1.1rem; block-size: 1.1rem; }
}

/* De stippen. De actieve wordt een pil, en in die pil loopt een balk vol tot
   het volgende beeld (`--cms-dia-duur` uit `siteCarrousel.js`). Staat hij stil
   - muis erop, focus erin, of beweging uit - dan is de pil gewoon vol. */
.cms-dia-stippen {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
  margin-block-start: 1rem;
}
.cms-dia-stip {
  position: relative;
  overflow: hidden;
  inline-size: 0.6rem;
  block-size: 0.6rem;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--line);
  cursor: pointer;
  transition: inline-size 350ms cubic-bezier(0.2, 0.8, 0.2, 1), background-color 200ms ease;
}
.cms-dia-stip:hover { background: color-mix(in oklch, var(--brand) 45%, var(--line)); }
.cms-dia-stip[aria-current="true"] {
  inline-size: 1.75rem;
  background: color-mix(in oklch, var(--brand) 28%, var(--line));
}
.cms-dia-stip[aria-current="true"]::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--brand);
  transform-origin: left center;
}
.cms-dia-stippen.is-lopend .cms-dia-stip[aria-current="true"]::after {
  animation: cms-dia-loop var(--cms-dia-duur, 4500ms) linear both;
}
@keyframes cms-dia-loop {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

/* --- Smal scherm --------------------------------------------------------- */

@media (max-width: 600px) {
  .cms-tekst .cms-sectie-hero { border-radius: 0; margin-inline: -1.25rem; }
  .cms-kaart { border-radius: 0; border-inline: 0; margin-inline: -1.25rem; }
  body.cms[data-indeling="kop"] .cms-boom { justify-content: center; }
}

/* ==========================================================================
   KOP EN VOET OP EEN TELEFOON (DESIGNREGELS.md D55 en D56, Alper 22-10-2026).

   "er is een header regel waar storebos (webshop & CMS staat); daaronder 4
   regels voor het menu. dat is al 40% van mijn scherm. [...] de footer is ook
   bijna een heel scherm". Gemeten op 384 x 832: kop 57 px plus een menu van
   166 px dat bleef plakken, en een voet van 540 px.

   DE KOP is één regel: de naam links, de knop "Menu" rechts. Het menu zelf
   (`.cms-zijkant`, in beide indelingen) staat niet meer in de pagina maar
   opent als lade onder de kop over het hele scherm (D15). Ook in liggende
   stand (500 px hoog): daar is de hoogte schaars (D17). Alleen het SITE-
   kanaal: de kennisbank heeft geen menuknop en houdt zijn boom.

   DE VOET zet elke kolom als uitklapper (`siteVoet.js` maakt er `<details>`
   van) en houdt de juridische links open. Op een breed scherm staat elke
   kolom open en is de kop gewoon een kop.

   Het budget dat dit haalt, staat in `scripts/breedteproef-hoogte.mjs`; de
   proef meet het in een echte browser (`core/tests/breedteproef_zelfproef.mjs`,
   de echte schil op 320, 390 en 430 px).
   ========================================================================== */
.cms-menuknop { display: none; }

.cms-voetkolom summary {
  list-style: none;
  pointer-events: none;
}
.cms-voetkolom summary::-webkit-details-marker { display: none; }

/* DE VORM VAN HET MENU KIEST `siteMenuLade.js` en zet hij als
   `data-menuvorm` op <body>: `lade` op een telefoon (onder 600 px breed of
   500 px hoog) en in de kop-indeling als het menu niet op de regel past;
   `rij` als het wel past (D57, hieronder). Daarom hangen deze regels aan dat
   attribuut en niet aan een media query: de meting "past het" kan CSS niet. */
body.cms[data-kanaal="SITE"][data-menuvorm="lade"] .cms-menuknop {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex: 0 0 auto;
  min-block-size: 44px;
  min-inline-size: 44px;
  padding: 0 0.85rem;
  font: inherit;
  font-size: 0.9375rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
}
.cms-menuknop svg { inline-size: 1.15rem; block-size: 1.15rem; }

body.cms[data-kanaal="SITE"][data-menuvorm="lade"] .cms-zijkant {
  display: none;
}
body.cms[data-kanaal="SITE"][data-menuvorm="lade"][data-menu="open"] .cms-zijkant {
  display: block;
  position: fixed;
  inset: var(--cms-kop-hoog) 0 0;
  z-index: 6;
  max-block-size: none;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0.5rem 1.25rem 2rem;
  border: 0;
  background: var(--panel-vast);
  backdrop-filter: none;
}
body.cms[data-kanaal="SITE"][data-menuvorm="lade"] .cms-zijkant .cms-boom {
  display: block;
  max-width: none;
  padding: 0;
}
body.cms[data-kanaal="SITE"][data-menuvorm="lade"] .cms-zijkant .cms-boom a {
  min-block-size: 48px;
  margin: 0;
  padding: 0 0.5rem;
  font-size: 1.0625rem;
  border-radius: 0;
  border-block-end: 1px solid var(--line);
}
/* Een submenu staat in de lade open, ingesprongen onder zijn ouder. */
body.cms[data-kanaal="SITE"][data-menuvorm="lade"] .cms-zijkant .cms-boom ul {
  display: block;
  padding-inline-start: 1.25rem;
}

/* --- KOP EN MENU OP EEN REGEL (D57, Alper 22-10-2026) ---------------------
   "Kop en menubalk op één regel op een breed scherm: merk links, menu in het
   midden, Inloggen/Werkplek rechts." Het menu staat in de kop
   (`siteMenuLade.js` verhuist het) en neemt de ruimte tussen merk en
   `.cms-wie`; de lijst breekt niet af, want of hij past, meet het script. */
body.cms[data-kanaal="SITE"][data-menuvorm="rij"] .cms-kop-binnen { gap: 1rem; }
body.cms[data-kanaal="SITE"][data-menuvorm="rij"] .cms-zijkant {
  position: static;
  flex: 1 1 auto;
  min-inline-size: 0;
  align-self: stretch;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  background: none;
  backdrop-filter: none;
}
body.cms[data-kanaal="SITE"][data-menuvorm="rij"] .cms-boom {
  align-self: stretch;
  align-items: stretch;
  margin: 0;
  padding: 0;
  gap: 0 0.125rem;
  flex-wrap: nowrap;
  white-space: nowrap;
}
body.cms[data-kanaal="SITE"][data-menuvorm="rij"] .cms-boom > li { position: relative; display: flex; align-items: center; }
body.cms[data-kanaal="SITE"][data-menuvorm="rij"] .cms-boom > li > a { font-size: 0.875rem; padding: 0.4rem 0.55rem; }
/* Het submenu klapt onder de kop uit, als de knop open staat. */
body.cms[data-kanaal="SITE"][data-menuvorm="rij"] .cms-boom li > ul {
  position: absolute;
  inset-block-start: 100%;
  inset-inline-start: 0;
  min-inline-size: 14rem;
  padding: 0.35rem;
  background: var(--panel-vast);
  border: 1px solid var(--line);
  border-radius: var(--cms-radius);
  box-shadow: 0 8px 24px color-mix(in oklch, var(--ink) 12%, transparent);
}
body.cms[data-kanaal="SITE"][data-menuvorm="rij"] .cms-boom .cms-klap[aria-expanded="true"] + ul { display: block; }
body.cms[data-kanaal="SITE"][data-menuvorm="rij"] .cms-boom li > ul a { display: block; }

/* De uitklapknop is er alleen in de rij; in de lade en de zijkant staat een
   submenu altijd open. Het pijltje is dezelfde hoek als in de voet. */
.cms-klap { display: none; }
body.cms[data-kanaal="SITE"][data-menuvorm="rij"] .cms-klap {
  display: inline-grid;
  place-items: center;
  inline-size: 1.75rem;
  block-size: 1.75rem;
  margin-inline-start: -0.5rem;
  padding: 0;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}
body.cms[data-kanaal="SITE"][data-menuvorm="rij"] .cms-klap::after {
  content: "";
  inline-size: 0.4rem;
  block-size: 0.4rem;
  border: solid currentColor;
  border-width: 0 1.5px 1.5px 0;
  transform: translateY(-25%) rotate(45deg);
}
body.cms[data-kanaal="SITE"][data-menuvorm="rij"] .cms-klap[aria-expanded="true"]::after {
  transform: translateY(25%) rotate(-135deg);
}
@media (pointer: coarse) {
  body.cms[data-kanaal="SITE"][data-menuvorm="rij"] .cms-klap { inline-size: 44px; block-size: 44px; }
}

@media (max-width: 600px) {
  /* Merk, Inloggen en de menuknop op 320 px: met 1,5rem ertussen kwam de knop
     3 px te ver. */
  .cms-kop-binnen { gap: 0.75rem; }
  .cms-voet,
  body.cms[data-indeling="kop"] .cms-voet {
    padding: 0 1.25rem 0.75rem;
  }
  .cms-voetkolommen {
    display: block;
    padding-block-end: 0;
  }
  .cms-voetkolom { border-block-end: 1px solid var(--line); }
  .cms-voetkolom summary {
    pointer-events: auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-block-size: 44px;
    cursor: pointer;
  }
  .cms-voetkolom h2 { margin: 0; }
  /* Het pijltje: een hoek van twee randen, dicht naar beneden, open naar boven. */
  .cms-voetkolom summary::after {
    content: "";
    inline-size: 0.5rem;
    block-size: 0.5rem;
    margin-inline-end: 0.25rem;
    border: solid currentColor;
    border-width: 0 1.5px 1.5px 0;
    transform: translateY(-25%) rotate(45deg);
  }
  .cms-voetkolom[open] summary::after { transform: translateY(25%) rotate(-135deg); }
  .cms-voetkolom ul { padding-block-end: 0.5rem; }
  .cms-voetkolom li + li { margin-block-start: 0; }
  .cms-voetkolommen:not([hidden]) + .cms-voetbalk {
    padding-block-start: 0.25rem;
    border-block-start: 0;
  }
  .cms-voetbalk,
  .cms-voetlinks ul { gap: 0 1.25rem; }
  /* De 5rem onder de inhoud was op een telefoon een lege band boven de voet. */
  body.cms[data-indeling="kop"] .cms-inhoud { padding-block-end: 2rem; }
}

/* D3: een link in de voet is met een vinger 44 px hoog, en dat hangt aan de
   invoermethode en niet aan de breedte. */
@media (pointer: coarse) {
  .cms-voetkolom a,
  .cms-voetlinks a,
  .cms-voet-powered a,
  .cms-inloggen,
  .cms-werkplek {
    display: inline-flex;
    align-items: center;
    min-block-size: 44px;
  }
}
/* --- einde KOP EN VOET OP EEN TELEFOON (de zelfproef knipt tot hier) --- */

@media (prefers-reduced-motion: reduce) {
  .cms-tekst .cms-sectie-hero::before,
  .cms-tekst .cms-sectie-hero::after { animation: none; }
  .cms-dia, .cms-kaart, .cms-dia-pijl, .cms-dia-pijl svg, .cms-dia-stip { transition: none; }
}

/* ==========================================================================
   DE WINKEL.

   Waarom deze regels hier staan en niet in modules/webshop/web/: de webshop is
   een EXTENSIE van het CMS en houdt de CMS-stylesheet (CLAUDE.md sectie 4,
   zone 2). Een tweede publieke stylesheet is hoe een site er bij de derde
   module uitziet als drie sites.

   Alles komt uit de tokens hierboven. De enige waarden die uit een RIJ komen
   zijn --tint-a en --tint-b: die zet het sjabloon per product in een
   style-attribuut, en ze zijn aan de serverkant door dezelfde keuring gegaan
   als een themawaarde (webshop_catalog.cpp).

   BEWEGING (DOELSTELLING.md 3.4): in CSS en zonder bibliotheek, want er is geen
   bouwstap voor JS en geen CDN. En `prefers-reduced-motion` wordt gerespecteerd
   - beweging die je niet uit kunt zetten is een toegankelijkheidsfout.
   ========================================================================== */

.shop {
  --shop-tegel-hoog: 62vh;
  font-family: var(--site-letter-ui, system-ui, sans-serif);
}

.shop-blad { max-width: var(--breedte); margin: 0 auto; padding: 0 1.25rem 6rem; }

/* --- De kop: bordeaux servicebalk, wit merkblok (Krispo) ----------------- */

.shop-service {
  background: var(--brand);
  color: var(--brand-ink);
  font-size: 0.78rem;
  letter-spacing: var(--tracking);
  text-transform: uppercase;
  text-align: center;
  padding: 0.55rem 1rem;
}

.shop-kop {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--panel);
  border-bottom: 1px solid color-mix(in oklch, var(--line) 70%, transparent);
  backdrop-filter: blur(8px);
}

.shop-kop-binnen {
  max-width: var(--breedte);
  margin: 0 auto;
  padding: 0.9rem 1.25rem;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1rem;
}

.shop-merk { text-align: center; text-decoration: none; color: inherit; display: block; }
.shop-merk-naam {
  display: block;
  font-family: var(--site-letter, Georgia, serif);
  font-size: 1.6rem;
  letter-spacing: var(--tracking);
  color: var(--goud);
}
.shop-merk-sub {
  display: block;
  font-size: 0.66rem;
  letter-spacing: var(--tracking);
  text-transform: uppercase;
  color: var(--muted);
}

.shop-kop-acties { display: flex; gap: 0.4rem; justify-content: flex-end; align-items: center; }
.shop-kop-links { display: flex; gap: 0.4rem; align-items: center; }

/* DE KOP OP EEN TELEFOON, EN HIJ HAD HIER NIETS (D11, D12).
   `.shop-kop-binnen` is `1fr auto 1fr` en had in dit hele bestand geen enkele
   media query. Een `1fr` is `minmax(auto, 1fr)` en die `auto` is de MIN-CONTENT
   van wat erin staat: vier knoppen met een woord erop dat niet kan afbreken,
   plus een merknaam van een woord. Opgeteld ligt de kop rond de 470 px, dus hij
   past op geen enkele telefoon in portret - en omdat hij `position: sticky` is,
   schoof de hele pagina mee. Dat is wat Alper op 08-09-2026 fotografeerde: een
   kop die links buiten beeld begint.

   DE OPLOSSING IS STAPELEN EN LATEN AFBREKEN, en niet krimpen. Kleiner maken zou
   de aanraakdoelen onder de maat duwen die D3 juist eist; hieronder worden ze
   groter. Met `flex-wrap` op beide groepen kan er per definitie geen overloop
   meer ontstaan, ook niet op 320 px en ook niet als er ooit een knop bij komt.

   De breekpunten staan in `px` en niet in `rem` (D2): `rem` betekent in een media
   query altijd 16 px, ook op deze pagina waar de wortel op de themamaat staat. */
@media (max-width: 600px) {
  .shop-kop-binnen {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    gap: 0.5rem;
    padding-inline: 1rem;
  }
  /* De merknaam bovenaan; dat is de gewone volgorde op een telefoon en het houdt
     de knoppen binnen duimbereik. */
  .shop-merk { order: -1; }
  .shop-kop-links,
  .shop-kop-acties {
    flex-wrap: wrap;
    justify-content: center;
    max-width: 100%;
  }
  /* D3: een aanraakdoel is 44 x 44. Met alleen padding haalde `.shop-icoon`
     ongeveer 30 px, en dat voelt op een telefoon als "hij doet het soms niet". */
  .shop-icoon {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}

/* DE TASREGEL OP EEN TELEFOON (D11: één kolom, D1: nooit horizontale scroll).
 *
 * Op 320 px is er na de kop nog ~288 px over. De brede stand vraagt daar
 * beeld (72) + teller (128) + bedrag (70) + twee tussenruimtes naast elkaar op
 * rij twee, en dat past niet - de teller is met D3 erbij (44 px per knop) niet
 * smaller te maken. Dus: het bedrag onder de naam, en de twee handelingen op
 * een eigen rij over de volle breedte. */
@media (max-width: 600px) {
  .shop-wagenregel {
    grid-template-columns: 3.25rem minmax(0, 1fr);
    grid-template-areas:
      "beeld  tekst"
      "beeld  totaal"
      "acties acties";
  }
  .shop-wagenregel .shop-regeltotaal { text-align: start; }
  /* Het somblok vult de breedte; rechts uitlijnen heeft hier geen zin meer. */
  .shop-wagenvoet { justify-content: stretch; }
}

/* D17: WAT VASTGEPLAKT STAAT, TELT OP. Een liggende telefoon is ~330 px hoog en
   deze kop draagt vier rijen (merk, knoppen, categorieen, zoekveld). Plakken
   kost daar de halve pagina, dus onder 500 px hoogte plakt hij niet meer en
   scrollt hij gewoon mee. */
@media (max-height: 500px) {
  .shop-kop { position: static; }
}

.shop-icoon {
  border: 1px solid color-mix(in oklch, var(--line) 80%, transparent);
  background: transparent;
  color: var(--ink);
  border-radius: var(--radius-pil);
  padding: 0.45rem 0.85rem;
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease;
}
.shop-icoon:hover { background: color-mix(in oklch, var(--ink) 6%, transparent); }
/* HET SLOT ONDER `hidden`, EN DE SUITE VROEG ER ZELF OM. De omgevingsbadge in
   `shop.html` is een `<span class="shop-icoon" data-veld="omgeving" hidden>`, en
   zodra deze klasse ergens een eigen `display` krijgt - hieronder in de
   telefoonstand - wint die AUTEURSregel van de `[hidden] { display: none }` uit
   de UA-stylesheet. Dan staat "dev" op productie in beeld zonder dat iets het
   meldt. Dat is MAREE-8, en `modules/cms/tests/hidden_wordt_niet_overstemd.h`
   ving deze poging meteen. */
.shop-icoon[hidden] { display: none; }
.shop-teller {
  display: inline-block;
  min-width: 1.25em;
  margin-inline-start: 0.35em;
  border-radius: var(--radius-pil);
  background: var(--actie);
  color: var(--brand-ink);
  font-size: 0.7rem;
  text-align: center;
}

.shop-menu {
  display: flex;
  gap: 1.25rem;
  justify-content: center;
  padding: 0.6rem 1rem;
  flex-wrap: wrap;
  border-top: 1px solid color-mix(in oklch, var(--line) 55%, transparent);
}
/* DE MENUKNOP MOET DIT WERKELIJK KUNNEN WEGHALEN. `shopSite.js` zet
   `el.menu.toggleAttribute("hidden", ...)` en dat kwam er ook op te staan - er
   veranderde alleen niets, want de `display: flex` hierboven is een
   AUTEURSregel en die wint van de `[hidden] { display: none }` uit de
   UA-stylesheet, ongeacht specificiteit. Dat was MAREE-8: *"menu doet volgens
   mij niks"*. De zoekbalk hieronder werkte wel, met exact dezelfde vijf regels
   JavaScript, puur omdat `.shop-zoek[hidden]` er al stond.
   Vastgepind door modules/cms/tests/hidden_wordt_niet_overstemd.h. */
.shop-menu[hidden] { display: none; }
.shop-menu-item {
  color: var(--muted);
  text-decoration: none;
  font-size: 0.78rem;
  letter-spacing: var(--tracking);
  text-transform: uppercase;
  white-space: nowrap;
  padding-bottom: 0.2rem;
  border-bottom: 2px solid transparent;
}
.shop-menu-item:hover { color: var(--ink); border-bottom-color: var(--goud); }

/* SMAL SCHERM: DE RIJ WIKKELT (D12, D14, D39). Een scroller met
   `justify-content: center` duwt het begin van de rij buiten het scrollbereik,
   en een verborgen scroller laat niet zien dat er meer staat. `flex-wrap` op de
   basisregel laat de items op een telefoon over meer regels lopen; op smal
   scherm alleen wat minder ruimte ertussen. De begintoestand (open, zie
   `modules/webshop/tests/menuknop_toont_de_navigatie.h`) verandert niet. */
@media (max-width: 832px) {
  .shop-menu {
    gap: 0.5rem 0.9rem;
    padding-inline: 1.25rem;
  }
}

/* HET ZOEKVELD STAAT PERMANENT IN DE KOP, DE TOGGLE ALLEEN OP SMAL.
   Tot 04-09-2026 droeg `<form class="shop-zoek">` een `hidden` en was de knop
   `Zoeken` in de kop de enige weg ernaartoe. Zoeken kostte dus drie
   handelingen - klikken, typen, en dan nog een tweede knop `Zoek` - voor wat in
   een winkel het vaakst gebeurt. Alper: *"ik moet op zoeken drukken om de
   zoekbalk te krijgen; met nog een zoekknop; beetje onlogisch"*.

   Het formulier staat nu open bij het laden en de TOGGLE verdwijnt op een breed
   scherm. Dat is met opzet de kant die in de STYLESHEET wordt geregeld: de
   andere kant - het formulier zelf op `display: none` zetten boven 832px - zou
   de begintoestand uit twee bronnen laten komen die het oneens kunnen zijn, en
   dat weigert `modules/cms/tests/hidden_wordt_niet_overstemd.h` terecht. Een
   knop die er niet is, kan niets dichtklappen wat de bezoeker nodig heeft.

   `.shop-zoek[hidden]` BLIJFT staan, en niet als restant: op smal scherm is de
   toggle er wel, en zonder deze regel wint de `display: flex` hierboven van de
   `[hidden] { display: none }` uit de UA-stylesheet. Dat is MAREE-8, en het
   is precies de regel die daar ontbrak bij `.shop-menu`. */
.shop-zoek { display: flex; gap: 0.4rem; padding: 0.6rem 1.25rem; max-width: var(--breedte); margin: 0 auto; }
.shop-zoek[hidden] { display: none; }
.shop-zoek-knop { display: inline-flex; align-items: center; justify-content: center; padding-inline: 0.7rem; }
.shop-zoek-knop svg { width: 1.05rem; height: 1.05rem; }
.shop-zoek-toggle { display: none; }
@media (max-width: 832px) {
  .shop-zoek-toggle { display: inline-block; }
}
.shop-zoek input {
  flex: 1;
  font: inherit;
  padding: 0.6rem 1rem;
  border-radius: var(--radius-pil);
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
}

/* --- Campagnetegels: één ding per scherm --------------------------------- */

.shop-hero-stapel, .shop-tegels { display: grid; gap: 1.25rem; margin: 1.5rem 0; }
/* `min(19rem, 100%)` EN NIET `19rem` (D1). Een kale ondergrens krimpt niet mee:
   19rem is bij de wortelmaat van 17 px 323 px, en met de bladmarge past dat pas
   vanaf 366 px vensterbreedte. Daaronder liep de tegelrij over en schoof de HELE
   pagina zijwaarts - een van de twee bronnen van de melding van 08-09-2026. */
.shop-tegels { grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr)); }

.shop-hero, .shop-tegel {
  position: relative;
  isolation: isolate;
  border-radius: var(--radius);
  overflow: hidden;
  min-height: var(--shop-tegel-hoog);
  display: grid;
  align-items: end;
}
.shop-tegel { min-height: 22rem; }

.shop-hero-vlak, .shop-tegel-vlak {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(120% 90% at 25% 15%, var(--tint-a, var(--bg-diep)) 0%, transparent 60%),
    linear-gradient(160deg, var(--tint-a, var(--bg-diep)), var(--tint-b, var(--brand)));
  transform: scale(1.06);
  animation: shop-adem 18s ease-in-out infinite alternate;
}

@keyframes shop-adem {
  from { transform: scale(1.06) translate3d(0, 0, 0); }
  to   { transform: scale(1.14) translate3d(0, -2%, 0); }
}

.shop-hero-tekst, .shop-tegel-tekst {
  padding: 2rem;
  color: var(--brand-ink);
  text-shadow: 0 1px 12px color-mix(in oklch, var(--ink) 45%, transparent);
  animation: shop-opkomst 700ms ease both;
}

@keyframes shop-opkomst {
  from { opacity: 0; transform: translateY(1.25rem); }
  to   { opacity: 1; transform: none; }
}

.shop-kicker {
  margin: 0 0 0.5rem;
  font-size: 0.7rem;
  letter-spacing: var(--tracking);
  text-transform: uppercase;
  opacity: 0.9;
}
.shop-hero-titel {
  margin: 0 0 0.6rem;
  font-family: var(--site-letter, Georgia, serif);
  font-size: clamp(2.4rem, 7vw, 4.5rem);
  line-height: 1;
  letter-spacing: 0.02em;
}
.shop-hero-lead { margin: 0 0 1.25rem; max-width: 34ch; font-size: 1.05rem; }
.shop-tegel-tekst h3 { margin: 0 0 0.4rem; font-family: var(--site-letter, Georgia, serif); font-size: 1.6rem; }
.shop-tegel-tekst p { margin: 0 0 1rem; max-width: 32ch; }

.shop-pil {
  display: inline-block;
  padding: 0.85rem 1.75rem;
  border-radius: var(--radius-pil);
  background: var(--panel);
  color: var(--ink);
  text-decoration: none;
  font-size: 0.78rem;
  letter-spacing: var(--tracking);
  text-transform: uppercase;
  transition: transform 180ms ease, box-shadow 180ms ease;
}
.shop-pil:hover { transform: translateY(-2px); box-shadow: 0 10px 24px color-mix(in oklch, var(--ink) 25%, transparent); }
/* DE KNOP DIE ALPER MELDDE (08-09-2026). Hier stond `Canvas 88%`, en dat is in
   de donkere stand bijna zwart terwijl `.shop-pil` er `var(--ink)` op zet: een
   zwarte knop met donkere letters, op elke campagnetegel. De knop op de HERO
   ernaast was wel goed, want die gebruikt gewoon `var(--panel)` - twee knoppen
   naast elkaar, een ervan stuk, en dat is precies waarom D26 een systeemkleur
   als WAARDE verbiedt: hij beweegt mee met het toestel en het thema niet. */
.shop-pil-licht { background: color-mix(in oklch, var(--panel) 88%, transparent); }

/* Beeld IN een pagina (`![alt](media:naam)` in de markdown). Een aparte regel
   van de winkelfoto hierboven: die vult een vak met een vaste verhouding, deze
   staat in een tekstkolom en moet zijn EIGEN verhouding houden.

   De hoogtebegrenzing is er omdat een staande foto anders het hele scherm vult
   en de lezer de tekst eronder niet meer vermoedt. */
.cms-inhoud img,
.site-inhoud img,
article img {
  max-width: 100%;
  max-height: 26rem;
  height: auto;
  display: block;
  margin: 1.5rem auto;
  border-radius: var(--radius);
  object-fit: cover;
}

/* --- DE FOTO ------------------------------------------------------------- */
/*
   Eén regel voor elk beeldvlak: hero, tegel, kaart en detail. Ze hebben alle
   vier al een `aspect-ratio` en een verloop als achtergrond, dus een foto hoeft
   alleen die ruimte VOLLEDIG te vullen zonder te vervormen.

   Waarom `object-fit: cover` en niet `contain`: een winkel toont geen witte
   randen. Een foto die niet precies past wordt bijgesneden, en dat is wat elke
   webshop doet - het alternatief is een raster dat per product een andere
   hoogte krijgt.

   De achtergrond eronder BLIJFT staan. Dat is geen restant: hij is wat je ziet
   terwijl de foto nog laadt, en hij is de terugval voor een product zonder
   beeld (punt 7 van DOELSTELLING.md). Zone 2, dus alleen `var(--...)` en geen
   losse kleur.
*/
.shop-foto {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* DE MAAT DIE HET VLAK NIET LIET VULLEN. Hierboven staat een regel voor
     beeld IN een pagina, en die selecteert onder meer op `article img`. Elk
     winkelsjabloon zet zijn hero, tegel en kaart in een <article>, dus die
     regel raakt ELKE `.shop-foto` - en elke maatvoerende eigenschap die deze
     regel zelf niet noemde, bleef daaruit staan:

       `max-height: 26rem`  kapte de foto af op 416 px. In de hero (62vh) bleef
                            daaronder ruim achthonderd pixels kaal verloop
                            staan; op een kaart (3/4 van 19rem) een paar
                            tientallen pixels, en dat leest als een rand.
       `margin: 1.5rem auto` duwde hem daarbovenop nog 24 px omlaag.
       `max-width: 100%`    deed vandaag niets (de breedte is al 100%) en staat
                            er toch bij: overerven wat je zelf hoort te bepalen
                            is de fout, niet de waarde die er nu toevallig uit
                            komt.

     De tegel was het geval dat het verborg: die is 22rem hoog, dus onder de
     grens, en daar zag het er goed uit. Dit blijft dus expliciet staan ook als
     de regel hierboven ooit anders wordt gescopeerd - een beeldvlak bepaalt
     zijn eigen maat (todo/webshop.md, P3). */
  max-height: none;
  max-width: none;
  margin: 0;
  /* De hero en de tegel hebben tekst OVER het beeld heen. Zonder dit is een
     lichte foto met witte letters onleesbaar - en dat is niet te zien zolang
     de demo toevallig donkere foto's heeft. */
  filter: saturate(0.95);
}
.shop-hero-vlak .shop-foto,
.shop-tegel-vlak .shop-foto { opacity: 0.55; }

/* --- Rooster en kaarten -------------------------------------------------- */

.shop-sectie { margin: 3.5rem 0; }
.shop-sectiekop { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: 1.25rem; }
.shop-sectiekop h2 { margin: 0; font-family: var(--site-letter, Georgia, serif); font-size: 1.9rem; }
.shop-tel { margin: 0; color: var(--muted); font-size: 0.85rem; }

/* DE PRIJZEN OP EEN LIJN, EN DAT VRAAGT STRUCTUUR (todo/webshop.md, P2).
   Alper, 31-08-2026: *"de uitlijning klopt niet (in de tas is lager dan de
   rest)"*. Het rooster legde de kaarten al even HOOG neer, maar de inhoud
   stapelt van BOVEN: een teaser van twee regels duwde alles eronder een regel
   omlaag. `margin-top: auto` was de halve reparatie - dat plakt de knop aan de
   onderkant en laat de PRIJS zweven, en de prijs was de klacht.

   GEKOZEN: SUBGRID. De kaart deelt de RIJEN van het rooster, dus rij 1 (beeld)
   en rij 2 (de tekst die varieert) zijn voor de hele rij kaarten even hoog en
   begint rij 3 (de prijs) overal op dezelfde hoogte. De teaser mag daarbij zo
   lang zijn als hij is.

   WAT DE ANDERE WEG HAD GEKOST. `todo/webshop.md` noemde ook: de teaser
   afkappen op een vast aantal regels (`line-clamp`). Dat is wat de meeste
   winkels doen, maar het lijnt alleen uit als je er OOK een vaste hoogte aan
   hangt - anders staat een kaart met één regel teaser nog steeds hoger - en dan
   is het een keuze over WELKE tekst wegvalt, genomen in een stylesheet in plaats
   van door wie hem schreef. Bovendien lost het de PRODUCTNAAM niet op: die kan
   net zo goed over twee regels lopen, en dan zwaait de prijs alsnog. Subgrid
   raakt de inhoud niet aan en dekt allebei de gevallen.

   DE RIJ-AFSTAND VERHUIST DAARMEE NAAR HET ROOSTER. Bij subgrid is er nog maar
   EEN rij-afstand: die van de ouder, en die geldt zowel binnen een kaart als
   tussen twee rijen kaarten. Vandaar de lege VIERDE rij per kaart - die is de
   lucht tussen twee rijen kaarten, en zonder hem plakken ze tegen elkaar.

   BUITEN `@supports` blijft alles bij het oude, inclusief de rij-afstand van
   1,5rem. Een browser zonder subgrid krijgt dan geen uitlijning maar ook geen
   kapotte kaart. */
.shop-rooster {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
  column-gap: 1.25rem;
  row-gap: 1.5rem;
}

/* DE VLAKKE DECLARATIES VAN DE KAART ZIJN DATA (V90-optie 1, RING 2 tweede
   helft). `.shop-kaart-beeld`, `.shop-monogram`(-groot), `.shop-badge`,
   `.shop-kaart-cat`, `.shop-kaart-teaser` en `.shop-prijs` (alleen zijn basis)
   komen niet meer hiervandaan maar uit `cms_block_style`
   (modules/cms/cms_theme.h), gevuld door
   `modules/cms/bootstrap/zaai/30-blokopmaak.json`, en worden via
   CSSOM toegepast door `modules/cms/web/scripts/siteTheme.js`
   (`pasBlokOpmaakToe`). WAT HIER BLIJFT STAAN EN WAAROM:

     * `.shop-kaart` EN `.shop-kaart-vlak` MIGREREN BEWUST NIET MEE, ook al
       zijn het vlakke declaraties. `modules/webshop/tests/
       tasknop_ontsnapt_niet.h` pint vast dat hun `position` SYNCHROON in deze
       stylesheet staat: het is het vangnet voor een tasknop die anders op het
       initial containing block valt, en dat vangnet moet er staan voordat er
       ooit een fetch is gedaan - niet pas na een asynchrone CSSOM-toepassing.
       Zie de kop hieronder.
     * `@supports (grid-template-rows: subgrid)` hieronder is een MEDIA QUERY
       en past niet in een platte (eigenschap, waarde)-rij; blijft staan.
     * `.shop-kaart-vlak:hover`, `.shop-kaart-tekst h3(...)` en `.shop-prijs
       strong`/`.shop-prijs s` zijn een PSEUDO-CLASS resp. een AFSTAMMELING-
       selector - geen van beide is uit te drukken als blok+deel. Dit is de
       BEKENDE OPEN VRAAG uit V90 zelf en geen stille keuze; zie VRAGEN.md. */

/* HET VANGNET ONDER DE TASKNOP, en `position` is hier het hele punt.
   `.shop-tasknop` hieronder is `position: absolute`, en zijn BEDOELDE
   referentievlak is `.shop-kaart-vlak` - de wikkel om het beeld. Draait er
   ergens een opmaak zonder die wikkel (een oudere `cms_template`-rij, een klant
   die zijn kaart heeft geredigeerd), dan heeft de knop geen enkele
   gepositioneerde voorouder meer en valt hij terug op het INITIAL CONTAINING
   BLOCK: alle knoppen van een lijst landen op dezelfde 2,4 rem tegen de
   schermrand, met de laatste bovenop, en die legt het verkeerde product in de
   tas. Dat gebeurde op 07-09-2026 op de draaiende dev-core - veertien knoppen,
   nul wikkels - en er viel niets om.

   Met deze regel kan dat niet meer: elke tasknop staat per definitie binnen een
   `.shop-kaart`, dus er is ALTIJD een voorouder om op terug te vallen. Wat er
   dan misgaat is dat de knop over de tekst ligt in plaats van op het beeld -
   lelijk, en zichtbaar, en dat is precies het verschil.

   Het is de TWEEDE lijn en niet de eerste: `.shop-kaart-vlak` staat dichterbij
   en wint. Vastgepind door modules/webshop/tests/tasknop_ontsnapt_niet.h. */
.shop-kaart { position: relative; display: grid; row-gap: 0.7rem; }

@supports (grid-template-rows: subgrid) {
  /* Nu de INTERNE regelafstand van een kaart; zie hierboven. */
  .shop-rooster { row-gap: 0.7rem; }

  .shop-kaart {
    /* beeld, tekst, prijs + EEN lege ademrij. Blijft dit getal achter bij het
       aantal directe kinderen in het sjabloon, dan valt het laatste kind buiten
       de gedeelde rijen en lijnt het niet meer uit - zonder dat er iets
       omvalt. Vastgepind door modules/webshop/tests/kaart_uitlijning.h. */
    grid-row: span 4;
    grid-template-rows: subgrid;
  }
}

/* Het OMHULLENDE blok om beeldlink + tasknop. Het moet er zijn omdat een
   <button> in een <a> ongeldige HTML is en toetsenbordnavigatie breekt
   (todo/webshop.md, P3). De link houdt zijn eigen `position: relative`: hij is
   het referentievlak voor `.shop-foto` (inset: 0) en voor de badge. */
.shop-kaart-vlak {
  position: relative;
  transition: transform 250ms ease;
}
.shop-kaart-vlak:hover { transform: translateY(-4px); }

.shop-kaart-tekst h3 { margin: 0 0 0.25rem; font-size: 1rem; font-weight: 600; }
.shop-kaart-tekst h3 a { color: inherit; text-decoration: none; }
.shop-kaart-tekst h3 a:hover { text-decoration: underline; }
/* `.shop-kaart-cat` en `.shop-kaart-teaser` staan sinds V90-optie 1 in
   `cms_block_style`. */

.shop-prijs strong { font-size: 1.05rem; }
.shop-prijs s { color: var(--muted); font-size: 0.85rem; }
/* HET DEAL-LABEL (Alper, 17-09-2026: "een kortingsicoon (DEAL) en doorgestreept
   het voor korting, zoals bij bol"). Staat alleen waar `list_price_text` staat,
   dus waar de omnibus-referentieprijs een echte korting oplevert. De kleur is
   `--besparing` en geen eigen tint: die wordt al op contrast getoetst
   (`thema_contrast.h`), en een label met een eigen hex zou dat stil ontlopen.
   Het woord staat er zelf, dus de kleur is niet de enige aanwijzing (D7). */
.shop-deal {
  display: inline-block;
  margin-inline-end: 0.4rem;
  padding: 0.05rem 0.4rem;
  border: 1px solid currentColor;
  border-radius: 4px;
  color: var(--besparing);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  vertical-align: 0.15em;
}
/* DE GROTE PRIJS KOMT UIT ZIJN PLAATS EN NIET UIT EEN EXTRA KLASSE (02-09-2026,
   ring 2). Tot die dag droeg de detailpagina `shop-prijs shop-prijs-groot` en de
   kaart alleen `shop-prijs`, en daarmee waren het twee VERSCHILLENDE fragmenten
   die verder letterlijk gelijk waren - dus niet samen te trekken tot een blok.
   Een klasse die alleen bestaat om te zeggen WAAR iets staat, hoort de selector
   te zijn. Zie modules/cms/cms_template.h. */
.shop-detail-tekst .shop-prijs strong { font-size: 1.8rem; }

/* --- Lijst, filters, kruimels -------------------------------------------- */

.shop-kruimels { margin: 1.5rem 0 0.75rem; font-size: 0.8rem; color: var(--muted); }
.shop-kruimels a { color: inherit; }

.shop-lijstkop h1 { margin: 0 0 0.25rem; font-family: var(--site-letter, Georgia, serif); font-size: 2.4rem; }
.shop-filterbalk { display: flex; flex-wrap: wrap; gap: 0.75rem; justify-content: space-between; margin: 1.25rem 0 2rem; }
.shop-chips { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.shop-chip {
  padding: 0.4rem 0.9rem;
  border-radius: var(--radius-pil);
  border: 1px solid var(--line);
  color: var(--ink);
  text-decoration: none;
  font-size: 0.78rem;
}
.shop-chip:hover { background: color-mix(in oklch, var(--ink) 7%, transparent); }
.shop-chip-sorteer { border-style: dashed; color: var(--muted); }

/* DE ALLERGEENCHIP IS EEN <button> EN GEEN <a> (07-09-2026, stap 5 van
   modules/webshop/ALLERGENEN.md). Hij navigeert niet - hij zet een keuze om die
   in localStorage staat en met opzet NIET in de route, want een gedeelde link
   zou dan iemands gezondheidsgegeven dragen (AVG art. 9, B515).

   Een <button> erft de knopstijl van de browser: zonder deze regels staat er
   een grijze systeemknop met de pilrand van .shop-chip eromheen, en verschilt
   hij zichtbaar van de categoriechips ernaast terwijl hij hetzelfde ding is.

   HIJ STAAT BOVEN .shop-chip-aan en niet eronder, en dat is geen volgorde die
   je mag omdraaien: bij gelijke specificiteit wint de LAATSTE regel, dus een
   `background` hieronder zou de aan-stand van de chip stil overschrijven.

   De doorhaling is de tweede aanwijzing naast de vulling. `aria-pressed` zegt
   het aan een schermlezer; wie kleur niet onderscheidt heeft aan een vulling
   alleen niets (WCAG 1.4.1). */
.shop-chip-zonder { background: none; font: inherit; font-size: 0.78rem; cursor: pointer; }
.shop-chip-zonder[aria-pressed="true"] { text-decoration: line-through; }
.shop-filterbalk-zonder { flex-direction: column; align-items: flex-start; gap: 0.4rem; margin: -1rem 0 2rem; }
.shop-chip-kop { color: var(--muted); font-size: 0.78rem; align-self: center; }

/* DE TELLER OP EEN CHIP, EN DE LEGE CHIP BLIJFT STAAN (04-09-2026).
   Elke categorie draagt sinds vandaag zijn aantal treffers BINNEN het huidige
   filter. Een categorie met nul treffers wordt niet geschrapt maar gedempt:
   wegnemen verbergt dat hij bestaat, en dan lijkt een lege uitkomst een storing
   in plaats van een antwoord. Zie `modules/webshop/webshop_lijst_route.h`. */
.shop-chip-tel { color: var(--muted); font-variant-numeric: tabular-nums; }
.shop-chip-aan { background: color-mix(in oklch, var(--ink) 10%, transparent); border-color: var(--ink); }
.shop-chip-leeg { color: var(--muted); border-style: dotted; }
.shop-chip-leeg .shop-chip-tel { color: inherit; }
.shop-leeg { color: var(--muted); padding: 2rem 0; }

/* --- Productpagina ------------------------------------------------------- */

.shop-detail { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2rem; margin: 1rem 0 4rem; }
@media (min-width: 832px) { .shop-detail { grid-template-columns: 1fr 1fr; align-items: start; } }

.shop-detail-beeld {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius);
  overflow: hidden;
  background: linear-gradient(155deg, var(--tint-a, var(--bg-diep)), var(--tint-b, var(--brand)));
}
.shop-detail-tekst h1 { margin: 0.25rem 0 0.5rem; font-family: var(--site-letter, Georgia, serif); font-size: 2.6rem; line-height: 1.05; }
.shop-detail-teaser { margin: 0 0 1.25rem; color: var(--muted); font-size: 1.05rem; }
.shop-body { margin-top: 1.5rem; white-space: pre-line; line-height: 1.65; }

/* Een GAT dat je moet kunnen zien. Een winkelwagen die er niet is, hoort niet
   weggelaten te worden maar benoemd - anders leest een half afgebouwd scherm
   als een af scherm (DOELSTELLING.md sectie 1). */
.shop-gat {
  margin: 1.5rem 0 0;
  padding: 0.9rem 1.1rem;
  border-inline-start: 3px solid var(--actie);
  background: color-mix(in oklch, var(--ink) 5%, transparent);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 0.85rem;
  color: var(--muted);
}

/* DEZELFDE TAAL ALS `.shop-gat` HIERBOVEN, maar over de GEKOZEN maat/kleur in
   plaats van het hele product (KASSA-4, `webshopVarianten.js`): een andere
   combinatie kan nog wel op voorraad zijn. */
.shop-variant-gat {
  margin: 0.75rem 0 0;
  padding: 0.9rem 1.1rem;
  border-inline-start: 3px solid var(--actie);
  background: color-mix(in oklch, var(--ink) 5%, transparent);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 0.85rem;
  color: var(--muted);
}

.shop-voet {
  border-top: 1px solid var(--line);
  padding: 2.5rem 1.25rem;
  text-align: center;
  color: var(--muted);
  font-size: 0.82rem;
}

/* Beweging die je niet uit kunt zetten is een toegankelijkheidsfout. */
@media (prefers-reduced-motion: reduce) {
  .shop-hero-vlak, .shop-tegel-vlak { animation: none; transform: none; }
  .shop-hero-tekst, .shop-tegel-tekst { animation: none; }
  .shop-pil, .shop-kaart-vlak, .shop-tasknop { transition: none; }
  /* De bevestiging mag niet WEG - dan is er geen enkel signaal meer dat de klik
     is aangekomen - maar hij mag niet SPRINGEN. Zonder beweging blijft de
     kleuromslag over, en die is hier het bericht. */
  .shop-tasknop[data-gelukt], .shop-pil-actie[data-gelukt] { animation: none; }
}

/* --- De bestelkant: tas, afrekenen, bestellingen ------------------------- */

/* DE TASKNOP OP HET BEELD (todo/webshop.md, P3). Alper, 31-08-2026: *"kunnen we
   niet beter een winkelwagen / tas logo hebben met een plusje er in (rechtsonder
   het plaatje, zoals een instagram like)? maak het hipper."*

   Hij stond tot die dag als een volle-breedte knop met het woord "In de tas"
   ONDER de tekst, en dat was meteen de breedste regel van de kaart. Wat het
   oplevert is dus niet alleen hipper: de kaart wordt smaller in zijn tekst en
   de knop verlaat de stapel die de uitlijning verstoorde.

   HIJ STAAT ER ALTIJD, en dat is de reden dat er geen `:hover`-regel is die hem
   tevoorschijn haalt. Instagram doet dat ook niet, hoe het er ook uitziet: op
   een telefoon bestaat hover niet, en een knop die alleen bij hover opduikt
   bestaat voor de helft van de bezoekers niet.

   Het ICOON draagt de betekenis en er staat geen woord bij, dus de naam zit in
   het `aria-label` van de knop in het sjabloon - inclusief de productnaam, want
   vier identieke knoppen op een rij zijn voor een schermlezer anders niet uit
   elkaar te houden. */
.shop-tasknop {
  position: absolute;
  inset-block-end: 0.6rem;
  inset-inline-end: 0.6rem;
  display: grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  padding: 0;
  border-radius: var(--radius-pil);
  border: 1px solid color-mix(in oklch, var(--line) 55%, transparent);
  background: color-mix(in oklch, var(--panel) 88%, transparent);
  color: var(--ink);
  cursor: pointer;
  transition: background 160ms ease, color 160ms ease, transform 160ms ease;
}
.shop-tasknop svg { width: 1.15rem; height: 1.15rem; display: block; }
.shop-tasknop:hover { background: var(--actie); color: var(--brand-ink); border-color: transparent; }
.shop-tasknop:focus-visible { outline: 2px solid var(--actie); outline-offset: 2px; }

/* DE BEVESTIGING. Verdwijnt de tekst uit de knop, dan moet iets anders laten
   zien dat het gelukt is (todo/webshop.md, P3). Dat is hier twee dingen tegelijk
   en met opzet niet één: de teller in de menubalk telt op, en de knop zelf slaat
   even om. Wie op de knop kijkt ziet het eerste niet, en andersom.
   Voor wie niet kijkt staat er een `role="status"` in shop.html. */
.shop-tasknop[data-gelukt] {
  background: var(--actie);
  color: var(--brand-ink);
  border-color: transparent;
  animation: shop-tas-pop 320ms ease;
}
/* Dezelfde bevestiging op de DETAILpagina. Die knop draagt nog wel een woord, en
   toch was er tot 31-08-2026 niets te zien: de pagina tekende zich opnieuw en
   kwam er identiek uit. */
.shop-pil-actie[data-gelukt] { animation: shop-tas-pop 320ms ease; }

@keyframes shop-tas-pop {
  0% { transform: scale(1); }
  40% { transform: scale(1.18); }
  100% { transform: scale(1); }
}

/* Alleen voor een schermlezer: de bevestiging in woorden. Niet `display: none`
   en niet `hidden` - die twee halen hem ook bij de schermlezer weg, en dan is er
   voor wie niet kijkt niets veranderd. */
.shop-status {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.shop-pil-actie { background: var(--actie); color: var(--brand-ink); border: none; font: inherit;
  font-size: 0.78rem; letter-spacing: var(--tracking); text-transform: uppercase; cursor: pointer;
  padding: 0.85rem 1.75rem; border-radius: var(--radius-pil); text-decoration: none; display: inline-block; }

.shop-wagen { list-style: none; margin: 2rem 0; padding: 0; display: grid; gap: 1rem; }

/* DE TASREGEL HEEFT BENOEMDE VAKKEN, EN DAT IS EEN REPARATIE (08-09-2026).
 *
 * Hier stond `grid-template-columns: 4.5rem 1fr auto` zonder vakken, met
 * `grid-column: 2` op de teller en `grid-column: 3` op Verwijder. De
 * automatische plaatsing maakte daar DRIE rijen van met een leeg vak
 * rechtsboven: de naam bovenaan, de teller eronder, het regelbedrag daar weer
 * onder in de derde kolom, en Verwijder in een vierde rij. Alper zag dat als
 * "alles onder elkaar en niets bij elkaar"; het was niet ontworpen maar
 * uitgerekend.
 *
 * Met vakken staat het bedrag naast de naam waar het bij hoort, en zitten de
 * twee HANDELINGEN (aantal en weghalen) op één rij eronder. De acties staan in
 * een eigen omhulsel: op een telefoon moeten ze samen een rij vullen, en een
 * los rasteritem in een track van 3,25rem zou uit zijn kolom lopen - en dat is
 * D1, horizontale scroll. */
.shop-wagenregel {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr) auto;
  grid-template-areas:
    "beeld tekst  totaal"
    "beeld acties acties";
  column-gap: 1rem;
  row-gap: 0.75rem;
  align-items: start;
  padding-bottom: 1rem;
  border-bottom: 1px solid color-mix(in oklch, var(--line) 70%, transparent);
}
.shop-wagenbeeld {
  grid-area: beeld;
  display: grid;
  place-items: center;
  aspect-ratio: 1;
  border-radius: var(--radius);
  text-decoration: none;
  background: linear-gradient(155deg, var(--tint-a, var(--bg-diep)), var(--tint-b, var(--brand)));
}
.shop-wagenbeeld .shop-monogram { font-size: 1.5rem; }
.shop-wagentekst { grid-area: tekst; }
.shop-wagentekst h3 { margin: 0 0 0.2rem; font-size: 1rem; }
.shop-wagentekst h3 a { color: inherit; text-decoration: none; }
/* Het regelbedrag rechts op de naamregel. `tabular-nums` zodat de bedragen
   onder elkaar op de komma uitkomen; zonder dat danst een kolom cijfers. */
.shop-wagenregel .shop-regeltotaal {
  grid-area: totaal;
  text-align: end;
  font-variant-numeric: tabular-nums;
}
.shop-wagenacties {
  grid-area: acties;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.shop-aantal { display: flex; align-items: center; gap: 0.5rem; }
.shop-aantal-waarde { min-width: 2ch; text-align: center; }
.shop-regeltotaal { margin: 0; font-weight: 600; }
/* EEN BESPARING IS GEEN WAARSCHUWING, EN TOT 08-09-2026 DEELDEN ZE EEN KLASSE.
 * `.shop-korting` droeg `var(--actie)` - de KNOPkleur, bij dit thema rood - en
 * stond zowel op de kortingsbadge in de etalage als op "Uitverkocht" in de tas.
 * Alper: "waarom is korting rood. Dat is Groen." Hij heeft gelijk, en het is
 * niet alleen smaak: een bedrag dat de koper WINT in de kleur van een fout is
 * dezelfde soort leugen als een bedrag zonder muntteken.
 *
 * Twee klassen dus, want het waren altijd al twee betekenissen. De kleur is
 * nooit de enige aanwijzing (D7): voor de besparing staat een minteken, en de
 * waarschuwing is een zin. */
.shop-besparing { color: var(--besparing); font-size: 0.78rem; font-weight: 600; }
.shop-waarschuwing { color: var(--actie); font-size: 0.78rem; font-weight: 600; }
.shop-weg {
  background: none;
  border: none;
  color: var(--muted);
  font: inherit;
  font-size: 0.78rem;
  text-decoration: underline;
  cursor: pointer;
}
.shop-weg:hover { color: var(--actie); }

/* DE SOM STAAT ONDER ELKAAR EN NIET NAAST ELKAAR (Alper, 08-09-2026).
 *
 * `.shop-wagenvoet` was een flexrij met `space-between`, en daarmee stonden
 * "Artikelen", "Verzendkosten", "Korting", "Totaal" en de afrekenknop op EEN
 * regel naast elkaar - vier bedragen zonder verband, waarvan er drie niet op
 * elkaar aansluiten. Een som hoort de vorm van een kassabon te hebben: label
 * links, bedrag rechts, een streep voor het eindbedrag.
 *
 * EN DE GROOTTE ZEGT WAT ER TELT. De tussenregels zijn de opbouw; het
 * eindbedrag is waar de koper voor tekent en staat dus groter. */
.shop-wagenvoet {
  display: grid;
  justify-content: end;
  justify-items: stretch;
  gap: 1.25rem;
  margin: 2rem 0;
}
.shop-som {
  margin: 0;
  display: grid;
  gap: 0.5rem;
  min-width: min(20rem, 100%);
}
.shop-somregel {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem;
}
.shop-somregel dt { color: var(--muted); font-size: 0.9rem; }
.shop-somregel dd { margin: 0; font-size: 0.95rem; font-variant-numeric: tabular-nums; }
.shop-somregel.shop-besparing dt,
.shop-somregel.shop-besparing dd { color: var(--besparing); }
.shop-somtotaal {
  margin-top: 0.25rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--line);
}
.shop-somtotaal dt { color: var(--ink); font-size: 1rem; font-weight: 600; }
.shop-somtotaal dd { font-size: 1.5rem; font-weight: 700; line-height: 1.2; }
/* De afrekenknop vult de breedte van de som: hij hoort bij dat bedrag, en een
   knop die smaller is dan zijn eigen totaal ziet eruit als een voetnoot. */
.shop-wagenvoet .shop-pil-actie { text-align: center; }
.shop-totaal { margin: 0; font-size: 1.1rem; }

.shop-formulier { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; max-width: 34rem; margin: 2rem 0; }
.shop-formulier label { display: grid; gap: 0.35rem; font-size: 0.85rem; color: var(--muted); }
.shop-formulier input {
  font: inherit;
  padding: 0.7rem 1rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
}
.shop-formulier-rij { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: 1rem; }
/* Een invoervak heeft een eigen minimumbreedte (`size=20`); zonder dit duwen twee
   naast elkaar het afrekenformulier op 390 px breder dan het scherm (D1, D44). */
.shop-formulier input:not([type="checkbox"]):not([type="radio"]),
.shop-formulier select { min-width: 0; width: 100%; box-sizing: border-box; }
.shop-formulierfout { color: var(--actie); margin: 0; }

/* STAP 3 VAN DE AANVRAAGWIZARD: de domeinkeuze (`shopDomeinstap.js`). Een
   rondje of vinkje staat VOOR zijn zin en niet erboven: `.shop-formulier label`
   is een rooster dat elk kind een eigen regel geeft, en een vinkje op een
   eigen regel lijkt los te staan van wat het aanzet. De zin vouwt om
   (`minmax(0, 1fr)`), zodat hij op 390 px niet uitsteekt (D1, D44). */
.shop-domeinkeuze { display: grid; gap: 0.6rem; border: 0; padding: 0; margin: 1.5rem 0; min-width: 0; }
.shop-domeinkeuze legend { font-weight: 600; margin-bottom: 0.4rem; padding: 0; }
.shop-domeinkeuze-optie,
.shop-formulier label.shop-domeinkeuze-optie {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: 0.6rem;
  font-size: 1rem;
  color: var(--ink);
}
.shop-domeinkeuze-optie input { margin: 0.2rem 0 0; padding: 0; }
.shop-formulier select {
  font: inherit;
  padding: 0.7rem 1rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
}
.shop-domeinpaneel ol { padding-left: 1.4rem; }
/* Een invoervak heeft een eigen minimumbreedte (`size=20`). Twee naast elkaar
   in `.shop-formulier-rij` duwden het rooster op 390 px breder dan het scherm,
   en de zin bij het vinkje viel half buiten beeld (D44). Hier krimpen ze; de
   regel staat nu op `.shop-formulier` zelf en geldt dus ook voor het afrekenen. */

/* DE TOONKNOP BIJ EEN WACHTWOORD. Geen icoon maar een woord: dit staat er een
   keer, er is geen ruimtegebrek, en een oogje is voor de helft van de bezoekers
   dubbelzinnig - staat het aan of zet ik het aan? De schil zet hem neer
   (shopSite.js); hij staat hier omdat de winkel de stylesheet van het CMS deelt
   en er geen tweede publieke stylesheet bij mag komen (CLAUDE.md sectie 4). */
.shop-toon {
  justify-self: start;
  margin-top: -0.6rem;
  padding: 0;
  background: none;
  border: none;
  color: var(--muted);
  font: inherit;
  font-size: 0.8rem;
  text-decoration: underline;
  cursor: pointer;
}
.shop-toon:hover { color: var(--ink); }
.shop-toon[aria-pressed="true"] { color: var(--ink); }

.shop-order {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.5rem;
  margin: 1.5rem 0;
}
.shop-orderkop { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; }
.shop-orderkop h2 { margin: 0; font-family: var(--site-letter, Georgia, serif); font-size: 1.3rem; }
.shop-orderregels { list-style: none; margin: 1rem 0; padding: 0; display: grid; gap: 0.4rem; }
.shop-orderregels li { display: grid; grid-template-columns: 3rem 1fr auto; gap: 0.75rem; }
.shop-orderaantal { color: var(--muted); }

.shop-bedankt { max-width: 42rem; margin: 4rem auto; text-align: center; }
.shop-bedankt h1 { font-family: var(--site-letter, Georgia, serif); font-size: 2.6rem; margin: 0.5rem 0 1rem; }
.shop-bedankt-acties { display: flex; gap: 0.75rem; justify-content: center; flex-wrap: wrap; margin: 2rem 0; }
.shop-account {
  margin-top: 3rem;
  padding: 1.5rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  text-align: start;
}
.shop-account h2 { margin: 0 0 0.5rem; font-size: 1.1rem; }
.shop-account p { margin: 0 0 1rem; color: var(--muted); font-size: 0.9rem; }

/* --- DIAGRAMMEN IN EEN WIKI-PAGINA -----------------------------------------
 *
 * De SVG komt uit `cms::render_diagram` en draagt met opzet GEEN kleuren: die
 * horen uit de tokenlaag van het thema te komen (CLAUDE.md sectie 4, zone 2).
 * Een tekening met vaste kleuren erin zou op een donkere site onleesbaar zijn,
 * en op de site van een klant met een eigen huisstijl misstaan.
 *
 * Alles hieronder loopt daarom via `var(--...)` en `color-mix`, net als de rest
 * van deze stylesheet. En ZONDER hex-terugval: een `var(--site-ink, #000)` ziet
 * eruit als voorzichtigheid en is het tegendeel - hij zet de kleur vast op het
 * moment dat het thema hem juist zou leveren.
 *
 * ⚠️ DE GEDEELDE TOKENS, NIET DE SITETOKENS (14-09-2026, BUG-65a421d4). Hier
 * stond `var(--site-ink)`, en in de KENNISBANK tekende elk diagram daardoor als
 * zwarte blokken: de wiki laadt deze stylesheet ook, maar is zone 1 en heeft
 * geen sitethema, dus `--site-ink` bestond daar niet en de SVG-vulling viel
 * terug op zwart. `--ink` en `--muted` staan in beide zones - op de site leidt
 * `:root` hierboven ze af uit het thema, in de wiki levert `app.css` ze. Op de
 * site verandert er dus niets. Vastgepind door
 * `modules/cms/tests/sitetoken_zonder_terugval.h`. */
.cms-diagram {
  margin: 1.5rem 0;
  overflow-x: auto;
}
.cms-diagram svg {
  max-width: 100%;
  height: auto;
}
.cms-diagram-blok {
  fill: color-mix(in oklch, var(--ink) 6%, transparent);
  stroke: color-mix(in oklch, var(--ink) 25%, transparent);
  stroke-width: 1;
}
.cms-diagram-tekst {
  fill: var(--ink);
  font-size: 15px;
  font-family: inherit;
}
.cms-diagram-pijl {
  stroke: color-mix(in oklch, var(--ink) 45%, transparent);
  stroke-width: 1.5;
}
.cms-diagram-label {
  fill: var(--muted);
  font-size: 12px;
  font-family: inherit;
  text-anchor: end;
}
.cms-diagram-fout {
  margin: 1.5rem 0;
  padding: 0.75rem 1rem;
  border-inline-start: 3px solid color-mix(in oklch, var(--ink) 35%, transparent);
  color: var(--muted);
  font-size: 0.9rem;
}

/* EMBED (TRN-20): 16:9 via de paddingtruc, want een `<iframe>` heeft geen
 * intrinsieke maat om `aspect-ratio` op te laten steunen zonder JS. Zelfde
 * tokens als `.cms-diagram` hierboven - geen eigen kleur voor deze zone. */
.cms-embed {
  position: relative;
  margin: 1.5rem 0;
  padding-block-start: 56.25%;
  background: color-mix(in oklch, var(--ink) 6%, transparent);
  border-radius: var(--cms-radius, 0.75rem);
  overflow: hidden;
}
.cms-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
/* --- Widget: fotogalerij (REDACTIE-13) -------------------------------------
   De diavoorstelling hergebruikt `.cms-dias`/`.cms-dia` ongewijzigd; alleen het
   raster krijgt hier eigen opmaak. */
.cms-widget-galerij {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr));
  gap: 0.75rem;
  margin-block: 1.5rem;
}
.cms-widget-galerij img {
  display: block;
  inline-size: 100%;
  block-size: 12rem;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: var(--cms-radius, 0.75rem);
}

.cms-embed-fout {
  margin: 1.5rem 0;
  padding: 0.75rem 1rem;
  border-inline-start: 3px solid color-mix(in oklch, var(--ink) 35%, transparent);
  color: var(--muted);
  font-size: 0.9rem;
}

/* ==========================================================================
   D38 OP EEN KLANTSITE: ÉÉN OPPERVLAK IN PORTRET.
   ==========================================================================
   Dezelfde laag als onderaan `core/network/gateway_web/styles/app.css`, en hij
   staat hier OOK omdat app.css op een klantsite niet wordt ingeladen - zone 2
   heeft zijn eigen huisstijl (CLAUDE.md sectie 4). Een regel die alleen in
   app.css staat, geldt dus voor de helft van wat we uitleveren.

   Alper vroeg er expliciet naar op 23-09-2026: "een algemene regel die echt
   OVERAL toegepast moet worden (ook bij user made themes)". Dat werkt hier via
   het token en niet via een lijst, want een klantthema levert alleen KLEUREN
   (`cms_theme_token` -> `--site-*`) en schrijft zelf geen media query. Kantelt
   `--panel`, dan kantelt elk vlak dat op dat thema draait mee, ook een thema
   dat vandaag nog niet bestaat.

   Het blok staat onderaan met opzet: `:root` in een media query heeft dezelfde
   gewichtsklasse als de `:root` bovenaan, dus alleen de VOLGORDE beslist. */
@media (max-width: 600px) {
  :root { --panel: var(--bg); }

  /* WAT ZWEEFT houdt zijn vlak: de kopbalk plakt en de inhoud scrolt eronder
     door, en de skip-link springt over de pagina heen tevoorschijn. */
  dialog,
  .cms-kop,
  .shop-kop,
  .cms-skip {
    --panel: var(--panel-vast);
  }

  /* WAT JE BEDIENT houdt zijn vlak: een veld of een knop zonder eigen vlak is
     een veld dat je op een telefoon niet terugvindt. */
  input,
  select,
  textarea,
  button,
  .cms-knop,
  .cms-kanaal,
  .shop-pil,
  .shop-pil-licht,
  .shop-tasknop {
    --panel: var(--panel-vast);
  }

  /* En de tegels zelf verliezen hun kader, ronding en zijmarge. De achtergrond
     zit al in de kanteling hierboven; dit is wat een token niet kan doen. */
  .cms-fout {
    border-inline: 0;
    border-radius: 0;
    box-shadow: none;
    margin-inline: 0;
  }
}
