/* =============================================================
   Template App - Basisstijlen (publiek + admin)
   Gebruik altijd CSS-variabelen, hardcode nooit kleuren.
   ============================================================= */

/* --- Root variabelen --- */
:root {
    --primary:        #6366f1;
    --primary-dark:   #4f46e5;
    --accent:         #10b981;
    --accent-dark:    #059669;
    --sidebar-bg:     #1e1b4b;
    --sidebar-text:   rgba(255,255,255,.75);
    --sidebar-active: rgba(255,255,255,.12);
    --sidebar-border: rgba(255,255,255,.08);

    --success:        #10b981;
    --warning:        #f59e0b;
    --danger:         #ef4444;
    --danger-dark:    #dc2626;
    --info:           #3b82f6;

    /* ── Signaalkleuren: kleur heeft drie betekenissen (Henri, 15-09-2026) ──
       blauw = tip (het kan beter), oranje = let op (er klopt iets niet),
       rood = fout (dit gaat mis). Groen blijft van "klaar", de huisstijlkleuren
       zijn van de bediening. Elke signaalkleur heeft een zacht vlak (10%) en een
       rand (28%). Deze waarden zijn de standaard; appKleuren() in api/data.php
       overschrijft ze per omgeving met de kleuren uit Instellingen > Huisstijl. */
    --tip:                 #1d4ed8;
    --tip-zacht:           rgba(29,78,216,.10);
    --tip-rand:            rgba(29,78,216,.28);
    --waarschuwing:        #ea580c;
    --waarschuwing-zacht:  rgba(234,88,12,.10);
    --waarschuwing-rand:   rgba(234,88,12,.28);
    --fout-zacht:          rgba(220,38,38,.08);
    --fout-rand:           rgba(220,38,38,.28);

    --bg-body:        #f8fafc;
    --bg-card:        #ffffff;
    --text-main:      #0f172a;
    --text-muted:     #64748b;
    --text-light:     #94a3b8;
    --border:         #e2e8f0;
    --border-strong:  #cbd5e1;

    /* -- Ruimteschaal (Henri, 17-09-2026) ------------------------------
       Eén plek voor élke afstand: padding, margin, gap en plaatsing. Er stonden
       47 verschillende waarden in de stylesheets (.3 / .35 / .375 / .4 / .45rem
       door elkaar voor dezelfde soort ruimte), waardoor twee blokken naast
       elkaar zichtbaar anders oogden. Alles staat nu op deze stappen; geen
       enkele verschuiving was groter dan 1px.
       De bouwregel: kies de dichtstbijzijnde stap. Zit er echt een gat, voeg
       hier een stap toe; zet nooit een losse waarde in een component. In px,
       zodat de indeling niet meebeweegt met de tekstmaat: tekst groeit, de
       ruimte eromheen blijft rustig. */
    --ruimte-1:        2px;    /* haarfijn: binnenruimte van een pil, icoon tegen tekst */
    --ruimte-2:        4px;
    --ruimte-3:        6px;
    --ruimte-4:        8px;    /* de standaardruimte binnen een blok */
    --ruimte-5:       10px;
    --ruimte-6:       12px;
    --ruimte-7:       16px;    /* tussen blokken */
    --ruimte-8:       20px;
    --ruimte-9:       24px;
    --ruimte-10:      32px;
    --ruimte-11:      48px;    /* paginamarge, lege toestand */

    /* ── Typografie-schaal (Henri, 06-09-2026) ──────────────
       Eén plek voor élke tekstmaat in de app. Er stonden meer dan dertig
       verschillende waarden verspreid over de stylesheets (.66, .68, .7, .72,
       .74, .75, .76, .78, .8, .8125, .82, .83, .85, .875, .88, .9 ...), waardoor
       twee blokken naast elkaar zichtbaar anders oogden. Alles is nu op deze
       stappen gezet; geen enkele verschuiving was groter dan 1px.
       Zet een nieuwe maat NOOIT los in een regel: kies de dichtstbijzijnde stap,
       of voeg hier een stap toe als er echt een gat zit. Een responsieve
       aanpassing hoort ook hier (de variabele omzetten in een media query),
       niet als tweede font-size-declaratie verderop. */
    /* Samengevoegd op 17-10-2026 (Henri): van dertien stappen naar negen. Er stonden drie
       stappen binnen 2px van elkaar (12, 13, 14px) en die deden samen 68% van al het werk in
       de app; niemand ziet dat verschil, maar je moest er elke keer over beslissen. Hetzelfde
       aan de randen: 10 naast 11, en 15 naast 16. Die zijn nu één stap, met de oude naam als
       alias, zodat geen enkele component hoeft te wijzigen en de bedoeling zichtbaar blijft.
       De regel is nu: 11 is een uppercase labeltje, 12 is bijzaak, 13 is alles waarmee je
       werkt en leest, en daarboven begint de koppenreeks.
       Compacter op 21-10-2026 (Henri: "het hele gevoel van alle apps mag wat compacter/kleiner",
       en grootste, normale en kleine tekst in elke app gelijk). De koppenreeks schoof een stap
       omlaag: de grootste tekst in een app is nu 16 (paginatitel, sectiekop, venstertitel), een
       kaarttitel 14. De werkmaat 13 en de kleine maten bleven. */
    --tekst-micro:    0.6875rem;   /* 11px  uppercase kolomlabels, pillen, mini-badges */
    --tekst-klein:    0.75rem;     /* 12px  meta, hints, hulptekst: bijzaak */
    --tekst-ui:       0.8125rem;   /* 13px  alles waar je mee werkt EN leest */
    --tekst-titel:    0.875rem;    /* 14px  kaarttitel, nadruk binnen een kaart */
    --tekst-kop:      1rem;        /* 16px  de grootste tekst in een app: paginatitel, sectiekop, venstertitel */
    --tekst-kop-l:    1.125rem;    /* 18px  h3 */
    --tekst-kop-2xl:  1.25rem;     /* 20px  h2 */
    --tekst-pagina:   1.5rem;      /* 24px  het ene grote cijfer op een dashboardkaart */
    --tekst-hero:     2rem;        /* 32px  alleen de publieke kant */
    /* Oude namen, nu aliassen. Gebruik ze niet in nieuwe code. */
    --tekst-mini:     var(--tekst-micro);
    --tekst-body:     var(--tekst-ui);
    --tekst-groot:    var(--tekst-titel);
    --tekst-kop-xl:   var(--tekst-kop-l);
    /* Invoervelden in een popup staan compacter dan op een pagina: een dialoog is klein
       en de velden staan onder elkaar (Henri, 06-09-2026). Op smalle schermen terug naar
       16px, want alles daaronder laat iOS bij focus inzoomen. */
    /* Invoervelden: een maat voor de hele app, gelijk aan een tabelcel (Henri, 15-09-2026).
       Op smal scherm 16px, anders zoomt iOS bij het focussen. */
    --tekst-invoer:   var(--tekst-ui);
    /* Eén dichtheid voor de hele app (Henri, 17-10-2026). Een popup was tot vandaag compacter
       dan een pagina (veld 38 tegen 44px, marges 12 tegen 16), dus hetzelfde formulier zag er
       anders uit al naar gelang waar het stond. De maat staat nu hier en nergens anders. */
    /* Compacter op 21-10-2026 (Henri): een veld en een knop zijn even hoog, en een tabelrij en
       een menu-item volgen dezelfde maat. Tot die dag was een knop 34, een veld 38 en een
       tabelrij 42px, omdat elk zijn hoogte uit opvulling en regelafstand haalde. */
    --veld-h:         2rem;       /* 32px: invoer, keuzelijst, zoekkiezer, knop, knop in een veldrij */
    --veld-h-klein:   1.625rem;   /* 26px: kleine knop (btn-sm), compacte bediening in een balk */
    --tabel-cel-py:   var(--ruimte-3);   /* boven en onder in een tabelcel: met regelhoogte 1.4 een rij van 32px */
    /* Afstand van een popup tot de bovenkant (DS 1.38.1): vast, zodat de kop niet springt als
       de popup van hoogte wisselt. Ruim op een groot scherm, nooit krapper dan een blokafstand. */
    --modal-boven:    clamp(var(--ruimte-7), 8vh, 88px);
    --veld-h-groot:   4.5rem;     /* 72px: ondergrens van een tekstvak, waar het ook staat */

    /* Ronding: vijf stappen met elk een rol. Nooit een losse waarde. Van zeven naar vijf op
       27-10-2026 (Henri: "minder variaties"): 3 en 5px naast 6px zag niemand, en 24px gebruikte
       alleen de publieke kant van Venturi. */
    --radius-xs:      3px;     /* klein: markering in tekst, swatch, kleine icoonknop */
    --radius-sm:      6px;     /* knop, veld, tabel, kanbankaart, een blok binnen een sectie */
    --radius-md:      8px;     /* sectie (.settings-section), kolom, paneel, menu */
    --radius-lg:      10px;    /* popup, een losse kaart die op zichzelf staat */
    --radius-pil:     999px;   /* een pil: altijd deze, niet 99/999/9999 door elkaar */
    /* Oude namen, nu aliassen. Gebruik ze niet in nieuwe code; de toets Oude tokennamen meldt ze. */
    --radius-2xs:     var(--radius-xs);
    --radius-xl:      var(--radius-lg);

    /* Randdikte: drie stappen. 1px is de haarlijn tussen dingen, 1.5px hoort bij
       iets wat je aanwijst of intypt, 2px is nadruk (actieve tab, ring, sleepvlak). */
    --lijn:           1px;
    --lijn-mid:       1.5px;
    --lijn-dik:       2px;

    --shadow-sm:      0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
    --shadow-md:      0 4px 12px rgba(0,0,0,.08), 0 2px 4px rgba(0,0,0,.04);
    --shadow-lg:      0 8px 24px rgba(0,0,0,.10), 0 4px 8px rgba(0,0,0,.06);
    --shadow-kolom:   6px 0 6px rgba(15,23,42,.07);   /* de vastgezette eerste kolom van een brede tabel (sinds DS 1.42.0) */

    --white:          #ffffff;

    /* Kleuren die geen huisstijl zijn maar wel overal hetzelfde moeten (sinds DS 1.42.0): stonden
       als losse rgba() in de vaste bestanden. Een component noemt nooit zelf een kleur. */
    --overlay:              rgba(15,23,42,.5);      /* achter een popup of zijpaneel */
    --rand-op-kleur:        rgba(15,23,42,.12);     /* rand om een kleurvlakje (swatch) */
    --scroll-duim:          rgba(15,23,42,.16);     /* de duim van een dunne scrollbalk */
    --scroll-duim-hover:    rgba(15,23,42,.32);
    --knop-op-kleur:        rgba(255,255,255,.55);  /* knop in een gekleurde melding */
    --knop-op-kleur-hover:  rgba(255,255,255,.9);
    /* Standaarden van de afgeleide kleuren: appKleurenCss() (php/kern.php) zet ze per omgeving,
       maar een pagina zonder die stijl (inlogpagina, styleguide) heeft ze ook nodig. */
    --on-primary:     #ffffff;
    --on-accent:      #ffffff;
    --sidebar-fel:    #ffffff;
    --appbar-text:    #ffffff;
    --appbar-zacht:   rgba(255,255,255,.85);
    --appbar-actief:  rgba(255,255,255,.12);
    --hover-bg:       #f5f7ff;
    --label-color:    #334155;

    --focus-ring:     0 0 0 3px rgba(99,102,241,.12);
    --primary-a08:    rgba(99,102,241,.08);
    --primary-a12:    rgba(99,102,241,.12);
    --primary-a25:    rgba(99,102,241,.25);

    --badge-success-bg:   #dcfce7;
    --badge-success-text: #166534;
    --badge-success-rand: #a7f3d0;
    --success-zacht:      #f0fdf4;   /* zacht groen vlak: een rij of balk die klaar is */
    --badge-warning-bg:   #fef3c7;
    --badge-warning-text: #92400e;
    --badge-danger-bg:    #fee2e2;
    --badge-danger-text:  #991b1b;
    --badge-info-bg:      #dbeafe;
    --badge-info-text:    #1e40af;
    --badge-info-rand:    #bfdbfe;
    --badge-neutral-bg:   #f1f5f9;
    --badge-neutral-text: #475569;

    --chevron-b:      8px;     /* het pijltje in een kolomkop; de kop rekent ermee in zijn padding */
    /* De verticale as waarop ELK menu-icoon staat, gemeten vanaf de linkerrand van het scherm:
       in het uitgeklapte menu, in de icoonrail en in de app-balk. Stond op drie plekken net
       anders (32, 30 en 33px) en dat zie je meteen (Henri, 17-10-2026). */
    --icoon-as:       32px;
    --icoon-blok:     40px;   /* het aanraakvlak om een menu-icoon: in de rail en in de app-balk */
    --sidebar-w:      240px;
    --sidebar-rail-w:  60px;   /* ingeklapte zijbalk: alleen iconen */
    --devbanner-h:     30px;   /* de gele balk op localhost; de app-balk rekent met --appbar-h */
    /* -- Beweging (Henri, 17-10-2026) ---------------------------------
       Een plek voor elke duur en elke easing, net als bij ruimte en tekst. Er
       stonden tien verschillende duren in de stylesheets (.1 / .12 / .15 / .18 /
       .2 / .25s door elkaar voor dezelfde soort overgang) en overal het kale
       `ease`. Dat begint traag: precies op het moment dat je kijkt gebeurt er
       nog niets, dus de app voelt langzamer dan hij is.
       De regel: kies de stap die bij de afstand hoort. Wat onder je vinger
       reageert is --snel, wat verschijnt of verdwijnt --normaal, een paneel dat
       van de rand komt --rustig. Weggaan mag altijd een stap sneller dan komen:
       daar wacht niemand op. Zet nooit een losse duur of easing in een
       component; past er niets, voeg hier een stap toe. */
    --snel:           120ms;   /* indrukken, hover, een pil die aangaat */
    --normaal:        180ms;   /* popup, uitklapmenu, melding */
    --rustig:         260ms;   /* zijpaneel, de zijbalk in- en uitklappen */
    --ease-uit:       cubic-bezier(.23, 1, .32, 1);     /* komt en gaat: begint vol, voelt meteen */
    --ease-door:      cubic-bezier(.77, 0, .175, 1);    /* beweegt van A naar B op het scherm */
    --ease-paneel:    cubic-bezier(.32, .72, 0, 1);     /* het zijpaneel vanaf de rand */
    --transition:     var(--snel) var(--ease-uit);
    --druk:           scale(.97);   /* indrukstand van een knop, pil of klein vlak */
    --druk-breed:     scale(.99);   /* indrukstand van een brede rij of kaart: dezelfde krimp leest daar als wiebelen */
    --draai:          900ms;        /* een rondje van het wachticoon; sneller leest als sneller laden */
}

