/* GEGENEREERD BESTAND, NIET HANDMATIG AANPASSEN.
   Kopie uit content/growthos-nl/design-system/, want de site wordt vanuit
   deze map gedeployed en kan niet buiten zijn eigen wortel lezen.
   Bijwerken: python scripts/sync-design-tokens.py
   ===================================================================== */

/* =====================================================================
   Ensemblo Design System — Patronen  (v1.0 · 2026-08-07)

   Wat te doen met blokken die te groot worden: te veel items, te veel
   tekst, te groot beeld. Eén set gedragingen, overal dezelfde.

   Laad ná tokens.css en motion.css. Gebruikt alleen var(--token), dus het
   werkt in elk merk dat diezelfde tokens zet.

   Het uitgangspunt: een bezoeker scant, hij leest niet. Een blok dat groter
   is dan wat je in één oogopslag opneemt, kost hem een beslissing die hij
   niet wilde nemen. Dan knippen we het, en geven we hem de rest op verzoek.

   Drempels staan in patronen-v1.md en worden gecontroleerd door
   scripts/plakkaat-check.py, zodat dit geen kwestie van smaak blijft.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Te veel items — [data-inklap="N"]

   Zonder JavaScript zie je gewoon de hele lijst: dat is compleet, alleen
   langer. Met JavaScript klapt hij in tot N met een knop erbij. De
   .js-klasse wordt in de <head> gezet, dus er flitst nooit een volle lijst
   voorbij die daarna inklapt.

   CSS kan het getal in het attribuut niet lezen, vandaar één regel per
   ondersteunde waarde. Meer dan deze vier hebben we nog nooit nodig gehad.
   --------------------------------------------------------------------- */
.js [data-inklap="3"] > :nth-child(n + 4),
.js [data-inklap="4"] > :nth-child(n + 5),
.js [data-inklap="6"] > :nth-child(n + 7),
.js [data-inklap="8"] > :nth-child(n + 9) {
  display: none;
}

/* Zodra het script de rest heeft verplaatst, mag die regel weer los: de
   items zitten dan in de uitklapper en die regelt de zichtbaarheid. */
.js [data-inklap][data-ingeklapt] > :nth-child(n + 1) {
  display: inline-flex;
}

/* De uitklapper. Van 0fr naar 1fr is een echte hoogte-animatie, zonder dat
   we in JavaScript pixels hoeven te meten, en hij blijft kloppen als de
   inhoud van lengte verandert. */
.uitklap {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-paneel) var(--ease-land);
}
.uitklap > * {
  overflow: hidden;
  visibility: hidden;
  /* Bij sluiten pas onbereikbaar worden als de animatie klaar is; bij
     openen meteen. Anders kun je met tab in iets landen dat wegschuift. */
  transition: visibility 0s linear var(--dur-paneel);
}
[data-open] .uitklap,
.uitklap[data-open] {
  grid-template-rows: 1fr;
}
[data-open] .uitklap > *,
.uitklap[data-open] > * {
  visibility: visible;
  transition-delay: 0s;
}

/* De items komen één voor één na, zodat je oog de rij afloopt in plaats van
   acht dingen tegelijk te moeten opnemen. Na de zesde stopt de trap: daarna
   is het wachten in plaats van richting. */
[data-open] .uitklap > * > * {
  animation: ds-item-in var(--dur-paneel) var(--ease-land) backwards;
  animation-delay: 60ms;
}
/* De trap komt uit de positie in de rij, niet uit een --i die iemand moet
   meegeven. Zo werkt hij hetzelfde voor het script op de site en voor de
   React-component in de app. Na het zesde item stopt de vertraging: daarna
   is het wachten in plaats van richting. */
[data-open] .uitklap > * > :nth-child(2) { animation-delay: 105ms; }
[data-open] .uitklap > * > :nth-child(3) { animation-delay: 150ms; }
[data-open] .uitklap > * > :nth-child(4) { animation-delay: 195ms; }
[data-open] .uitklap > * > :nth-child(5) { animation-delay: 240ms; }
[data-open] .uitklap > * > :nth-child(n + 6) { animation-delay: 285ms; }

