/* app.css — base layout + the fixed component vocabulary (STYLE.md §5, D-110):
 * list · table · form · badge · card · button · panel (overlay) · signaalregel.
 * Tokens come from tokens.css (generated from STYLE.md — never restyle ad hoc).
 * Laptop-first ~1366×768 (D-112); fluid grid + container queries + two
 * content-driven breakpoints (~640 / ~1024). */

* { box-sizing: border-box; }

html { height: 100%; }

body {
  margin: 0;
  min-height: 100%;
  font-family: var(--font-basis);
  font-size: var(--maat-14);
  line-height: var(--regelhoogte-basis);
  color: var(--tekst);
  background: var(--wit);
}

/* ---- Typography --------------------------------------------------------- */

h1, h2, h3 {
  font-family: var(--font-kop);
  color: var(--kleur-kop);
  margin: 0 0 var(--ruimte-12);
}
h1 { font-size: var(--maat-20); font-weight: 600; }
h2 { font-size: var(--maat-16); font-weight: 600; }
h3 { font-size: var(--maat-14); font-weight: 600; }

a { color: var(--kleur-primair); }
a:hover { text-decoration-thickness: 2px; }

/* `<output>` is van huis uit inline. Deze app zet er klassen op die zelf geen
   `display` meegeven (`.kaart`, `.melding-ok`, `.melding-balk`, `.hulp`,
   `.scan-kaart`, …); zonder deze regel krimpt zo'n kaartrand naar de tekst
   (shrink-to-fit), reserveert verticale padding geen ruimte meer en worden
   verticale marges genegeerd — precies de verschuivingen die de omzetting van
   `<div role="status">` naar `<output>` anders zou opleveren.
   De bestaande `<output class="gedempt klein">` staan in `.toolbalk`,
   `.bulkbalk` en `.weergavebalk` (alle drie `display: flex`) en zijn daar al
   geblockificeerd, dus hun computed value verandert hier niet. De twee die hun
   eigen display zetten (`.leegstand` grid, `.melding-undo` flex) winnen op
   specificiteit. */
output { display: block; }

.gedempt { color: var(--tekst-gedempt); }
.klein { font-size: var(--maat-12); }

/* Times, counts, money. */
.numeriek { font-variant-numeric: tabular-nums; }

/* Merk-regel: letter-spaced caps, Montserrat SemiBold 12px, primair. */
.merkregel {
  font-family: var(--font-kop);
  font-weight: 600;
  font-size: var(--maat-12);
  letter-spacing: var(--letterafstand-merk);
  text-transform: uppercase;
  color: var(--kleur-primair);
}

/* Dotted-line motif (from the logo) as a section divider — sparing. */
.stippellijn {
  border: none;
  border-top: 3px dotted var(--kleur-merk-rood);
  margin: var(--ruimte-24) 0;
}
.stippellijn-primair { border-top-color: var(--kleur-primair); }

/* ---- Focus (the only shadow in the app) --------------------------------- */

:focus-visible {
  outline: 2px solid var(--kleur-primair);
  outline-offset: 1px;
  box-shadow: var(--focusring);
}

/* ---- Header & nav: fixed shallow bar (D-103, D-112 vertical economy) ---- */

.kop-balk {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: var(--ruimte-24);
  padding: 0 var(--ruimte-16);
  min-height: 48px;
  background: var(--wit);
  border-bottom: 1px solid var(--rand);
}

/* Archief-modus (W-050, D-033): the unmistakable visual shift — muted panel
 * fill instead of white, plus a merk-rood baseline (the same "editie marker"
 * use of merk-rood as .badge-editie, D-119) — existing tokens only. */
.kop-balk--archief {
  background: var(--vlak);
  border-bottom: 1px solid var(--kleur-merk-rood);
}

.merk {
  font-family: var(--font-kop);
  font-weight: 600;
  font-size: var(--maat-12);
  letter-spacing: var(--letterafstand-merk);
  text-transform: uppercase;
  color: var(--kleur-primair);
  text-decoration: none;
  white-space: nowrap;
}

/* Actieve persoon in de kop (W-171, D-101). Rechts uitgelijnd omdat de
 * persoonsschil daar niets anders heeft staan (geen hoofdnav, geen kop-acties);
 * de bestuursschil krijgt dit blok niet, dus het botst niet met de bestuurskop.
 * Bestaande tokens, geen nieuwe kleuren.
 * De naam wordt nooit afgekapt — hem onleesbaar maken haalt de hele reden weg —
 * dus hij mag afbreken en de kop meegroeien (min-height, geen height). */
.kop-persoon {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--ruimte-8);
  min-width: 0;
  padding: var(--ruimte-4) 0;
  font-size: var(--maat-12);
  line-height: 1.3;
}

.kop-persoon-label { color: var(--tekst-gedempt); }
.kop-persoon strong { color: var(--kleur-kop); overflow-wrap: anywhere; }

.kop-avatar {
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--rand);
}

.hoofdnav {
  display: flex;
  gap: var(--ruimte-4);
  overflow-x: auto;              /* nav never wraps to a second row */
  -webkit-overflow-scrolling: touch;
}

/* Archief, Verlaat archief en Uitloggen zijn POST-formulieren met een
 * CSRF-token, geen link (W-400) — ze staan dus als <form> tussen de
 * <a>-elementen. `display: contents` laat het formulier zelf uit de flexrij
 * verdwijnen, zodat de <button> erin het echte flex/kolom-item wordt en
 * dezelfde plek krijgt als een nav-<a>; anders zou het <form>-blok een eigen
 * doos met margin om de knop trekken en als vreemde eend in de rij staan.
 *
 * De knop deelt ELKE opmaakregel met de link, hier en in de smalle-schermquery
 * verderop, en staat daarom in dezelfde selector in plaats van in een eigen
 * blok ernaast. Twee blokken die hetzelfde moeten zeggen lopen uiteen zodra
 * iemand er één aanpast — dezelfde val die W-397 zes dagen lang openhield. Wat
 * de knop extra krijgt is uitsluitend het wegpoetsen van de browserknop. */
.hoofdnav form { display: contents; }

.hoofdnav a,
.hoofdnav button {
  font-family: var(--font-kop);
  font-weight: 500;
  font-size: 15px;
  color: var(--tekst);
  text-decoration: none;
  padding: 13px var(--ruimte-8) 11px;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}

.hoofdnav button {
  background: none;
  line-height: inherit;         /* een knop erft die niet vanzelf van de kopbalk */
  border-left: none;
  border-right: none;
  border-top: none;
  text-align: left;
  cursor: pointer;
}

.hoofdnav a:hover,
.hoofdnav button:hover { color: var(--kleur-primair); background: var(--kleur-selectie); }

.hoofdnav a.actief {
  color: var(--kleur-primair);
  font-weight: 600;
  border-bottom-color: var(--kleur-primair);
}

/* ---- Kopbalk op een telefoon (W-257) ------------------------------------
 *
 * Negen navigatie-items pasten niet, en stonden daarom in een horizontaal
 * scrollende rij. Dat is geen oplossing maar een verstopplek: op een telefoon
 * zie je er drie en niets zegt dat er zes meer zijn. Onder 720px klapt de nav
 * dicht achter een hamburger; daarboven verandert er niets.
 *
 * De open/dicht-toestand hangt aan aria-expanded op de knop en niet aan een
 * eigen klasse — één waarheid, die bovendien de toestand ook aan een
 * schermlezer vertelt. Zonder JavaScript staat aria-expanded op "false" maar
 * bestaat de knop niet als toggle; daarom verbergt de regel hieronder de nav
 * alleen als er ook echt een zichtbare knop naast staat.
 */
.menuknop {
  display: none;               /* pas zichtbaar onder de breekgrens */
  flex: none;
  margin-left: auto;
  width: var(--aanraakdoel);
  height: var(--aanraakdoel);
  padding: 0;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: 1px solid var(--rand);
  border-radius: var(--radius-knop);
  cursor: pointer;
}

.menuknop-streep {
  display: block;
  width: 20px;
  height: 2px;
  background: var(--kleur-kop);
}

.menuknop[aria-expanded="true"] {
  background: var(--kleur-selectie);
  border-color: var(--kleur-primair);
}

.menuknop[aria-expanded="true"] .menuknop-streep { background: var(--kleur-primair); }

@media (max-width: 719px) {
  .menuknop { display: flex; }

  .kop-balk { flex-wrap: wrap; gap: var(--ruimte-8); }

  /* De nav valt onder het logo op een eigen rij, als een lijst en niet als een
   * rij die wegscrollt: op deze breedte is verticaal de enige vorm waarin
   * negen items alle negen te zien zijn. */
  .hoofdnav {
    flex-basis: 100%;
    flex-direction: column;
    gap: 0;
    overflow-x: visible;
    border-top: 1px solid var(--rand);
  }

  /* De knop van Archief/Verlaat archief/Uitloggen loopt mee met de link (W-400,
   * zie de toelichting bij .hoofdnav form hierboven): in de verticale lijst is
   * een nav-item een rij met een randlijn eronder, en dat geldt voor alle drie
   * de vormen. */
  .hoofdnav a,
  .hoofdnav button {
    padding: var(--ruimte-12) var(--ruimte-8);
    border-bottom: 1px solid var(--rand);
    border-left: 3px solid transparent;
  }

  .hoofdnav a.actief {
    border-bottom-color: var(--rand);
    border-left-color: var(--kleur-primair);
    background: var(--kleur-selectie);
  }

  .menuknop[aria-expanded="false"] + .hoofdnav { display: none; }

  /* W-400: hier stond ooit `.kop-acties { flex-basis: 100%; … }`, om de rij
   * Zoeken/Archief/Uitloggen die niet naast logo + Mijn + hamburger paste op
   * een eigen volle rij te zetten. Archief en Uitloggen zijn verhuisd naar de
   * hoofdnav hierboven; kop-acties draagt nu alleen nog Zoeken, en dat past
   * gewoon op de eerste rij — een geforceerde eigen rij zou dat juist weer
   * breken (acceptatie-eis 1: logo, Mijn, Zoeken, hamburger op één regel). */
}

/* ---- Page shell --------------------------------------------------------- */

main {
  max-width: var(--inhoud-max); /* line-length ceiling, fluid below it */
  margin: 0 auto;
  padding: var(--ruimte-24) var(--ruimte-16);
}

.pagina-kop {
  display: flex;
  align-items: baseline;
  gap: var(--ruimte-12);
  flex-wrap: wrap;
  margin-bottom: var(--ruimte-16);
}
.pagina-kop h1 { margin-bottom: 0; }


/* Fluid grid: columns reflow by available space, no breakpoint soup.

   De ondergrens ging in W-301 van 280px naar 360px. Bij 280px paste er op een
   gewoon laptopscherm een vierde kolom bij, en dan is elke tegel zo smal dat
   de labelkolom van .gegevens (minmax(120px, …)) bijna de halve tegel opeet —
   de inhoud staat er dan technisch in, maar geperst. Drie ruimere tegels lezen
   beter dan vier krappe.

   Nog steeds auto-fit en geen vaste drie: op een breed scherm mag er een
   vierde kolom bij komen, en die is dan óók 360px of breder. De grens gaat
   over de minimale leesbare tegelbreedte, niet over een aantal. */
.grid-fluid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr));
  gap: var(--ruimte-16);
  /* W-306: het tegelraster is een <div> en wat erop volgt een <section>, dus de
     regel `section + section` hieronder raakt deze overgang niet. Zonder deze
     marge plakken de actieblokken (Archief, Afmelden, AVG-wissen) direct tegen
     de onderste tegelrij aan, op vier detailpagina's tegelijk. Dezelfde
     ruimte als tussen twee secties, want het is dezelfde soort overgang. */
  margin-bottom: var(--ruimte-24);
}

/* Een kaart die de volle rasterbreedte pakt (W-306). `1 / -1` en niet
   `span 2`: bij één kolom — een telefoon — zou span 2 een tweede, lege kolom
   aanmaken en het raster juist breken. Dit werkt bij elk aantal kolommen.

   Bedoeld voor een blok dat formulieren of een brede lijst draagt, waar een
   tegel van 360px de inhoud meetbaar krapper maakt dan nodig. Niet gebruiken
   om een blok "belangrijker" te laten ogen: dan is de tegelbreedte het
   verkeerde gereedschap. */
.kaart-vol { grid-column: 1 / -1; }

/* ---- Buttons: two variants only (STYLE.md §5) --------------------------- */

.knop {
  display: inline-flex;
  align-items: center;
  gap: var(--ruimte-8);
  padding: var(--knopvulling);
  font-family: var(--font-basis);
  font-size: var(--maat-14);
  line-height: 1.2;
  border-radius: var(--radius-knop);
  cursor: pointer;
  text-decoration: none;
}

/* Primair: blauw gevuld. */
.knop-primair {
  background: var(--kleur-primair);
  color: var(--wit);
  border: 1px solid var(--kleur-primair);
}
.knop-primair:hover { background: var(--kleur-kop); border-color: var(--kleur-kop); }

/* Secundair: wit, blauwe rand. */
.knop-secundair {
  background: var(--wit);
  color: var(--kleur-primair);
  border: 1px solid var(--kleur-primair);
}
.knop-secundair:hover { background: var(--kleur-selectie); }

/* Destructief = secundair met signaalrood tekst. */
.knop-destructief {
  background: var(--wit);
  color: var(--signaalrood);
  border: 1px solid var(--signaalrood);
}
.knop-destructief:hover { background: var(--sein-rood-vlak); }

.knop:disabled, .knop[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

@media (pointer: coarse) {
  .knop { min-height: var(--aanraakdoel); } /* D-092 touch targets */
}

/* ---- Badges: pill (STYLE.md §3) ----------------------------------------- */

.badge {
  display: inline-block;
  padding: 1px 10px;
  border-radius: var(--radius-pil);
  font-size: var(--maat-12);
  line-height: 1.6;
  background: var(--vlak);
  color: var(--tekst-gedempt);
  border: 1px solid var(--rand);
}
.badge-groen { background: var(--sein-groen-vlak); color: var(--sein-groen-tekst); border-color: transparent; }
.badge-amber { background: var(--sein-amber-vlak); color: var(--sein-amber-tekst); border-color: transparent; }
.badge-rood  { background: var(--sein-rood-vlak);  color: var(--sein-rood-tekst);  border-color: transparent; }
/* Editie marker: merk-rood is identity, never error (D-119). */
.badge-editie { background: var(--wit); color: var(--kleur-merk-rood); border-color: var(--kleur-merk-rood); }

/* Het notitieteken (W-250): één karakter in plaats van een woord.
   .badge staat op padding 1px 10px, want die is gemaakt voor "gepubliceerd" en
   "onder voorbehoud". Om één uitroepteken heen levert dat een pil op die twee
   keer zo breed is als zijn inhoud; 6px maakt er een rond tekentje van. Verder
   erft hij alles van .badge, inclusief rand en gedempte kleur — het teken is
   een signaal, geen waarschuwing. */
.badge-teken { padding: 0 6px; font-weight: 600; }

/* De kolom eromheen: zo smal als zijn inhoud. `width: 1%` laat de tabel de
   kolom tot het minimum knijpen en de vrijgekomen breedte aan de tekstkolommen
   geven — de gebruikelijke truc voor een tekenkolom. Geldt voor kop én cel,
   zodat het teken onder zijn eigen "!" staat en niet ernaast. */
.tabel th.teken,
.tabel td.teken { width: 1%; white-space: nowrap; text-align: center; }

/* ---- Cards -------------------------------------------------------------- */

.kaart {
  background: var(--wit);
  border: 1px solid var(--rand);
  border-radius: var(--radius-kaart);
  padding: var(--ruimte-16);
}

.kaart h2, .kaart h3 { margin-bottom: var(--ruimte-8); }

/* Container queries: cards adapt to their container, which makes D-112's
 * overlay side panels cheap. */
.kaart-houder { container-type: inline-size; }

.persoon-kaart {
  display: flex;
  gap: var(--ruimte-12);
  align-items: center;
}
.persoon-kaart .avatar {
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: 50%;
  background: var(--kleur-selectie);
  color: var(--kleur-primair);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-kop);
  font-weight: 600;
}
@container (max-width: 240px) {
  .persoon-kaart { flex-direction: column; align-items: flex-start; }
}

/* ---- Lists (dense, in-list references clickable — D-092) ---------------- */

.lijst {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--rand);
  border-radius: var(--radius-knop);
}

.lijst li {
  display: flex;
  align-items: center;
  gap: var(--ruimte-12);
  padding: var(--ruimte-8) var(--ruimte-12);
  font-size: var(--maat-13);
  line-height: var(--regelhoogte-dicht);
}
.lijst li + li { border-top: 1px solid var(--rand); }
.lijst li:hover { background: var(--kleur-selectie); }
.meta { color: var(--tekst-gedempt); font-size: var(--maat-12); }
.lijst .meta { margin-left: auto; color: var(--tekst-gedempt); font-size: var(--maat-12); }

/* ---- Tables (dense UI, column priority — D-112/D-113) ------------------- */

.tabel-houder { overflow-x: auto; } /* horizontal scroll is last resort, never strategy */

table.tabel {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--maat-13);
  line-height: var(--regelhoogte-dicht);
}

.tabel th {
  text-align: left;
  font-family: var(--font-kop);
  font-weight: 600;
  font-size: var(--maat-12);
  color: var(--kleur-kop);
  background: var(--vlak);
  padding: var(--celvulling);
  border-bottom: 1px solid var(--rand);
}

.tabel td {
  padding: var(--celvulling);
  border-bottom: 1px solid var(--rand);
}

.tabel tbody tr:hover { background: var(--kleur-selectie); }
.tabel tr.actief { background: var(--kleur-selectie); }

/* Column priority: essential columns always survive; useful ones drop below
 * ~1024px, nice-to-have below ~640px. Detail stays one click away. */
@media (max-width: 1023px) { .kol-extra  { display: none; } }
@media (max-width: 639px)  { .kol-nuttig { display: none; } }

/* Coverage cells (rooster family). Vier standen, D-232(5): leeg rood, deels
 * amber, vol-maar-iets-nog-concept blauw, vol-en-gepubliceerd groen. Blauw zit
 * tussen compleet en gepubliceerd in — bemenst, maar nog niet iedereen weet
 * het — dus groen betekent "af". Kleur is nooit het enige kanaal: de teller en
 * het woord "(concept)" staan er als tekst naast. */
.cel-vol     { background: var(--sein-groen-vlak); color: var(--sein-groen-tekst); }
.cel-concept { background: var(--sein-blauw-vlak); color: var(--sein-blauw-tekst); }
.cel-deels   { background: var(--sein-amber-vlak); color: var(--sein-amber-tekst); }
.cel-leeg    { background: var(--sein-rood-vlak);  color: var(--sein-rood-tekst); }

/* ---- Forms -------------------------------------------------------------- */

.formulier { display: grid; gap: var(--ruimte-12); max-width: 560px; }

.veld { display: grid; gap: var(--ruimte-4); }

.veld label {
  font-family: var(--font-kop);
  font-weight: 500;
  font-size: var(--maat-13);
  color: var(--kleur-kop);
}

.veld input[type="text"],
.veld input[type="password"],
.veld input[type="search"],
.veld input[type="email"],
.veld input[type="number"],
.veld input[type="date"],
.veld input[type="time"],
.veld select,
.veld textarea {
  font-family: var(--font-basis);
  font-size: var(--maat-14);
  padding: var(--invoervulling);
  border: 1px solid var(--rand-invoer);
  border-radius: var(--radius-knop);
  background: var(--wit);
  color: var(--tekst);
  width: 100%;
}

/* .hulp en .meta worden in 48 templates ook BUITEN een .veld gebruikt, als
   toelichting onder een kop of naast een waarde. Zonder deze kale regels
   renderden die als gewone bodytekst: de hele bedoelde hiërarchie -- uitleg
   gedempt en klein, meta nog kleiner -- viel weg en elke pagina werd een
   vlakke muur van 14px. De .veld- en .lijst-varianten hieronder/hierboven
   blijven staan; die voegen alleen positionering toe. */
.hulp { font-size: var(--maat-12); color: var(--tekst-gedempt); }
.veld .hulp { font-size: var(--maat-12); color: var(--tekst-gedempt); }

/* De tekstteller onder een vrij tekstveld (W-366).
   Dezelfde maat en kleur als .hulp -- hij is een terloopse mededeling, geen
   waarschuwing -- tot de tekst over de grens gaat. Rechts uitgelijnd zodat hij
   niet met de hulptekst eronder concurreert. Het element is een <output>, dat
   de juiste rol al zelf draagt (D-315).
   static/js/teller.js maakt hem aan en schakelt hem op focus/blur; zonder
   JavaScript bestaat hij niet en werkt het formulier gewoon. */
.teller {
  display: block;
  text-align: right;
  font-size: var(--maat-12);
  color: var(--tekst-gedempt);
  font-variant-numeric: tabular-nums;
}
/* W-474: hier stond `display: none`. Een klik op een knop of vakje vlak onder
   het tekstvak begint met mousedown, dat het veld al zijn focus laat verliezen
   voordat mouseup vuurt; `blur` verstopte de teller meteen, de bladspiegel
   schoof een regel omhoog, en de knop die onder de vinger stond stond bij
   mouseup niet meer onder de muis -- de browser vuurt `click` dan op de
   gemeenschappelijke voorouder, niet op de knop. `visibility` verbergt de tekst
   maar laat het blok in de flow: zijn hoogte blijft gereserveerd, dus blur
   verplaatst niets meer en de klik komt aan (gemeten bij W-393 fase 3,
   docs/BACKLOG.md W-474). `pointer-events` is niet nodig: `visibility: hidden`
   sluit het element zelf al uit van hit-testing. */
.teller[hidden] { visibility: hidden; }
.teller-over { color: var(--signaalrood); }

/* Error state: calmer semantic red, never merk-rood. */
.veld.fout input, .veld.fout select, .veld.fout textarea { border-color: var(--signaalrood); }

/* W-488 — het tekstveld "Anders, nl:" bij de dieetwens is alleen zichtbaar
   zolang de dropdown op Anders staat. Puur CSS met :has() en geen JavaScript:
   de CSP verbiedt inline JS, en een script zou na elke htmx-swap (het blok
   wisselt met outerHTML) opnieuw moeten binden. Zonder :has-ondersteuning
   staat het veld gewoon altijd open — het formulier werkt dan nog steeds. Bij
   een veldfout (klasse fout) blijft het altijd zichtbaar, anders hoort een
   422 nergens bij. De hooks staan in templates/partials/dieet-velden.html. */
[data-dieet-veld]:has(select[name="dieet"] option[value="anders"]:not(:checked)) [data-dieet-toelichting]:not(.fout) {
	display: none;
}
/* W-506 — hetzelfde voor de externe groep op het productieformulier, waar
   "Anders" een getalveld is en geen dropdown. Een getalveld heeft geen
   :checked, wel :placeholder-shown: de vijf velden dragen placeholder 0 en
   een nul wordt als LEEG veld getekend (voegDieetToeAanW), dus "placeholder
   zichtbaar" is precies "geen anders". Live, want :placeholder-shown volgt de
   invoer en niet het value-attribuut. De hooks staan in
   templates/partials/productie-formulier.html. */
[data-productie-dieet-veld]:has(input[name="dieet_anders"]:placeholder-shown) [data-dieet-toelichting]:not(.fout) {
	display: none;
}
[data-productie-dieet-veld] [data-dieet-toelichting] { margin-top: 0.5rem; }
.foutmelding { color: var(--signaalrood); }
.veld .foutmelding { font-size: var(--maat-12); color: var(--signaalrood); }

.keuze { display: flex; align-items: center; gap: var(--ruimte-8); font-size: var(--maat-14); }
.keuze label { font-family: var(--font-basis); font-weight: 400; color: var(--tekst); }

.formulier-acties { display: flex; gap: var(--ruimte-8); margin-top: var(--ruimte-4); }

/* Wider variant for forms that carry a map pane. */
.formulier-breed { max-width: 760px; }

/* Fieldsets group without chrome; the legend is the section head. */
.formulier fieldset {
  border: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--ruimte-8);
}
.formulier legend {
  font-family: var(--font-kop);
  font-weight: 600;
  font-size: var(--maat-14);
  color: var(--kleur-kop);
  padding: 0;
  margin-bottom: var(--ruimte-4);
}
.formulier fieldset + fieldset { margin-top: var(--ruimte-16); }

/* Short fields side by side, reflowing by available space. */
.veldgroep {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--ruimte-12);
}

/* Label look for grouped controls that have no single <label>. */
.veldlabel {
  font-family: var(--font-kop);
  font-weight: 500;
  font-size: var(--maat-13);
  color: var(--kleur-kop);
}

@media (pointer: coarse) {
  .veld input, .veld select { min-height: var(--aanraakdoel); }
}

/* ---- Panel (overlay side panel — D-112: overlay, never squeeze) --------- */

.paneel {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(420px, 100%);
  z-index: 20;
  background: var(--wit);
  border-left: 1px solid var(--rand);
  padding: var(--ruimte-16);
  overflow-y: auto;
}