/* --- Reset --- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
/* Het hidden-attribuut wint altijd. Zonder deze regel overstemt elke klasse met een display
   (.btn is inline-flex) het verbergen, en blijft een knop met hidden gewoon staan (gevonden in de
   agenda, 17-10-2026: Verwijderen en Afwijzen zichtbaar voor wie ze niet mag gebruiken). */
[hidden] { display: none !important; }
html { scroll-behavior: smooth; }
/* De hoofdpagina scrolt niet zodra de app-balk er staat: het raamwerk is precies zo hoog als het
   venster en alleen .inhoud scrolt. Dit staat ook op html en niet alleen op body, zodat er geen
   scrollbare overloop op de wortel ontstaat die code (een scrollIntoView, een autofocus) alsnog
   kan verschuiven. Alleen op het scherm: bij printen moet de pagina juist doorlopen. */
@media screen {
    html:has(> body.heeft-appbar) { height: 100%; overflow: hidden; }
}
body {
    font-family: 'Outfit', sans-serif;
    background: var(--bg-body);
    color: var(--text-main);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: var(--primary); text-decoration: none; }

/* --- Typografie --- */
/* Regelhoogte hoort bij de tekstmaat (17-10-2026). h3 en h4 erfden de 1,6 van body, een
   leesmaat voor lopende tekst: een kop van 20px werd daardoor 32px hoog voor een regel van
   een woord. Een kop leest op 1,25, lopende tekst op 1,6. */