@keyframes ds-item-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* De knop die zegt hoeveel er nog volgen. Geen "Meer": een getal vertelt of
   het de moeite is om te klikken. */
.meer-knop {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: var(--fs-13, 13.5px);
  font-weight: 600;
  cursor: pointer;
}
.meer-knop:hover {
  border-color: var(--brand);
  color: var(--brand);
  background: var(--panel);
}
.meer-knop-n { font-variant-numeric: tabular-nums; }

/* De pijl wijst naar beneden, want daar komt het vandaan. Bij open draait
   hij om: de richting vertelt wat de klik doet.

   Maatvoering uit --icoon, net als elk ander icoon. Hij stond op 9px terwijl
   dezelfde pijl elders 8px was en weer ergens anders een tekstteken op 19px:
   drie tekens voor één gebaar, en dat leest als slordig zonder dat je kunt
   aanwijzen waarom. Het teken is kleiner dan de icoonmaat, want een chevron
   die de volle maat vult oogt zwaarder dan een logo van gelijke afmeting. */
.meer-pijl {
  /* 0.5 en niet minder: dat houdt hem op de 9px die hij al had, zodat de
     live site niet verschuift door een opruiming die alleen over eenheid
     ging. De app volgt deze maat, niet andersom. */
  width: calc(var(--icoon, 18px) * 0.5);
  height: calc(var(--icoon, 18px) * 0.5);
  border-right: var(--icoon-lijn, 2px) solid currentColor;
  border-bottom: var(--icoon-lijn, 2px) solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
  transition: transform var(--dur-paneel) var(--ease-land);
}
[data-open] .meer-pijl {
  transform: rotate(-135deg) translate(-2px, -2px);
}

/* ---------------------------------------------------------------------
   2. Te veel tekst — [data-inkort]

   Niet de tekst weggooien, wel de muur. Je ziet het begin, het onderste
   deel loopt weg in de achtergrond, en je beslist zelf of je verder leest.
   --------------------------------------------------------------------- */
.js [data-inkort] .inkort-body {
  position: relative;
  max-height: var(--inkort-hoogte, 12em);
  overflow: hidden;
  transition: max-height var(--dur-paneel) var(--ease-land);
}
.js [data-inkort][data-open] .inkort-body {
  max-height: var(--inkort-vol, 200em);
}
/* De vervaging zegt zonder woorden dat er meer is. Weg zodra het open is,
   anders lijkt er nóg meer te volgen. */
.js [data-inkort] .inkort-body::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 4.5em;
  background: linear-gradient(to bottom, transparent, var(--vervaag-naar, var(--bg)));
  pointer-events: none;
  transition: opacity var(--dur-hover) var(--ease-inuit);
}
.js [data-inkort][data-open] .inkort-body::after { opacity: 0; }

/* ---------------------------------------------------------------------
   3. Te groot beeld — .beeld

   Een afbeelding mag nooit het hele scherm opeten: dan scrollt iemand langs
   iets waarvan hij de vorm niet eens ziet. En altijd met een vaste
   verhouding, zodat de pagina niet verspringt terwijl hij laadt.
   --------------------------------------------------------------------- */
.beeld {
  display: block;
  width: 100%;
  height: auto;
  max-height: var(--beeld-max, 70vh);
  object-fit: cover;
  border-radius: var(--r-lg);
  background: var(--panel-2);
}
.beeld--portret { --beeld-max: 80vh; }
.beeld--band    { --beeld-max: 42vh; }

/* Alleen voor schermlezers: de knop zegt "+5", deze tekst zegt waarvan. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

@media (prefers-reduced-motion: reduce) {
  [data-open] .uitklap > * > * { animation: none; }
}

/* ---------------------------------------------------------------------
   4. Geen sprong als er een scrollbalk bij komt.

   Dit is geen effect maar een correctie: zodra een pagina langer wordt dan
   het scherm verschijnt de scrollbalk en schuift alles een stukje naar
   links. Hoort overal, ook in een platform.
   --------------------------------------------------------------------- */