.paneel-kop {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ruimte-12);
  margin-bottom: var(--ruimte-12);
}
.paneel-kop h2 { margin: 0; }

@media (max-width: 639px) {
  .paneel { width: 100%; border-left: none; } /* single column below ~640 */
}

/* ---- Signaalregel (⚠ count → click, D-093 anti-dashboard shape) --------- */

.signaalregel {
  display: flex;
  align-items: center;
  gap: var(--ruimte-8);
  padding: var(--ruimte-8) var(--ruimte-12);
  border: 1px solid var(--rand);
  border-left: 3px solid var(--signaalrood);
  border-radius: var(--radius-knop);
  font-size: var(--maat-13);
  color: var(--tekst);
  text-decoration: none;
}
.signaalregel:hover { background: var(--kleur-selectie); }
.signaalregel .aantal {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--signaalrood);
}
.signaalregel .pijl { margin-left: auto; color: var(--kleur-primair); }
.signaalregels { display: grid; gap: var(--ruimte-8); }

/* ---- Empty state (skyline illustrations, STYLE.md §6) ------------------- */

.leegstand {
  display: grid;
  justify-items: center;
  gap: var(--ruimte-12);
  padding: var(--ruimte-24);
  text-align: center;
  color: var(--tekst-gedempt);
}
.leegstand img { max-width: 100%; height: 96px; }
.leegstand p { margin: 0; }

/* ---- Global search (home front door, D-104) ----------------------------- */

.zoek-groot { max-width: 560px; margin: 0 auto; }
.zoek-groot input[type="search"] {
  width: 100%;
  font-size: var(--maat-16);
  padding: var(--ruimte-8) var(--ruimte-12);
  border: 1px solid var(--rand-invoer);
  border-radius: var(--radius-knop);
}
#zoekresultaten { max-width: 560px; margin: var(--ruimte-8) auto 0; }

/* ---- Utilities ---------------------------------------------------------- */

img { max-width: 100%; }

/* Full-width readonly field (e.g. the enrollment link to copy). */
.veld-breed { width: 100%; box-sizing: border-box; }

.rij { display: flex; align-items: center; gap: var(--ruimte-8); flex-wrap: wrap; }

.mt-16 { margin-top: var(--ruimte-16); }
.mb-16 { margin-bottom: var(--ruimte-16); }

.persoon-kaart p { margin: 0; }

/* Color swatches (stijlgids). Inline style= is blocked by the CSP
 * (style-src 'self'), so every swatch is a class. */
.staal {
  display: inline-block;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-knop);
  border: 1px solid var(--rand);
  vertical-align: middle;
}
.staal-primair       { background: var(--kleur-primair); }
.staal-kop           { background: var(--kleur-kop); }
.staal-selectie      { background: var(--kleur-selectie); }
.staal-selectie-rand { background: var(--kleur-selectie-rand); }
.staal-merk-rood     { background: var(--kleur-merk-rood); }
.staal-signaalrood   { background: var(--signaalrood); }

/* Plain text with whitespace preserved — migrated long text is plain text by
 * design (D-118) and renders escaped with line breaks kept. */
.preline { white-space: pre-line; }

/* ---- List equipment (filter/sort/search toolbar — D-113) ---------------- */

.filterbalk {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--ruimte-12);
  margin-bottom: var(--ruimte-12);
}
.filterbalk .veld { min-width: 140px; }
.filterbalk .veld-zoek { flex: 1 1 220px; }

/* Filterpaneel (W-203): het scherm achter de Filter-knop. De vorm komt van de
 * bestaande .paneel-klasse hierboven — lade op breed, volledig scherm onder
 * ~640 (D-112's tweede breekpunt). Dat is met opzet CSS en geen Go-tak: de
 * viewport is server-side niet bekend bij het eerste verzoek, dus één partial
 * en één render-pad. Hieronder alleen wat .paneel nog niet had. */
.filterpaneel { z-index: 30; display: flex; flex-direction: column; }
.filterpaneel-form { max-width: none; overflow-y: auto; }

/* De acties blijven in beeld: op een telefoon vult het paneel het hele scherm
 * en zou Toepassen anders onder de vouw verdwijnen. */
.filterpaneel-acties {
  position: sticky;
  bottom: 0;
  align-items: center;
  flex-wrap: wrap;
  background: var(--wit);
  border-top: 1px solid var(--rand);
  padding-top: var(--ruimte-12);
  margin-top: var(--ruimte-8);
}

.filterpaneel-sluit {
  border: none;
  background: none;
  color: var(--tekst-gedempt);
  font-size: var(--maat-20);
  line-height: 1;
  padding: var(--ruimte-4);
  cursor: pointer;
}
.filterpaneel-sluit:hover { color: var(--tekst); }

/* De lege houder mag geen ruimte innemen zolang het paneel dicht is. */
.filterpaneel-houder:empty { display: none; }

/* W-204: hetzelfde paneel als pil-editor in Beheer. Daar staat het IN de
 * pagina, dus zonder de position:fixed-lade van .paneel — een editor die als
 * overlay over het beheerscherm valt zou de lijst met bestaande pillen
 * bedekken, precies wat je bij het samenstellen naast je wilt hebben. Verder
 * niets eigens: dezelfde velden, dezelfde .veld/.knop-vormgeving. */
.filterpaneel-ingebed {
  border: 1px solid var(--rand);
  border-radius: var(--radius-knop);
  padding: var(--ruimte-16);
  margin-top: var(--ruimte-12);
}
.filterpaneel-ingebed .filterpaneel-acties { position: static; }

/* Count + export row above a table. */
.toolbalk {
  display: flex;
  align-items: baseline;
  gap: var(--ruimte-12);
  margin-bottom: var(--ruimte-8);
}
.toolbalk .rechts { margin-left: auto; }

/* Sortable column headers: the link inherits the header look; the active
 * sort direction is a text arrow, not an icon font. */
.tabel th a { color: inherit; text-decoration: none; }
.tabel th a:hover { color: var(--kleur-primair); }
.tabel th .sorteer { color: var(--kleur-primair); }

/* ---- Key-value pairs on detail pages ------------------------------------ */

/* De waardekolom is minmax(0, 1fr) en niet 1fr, en dat is geen smaak (W-301).
   Een grid-item heeft standaard `min-width: auto`: het krimpt nooit onder de
   min-content-breedte van zijn inhoud. Een lang e-mailadres zonder breekpunt
   duwt de kolom dan breder dan de tegel, en de tekst wordt door de tegelrand
   doormidden gesneden — letterlijk zichtbaar op de persoonsdetailpagina.
   `.rooster-kolommen` deed dit al goed; hier was het niet toegepast. */
.gegevens {
  display: grid;
  grid-template-columns: minmax(120px, max-content) minmax(0, 1fr);
  gap: var(--ruimte-4) var(--ruimte-16);
  margin: 0;
  font-size: var(--maat-13);
  line-height: var(--regelhoogte-dicht);
}
.gegevens dt { color: var(--tekst-gedempt); }
/* De tweede helft, en zonder deze werkt de eerste niet: de kolom mag nu wel
   krimpen, maar een adres zonder spatie heeft geen breekpunt en loopt er dan
   alsnog uit. `anywhere` en niet `break-word`, omdat alleen `anywhere` ook
   meetelt bij het berekenen van de min-content-breedte — precies het getal
   waar de kolom hierboven op afgaat. */
.gegevens dd { margin: 0; overflow-wrap: anywhere; }

/* ---- Map pane (Leaflet, D-098) ------------------------------------------ */

.kaart-vlak {
  height: 320px;
  border: 1px solid var(--rand);
  border-radius: var(--radius-knop);
}
.kaart-vlak-klein { height: 240px; }
.kaart-hulp { margin-top: var(--ruimte-4); }
.kaart-fout { margin-top: var(--ruimte-8); }

/* De overzichtskaart van de routekaart (W-367). Hij is het onderwerp van zijn
 * scherm en niet een detail ernaast, dus hij is ruim: Herman vroeg 2026-08-25
 * uitdrukkelijk om de dubbele hoogte, en de eerste versie (60vh, max 620px)
 * was te laag om te zien of locaties ten opzichte van elkaar kloppen — wat de
 * hele reden van dit scherm is. Hoger en NIET breder: de kaart blijft binnen
 * dezelfde kolom als de lijst ernaast.
 *
 * De knop "Posities opslaan" komt hierdoor onder de vouw. Dat is aanvaard en
 * niet vergeten: de opslagbalk is sticky (zie .kaart-opslaan), dus hij blijft
 * in beeld terwijl je sleept. */
.kaart-vlak-groot { height: clamp(640px, 85vh, 1200px); }

/* ---- De speld van de overzichtskaart (W-369) -----------------------------
 *
 * Een gekleurde cirkel met het programmanummer erin, exact het beeld dat het
 * PHP-beheerpaneel maakt (index.php ~r.474) en dus exact wat de bezoeker op de
 * publieke kaart ziet. Alleen de WEG is anders: index.php zet de kleur in een
 * `style`-attribuut, en dat werkt hier niet — de CSP draagt `style-src 'self'`
 * zonder 'unsafe-inline', dus de browser negeert zo'n attribuut zonder een
 * fout te melden en de speld wordt wit. Vandaar klassen plus tokens.
 *
 * De kleur komt uit tokens.css, waar hij één keer staat; de categorie waar hij
 * bij hoort komt uit kaartAard, gedeeld met de publieke geojson. Een categorie
 * die de woordenschat niet kent, krijgt geen eigen klasse en houdt dus de
 * grijze achtergrond hieronder — dezelfde terugval als op de publieke kaart.
 *
 * CONTRAST, EN DAT IS EEN VASTGELEGDE KEUZE. Het cijfer is wit op elke kleur,
 * ook op de gele --kaartkleur-toilet: ongeveer 1,6:1 waar WCAG 4,5:1 vraagt.
 * Herman is daarop gewezen en koos bij W-369 uitdrukkelijk voor exact gelijk
 * aan de publieke kaart, omdat twee kaarten die identiek ogen ook iets waard
 * zijn. Dit is dus geen over het hoofd geziene fout: repareer het op alle vier
 * de plekken tegelijk (zie de lijst in tokens.css) of nergens. Kleur is hier
 * bovendien niet het enige kanaal — de naam staat in de tooltip en de legenda
 * benoemt elke categorie in tekst. */
/* Een kale klasse volstaat, en dat is niet vanzelfsprekend: Leaflet zet
 * `.leaflet-marker-icon { display: block }` met exact dezelfde specificiteit
 * (leaflet-1.9.4.css:42-44). Dat het hier tóch werkt, komt doordat base.html de
 * vendor-CSS in `head_vendor_css` laadt VÓÓR app.css — zie de toelichting daar.
 * Stond die link weer in `head_extra`, dan won Leaflet en viel het cijfer stil
 * terug naar linksboven. Gemeten op 2026-08-25 (W-369). */
.kaart-speld {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--kaartkleur-onbekend);
  color: var(--wit);
  font-family: var(--font-basis);
  font-size: var(--maat-14);
  font-weight: 600;
  line-height: 1;
}
/* EHBO is groter en draagt het woord in plaats van een nummer — de uitzondering
 * die het PHP-paneel ook maakt (index.php r.481), en hij hoort bij "exact". */
.kaart-speld-groot {
  width: 50px;
  height: 50px;
  font-size: var(--maat-16);
}
.kaart-speld-ehbo { background: var(--kaartkleur-ehbo); }
.kaart-speld-opening { background: var(--kaartkleur-opening); }
.kaart-speld-binnen { background: var(--kaartkleur-binnen); }
.kaart-speld-buiten { background: var(--kaartkleur-buiten); }
.kaart-speld-mobiel { background: var(--kaartkleur-mobiel); }
.kaart-speld-toilet { background: var(--kaartkleur-toilet); }
.kaart-speld-horeca { background: var(--kaartkleur-horeca); }
/* Expliciet grijs voor de legenda-stip "Zonder aard". Op de kaart zelf komt
 * grijs uit de basisregel hierboven — een speld zonder herkende categorie krijgt
 * geen kleurklasse — maar de legenda moet die toestand kunnen BENOEMEN, en dan
 * is een klasse die niets zegt geen optie. */
.kaart-speld-onbekend { background: var(--kaartkleur-onbekend); }

/* Een speld die verzet is en nog niet opgeslagen. Dit was een `filter`, omdat
 * de Leaflet-standaardspeld een <img> was en verkleuren de enige weg. Met een
 * divIcon vervalt die reden, en de oude weg is nu bovendien SCHADELIJK: een
 * hue-rotate op een speld waarvan de kleur betekenis draagt, maakt van een
 * EHBO-post een willekeurige andere categorie. Een ring eromheen zegt hetzelfde
 * — "deze heb je verzet en nog niet opgeslagen" — en laat de kleurcodering
 * intact. */
.kaart-speld-verzet {
  box-shadow: 0 0 0 3px var(--wit), 0 0 0 6px var(--kleur-primair);
}

/* De legenda onder de kaart. Dezelfde speldklassen als op de kaart zelf, zodat
 * de stip niet los kan lopen van wat er getekend wordt. */
.kaart-legenda { margin-top: var(--ruimte-12); }
.kaart-legenda h2 { margin-bottom: var(--ruimte-4); }
.kaart-legenda-lijst {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ruimte-4) var(--ruimte-16);
  margin: 0 0 var(--ruimte-4);
  padding: 0;
  list-style: none;
}
.kaart-legenda-lijst li {
  display: flex;
  align-items: center;
  gap: var(--ruimte-4);
}
/* Geen achtergrond hier: die komt van de kaart-speld-klasse ernaast. Zou de
 * stip zelf een kleur zetten, dan zou de volgorde in dit bestand bepalen welke
 * wint — en dat is precies het soort afhankelijkheid dat een jaar later
 * onverklaarbaar grijs oplevert. */
.kaart-legenda-stip {
  width: 12px;
  height: 12px;
  border-radius: 50%;
}

/* Sticky, want de kaart erboven is hoger dan het scherm: zonder dit staat de
 * enige opslagknop van een expliciete opslagstap onder de vouw, en dan is een
 * sleep die je niet bewaart net zo stil als de bug die dit scherm moest
 * wegnemen. */
.kaart-opslaan {
  position: sticky;
  bottom: 0;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ruimte-8);
  margin-top: var(--ruimte-16);
  padding: var(--ruimte-12) 0;
  background: var(--wit);
}

/* Bezig-toestand voor een knop die op een ander systeem wacht (W-244a): de
 * SMTP-verbindingstest, de geojson-push en de Laposta-synchronisatie. Alleen
 * die drie -- een spinner op elke knop zegt niets meer over "dit duurt even".
 * Hier stonden tot 2026-08-06 de drie .htmx-indicator-regels; die waren dood
 * (nul gebruikers in templates/ en static/js/) en konden dat ook niet worden:
 * alle drie de knoppen zijn gewone form-POSTs, dus htmx draait er nooit op en
 * .htmx-request verschijnt er nooit. */
.knop-bezig { pointer-events: none; opacity: 0.65; }

/* ---- Uitloggen (kop-balk, auth package) ----------------------------------
 * W-400: hier stonden `.uitloggen { margin-left: auto; }` en
 * `.uitloggen .knop { font-size: … }`, toen het formulier nog los in
 * kop-acties stond. Het staat nu in <nav class="hoofdnav"> met
 * `display: contents` (zie .hoofdnav form hierboven) — een element zonder
 * eigen box heeft niets aan een margin, en de knop draagt geen .knop-klasse
 * meer, dus beide regels waren dood. */

/* ---- Mijn Toneelroute (journey 1) — mobile-first personal page (D-112) ---- */

.mijn {
  max-width: 640px;
  margin: 0 auto;
  display: grid;
  gap: var(--ruimte-24, 24px);
}

.mijn-kop h1 { margin: var(--ruimte-4) 0 0; }

/* Twee paginablokken onder elkaar raken elkaar niet (W-306, W-308).
   `section + section` dekte alleen secties, en de blokken van deze app zijn
   soms een <section> en soms een <div> — `editiedeelname` is zelfs allebei,
   afhankelijk van de pagina: op rekwisieten staat hij ín het tegelraster, op
   producties eronder als paginablok. Een regel die op het ELEMENT afgaat, moet
   daarom wel een keer misgaan, en dat is drie keer gebeurd.

   Deze regel gaat op de ROL af: `.kaart` betekent "dit is een blok", en de
   uitzondering is precies één geval — een kaart die een rastercel is. Vandaar
   `:not(.grid-fluid) >`: binnen het raster doet `gap` het werk, en een marge
   daar zou de tegelrijen uit lijn duwen. */
:not(.grid-fluid) > .kaart + .kaart { margin-top: var(--ruimte-24); }

/* Binnen één sectie staan blokken dichter op elkaar: het zijn regels van
   dezelfde lijst (de actiepunten, de rollen), geen losse paginablokken. Ná de
   regel hierboven, want de specificiteit is gelijk en dan wint de volgorde. */
.mijn-sectie .kaart + .kaart { margin-top: var(--ruimte-12); }

.mijn-sectie h2 {
  font-size: var(--maat-14);
  color: var(--tekst-gedempt);
  margin-bottom: var(--ruimte-8);
}

.mijn-lijst li { align-items: flex-start; }
.mijn-regel p { margin: 0; }
.mijn-regel { display: grid; gap: var(--ruimte-4); }

.eerstvolgende {
  background: var(--kleur-selectie);
  border-color: var(--kleur-selectie-rand);
}
.eerstvolgende p { margin: 0 0 var(--ruimte-4); }
.eerstvolgende-titel { font-weight: 600; color: var(--kleur-kop); }

.rsvp-acties { display: flex; gap: var(--ruimte-8); margin-top: var(--ruimte-8); }
.rsvp-acties .knop { flex: 1 1 auto; }

.avatar-groot {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--rand);
}

.melding-ok {
  background: var(--sein-groen-vlak);
  color: var(--sein-groen-tekst);
  border-color: transparent;
}

/* Degraded state: something could not be determined. Deliberately amber and
   not green — "niet gemeten" mag nooit lezen als "in orde" (W-147). */
.melding-amber {
  background: var(--sein-amber-vlak);
  color: var(--sein-amber-tekst);
  border-color: transparent;
}

.mijn-voet {
  display: grid;
  gap: var(--ruimte-12);
  justify-items: center;
  padding-bottom: var(--ruimte-24, 24px);
}

.mijn-rol details summary { cursor: pointer; color: var(--kleur-primair); }

/* ===== Dienstenrooster (journey 2, package 7) =====================   Board + fill panel + beheer. Reuses the fixed vocabulary (card, badge,
   knop, tabel, filterbalk); the additions below are all board-specific
   (rooster-*) plus three generic utilities the vocabulary lacked
   (.inline, .destructief, .visueel-verborgen). MERGE NOTE: promote the three
   utilities into the shared vocabulary if another package needs them. */

.inline { display: inline-block; margin: 0; }
.destructief { color: var(--sein-rood-tekst); border-color: var(--rand-invoer); }
.visueel-verborgen {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* W-101 blok B: de dagkop van de eendaagse kalender — pijl, datum, pijl. */
.rooster-dagkop { display: flex; align-items: center; gap: var(--ruimte-12); margin-bottom: var(--ruimte-16); }
.rooster-dagtitel { font-size: var(--maat-20); margin: 0; }
.rooster-pijl {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.25rem; height: 2.25rem; border: 1px solid var(--rand); border-radius: 99px;
  color: var(--kleur-primair); text-decoration: none; font-size: var(--maat-20); line-height: 1;
}
.rooster-pijl:hover { background: var(--vlak); }
.rooster-pijl.uit { color: var(--tekst-gedempt); border-color: var(--rand-invoer); cursor: default; }
/* W-101 blok D: de dagbrede publiceerknop staat rechts in de dagkop, tegenover
   de pijlen. Geen waarschuwingstekst op de knop — de bevestigingsdialoog
   erachter is de rem (D-232(6)). */
.rooster-dagknop { margin-left: auto; }

.rooster-sectie { margin-bottom: var(--ruimte-24); }
.rooster-sectie-kop { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--ruimte-8); margin-bottom: var(--ruimte-8); }
.rooster-sectie-titel { font-size: var(--maat-16); margin: 0; }
.rooster-sectie-teller { font-size: var(--maat-13); color: var(--tekst-gedempt); font-variant-numeric: tabular-nums; }
.rooster-sectie-teller.open { color: var(--sein-rood-tekst); font-weight: 600; }

.rooster-werkblad { display: grid; grid-template-columns: 1fr; gap: var(--ruimte-16); }
@media (min-width: 1024px) { .rooster-werkblad { grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr); align-items: start; } }

/* Hoogstens drie tegelkolommen (W-101 blok B). Expliciete breakpoints in plaats
   van auto-fit: dat laatste vult de breedte met zoveel kolommen als er passen,
   dus vier of vijf op een breed scherm. Bij één kolom voegt de tegel zich naar
   de volle breedte in plaats van een smal geworden kolomopmaak te houden.
   Bewaakt door TestRoosterTegelsHoogstensDrieKolommen. */
.rooster-kolommen { display: grid; grid-template-columns: 1fr; gap: var(--ruimte-12); }
@media (min-width: 640px) { .rooster-kolommen { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1280px) { .rooster-kolommen { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.rooster-kolom { border: 1px solid var(--rand); border-radius: 12px; overflow: hidden; }
.rooster-kolom-kop { padding: var(--ruimte-8); background: var(--vlak); border-bottom: 1px solid var(--rand); display: grid; gap: 2px; }
.rooster-locatie { font-weight: 600; color: var(--kleur-kop); }

/* W-101 blok C (D-232(5)): de tegel draagt de dekkingsklasse van zijn
 * SLECHTSTE slot, zodat een locatie met een gat vindbaar is terwijl je scrollt.
 * De kop wordt doorzichtig zodat de kleur van de tegel er doorheen komt — de
 * .cel-*-regel hierboven zet de vulling en de tekstkleur al — en de slotbanden
 * eronder houden hun eigen kleur, zodat je ziet wélk slot het gat is. */
.rooster-kolom.cel-vol > .rooster-kolom-kop,
.rooster-kolom.cel-concept > .rooster-kolom-kop,
.rooster-kolom.cel-deels > .rooster-kolom-kop,
.rooster-kolom.cel-leeg > .rooster-kolom-kop { background: transparent; }
.rooster-kolom.cel-vol .rooster-locatie,
.rooster-kolom.cel-concept .rooster-locatie,
.rooster-kolom.cel-deels .rooster-locatie,
.rooster-kolom.cel-leeg .rooster-locatie { color: inherit; }

.rooster-slot {
  display: grid; gap: 2px; width: 100%; text-align: left; cursor: pointer;
  padding: var(--ruimte-8); border: none; border-bottom: 1px solid var(--rand);
  font: inherit;
}
/* Twee selectors en niet één, sinds W-456: elke tegel heeft een papieren
 * tweeling naast zich (`.rooster-slot.alleen-print`), dus de laatste ZICHTBARE
 * tegel is op het scherm de op-één-na-laatste knoop en op papier de laatste.
 * Met alleen `:last-child` kreeg de onderste schermtegel zijn scheidingslijn
 * terug, pal boven de onderrand van de kolom — een dubbele streep. */
.rooster-slot:nth-last-child(2), .rooster-slot:last-child { border-bottom: none; }
.rooster-slot:hover { filter: brightness(0.97); }
.rooster-slot.actief { outline: 3px solid var(--kleur-primair); outline-offset: -3px; }
.rooster-slot-tijd { font-weight: 600; }
.rooster-slot-teller { font-variant-numeric: tabular-nums; }
.rooster-slot-namen { font-size: var(--maat-12); }

.rooster-panel { border: 1px solid var(--rand); border-radius: 12px; padding: var(--ruimte-16); background: var(--wit); }
.rooster-panel h2 { font-size: var(--maat-16); margin: 0 0 var(--ruimte-4); }
.rooster-toegewezen { list-style: none; margin: var(--ruimte-12) 0; padding: 0; display: grid; gap: var(--ruimte-8); }
.rooster-kandidaten { margin: var(--ruimte-12) 0 0; }
.rooster-toegewezen li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ruimte-8); }
.rooster-toegewezen-naam { font-weight: 600; }
/* De rij houdt zijn twee acties (concept / publiceer) en zijn conflictregels;
   alleen de omlijsting en de ruimte komen nu uit .kiezerlijst. */
.rooster-kandidaat { display: grid; gap: var(--ruimte-4); }
.rooster-kandidaat-info { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ruimte-8); }
.rooster-kandidaat-acties { display: flex; flex-wrap: wrap; gap: var(--ruimte-4); }
.rooster-conflict { color: var(--sein-rood-tekst); font-size: var(--maat-12); width: 100%; }
.rooster-zoek { margin: var(--ruimte-12) 0; }
.rooster-clone-blok { margin-top: var(--ruimte-16); }
.rooster-clone-blok summary { cursor: pointer; color: var(--kleur-primair); }
.rooster-beheer { display: grid; gap: var(--ruimte-16); }