h1 { font-size: var(--tekst-hero);   font-weight: 700; line-height: 1.2; }
h2 { font-size: var(--tekst-kop-2xl); font-weight: 700; line-height: 1.3; }
h3 { font-size: var(--tekst-kop-l); font-weight: 600; line-height: 1.25; }
h4 { font-size: var(--tekst-titel);   font-weight: 600; line-height: 1.25; }
.card-title, .modal-title, .drawer-title, .page-title { line-height: 1.25; }

/* --- Knoppen --- */
.btn {
    display: inline-flex; align-items: center; gap: var(--ruimte-3);
    min-height: var(--veld-h); padding: 0 var(--ruimte-7); border-radius: var(--radius-sm);
    font-size: var(--tekst-ui); font-weight: 600; font-family: inherit;
    border: none; cursor: pointer;
    transition: background var(--transition), box-shadow var(--transition), transform var(--snel) var(--ease-uit);
    text-decoration: none; white-space: nowrap; line-height: 1.4;
}
/* Indrukken (Henri, 17-10-2026): alles wat je kunt indrukken duwt terug. Zonder dat
   voelt een knop dood, want tussen de klik en wat de server terugstuurt zit niets. De
   stand komt uit --druk, zodat hij overal gelijk is; een uitgeschakelde knop doet niet mee. */