html {
  scrollbar-gutter: stable;
}
/* Voor een vak dat zelf scrollt en gecentreerd moet blijven: ruimte aan
   beide kanten, zodat het midden ook echt het midden is. */
.scroll-vak {
  overflow-y: auto;
  scrollbar-gutter: stable both-edges;
}

/* ---------------------------------------------------------------------
   5. CSS die werk uit JavaScript haalt — functioneel, dus overal.

   Alles hieronder is gekozen op één criterium: wat gebeurt er in een
   browser die het niet kent. Elke regel hier valt stilletjes weg en laat
   een werkende, iets minder verfijnde versie achter. Niets breekt.

   Dat criterium is belangrijker dan een ondersteuningspercentage. Onze
   meting was op Chromium; Safari en Firefox lopen soms achter. Met deze
   selectie maakt dat niet uit.
   --------------------------------------------------------------------- */

/* 5a. Koppen die niet met één woord op de laatste regel eindigen.
   Eén regel, en elke kop op elke pagina leest beter. Onbekend? Dan breekt
   de tekst gewoon zoals hij altijd al deed. */
h1, h2, h3, .koppel-zonetitel, .sec-head h2 {
  text-wrap: balance;
}
/* Bij lopende tekst gaat het om de laatste regel: geen los woord als
   afsluiter. 'pretty' is subtieler dan 'balance' en hoort bij alinea's. */
p, li, .leesbaar {
  text-wrap: pretty;
}

/* 5b. Een tekstvak dat meegroeit met wat je typt. Dit werd tot voor kort
   met een scriptje van vijftien regels gedaan dat de scrollHeight meet.
   Kent de browser het niet, dan krijg je het gewone vaste vak terug. */
textarea {
  field-sizing: content;
  min-height: 4lh;
  max-height: 20lh;
}

/* 5c. Foutmeldingen pas ná de invoer, niet ervoor.
   :invalid kleurt een leeg verplicht veld meteen rood, terwijl de bezoeker
   nog niets fout heeft gedaan. :user-invalid wacht tot hij het veld heeft
   gebruikt en verlaten. Dat is precies het verschil dat anders met een
   'touched'-vlag in JavaScript wordt nagebouwd. */
input:user-invalid,
textarea:user-invalid,
select:user-invalid {
  border-color: var(--crit);
}
input:user-valid,
textarea:user-valid {
  border-color: var(--good);
}

/* 5c-bis. De omtrek van iets dat je bedient heeft 3:1 nodig (WCAG 1.4.11).
   --line is bewust zacht voor scheidingslijnen en komt op 1.31; voor een
   invoerveld is dat te weinig. Gemeten door scripts/contrast-check.py. */
input:not([type='checkbox']):not([type='radio']),
select,
textarea {
  border-color: var(--line-sterk);
}

/* 5d. Merkkleur in de systeem-onderdelen: vinkjes, radio's, voortgang.
   Scheelt het namaken van een checkbox, wat altijd toegankelijkheid kost. */
:root {
  accent-color: var(--brand);
}

/* 5e. Scrollen in een paneel neemt de pagina niet mee.
   Zonder dit scrolt de achtergrond door zodra je in een lijst onderaan
   bent. Vooral hinderlijk op mobiel en in dialogen. */
.scroll-vak,
dialog,
[role='dialog'] {
  overscroll-behavior: contain;
}

/* 5f. Lange lijsten die pas renderen als ze in beeld komen.
   Alleen bewust toepassen met .lange-lijst: de browser slaat het tekenen
   over tot het nodig is. De maat erbij, anders klopt de scrollbalk niet.
   Zonder ondersteuning wordt gewoon alles getekend, zoals nu. */
.lange-lijst > * {
  content-visibility: auto;
  contain-intrinsic-size: auto var(--rij-hoogte, 72px);
}