/* ── Producties & casting board (journey 4) ─────────────────────────────────
   Generic, reusable additions to the component vocabulary: a content section
   (blok), small button variants, an inline form that sits next to text, and
   the casting-board / picker layout. */

.blok { margin: var(--ruimte-24) 0; }
.blok > h2 { margin-bottom: var(--ruimte-8); }

.knop-klein { padding: 4px var(--ruimte-8); font-size: var(--maat-12); }
.knop-mini {
  padding: 0 6px;
  font-size: var(--maat-12);
  line-height: 1.6;
  border-radius: var(--rond-4, 4px);
}

/* A form that reads as part of a line of text (quick-set, delete buttons). */
.inline-form { display: inline-flex; align-items: center; gap: var(--ruimte-8); margin: 0; }

.casting-board { display: flex; flex-direction: column; gap: var(--ruimte-12); }

.casting-rol {
  border: 1px solid var(--rand);
  border-radius: var(--rond-8, 8px);
  padding: var(--ruimte-12) var(--ruimte-16);
  background: var(--wit);
}
.casting-rol.waarschuwing { border-color: var(--sein-amber-tekst); background: var(--sein-amber-vlak); }

.rol-kop { display: flex; align-items: center; gap: var(--ruimte-8); }
.rol-kop .rechts { margin-left: auto; display: inline-flex; align-items: center; gap: var(--ruimte-8); }

.rol-bezetting { display: flex; flex-wrap: wrap; gap: var(--ruimte-8); margin: var(--ruimte-8) 0; }

.cast-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--ruimte-4);
  padding: 2px var(--ruimte-8);
  background: var(--sein-groen-vlak);
  border-radius: 999px;
}

.kiezer { margin-top: var(--ruimte-8); }
/* Was een omwikkelende rij inline knoppen (W-284): één regel per speler leest
   sneller en geeft een groter aanraakdoel. */
.kiezer-resultaten { margin-top: var(--ruimte-8); }
/* Elke optie is een eigen mini-formulier (aanklikken cast meteen), maar leest
   als een regel: de knop draagt .kiezer-regel en dus de vorm van de lijst. */
.kiezer-optie { margin: 0; }

.upload-form { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ruimte-8); margin-top: var(--ruimte-12); }

/* Content-photo thumbnail (kledingstuk/rekwisiet foto, K2/R2 — D-094). Bounded
   so a large source never blows out the card; served from /bestanden/{id}. */
.foto-thumb { max-width: 100%; max-height: 240px; height: auto; border-radius: var(--radius, 6px); display: block; }

/* ===== Kleding & kledingtas (journey 5, package 9) ================   Inventory + tassen + intake scanner + controle. Reuses the fixed vocabulary
   (tabel, badge, knop, filterbalk, toolbalk, kaart, gegevens, leegstand) plus
   the generic .blok/.inline-form/.knop-klein the producties block above already
   defines (deduped on the package-8 merge). .mt-8 and the kleding-/tas-/scan-
   rules below are this package's own. */

.mt-8 { margin-top: var(--ruimte-8); }

/* Controle checklist — inline conditie radios per piece (D-076). */
.conditie-keuze { display: flex; flex-wrap: wrap; gap: var(--ruimte-12); }
.keuze-inline { display: inline-flex; align-items: center; gap: var(--ruimte-4); white-space: nowrap; }

.tas-acties { display: flex; flex-wrap: wrap; gap: var(--ruimte-8); margin-top: var(--ruimte-12); }

/* Delete-confirm disclosure on tas-detail (samengesteld only): a no-JS,
   CSP-safe <details> whose <summary> is styled as a destructive button. */
.tas-verwijderen summary { cursor: pointer; list-style: none; display: inline-block; }
.tas-verwijderen summary::-webkit-details-marker { display: none; }
.tas-verwijderen[open] summary { margin-bottom: var(--ruimte-8); }

/* Printable tag (D-055/D-013): code + QR + naam. */
.tas-tag {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--ruimte-24);
  border: 2px solid var(--rand); border-radius: var(--rond-8, 8px);
  padding: var(--ruimte-24); background: var(--wit); max-width: 640px;
}
.tas-tag-code { display: grid; gap: var(--ruimte-4); }
.tas-tag-label { font-weight: 600; color: var(--kleur-primair); }
.tas-tag-code strong { font-size: var(--maat-24, 1.5rem); }
.tas-tag-slug { font-size: var(--maat-16); letter-spacing: 0.1em; }
.tas-tag-qr { width: 256px; height: 256px; }

/* Intake scanner (K5) — mobile capture page. */
.scan-kop { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--ruimte-12); margin-bottom: var(--ruimte-16); }
.scan-teller {
  margin-left: auto; font-weight: 600; font-size: var(--maat-18, 1.125rem);
  padding: var(--ruimte-4) var(--ruimte-12); background: var(--vlak); border-radius: 99px;
}
.scan-camera {
  position: relative; border: 2px dashed var(--rand); border-radius: var(--rond-8, 8px);
  background: #000; overflow: hidden; margin-bottom: var(--ruimte-16); min-height: 200px;
  display: flex; align-items: center; justify-content: center;
}
.scan-video { width: 100%; max-height: 60vh; display: block; }
.scan-camera-uit { background: var(--vlak); }
.scan-camera-uit .scan-video { display: none; }
.scan-camera-status {
  position: absolute; left: 0; right: 0; bottom: 0; margin: 0;
  padding: var(--ruimte-8); text-align: center; font-size: var(--maat-13);
  color: var(--wit); background: rgba(0, 0, 0, 0.55);
}
.scan-camera-uit .scan-camera-status { position: static; color: var(--tekst-gedempt); background: transparent; }

.scan-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--ruimte-8); margin-bottom: var(--ruimte-16); }
.scan-form .veld-zoek { flex: 1 1 220px; }

.scan-lijst { display: grid; gap: var(--ruimte-8); }
.scan-kaart { padding: var(--ruimte-8) var(--ruimte-12); border-radius: var(--rond-4, 4px); border: 1px solid var(--rand); }
.scan-kaart-terug { background: var(--sein-groen-vlak); border-color: transparent; }
.scan-kaart-al { background: var(--sein-amber-vlak); border-color: transparent; }
.scan-kaart-onbekend { background: var(--sein-rood-vlak); border-color: transparent; }

/* ===== Financiën (journey 6, package 10) ================================
   Geld: boekjaar switcher + staat van baten en lasten + uitgaven/inkomsten
   lists + quick-add + begroting + categoriebeheer. Reuses the fixed
   vocabulary (card, badge, knop, tabel, filterbalk, toolbalk); the additions
   below are finance-specific plus one generic utility (.formulier-smal) and a
   print face for the staat. MERGE NOTE: promote .formulier-smal, .rij-acties
   and .cursief to the shared vocabulary if another package needs them. */

.formulier-smal { max-width: 32rem; display: grid; gap: var(--ruimte-16); }
.cursief { font-style: italic; }
.rij-acties { text-align: right; white-space: nowrap; }

.boekjaar-balk { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--ruimte-12); margin: var(--ruimte-12) 0 var(--ruimte-16); }
.boekjaar-label { font-weight: 600; color: var(--kleur-kop); }
.boekjaar-pillen { display: flex; flex-wrap: wrap; gap: var(--ruimte-4); }
.boekjaar-pil { padding: var(--ruimte-4) var(--ruimte-8); border: 1px solid var(--rand); border-radius: 99px; color: var(--kleur-primair); text-decoration: none; font-size: var(--maat-13); font-variant-numeric: tabular-nums; }
.boekjaar-pil.actief { background: var(--kleur-primair); color: var(--wit); border-color: var(--kleur-primair); }

/* W-456/D-343 — DE STAAT PAST OP ÉÉN A4, EN DAT IS EEN SCHERMWIJZIGING.
 * Herman, 2026-09-03: "de ruimte tussen de saldoregels is te groot. Het past nu
 * net niet op 1 A4 pagina. De ruimte iets indikken, ook op het scherm."
 *
 * Het rekensommetje achter de twee getallen hieronder. De staat is een grid met
 * acht kinderen op een gevulde pagina (Baten, Lasten, vier saldoregels, de
 * toelichting, de actieregel), dus zeven tussenruimten. Van 24px naar 12px
 * scheelt 7 x 12 = 84px. De vier saldoregels hadden 16px boven én onder; 8px
 * boven en onder scheelt nog eens 4 x 16 = 64px. Samen ~148px op een bruikbare
 * pagina van 1032px (A4 staand minus de @page-marge hieronder) — genoeg voor de
 * vier tot vijf regels die er te veel waren.
 *
 * INDIKKEN EN NIET VERKLEINEN: de bedragen houden hun 24px, want dat is wat de
 * saldoregel van een tabelrij onderscheidt. Wat weggaat is lucht, niet nadruk. */
.staat { display: grid; gap: var(--ruimte-12); max-width: 46rem; }
.staat-blok h2 { font-size: var(--maat-16); color: var(--kleur-kop); margin: 0 0 var(--ruimte-8); }
.staat-tabel td.numeriek, .staat-tabel th.numeriek { text-align: right; font-variant-numeric: tabular-nums; }
.staat-totaal th, .staat-totaal td { font-weight: 700; border-top: 2px solid var(--rand); }
.bedrag-negatief { color: var(--sein-rood-tekst); font-weight: 600; }

.staat-saldo { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--ruimte-12); padding: var(--ruimte-8) var(--ruimte-12); }
.staat-saldo-label { font-weight: 600; color: var(--kleur-kop); }
.staat-saldo-bedrag { font-size: var(--maat-24, 1.5rem); font-weight: 700; color: var(--kleur-kop); font-variant-numeric: tabular-nums; }
.staat-acties { margin-top: var(--ruimte-16); }
/* Deze toelichting is een gridkind van .staat en draagt dus de browsermarge van
 * een <p> (1em boven én onder) BOVENOP de gap — marges klappen in een grid niet
 * samen. Dat waren 28px die niemand had gekozen, en ze stonden precies tussen de
 * saldoregels waar het te ruim was (W-456). */
.staat-saldo-toelichting { margin: 0; }

/* Het bonblok van een uitgave en het bijlageblok van een inkomst (W-467) zijn
   hetzelfde ding met een ander woord: één bestand bij één rij, tonen, vervangen,
   verwijderen. Eén set regels dus, en geen tweede die er langzaam vanaf drijft. */
.bon-blok, .bijlage-blok { margin-top: var(--ruimte-24); max-width: 32rem; display: grid; gap: var(--ruimte-12); }
.bon-blok h2, .bijlage-blok h2 { font-size: var(--maat-16); color: var(--kleur-kop); margin: 0; }
.bon-huidig, .bijlage-huidig { margin: 0; }
.bon-upload, .bijlage-upload { display: grid; gap: var(--ruimte-8); }


.begroting-rij { display: flex; flex-wrap: wrap; gap: var(--ruimte-8); align-items: center; margin: 0; }
.begroting-rij input[type="text"] { max-width: 10rem; }
.categorie-nieuw { margin-bottom: var(--ruimte-16); }
.categorie-nieuw .rij, .categorie-hernoem { display: flex; gap: var(--ruimte-8); align-items: center; }
.categorie-hernoem { margin: 0; }
.categorie-hernoem input[type="text"] { max-width: 18rem; }

/* ── Journey 7 — generales (package 5). Generic, reusable pieces the fixed
      component vocabulary lacked: a text-only button, a status message bar, the
      planner day-sequence, and the schema print face. Reuses the shared
      .inline-form and .destructief utilities from the rooster/casting block. ── */

.knop-tekst {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--kleur-primair);
  cursor: pointer;
}
.knop-tekst:hover { text-decoration: underline; }

.melding-balk {
  background: var(--kleur-selectie);
  border: 1px solid var(--kleur-selectie-rand);
  border-radius: var(--radius, 8px);
  padding: var(--ruimte-12) var(--ruimte-16);
  margin-bottom: var(--ruimte-16);
}

.conflicten {
  border: 1px solid var(--rand);
  border-radius: var(--radius, 8px);
  padding: var(--ruimte-12) var(--ruimte-16);
  margin-bottom: var(--ruimte-16);
}
.conflicten h2 { margin-top: 0; }
.signaal-lijst { list-style: none; margin: var(--ruimte-8) 0 0; padding: 0; display: grid; gap: var(--ruimte-4, 4px); }
.signaal-regel { color: var(--sein-rood-tekst); }

.dag-blok { margin-bottom: var(--ruimte-24, 24px); }
.sequentie { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--ruimte-8); }
.sequentie-item {
  display: grid;
  grid-template-columns: 8rem 1fr;
  gap: var(--ruimte-4, 4px) var(--ruimte-16);
  padding: var(--ruimte-12);
  border: 1px solid var(--rand);
  border-radius: var(--radius, 8px);
  background: var(--wit);
}
.sequentie-tijd { color: var(--kleur-kop); font-weight: 600; }
.sequentie-titel { margin: 0; font-weight: 500; }
.sequentie-acties { margin-top: var(--ruimte-4, 4px); display: flex; gap: var(--ruimte-16); align-items: center; }
.sequentie-item.waarschuwing { border-color: var(--sein-rood-vlak); background: #fdf7f7; }
.sequentie-item.pauze { background: var(--vlak); }

@media (max-width: 640px) {
  .sequentie-item { grid-template-columns: 1fr; }
}

/* ===== Draaiboek & rekwisieten (journey 3, package 11a) =================
   S4 editor + S5 master timeline (draaiboek items interleaved with diensten,
   a "Nu" strip, and a print face) + R1/R2 rekwisieten. Reuses the fixed
   vocabulary (tabel, badge, knop, filterbalk, toolbalk, veldgroep, fieldset,
   leegstand); the additions below are timeline-specific plus one generic
   utility (.tijd, .geen-print). MERGE NOTE: promote .tijd and .geen-print to
   the shared vocabulary if another package needs them. */

.tijd { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* "Nu" strip (S5 beat 4): active + next timeline rows, highlighted. */
.nu-strip {
  border: 1px solid var(--kleur-primair); border-radius: var(--rond-8, 8px);
  padding: var(--ruimte-16); margin-bottom: var(--ruimte-24); background: var(--vlak);
}
.nu-strip .lijst li { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--ruimte-8); }

.tijdlijn-dag { margin-top: var(--ruimte-24); }
.tijdlijn-titel { font-weight: 600; color: var(--kleur-kop); }

/* Timeline rows: the active row stands out, past rows recede. */
.tabel.tijdlijn tr.nu-actief > td { background: var(--sein-groen-vlak); }
.tabel.tijdlijn tr.verleden > td { color: var(--tekst-gedempt); }

/* ── UX-ronde 2026-07-25 (D-163..D-173) — de gedeelde generieken ─────────────
   Sectietabs vervangen .rooster-tab (pills lazen als knoppen, D-164). De rest
   is nieuw vocabulaire uit STYLE.md §5a-§5g; schermen bouwen hier alleen mee,
   nooit een eigen variant (D-170). */

/* §5a Sectietabs — naamwoorden navigeren. Underline, nooit pill. */
/* padding-bottom is 3px en niet 2px, en dat is geen smaak: .sectietab draagt
 * margin-bottom: -3px, dus zijn onderrand steekt 3px onder de contentbox uit.
 * Bij 2px bleef er 1px over, en omdat overflow-x: auto van overflow-y géén
 * `visible` meer kan maken (CSS-regel) leverde die ene pixel een VERTICALE
 * scrollbar op naast elke tabrij. Zichtbaar op /locaties, 2026-08-25. */
.sectietabs {
  display: flex; gap: var(--ruimte-16); margin: var(--ruimte-16) 0 var(--ruimte-12);
  padding: 0 0 3px; border-bottom: 1px solid var(--rand);
  overflow-x: auto; overflow-y: hidden; scrollbar-width: thin;
}
.sectietab {
  flex: 0 0 auto; padding: var(--ruimte-8) 2px; margin-bottom: -3px;
  color: var(--tekst-gedempt); text-decoration: none; white-space: nowrap;
  border-bottom: 3px solid transparent;
}
.sectietab:hover { color: var(--tekst); border-bottom-color: var(--kleur-selectie-rand); }
.sectietab.actief { color: var(--kleur-primair); font-weight: 600; border-bottom-color: var(--kleur-primair); }
.tabtelling {
  display: inline-block; margin-left: var(--ruimte-4); padding: 0 var(--ruimte-8);
  border-radius: 99px; background: var(--vlak); color: var(--tekst-gedempt);
  font-size: var(--maat-12); font-variant-numeric: tabular-nums;
}
.sectietab.actief .tabtelling { background: var(--kleur-selectie); color: var(--kleur-primair); }

/* §5a flow-variant (W-114, D-164) — een decoratieve pijl tussen tabs die een
 * causale volgorde tonen (Kleding: Voorraad → Toewijzing → Samenstellen →
 * Tassen → Inname). `.sectietabs-stroom` is een modifier op de bestaande
 * `.sectietabs`, geen nieuw component: dezelfde flex-rij, dezelfde
 * horizontal-scroll. De pijl zelf (`.sectietab-pijl`) is een los <span
 * aria-hidden="true">, geen ::after op de tab: pseudo-elementinhoud
 * (`content:`) kan in sommige browser/schermlezer-combinaties toch in de
 * accessibility-tree belanden, en `aria-hidden` is op een pseudo-element niet
 * te zetten — een los element is de enige manier om "nooit als inhoud
 * voorgelezen" hard te garanderen. Small en gedempt (`--rand`, dezelfde tint
 * als de tabstrip-baseline): D-164 waarschuwt dat een tabstrip navigatie
 * blijft, geen wizard, dus de pijl mag nooit meer contrast trekken dan de
 * tabs zelf. Geen pijl vóór een tab die geen stap in de keten is (Kleding:
 * Reparatie/Vermist) — die twee zijn uitzonderingen, geen zesde/zevende
 * stap; de bestaande `gap` van de flex-rij scheidt ze zonder een pijl die een
 * keten claimt die er niet is. */
.sectietabs-stroom .sectietab-pijl {
  flex: 0 0 auto; align-self: center; white-space: nowrap;
  color: var(--rand); font-size: var(--maat-14); line-height: 1;
  user-select: none;
}

/* §5b Weergavebalk + filterchips — filters zijn zichtbaar, nooit verborgen staat. */
.weergavebalk {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--ruimte-8);
  margin-bottom: var(--ruimte-12);
}
.chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ruimte-4); }
.chip {
  display: inline-flex; align-items: center; gap: var(--ruimte-4);
  padding: var(--ruimte-4) var(--ruimte-8); border-radius: 99px;
  background: var(--kleur-selectie); border: 1px solid var(--kleur-selectie-rand);
  color: var(--kleur-kop); font-size: var(--maat-13);
}
/* A chip that is itself the clickable control (W-092 functie switcher: a
 * single-select filter rendered as chips, no × per chip) is an <a>, not a
 * <span> + button — drop the browser's default link underline so it still
 * reads as a pill. */
a.chip { text-decoration: none; }
a.chip:hover { background: var(--kleur-selectie-rand); }
/* Een standaardscope ziet er anders uit dan een zelf gekozen filter: gestippeld. */
.chip-scope { background: var(--vlak); border-style: dashed; }
.chip-wis {
  background: none; border: none; padding: var(--ruimte-4);
  color: var(--kleur-primair); font: inherit; font-size: var(--maat-13); cursor: pointer;
  text-decoration: underline;
}
.chip-af {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border: none; border-radius: 99px;
  background: none; color: var(--kleur-kop); font: inherit; line-height: 1; cursor: pointer;
}
.chip-af:hover { background: var(--kleur-selectie-rand); }
/* Het aanraakvlak van het kruisje staat in het W-396-blok onderaan: daar wordt
   het met een ::after opgerekt in plaats van door de chip zelf hoger te maken.
   Zo blijft D-092 gehaald zonder dat een filterchip twee keer zo hoog wordt als
   de bereikpil ernaast (gemeld 2026-08-26). */

/* W-205: chip = stand (klik zet een filter UIT), pil = ingang (klik zet een
 * filter AAN, blijft --kleur-selectie blauw hierboven) — twee verschillende
 * handelingen mogen niet identiek ogen, en stonden hier allebei in hetzelfde
 * pale blue direct tegen elkaar aan. Bestaande badge-groentint hergebruikt
 * (D-170: geen nieuwe kleur), dezelfde tint als .badge-groen/.cel-vol, die al
 * "actief/vol" betekent — een amber- of roodtint zou hier ten onrechte een
 * waarschuwing suggereren. .chip-scope (de niet-afneembare standaardscope,
 * gestippeld grijs) blijft bewust buiten deze regel: die onderscheiding
 * bestond al en is geen deel van dit probleem. Contrast #EAF3DE/#27500A
 * gemeten (WCAG-formule, relatieve luminantie): ~8.2:1, ruim boven de 4.5:1
 * die AA voor 13px tekst eist. Deze stijlsheet heeft geen dark-theme variant
 * (tokens.css: "Light theme only"), dus één richting is de volledige proef. */
.chips-filters .chip:not(.chip-scope) {
  background: var(--sein-groen-vlak); border-color: transparent; color: var(--sein-groen-tekst);
}
.chips-filters a.chip:not(.chip-scope):hover,
.chips-filters button.chip:not(.chip-scope):hover {
  border-color: var(--sein-groen-tekst);
}
.chips-filters .chip:not(.chip-scope) .chip-af { color: var(--sein-groen-tekst); }
.chips-filters .chip:not(.chip-scope) .chip-af:hover {
  background: color-mix(in srgb, var(--sein-groen-tekst) 20%, transparent);
}
/* Een chip is niet altijd een <span>: als filterlink is hij een <a>, als
   éénklikactie een submit-<button> (actielijst, D-182d). Dezelfde vorm, dus
   dezelfde regels — knoppen erven het pagina-lettertype niet vanzelf en links
   zijn standaard onderstreept. */
a.chip, button.chip { font-family: inherit; text-decoration: none; cursor: pointer; }
a.chip:hover, button.chip:hover { border-color: var(--kleur-primair); }
a.chip[aria-current], button.chip:disabled { cursor: default; }
a.chip-af { text-decoration: none; }

/* W-107: chip as a CHOOSER (one-owner assign, single-select filter, status)
 * needs on/off contrast, not the removable-filter look .chip owns — the
 * actielijst reused .chip/.chip-scope for this and both states read as the
 * same pale blue. Modifier, not a redefinition of .chip (D-170): redefining
 * would silently restyle every removable filter chip in the app. Unselected
 * = white fill + --rand-invoer border (measured ~15:1 text contrast, tekst
 * on wit); selected = --kleur-primair fill + white text (measured ~6.9:1,
 * passes AA). Combine with .chip for shape: class="chip chip-keuze", add
 * chip-keuze-actief for the chosen one. */
.chip-keuze { background: var(--wit); border-color: var(--rand-invoer); color: var(--tekst); }
a.chip-keuze:hover, button.chip-keuze:hover { background: var(--kleur-selectie); }
.chip-keuze-actief,
a.chip-keuze-actief:hover, button.chip-keuze-actief:hover {
  background: var(--kleur-primair); border-color: var(--kleur-primair); color: var(--wit);
}

/* §5c Selectie + bulkbalk. */
.rij-geselecteerd > td { background: var(--kleur-selectie); }
.bulkbalk {
  position: sticky; bottom: var(--ruimte-12); z-index: 5;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--ruimte-12);
  padding: var(--ruimte-8) var(--ruimte-12); border-radius: 12px;
  background: var(--kleur-kop); color: var(--wit);
}
.bulkbalk .knop-secundair { background: transparent; color: var(--wit); border-color: var(--wit); }
.bulkbalk-teller { font-weight: 600; font-variant-numeric: tabular-nums; }
.bulkbalk-uitsluiting { color: var(--kleur-selectie-rand); font-size: var(--maat-13); }
.bulkbalk a, .bulkbalk button.tekstknop { color: var(--wit); }
/* De balk draagt twee echte formulieren (GET voor exporteren/mailen, POST voor
   magic links/Laposta — gescheiden zodat het CSRF-token nooit in een
   querystring belandt). display:contents laat hun knoppen gewoon meedoen in de
   flexrij, alsof de formulieren er niet tussen zitten. */
.bulkbalk form { display: contents; }
/* Selectiekolom: zo smal mogelijk, want de essenti&euml;le kolommen moeten op
   1366px blijven passen (D-112). */
.kol-selectie { width: 1%; white-space: nowrap; }
.kol-selectie input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; }
/* Preset-chips (W-036) zetten een filter in plaats van het weg te halen, dus
   ze lezen als knoppen, niet als "actieve toestand". */