.btn:not(:disabled):active { transform: var(--druk); }
/* --on-primary is afgeleid van de huisstijlkleur (omgevingKleuren() in
   api/data.php): bij een lichte primaire kleur wordt de tekst donker in plaats
   van wit, anders is een knop niet te lezen. */
.btn-primary   { background: var(--primary); color: var(--on-primary, var(--white)); }
.btn-secondary { background: var(--border); color: var(--text-main); }
.btn-danger    { background: var(--danger); color: var(--white); }
.btn-ghost     { background: transparent; color: var(--primary); border: var(--lijn-mid) solid var(--border-strong); }
.btn-sm        { min-height: var(--veld-h-klein); padding: 0 var(--ruimte-6); font-size: var(--tekst-ui); }
.btn-lg        { padding: var(--ruimte-6) var(--ruimte-9); font-size: var(--tekst-titel); }
.btn-icon-only { padding: 0 var(--ruimte-4); background: transparent; color: var(--text-muted); border: none; border-radius: var(--radius-sm); }

@media (hover: hover) {
    .btn-primary:hover   { background: var(--primary-dark); box-shadow: var(--shadow-sm); }
    .btn-secondary:hover { background: var(--border-strong); }
    .btn-danger:hover    { background: var(--danger-dark); }
    .btn-ghost:hover     { background: var(--hover-bg); }
    .btn-icon-only:hover { background: var(--border); color: var(--text-main); }
}