.chips-preset { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ruimte-8); }
/* W-351: gekozen en niet-gekozen stonden hier 1.06:1 uit elkaar — #EAF0F9 tegen
 * #F4F6FA, vier hexstappen in dezelfde tint, met bij allebei dezelfde rand. Dat
 * is geen onderscheid maar een nuance, en op een pilrij die het BEREIK van de
 * hele lijst bepaalt las het als "er staat wat blauws".
 *
 * Dit is de derde keer dat deze vorm terugkomt: W-107 gaf de actielijst
 * .chip-keuze omdat filter-blauw en gekozen-blauw daar identiek lazen, W-205
 * verhuisde de filterchips naar groen omdat een STAND en een INGANG niet
 * identiek mogen ogen. De ingang-pillen zelf waren het laatste paar dat nog
 * bleekblauw tegen bleekblauw stond.
 *
 * Gekozen: wit tegen --sein-blauw-vlak, allebei bestaande tokens (D-170), plus
 * een eigen rand zodat de omtrek meedoet. Gemeten met de WCAG-formule:
 * vulling/vulling 1.06:1 -> 1.22:1, ruim drie keer de oude sprong. Tekst blijft
 * --kleur-kop en houdt 10.7:1 op de gekozen vulling, ver boven de 4.5:1 die AA
 * bij 13px eist.
 *
 * BEWUST NIET verder: --kleur-primair vullen is .chip-keuze-actief in de
 * actielijst, en die sprong is voor dit besturingselement te groot; groen is
 * hier al bezet door "afneembaar filter" (W-205) en twee betekenissen op één
 * kleur is precies wat die ronde oploste.
 *
 * Beide rijen die .chips-preset gebruiken — Bereik en Doelgroep — krijgen dit,
 * want het is hetzelfde besturingselement met hetzelfde probleem. Ze uit elkaar
 * trekken zou nieuwe markup vragen zonder dat er iets mee opgelost wordt. */
.chip-preset { background: var(--wit); }
.chips-preset .chip[aria-current] {
  background: var(--sein-blauw-vlak);
  border-color: var(--sein-blauw-rand);
}

/* §5d Lege staat die bekent: nooit een blanco tabel zonder uitleg. */
.leegstand-uitleg {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--ruimte-8); color: var(--tekst-gedempt); font-size: var(--maat-13);
}

/* §5e Impact-dialoog — wat hangt hieraan, vóór het verwijderen. */
.impact {
  width: min(560px, calc(100vw - var(--ruimte-24)));
  padding: var(--ruimte-16); border: 1px solid var(--rand); border-radius: 12px;
  color: var(--tekst); background: var(--wit);
}
dialog.impact::backdrop { background: rgb(29 48 86 / 45%); }
.impact-kop { margin: 0 0 var(--ruimte-8); font-family: var(--font-kop); color: var(--kleur-kop); }
.impact-lijst { margin: 0 0 var(--ruimte-16); padding-left: var(--ruimte-16); }
.impact-lijst li { margin-bottom: var(--ruimte-4); }
.impact-lijst li .gedempt { margin-left: var(--ruimte-8); }
.impact-acties { display: flex; flex-wrap: wrap; gap: var(--ruimte-8); justify-content: flex-end; }
/* Eén groep per soort, geteld — nooit één ongesorteerde hoop afhankelijkheden. */
.impact-groep { margin-bottom: var(--ruimte-16); }
.impact-groep-kop {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--ruimte-8); margin: 0 0 var(--ruimte-4);
}
/* Het beleid van W-030 staat er letterlijk bij: de gebruiker hoort te zien of
   iets blokkeert of gewoon blijft staan, niet te moeten raden. */
.impact-beleid {
  padding: 1px var(--ruimte-8); border-radius: 999px;
  font-size: var(--maat-12); color: var(--tekst-gedempt); background: var(--vlak);
}
.impact-beleid-blokkeren { color: var(--sein-rood-tekst); background: var(--sein-rood-vlak); }
/* Volle-paginavariant (geen JavaScript): een <dialog> zonder `open` is
   display:none, dus daar wordt hij een gewoon blok. Zelfde markup, zelfde
   tekst — alleen niet modaal. */
.impact-pagina dialog.impact { display: block; margin-inline: auto; }

/* §5f ⌘K-palet — resultaten gegroepeerd per soort, nooit één gemengde ranglijst. */
.palet {
  width: min(640px, calc(100vw - var(--ruimte-24)));
  padding: 0; border: 1px solid var(--rand); border-radius: 12px; background: var(--wit);
}
.palet-invoer { width: 100%; padding: var(--ruimte-12); border: none; border-bottom: 1px solid var(--rand); font: inherit; font-size: var(--maat-16); }
.palet-groep { padding: var(--ruimte-8) var(--ruimte-12) var(--ruimte-4); color: var(--tekst-gedempt); font-size: var(--maat-12); text-transform: uppercase; letter-spacing: 1px; }
.palet-rij { display: flex; align-items: center; justify-content: space-between; gap: var(--ruimte-8); padding: var(--ruimte-8) var(--ruimte-12); color: var(--tekst); text-decoration: none; }
.palet-rij:hover, .palet-rij.actief { background: var(--kleur-selectie); }
.palet-meer { display: block; padding: var(--ruimte-4) var(--ruimte-12) var(--ruimte-8); color: var(--kleur-primair); font-size: var(--maat-13); }
.palet-toets { padding: 1px var(--ruimte-4); border: 1px solid var(--rand); border-radius: 4px; color: var(--tekst-gedempt); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--maat-12); }
.palet-titel { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* De uitslag zit óók op de home-pagina (één partial, twee ingangen); in de
   dialoog begrenst dit de hoogte, op de pagina groeit het gewoon mee. */
.palet-uitslag { overflow-y: auto; }
dialog.palet .palet-uitslag { max-height: min(60vh, 480px); }
.palet-melding { padding: var(--ruimte-12); margin: 0; color: var(--tekst-gedempt); font-size: var(--maat-13); }
.palet-voet { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ruimte-4); margin: 0; padding: var(--ruimte-8) var(--ruimte-12); border-top: 1px solid var(--rand); color: var(--tekst-gedempt); font-size: var(--maat-12); }
/* <dialog> centreert standaard verticaal; een palet hoort bovenin te staan. */
dialog.palet { margin-top: 10vh; margin-bottom: auto; }
dialog.palet::backdrop { background: rgb(29 48 86 / 45%); }
/* Kopbalk-ingang naast de sneltoets — nooit alleen een sneltoets (D-113). */
.kop-acties { display: flex; align-items: center; gap: var(--ruimte-8); margin-left: auto; }
.palet-knop {
  display: inline-flex; align-items: center; gap: var(--ruimte-4);
  padding: var(--ruimte-4) var(--ruimte-8); border: 1px solid var(--rand); border-radius: 8px;
  color: var(--tekst-gedempt); text-decoration: none; font-size: var(--maat-12); white-space: nowrap;
}
.palet-knop:hover { color: var(--kleur-primair); border-color: var(--kleur-primair); }

/* §5g Undo-melding — vervangt de bevestigingsdialoog voor alles wat omkeerbaar is. */
.melding-undo {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--ruimte-12);
  padding: var(--ruimte-8) var(--ruimte-12); border: 1px solid var(--rand);
  border-radius: 8px; background: var(--vlak);
}

/* ---- Actielijst: rollende lijst gegroepeerd op onderwerp (W-041, D-182) --- */
.actiegroep + .actiegroep { margin-top: var(--ruimte-16); }
.actiegroep-kop {
  margin-bottom: var(--ruimte-8);
  font-size: var(--maat-16); color: var(--kleur-kop);
}
.actiepunt + .actiepunt { margin-top: var(--ruimte-8); }
/* De twee éénklikbalken (wie / status) staan onder de tekst en lezen als één
   regel: label, dan de chips. */
.actiepunt-wie, .actiepunt-status { margin-top: var(--ruimte-8); }
.actiepunt details { margin-top: var(--ruimte-8); }
.actiepunt summary { cursor: pointer; color: var(--kleur-primair); }

/* ── W-040 Timetable — de meerjarenplanning (D-183) ──────────────────────────
   Twee gezichten op één dataset (D-035): de matrix (rubriek × maand, een balk
   per taak) en de lijst per verantwoordelijke. De lijst gebruikt uitsluitend de
   bestaande generieken (tabel · badge · chip · toolbalk · filterbalk ·
   leegstand); alleen de matrix heeft eigen vocabulaire nodig — een tabel met
   één cel per maand bestaat nog nergens anders.

   D-112 (1366px, `main` biedt 1168px): table-layout:fixed met een taakkolom van
   240px en maandkolommen van 40px. 21 maanden = 240 + 840 = 1080px en past dus
   zonder schuiven. Groeit de as verder (een derde jaar), dan schuift alleen
   .tt-houder horizontaal terwijl de taakkolom links vastgeplakt blijft — de
   pagina zelf schuift nooit mee. */

/* De matrix is een eigen schuifvlak, geen stuk pagina dat meeschuift. Reden:
   `overflow-x: auto` maakt de houder sowieso al een scroll-container (CSS laat
   `overflow-y: visible` dan naar `auto` verspringen), dus een `position: sticky`
   maandkop zou tegen de PAGINA-scroll nooit blijven plakken. Met een begrensde
   hoogte klopt het wél: 81 taken schuiven binnen dit vlak langs een maandkop en
   een taakkolom die allebei staan blijven, en de pagina eromheen staat stil. */
.tt-houder {
  max-width: 100%;
  max-height: calc(100vh - 260px);
  min-height: 220px;
  overflow: auto;
}

/* `table.tt-matrix` en niet `.tt-matrix`: de matrix draagt óók de klasse
   `tabel`, en `table.tabel` (0,1,1) hierboven wint van een kale klasse (0,1,0).
   Zolang deze regel `.tt-matrix` heette waren `width` en `font-size` dood —
   de tabel stond op `width: 100%` en 13px in plaats van max-content en 12px,
   precies de twee dingen die de toelichting hierboven belooft (W-447). */
table.tt-matrix {
  table-layout: fixed;
  width: max-content;
  max-width: none;
  border-collapse: collapse;
  font-size: var(--maat-12);
  line-height: var(--regelhoogte-dicht);
}

/* De maandkoppen plakken bovenaan het schuifvlak van .tt-houder (top: 0 is
   daarom relatief aan die houder, niet aan de pagina). z-index blijft ruim
   onder die van .kop-balk, zodat de hoofdnav er altijd overheen ligt. */
.tt-matrix thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--vlak);
  padding: var(--ruimte-4);
  text-align: center;
  white-space: nowrap;
}
.tt-matrix .tt-hoek,
.tt-matrix .tt-taak {
  width: 240px;
  min-width: 240px;
  position: sticky;
  left: 0;
  background: var(--wit);
  text-align: left;
}
.tt-matrix .tt-hoek { z-index: 4; background: var(--vlak); }
.tt-matrix .tt-taak { z-index: 1; font-weight: 400; padding: var(--ruimte-4) var(--ruimte-8); }
.tt-matrix tbody tr:hover .tt-taak { background: var(--kleur-selectie); }
.tt-matrix .tt-taak a { font-weight: 600; }
.tt-meta { display: block; }

.tt-jaar {
  font-family: var(--font-kop);
  color: var(--kleur-kop);
  border-left: 2px solid var(--rand);
}
.tt-maandkop { width: 40px; min-width: 40px; color: var(--tekst-gedempt); }
.tt-maandkop abbr { text-decoration: none; }
.tt-jaargrens { border-left: 2px solid var(--rand); }
/* De lopende maand: één verticale lijn door de hele matrix. De maandkop krijgt
   daarbovenop kleur en gewicht, maar de lijn is het antwoord op de eerste vraag
   die iemand bij dit overzicht stelt — waar staan we nu. Tot W-447 stond
   `tt-nu` alleen op de maandkop en beloofde dit commentaar iets dat de code
   niet deed; de kolomklasse staat nu per cel in de sjabloon.
   0,2,0 is met opzet: `.tt-balk` maakt zijn linkerrand doorzichtig en het
   printblok zet `border: 1px solid #999` op `.tt-cel` — allebei 0,1,0, dus
   allebei verliezen ze hier, op scherm én op papier. */
.tt-matrix .tt-nu { color: var(--kleur-primair); font-weight: 600; }
.tt-matrix .tt-nu-kolom { border-left: 2px solid var(--kleur-primair); }

.tt-categorie-kop {
  padding: var(--ruimte-8);
  background: var(--vlak);
  font-family: var(--font-kop);
  color: var(--kleur-kop);
  text-align: left;
}
/* De rubriekband loopt over de hele breedte, dus `sticky` op de cel zelf doet
   niets — de tekst erbinnen moet plakken, anders schuift de rubrieknaam uit
   beeld zodra de maandas breder is dan het venster. */
.tt-categorie-plak { position: sticky; left: var(--ruimte-8); display: inline-block; }
.tt-categorie-nieuw { margin-left: var(--ruimte-8); font-weight: 400; }

.tt-cel {
  width: 40px;
  min-width: 40px;
  height: 22px;
  padding: 0;
  border-left: 1px solid var(--rand);
}
/* De balk: aaneengesloten cellen met dezelfde vulling lezen als één streep,
   de uiteinden krijgen een ronding zodat begin en einde zichtbaar zijn. Loopt
   een taak door buiten de zichtbare as, dan blijft dat uiteinde recht. */
.tt-balk { border-left-color: transparent; }
.tt-balk-start { border-top-left-radius: 99px; border-bottom-left-radius: 99px; }
.tt-balk-eind { border-top-right-radius: 99px; border-bottom-right-radius: 99px; }
.tt-open  { background: var(--kleur-selectie-rand); }
.tt-loopt { background: var(--sein-amber-vlak); box-shadow: inset 0 0 0 1px var(--sein-amber-tekst); }
.tt-klaar { background: var(--sein-groen-vlak); box-shadow: inset 0 0 0 1px var(--sein-groen-tekst); }

/* De kopregel van de matrix: de weergavebalk links, de legenda rechts. De
   legenda staat met opzet BUITEN de weergavebalk (W-446) — het gedeelde
   printblok verbergt `.weergavebalk` en nam de sleutel bij de kleuren mee, wat
   D-288 juist verbiedt. Deze rij eromheen houdt ze op scherm op één regel.
   `margin-left: auto` staat hier en niet op een losse `.rechts`: die klasse
   heeft alleen betekenis binnen `.toolbalk` en `.rol-kop`, dus in een
   weergavebalk deed hij niets. */
.tt-kopbalk {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--ruimte-8);
  margin-bottom: var(--ruimte-12);
}
.tt-kopbalk .weergavebalk { margin-bottom: 0; }
.tt-kopbalk .tt-legenda { margin-left: auto; }

.tt-legenda { color: var(--tekst-gedempt); font-size: var(--maat-12); }
.tt-legenda-vlak {
  display: inline-block; width: 20px; height: 12px; border-radius: 99px;
  vertical-align: middle;
}

/* Een chip die naar een andere scope linkt (de jaarchips boven de matrix). */
.chip-link { text-decoration: none; }
.chip-link:hover { background: var(--kleur-selectie-rand); }

/* Lijst per verantwoordelijke: de groepskop is de enige toevoeging, de tabel
   eronder is de gewone .tabel. */
.tt-groep + .tt-groep { margin-top: var(--ruimte-24); }
.tt-groep-kop { display: flex; align-items: center; gap: var(--ruimte-8); margin-bottom: var(--ruimte-8); }

/* W-081 — compacte redenenlijst in een tabelcel: waarom staat dit paar hier?
   Generiek gehouden (geen dubbelen-prefix): elke lijst die per rij een handvol
   korte verantwoordingsregels toont, kan hem gebruiken. */
.redenen {
  list-style: none;
  margin: 0;
  padding: 0;
}
.redenen li + li {
  margin-top: 0.15rem;
}

/* ---- Merklogo en inlogscherm ------------------------------------------- */

/* Het liggende logo vervangt de merknaam in de kopbalk. Hoogte vast, breedte
   naar rato: het bestand is ~4:1, dus een vaste breedte zou het uitrekken op
   het moment dat iemand het logo vervangt. */
.merk-logo {
  display: block;
  height: 26px;
  width: auto;
}

/* W-257: 26px is op een telefoon te klein om het merk te herkennen — daar is de
 * kopbalk bijna het enige dat in beeld staat, terwijl op een monitor de hele
 * app eromheen de context al geeft. De kopbalk groeit mee (min-height, geen
 * height), dus dit duwt niets uit vorm. */
@media (max-width: 719px) {
  .merk-logo { height: 38px; }
}

/* Het inlogscherm is de enige pagina zonder kopbalk (layout.ZonderKop), dus
   het draagt zijn eigen identiteit: logo groot boven het formulier, alles
   gecentreerd, en een kolom die niet meegroeit met het venster — een
   inlogformulier over de volle breedte van een monitor leest slecht.
   min-height zet het blok in het verticale midden zonder het bij een klein
   scherm van de bovenkant af te duwen. */
.inlogscherm {
  max-width: 22rem;
  margin-inline: auto;
  min-height: calc(100vh - 8rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--ruimte-16);
}

.inlog-logo {
  display: block;
  width: min(17rem, 80%);
  height: auto;
  margin-inline: auto;
  margin-bottom: var(--ruimte-8);
}

/* De koptekst en het formulier binnen het inlogscherm volgen de kolom in
   plaats van hun eigen breedte te kiezen. */
.inlogscherm .pagina-kop { margin-bottom: 0; }
.inlogscherm .formulier { max-width: none; }
.inlogscherm .formulier-acties { margin-top: var(--ruimte-8); }

/* ---- Instelblokken ------------------------------------------------------ */

/* Elk onderwerp op een instellingenscherm is een eigen kaart. Zonder die grens
   liep alles als één kolom door: negen formulieren, tien knoppen en geen
   zichtbaar verband tussen velden en de knop die ze opslaat. De kaart doet het
   werk dat een scheidingslijn ook zou doen, maar dan met een binnen- en een
   buitenkant. */
.instelblok + .instelblok { margin-top: var(--ruimte-12); }
.instelblok > :last-child { margin-bottom: 0; }

/* De kop van een blok hoort bij het blok, niet bij de tekst eronder. */
.instelblok > h3:first-child { margin-top: 0; }

/* Meer lucht tussen de hoofdonderwerpen dan tussen de kaarten binnen zo'n
   onderwerp -- anders zijn de twee niveaus even ver uit elkaar en betekent de
   afstand niets. */
section + section { margin-top: var(--ruimte-24); }
section > h2 { margin-bottom: var(--ruimte-12); }


/* ---- Personenkiezer (W-115) ---------------------------------------------
 * Vervangt een <select> met 829 opties. Hij leeft binnen een gewone `.veld`,
 * dus label, invoer en hulptekst erven de formulieropmaak uit de stijlgids en
 * er komt geen tweede formuliertaal bij. Alleen de keuze-regel en de
 * trefferlijst zijn eigen. */
.persoonkiezer .kiezer-keuze {
  display: flex;
  align-items: center;
  gap: var(--ruimte-8);
  flex-wrap: wrap;
  margin: 0 0 var(--ruimte-8);
}
.persoonkiezer .kiezer-naam { font-weight: 600; }

/* KIEZERLIJST — één vorm voor elke lijst waaruit je iemand aanwijst (W-284).
 *
 * Er waren er drie: de trefferlijst van de gedeelde kiezer (een omsloten vak),
 * de roosterkandidaten (alleen een haarlijn boven elke rij) en de spelerkiezer
 * bij casting (een rij inline knoppen die omwikkelt). Drie vormen voor één
 * handeling — Herman las dat als "het form is lelijk, niet according de style
 * guide: ruimtes, box edges".
 *
 * Bewust een lijst in de stroom en geen zwevend paneel: op een telefoon duwt hij
 * de rest omlaag in plaats van eroverheen te vallen, en dat is het verschil
 * tussen kunnen kiezen en niet. */
.kiezerlijst {
  list-style: none;
  margin: var(--ruimte-8) 0 0;
  padding: 0;
  border: 1px solid var(--rand);
  border-radius: var(--radius-knop);
  overflow: hidden;
  background: var(--wit);
}
.kiezerlijst > li + li { border-top: 1px solid var(--rand); }
.kiezerlijst > li { padding: var(--ruimte-8) var(--ruimte-12); }

/* Een regel die in zijn geheel aanklikbaar is: het aanraakdoel is de hele
 * regel en niet alleen de tekst (D-092). De negatieve marge trekt de link over
 * de celvulling heen, zodat hover en focus de volle breedte pakken. */
.kiezerlijst > li > .kiezer-regel {
  display: block;
  width: 100%;
  text-align: left;
  margin: calc(var(--ruimte-8) * -1) calc(var(--ruimte-12) * -1);
  padding: var(--ruimte-8) var(--ruimte-12);
  background: none;
  border: none;
  border-radius: 0;
  font: inherit;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}
.kiezerlijst > li > .kiezer-regel:hover,
.kiezerlijst > li > .kiezer-regel:focus-visible { background: var(--vlak); }

/* ---- Paneel op smalle schermen (W-281) -----------------------------------
 * D-112 zegt "side panels overlay instead of squeeze", en .paneel doet dat al:
 * vaste lade rechts, volledig scherm onder 640px. Het roosterpaneel was de ene
 * die dat nooit overnam — het stond als gewone kaart in een rasterkolom, dus
 * onder 1024px landde het ONDER het rooster. Op de festivaldag staan er 45
 * diensten op één dag, dus dat is 45 tegels lager en buiten beeld.
 *
 * Deze klasse is de gedeelde vorm voor "ernaast op breed, eroverheen op smal".
 * Hij hangt aan :has() zodat een LEEG paneel niets afdekt: pas als de server er
 * inhoud in heeft geswapt wordt het een overlay. Kent een browser :has() niet,
 * dan blijft alles zoals het was — geen regressie, alleen geen verbetering. */
@media (max-width: 1023px) {
  .paneel-op-smal:has(.rooster-panel-inhoud) {
    position: fixed;
    inset: 0;
    z-index: 20;
    margin: 0;
    border: none;
    border-radius: 0;
    overflow-y: auto;
    padding: var(--ruimte-16);
  }
}

/* De sluitknop van een paneel. Op breed leest hij als "leegmaken", op smal als
 * "terug naar het rooster" — zelfde knop, en dat is met opzet: één plek om weg
 * te komen, waar je ook bent. */
.paneel-sluit {
  float: right;
  width: var(--aanraakdoel);
  height: var(--aanraakdoel);
  display: flex;
  align-items: center;
  justify-content: center;
  margin: calc(var(--ruimte-8) * -1) calc(var(--ruimte-8) * -1) 0 0;
  font-size: var(--maat-20);
  line-height: 1;
  color: var(--kleur-kop);
  text-decoration: none;
  border-radius: var(--radius-knop);
}
.paneel-sluit:hover,
.paneel-sluit:focus-visible { background: var(--vlak); }

/* Meervoudige kiezer (W-282): de gekozen rij boven het zoekveld. Geen chips met
 * eigen kleur — het zijn regels in dezelfde lijstvorm als de treffers eronder,
 * zodat "wat je koos" en "wat je kunt kiezen" één taal spreken. */
.kiezer-gekozen {
  list-style: none;
  margin: 0 0 var(--ruimte-8);
  padding: 0;
}
.kiezer-gekozen li {
  display: flex;
  align-items: center;
  gap: var(--ruimte-8);
  padding: var(--ruimte-4) 0;
}
.kiezer-af {
  margin-left: auto;
  width: var(--aanraakdoel);
  height: var(--aanraakdoel);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--kleur-kop);
  text-decoration: none;
  border-radius: var(--radius-knop);
}
.kiezer-af:hover,
.kiezer-af:focus-visible { background: var(--vlak); }

/* W-338 ronde 3 — de standenlijst van een meervoudig bulkveld.
   Eén regel per waarde: stand-vinkje, naam, telling, en de handeling rechts.
   Zelfde vorm als .kiezer-gekozen li, want het is dezelfde soort lijst: korte
   regels met een actie aan het eind. Het vinkje is `disabled` en toont alleen
   de stand — de knop is de bediening — dus de gedempte kleur die een browser
   eraan geeft is hier juist gewenst en wordt niet weggepoetst. */
.bulk-standen-kop { margin-bottom: var(--ruimte-4); }
.bulk-standen { list-style: none; margin: 0; padding: 0; }
.bulk-stand {
  display: flex;
  align-items: center;
  gap: var(--ruimte-8);
  padding: var(--ruimte-4) 0;
}
.bulk-stand-naam { font-weight: 600; }
.bulk-stand .knop { margin-left: auto; }
/* W-341 — de herkomst achter de telling. Geen eigen kleur en geen badge: het is
   een bijzin bij een getal, en een gekleurd label zou alarmeren waar het besluit
   juist zegt dat dit scherm dat niet moet doen (D-305). */
.bulk-stand-herkomst { white-space: nowrap; }