/* --- Formulieren --- */
/* Compacter per 17-10-2026 (Henri): het label stond op regelhoogte 1,6, geërfd van body. Dat
   is een leesmaat voor lopende tekst, niet voor een labeltje van 13px, en het kostte 4,5px per
   veld aan lucht die niemand had gekozen. De marge tussen velden ging van 16 naar 12px, wat tot
   vandaag alleen in een popup gold. Een veldblok is nu 70,9px in plaats van 84,8px. */
.form-group      { margin-bottom: var(--ruimte-6); display: flex; flex-direction: column; }
/* De volgorde binnen een veld ligt vast: label, hint, en dan het veld zelf. ALLES wat geen
   label of hint is staat onder het label, ook een eigen omhulsel om een invoerveld (een div met
   een logo erin, een kiezer, wat dan ook). Hier stond een opsomming van wat order 3 kreeg, en
   een omhulsel dat er niet in stond viel terug op order 0 en sprong daarmee BOVEN het label
   (Henri, 07-10-2026: "opeens staan sommige labels onder het veld"). Een opsomming die je bij
   elke nieuwe wikkel moet bijwerken, is precies het soort afspraak dat stilletjes breekt. */
.form-group > *           { order: 3; }
.form-group > .form-label { order: 1; display: block; font-size: var(--tekst-ui); font-weight: 600; line-height: 1.3; color: var(--label-color); margin-bottom: var(--ruimte-2); }
/* De hint hoort BIJ het veld eronder, dus staat hij er dichter op dan de afstand tussen twee
   velden (12px). Stond hij verder weg, dan leest een veldblok als twee blokken. */
.form-group > .form-hint  { order: 2; display: block; font-size: var(--tekst-klein); color: var(--text-muted); margin: 0 0 var(--ruimte-2); line-height: 1.35; }
/* Standalone hint buiten een form-group: behoudt oude appearance */
.form-label      { display: block; font-size: var(--tekst-ui); font-weight: 600; color: var(--label-color); margin-bottom: var(--ruimte-3); }
.form-hint       { display: block; font-size: var(--tekst-klein); color: var(--text-muted); margin-top: var(--ruimte-2); }
.form-input, .form-select, .form-textarea {
    width: 100%; padding: 0 var(--ruimte-6);
    border: var(--lijn-mid) solid var(--border-strong);
    border-radius: var(--radius-sm);
    height: var(--veld-h); box-sizing: border-box;
    font-size: var(--tekst-invoer); font-family: inherit; color: var(--text-main);
    background: var(--bg-card);
    outline: none; transition: border-color var(--transition), box-shadow var(--transition);
}
.form-textarea { height: auto; min-height: var(--veld-h-groot); padding-top: var(--ruimte-4); padding-bottom: var(--ruimte-4); }
div.form-input { display: flex; align-items: center; }   /* leesveld: tekst verticaal gecentreerd zoals in een echt invoerveld */
.form-select   { padding-top: 0; padding-bottom: 0; }   /* tekst verticaal gecentreerd binnen dezelfde vaste hoogte als .form-input */
@media (max-width: 768px) {
    /* Onder de 16px zoomt iOS in bij het focussen. De velden lezen var(--tekst-invoer), dus
       hier alleen de variabele omzetten; een losse font-size op de elementen hoort niet. */
    :root { --tekst-invoer: 16px; }
}
.form-input:focus, .form-select:focus, .form-textarea:focus {
    border-color: var(--primary);
    box-shadow: var(--focus-ring);
}

/* --- Toetsenbordfocus (WCAG 2.2 AA, 2.4.7 en 2.4.11) ---
   Alles wat je met Tab kunt bereiken krijgt een zichtbare rand zodra je met het
   toetsenbord navigeert. :focus-visible en niet :focus, zodat een muisklik geen
   rand achterlaat. Invoervelden houden hun eigen ring hierboven. */
:is(a, button, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* --- Minder beweging (WCAG 2.3.3) ---
   Wie in het systeem heeft aangegeven beweging te willen beperken, krijgt de app
   zonder overgangen en animaties. De app blijft verder gelijk. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}
.form-textarea   { resize: vertical; }   /* de ondergrens staat hierboven, als token */
.form-row        { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr)); gap: var(--ruimte-7); }   /* stapelt vanzelf als de ruimte op is */
/* In een popup staan invoervelden ALTIJD onder elkaar: een dialoog is smal, en twee
   kolommen maakten van vier korte velden een puzzel (Henri, 06-09-2026). */
.modal .form-row { grid-template-columns: 1fr; row-gap: 0; }
/* row-gap 0 (Henri, 27-10-2026): onder elkaar telt alleen de ondermarge van .form-group. Met de gap
   van de rij erbij stonden velden in een .form-row verder uit elkaar dan losse velden. */
/* Invoerveld en keuzelijst zijn ALTIJD even hoog (Henri, 15-09-2026): een vaste height, nooit
   height: auto, want een <select> en een <input> rekenen hun eigen hoogte anders uit en dan
   staan ze in een form-row scheef naast elkaar. Alleen een tekstvak groeit.
   Hier stonden tot 17-10-2026 vier regels die een popup compacter maakten dan een pagina. Die
   zijn weg: een popup ziet eruit als de rest, de maat komt uit --veld-h. */

/* --- Kaarten --- */
.card {
    background: var(--bg-card);
    border: var(--lijn) solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: var(--ruimte-8);
}
.card-header {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: var(--ruimte-7); padding-bottom: var(--ruimte-7);
    border-bottom: var(--lijn) solid var(--border);
}
.card-title { font-size: var(--tekst-titel); font-weight: 700; }

/* --- Status badges --- */
.badge {
    display: inline-flex; align-items: center; gap: var(--ruimte-2);
    padding: var(--ruimte-2) var(--ruimte-5); border-radius: var(--radius-pil);
    font-size: var(--tekst-klein); font-weight: 600; line-height: 1.4;
    text-transform: uppercase; letter-spacing: .05em;
}
.badge svg { width: 11px; height: 11px; flex-shrink: 0; }
.badge-actief      { background: var(--badge-success-bg); color: var(--badge-success-text); }
.badge-concept     { background: var(--badge-warning-bg); color: var(--badge-warning-text); }
.badge-gearchiveerd { background: var(--badge-neutral-bg); color: var(--text-muted); }
.badge-archief     { background: var(--badge-neutral-bg); color: var(--badge-neutral-text); }
.badge-pending     { background: var(--badge-warning-bg); color: var(--badge-warning-text); }
.badge-approved    { background: var(--badge-success-bg); color: var(--badge-success-text); }
.badge-denied      { background: var(--badge-danger-bg); color: var(--badge-danger-text); }

/* --- Tabellen --- */
.table-wrap        { overflow-x: auto; border-radius: var(--radius-sm); border: var(--lijn) solid var(--border); }
table              { width: 100%; border-collapse: collapse; font-size: var(--tekst-ui); }
thead th {
    padding: var(--ruimte-5) var(--ruimte-7); text-align: left;
    font-size: var(--tekst-klein); font-weight: 700; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: .05em;
    background: var(--bg-body); border-bottom: var(--lijn) solid var(--border);
}
tbody td           { padding: var(--ruimte-5) var(--ruimte-7); border-bottom: var(--lijn) solid var(--border); vertical-align: middle; }
tbody tr:last-child td { border-bottom: none; }
@media (hover: hover) { tbody tr:hover td { background: var(--hover-bg); } }