/* ── W-376 Mensen als kaarten op een smal scherm ────────────────────────────
 *
 * DE OMKERING. De tabel loste smal op door WEG te laten: .kol-extra verdwijnt
 * onder 1024px, .kol-nuttig onder 640px (zie "Column priority" hierboven). Op
 * een telefoon zag je daardoor het minst, terwijl daar juist alles moet passen.
 * Een kaart heeft geen kolombreedte-probleem, dus hij kan alles dragen — dit
 * blok maakt van elke rij een kaart en zet die twee uitsluitingen weer aan.
 *
 * EEN DOM, GEEN TWEEDE MARKUP. De kaart IS de tabelrij; alleen de opmaak
 * verschilt. Dat is geen zuinigheid maar noodzaak: de server kent de
 * schermbreedte niet, dus "kaarten renderen op mobiel" zou betekenen dat beide
 * vormen tegelijk in de DOM staan — met twee selectievinkjes per persoon en een
 * teller die dubbeltelt (W-027). Nu erft de kaart alles wat de rij al kan:
 * selectie, sorteer-URL's, htmx-swaps, de kolominstelling uit Beheer.
 *
 * HET LABEL KOMT UIT HET REGISTER. Zonder koppenrij heeft een waarde geen
 * opschrift meer, dus elke cel draagt `data-label` mee (mensenTabel.Kaartcel,
 * mensen.go) en ::before zet dat ervoor. Eén bron voor <th> en kaartregel.
 *
 * CONTAINER EN GEEN MEDIA QUERY, zelfde keuze als .persoon-kaart: de lijst
 * reageert op de ruimte die hij krijgt, wat hem meteen bruikbaar maakt in een
 * zijpaneel (D-112). De grens ligt op dezelfde 1023px als de kolomprioriteit
 * hierboven, en de container is nooit breder dan het venster — dus op elk
 * scherm waar de tabel een kolom zou laten vallen, staat de kaart er al.
 *
 * TERUGDRAAIEN: één schakelaar, mensenKaartenOpMobiel in
 * internal/handlers/mensen_weergave.go. Die zet de klasse .lijst-kaarten op de
 * omhullende div; zonder die klasse is elke regel hieronder onbereikbaar en is
 * de lijst weer precies de tabel van vóór W-376.
 *
 * VIJF LIJSTEN SINDS W-402. Dit begon als proef op Mensen; Mensen, Producties,
 * Kleding, Rekwisieten en Locaties gebruiken hem nu. Elke selector begint nog
 * steeds met .lijst-kaarten, dus een lijst die de schakelaar niet aanzet merkt
 * er niets van — Planning, Geld en Beheer blijven bewust tabel (Herman,
 * 2026-08-26: "planning lijkt me nu al ok, niet aan doen"). Alle vijf de
 * registers dragen een `naam`-kolom (Vast) en de `notitie`-tekenkolom, dus de
 * twee vormregels hieronder die op `data-kolom` mikken gelden voor alle vijf.
 *
 * Ze staan alleen niet overal vooraan: Producties heeft `nummer` vóór `naam`
 * in het register (standaard uit, W-249). Zet een beheerder die kolom aan, dan
 * zou de kaarttitel ineens de tweede regel zijn. `order` lost dat op zonder de
 * DOM-volgorde aan te raken — en die volgorde moet blijven zoals hij is, want
 * TestElkeRegisterkolomHeeftPreciesEenCel meet hem per positie na. */

/* W-437 — EN HET IS SCHERMWERK: DE KAARTVORM GAAT NIET MEE NAAR DE PRINTER.
 *
 * Herman, 2026-08-31: "CMD+P uses now the single column mobile view as basis
 * for the print output, instead of the — most probably better for printing —
 * desktop view." Dat gebeurde ook, en de container query hieronder deed niets
 * verkeerd: hij meet het CONTAINERVLAK, en dat vlak is bij het printen een A4
 * (~794px staand, ~1123px liggend). De grens van 1023px wordt op staand papier
 * dus gepasseerd en de kaartvorm slaat aan — op het enige medium waar hij niets
 * oplost, want daar is niets smal en scrollt niets.
 *
 * NAGEMETEN IN DE BROWSER en niet afgeleid: op /mensen is `div.lijst-kaarten`
 * het enige containerelement, op het scherm 1168px breed (tabelvorm); zet je
 * hem met de hand op 794px, dan klapt de lijst zichtbaar om naar de kaartvorm.
 * Dat is exact het symptoom dat Herman op papier zag.
 *
 * `@media screen` eromheen is de hele reparatie, en het is de goedkoopste die
 * ook blijft kloppen. Op papier bestaat de kaartvorm dan niet, dus printen deze
 * lijsten weer als de gewone tabel mét koppenrij — precies wat elke andere
 * lijst in deze app op papier al deed.
 *
 * WAAROM NIET DE ANDERE ROUTE (binnen `@media print` de tabelvorm terugzetten).
 * Die zou niet vijf display-regels moeten herstellen maar drieëntwintig, elk
 * met genoeg specificiteit om zijn tegenhanger hierin te verslaan — en dáárna
 * pas alles wat de kaart verder meebrengt: de kaartranden, de marges tussen de
 * rijen, de tint van een geselecteerde rij, de opschriften die ::before uit
 * `data-label` haalt (die zou anders elke kolomnaam náást de teruggekeerde
 * koppenrij nog een keer afdrukken), en de overslaanregel van W-392 die een
 * browser laat weglaten wat buiten beeld staat — op papier bestaat "buiten
 * beeld" niet zoals op een scherm, en wat daar misgaat zie je pas op de
 * afdruk. Van de 31 regels hieronder dragen er maar 23 een `display`; de acht
 * andere zijn precies wat zo'n herstel stil laat staan. En elke kaartregel die
 * er later bij komt zou dan opnieuw een tegenregel nodig hebben; het vergeten
 * daarvan is precies de bug die W-437 meldde. Het hek heeft nul tegenregels
 * nodig en veroudert niet.
 *
 * DE `container-type` STAAT ER BEWUST BINNEN. Zonder query eromheen doet hij op
 * papier niets nuttigs meer, terwijl containment op een omhullende div wél kan
 * meespelen in hoe de browser de pagina breekt. Buiten beeld houden is hier
 * goedkoper dan uitzoeken hoe elke browser dat doet.
 *
 * DE INHOUD HIERONDER IS MET OPZET NIET INGESPRONGEN. Twee spaties erbij zou
 * ruim driehonderd regels aanraken voor een reparatie van twee, en meerdere
 * ratels lezen regels uit dit blok letterlijk terug. Het hek sluit onderaan bij
 * "einde @media screen (W-437)".
 *
 * Bewaakt door TestElkeKaartlijstPrintAlsTabel, en die pint niet deze vijf
 * lijsten maar de klasse: elke regel die met .lijst-kaarten een tabel uiteen
 * haalt moet binnen dit hek staan, of in @media print per elementsoort worden
 * teruggezet. */
@media screen {

.lijst-kaarten { container-type: inline-size; }

/* W-384 heeft de sorteerbalk van W-376 hier weggehaald. Hij gaf in kaartvorm de
   sorteerlinks terug die met de koppenrij wegvielen, maar W-375b had sorteren
   toen al in het filterpaneel gezet — en die twee spraken elkaar tegen: de balk
   bood drie sorteringen, het paneel de volledige lijst. Sorteren staat nu op
   één plek. Bewaakt door TestSorterenWordtOpEenPlekAangeboden. */

@container (max-width: 1023px) {
  /* De houder scrollt niet meer horizontaal: er valt niets meer buiten te
     vallen zodra de rij een blok is. */
  .lijst-kaarten .tabel-houder { overflow: visible; }

  /* Tabelvorm uit. De koppenrij gaat weg omdat elke waarde zijn opschrift nu
     zelf draagt; sorteren gebeurt sinds W-384 in het filterpaneel, dat op
     dezelfde breedtes een volledig scherm is. */
  .lijst-kaarten .tabel,
  .lijst-kaarten .tabel tbody,
  .lijst-kaarten .tabel tr,
  .lijst-kaarten .tabel td { display: block; }
  .lijst-kaarten .tabel thead { display: none; }

  /* Iets ruimer dan de tabeldichtheid: dit is de enige weergave op dit scherm,
     niet een overzicht dat je met de ogen afscant. */
  .lijst-kaarten .tabel { font-size: var(--maat-14); }

  /* DE KAART. Zelfde chrome als .kaart (wit vlak, --rand, --radius-kaart), want
     het is dezelfde soort doos — geen tweede kaartvorm in de stijlgids. */
  /* W-392 — DE BROWSER SLAAT OVER WAT BUITEN BEELD STAAT.
   *
   * ListPersonen heeft geen limit: de lijst rendert elke rij die aan het filter
   * voldoet. Op de database kost dat niets (gemeten op productie: 16 ms voor
   * alle 815), maar in kaartvorm is een rij geen 40px meer maar ~190px. De
   * stand vooraf — bereik = deze editie, 130 mensen — is dan al ~24.700px, en
   * "hele bestand" is ~155.000px met 815 kaarten in de DOM.
   *
   * Hier heeft D-320 een prijs, en dit is waar hij betaald wordt. Eén DOM
   * betekent dat de server niet weet of hij een tabel of kaarten voedt, dus "op
   * mobiel minder sturen" bestaat niet. De browser kan wel minder DOEN.
   *
   * `content-visibility: auto` laat opmaak en tekening weg voor alles buiten
   * beeld. De inhoud blijft in de DOM: zoeken op de pagina vindt hem, en
   * htmx-swaps en de selectieteller merken er niets van. Dat is precies de
   * eigenschap die deze regel geschikt maakt voor een lijst waarin je zoekt.
   *
   * `contain-intrinsic-size: auto 190px` is de schatting die de scrollbalk
   * overeind houdt: zonder maat zou een overgeslagen kaart nul hoog zijn en
   * klapt de balk in elkaar. Het `auto`-voorvoegsel is het belangrijke deel —
   * de browser onthoudt de ECHT gemeten hoogte zodra hij een kaart een keer
   * heeft opgemaakt, dus de 190px is alleen de eerste gok en veroudert niet
   * wanneer de kaart door W-389 twee regels langer wordt.
   *
   * BINNEN DE CONTAINER QUERY EN NIET ERBUITEN. In tabelvorm is een rij
   * `display: table-row`, en containment op een tabelonderdeel laat de
   * kolombreedtes meebewegen tijdens het scrollen: de browser kan de breedte
   * van iets wat hij niet opmaakt niet meewegen. Het voorstel noemt "ook op
   * desktop, ook voor de tabel" als extra winst; dat kan waar zijn, maar het is
   * een ander experiment met een ander risico.
   *
   * DIT IS LAAG 1 VAN TWEE, EN LAAG 2 WACHT OP EEN METING. Als deze regel de
   * scroll op een echt toestel vloeiend maakt, is een afkapping alleen nog een
   * argument over payload en mag de grens veel hoger liggen dan wanneer hij ook
   * de opmaak moet redden. Meten dus vóór laag 2: /mensen met bereik "hele
   * bestand", scrollen op een telefoon. Paginering komt er niet (zie W-392 in
   * BACKLOG.md voor de drie redenen).
   *
   * TERUGDRAAIEN: de twee declaraties hieronder weghalen. */
  /* W-402 — DE KAART IS EEN FLEXRIJ EN NIET MEER EEN BLOK.
   *
   * Herman, 2026-08-26, over Kleding en Rekwisieten: "een rij onder elkaar
   * wordt een lange kaart. Sommige infovelden kunnen makkelijk naast elkaar om
   * verticale ruimte te besparen." Kleding draagt negen kolommen en
   * Rekwisieten dertien; onder elkaar is dat een kaart van een half scherm per
   * stuk.
   *
   * `flex-wrap` met `flex: 1 1 100%` als STANDAARD verandert niets aan wat er
   * al stond: een cel die de volle breedte pakt stapelt precies zoals een blok
   * dat deed. Alleen een cel die met `kaart-half` gemerkt is neemt de halve
   * breedte, en dan schuiven er twee naast elkaar. Mensen merkt hier dus niets
   * van — daar is geen enkele cel gemerkt.
   *
   * Alleen `column-gap`: een `row-gap` zou tussen ALLE cellen ruimte zetten en
   * de kaart juist langer maken. De verticale ruimte komt uit de padding van de
   * cel, net als eerst.
   *
   * De twee cellen die uit de stroom staan (het vinkje en het notitieteken)
   * zijn absoluut gepositioneerd en zijn dus geen flexitem — die merken er
   * niets van. */
  .lijst-kaarten .tabel tr {
    content-visibility: auto;
    contain-intrinsic-size: auto 190px;
    display: flex;
    flex-wrap: wrap;
    column-gap: var(--ruimte-12);
    position: relative;
    background: var(--wit);
    border: 1px solid var(--rand);
    border-radius: var(--radius-kaart);
    padding: var(--ruimte-12);
    margin-bottom: var(--ruimte-12);
  }
  .lijst-kaarten .tabel tr.rij-geselecteerd {
    background: var(--kleur-selectie);
    border-color: var(--kleur-selectie-rand);
  }
  /* De tint hoort bij de hele kaart, niet bij elke losse regel erin. */
  .lijst-kaarten .tabel tr.rij-geselecteerd > td { background: transparent; }

  /* EEN VELDREGEL: opschrift links, waarde rechts. minmax(0, 1fr) plus
     overflow-wrap, want een lang e-mailadres loopt anders de kaart uit — die
     twee horen altijd samen. */
  /* W-396 — de ruimte tussen opschrift en waarde is gehalveerd (Herman,
     2026-08-26: "die kan misschien wel met de helft verminderd worden"). De
     labelkolom stond op 7.5rem terwijl het langste opschrift ("Geboortedatum")
     ~5.3rem meet, dus er stond bij elk veld een handbreedte leegte. 5.5rem laat
     elk opschrift nog op één regel staan en haalt die leegte weg; de gap gaat
     van 8 naar 4. */
  .lijst-kaarten .tabel td {
    border-bottom: 0;
    padding: var(--ruimte-4) 0;
    /* W-402: de volle breedte is de standaard, dus ongemerkte cellen stapelen
       zoals ze altijd deden. */
    flex: 1 1 100%;
    display: grid;
    grid-template-columns: minmax(0, 5.5rem) minmax(0, 1fr);
    gap: var(--ruimte-4);
    align-items: baseline;
    overflow-wrap: anywhere;
    text-align: left;
    width: auto;
    white-space: normal;
  }
  .lijst-kaarten .tabel td::before {
    content: attr(data-label);
    font-family: var(--font-kop);
    font-weight: 500;
    font-size: var(--maat-12);
    color: var(--tekst-gedempt);
  }

  /* HIER WORDT DE BELOFTE INGELOST: de twee prioriteitsklassen die de tabel op
     dit formaat verbergt, staan op de kaart gewoon in beeld. De regel hierboven
     wint al op specificiteit, maar dat is een toevallige eigenschap van een
     selector — deze twee regels zeggen het met zoveel woorden, en de
     regressietest van W-376 leest ze. */
  .lijst-kaarten .tabel td.kol-nuttig { display: grid; }
  .lijst-kaarten .tabel td.kol-extra  { display: grid; }

  /* W-402 — TWEE KORTE VELDEN OP ÉÉN REGEL.
   *
   * `kaart-half` is een opmaakkeuze per kolom en staat daarom in de markup van
   * die lijst, naast de prioriteitsklasse, en niet in het kolomregister: het
   * register beantwoordt "welke kolommen en in welke volgorde", niet "hoe breed
   * leest deze waarde". De regel om te kiezen is kort: een korte, op zichzelf
   * staande waarde
   * (maat, aantal, kleur, een ja/nee, een jaartal) mag half; vrije tekst, een
   * naam, een link of een opsomming niet — die breken dan af en kosten juist
   * meer regels dan ze besparen. Die regel staat voluit in CONVENTIONS.md §15,
   * samen met de drie stappen om een lijst op kaartvorm te zetten.
   *
   * Het opschrift gaat BOVEN de waarde in plaats van ernaast: op de halve
   * breedte blijft er naast een labelkolom van 5.5rem te weinig over om een
   * waarde nog op één regel te houden, en dan is de winst weg.
   *
   * W-408 — DE `1` IN `flex: 1 1 …` IS OOK DE GARANTIE DAT EEN UITGEZETTE
   * KOLOM GEEN GAT LAAT. Zet een beheerder in Beheer (W-207) `aard` of
   * `programma_nummer` uit, dan resteert op die regel bijvoorbeeld 0,75 in
   * plaats van een heel getal — precies de stand die
   * TestElkeKaartregelIsEenHeleRegel niet ziet, want die leest de sjabloon-
   * TEKST en dus altijd de stand met alle kolommen aan. Het item ging uit van
   * een zichtbaar gat en stelde als derde weg voor: de flexrij zelf laten
   * uitvullen, zodat de laatste cel op een regel de restbreedte pakt — zonder
   * de opmaak een tweede toestand per kolomcombinatie te geven (D-320).
   *
   * GEMETEN IN PLAATS VAN AANGENOMEN: die derde weg bleek bij het bouwen al
   * precies dit te zijn. De flex-grow-factor `1` hierboven — dezelfde op
   * `kaart-derde` en `kaart-kwart` verderop — laat elke resterende cel op een
   * onvolledige regel automatisch de vrije ruimte overnemen, gelijk verdeeld
   * over alle cellen met grow 1 op die regel. Geverifieerd in een echte
   * Chrome (152), sub-pixel nauwkeurig, op drie lijsten (Locaties, Kleding,
   * Rekwisieten) over acht losse kolomstanden: in elk geval sluit de laatste
   * cel exact op de rand van de kaart aan, geen 0,1px gat. Er was dus geen
   * tweede opmaaktoestand nodig — de regel bestond al sinds W-402/W-404/
   * W-407, ruim vóór dit item werd gefileerd. TestKaartRegelBlijftHeelBij-
   * EenUitgezetteKolom (lijstkaart_test.go) rendert die acht standen nu ook
   * echt (niet de sjabloontekst) en meet — leest, niet neemt aan — dat deze
   * flex-grow-regel er voor elke resterende breedteklasse nog staat.
   *
   * TERUGDRAAIEN VAN DEZE GARANTIE: verlaag `flex: 1 …` naar `flex: 0 …` op
   * een van de drie breedteklassen. TestKaartRegelBlijftHeelBijEenUitgezette-
   * Kolom hoort daar dan rood op te gaan. */
  .lijst-kaarten .tabel td.kaart-half {
    flex: 1 1 calc(50% - var(--ruimte-12));
    display: block;
  }
  .lijst-kaarten .tabel td.kaart-half.kol-nuttig,
  .lijst-kaarten .tabel td.kaart-half.kol-extra { display: block; }
  .lijst-kaarten .tabel td.kaart-half::before { display: block; }

  /* W-404 — EEN DERDE, voor drie korte velden op één regel. Herman keek op de
     kleinste telefoon en vroeg er drie naast elkaar bij Kleding (doelgroep,
     materiaal, doos) en bij Locaties (aard, programmanr., gebruik).
     3 × (33.333% − gap) + 2 × gap = 100% − gap, dus het past altijd. */
  .lijst-kaarten .tabel td.kaart-derde {
    flex: 1 1 calc(33.333% - var(--ruimte-12));
    display: block;
  }
  .lijst-kaarten .tabel td.kaart-derde.kol-nuttig,
  .lijst-kaarten .tabel td.kaart-derde.kol-extra { display: block; }
  .lijst-kaarten .tabel td.kaart-derde::before { display: block; }

  /* W-407 — EEN KWART, en daarmee ook ONGELIJKE breedtes op één regel.
     Herman, 2026-08-28, over de Locatiekaart: "het lijkt me dat gebruik veld en
     content wel in de breedte zou moeten passen met Nr. en Aard." Dat lukte
     niet, en de reden was dat alle drie de cellen exact een derde kregen
     terwijl "Aard Buiten" en "Nr. 2" veel smaller zijn dan
     "Gebruik Speellocatie" — die laatste brak daardoor af tot "Speello / catie".

     De oplossing is niet meer ruimte maar een eerlijkere verdeling: een cel
     krijgt de breedte die zijn inhoud vraagt. Aard en Nr. staan op een kwart,
     Gebruik op een half, samen precies één regel. Dat is ook waarom de klassen
     BREEDTES heten en geen posities: ze mogen door elkaar op één regel staan
     zolang ze samen op een heel getal uitkomen, en
     TestElkeKaartregelIsEenHeleRegel telt dat na — voor élke kaartlijst
     tegelijk, dus een nieuwe lijst valt er vanzelf onder.

     4 × (25% − gap) + 3 × gap = 100% − gap, dus vier op een regel past ook. */
  .lijst-kaarten .tabel td.kaart-kwart {
    flex: 1 1 calc(25% - var(--ruimte-12));
    display: block;
  }
  .lijst-kaarten .tabel td.kaart-kwart.kol-nuttig,
  .lijst-kaarten .tabel td.kaart-kwart.kol-extra { display: block; }
  .lijst-kaarten .tabel td.kaart-kwart::before { display: block; }

  /* W-479 — DRIEKWART, voor één lange waarde naast één korte. Herman, 2026-09-05,
     op de telefoon: de productienaam op een kwart brak af tot "Th/ea/ter/ko/or"
     — een letter of drie per regel, twaalf regels hoog. Een productienaam is
     de langste waarde op de locatiekaart en hoort bij het programmanummer
     ("het productienr moet bij de productienaam blijven"): Nr. op een kwart,
     Productie op driekwart, samen precies één regel. Zelfde rekenregel als de
     andere breedtes: 1 × (75% − gap) + 1 × (25% − gap) + gap = 100% − gap. */
  .lijst-kaarten .tabel td.kaart-driekwart {
    flex: 1 1 calc(75% - var(--ruimte-12));
    display: block;
  }
  .lijst-kaarten .tabel td.kaart-driekwart.kol-nuttig,
  .lijst-kaarten .tabel td.kaart-driekwart.kol-extra { display: block; }
  .lijst-kaarten .tabel td.kaart-driekwart::before { display: block; }

  /* W-404 — HET OPSCHRIFT ERNAAST IN PLAATS VAN ERBOVEN. Een halve of derde cel
     stapelt standaard (opschrift boven de waarde), want op die breedte past er
     naast een labelkolom van 5.5rem meestal niets meer. Bij een KORTE waarde is
     dat juist verspilling: Herman, 2026-08-27, over Rekwisieten — "kunnen het
     aantal en het gebruik op 1 regel achter hun veldnaam" — en dat scheelt daar
     twee regels per kaart.

     `auto` en niet de vaste 5.5rem: het opschrift krijgt precies de breedte die
     het nodig heeft, zodat "Aantal 1" niet de halve cel aan leegte verspilt.
     Alleen gebruiken waar de waarde kort en voorspelbaar is — een lange waarde
     wikkelt dan onder het opschrift door en leest slechter dan gestapeld. */
  .lijst-kaarten .tabel td.kaart-inline {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--ruimte-4);
    align-items: baseline;
  }
  .lijst-kaarten .tabel td.kaart-inline.kol-nuttig,
  .lijst-kaarten .tabel td.kaart-inline.kol-extra,
  .lijst-kaarten .tabel td.kaart-inline.kaart-kwart { display: grid; }
  .lijst-kaarten .tabel td.kaart-inline::before { display: block; }

  /* Een cel zonder inhoud (het notitieteken bij iemand zonder notitie) zou een
     lege regel met een opschrift worden. */
  .lijst-kaarten .tabel td:empty { display: none; }

  /* De naam is de kaarttitel: geen opschrift ervoor, over de volle breedte, en
     ruimte rechts vrijhouden voor het selectievinkje. */
  .lijst-kaarten .tabel td[data-kolom="naam"] {
    /* W-402: de titel staat bovenaan de kaart, ook als het register een kolom
       vóór `naam` heeft staan die iemand in Beheer aanzet. Alleen de volgorde
       in beeld verschuift; de DOM-volgorde blijft die van het register. */
    order: -1;
    display: block;
    font-family: var(--font-kop);
    font-size: var(--maat-16);
    font-weight: 600;
    /* Rechtsboven staan sinds W-389 het vinkje én het notitieteken naast
       elkaar, dus de titel houdt daar ruimte vrij. W-404 bracht dat van twee
       aanraakdoelen terug naar 1,75: het teken lijnt nu rechts uit binnen zijn
       vak, dus het loopt niet meer tot de linkerrand daarvan. Dat is ~11px
       extra titelbreedte, en precies waarom Herman erom vroeg — afbreken mag,
       maar hoeft niet zo vaak. */
    padding: 0 calc(var(--aanraakdoel) * 1.75) var(--ruimte-8) 0;
  }
  .lijst-kaarten .tabel td[data-kolom="naam"]::before { content: none; }

  /* Het notitieteken draagt zijn eigen uitleg in title/aria-label; het
     registeropschrift is "!" en dat twee keer tonen zegt niets. */
  /* De tekenkolom is in tabelvorm 1% breed en gecentreerd (.tabel td.teken);
     op een kaart is dat een kolombreedte die niet meer bestaat. Uitdrukkelijk
     teruggezet en niet op volgorde vertrouwd: die twee selectoren wegen even
     zwaar, en dan beslist de plaats in het bestand — te zwakke grond voor een
     regel die je stil kwijtraakt zodra iemand hierboven iets invoegt. */
  /* W-389 — het teken staat NAAST het vinkje en niet als eigen regel eronder.
     Herman, 2026-08-26: als losse regel, kale cirkel zonder opschrift, las het
     als een waarschuwing bij de hele kaart in plaats van als een teken bij de
     naam. Absoluut gepositioneerd, één aanraakdoel links van td.kol-selectie,
     op dezelfde hoogte — zo staan de twee tekens als één groep rechtsboven.

     Met CSS en niet door de cel te verplaatsen: de DOM-volgorde is
     selectie → naam → notitie en die volgt het kolomregister, dat
     TestElkeRegisterkolomHeeftPreciesEenCel per positie nameet. */
  .lijst-kaarten .tabel td[data-kolom="notitie"] {
    position: absolute;
    top: var(--ruimte-8);
    right: calc(var(--ruimte-8) + var(--aanraakdoel));
    display: flex;
    align-items: center;
    /* W-404 — het teken schuift tegen het vinkje aan. Herman, 2026-08-27: "op
       alle kaarten mag de ruimte tussen uitroepteken en selectiebox wat kleiner,
       dat levert meer ruimte voor titels op". Beide vakjes zijn een aanraakdoel
       breed (D-092) met een icoon van ~24px in het midden, en die twee middens
       staan daardoor ~20px uit elkaar. Rechts uitlijnen in plaats van centreren
       halveert dat zonder één aanraakdoel te verkleinen — het teken is geen
       knop, dus zijn vák hoeft geen vingermaat te houden; het vinkje ernaast wel
       en dat blijft ongemoeid. */
    justify-content: flex-end;
    width: var(--aanraakdoel);
    height: var(--aanraakdoel);
    padding: 0;
    text-align: center;
    white-space: normal;
  }
  /* Zonder notitie is de cel leeg, en dan hoort er ook geen lege doos te staan.
     Uitdrukkelijk hier en niet via td:empty hierboven: die twee selectoren
     wegen even zwaar (0,3,1), dus de laatste in het bestand wint — en dat is
     deze. Op volgorde vertrouwen is te zwakke grond voor een regel die je stil
     kwijtraakt zodra iemand hierboven iets invoegt. */
  .lijst-kaarten .tabel td[data-kolom="notitie"]:empty { display: none; }
  .lijst-kaarten .tabel td[data-kolom="notitie"]::before { content: none; }

  /* Het selectievinkje rechtsboven op de kaart, met --aanraakdoel als
     aanraakvlak eromheen (D-092). */
  .lijst-kaarten .tabel td.kol-selectie {
    position: absolute;
    top: var(--ruimte-8);
    right: var(--ruimte-8);
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--aanraakdoel);
    height: var(--aanraakdoel);
    padding: 0;
  }
  .lijst-kaarten .tabel td.kol-selectie::before { content: none; }
}

} /* einde @media screen (W-437) — hierboven staat de hele kaartvorm, en die
     bestaat dus alleen op een scherm. Zie de toelichting bij het openen. */

/* W-375b — DE LIJSTKOP KLAPT DICHT OP EEN TELEFOON.
 *
 * Herman, 2026-08-25: "op mobiel is die header van elke pagina, dus elke lijst,
 * behoorlijk geklutterd". Dat klopte: .filterbalk was enkel flex-wrap, dus op
 * smal wrapten het zoekveld, de sorteerstand en tot vijf keuzelijsten onder
 * elkaar en vulden ze het scherm vóór de eerste rij. Onder de breekgrens blijft
 * hier alleen de Filter-knop staan; alles wat verdwijnt is sinds deel b op één
 * plek terug te vinden, in het filterpaneel — dat op precies dezelfde 639px een
 * volledig scherm wordt (zie .paneel hierboven).
 *
 * GEEN TWEEDE RENDERING, EN DAAROM GEEN DUBBEL VERZONDEN VELD. De server kent
 * de schermbreedte niet, dus de verleiding is om elk veld twee keer te
 * renderen: een brede en een smalle. Dat zou twee besturingselementen met
 * dezelfde `name` in hetzelfde formulier opleveren, allebei "successful
 * controls", en dan verzendt de browser `?zoek=a&zoek=a` — met de oude waarde
 * mogelijk vooraan. Precies de klasse fout die W-376 vermeed door de kaart de
 * tabelrij ZELF te laten zijn. Hier dus hetzelfde: één rendering, en `display:
 * none` haalt hem uit BEELD zonder hem uit het FORMULIER te halen. Een veld dat
 * op deze manier verborgen is verzendt gewoon door en reist ook gewoon mee in
 * hx-include="closest form" — de kop blijft dus de drager van de filterstand en
 * houdt alleen op een bediening te zijn.
 *
 * NUL MARKUP-WIJZIGING, en dat is met opzet. De selector zoekt de kop op wat
 * hem tot kop maakt: een filterbalk met een poort naar het filterpaneel. Dat is
 * woordelijk de definitie die de ratel van deel a hanteert
 * (TestElkeLijstpaginaGebruiktDeGedeeldeLijstkop). Daardoor staat er geen enkel
 * nieuw attribuut of klassewoord in templates/partials/lijstkop.html en zijn de
 * zestien koppen op desktopbreedte byte voor byte dezelfde als ervóór — de
 * sterkst mogelijke vorm van "op desktop verandert er zichtbaar niets". Het
 * houdt bovendien de tien werkbalken die `.filterbalk` lenen (archief,
 * beheer-gebruikers, de twee dubbelenschermen, …) buiten schot: die hebben geen
 * paneel om hun velden in terug te vinden en zouden onbruikbaar worden.
 *
 * :has() is hier al eerder zo gebruikt (.paneel-op-smal). Kent een browser het
 * niet, dan blijft de kop precies zoals hij vandaag is: geen regressie, alleen
 * geen verbetering.
 *
 * .heeft-filterpaneel ZET filterpaneel.js op <html>. Zonder JavaScript gaat het
 * paneel nooit open — de Filter-knop valt dan terug op zijn noscript-tweeling,
 * een gewone submit — en een kop die daar dichtklapt zou een kop zonder énige
 * bediening zijn. De haak is dus geen versiering maar de voorwaarde.
 *
 * WAT BLIJFT STAAN: de Filter-knop (geen .veld), en de weergavebalk met de
 * chips en de bereikpil — die staat buiten dit formulier, in het swapdoel van
 * de lijst, en blijft op mobiel zichtbaar (Herman, 2026-08-25: "dat scheelt in
 * aanpassen", D-225).
 *
 * TERUGDRAAIEN: dit blok weghalen. Er hangt geen Go-tak en geen markup aan. */
@media (max-width: 639px) {
  .heeft-filterpaneel .filterbalk:has([data-filterpaneel-open]) .veld { display: none; }

  /* De knop mag de volle breedte pakken zodra hij alleen staat: hij is dan het
     enige aanraakdoel in de kop, en een knopje van 70px links in een lege regel
     leest als een restant in plaats van als de ingang. */
  .heeft-filterpaneel .filterbalk:has([data-filterpaneel-open]) [data-filterpaneel-open] {
    flex: 1 1 100%;
    min-height: var(--aanraakdoel);
  }
}

/* W-384 — DE MOBIELE LIJST DRAAGT ALLEEN NOG WAT JE NODIG HEBT OM IEMAND TE
 * VINDEN.
 *
 * Herman, 2026-08-26, na het bekijken van wat W-375b en W-376 hadden
 * opgeleverd: "De header is volledig cluttered." Zijn opsomming van boven naar
 * beneden was: signaalveld weg, filterknop naast Nieuw persoon, bereik blijft,
 * doelgroeppillen via de popup, export weg, sorteren alleen in de popup.
 *
 * W-375b had de VELDEN van de kop al weggeklapt. Wat overbleef stond daar
 * buiten: de signaalregel staat vóór het formulier, de exportknoppen erna in
 * het swapdoel. Dit blok haalt die twee weg; de Filter-knop naast de hoofdactie
 * en de presetpillen naar het paneel zijn groter en staan als W-390.
 *
 * DE REGEL DIE HET BESLIST (D-321, voorstel §1): op een telefoon wint opzoeken
 * en contact leggen van compleetheid. Twee wensen trokken tot dit besluit
 * tegengesteld — "de kop is geklutterd" vraagt om MINDER, "de kaart moet alle
 * info dragen" om MEER — en de gebouwde versie droeg ze allebei. Met deze regel
 * is de knoop door: wat je niet nodig hebt om iemand te vinden en te bereiken
 * hoort hier niet in beeld.
 *
 * DE EXPORTKNOPPEN, VIA HUN EIGEN HAAK. Zevenenveertig exportknoppen in
 * tweeëntwintig bestanden, en tot W-384 hadden ze niets gemeenschappelijks
 * behalve hun zichtbare tekst. Ze dragen nu `data-export`, zodat één regel ze
 * alle dekt in plaats van een opsomming van lijsten die veroudert zodra er een
 * lijst bij komt. Het geldt bewust breder dan de lijsten: exporteren is
 * bureauwerk, en op een telefoon is een CSV-download geen antwoord op een
 * vraag. Bewaakt door TestElkeExportknopIsAlsZodanigHerkenbaar, beide kanten op.
 *
 * MIK NIET OP .toolbalk .rechts. Dat lijkt de kortste weg — daar staan de
 * exportknoppen in — maar op de actielijst staat in diezelfde rij ook "Alles
 * inklappen", en dat is op een telefoon juist het nuttigste dat er staat. Een
 * rij is geen classificatie; de haak wel.
 *
 * DE SIGNAALREGEL, ALLEEN OP EEN LIJSTPAGINA. `.signaalregels` staat op dertien
 * schermen, waarvan zes lijsten. Op de voordeur is het blok de kern van de
 * pagina en op /beheer/signalen is het het onderwerp; die moeten blijven. De
 * selector zoekt de lijstpagina daarom op wat haar tot lijstpagina maakt — een
 * filterbalk met een poort naar het filterpaneel — woordelijk dezelfde
 * definitie die W-375a en W-375b hanteren. Nul markup-wijziging, en niets om
 * bij te houden als er een lijst bij komt.
 *
 * WAT DE GEBRUIKER ER NIET DOOR VERLIEST: het signaal staat op de voordeur nog
 * steeds, en die is op een telefoon één tik weg. Dat is de reden dat dit mag —
 * niet dat het signaal onbelangrijk zou zijn.
 *
 * WAT BLIJFT STAAN: de bereikpil, de filterchips en de Filter-knop. Die staan
 * hieronder met opzet niet genoemd, en de ratel faalt als ze er alsnog in
 * belanden.
 *
 * TERUGDRAAIEN: dit blok weghalen. `data-export` mag dan blijven staan — het is
 * inert zonder deze regel. */
@media (max-width: 639px) {
  [data-export] { display: none; }

  #main:has(.filterbalk [data-filterpaneel-open]) .signaalregels { display: none; }
}

/* W-386 — DE PERSOONLIJKE PAGINA IS OP EEN TELEFOON ÉÉN TIK WEG.
 *
 * "Mijn" stond al in de hoofdnav, en dat was op een monitor genoeg. Onder 719px
 * klapt die nav achter de hamburger (W-257): daar is /persoonlijk ineens twee
 * tikken weg, en de eerste van die twee is een knop die je eerst moet vinden.
 * Precies op het apparaat waarvoor de persoonlijke pagina gemaakt is.
 *
 * WAAROM DIT MET CSS GAAT EN NIET MET EEN REDIRECT. De acceptatie liet ook
 * "land op /mijn bij een smal scherm" toe. Dat kan niet: de server kent de
 * schermbreedte niet, dus zo'n redirect vraagt apparaat- of breedtedetectie —
 * wat D-320 verbiedt, omdat detectie het renderpad splitst en de tweede tak
 * altijd veroudert. Eén DOM, en de breedte beslist alleen wat ervan te zien is.
 * Daarom is dit ook geen tweede navigatieboom: het is één bestemming die per
 * breedte op een andere plek staat.
 *
 * PRECIES ÉÉN INGANG IN BEELD. De twee anchors delen `data-mijn`. Boven de
 * breekgrens draagt de hoofdnav hem en is de kopbalk-ingang weg; eronder
 * andersom. Twee zichtbare ingangen naar dezelfde bestemming op één scherm zijn
 * verboden (UX-PRINCIPLES, page header contract; CONVENTIONS §11), en nul
 * ingangen is het probleem dat dit item kwam oplossen. Eén gedeelde haak in
 * plaats van twee selectors, want twee selectors kunnen uiteen lopen — dan komt
 * er een strook breedtes met nul of twee.
 *
 * DEZELFDE 719px ALS W-257, EN DAT IS DE HELE REDEN. De wissel moet exact
 * samenvallen met het moment waarop de nav inklapt. Een eigen breekgrens laat
 * per definitie een strook over waarin de nav al dicht is maar de kopbalk-ingang
 * nog niet staat (of allebei staan).
 *
 * WAT ER NIET IN STAAT. De menuknop wordt niet aangeraakt: die is van W-257 en
 * moet gewoon blijven werken. De hoofdnav-items als geheel worden niet
 * herstijld — alleen het ene item met de haak gaat uit — want acceptatie-eis 2
 * is een NIET-verandering: op een desktop verandert er niets, en het enige wat
 * daar aan de markup toegevoegd is, is een display:none-anchor.
 *
 * TERUGDRAAIEN: dit blok weghalen en de anchor met class="kop-mijn" uit
 * base.html. `data-mijn` op het nav-item mag blijven staan — dat is inert. */
.kop-mijn { display: none; }

@media (max-width: 719px) {
  .kop-balk .hoofdnav a[data-mijn] { display: none; }

  .kop-balk .kop-mijn { display: inline-flex; }
}

/* W-391 — EEN BELOFTE DIE HET APPARAAT IN JE HAND NIET KAN INLOSSEN, STAAT DAAR
 * NIET.
 *
 * `Zoeken ⌘K` in de kopbalk is twee dingen tegelijk: een knop, en een belofte
 * dat er een snellere weg is. De knop werkt op een telefoon prima — palet.js
 * vangt de klik, en zonder JavaScript is het een echte link naar de voordeur.
 * Het toetskapje ernaast werkt daar niet: er is geen ⌘-toets. Wat overblijft is
 * een instructie die de gebruiker niet kan opvolgen, op precies het apparaat
 * waar schermruimte het schaarst is.
 *
 * WAAROM `hover: none` EN NIET `pointer: coarse`. De drie bestaande
 * coarse-regels hierboven gaan over raakdoelgrootte (--aanraakdoel), en dat is
 * de juiste vraag voor een vingermaat. Hier is de vraag een andere: is er een
 * toetsenbord? Daar zegt `pointer: coarse` niets over. Een laptop met
 * aanraakscherm meldt zijn trackpad als primaire aanwijzer — `pointer: fine`,
 * `hover: hover` — en houdt het kapje dus, terecht, want er zit een ⌘-toets op.
 * Een apparaat waarvan de primaire aanwijzer niet kan zweven is een apparaat dat
 * je vasthoudt, en dat is het dichtste dat CSS bij "geen toetsenbord" komt.
 * Breedte is expliciet niet de maat (D-320): een smal venster op een monitor
 * heeft nog steeds een toetsenbord, dus er staat hier geen max-width.
 *
 * WAT DIT KOST EN AAN WIE. Een iPad met een aangekoppeld toetsenbord meldt
 * `hover: none` en verliest de hint terwijl ⌘K er wél werkt. Bewuste keuze: dat
 * is een handvol bestuursleden die de sneltoets allang kent, tegenover de
 * tweehonderdvijftig die op een telefoon een toets beloofd krijgen die er niet
 * is. De sneltoets zelf wordt nergens uitgeschakeld — alleen de belofte.
 *
 * DE HAAK, NIET EEN OPSOMMING VAN SCHERMEN. `data-toetsbelofte` markeert het
 * stuk tekst dat zonder toetsenbord niets betekent. Soms is dat het kapje zelf
 * (base.html), soms de zin eromheen (home.html: alleen het kapje weghalen laat
 * "Overal in de app: ." staan, en dat is erger dan niets doen). Een derde plek
 * die morgen een sneltoets belooft, draagt dezelfde haak en is meteen gedekt.
 * Bewaakt door TestElkeToetskapDraagtDeHaakDieHemZonderToetsenbordWeghaalt.
 *
 * WAT HIER BEWUST NIET STAAT. `.palet-toets` wordt niet klassikaal verborgen:
 * in de paletvoet zou dan " kiezen · openen · sluiten" overblijven. En de
 * paletvoet zelf gaat óók niet uit — `Esc sluiten` is vandaag de enige uitgang
 * uit die dialoog (palet.js sluit niet op een klik ernaast, <dialog> ook niet),
 * dus hem weghalen sluit een touchgebruiker op. Dat vraagt een zichtbare
 * sluitknop, en dat is W-394. De inventaris van alle vindplaatsen staat in
 * docs/audit/2026-08-26-touch-affordances.md.
 *
 * TERUGDRAAIEN: dit blok weghalen. `data-toetsbelofte` mag dan blijven staan —
 * het attribuut is inert zonder deze regel. */
@media (hover: none) {
  [data-toetsbelofte] { display: none; }
}

/* W-387 — DE VOETBALK IS ÉÉN REGEL TOT JE HEM NODIG HEBT.
 *
 * Herman, 2026-08-26: "Dan de footer; misschien ook als popup configureren? Dus
 * één regel blauwe balk en dan de inhoud openklappen of iets dergelijks."
 *
 * WAT ER GEMETEN IS, want het item ging over iets anders dan het leek. De
 * partial rendert de balk sinds W-027 met `hidden` zolang de selectie leeg is,
 * en het commentaar erboven zei dat ook. Alleen: `.bulkbalk` in §5c hierboven
 * zet `display: flex`, en een auteurregel wint van de `[hidden] { display: none }`
 * van de browser — dat is een ORIGINE-conflict en geen specificiteitsconflict,
 * dus specificiteit maakt het niet uit. De balk stond op alle vijf de lijsten
 * altijd in beeld, op elke breedte, ook bij nul geselecteerd. Dat is precies
 * Hermans "~40% van het scherm bij nul geselecteerd", en het verklaart ook
 * waarom het op desktop nooit opviel: daar is hij één regel. Dezelfde val is
 * elders wél afgevangen — `.teller[hidden]`, regel ~540 — en verder nergens in
 * app.css of tokens.css.
 *
 * DAAROM GEEN `.bulkbalk[hidden]`-REPARATIE. Die zou de balk op desktop bij nul
 * geselecteerd laten verdwijnen, en dát is wél een desktopverandering. Het
 * attribuut is uit de partial gehaald. De balk staat er altijd; wat verandert
 * is zijn TOESTAND.
 *
 * ÉÉN WAARHEID: `aria-expanded` op de schakelaar, gezet door selectie.js en
 * gelezen door de regel hieronder. Geen tweede klasse die uit de pas kan lopen
 * met wat een schermlezer hoort — dezelfde afspraak als data-menuknop in ui.js.
 *
 * .heeft-bulkbalk-schakelaar ZET selectie.js op <html>. Zonder JavaScript gaat
 * de balk nooit open, en een dichtgeklapte balk zonder bediening is erger dan
 * een hoge balk: dan blijft alles staan zoals het vandaag staat.
 *
 * OP DESKTOP VERANDERT ER NIETS. De schakelaar is er niet, en de nieuwe
 * omhulling is display:contents — dezelfde truc als `.bulkbalk form` in §5c —
 * dus de acties blijven flexitems van .bulkbalk in dezelfde volgorde.
 *
 * TERUGDRAAIEN: dit blok weghalen. De balk staat dan weer altijd volledig open,
 * ook op smal: de toestand van vóór W-387, zonder de onwaarheid van `hidden`. */
.bulkbalk-acties { display: contents; }
.bulkbalk-schakelaar { display: none; }

@media (max-width: 639px) {
  .heeft-bulkbalk-schakelaar .bulkbalk-schakelaar {
    display: inline-flex; align-items: center; justify-content: space-between;
    flex: 1 1 auto; gap: var(--ruimte-8);
    min-height: var(--aanraakdoel); padding: 0 var(--ruimte-12);
    font: inherit; cursor: pointer;
    color: var(--wit); background: transparent;
    border: 1px solid var(--wit); border-radius: 8px;
  }
  .heeft-bulkbalk-schakelaar .bulkbalk-schakelaar[aria-expanded="false"] + .bulkbalk-acties {
    display: none;
  }
  .heeft-bulkbalk-schakelaar .bulkbalk-schakelaar[aria-expanded="true"] .bulkbalk-kapje {
    transform: rotate(180deg);
  }
}

/* W-390 — DE DOELGROEPRIJ IS OP EEN TELEFOON EEN SNELKOPPELING TE VEEL.
 *
 * Vijf presetpillen over drie regels, vóór de eerste rij van de lijst — dat is
 * een van de dingen die Herman op 2026-08-26 "volledig cluttered" noemde. Ze
 * zijn sinds W-390 ook in het filterpaneel te vinden, dus hier mogen ze weg.
 *
 * OP MOBIEL VERBERGEN EN NIET VERPLAATSEN, en dat is een correctie op mijn
 * eerste opzet. Die haalde de aanklikbare pillen uit de kop en liet alleen de
 * actieve stand staan — één DOM, geen dubbele bestemming. Het klopte alleen
 * niet met wat dit project al doet: TestElkeKopbedieningZitOokInHetFilterpaneel
 * (W-326/D-286) EIST dat elk kopveld óók in het paneel staat, en W-375b lost
 * dat op door de kop op smal te verbergen in plaats van hem leeg te halen. Het
 * paneel is bovendien een overlay: kop en paneel staan nooit tegelijk in beeld.
 * Herman, 2026-08-26: "Op desktop is dit geen probleem. Waarom moeten we op
 * desktop opeens veranderen?" Terecht — deze regel raakt desktop niet.
 *
 * `.chips-doelgroep` EN NIET `.chips-preset`. De bereikpil draagt exact dezelfde
 * drie klassen en moet op mobiel juist blijven staan (D-225, Herman 2026-08-25:
 * "dat scheelt in aanpassen"). Een selector op de gedeelde klasse zou hem
 * meenemen, en dan verlies je op een telefoon het enige dat nog zegt wélk deel
 * van het bestand je ziet.
 *
 * `.heeft-filterpaneel` is de poort, zoals bij W-375b: zonder JavaScript gaat
 * het paneel nooit open, en dan zou verbergen betekenen dat de presets nergens
 * meer bereikbaar zijn.
 *
 * TERUGDRAAIEN: dit blok weghalen. De klasse mag blijven staan; hij is inert. */
@media (max-width: 639px) {
  .heeft-filterpaneel .chips-doelgroep { display: none; }
}

/* W-396 — DRIE DINGEN DIE PAS OP EEN ECHTE TELEFOON ZICHTBAAR WORDEN.
 *
 * Herman keek op 2026-08-26 naar de uitgerolde versie op zijn toestel. Alle
 * drie de punten hieronder zijn van dat kijken, en geen van drieën was uit een
 * schermafbeelding van een browservenster te halen — dat is precies het gat dat
 * W-393 wil dichten.
 *
 * 1. DE FILTER-KNOP EN DE HOOFDACTIE DELEN DE REGEL.
 *
 * W-375b gaf de Filter-knop `flex: 1 1 100%` toen hij als enige overbleef in de
 * kop: een knopje van 70px links in een lege regel leest als een restant. Sinds
 * W-390 staat de hoofdactie ernaast, en die 100% duwt hem dus naar de volgende
 * regel — twee volle-breedte knoppen onder elkaar, precies de stapeling die
 * W-384 kwam opruimen. Herman: "Je ziet filter nu boven nieuw persoon staan.
 * Onhandig. Kunnen naast elkaar en delen de breedte."
 *
 * De regel hieronder geldt alleen als er ook echt een hoofdactie staat
 * (`:has()`), zodat de vier lijsten zonder hoofdactie hun volle-breedte knop
 * houden. `flex: 1 1 0` op allebei geeft ze een gelijke helft, ongeacht hoe
 * lang "Nieuw kledingstuk" is.
 *
 * 2. DE ARCHIEFREGEL GAAT UIT BEELD.
 *
 * "Archief (8) — gearchiveerde rijen staan niet in deze lijst" staat op vijf
 * lijsten tussen de kop en de eerste rij. Op een monitor is dat een voetnoot;
 * op een telefoon is het een regel die je moet passeren om bij je mensen te
 * komen. Herman: "De archief regel mag er van mij op mobiel wel uit." Hij blijft
 * in de DOM en op desktop staan — dit is dezelfde ingreep als bij de
 * signaalregel (W-384), inclusief de haak die niet uit een opsomming van
 * pagina's bestaat.
 *
 * 3. EEN AFNEEMBARE FILTERCHIP WORDT NIET ZO HOOG ALS EEN KNOP.
 *
 * `.chip-af` (het kruisje) krijgt op een aanraakapparaat `--aanraakdoel` als
 * breedte én hoogte (D-092, regel ~1472). Dat maakt het aanraakvlak goed, maar
 * het rekt óók de chip eromheen op: naast de bereikpillen van 32px stond er een
 * chip van bijna 60px. Herman: "die moet zelfde grote krijgen als de bereik
 * pillen."
 *
 * De oplossing houdt het aanraakvlak en laat het uiterlijk met rust: het
 * kruisje blijft visueel klein en een absoluut gepositioneerd ::after strekt
 * het raakbare gebied tot --aanraakdoel, gecentreerd op het kruisje. Zo blijft
 * D-092 gehaald zonder dat de chip meegroeit — een kleiner aanraakvlak zou de
 * verkeerde helft van de afweging weggeven.
 *
 * TERUGDRAAIEN: dit blok weghalen; de klasse lijst-archiefregel mag blijven. */