/* --- Modal overlay --- */
.modal-overlay {
    position: fixed; inset: 0; z-index: 420;
    /* De vraag-, invoer- en keuzepopup (appConfirm, appPrompt, appKeuze) liggen een laag hoger,
       zie verderop: ze worden vanuit een andere popup geopend en moeten daar altijd bovenop komen. */
    background: var(--overlay);
    backdrop-filter: blur(3px);
    /* Op een vaste afstand van de bovenkant, niet verticaal gecentreerd (Henri, 27-10-2026): een
       popup met tabs wisselt van hoogte, en gecentreerd sprong hij dan op en neer. Nu blijft de kop
       staan en beweegt alleen de onderkant. */
    display: flex; align-items: flex-start; justify-content: center;
    padding: var(--ruimte-7);
    padding-top: var(--modal-boven);
    /* Komen en gaan loopt via een overgang, niet via een keyframe (17-10-2026). Een
       keyframe kent alleen de ingang: sluiten was daardoor een harde sprong naar
       display:none. Met `display ... allow-discrete` plus @starting-style blijft het
       venster staan tot de overgang klaar is, en blijft de app zelf hetzelfde doen
       (een klasse `hidden` erop of eraf). Weg is sneller dan kom. */
    transition: opacity var(--normaal) var(--ease-uit),
                display var(--normaal) var(--ease-uit) allow-discrete;
}
.modal-overlay.hidden {
    display: none; opacity: 0; pointer-events: none;
    transition: opacity var(--snel) var(--ease-uit),
                display var(--snel) var(--ease-uit) allow-discrete;
}
@starting-style { .modal-overlay:not(.hidden) { opacity: 0; } }
.modal {
    background: var(--bg-card); border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    width: 100%; max-width: 520px;
    max-height: calc(100vh - var(--modal-boven) - var(--ruimte-7));
    max-height: calc(100dvh - var(--modal-boven) - var(--ruimte-7));
    /* Het venster zelf komt van iets lager: een popup hangt niet aan een knop, dus hij
       schaalt vanuit het midden (anders dan een uitklapmenu). */
    transition: opacity var(--normaal) var(--ease-uit), transform var(--normaal) var(--ease-uit);
    display: flex; flex-direction: column;
}
@starting-style {
    .modal-overlay:not(.hidden) .modal { opacity: 0; transform: translateY(12px) scale(.98); }
}
.modal-overlay.hidden .modal {
    opacity: 0; transform: translateY(6px) scale(.99);
    transition-duration: var(--snel);
}
.modal-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: var(--ruimte-8) var(--ruimte-9) 0;
    margin-bottom: var(--ruimte-6);
    flex-shrink: 0;
}
.modal-title { font-size: var(--tekst-kop); font-weight: 700; }
.modal-body {
    flex: 1; overflow-y: auto; padding: 0 var(--ruimte-9) var(--ruimte-7);
    min-height: 0;
    /* Lucht onder de laatste regel, anders plakt de inhoud tegen de knoppenbalk en lijkt een
       scrollende lijst afgesneden (Henri, 15-09-2026). */
    scroll-padding-bottom: 1rem;
}
.modal-footer {
    display: flex; justify-content: flex-end; gap: var(--ruimte-4);
    padding: var(--ruimte-6) var(--ruimte-9) var(--ruimte-8);
    border-top: var(--lijn) solid var(--border);
    flex-shrink: 0;
}
/* Fallback: als er geen .modal-body is, scroll de hele modal */
.modal:not(:has(.modal-body)) {
    padding: var(--ruimte-9);
    overflow-y: auto;
}
.modal:not(:has(.modal-body)) .modal-header { padding: 0; }
.modal:not(:has(.modal-body)) .modal-footer { padding: 0; padding-top: var(--ruimte-8); margin-top: var(--ruimte-9); }

/* --- Drawer (zijpaneel) --- */
/* Bouwblok naast de modal: schuift rechts in voor uitgebreide bewerk-formulieren
   waarbij de pagina-context zichtbaar blijft. z-index 410: boven de sidebar (300) en de
   app-balk (400), die anders de kop van het paneel afdekte,
   onder de modals (420) zodat een confirm-modal bovenop een open drawer kan. */
.drawer-overlay {
    position: fixed; inset: 0; z-index: 410;
    background: var(--overlay);
    backdrop-filter: blur(3px);
    display: flex; justify-content: flex-end;
    transition: opacity var(--normaal) var(--ease-uit),
                display var(--rustig) var(--ease-paneel) allow-discrete;
}
.drawer-overlay.hidden {
    display: none; opacity: 0; pointer-events: none;
    transition: opacity var(--normaal) var(--ease-uit),
                display var(--normaal) var(--ease-paneel) allow-discrete;
}
@starting-style { .drawer-overlay:not(.hidden) { opacity: 0; } }
.drawer {
    background: var(--bg-card);
    box-shadow: var(--shadow-lg);
    width: 100%; max-width: 480px;
    height: 100%;
    display: flex; flex-direction: column;
    /* Het paneel komt van de rand, dus over zijn eigen breedte (100%), niet met een
       duwtje van 40px. Een percentage klopt bij elke breedte, ook schermbreed op
       mobiel. De curve is die van een paneel: vol beginnen, zacht landen. */
    transition: transform var(--rustig) var(--ease-paneel);
}
@starting-style { .drawer-overlay:not(.hidden) .drawer { transform: translateX(100%); } }
.drawer-overlay.hidden .drawer {
    transform: translateX(100%);
    transition-duration: var(--normaal);
}
.drawer-header {
    display: flex; align-items: center; justify-content: space-between; gap: var(--ruimte-6);
    padding: var(--ruimte-9) var(--ruimte-9);
    border-bottom: var(--lijn) solid var(--border);
    flex-shrink: 0;
}
.drawer-title { font-size: var(--tekst-kop); font-weight: 700; }
.drawer-body { flex: 1; overflow-y: auto; padding: var(--ruimte-9) var(--ruimte-9); min-height: 0; }
.drawer-footer {
    display: flex; justify-content: flex-end; gap: var(--ruimte-4);
    padding: var(--ruimte-8) var(--ruimte-9);
    border-top: var(--lijn) solid var(--border);
    flex-shrink: 0;
}
@media (max-width: 600px) { .drawer { max-width: none; } }