@media (max-width: 639px) {
  .heeft-filterpaneel .filterbalk:has([data-filterpaneel-open]):has(.lijstkop-hoofdactie) [data-filterpaneel-open],
  .heeft-filterpaneel .filterbalk:has([data-filterpaneel-open]) .lijstkop-hoofdactie {
    flex: 1 1 0;
    min-width: 0;
  }

  .lijst-archiefregel { display: none; }
}

@media (pointer: coarse) {
  .chip-af {
    position: relative;
    width: 18px;
    height: 18px;
  }

  /* Het raakbare gebied, niet het zichtbare. */
  .chip-af::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--aanraakdoel);
    height: var(--aanraakdoel);
    transform: translate(-50%, -50%);
  }
}

/* W-394 — WAT ALLEEN IN EEN TOOLTIP STOND, STAAT NU OP HET SCHERM.
 *
 * Drie vormen, en meer worden het er niet: een uitklapregel bij een zoekveld,
 * een uitlegregel bij een tabel, en een zichtbare uitgang uit het palet.
 * TERUGDRAAIEN: dit blok weghalen; er hangt geen Go-tak aan.
 *
 * <details> en geen vaste regel: de veldenlijst van Mensen is 250 tekens en zou
 * onder elk van de zestien gedeelde lijstkoppen twee regels kosten (D-112,
 * verticale zuinigheid). Native, dus zonder JS en zonder eval — wat de CSP hier
 * ook afdwingt. */
.zoekbereik { margin-top: var(--ruimte-4); font-size: var(--maat-12); color: var(--tekst-gedempt); }
.zoekbereik > summary { cursor: pointer; }
.zoekbereik > p { margin: var(--ruimte-4) 0 0; }

/* Uitleg bij een tabel als <caption>: hij hoort bij de tabel, en een schermlezer
 * leest hem vóór de eerste rij. Links uitgelijnd — gecentreerde uitleg boven een
 * brede tabel leest als een titel. */
.tabel-uitleg {
  caption-side: top; text-align: left; padding-bottom: var(--ruimte-4);
  font-size: var(--maat-12); font-weight: 400; color: var(--tekst-gedempt);
}

/* De zichtbare uitgang uit het palet. Hij staat NAAST [data-toetsbelofte] en
 * nooit erbinnen: verdwijnt de toetsenregel op een apparaat zonder toetsenbord,
 * dan is deze knop precies wat er dan overblijft. .knop krijgt zijn aanraakmaat
 * al uit het (pointer: coarse)-blok eerder in dit bestand. */
.palet-sluit { margin-left: auto; }

@media (pointer: coarse) {
  .zoekbereik > summary { min-height: var(--aanraakdoel); display: flex; align-items: center; }
}

/* W-438 — DE UITKLAPPER ZIT IN DE LABELRIJ EN ZWEEFT ALS HIJ OPENGAAT.
 *
 * Gemeld door Herman op 2026-08-31: "De tooltips onder alle zoekbalken zorgt
 * ervoor dat de overige dropdown selectors en filter button etc nu meebewegen
 * met het openen van de tooltip, en dat ze niet meer op 1 lijn liggen met het
 * zoekbalk."
 *
 * MECHANISCH, EN HET ZIJN TWEE HELFTEN. `.filterbalk` is flex met
 * `align-items: flex-end`: de crossmaat van de regel is de hoogte van het
 * hóógste item en élk item wordt met zijn ONDERKANT tegen die regelonderkant
 * gezet. De zoekkolom is een `.veld` — een grid met `gap: var(--ruimte-4)` — en
 * had DRIE rijen (label, invoer, uitklapper) waar elke buurkolom er twee heeft
 * en de Filter-knop er nul. Die derde rij zit ONDER de invoer, dus:
 *
 *   a) dicht al staat elke buur ±22px lager dan het zoekveld (4px gap + een
 *      summary op --maat-12), en op een aanraakapparaat ±44px, want daar krijgt
 *      de summary --aanraakdoel. Dat is de helft die niemand gemeld heeft en
 *      die het meest opvalt;
 *   b) openklappen maakt die rij hoger, de regel groeit mee, en élke buur zakt
 *      exact evenveel. Dat is de gemelde helft.
 *
 * DE INGREEP, en die lost allebei op met dezelfde twee regelgroepen:
 *
 *   a) de summary verhuist naar de LABELRIJ, rechts naast het label — Herman,
 *      2026-08-31, gevraagd of de uitklapregel naast het label gaat of onder het
 *      veld blijft staan: "Naast het label". Daarmee heeft de zoekkolom evenveel
 *      gridrijen als zijn buren, eindigt hij op zijn invoerveld, en ligt de
 *      onderkant van dat invoerveld weer op de regelonderkant — dezelfde lijn
 *      als de keuzelijsten, de Filter-knop en de hoofdactie;
 *   b) het open lichaam wordt absoluut gepositioneerd ONDER het veld. Het staat
 *      dan buiten de layoutstroom, dus openklappen verplaatst niets meer.
 *
 * DE ANDERE ROUTE IS BEWUST NIET GEKOZEN. Alleen het openklappen onschadelijk
 * maken (b zonder a) laat de ±20px verspringing uit (a) gewoon staan.
 *
 * WAT HIER MET OPZET NIET STAAT. `align-items: flex-start` op `.filterbalk`
 * lost (a) ook op en maakt twee nieuwe fouten: de Filter-knop heeft geen label
 * boven zich en zou dan uitlijnen op de BOVENkant van de labels in plaats van op
 * de invoervelden, en een label dat op twee regels wrapt duwt zijn eigen invoer
 * scheef. En de uitklapper naar een eigen volle-breedte rij verplaatsen haalt
 * hem buiten `.veld` — dan blijft hij op een telefoon staan terwijl het zoekveld
 * eronder door W-375b is weggeklapt, en het maakt
 * TestElkZoekveldZonderPaneelNoemtZijnBereikZichtbaar rood op de zes losse
 * pagina's. De markup blijft daarom byte voor byte zoals hij was.
 *
 * WAAROM :has() EN NIET `.veld-zoek`. `.veld` is het gedeelde veldblok van élk
 * formulier in de app; deze regels mogen alleen de zoekkolom raken, dus er moet
 * iets in de selector staan dat die kolom van de rest onderscheidt. `.veld-zoek`
 * is dat NIET: van de tweeëntwintig filterbalken met een uitklapper wikkelen er
 * twee hun zoekveld in kaal `<div class="veld">` — beheer-links en
 * beheer-huislinks — en een regel op `.veld-zoek` zou precies die twee
 * overslaan. De selector zoekt de zoekkolom daarom op wat haar tot zoekkolom
 * maakt: een `.veld` met een uitklapper erin. Dezelfde vorm als de mobiele
 * lijstkop van W-375b, die de kop opzoekt op zijn poort naar het filterpaneel.
 *
 * DEGRADATIE ALS EEN BROWSER :has() NIET KENT. Elke regel hieronder draagt de
 * `:has()` in zijn ENIGE selector, dus een browser die hem niet begrijpt gooit
 * die regels stuk voor stuk weg — geen selectorlijst die één ongeldige tak
 * meesleept, geen ongeldige declaratie, geen kapotte pagina. Wat er dan staat is
 * letterlijk het scherm van vandaag: de uitklapregel onder het veld, de buren
 * ±20px lager. Geen regressie, alleen geen verbetering. Hetzelfde standpunt als
 * bij `.paneel-op-smal` (W-384) en de mobiele lijstkop (W-375b), en dezelfde
 * ondergrens: :has() draait in elke doelbrowser.
 *
 * BUITEN HET `@media screen`-HEK VAN W-437, met opzet. `@media print` verbergt
 * `.filterbalk` volledig met `display: none !important`, dus op papier bestaat
 * deze hele kolom niet en is er niets af te hekken.
 *
 * TERUGDRAAIEN: dit blok weghalen. Er hangt geen Go-tak en geen markup aan. */

.veld:has(> .zoekbereik) {
  position: relative; /* het anker voor het zwevende lichaam hieronder */
  /* minmax(0, 1fr) en niet 1fr: een kale fractiekolom krimpt niet onder zijn
     inhoud, en het zoekveld eronder overspant deze kolom. Een lang e-mailadres
     zonder spaties zou de tegel dan uitlopen — TestGeenGridkolomDieZijnTegelKanUitlopen
     ving dat hier meteen. */
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--ruimte-12);
  /* Label en summary op één lijn, ook als de summary op een aanraakapparaat
     --aanraakdoel hoog is en het label één tekstregel. Zonder dit staat de
     labeltekst bovenin de rij en de summarytekst in het midden. */
  align-items: center;
}
.veld:has(> .zoekbereik) > label { grid-row: 1; grid-column: 1; }
.veld:has(> .zoekbereik) > .zoekbereik {
  grid-row: 1;
  grid-column: 2;
  margin-top: 0; /* de 4px van .zoekbereik hoort bij de oude derde rij */
}
/* De invoer pakt beide kolommen: hij was volle breedte en moet dat blijven. */
.veld:has(> .zoekbereik) > input { grid-row: 2; grid-column: 1 / -1; }

/* Het open lichaam zweeft onder het veld. `top: 100%` van de `.veld` is de
   onderkant van het invoerveld, want de uitklapper zit nu in de labelrij.
   `width: max-content` met een plafond erop, omdat een absoluut gepositioneerd
   blok anders shrink-to-fit wordt binnen de breedte van de zoekkolom — op
   beheer-links is dat ~200px en dan wordt een tekst van 250 tekens een smalle
   toren. Het plafond loopt mee met het scherm zodat de tekst op 360px binnen de
   inhoudsbreedte blijft (main heeft --ruimte-16 aan weerszijden).
   Eigen achtergrond en rand, want hij zweeft over de lijst eronder; geen
   schaduw — de focusring is de enige schaduw in deze app (zie boven). */
.veld:has(> .zoekbereik) > .zoekbereik > p {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 6;
  width: max-content;
  max-width: min(32rem, calc(100vw - var(--ruimte-16) * 2));
  margin: var(--ruimte-4) 0 0;
  padding: var(--ruimte-8) var(--ruimte-12);
  background: var(--wit);
  border: 1px solid var(--rand);
  border-radius: var(--radius-knop);
}

/* W-458 — DE DAGKAART VAN HET DRAAIBOEK (D-344).
   De festivaldag als één beeld: één regel per draaiboekitem, een kwartierraster
   als X-as, gegroepeerd naar doelgroep met "iedereen" bovenaan.

   HET RASTER IS EEN GRID MET ÉÉN KOLOM PER KWARTIER, en dat is geen
   stijlvoorkeur maar de enige weg die de CSP openlaat. `style-src 'self'` kent
   geen 'unsafe-inline', dus een `style="--a: 36%"` op een balk wordt stil
   genegeerd (TestSpeldZetGeenStijlattribuut, W-369) — positioneren kan hier
   alleen met klassen. Een balk is dus een rij aaneengesloten gevulde vakjes en
   de nu-lijn is één kolom die in élke regel dezelfde markering draagt: exact
   het mechanisme van `tt-nu-kolom` in de Timetable (W-447), één as verder
   ingezoomd. De twee delen daarmee hun kleuren en hun vorm, zoals D-344 vraagt.

   HOEVEEL KOLOMMEN ER ZIJN STAAT NERGENS IN DIT BESTAND. `grid-auto-flow:
   column` met `grid-auto-columns: minmax(0, 1fr)` maakt er precies zoveel als
   de server vakjes rendert, allemaal even breed. Zonder die truc zou hier een
   `grid-template-columns` per daglengte moeten staan — vierentwintig regels die
   niemand ooit naleest. */

.dk-houder {
  max-width: 100%;
  overflow-x: auto;
  overflow-y: visible;
  padding-bottom: var(--ruimte-4);
}

/* min-width: de kaart wordt niet leesbaarder door hem in een telefoonbreedte te
   persen; dan is horizontaal schuiven eerlijker dan vakjes van één pixel. */
.dk {
  --dk-etiket: 260px;
  --dk-rij: 26px;
  min-width: 860px;
}

.dk-regel {
  display: grid;
  grid-template-columns: var(--dk-etiket);
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  align-items: stretch;
  min-height: var(--dk-rij);
}

.dk-etiket {
  min-width: 0;
  padding: 2px var(--ruimte-8) 2px var(--ruimte-4);
  align-self: center;
}
.dk-titel {
  display: block;
  font-size: var(--maat-13);
  font-weight: 600;
  color: var(--kleur-kop);
  line-height: 1.3;
  overflow-wrap: break-word;
}
.dk-meta {
  display: block;
  font-size: var(--maat-12);
  color: var(--tekst-gedempt);
  line-height: 1.35;
  font-variant-numeric: tabular-nums;
}
/* Gestippeld betekent in deze app al "nog niet vastgezet" (.chip-scope), dus de
   conceptpil voert geen nieuwe taal in. De TITEL blijft op volle leeskleur: een
   halfzichtbare titel is op een festivaldag geen nadruk maar een leesprobleem. */
.dk-conceptpil {
  display: inline-block;
  padding: 0 5px;
  border: 1px dashed var(--kleur-selectie-rand);
  border-radius: var(--radius-pil);
  font-size: var(--maat-12);
  color: var(--tekst-gedempt);
}

/* Eén kwartiervakje. De linkerrand is de rasterlijn; op een heel uur is hij
   donkerder, zodat je de uurlabels kunt volgen zonder ze te lezen. */
.dk-cel {
  position: relative;
  border-left: 1px solid var(--rand);
}
.dk-uurgrens { border-left-color: var(--rand-invoer); }

/* De uurregel. Hij is hoger dan een gewone regel omdat er twee dingen boven
   elkaar staan: het uurlabel onderin en het nu-klokje erboven, zodat die twee
   elkaar nooit overschrijven. */
.dk-asrij { min-height: 34px; }
.dk-askop { align-self: end; }
.dk-ascel { border-left-color: var(--rand-invoer); }
.dk-uurlabel {
  position: absolute;
  left: 2px;
  bottom: 0;
  font-style: normal;
  font-size: var(--maat-12);
  color: var(--tekst-gedempt);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.dk-nulabel {
  position: absolute;
  left: -1px;
  top: 0;
  font-style: normal;
  font-family: var(--font-kop);
  font-weight: 600;
  font-size: var(--maat-12);
  letter-spacing: .04em;
  padding: 0 6px;
  border-radius: var(--radius-pil);
  background: var(--kleur-primair);
  color: var(--wit);
  white-space: nowrap;
}

/* De balk. Eén kleur voor alle balken — de groepskop doet het onderscheid, niet
   het palet (D-344, schetsbesluit). De ronde uiteinden zitten op het eerste en
   het laatste vakje van de reeks, zodat een reeks vakjes als één balk leest. */
.dk-vak {
  background: var(--kleur-selectie-rand);
  margin: 5px 0;
}
.dk-vak-begin { border-top-left-radius: var(--radius-pil); border-bottom-left-radius: var(--radius-pil); }
.dk-vak-eind { border-top-right-radius: var(--radius-pil); border-bottom-right-radius: var(--radius-pil); }
/* Concept: bleek met een gestippelde rand. Staat ná .dk-vak, dus wint op
   volgorde bij gelijke specificiteit — en werkt ook los, als legendavakje. */
.dk-vak-concept {
  background: var(--kleur-selectie);
  border-top: 1px dashed var(--kleur-selectie-rand);
  border-bottom: 1px dashed var(--kleur-selectie-rand);
}
.dk-vak-concept.dk-vak-begin { border-left: 1px dashed var(--kleur-selectie-rand); }
.dk-vak-concept.dk-vak-eind { border-right: 1px dashed var(--kleur-selectie-rand); }

/* Het merkteken: een ruit op het starttijdstip van een item zonder eindtijd.
   Geen korte balk — die heeft een breedte die je als duur leest, en Go/No Go
   ís een moment (D-344). */
.dk-moment::after {
  content: "";
  position: absolute;
  left: -5px;
  top: 50%;
  width: 10px;
  height: 10px;
  margin-top: -5px;
  background: var(--kleur-kop);
  border-radius: 2px;
  transform: rotate(45deg);
}

/* De nu-lijn: dezelfde vorm als de lopende maand in de Timetable (W-447), een
   linkerrand van 2px in de primaire kleur op élke regel. Specificiteit 0,1,0
   met de tagselector erbij zou hier niets winnen; wat hem laat winnen is de
   volgorde — deze regel staat ná .dk-cel en .dk-uurgrens. */
.dk-nu-kolom { border-left: 2px solid var(--kleur-primair); }

/* De groepskop. Hij doet het werk dat kleur niet doet: zeggen voor wie deze
   regels zijn. */
.dk-groep { margin-top: var(--ruimte-12); }
.dk-groep-kop {
  display: flex;
  align-items: baseline;
  gap: var(--ruimte-8);
  padding: 3px var(--ruimte-8);
  background: var(--vlak);
  border-radius: var(--radius-knop);
  margin-bottom: var(--ruimte-4);
}
.dk-groep-kop h3 {
  font-size: var(--maat-13);
  font-weight: 600;
  margin: 0;
}

/* De bereikpillen. Zelfde chrome als de bereikpil van de lijsten (.chip), want
   een pil is een ingang en daar is al een vorm voor (STYLE §5b, D-170); wat
   hier anders is, is dat er meer dan één tegelijk aan kan staan. */
.dk-bereik { align-items: baseline; }
.dk-pil-aan { background: var(--sein-blauw-vlak); border-color: var(--sein-blauw-rand); }
.dk-pil-telling { color: var(--tekst-gedempt); font-variant-numeric: tabular-nums; }
.dk-telling { margin-left: auto; }

/* De legenda. Buiten elke bedieningsbalk — precies daar ging het bij de
   Timetable-legenda mis (W-446): een sleutel die met de bediening van het
   papier valt, laat een gekleurd raster zonder betekenis achter (D-288). */
.dk-legenda {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ruimte-16);
  margin-top: var(--ruimte-12);
  padding-top: var(--ruimte-8);
  border-top: 1px solid var(--rand);
  font-size: var(--maat-12);
  color: var(--tekst-gedempt);
}
.dk-legenda span { display: inline-flex; align-items: center; gap: var(--ruimte-8); }
.dk-legenda-vlak {
  position: relative;
  flex: none;
  width: 22px;
  height: 10px;
  border-radius: var(--radius-pil);
}

@media (max-width: 720px) {
  .dk { --dk-etiket: 180px; }
  .dk-telling { margin-left: 0; }
}


/* ── Uitrijlijst (W-482, D-358) ────────────────────────────────────────────
   Het werkpapier van /rekwisieten/uitrijlijst: een blok per locatie met een
   grijze kopregel, de contactpersoon eronder en een tabel met een foto per
   regel. De vorm komt van de PDF uit ronde 3 (docs/voorstellen/W-482-
   uitrijlijst.md §2). Geen eigen kleuren: de kop gebruikt dezelfde
   oppervlakte- en randtokens als de rest. */
.uitrij-blok { margin: 0 0 20px; border: 1px solid var(--rand); border-radius: 6px; overflow: hidden; }
.uitrij-kop { margin: 0; padding: 6px 10px; font-size: 1rem; background: var(--vlak); }
.uitrij-kop .uitrij-adres { font-weight: 400; }
.uitrij-contact { margin: 0; padding: 4px 10px; font-style: italic; }
/* W-525 — de inforegel: productie · programmanummer · capaciteit · klok. Eén
   regel, ook op papier: geen wrap, en wat niet past kapt af in plaats van het
   blok twee regels hoog te maken. */
.uitrij-info { margin: 0; padding: 4px 10px; border-bottom: 1px solid var(--rand); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.uitrij-info .uitrij-info-scheiding { margin: 0 6px; color: var(--tekst-gedempt); font-weight: 400; }
/* Op een telefoon kapte die ene regel programmanummer en klok weg; daar wrapt
   hij (2026-09-11). Alleen scherm: papier houdt de ene regel hierboven. */
@media screen and (max-width: 719px) {
  .uitrij-info { white-space: normal; overflow: visible; text-overflow: clip; }
}
.uitrij-tabel td, .uitrij-tabel th { vertical-align: top; }
.uitrij-tabel .uitrij-tekst { white-space: pre-line; }
.uitrij-tabel .uitrij-vink { text-align: center; white-space: nowrap; }
.uitrij-foto img { max-width: 90px; max-height: 90px; display: block; border-radius: 3px; }
.uitrij-stardrift td { color: var(--tekst-gedempt); } /* stardrift én act-materiaal (W-489): allebei materiaal dat de ploeg niet zelf hoeft te regelen */
.uitrij-samenvatting { margin-top: 32px; }
.uitrij-samenvatting h2 { font-size: 1.1rem; margin: 0 0 4px; }
/* W-526: vijf regels van label + getal. Zo smal als de inhoud, want een tabel
   van twee kolommen die de hele bladbreedte pakt laat het getal een halve
   meter van zijn label af staan. */
.uitrij-samenvatting-tabel { width: auto; min-width: min(100%, 340px); }
.uitrij-samenvatting-tabel th[scope="row"] { text-align: left; font-weight: 600; }
.uitrij-samenvatting-tabel th[scope="row"] .gedempt { display: block; font-weight: 400; }
.veld-vinkje label { display: inline-flex; align-items: center; gap: 6px; }

/* ── Lunchweergave (W-492, D-360; drie groepen sinds W-502/D-363) ──────────
   /mensen?weergave=lunch. Locaties deed dit eerst met Lijst/Kaart (W-367,
   D-317): geen aparte pagina, een tweede WEERGAVE van Mensen. Bovenaan het
   skelet (aantal tasjes per groep + dieetwensen, twee tabellen naast
   elkaar), daaronder per groep een sectie met de plekken als <details>.

   D-363 schrapte de distributiekeuze (geen <select> meer op skelet of plek —
   de distributie is nu een vaste pil per groep) en de marge per dieetwens
   (vervangen door de dieetaantallen die elke externe groep zelf opgeeft,
   W-501). Wat overblijft is de W-499-keuze op een externe groepsregel
   ("op de plek | ophalen bij het uitdeelpunt").

   Klassencontract met de markup (internal/handlers/mensenlunch_weergave.go,
   templates/partials/mensen-lunch-*):
     .lunch-skelet            grid, de twee tabellen onder elkaar (W-527)
     .lunch-blokkop           kopregel boven zo'n tabel (titel + gedempte metatekst)
     .lunch-skelet-tabel      <table class="tabel lunch-skelet-tabel"> — aantallen
     .lunch-dieet-tabel       <table class="tabel lunch-dieet-tabel"> — dieetwensen
     .lunch-distributie       de <select> op een externe groepsregel (W-499)
     .lunch-sectie            <section> per groep (Speellocaties/Mobiel/Uitdeelpunt)
     .lunch-sectie-kop        de <h2> erin (titel · subtekst · som rechts)
     .lunch-sectie-som        de "N tasjes · eigen + extern · plekken" tekst
     .lunch-koprij            kolomkoppen boven de plekkenlijst (zelfde grid als summary)
     .lunch-plek              <details class="lunch-plek"> — één plek of het uitdeelpunt
     .lunch-plek-naam         naam + pijl in de <summary>
     .lunch-plek-pijl         het driehoekje, roteert op [open]
     .lunch-extern-naam       "· Groep (12)" achter de naam, gedempt en klein
     .lunch-binnenin          de uitgeklapte inhoud (tabel met personen + evt. groepen)
     .lunch-extern-rij        <tr> voor een externe groep binnenin — niet aan te vinken
     .lunch-ook               "ook op"-tekst in de laatste kolom van een persoonsrij
     [data-lunch-plek-vinkje] het plekvinkje in de <summary> — static/js/lunch.js
     [data-selectie-rij]      de gewone rijvinkjes — static/js/selectie.js
     .lunch-groep-vorm        W-499: de "op de plek | ophalen bij het uitdeelpunt"-select op een externe groepsrij
   .pil / .pil-groen/-amber/-rood/-blauw/-grijs en .aard / .aard-binnen/-buiten
   staan hieronder als eigen klassen: het bestaande .badge kent geen blauwe
   variant en de kleine statusstip is iets anders dan de genummerde
   .kaart-speld op de kaartpagina's (W-369). De aard blijft zichtbaar op de
   PLEKREGEL (binnen/buiten, uit LocatieAard) — D-363 schrapte alleen de
   groepering ernaar, niet de informatie zelf. */

/* W-527: de twee tabellen staan ONDER elkaar, allebei op de volle breedte.
   Ze stonden naast elkaar (1.6fr / 1fr), maar de dieettabel kreeg daardoor
   te weinig ruimte: de kolom "waar" (productie per productie) stapelde tot
   een smalle hoge kolom en de tabel las als een muur. Herman, 2026-09-10:
   "Kun je hem onder lunchtasjes per groep zetten en gewoon op het hele
   frame laten gaan." Eén kolom, geen mediaquery meer nodig. */
.lunch-skelet {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ruimte-24);
  align-items: start;
  margin-bottom: var(--ruimte-24);
}