/* --- Animaties ---
   De vensters, het zijpaneel en de meldingen gebruiken sinds 17-10-2026 overgangen in
   plaats van keyframes: een overgang kan halverwege omkeren en kent ook de uitgang.
   Deze twee blijven staan voor een app die iets eenmaligs laat binnenkomen. */
@keyframes fadeIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes slideUp { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }

/* --- Toast notificaties --- */
.toast-container {
    position: fixed; bottom: var(--ruimte-9); right: var(--ruimte-9);
    z-index: 500; display: flex; flex-direction: column; gap: var(--ruimte-4);
}
.toast {
    padding: var(--ruimte-6) var(--ruimte-7);
    border-radius: var(--radius-md);
    font-size: var(--tekst-ui); font-weight: 500;
    box-shadow: var(--shadow-lg);
    min-width: 240px;
    /* Een melding komt van onder en gaat ook weer weg (17-10-2026): zonder uitgang
       sprong de stapel eronder in een keer omhoog, precies op het moment dat je ernaar
       keek. showToast() in js/basis.js zet `is-weg` en ruimt op na de overgang. */
    transition: opacity var(--normaal) var(--ease-uit), transform var(--normaal) var(--ease-uit);
}
@starting-style { .toast { opacity: 0; transform: translateY(8px); } }
.toast.is-weg { opacity: 0; transform: translateY(4px); transition-duration: var(--snel); }
.toast-success { background: var(--badge-success-bg); color: var(--badge-success-text); border: var(--lijn) solid var(--badge-success-rand); }
.toast-error   { background: var(--badge-danger-bg); color: var(--badge-danger-text); border: var(--lijn) solid var(--badge-danger-bg); }
.toast-info    { background: var(--badge-info-bg); color: var(--badge-info-text); border: var(--lijn) solid var(--badge-info-rand); }

/* --- Markdown gerenderde output --- */
.md-output strong { color: var(--primary); font-weight: 600; }
.md-output em     { font-style: italic; }
.md-output a, .md-link {
    color: var(--primary); text-decoration: underline;
    text-underline-offset: 2px;
}

/* --- Dev-banner (localhost waarschuwing) --- */
.dev-banner {
    background: var(--warning);
    color: var(--white);
    text-align: center;
    font-size: var(--tekst-klein);
    font-weight: 700;
    height: var(--devbanner-h);
    box-sizing: border-box;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--ruimte-3);
    text-transform: uppercase;
    letter-spacing: .08em;
    position: sticky;
    top: 0;
    z-index: 9999;
    box-shadow: var(--shadow-sm);
    background-image: repeating-linear-gradient(45deg, transparent, transparent 10px, color-mix(in srgb, var(--text-main) 6%, transparent) 10px, color-mix(in srgb, var(--text-main) 6%, transparent) 20px);
}
@media print { .dev-banner { display: none !important; } }

/* --- Utility --- */
.text-muted    { color: var(--text-muted); }
.text-sm       { font-size: var(--tekst-ui); }
.text-xs       { font-size: var(--tekst-klein); }
.font-bold     { font-weight: 700; }
.mt-1          { margin-top: var(--ruimte-2); }
.mt-2          { margin-top: var(--ruimte-4); }
.mt-4          { margin-top: var(--ruimte-7); }
.mb-4          { margin-bottom: var(--ruimte-7); }
.flex          { display: flex; }
.items-center  { align-items: center; }
.gap-2         { gap: var(--ruimte-4); }
.gap-3         { gap: var(--ruimte-6); }
.justify-between { justify-content: space-between; }
.hidden        { display: none !important; }

/* --- Responsief --- */
@media (max-width: 640px) {
    h1 { font-size: var(--tekst-kop-2xl); }
    h2 { font-size: var(--tekst-kop-l); }
    .modal-header { padding: var(--ruimte-8) var(--ruimte-8) 0; }
    .modal-body { padding: 0 var(--ruimte-8); }
    .modal-footer { padding: var(--ruimte-7) var(--ruimte-8) var(--ruimte-8); }
    .modal:not(:has(.modal-body)) { padding: var(--ruimte-8); }
}

/* Een popup die vanuit een popup wordt geopend (27-09-2026, gezien in Atlas Connect: een
   invoerveld vanuit het berichtscherm bleef erachter). Alle overlays delen laag 420 en dan wint
   wie later in de pagina staat; de drie generieke vraagpopups liggen daarom een laag hoger. */
#confirm-modal, #prompt-modal, #keuze-modal { z-index: 430; }