.lunch-blokkop { display: flex; align-items: baseline; gap: var(--ruimte-8); margin-bottom: var(--ruimte-8); }
.lunch-blokkop .klein { margin-left: auto; }

.lunch-skelet-tabel td.numeriek, .lunch-skelet-tabel th.numeriek,
.lunch-dieet-tabel td.numeriek, .lunch-dieet-tabel th.numeriek {
  text-align: right;
}
.lunch-skelet-tabel tfoot td, .lunch-dieet-tabel tfoot td {
  font-weight: 600;
  color: var(--kleur-kop);
  background: var(--vlak);
}

.lunch-plek-getal { font-variant-numeric: tabular-nums; text-align: right; }

.lunch-distributie { display: flex; flex-direction: column; align-items: flex-start; gap: var(--ruimte-4); }
.lunch-distributie .veld { min-width: 170px; }

/* De pil: dezelfde vorm als .badge (STYLE.md §3) maar met een blauwe variant
   erbij (dieetwens) — .pil in plaats van .badge zodat de vijf kleuren bij
   elkaar staan zonder het bestaande .badge-gebruik elders te verzwaren. */
.pil {
  display: inline-block;
  padding: 1px 8px;
  border-radius: var(--radius-pil);
  font-size: var(--maat-12);
  line-height: 1.6;
  white-space: nowrap;
  border: 1px solid transparent;
}
.pil-groen { background: var(--sein-groen-vlak); color: var(--sein-groen-tekst); }
.pil-amber { background: var(--sein-amber-vlak); color: var(--sein-amber-tekst); }
.pil-rood  { background: var(--sein-rood-vlak);  color: var(--sein-rood-tekst); }
.pil-blauw { background: var(--sein-blauw-vlak); color: var(--sein-blauw-tekst); border-color: var(--sein-blauw-rand); }
.pil-grijs { background: var(--vlak); color: var(--tekst-gedempt); border-color: var(--rand); }

/* De aard-stip: de kleur van de uitdeelwijze als klein statusteken vóór een
   naam of titel. Nooit het enige kanaal (D-093) — er staat altijd het woord
   "binnen"/"buiten"/… naast, hier en in .lunch-sectie-kop. */
.aard { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: var(--ruimte-4); vertical-align: baseline; }
.aard-binnen { background: var(--kaartkleur-binnen); }
.aard-buiten { background: var(--kaartkleur-buiten); }

.lunch-sectie { margin-top: var(--ruimte-24); }
.lunch-sectie-kop {
  display: flex;
  align-items: baseline;
  gap: var(--ruimte-8);
  padding-bottom: var(--ruimte-8);
  margin-bottom: 0;
  border-bottom: 2px solid var(--kleur-kop);
}
.lunch-sectie-som {
  margin-left: auto;
  font-size: var(--maat-13);
  font-weight: 500;
  color: var(--tekst-gedempt);
  font-variant-numeric: tabular-nums;
}
.lunch-sectie-som b { color: var(--kleur-kop); font-weight: 600; }

/* De kopregel én de <summary> van elke plek delen exact hetzelfde grid, anders
   staat de kolomkop niet boven zijn kolom. Onder 900px valt de aard-, extern-
   en dieet/distributiekolom weg (mock-breekpunt, plan §P3): wat overblijft is
   vinkje · naam · eigen · totaal, genoeg om een plek te herkennen en te
   selecteren op een telefoon. */
.lunch-koprij,
.lunch-plek > summary {
  display: grid;
  grid-template-columns: 24px minmax(0, 1.5fr) 90px 60px 60px 60px minmax(170px, 1fr);
  gap: var(--ruimte-8);
  align-items: center;
  padding: 7px 8px;
}
/* De dieetkolom draagt vrije tekst (de toelichting bij Anders): hij groeit mee
   met de plek in plaats van vast 170px te zijn, en een pil die dan nog te breed
   is breekt over regels in plaats van de tabel uit te lopen (2026-09-11). */
.lunch-plek > summary .pil { white-space: normal; }
.lunch-koprij {
  font-family: var(--font-kop);
  font-size: var(--maat-12);
  font-weight: 600;
  color: var(--kleur-kop);
  background: var(--vlak);
  border-bottom: 1px solid var(--rand);
}
.lunch-plek { border-bottom: 1px solid var(--rand); }
.lunch-plek > summary {
  list-style: none;
  cursor: pointer;
  font-size: var(--maat-13);
}
.lunch-plek > summary::-webkit-details-marker { display: none; }
.lunch-plek > summary:hover { background: var(--kleur-selectie); }
.lunch-plek[open] > summary { background: var(--vlak); }

.lunch-plek-naam {
  font-weight: 600;
  color: var(--kleur-kop);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lunch-plek-pijl { display: inline-block; width: 12px; color: var(--tekst-gedempt); transition: transform 0.15s; }
.lunch-plek[open] .lunch-plek-pijl { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .lunch-plek-pijl { transition: none; } }

.lunch-extern-naam {
  font-weight: 500;
  color: var(--tekst-gedempt);
  font-size: var(--maat-12);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.lunch-binnenin { padding: var(--ruimte-4) var(--ruimte-8) var(--ruimte-12) 40px; }
.lunch-binnenin .tabel { font-size: var(--maat-13); }
.lunch-binnenin .tabel th { background: transparent; }
.lunch-extern-rij td { color: var(--tekst-gedempt); font-style: italic; }
.lunch-extern-rij td b { font-style: normal; color: var(--tekst); }
.lunch-ook { color: var(--tekst-gedempt); font-size: var(--maat-12); }

/* W-499 (2026-09-08, D-362) — geen nieuwe lay-out, past in de bestaande tabel. */
.lunch-groep-vorm { display: inline-flex; align-items: center; gap: var(--ruimte-4); margin-top: var(--ruimte-4); }

/* W-501 — de vijf dieetvelden op het productieformulier: één rij, klein
   (zelfde .rij-utility als elders, .veld-idioom voor de omhulling). */
.lunch-dieet-formulier { flex-wrap: nowrap; }
.lunch-dieet-getal { width: 4.5em; text-align: right; }
.lunch-groep-vorm .lunch-distributie { flex-direction: row; }

@media (max-width: 900px) {
  .lunch-koprij, .lunch-plek > summary { grid-template-columns: 24px minmax(0, 1fr) 60px 60px; }
  .lunch-koprij > :nth-child(3), .lunch-koprij > :nth-child(5), .lunch-koprij > :nth-child(7),
  .lunch-plek > summary > :nth-child(3), .lunch-plek > summary > :nth-child(5), .lunch-plek > summary > :nth-child(7) {
    display: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   HET PRINTCONTRACT — W-456, D-343, uitgemeten in docs/voorstellen/W-439-printvorm.md
   ═══════════════════════════════════════════════════════════════════════════

   TOT HIER STONDEN ER ZES @media print-BLOKKEN, verspreid over het bestand,
   elk ontstaan bij één scherm: het scanblok (1285), Financiën (1331), de
   generale-planner (1394), de draaiboek-tijdlijn (1704), de Timetable (1861) en
   de notule (1974). Ze wáren al één contract — app.css laadt op elke pagina en
   alle 34 regels gebruikten globale selectors — alleen een ongeschreven. Wat er
   uit de printer kwam was dus niet "het blok van dit scherm" maar de som van
   alle zes, op élk scherm, en precies dáár kwamen de drie ernstigste fouten
   vandaan (§0/C1 van het voorstel):

     * de notuleregel `.kol-extra { display: table-cell !important }` besliste de
       kolomkeuze van élke lijst;
     * het draaiboekblok verborg `.weergavebalk` en doodde daarmee de
       Timetable-legenda — een gekleurd rooster zonder sleutel (§3 C6, W-446);
     * de generaleregel `li { break-inside: avoid }` maakte elk lijstitem in de
       hele app onbreekbaar zonder dat dat ooit besloten was.

   Zes blokken zijn hier één sectie geworden met VIER LAGEN (§5b): bediening
   weg, paginaopmaak, identiteit, en per gezicht alleen wat echt verschilt. Drie
   regels zijn geschrapt in plaats van verhuisd: `a[href]:after { content: "" }`
   stond drie keer om een `attr(href)`-regel te neutraliseren die nergens in dit
   bestand bestaat, en de kale `li` uit de break-inside-lijst.

   DE DRIE GEZICHTEN VAN D-343 STAAN IN DE MARKUP, niet hier: elke pagina die
   iets afdrukt draagt `data-printgezicht="werkpapier|archiefstuk|overzicht"` op
   haar wortel en rendert `{{template "print-voet" …}}`. Welke pagina welk
   gezicht heeft is één tabel op twee plekken —
   internal/handlers/printgezicht_w455_test.go (structuur, in de poort) en
   e2e/specs/printvorm.spec.ts (de browsermeting, on demand per D-341).

   WAT HIER NIET STAAT EN OOK NIET HOORT TE STAAN: een tweede CSS-gezicht voor
   liggend. "Eerst liggend" is een keuze van wie print, in zijn eigen
   printdialoog; een stylesheet die dat overneemt kan het maar op één manier
   fout doen (D-343 V7). */

/* ── Laag 3 — identiteit, de schermhelft ────────────────────────────────────
   `.alleen-print` bestaat op het scherm niet; op papier wel. Dat is het
   mechanisme waarmee de notule sinds W-264 een kop op papier draagt zonder dat
   de pagina-kop met zijn knoppen mee hoeft, en het is nu ook het mechanisme
   onder de gedeelde voetregel. Buiten @media print, want de schermregel is dat
   het er níét staat. */
.alleen-print { display: none; }

/* De voetregel zelf (D-343 V2). Klein en grijs, met een dunne scheiding erboven
   zodat hij als onderschrift leest en niet als laatste alinea. De opmaak staat
   hier en niet in het printblok: het element bestaat op het scherm toch niet,
   dus er valt niets te overschrijven — en een regel die alleen in @media print
   kan winnen hoort daar te staan omdat hij daar iets terugdraait, niet omdat
   hij toevallig over papier gaat. */
.print-voet {
  margin-top: var(--ruimte-16);
  padding-top: var(--ruimte-8);
  border-top: 1px solid #999;
  font-size: var(--maat-12);
  color: #555;
}
.print-voet-titel { font-weight: 600; }

/* ── Laag 2 — paginaopmaak: één @page ──────────────────────────────────────
   Vandaag stond hier niets, dus wat er uit de printer kwam was browserstandaard
   en dat verschilt per browser. GEEN PAGINANUMMER: dat kan alleen via
   margeboxen (@bottom-center), en die werken in Chrome wel en in Firefox en
   Safari niet — een belofte die twee van de drie browsers stil negeren is geen
   contract (D-343 V7, Herman: "pagina nummer mag komen te vervallen").

   WAAROM 12mm × 10mm. Verticaal 12mm: ruim boven de ~10mm die vrijwel elke
   inkjet en laser fysiek niet kán bedrukken, en genoeg lucht onder de laatste
   regel om de voetregel niet tegen de rand te duwen. Horizontaal 10mm: dat laat
   190mm over (≈718 css-px bij 96 dpi) voor de dichtste lijsten — Rekwisieten
   heeft elf kolommen — terwijl de gemeten tabellen op 762px al vouwden in
   plaats van af te kappen (§3 C5). Krapper zou de eerste onvouwbare kolom
   dichterbij halen; ruimer kost een kolom aan lucht die niets draagt. */
@page { margin: 12mm 10mm; }

@media print {
  /* ── Laag 1 — BEDIENING GAAT WEG ─────────────────────────────────────────
     Eén woord voor "dit is bediening": `.geen-print`. Tot W-456 waren er drie
     klassen — `.nietprint`, `.geen-print` en `.alleen-print` — waarvan de eerste
     twee synoniemen waren en elk maar in de helft van de verbergregels stonden.
     Dat het geen kwaad kon kwam alleen doordat de blokken optelden (§5a punt 4).
     `.alleen-print` blijft en betekent het tegenovergestelde; `.nietprint` is
     overal omgezet.

     Daarnaast de generieke haken: de balken die per definitie bediening zijn, en
     de elementsoorten. Een knop op papier is inkt en niets anders (§4 K4). */
  .geen-print,
  .kop-balk, .hoofdnav, #meldingen,
  .toolbalk, .filterbalk, .weergavebalk, .bulkbalk, .sectietabs,
  .filterpaneel-houder, .formulier-acties, .melding-balk, .conflicten,
  .staat-acties, .boekjaar-pillen, .rooster-panel, .rooster-pijl,
  .actiepunt-wie, .actiepunt-status { display: none !important; }

  /* Knoppen, in beide vormen: het element en de klasse die een <a> als knop
     laat lezen. De browsermeting van W-455 telt letterlijk elke zichtbare
     <button> op de gesimuleerde A4, dus de knopVORM wegnemen is niet genoeg —
     het element moet weg. Waar de tekst eronder wél op papier hoort (de
     roostertegel, D-343 V5=B) staat er een `.alleen-print`-tweeling naast. */
  button { display: none !important; }
  .knop { display: none !important; }

  /* Invoervelden. Een leeg vakje op papier belooft iets invulbaars dat het niet
     is, en een uitgeklapte keuzelijst toont één van de negen keuzes zonder te
     zeggen dat het een keuze was. Formulierschermen dragen volgens D-343 geen
     gezicht: daar is de afdruk sowieso zinloos, en dat mag gezegd worden. */
  input { display: none !important; }
  select { display: none !important; }
  textarea { display: none !important; }

  /* Bedieningstékens: de sorteerpijl in de koppenrij (dát er gesorteerd is
     hoort in de voetregel, niet als pijl in een kop), de × achter een actieve
     filterchip en "wis alle filters" ernaast, en de niet-gekozen pillen van een
     chipmenu. Wat blijft staan is de STAND: de actieve chip is een <span> en
     draagt geen van deze klassen. Dat repareert meteen §3 C4 — op papier waren
     "deze editie" en "hele bestand" twee identieke pillen naast elkaar, want
     print-color-adjust laat de vulling van de actieve pil weg. */
  .sorteer { display: none !important; }
  .chip-af { display: none !important; }
  .chip-wis { display: none !important; }
  .chip-preset { display: none !important; }
  .chip-link { display: none !important; }
  .tt-categorie-nieuw { display: none !important; }

  /* De tabelonderschriften van deze app leggen uit wat er gebeurt als je KLIKT
     ("Klik op een bron voor alle inkomsten daarvan"). Dat is bedieningsuitleg,
     en op /geld kost hij bovendien de regels die de staat over de paginarand
     duwden. */
  .tabel-uitleg { display: none !important; }

  /* De opmerkingen zijn deel van de notule en horen dus mee. Wat er NIET bij
     hoort is de bediening eromheen: de metaregels per opmerking, het
     toevoegformulier, en de uitleg over waar een actiepunt thuishoort. */
  #notule-opmerkingen .meta { display: none !important; }
  #notule-opmerkingen form { display: none !important; }
  #notule-opmerkingen > p.gedempt { display: none !important; }
  /* Wat er WEL blijft leest als lopende tekst en niet als opsomming: een notule
     kent alinea's, geen bullets. */
  #notule-opmerkingen .lijst { list-style: none; padding-left: 0; }

  /* ── Laag 2 — PAGINAOPMAAK ──────────────────────────────────────────────
     Wit papier, zwarte inkt, geen linkkleur die als grijs uit een zwart-wit
     printer komt. `main` laat zijn schermkolom en zijn eigen marge los: de
     paginamarge staat nu in @page hierboven, en twee marges over elkaar heen is
     een halve kolom papier weggegooid. */
  body { background: #fff; color: #000; }
  main { max-width: none; padding: 0; }
  a { color: #000; text-decoration: none; }

  /* Waar een pagina NIET mag breken (D-343 V5): niet midden in een kaart of een
     blok. Eén contractregel in plaats van vier schermregels — en met opzet
     zonder de kale `li` die hier tot W-456 in stond: die maakte élk lijstitem in
     de hele app onbreekbaar en is nooit besloten. `#notule-opmerkingen li` is de
     ene plek waar het wél besloten is en staat er daarom apart. */
  .kaart, .blok, .dag-blok, .tijdlijn-dag, .mijn-sectie, .staat-blok,
  .tabel tr, .dk-regel, #notule-opmerkingen li { break-inside: avoid; }

  /* Niets wordt stil afgekapt (§4 K3). `.tabel-houder` houdt op het scherm
     `overflow-x: auto` als laatste redmiddel; op papier bestaat schuiven niet,
     dus dan is dat geen redmiddel maar een schaar. Vandaag verandert er niets
     zichtbaars — de vier gemeten lijsten vouwden alle vier binnen het
     paginavlak — en de val is dicht voor de eerste cel die niet kan vouwen. */
  .tabel-houder { overflow: visible; }

  /* ── Laag 3 — IDENTITEIT ────────────────────────────────────────────────
     Wat alleen op papier bestaat, verschijnt hier: de kop die de notule al had
     en de gedeelde voetregel van D-343. */
  .alleen-print { display: block; }

  /* ── TABELLEN — het gezicht dat alle drie de gezichten delen ────────────
     Randen om elke cel, want een tabel zonder lijnen leest op papier als een
     kolommenbrij: er is geen hover om een rij mee te volgen. */
  .tabel { border-collapse: collapse; width: 100%; }
  .tabel td, .tabel th { border: 1px solid #999; padding: 4px 6px; }

  /* De kolommen die een smal SCHERM weglaat horen op papier juist mee: het
     paginavlak is 794px en passeert daarmee de mobiele grens, terwijl een
     bestuurslid op zijn monitor wél alle kolommen ziet. Papier krijgt dus de
     kolommen die in de app aan staan (D-343 V3 = A); wie álles wil, neemt de
     export (D-224b bewaakt dat die de kolominstelling niet leest). */
  .kol-extra { display: table-cell !important; }

  /* ── Gezicht WERKPAPIER — de roostertegel ───────────────────────────────
     De tegel is op het scherm een <button>: hij opent het toewijspaneel. Op
     papier is dat inkt, maar de tekst erin — tijd, bezetting, namen — is precies
     waarvoor je het vel meeneemt (D-343 V5 = B). De button gaat dus weg en de
     `.alleen-print`-tweeling ernaast neemt de inhoud over; beide komen uit
     dezelfde partial, zodat ze niet uit elkaar kunnen lopen.
     `display: grid` en niet het `block` van `.alleen-print`: de drie regels van
     een tegel horen onder elkaar, net als op het scherm. */
  .rooster-slot.alleen-print { display: grid; }

  /* ── Gezicht ARCHIEFSTUK — de staat van baten en lasten ─────────────────
     De schermkolom van 46rem is er om een lange regel leesbaar te houden; op
     papier bepaalt @page de breedte al. */
  .staat { max-width: none; }

  /* ── Gezicht WERKPAPIER — de draaiboek-tijdlijn ─────────────────────────
     De "nu bezig"-band is een schermsignaal dat op het moment van afdrukken al
     verlopen is. Hij drukt af als een grijze balk over één willekeurige rij, en
     dat leest als nadruk die er niet hoort te zijn. */
  .tabel.tijdlijn tr.nu-actief > td { background: transparent; }

  /* ── Gezicht OVERZICHT — de Timetable-matrix ────────────────────────────
     Dit is precies het overzicht dat aan de muur hangt tijdens een
     bestuursvergadering: laat hem uitschuiven in plaats van afkappen. */
  .tt-houder { max-height: none; min-height: 0; overflow: visible; }
  /* `table.` ervoor: zonder dat verliest deze regel van `table.tabel` en bleef
     de afdruk op 13px staan (W-447). */
  table.tt-matrix { width: 100%; font-size: 9px; }
  .tt-matrix thead th { position: static; }
  .tt-matrix .tt-hoek, .tt-matrix .tt-taak, .tt-categorie-plak { position: static; }
  .tt-cel, .tt-matrix th { border: 1px solid #999; }
  /* W-210 bevinding C / W-446: de legenda BLIJFT staan. De status van een taak
     zit op scherm in drie kanalen — kleur, legenda en een `title` op de balk —
     en op papier valt dat derde kanaal per definitie weg. Compacter mag, weg
     niet; TestPrintVerbergtGeenLegenda meet dat als uitkomst en niet als
     selectornaam. De legenda staat sinds W-446 náást `.weergavebalk` en niet
     erin, precies omdat die balk hierboven verborgen wordt. */
  .tt-legenda { font-size: 8px; }
  /* En zonder dit laat een browser standaard élke achtergrondvulling weg, dus
     dan zijn de balken én de legendavakjes drie keer hetzelfde lege blokje. */
  .tt-open, .tt-loopt, .tt-klaar, .tt-legenda-vlak { print-color-adjust: exact; -webkit-print-color-adjust: exact; }

  /* ── Gezicht WERKPAPIER — de dagkaart ───────────────────────────────────
     W-458/D-344. Vier regels, en elke regel doet iets dat op papier anders moet
     dan op het scherm.

     De schuifbalk van de kaart is op papier geen redmiddel maar een schaar
     (§4 K3): laat hem uitschuiven, en laat de minimumbreedte los zodat de
     vakjes zich naar het paginavlak voegen in plaats van eroverheen te lopen.
     Een groepskop hoort niet als laatste regel op een pagina te eindigen met
     zijn eerste item op de volgende. En zonder print-color-adjust laat een
     browser élke achtergrondvulling weg — dan zijn de balk, de concept-balk,
     het merkteken en de nu-lijn vier keer hetzelfde lege vakje, óók in de
     legenda (TestKleurbetekenisOverleeftDePrinter). */
  .dk-houder { overflow-x: visible; }
  .dk { min-width: 0; }
  .dk-groep-kop { break-after: avoid; }
  .dk-vak, .dk-vak-concept, .dk-moment, .dk-nu-kolom, .dk-legenda-vlak {
    print-color-adjust: exact; -webkit-print-color-adjust: exact;
  }


  /* ── Uitrijlijst (W-482): blok per locatie op één vel ───────────────────
     Géén liggende @page hier, met opzet: D-343 V7 legt "eerst liggend" bij wie
     print, in de printdialoog — de handleiding zegt het erbij. Wat de
     stylesheet wél mag bepalen is de opdeling: een locatieblok breekt niet over
     twee vellen (de ploeg leest per locatie), een regel evenmin, en de
     voorzieningen beginnen op een nieuw vel zodat team 1 en team 2 elk hun
     eigen stapel hebben. De grijze kopregel houdt zijn vulling, anders is een
     blokgrens op papier onzichtbaar. */
  .uitrij-blok { break-inside: avoid; }
  .uitrij-tabel tr { break-inside: avoid; }
  /* W-526: de samenvatting is een slotsom van vijf regels en begint dus geen
     eigen vel; ze moet alleen niet doormidden gescheurd worden. */
  .uitrij-samenvatting { break-inside: avoid; }
  .uitrij-foto img { max-width: 70px; max-height: 70px; }
  .uitrij-kop { print-color-adjust: exact; -webkit-print-color-adjust: exact; }

  /* ── Lunchweergave (W-492): één plek per blok, geen paginaeinde erdoorheen ──
     Werkpapier (plan §0.2, geen printroute — het scherm ís het A4): elke plek
     is een eigen afvinkbaar blokje en mag niet over twee vellen splitsen, net
     als een uitrijblok hierboven. De <details> zelf staat vóór dit blok al
     open — static/js/lunch.js doet dat op "beforeprint", want
     `details:not([open]) > *:not(summary) { display: block }` overschrijven
     werkt niet meer: browsers verbergen gesloten details-inhoud tegenwoordig
     via content-visibility, niet via een gewone display-regel die een
     auteurstijl kan winnen. select/input zijn al globaal weg (Laag 1
     hierboven) — behalve het plek- en rijvinkje, want dit ís een werkpapier en
     daar is een leeg hokje het punt (regel hieronder). De aard-stip en de
     dieet/distributiepillen dragen kleur als betekenis, dus die kleur moet de
     printer ook echt krijgen. */
  .lunch-plek { break-inside: avoid; }
  .lunch-sectie-kop { break-after: avoid; }
  /* pil-rood staat hier bewust niet bij: op deze pagina komt hij nergens voor
     (geen enkele lunchpil is rood), en een regel die niets raakt is dode
     printregel-gewicht (TestGeenPrintregelDieNietsDoet, W-447). */
  .aard, .pil-groen, .pil-amber, .pil-blauw {
    print-color-adjust: exact; -webkit-print-color-adjust: exact;
  }

  /* De vinkjeskolom is de ENIGE uitzondering op "geen invoer op papier", en
     alleen op een werkpapier: op een afvinklijst voor de festivaldag is een leeg
     hokje precies het punt (D-343 V4 = B, Herman). Op een archiefstuk of een
     overzicht is een vinkje gewoon bediening, en dan geldt de regel hierboven.
     Dit is de plek waar `data-printgezicht` echt werk doet: één haak in plaats
     van een lijst met lijstnamen die veroudert zodra er een lijst bij komt. */
  [data-printgezicht="werkpapier"] input[type="checkbox"] { display: inline-block !important; }
}
