/* =====================================================================
   Cursistenportal — design system
   Strak, modern, rustig. Systeem-fonts, veel witruimte, zachte schaduwen.
   ===================================================================== */

:root {
    --bg:            #f4f5f7;
    --surface:       #ffffff;
    --surface-2:     #fafbfc;
    --border:        #e6e8ec;
    --border-strong: #d3d7de;
    --weekend:       #f1f3f6;   /* de weekendkolommen op de tijdlijn */

    --text:          #1a1d23;
    --text-soft:     #5b616e;
    --text-faint:    #8b93a1;

    /* Huisstijl Nedcranes (Kleurgebruik_logo_Nedcranes.pdf): blauw is
       Pantone 300 C, RGB 0/94/184; rood is Warm Red C, RGB 249/66/58.
       Blauw draagt alles wat je aanklikt. Rood is alleen accent, zoals de
       streep onder het logo: wit op dat rood haalt geen 4,5:1, en rood
       lijkt hier al snel op een foutmelding. */
    --brand:         #005eb8;
    --brand-600:     #004c96;
    --brand-50:      #e8f1fa;
    --brand-ink:     #ffffff;
    --accent:        #f9423a;

    --success-bg:    #e7f7ee;  --success-tx: #0f7a43;  --success-bd: #b6e6cb;
    --error-bg:      #fdecec;  --error-tx:   #b3261e;  --error-bd:   #f5c6c3;
    --info-bg:       #e8f1fa;  --info-tx:    #004c96;  --info-bd:    #bcd6ef;
    --warn-bg:       #fff4e0;  --warn-tx:    #8a5200;  --warn-bd:    #f0d5a4;
    /* Roze, alleen voor storingen. Rood is hier al bezet door foutmeldingen
       en oranje door "let op"; een storing hoort geen van beide te zijn maar
       een eigen kleur te hebben die je op het dashboard meteen ziet. */
    --storing-bg:    #fdeaf4;  --storing-tx: #a3155f;  --storing-bd: #f6c6de;

    /* De pictogrammen in een overzicht die om iets vragen: mailen, naar Yuki.
       Een eigen kleur en niet die van een waarschuwing -- er is niets mis, er
       moet alleen nog iets gebeuren, en tussen grijze pictogrammen valt dit
       roze op zonder dat het op een foutmelding lijkt. */
    --attentie:      #d12670;

    --radius:        14px;
    --radius-sm:     10px;
    --shadow-sm:     0 1px 2px rgba(16,24,40,.06), 0 1px 3px rgba(16,24,40,.10);
    --shadow-md:     0 4px 12px rgba(16,24,40,.08), 0 12px 32px rgba(16,24,40,.08);
    --shadow-lg:     0 18px 50px rgba(16,24,40,.16);

    --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
            Arial, "Noto Sans", sans-serif;
    --sidebar-w: 264px;
    --sidebar-w-smal: 76px;
    --impbar-h:  34px;
}

@media (prefers-color-scheme: dark) {
    :root {
        --bg:            #0f1115;
        --surface:       #171a21;
        --surface-2:     #1d212a;
        --border:        #262b36;
        --border-strong: #333a48;
        --weekend:       #1b1f28;
        --text:          #eef0f4;
        --text-soft:     #a7aebc;
        --text-faint:    #737b8a;
        /* Het huisstijlblauw zelf is op een donker vlak te donker (2,7:1). */
        --brand:         #3a86d4;
        --brand-600:     #2e7fd0;
        --brand-50:      #15243a;
        --success-bg:#12291d; --success-tx:#7ee2ac; --success-bd:#1f4a33;
        --error-bg:  #2c1615; --error-tx:  #f5a29c; --error-bd:  #4d2723;
        --info-bg:   #15243a; --info-tx:   #9cc4ef; --info-bd:   #23406a;
        --warn-bg:   #2c2113; --warn-tx:   #f0c98a; --warn-bd:   #4e3a1d;
        --storing-bg:#2d1522; --storing-tx:#f7a8cd; --storing-bd:#4e2138;
        /* Lichter dan overdag: #d12670 op een donker vlak is te weinig verschil
           om een pictogram van 16 pixels mee te laten opvallen. */
        --attentie:  #f2709f;
        --shadow-lg: 0 18px 50px rgba(0,0,0,.55);
    }
}

* { box-sizing: border-box; }

/* De browser verbergt [hidden] met display:none, maar dat is een regel op een
   attribuut: elke klasse hieronder die zelf een display meegeeft (flex, grid)
   wint daarvan en zet het element weer aan. Vandaar hier één regel die vóór
   gaat — anders staat een blok dat als hidden begint meteen in beeld. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
    margin: 0;
    font-family: var(--font);
    color: var(--text);
    background: var(--bg);
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

h1, h2, h3 { margin: 0; line-height: 1.2; letter-spacing: -.01em; }
a { color: inherit; }

/* ---------------------------------------------------------------- Brand */
.brand-mark {
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; border-radius: 11px;
    background: linear-gradient(135deg, var(--brand), var(--brand-600));
    color: var(--brand-ink); font-weight: 700; font-size: .82rem;
    letter-spacing: .02em; box-shadow: var(--shadow-sm);
}
.brand-mark--lg { width: 52px; height: 52px; border-radius: 14px; font-size: 1rem; }

/* ============================================================= AUTH view */
.is-auth {
    display: grid; place-items: center; min-height: 100vh; padding: 24px;
    background:
        radial-gradient(1100px 520px at 15% -10%, var(--brand-50), transparent 60%),
        radial-gradient(900px 500px at 100% 110%, var(--brand-50), transparent 55%),
        var(--bg);
}
.auth-wrap { width: 100%; max-width: 420px; }
.auth-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    padding: 38px 34px 34px;
}
.auth-card__head {
    display: flex; flex-direction: column; align-items: center; gap: 14px;
    margin-bottom: 22px; text-align: center;
}
.auth-card__head h1 { font-size: 1.18rem; font-weight: 650; }
.auth-title { font-size: 1.5rem; font-weight: 680; margin-bottom: 4px; }
.auth-sub { color: var(--text-soft); font-size: .93rem; margin: 0 0 22px; }
.auth-alt { text-align: center; margin: 20px 0 0; font-size: .9rem; }
.auth-foot { text-align: center; color: var(--text-faint); font-size: .8rem; margin: 20px 0 0; }

/* "Ingelogd blijven" staat tussen het wachtwoordveld en de knop. Iets kleiner
   dan een veldlabel: het is een keuze en niet iets dat je moet invullen. */
.auth-onthoud { font-size: .88rem; margin-top: -4px; }

/* ================================================================= Forms */
.form { display: flex; flex-direction: column; gap: 16px; }
.field { display: flex; flex-direction: column; gap: 7px; }
.field__label { font-size: .82rem; font-weight: 600; color: var(--text-soft); }
.field__hint { font-size: .78rem; color: var(--text-faint); line-height: 1.4; }
/* Een hint die ergens op wijst in plaats van iets uitlegt: het bedrag dat
   afwijkt van de factuurregels. */
.field__hint--let-op { color: var(--warn-tx); }

/* Even hoog als .select hieronder: dezelfde tekstgrootte en dezelfde padding.
   Ze staan in hetzelfde formulier onder elkaar, en dan valt een verschil van
   zes pixels op — een keuzelijst is nu eenmaal wat compacter dan een
   invoerveld, dus dat is de maat waar de rest zich naar voegt. Verandert er
   iets aan de een, dan moet het bij de ander mee. */
input[type=email], input[type=password], input[type=text], input[type=tel],
input[type=datetime-local],
/* Een getal en een datum in een formulier horen er hetzelfde uit te zien als
   de velden ernaast. Binnen .form en niet overal, want de filterbalk en de
   balk boven het weekoverzicht hebben hun eigen, compactere datumveld. */
.form input[type=number], .form input[type=date] {
    width: 100%; padding: 10px 14px;
    font: inherit; font-size: .92rem; color: var(--text);
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    transition: border-color .15s, box-shadow .15s, background .15s;
}
/* Een tekstvak hoort er hetzelfde uit te zien als een invoerveld. min-height
   in plaats van een vaste hoogte: het rows-attribuut bepaalt hoe groot het
   opent, en slepen mag. */
.form textarea {
    width: 100%; min-width: 0; box-sizing: border-box;
    padding: 10px 14px;
    font: inherit; font-size: .92rem; color: var(--text);
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    resize: vertical;
    transition: border-color .15s, box-shadow .15s, background .15s;
}
input::placeholder, textarea::placeholder { color: var(--text-faint); }
input:focus, .form textarea:focus {
    outline: none; background: var(--surface);
    border-color: var(--brand);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent);
}

.form__row {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; flex-wrap: wrap; font-size: .88rem;
}
.checkbox { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; color: var(--text-soft); }
.checkbox input { width: 16px; height: 16px; accent-color: var(--brand); }

.link { color: var(--brand); text-decoration: none; font-weight: 550; }
.link:hover { text-decoration: underline; }

/* ============================================================== Buttons */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    padding: 12px 18px; font: inherit; font-weight: 600; cursor: pointer;
    /* Een <a> die als knop is opgemaakt hoort geen onderstreping te hebben. */
    text-decoration: none;
    border: 1px solid transparent; border-radius: var(--radius-sm);
    transition: transform .05s, background .15s, box-shadow .15s, border-color .15s;
}
.btn:active { transform: translateY(1px); }
/* Icoontje in een knop: schaalt mee met de tekst en volgt de tekstkleur. */
.btn__ico { flex: none; width: 1.05em; height: 1.05em; }
.btn--block { width: 100%; }
.btn--primary {
    background: var(--brand); color: var(--brand-ink); box-shadow: var(--shadow-sm);
}
.btn--primary:hover { background: var(--brand-600); }
.btn--ghost {
    background: transparent; color: var(--text-soft); border-color: var(--border-strong);
}
.btn--ghost:hover { background: var(--surface-2); color: var(--text); }

/* ================================================================ Alerts */
.alert {
    padding: 12px 15px; border-radius: var(--radius-sm); font-size: .9rem;
    border: 1px solid transparent; margin-bottom: 16px;
}
.alert--success { background: var(--success-bg); color: var(--success-tx); border-color: var(--success-bd); }
.alert--error   { background: var(--error-bg);   color: var(--error-tx);   border-color: var(--error-bd); }
.alert--info    { background: var(--info-bg);    color: var(--info-tx);    border-color: var(--info-bd); }

/* Zwevende meldingen: over de inhoud heen in plaats van erboven.

   Het weekoverzicht vult de hoogte van het venster, met de legenda tegen de
   onderrand. Een melding erboven duwt dat hele raster omlaag -- je slaat een
   regel op en het scherm waar je naar zat te kijken zakt weg, en na een paar
   seconden weer terug. Hier zweeft ze rechtsonder over het raster heen: de
   pagina eronder verschuift geen millimeter.

   Rechtsonder en niet bovenaan: bovenin zit de balk met de weeknavigatie, en
   daar wordt geklikt. Zie views/layout.php ($meldingZweeft) en
   assets/js/raster.js, dat na het opslaan in deze bak schrijft. */
.meldingen-zweef {
    position: fixed; right: 24px; bottom: 24px; z-index: 65;
    width: min(420px, calc(100vw - 32px));
    display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
    /* De bak zelf vangt geen klikken: hij is bijna altijd leeg en ligt over
       het raster heen. Alleen de meldingen erin doen dat wel. */
    pointer-events: none;
}
.meldingen-zweef .alert {
    margin: 0; pointer-events: auto;
    max-width: 100%;
    box-shadow: 0 10px 30px rgb(15 23 42 / .18);
    animation: melding-op .22s ease-out;
}
/* Een gelukte handeling meldt zichzelf en verdwijnt weer; blijven staan zou
   betekenen dat je hem moet wegklikken, en dat is een handeling erbij voor
   iets wat goed ging. Een foutmelding blijft: die is er nog niet klaar mee. */
.meldingen-zweef .alert--success,
.meldingen-zweef .alert--info {
    animation: melding-op .22s ease-out, melding-weg .45s ease-in 4.5s forwards;
}
@keyframes melding-op {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}
@keyframes melding-weg {
    to { opacity: 0; transform: translateY(8px); visibility: hidden; }
}
@media (max-width: 640px) {
    .meldingen-zweef { right: 12px; left: 12px; bottom: 12px; width: auto; }
}
@media (prefers-reduced-motion: reduce) {
    /* Niet wegschuiven, wel wegvallen: het gaat erom dat de melding niet
       blijft staan, niet om de beweging. */
    .meldingen-zweef .alert { animation: none; }
    .meldingen-zweef .alert--success,
    .meldingen-zweef .alert--info { animation: melding-weg .01s linear 4.5s forwards; }
}

/* ============================================================== APP shell */
.app { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }

.sidebar {
    position: sticky; top: 0; align-self: start; height: 100vh;
    display: flex; flex-direction: column;
    background: var(--surface); border-right: 1px solid var(--border);
    padding: 22px 18px;
}
.sidebar__brand { display: flex; align-items: center; gap: 12px; padding: 4px 6px 22px; }
.brand-name { font-weight: 680; font-size: 1.02rem; letter-spacing: -.01em; line-height: 1.2rem; }

/* Logo en naam zijn samen de weg terug naar het dashboard — zoals een logo
   linksboven op vrijwel elke site. Het is een link, geen knop: er staat een
   echte URL achter, dus middelklikken en "in nieuw tabblad openen" werken. */
.brand-link {
    display: flex; align-items: center; gap: 12px; min-width: 0;
    color: inherit; text-decoration: none; border-radius: var(--radius-sm);
}
.brand-link:hover .brand-name { color: var(--brand); }
.brand-link:focus-visible {
    outline: 2px solid var(--brand); outline-offset: 3px;
}

.sidebar__nav { display: flex; flex-direction: column; gap: 4px; flex: 1; }
.sidebar__nav a {
    display: flex; align-items: center; gap: 12px;
    padding: 11px 13px; border-radius: var(--radius-sm);
    color: var(--text-soft); text-decoration: none; font-weight: 550; font-size: .94rem;
    transition: background .15s, color .15s;
}
.sidebar__nav a:hover { background: var(--surface-2); color: var(--text); }
.sidebar__nav a.active {
    background: var(--brand-50); color: var(--brand);
    box-shadow: inset 3px 0 0 var(--accent);
}
.nav-ico { width: 20px; text-align: center; opacity: .9; }

.sidebar__foot { border-top: 1px solid var(--border); padding-top: 16px; }
.user-chip { display: flex; align-items: center; gap: 11px; margin-bottom: 12px; padding: 0 4px; }
.user-chip__avatar {
    width: 38px; height: 38px; flex: none; border-radius: 50%;
    display: grid; place-items: center; font-weight: 700;
    background: var(--brand-50); color: var(--brand);
}
.user-chip__meta { display: flex; flex-direction: column; overflow: hidden; }
.user-chip__meta strong { font-size: .9rem; }
.user-chip__meta small { color: var(--text-faint); font-size: .78rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ================================================================ Content */
/* min-width: 0 is hier geen detail: als grid-item krijgt .content standaard
   min-width: auto, en dan kan de kolom niet smaller worden dan de breedste
   tabel erin. Een breed cursusoverzicht duwde de hele pagina zo buiten het
   scherm. Met 0 krimpt de kolom netjes mee en scrolt de tabel binnen haar
   eigen .table-wrap. */
.content { padding: 40px clamp(20px, 5vw, 56px); max-width: 1080px; min-width: 0; }
/* Pagina's met een brede tabel laten de leesbreedte los en vullen het scherm.
   Een lopende tekst blijft op 1080px, want regels van een halve meter lezen
   niet; een tabel met negen kolommen wél. */
.content--breed { max-width: none; }

.page-head { margin-bottom: 30px; }
.eyebrow { text-transform: uppercase; letter-spacing: .09em; font-size: .72rem;
    font-weight: 700; color: var(--brand); margin: 0 0 6px; }
.page-head h1 { font-size: 1.8rem; font-weight: 700; }
.page-head__sub { color: var(--text-soft); margin: 6px 0 0; }

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 18px; margin-bottom: 30px; }
.card {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 22px; box-shadow: var(--shadow-sm);
    transition: transform .12s, box-shadow .12s;
}
.card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.card__ico {
    width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center;
    background: var(--brand-50); color: var(--brand); font-size: 1.2rem; margin-bottom: 14px;
}
.card h3 { font-size: 1.05rem; margin-bottom: 6px; }
.card p { color: var(--text-soft); font-size: .92rem; margin: 0 0 14px; }

/* clip en niet hidden: allebei knippen ze de inhoud af bij de afgeronde hoek,
   maar hidden maakt van het paneel een scrollbak, en dan kan er binnenin niets
   meer sticky staan ten opzichte van de pagina. Met clip blijft de zwevende
   tabelkop werken. */
.panel {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: var(--shadow-sm); overflow: clip;
}
.panel__head { padding: 20px 24px; border-bottom: 1px solid var(--border); }
.panel__head h2 { font-size: 1.1rem; }
.panel__body { padding: 22px 24px; }

/* Uitklapbaar paneel: voor een blok dat er wel moet zijn, maar zelden nodig is
   (zoals handmatig een account aanmaken). Dichtgeklapt neemt het één regel in. */
.panel--inklap > summary {
    display: flex; align-items: center; gap: 10px;
    padding: 13px 24px; cursor: pointer; user-select: none;
    list-style: none;
}
.panel--inklap > summary::-webkit-details-marker { display: none; }
.panel--inklap > summary::before {
    content: ''; flex: none; width: 7px; height: 7px; margin-left: 2px;
    border-right: 2px solid var(--text-faint); border-bottom: 2px solid var(--text-faint);
    transform: rotate(-45deg); transition: transform .15s ease;
}
.panel--inklap[open] > summary::before { transform: rotate(45deg); }
.panel--inklap[open] > summary { border-bottom: 1px solid var(--border); }
.panel--inklap > summary:hover { background: var(--surface-2); }
.panel--inklap > summary:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.panel--inklap > summary h2 {
    display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
    font-size: .95rem; margin: 0;
}
.inklap-hint { color: var(--text-faint); font-size: .82rem; font-weight: 400; }

/* Een icoon bij een kop. Loopt mee met de tekstgrootte (em, geen px) en neemt
   de kleur van de kop over; het is een herkenningspunt en geen mededeling, dus
   in de HTML staat er aria-hidden op. align-self omdat de kop van een
   uitklappaneel op de basislijn uitlijnt — een tekening heeft die niet. */
.kop-ico {
    flex: none; align-self: center;
    width: 1.15em; height: 1.15em;
    color: var(--text-faint);
}
.kop-met-ico { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.empty { padding: 44px 24px; text-align: center; color: var(--text-soft); }
.empty p { margin: 0 0 4px; font-weight: 550; }
.empty small { color: var(--text-faint); }

/* ============================================================ Data list */
.data-list { margin: 0; }
.data-list > div {
    display: grid; grid-template-columns: 200px 1fr; gap: 16px;
    padding: 15px 24px; border-bottom: 1px solid var(--border);
}
.data-list > div:last-child { border-bottom: none; }
.data-list dt { color: var(--text-soft); font-size: .9rem; }
.data-list dd { margin: 0; font-weight: 550; overflow-wrap: anywhere; }

.badge {
    display: inline-block; padding: 3px 10px; border-radius: 999px;
    font-size: .78rem; font-weight: 600;
    background: var(--surface-2); color: var(--text-soft); border: 1px solid var(--border);
}
.badge--active   { background: var(--success-bg); color: var(--success-tx); border-color: var(--success-bd); }
.badge--inactive { background: var(--info-bg);    color: var(--info-tx);    border-color: var(--info-bd); }
.badge--locked   { background: var(--error-bg);   color: var(--error-tx);   border-color: var(--error-bd); }

.note { color: var(--text-faint); font-size: .86rem; margin: 18px 2px 0; }

/* ============================================================= Responsive */
@media (max-width: 860px) {
    /* Het menu verlaat hier het raster: het wordt een paneel dat over de
       pagina schuift. Zie "Hoofdmenu op smalle schermen" verderop. Wat
       overblijft zijn twee rijen: de kopbalk en de inhoud.

       Die rijen moeten expliciet: .app is 100vh hoog, en zolang een pagina
       korter is dan het scherm — bijvoorbeeld terwijl de tabel nog geladen
       wordt — verdeelt een grid de ruimte die overblijft over zijn rijen. De
       witte kopbalk werd daardoor tijdelijk een paar honderd pixels hoog. Met
       1fr op de tweede rij slokt de inhoud die ruimte op en houdt de kopbalk
       de hoogte die hij hoort te hebben. */
    .app { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
    .content { padding: 26px 20px; }
    .data-list > div { grid-template-columns: 1fr; gap: 3px; padding: 13px 18px; }
    .data-list dt { font-size: .8rem; }
}

/* ============================================================= Accountbeheer */
.select {
    appearance: none;
    padding: 10px 34px 10px 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface)
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238b93a1' stroke-width='3'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E")
        no-repeat right 12px center;
    color: var(--text);
    font: inherit;
    font-size: .92rem;
    cursor: pointer;
}
.select:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-50); }
/* Een <select> is zo breed als zijn langste optie, en dat kan hier een
   klantnaam van tweeënzeventig tekens zijn ("Stichting Foundation Chinese Yang
   sheng and Traditional Chinese Medicine"). Als flex- of rasteritem krimpt hij
   daar uit zichzelf niet onder — min-width staat op auto en dat is bij een
   keuzelijst de breedte van die langste regel — en dan loopt hij het scherm uit
   in plaats van dat er een schuifbalk komt. Binnen een veld volgt hij dus dat
   veld; de uitgeklapte lijst zelf tekent de browser gewoon op eigen breedte. */
.field .select { width: 100%; min-width: 0; }
.select--sm { padding: 6px 28px 6px 10px; font-size: .84rem; background-position: right 9px center; }

.admin-create {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px;
    align-items: end;
}
.admin-create > button { grid-column: 1 / -1; justify-self: start; }

.btn--sm { padding: 6px 12px; font-size: .82rem; }
.btn--danger {
    background: var(--error-bg); color: var(--error-tx); border: 1px solid var(--error-bd);
}
.btn--danger:hover { background: #f9dbd9; }

.panel__head--split {
    display: flex; align-items: center; justify-content: space-between;
    gap: 16px; flex-wrap: wrap;
}
.search { display: flex; align-items: center; gap: 8px; margin: 0; }
.search input[type=search] {
    width: 240px; max-width: 100%;
    padding: 8px 12px; font: inherit; font-size: .9rem;
    color: var(--text); background: var(--surface);
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
}
.search input[type=search]::placeholder { color: var(--text-faint); }
.search input[type=search]:focus {
    outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-50);
}

.inline-form { display: inline-block; margin: 0; }
.count-pill {
    display: inline-block; margin-left: 8px; padding: 1px 9px; border-radius: 999px;
    font-size: .78rem; font-weight: 600; background: var(--brand-50); color: var(--brand-600);
    vertical-align: middle;
}
.muted { color: var(--text-faint); }

/* position: relative is hier geen opmaak maar een anker. Een absoluut
   gepositioneerd kind zonder gepositioneerde voorouder — zoals de .sr-only in
   de kop van de cursustabel — hangt anders aan het document zelf. Het schuift
   dan niet mee met deze scrollbak en wordt er ook niet door geclipt, maar
   blijft staan op zijn plek ver rechts van het venster, waardoor de hele
   pagina horizontaal scrollbaar wordt. Met relative valt zo'n kind binnen
   deze bak en blijft de overflow waar hij hoort. */
.table-wrap { overflow-x: auto; position: relative; }
.admin-table { width: 100%; border-collapse: collapse; font-size: .92rem; }
.admin-table th, .admin-table td {
    text-align: left; padding: 12px 14px; border-bottom: 1px solid var(--border);
    vertical-align: middle; white-space: nowrap;
}
.admin-table th {
    font-size: .78rem; font-weight: 600; color: var(--text-soft);
    text-transform: uppercase; letter-spacing: .03em;
}
.admin-table tbody tr:last-child td { border-bottom: none; }
.admin-table tr:hover td { background: var(--surface-2); }
/* Geen display:flex op de cel zelf: dan is het geen tabelcel meer, volgt haar
   hoogte de knop in plaats van de rij, en loopt de onderrand niet meer gelijk
   met de andere kolommen. De tussenruimte gaat daarom via een marge. */
.admin-table .col-actions { white-space: normal; }
.admin-table td.col-actions > * { vertical-align: middle; margin: 2px 8px 2px 0; }
.admin-table td.col-actions > *:last-child { margin-right: 0; }

/* "Zelf wachtwoord instellen" — een uitklapper onder de resetknop. Ingeklapt,
   want in verreweg de meeste gevallen druk je gewoon op resetten; een open
   invoerveld op elke rij maakt de kolom onrustig. */
.ww-zelf { display: inline-block; }
.ww-zelf > summary {
    display: inline-block; cursor: pointer; list-style: none;
    font-size: .82rem; color: var(--text-soft);
}
.ww-zelf > summary::-webkit-details-marker { display: none; }
.ww-zelf > summary:hover { color: var(--brand); text-decoration: underline; }
.ww-zelf > summary:focus-visible {
    outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 3px;
}
.ww-zelf__form { display: flex; align-items: center; gap: 6px; margin: 6px 0 0; }
.ww-zelf__form input[type=text] { width: 180px; padding: 7px 10px; font-size: .84rem; }

@media (max-width: 860px) {
    .admin-create { grid-template-columns: 1fr; }
}

/* ---------- Cursisten uit Tribe ---------- */
.tenant-grid {
    display: grid; gap: 10px;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}
.tenant-card {
    display: flex; flex-direction: column; gap: 3px;
    padding: 13px 15px; border: 1px solid var(--border); border-radius: var(--radius-sm);
    background: var(--surface-2); color: var(--text); text-decoration: none;
    transition: border-color .15s ease, background .15s ease;
}
.tenant-card:hover { border-color: var(--border-strong); }
.tenant-card strong { font-size: .94rem; font-weight: 620; }
.tenant-card small { font-size: .78rem; color: var(--text-soft); }
.tenant-card--active {
    border-color: var(--brand); background: var(--brand-50);
    box-shadow: 0 0 0 1px var(--brand);
}
.tenant-card__err { color: var(--error-tx) !important; }

.filterbar {
    display: flex; flex-wrap: wrap; gap: 8px;
    padding: 14px 24px; border-bottom: 1px solid var(--border);
}
.chip {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 5px 12px; border-radius: 999px; font-size: .83rem; font-weight: 550;
    border: 1px solid var(--border); background: var(--surface-2);
    color: var(--text-soft); text-decoration: none;
}
.chip:hover { border-color: var(--border-strong); color: var(--text); }
.chip--active { background: var(--brand-50); border-color: var(--brand); color: var(--brand); }
.chip__n { font-variant-numeric: tabular-nums; opacity: .75; font-size: .78rem; }

.admin-table .col-check { width: 34px; }
.admin-table .col-check input { width: 16px; height: 16px; accent-color: var(--brand); }
.admin-table tr.row--vervallen td { opacity: .55; }

.bulkbar {
    display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
    border-top: 1px solid var(--border);
}
.bulkbar--alt { padding-top: 0; border-top: none; }

.pager {
    display: flex; align-items: center; justify-content: center; gap: 8px;
    padding: 16px 24px; border-top: 1px solid var(--border); font-size: .88rem;
}
.pager__stand { margin: 0 8px; white-space: nowrap; }
/* Een knop die niet verder kan blijft staan, maar dan als vlek: zo springt de
   rij niet heen en weer terwijl je bladert. */
.pager .is-uit { opacity: .38; cursor: default; }

@media (max-width: 560px) {
    /* Vier knoppen mét woorden passen niet naast elkaar op een telefoon; de
       pijlen zeggen genoeg, en het aria-label houdt ze uitspreekbaar. */
    .pager__woord { position: absolute; width: 1px; height: 1px; overflow: hidden;
        clip-path: inset(50%); white-space: nowrap; }
    .pager { gap: 6px; padding: 14px 12px; }
}

.linked-meta { margin: 3px 0 0; font-size: .78rem; color: var(--text-faint); }

@media (max-width: 720px) {
    .filterbar { padding: 12px 18px; }
    .bulkbar { flex-direction: column; align-items: flex-start; }
}

/* ---------- "Inloggen als"-balk (impersonatie) ---------- */
.impbar {
    position: sticky; top: 0; z-index: 60;
    display: flex; align-items: center; gap: 10px;
    height: var(--impbar-h);
    padding: 0 clamp(12px, 3vw, 24px);
    background: var(--warn-bg); color: var(--warn-tx);
    border-bottom: 1px solid var(--warn-bd);
    font-size: .82rem; line-height: 1;
}
.impbar__ico { font-size: .92rem; line-height: 1; flex: none; }
/* Alles op één regel; te lange namen worden afgekapt in plaats van af te breken. */
.impbar__text {
    margin: 0; flex: 1; min-width: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.impbar__text strong { font-weight: 680; }
.impbar__sub { opacity: .75; }
.impbar__form { flex: none; margin: 0; }
.impbar__btn {
    padding: 3px 10px; font-size: .78rem; line-height: 1.4;
    background: var(--warn-tx); color: var(--warn-bg);
    border: 1px solid transparent; font-weight: 600;
}
.impbar__btn:hover { filter: brightness(1.12); }

/* De sidebar blijft onder de balk plakken in plaats van eronder te schuiven. */
body.has-impbar .sidebar { top: var(--impbar-h); height: calc(100vh - var(--impbar-h)); }
body.has-impbar .app { min-height: calc(100vh - var(--impbar-h)); }

@media (max-width: 720px) {
    /* Op smalle schermen valt het e-mailadres weg zodat de balk één regel blijft. */
    .impbar__sub { display: none; }
}

/* ============================== Hoofdmenu op smalle schermen ==============
   Onder 860px past een vaste zijbalk van 264px niet meer naast de inhoud.
   Het menu wordt daar een paneel dat vanaf rechts over de pagina schuift,
   met een waas eroverheen.

   Deze regels staan bewust ná die van de impersonatiebalk: die zet met
   `body.has-impbar .sidebar` de bovenkant en de hoogte van het menu, en dat
   geldt niet meer zodra het menu een zwevend paneel is.

   Het paneel is hier de standaard en niet iets dat JavaScript aanzet. Zou het
   andersom zijn, dan stond het menu bij elke pagina eerst even in het oude
   formaat voordat het script het opvouwt. Voor bezoekers zonder JavaScript
   staat de oude weergave in een <noscript>-blok in layout.php.
   ======================================================================== */

.mobielkop, .menu-waas, .menu-sluit { display: none; }

@media (max-width: 860px) {

    /* ---- De balk met de hamburger ---- */
    .mobielkop {
        display: flex; align-items: center; gap: 12px;
        position: sticky; top: 0; z-index: 40;
        padding: 10px clamp(12px, 4vw, 20px);
        background: var(--surface); border-bottom: 1px solid var(--border);
    }
    /* Onder de impersonatiebalk blijven plakken, niet eronder verdwijnen. */
    body.has-impbar .mobielkop { top: var(--impbar-h); }

    .mobielkop .brand-link { flex: 1; }
    .mobielkop .brand-name {
        min-width: 0;
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }

    .hamburger {
        flex: none; margin-left: auto;
        display: inline-flex; flex-direction: column; justify-content: center; gap: 5px;
        width: 44px; height: 44px; padding: 0 11px;
        background: transparent; color: var(--text);
        border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
        cursor: pointer;
    }
    .hamburger:hover { background: var(--surface-2); }
    .hamburger__streep {
        display: block; width: 100%; height: 2px; border-radius: 2px;
        background: currentColor;
        transition: transform .25s ease, opacity .15s ease;
    }
    /* Open: de buitenste strepen kruisen, de middelste verdwijnt. De strepen
       staan 7px uit elkaar (2px hoog, 5px ertussen), dus zoveel schuiven ze. */
    body.menu-open .hamburger__streep:nth-child(1) { transform: translateY(7px) rotate(45deg); }
    body.menu-open .hamburger__streep:nth-child(2) { opacity: 0; }
    body.menu-open .hamburger__streep:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

    /* ---- De waas over de pagina ---- */
    .menu-waas {
        display: block;
        position: fixed; inset: 0; z-index: 70;
        background: rgba(15, 18, 25, .45);
        opacity: 0; visibility: hidden;
        /* visibility springt pas ná de overgang terug, anders is het paneel al
           weg voordat het uitgeschoven is. */
        transition: opacity .28s ease, visibility 0s .28s;
    }

    /* ---- Het paneel zelf ---- */
    .sidebar,
    body.has-impbar .sidebar {
        position: fixed; top: 0; right: 0; left: auto; z-index: 80;
        width: min(330px, 86vw);
        height: 100vh; height: 100dvh;
        flex-direction: column; align-items: stretch; flex-wrap: nowrap; gap: 0;
        padding: 18px 16px;
        overflow-y: auto; overscroll-behavior: contain;
        border-right: 0; border-left: 1px solid var(--border);
        box-shadow: -18px 0 50px rgba(16, 24, 40, .18);
        transform: translateX(100%);
        visibility: hidden;
        transition: transform .28s cubic-bezier(.22, .61, .36, 1), visibility 0s .28s;
    }

    .sidebar__brand { padding: 4px 6px 20px; }

    .menu-sluit {
        display: inline-flex; align-items: center; justify-content: center;
        flex: none; margin-left: auto;
        width: 36px; height: 36px; font-size: 1.5rem; line-height: 1;
        background: transparent; color: var(--text-soft);
        border: 0; border-radius: var(--radius-sm); cursor: pointer;
    }
    .menu-sluit:hover { background: var(--surface-2); color: var(--text); }

    /* ---- Open ---- */
    body.menu-open .menu-waas {
        opacity: 1; visibility: visible;
        transition: opacity .28s ease, visibility 0s;
    }
    body.menu-open .sidebar,
    body.has-impbar.menu-open .sidebar {
        transform: translateX(0);
        visibility: visible;
        transition: transform .28s cubic-bezier(.22, .61, .36, 1), visibility 0s;
    }
    /* De pagina eronder mag niet meescrollen zolang het paneel open staat. */
    body.menu-open { overflow: hidden; }

    /* De menu-items komen één voor één binnen; net genoeg om het paneel te
       laten voelen als iets dat aankomt in plaats van dat het er ineens is. */
    body.menu-open .sidebar__nav a {
        animation: menu-item .32s both cubic-bezier(.22, .61, .36, 1);
    }
    .sidebar__nav a:nth-child(1) { animation-delay: .04s; }
    .sidebar__nav a:nth-child(2) { animation-delay: .08s; }
    .sidebar__nav a:nth-child(3) { animation-delay: .12s; }
    .sidebar__nav a:nth-child(4) { animation-delay: .16s; }
    .sidebar__nav a:nth-child(5) { animation-delay: .20s; }
    .sidebar__nav a:nth-child(6) { animation-delay: .24s; }
    .sidebar__nav a:nth-child(7) { animation-delay: .28s; }
    .sidebar__nav a:nth-child(8) { animation-delay: .32s; }
}

@keyframes menu-item {
    from { opacity: 0; transform: translateX(14px); }
    to   { opacity: 1; transform: none; }
}

/* Wie beweging heeft uitgezet krijgt hetzelfde menu, maar dan zonder reis. */
@media (prefers-reduced-motion: reduce) {
    .sidebar, .menu-waas, .hamburger__streep { transition-duration: .01ms; }
    body.menu-open .sidebar, body.menu-open .menu-waas { transition-duration: .01ms; }
    body.menu-open .sidebar__nav a { animation: none; }
}

/* Op papier hoort een menu sowieso niet thuis. */
@media print { .mobielkop, .menu-waas { display: none !important; } }

/* ========================= Menu inklappen op brede schermen ===============
   Boven 860px staat het menu vast links en kost het 264px. Wie die ruimte
   liever aan de inhoud geeft — een cursusoverzicht met negen kolommen, een
   presentielijst — klapt het terug tot een smalle strook met alleen de
   pictogrammen. De pagina's die daarop wachten (.content--breed) worden
   daarmee in één klap een stuk breder.

   De klasse staat op <html> en niet op <body>: een kort script in de <head>
   zet hem al vóór de eerste verf, en op dat moment bestaat <body> nog niet.
   De keuze ligt in localStorage, dus hij blijft staan tussen pagina's door.

   Alles hieronder zit in een min-width-vraag. Onder 860px is het menu een
   schuifpaneel dat al dicht is; daar valt niets in te klappen, en de klasse
   uit localStorage mag daar dus niets doen.
   ======================================================================== */

.menu-inklap { display: none; }
/* Uitloggen krijgt alleen een pictogram als het woord wegvalt. */
.uitlog .nav-ico { display: none; }

@media (min-width: 861px) {

    .menu-inklap {
        display: inline-flex; align-items: center; justify-content: center;
        flex: none; margin-left: auto;
        width: 30px; height: 30px; line-height: 1; font-size: .95rem;
        background: transparent; color: var(--text-faint);
        border: 1px solid transparent; border-radius: 9px; cursor: pointer;
        transition: background .15s, color .15s, border-color .15s;
    }
    .menu-inklap:hover {
        background: var(--surface-2); color: var(--text); border-color: var(--border);
    }
    .menu-inklap:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
    .menu-inklap__ico::before { content: '\00AB'; }

    /* De breedte van de kolom is het enige dat echt beweegt; de rest is
       zichtbaar of niet. Browsers die grid-template-columns niet animeren
       springen gewoon, en dat is hier geen verlies. */
    .app { transition: grid-template-columns .22s cubic-bezier(.22, .61, .36, 1); }

    /* ---- Ingeklapt ---- */
    .menu-ingeklapt .app { grid-template-columns: var(--sidebar-w-smal) 1fr; }

    /* In de smalle balk blijft van de lopende timers alleen het aantal over:
       titels zouden er overheen vallen, en het gaat er daar om dát er iets
       loopt. Alleen hier en niet buiten deze mediaquery -- op een telefoon is
       de zijbalk een schuifpaneel, en daar is de ruimte er wél. */
    .menu-ingeklapt .lopend { margin: 0 0 20px; padding: 8px 6px; text-align: center; }
    .menu-ingeklapt .lopend__lijst,
    .menu-ingeklapt .lopend__label { display: none; }
    .menu-ingeklapt .lopend__kop { justify-content: center; margin: 0; }
    .menu-ingeklapt .lopend__aantal { display: inline; font-size: .85rem; }

    .menu-ingeklapt .sidebar {
        padding-left: 14px; padding-right: 14px;
        /* Boven de inhoud, anders schuift een tekstballon eronder. */
        z-index: 5;
    }

    /* Merk en knop passen niet naast elkaar in 76px, dus onder elkaar. */
    .menu-ingeklapt .sidebar__brand {
        flex-direction: column; gap: 10px; padding: 4px 0 18px;
    }
    .menu-ingeklapt .brand-link { justify-content: center; }
    .menu-ingeklapt .menu-inklap { margin-left: 0; }
    .menu-ingeklapt .menu-inklap__ico::before { content: '\00BB'; }

    /* Alle opschriften weg; de pictogrammen blijven staan. De schoolkeuze
       verdwijnt helemaal: een keuzelijst is niet terug te brengen tot een
       pictogram, en wie wil wisselen klapt het menu even open. */
    .menu-ingeklapt .brand-name,
    .menu-ingeklapt .nav-tekst,
    .menu-ingeklapt .user-chip__meta,
    .menu-ingeklapt .sidebar__school { display: none; }

    .menu-ingeklapt .sidebar__nav a { justify-content: center; padding: 11px 0; }
    .menu-ingeklapt .user-chip { justify-content: center; padding: 0; }
    .menu-ingeklapt .uitlog { padding-left: 0; padding-right: 0; }
    .menu-ingeklapt .uitlog .nav-ico { display: block; }

    /* ---- Tekstballon bij de pictogrammen ----
       Zonder opschrift is een pictogram een raadsel. De ballon hangt buiten
       de strook, dus het menu mag hier niets afknippen. */
    .menu-ingeklapt .sidebar__nav a,
    .menu-ingeklapt .uitlog { position: relative; }

    .menu-ingeklapt .sidebar__nav a::after,
    .menu-ingeklapt .uitlog::after {
        content: attr(data-tip);
        position: absolute; left: calc(100% + 16px); top: 50%; z-index: 90;
        transform: translateY(-50%);
        padding: 6px 10px; border-radius: 8px;
        background: var(--text); color: var(--surface);
        font-size: .82rem; font-weight: 550; white-space: nowrap;
        box-shadow: var(--shadow-md); pointer-events: none;
        opacity: 0; visibility: hidden;
        transition: opacity .12s ease, visibility 0s .12s;
    }
    .menu-ingeklapt .sidebar__nav a:hover::after,
    .menu-ingeklapt .sidebar__nav a:focus-visible::after,
    .menu-ingeklapt .uitlog:hover::after,
    .menu-ingeklapt .uitlog:focus-visible::after {
        opacity: 1; visibility: visible;
        transition: opacity .12s ease, visibility 0s;
    }
}

@media (prefers-reduced-motion: reduce) {
    .app { transition: none; }
}

/* ---------- Inschrijvingen, detail en betaling ---------- */
.crumbs {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    margin: 0 0 18px; font-size: .86rem; color: var(--text-faint);
}
.crumbs span[aria-hidden] { opacity: .55; }

.link--strong { font-weight: 600; }

.inschrijvingen .col-jaar { white-space: nowrap; font-variant-numeric: tabular-nums; }
.inschrijvingen .col-geld,
.admin-table th.col-geld { text-align: right; white-space: nowrap;
    font-variant-numeric: tabular-nums; }

.badge--soft { background: var(--surface-2); color: var(--text-soft); }

/* Mijn inschrijvingen op mobiel: de tabel wordt een lijst met kaarten.
   Vijf kolommen passen niet op een telefoon; horizontaal scrollen door een
   tabel is daar onwerkbaar. Elke rij wordt daarom één blok met de cursus als
   kop, jaar en cursist als onderregel en het bedrag rechts daarvan. De hele
   kaart is aantikbaar via de bestaande link (zie .col-cursus .link::after),
   dus de losse "Bekijken"-knop — met een te kleine tap-target — vervalt. */
@media (max-width: 720px) {
    .inschrijvingen,
    .inschrijvingen tbody,
    .inschrijvingen tr,
    .inschrijvingen td { display: block; width: 100%; }

    .inschrijvingen thead { display: none; }

    .table-wrap--kaarten { overflow-x: visible; }

    .inschrijvingen tr {
        position: relative;
        display: flex; flex-wrap: wrap; align-items: baseline;
        gap: 2px 8px;
        padding: 14px 18px;
        border-bottom: 1px solid var(--border);
    }
    .inschrijvingen tbody tr:last-child { border-bottom: none; }
    .inschrijvingen tr:hover td { background: none; }
    .inschrijvingen tr:active { background: var(--surface-2); }

    .inschrijvingen td {
        width: auto; padding: 0; border-bottom: none;
        white-space: normal;
    }

    /* Cursusnaam als kop van de kaart; breekt netjes af bij lange namen. */
    .inschrijvingen .col-cursus {
        flex: 1 1 100%;
        font-size: 1rem; line-height: 1.35;
        overflow-wrap: anywhere;
    }
    .inschrijvingen .col-cursus .badge { margin-left: 6px; vertical-align: 2px; }

    /* Maakt de hele rij aantikbaar zonder extra markup. */
    .inschrijvingen .col-cursus .link::after {
        content: ""; position: absolute; inset: 0;
    }

    /* Onderregel: jaar · cursist links, bedrag rechts. */
    .inschrijvingen .col-jaar,
    .inschrijvingen .col-cursist {
        margin-top: 3px;
        font-size: .86rem; color: var(--text-soft);
    }
    .inschrijvingen .col-cursist::before {
        content: "·"; margin-right: 8px; color: var(--text-faint);
    }
    .inschrijvingen .col-geld {
        margin-top: 3px; margin-left: auto;
        font-size: .95rem; font-weight: 650; color: var(--text);
    }

    .inschrijvingen .col-actions { display: none; }
}

/* Facturen op mobiel: net als bij de inschrijvingen wordt elke rij een kaart.
   Hier blijven de knoppen wél staan — "Betalen" en "PDF openen" zijn echte
   acties — maar ze krijgen een volle regel en een grotere tap-target. De
   ingesprongen betalingsrijen blijven onder hun factuur hangen. */
@media (max-width: 720px) {
    .facturen,
    .facturen tbody,
    .facturen tr,
    .facturen td { display: block; width: 100%; }

    .facturen thead { display: none; }

    .facturen td {
        width: auto; padding: 0; border-bottom: none;
        white-space: normal;
    }
    .facturen tr:hover td { background: none; }

    /* Factuurrij als kaart: nummer + bedrag boven, datum + status eronder,
       daarna de knoppen. */
    .facturen tbody tr:not(.rij--betaling) {
        display: grid;
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "nummer bedrag"
            "datum  status"
            "acties acties";
        gap: 4px 12px;
        padding: 15px 18px;
    }
    .facturen tbody tr:not(.rij--betaling):not(:first-child) {
        border-top: 1px solid var(--border);
    }
    .facturen .col-nummer { grid-area: nummer; overflow-wrap: anywhere; }
    .facturen .col-datum  { grid-area: datum; }
    .facturen .col-status { grid-area: status; justify-self: end; text-align: right; }
    .facturen .col-actions {
        grid-area: acties;
        display: flex; flex-wrap: wrap; gap: 8px;
        margin-top: 8px;
    }
    .facturen tbody tr:not(.rij--betaling) .col-geld {
        grid-area: bedrag; justify-self: end;
        font-weight: 650;
    }

    /* Ruimere knoppen: btn--sm is met de muis prima, met een duim niet. */
    .facturen .col-actions .btn { padding: 9px 15px; font-size: .86rem; }

    /* Betalingen: ingesprongen subregel onder de factuur, zonder scheidingslijn. */
    .facturen .rij--betaling {
        display: flex; flex-wrap: wrap; align-items: baseline;
        gap: 2px 10px;
        padding: 0 18px 13px 30px;
        border-top: none;
    }
    .facturen .rij--betaling .col-betaling { flex: 1 1 100%; }
    .facturen .rij--betaling .col-geld { font-weight: 600; }
    .facturen .rij--betaling .col-status { margin-left: auto; }
    .facturen .rij--betaling .col-actions { display: none; }
    .facturen .rij--betaling + tr td { border-top: none; }
    .facturen .betaling-pijl { width: auto; margin-right: 6px; }
}

/* Heel smal: minder witruimte rondom, zodat de kaarten meer ruimte krijgen. */
@media (max-width: 480px) {
    .content { padding: 22px 14px; }
    .page-head h1 { font-size: 1.45rem; }
    .page-head { margin-bottom: 22px; }
    .panel__head { padding: 16px 18px; }
    .panel__body { padding: 18px; }
    .panel > .onder-actie { padding: 0 18px 18px; }
    .panel > .filterbalk { padding: 18px; }
    .panel > .note { padding: 12px 18px 16px; }
    .empty { padding: 34px 20px; }
    .inschrijvingen tr { padding: 13px 14px; }
    .facturen tbody tr:not(.rij--betaling) { padding: 14px; }
    .facturen .rij--betaling { padding: 0 14px 12px 26px; }
}

.badge--betaald               { background: var(--success-bg); color: var(--success-tx); border-color: var(--success-bd); }
.badge--openstaand            { background: var(--warn-bg);    color: var(--warn-tx);    border-color: var(--warn-bd); }
.badge--nog_niet_gefactureerd { background: var(--info-bg);    color: var(--info-tx);    border-color: var(--info-bd); }
.badge--niet_factureren       { background: var(--surface-2);  color: var(--text-soft); }

.kpi-row { display: flex; flex-wrap: wrap; gap: 14px 40px; }
.kpi { display: flex; flex-direction: column; gap: 5px; }
.kpi__label { font-size: .78rem; text-transform: uppercase; letter-spacing: .07em;
    font-weight: 650; color: var(--text-faint); }
.kpi__value { font-size: 1.6rem; font-weight: 680; letter-spacing: -.02em;
    font-variant-numeric: tabular-nums; }
.kpi__value--sm { font-size: 1rem; font-weight: 500; }

.data-list--money dd { font-variant-numeric: tabular-nums; }
.data-list__total { font-weight: 680; }
.data-list__total dt, .data-list__total dd { border-top: 1px solid var(--border); padding-top: 12px; }

.note--lead { margin-top: 18px; font-size: .9rem; color: var(--text-soft); }
.note--inline { margin: 2px 2px 0; }

/* ---------- Mijn gegevens ---------- */
.form-kop {
    font-size: .78rem; text-transform: uppercase; letter-spacing: .08em;
    font-weight: 700; color: var(--text-faint);
    margin: 26px 0 12px; padding-bottom: 8px; border-bottom: 1px solid var(--border);
}
.profiel-form .form-kop:first-of-type { margin-top: 0; }
.veldgroep { display: grid; gap: 14px; }
.veldgroep--naam    { grid-template-columns: 1.4fr .7fr 1.6fr; }
.veldgroep--adres   { grid-template-columns: 2fr .7fr .7fr .9fr 1.4fr; }
.veldgroep--contact { grid-template-columns: repeat(3, 1fr); }
.veldgroep--bank    { grid-template-columns: minmax(0, 340px) 1fr 1fr; }
.form-acties { margin-top: 24px; }

@media (max-width: 860px) {
    .veldgroep--naam,
    .veldgroep--adres,
    .veldgroep--contact,
    .veldgroep--bank    { grid-template-columns: 1fr 1fr; }
    .kpi-row { gap: 14px 26px; }
}
@media (max-width: 560px) {
    .veldgroep { grid-template-columns: 1fr !important; }
}

/* ---------- Facturen en betalen ---------- */
.panel--actie { border-color: var(--brand); box-shadow: 0 0 0 1px var(--brand); }
.betaal-cta {
    display: flex; align-items: center; justify-content: space-between;
    gap: 18px; flex-wrap: wrap;
}
.betaal-cta h2 { font-size: 1.05rem; }

.facturen .col-geld { text-align: right; }
.facturen .rij--betaling td { padding-top: 4px; padding-bottom: 10px;
    font-size: .85rem; color: var(--text-soft); border-top: none; }
.facturen .rij--betaling + tr td { border-top: 1px solid var(--border); }
.betaling-pijl { display: inline-block; width: 18px; color: var(--text-faint); }

@media (max-width: 620px) {
    .betaal-cta { flex-direction: column; align-items: stretch; }
    .betaal-cta .btn { width: 100%; }
}

/* ---------- Presentielijst ---------- */
.periodebalk {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: 16px; flex-wrap: wrap;
}
.periodes { display: flex; gap: 8px; flex-wrap: wrap; }
.periode-datum { display: flex; align-items: flex-end; gap: 8px; margin: 0; }
.periode-datum .field { min-width: 0; }
.periode-datum input[type=date] {
    padding: 6px 10px; font: inherit; font-size: .85rem;
    color: var(--text); background: var(--surface-2);
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
}

/* Elke les is een uitklapper. Dichtgeklapt zie je waar de les over gaat —
   datum, tijd en de regel eronder — en pas als je hem opent de presentietabel.
   Het pijltje staat in een eigen kolom, zodat de tweede regel netjes onder de
   datum uitkomt en niet onder het pijltje. */
.les > summary {
    display: grid; grid-template-columns: auto 1fr; gap: 3px 12px;
    padding: 18px 24px; cursor: pointer; user-select: none; list-style: none;
}
.les > summary::-webkit-details-marker { display: none; }
.les > summary::before {
    content: ''; grid-row: 1; grid-column: 1;
    width: 7px; height: 7px; margin-top: 7px;
    border-right: 2px solid var(--text-faint); border-bottom: 2px solid var(--text-faint);
    transform: rotate(-45deg); transition: transform .15s ease;
}
.les[open] > summary::before { transform: rotate(45deg); }
.les[open] > summary { border-bottom: 1px solid var(--border); }
.les > summary:hover { background: var(--surface-2); }
.les > summary:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.les > summary > * { grid-column: 2; min-width: 0; }

.les__kop { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
            font-size: 1.1rem; }
.les__datum { font-size: 1.05rem; }
.les__tijd {
    font-size: .9rem; font-weight: 600; color: var(--brand);
    background: var(--brand-50); padding: 2px 8px; border-radius: var(--radius-sm);
}
/* De cursus hoort in de hoofdregel: datum en tijd zeggen wanneer, de cursus
   zegt waarover. Bij een individuele les komt de naam van de cursist erachter,
   want anders heten twee lessen op één ochtend precies hetzelfde. Ze staan in
   een lichter gewicht dan de datum, zodat de regel niet één blok vet wordt. */
.les__cursus, .les__cursist { font-size: 1rem; font-weight: 500; }
.les__cursist { color: var(--text-soft); }
/* De scheidingsstip zit aan de cursist en niet in de tekst, zodat hij vanzelf
   wegvalt als er geen cursusnaam voor staat. */
.les__cursus + .les__cursist::before {
    content: '·'; margin-right: 8px; color: var(--text-faint);
}

/* Lokaal en instrument: kleiner, op de regel eronder. */
.les__meta { margin: 0; font-size: .85rem; color: var(--text-soft); }

.les__form { margin: 0; }
.les__acties {
    display: flex; justify-content: flex-end;
    padding: 14px 24px; border-top: 1px solid var(--border);
}

/* Invoervelden in de tabel: de labels zijn op desktop overbodig (de kolomkop
   zegt het al) en verschijnen pas in de kaartweergave op mobiel. */
.veld-mobiel { display: block; margin: 0; }
.veld-mobiel__label { display: none; }
/* De waarde zoals hij op papier hoort te staan; op het scherm staat op deze
   plek het invoerveld. Zie de afdrukweergave onderaan. */
.op-papier { display: none; }
.presentie .select--sm { width: 100%; min-width: 130px; }
.presentie .col-opmerking input[type=text] {
    width: 100%; padding: 6px 10px; font: inherit; font-size: .85rem;
    color: var(--text); background: var(--surface-2);
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
}
.presentie .col-opmerking input[type=text]:focus { outline: none; background: var(--surface); }

/* De presentielijst is een invulformulier, geen leestabel: hoe meer cursisten
   er tegelijk op het scherm passen, hoe minder je tijdens het aftekenen scrolt.
   De rijen zijn hier daarom strakker dan in de andere tabellen — de velden
   bepalen de hoogte en houden net genoeg lucht om uit elkaar te blijven.
   Alleen de verticale ruimte krimpt; links en rechts blijft het zoals het was.

   Bewust een min-width en niet gewoon deze regels vóór het mobiele blok: onder
   720px is dit geen tabel meer maar een rij kaarten, en daar hoort de ruimte
   juist te blijven — die wordt met een duim ingevuld. */
@media (min-width: 721px) {
    .presentie th { padding-top: 8px; padding-bottom: 8px; }
    .presentie td { padding-top: 5px; padding-bottom: 5px; }
    .presentie .select--sm,
    .presentie .col-opmerking input[type=text] { padding-top: 4px; padding-bottom: 4px; }

    /* E-mail en mobiel staan onder elkaar en zouden anders de hoogste cel van
       de rij worden; met een krappere regelafstand blijven ze binnen de
       rijhoogte die de keuzelijst bepaalt. */
    .presentie .col-contact { line-height: 1.35; }
}

@media (max-width: 560px) {
    .periodebalk { align-items: stretch; }
    .periodes .btn { flex: 1 1 auto; }
}

/* ---------- Presentielijst op een telefoon ----------
   Een presentielijst wordt staand in een lokaal ingevuld, dus daar telt elke
   tik. Onder 720px vervalt de tabelvorm: elke cursist wordt een kaart met de
   naam als kop en velden over de volle breedte, groot genoeg om met een duim
   te raken. De kolomkoppen verdwijnen; hun labels komen bij de velden terug. */
@media (max-width: 720px) {
    .presentie,
    .presentie tbody,
    .presentie tr,
    .presentie td { display: block; width: 100%; }

    .presentie thead { display: none; }

    .presentie tr {
        padding: 14px 0 16px;
        border-bottom: 1px solid var(--border);
    }
    .presentie tr:last-child { border-bottom: 0; }
    /* Zie de opmerking bij .cursussen: nowrap uit, anders lopen lange namen
       en e-mailadressen buiten het scherm. */
    .presentie td { border: 0; padding: 0 18px; white-space: normal; overflow-wrap: anywhere; }

    .presentie .col-naam { margin-bottom: 10px; font-size: 1rem; }
    .presentie .col-presentie,
    .presentie .col-opmerking { margin-bottom: 10px; }

    .veld-mobiel__label {
        display: block; margin-bottom: 4px;
        font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
        font-weight: 700; color: var(--text-faint);
    }
    /* 16px voorkomt dat iOS bij het focussen inzoomt. */
    .presentie .select--sm,
    .presentie .col-opmerking input[type=text] {
        font-size: 16px; padding: 10px 12px;
    }

    .presentie .col-contact { font-size: .85rem; }

    .les__acties { padding: 14px 18px; }
    .les__acties .btn { width: 100%; }
}

/* ---------- Informatiepagina ---------- */
.uitleg { margin: 0 0 12px; color: var(--text-soft); line-height: 1.6; }
.uitleg:last-child { margin-bottom: 0; }
.uitleg strong { color: var(--text); }
.uitleg-lijst {
    margin: 0; padding-left: 20px;
    color: var(--text-soft); line-height: 1.65; font-size: .94rem;
}
.uitleg-lijst li { margin-bottom: 6px; }
.uitleg-lijst li:last-child { margin-bottom: 0; }
.uitleg-lijst strong { color: var(--text); }
.uitleg-lijst + .note { margin-top: 14px; }

/* ---------- Uitvoer: exporteren en printen ---------- */
.kop-acties { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.uitvoer { display: flex; gap: 8px; }
.printkop { display: none; }

@media (max-width: 720px) {
    .kop-acties { width: 100%; }
    .kop-acties .search { flex: 1 1 100%; }
    .kop-acties .search input[type=search] { flex: 1 1 auto; width: auto; min-width: 0; }
    .uitvoer { flex: 1 1 100%; }
    .uitvoer .btn { flex: 1 1 auto; }
}

/* ---------- Afdrukweergave ----------
   Een presentielijst wordt uitgeprint om mee het lokaal in te nemen. Alles wat
   op papier niets doet gaat eruit: menu, filters, knoppen en de kolom met
   contactgegevens. De invoervelden worden platte tekst, want een keuzelijst
   drukt af als een leeg vakje. In plaats daarvan komt er per cursist een
   hokje om met de hand af te vinken. */
@media print {
    .sidebar, .impbar, .periodebalk, .kolomkeuze, .kop-acties, .les__acties,
    .page-head__sub, .eyebrow, .alert, .count-pill,
    /* Zoekveld, periodeknoppen en de knoppen "Exporteren" en "Printen": daar
       valt op papier niets meer mee te doen. */
    .panel--filters { display: none !important; }

    /* De paginakop staat al in .printkop, mét docent, periode en afdrukdatum.
       Twee keer "Presentielijst" onder elkaar is er één te veel, en de <h1>
       is de minst informatieve van de twee. Alleen waar zo'n printkop echt
       volgt: een browser zonder :has() houdt de oude, dubbele kop. */
    .page-head:has(~ .printkop) { display: none; }

    .app { display: block; }
    .content { padding: 0; }
    body { background: #fff; color: #000; }

    .printkop {
        display: block; margin-bottom: 14px;
        font-size: 11pt; border-bottom: 1px solid #000; padding-bottom: 6px;
    }

    .panel {
        border: 0; box-shadow: none; border-radius: 0;
        margin: 0 0 10px !important;
        break-inside: avoid; page-break-inside: avoid;
    }
    .panel__head,
    .les > summary { border-bottom: 1px solid #000; padding: 0 0 2px; }

    /* Op papier hoort een les gewoon open te staan: de lijst wordt uitgeprint
       om mee het lokaal in te nemen. inklap.js klapt ze open vlak voor het
       afdrukken; deze twee regels vangen op waar dat niet lukt (JavaScript uit)
       en halen het pijltje weg, dat op papier niets betekent. */
    .les > summary { display: block; }
    .les > summary::before { display: none; }
    .les:not([open]) > *:not(summary) { display: block; }

    .les__kop { font-size: 12pt; gap: 10px; }
    .les__tijd { background: none; color: #000; padding: 0; }
    /* Op papier moet juist de naam van de cursist opvallen. */
    .les__cursist { color: #000; }
    .les__meta { font-size: 9pt; color: #333; }

    .table-wrap { overflow: visible; }

    /* De planning wordt uitgeprint of meegenomen naar de administratie; op
       papier is de tabelvorm leesbaarder dan de kaarten van de mobiele
       weergave, dus die blijft hier gewoon een tabel. */
    .planning { width: 100%; border-collapse: collapse; font-size: 9.5pt; }
    .planning th, .planning td {
        border-bottom: 1px solid #999; padding: 5px 4px;
    }
    /* De voet van de planning (subtotalen en totaal) staat verderop, onder de
       schermregels: deze @media print komt in dit bestand vóór de sectie
       Planning, en bij gelijke specificiteit wint de regel die later staat. */

    .presentie { width: 100%; border-collapse: collapse; font-size: 10pt; }
    .presentie th, .presentie td {
        border-bottom: 1px solid #999; padding: 3px 4px; text-align: left;
        /* nowrap komt uit .admin-table en hoort hier niet: een lange naam of
           een uitgetypte opmerking moet op papier gewoon afbreken. */
        vertical-align: top; white-space: normal;
    }
    .presentie th { font-size: 7.5pt; }
    .presentie .col-contact { display: none; }
    .presentie th:nth-child(4) { display: none; }

    /* Kolombreedtes: in de opmerking wordt met de hand geschreven, dus die
       krijgt de meeste ruimte. Zonder dit verdeelt de browser de breedte naar
       de inhoud, en dan houdt een lege opmerking niets over. */
    .presentie .col-naam      { width: 34%; }
    .presentie .col-presentie { width: 22%; }
    .presentie .col-opmerking { width: 44%; }

    /* Bij een individuele les staat de naam al in de kop van de les en is er
       één regel. Kolomkoppen erboven zijn dan drie woorden ruis per les — over
       een dag met zeventien lessen scheelt dat een halve pagina. De naamkolom
       gaat om dezelfde reden weg: die zou de naam vijf millimeter lager nog
       een keer herhalen. Wat overblijft is een hokje, de stand, en ruimte om
       te schrijven. */
    .les--individueel .presentie thead { display: none; }
    .les--individueel .presentie .col-naam { display: none; }
    .les--individueel .presentie .col-presentie { width: 30%; }
    .les--individueel .presentie .col-opmerking { width: 70%; }

    /* Formuliervelden zijn op papier niets: een keuzelijst drukt af als een
       leeg vakje of als "— niet ingevuld —", en een invoervak als een grijs
       kadertje. Ze gaan eruit; wat erin stond komt terug als tekst. */
    .presentie .veld-mobiel { display: none; }
    .op-papier { display: inline; }

    /* Een opmerking wordt in het lokaal met de hand bijgeschreven. Er is dus
       schrijfruimte nodig, ook (juist) als er nog niets staat. Geen eigen
       onderlijn: die van de rij zit er al vlak onder, en twee lijntjes van
       drie pixels uit elkaar zien er rommelig uit. */
    .op-papier--lijn { display: block; min-height: 1.5em; }

    /* Ruimte om met de hand af te vinken. */
    .presentie .col-presentie::before {
        content: ''; display: inline-block;
        width: 11px; height: 11px; margin-right: 7px;
        border: 1px solid #000; vertical-align: -1px;
    }

    a[href]::after { content: none !important; }
}

/* ---------- Cursussen ---------- */
.cursussen .col-getal { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.cursussen th.col-getal { text-align: right; }
.cursussen .col-cursus { min-width: 200px; }
/* De cursusnaam is de regel waar je op zoekt in de kolom; de rest eronder is
   toelichting. 650 in plaats van de 700 van <strong>: dat is de dikte die de
   rest van het portaal aanhoudt. */
.cursussen .col-cursus strong { font-weight: 650; }
/* De naam van de cursist onder een individuele les is geen bijschrift maar de
   reden dat je die regel zoekt. Donkerder dan de standaard .linked-meta, die
   elders (accountbeheer) wél een terzijde is — vandaar hier en niet daar. */
.cursussen .linked-meta { color: var(--text); }
.cursussen .col-planning { min-width: 150px; }
.cursussen .totaalrij td {
    border-top: 2px solid var(--border-strong);
    background: var(--surface-2); font-variant-numeric: tabular-nums;
}

@media (max-width: 720px) {
    .cursussen,
    .cursussen tbody,
    .cursussen tfoot,
    .cursussen tr,
    .cursussen td { display: block; width: 100%; }

    .cursussen thead { display: none; }

    .cursussen tbody tr {
        padding: 14px 18px 16px;
        border-bottom: 1px solid var(--border);
    }
    /* De globale tabelregel zet white-space op nowrap. In een kaart mag niets
       meer nowrap zijn: een cursusnaam van negentig tekens duwt de pagina
       anders breder dan het scherm. Alleen de getallen blijven bij elkaar. */
    .cursussen td { border: 0; padding: 0 0 6px; text-align: left !important;
                    white-space: normal; overflow-wrap: anywhere; }
    .cursussen .col-getal { white-space: nowrap; overflow-wrap: normal; }
    .cursussen .col-cursus { padding-bottom: 10px; font-size: 1rem; min-width: 0; }

    /* De labels die op desktop de kolomkop zijn, komen hier bij de waarde. */
    .cursussen .veld-mobiel__label {
        display: inline-block; min-width: 120px; margin: 0;
        font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
        font-weight: 700; color: var(--text-faint);
    }
    .cursussen .col-cursus .veld-mobiel__label { display: none; }
    .cursussen .col-planning small { display: inline; }

    /* De actieknop wordt op een kaart een gewone, volle knop met tekst: een
       los icoontje van 30px is te klein om met een duim te raken en zegt
       zonder kolomkop ook niets meer. */
    .cursussen .col-actions { padding: 10px 0 0; }
    .cursussen .col-actions .btn { width: 100%; padding: 10px 14px; }

    .cursussen .totaalrij td { padding: 3px 18px; }
    .cursussen .totaalrij td:first-child { padding-top: 12px; }
    /* Kolommen die op een kaart niets toevoegen. */
    .cursussen .col-leeg { display: none; }
}

/* Tekst die alleen in de kaartweergave zin heeft. */
.knop-tekst, .alleen-mobiel { display: none; }

@media (max-width: 720px) {
    .knop-tekst { display: inline; }
    .alleen-mobiel { display: inline; font-weight: 400; color: var(--text-soft); }
}

/* ---------- Planning ----------
   Dezelfde opzet als de cursustabel: rechts uitgelijnde getallen op desktop,
   en onder 720px valt de tabel uiteen in kaarten met de kolomkop bij de
   waarde. De eenheid staat in de kolomkop en niet in elke cel, anders wordt
   een kolom van louter "30 min" onnodig breed en rekent hij niet mee. */
.planning .col-getal { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.planning th.col-getal { text-align: right; }
.planning .col-datum { min-width: 150px; }
/* Zoals de cursusnaam op Cursussen: de datum is waar je op zoekt in de rij.
   650 in plaats van de 700 van <strong>, de dikte van de rest van het portaal. */
.planning .col-datum strong { font-weight: 650; }
.planning .col-tijd { white-space: nowrap; font-variant-numeric: tabular-nums; }
.planning .col-cursus { min-width: 200px; white-space: normal; }
.planning .totaalrij td {
    border-top: 1px solid var(--border-strong);
    background: var(--surface-2); font-variant-numeric: tabular-nums;
}
/* De subtotalen per leeftijdscategorie staan boven de totaalregel: dezelfde
   band, maar rustiger, zodat het totaal de regel blijft die opvalt. */
.planning .subtotaalrij td {
    border-top: 1px solid var(--border);
    background: var(--surface-2); color: var(--text-soft);
    font-variant-numeric: tabular-nums;
}
.planning .subtotaalrij__aantal { margin-left: 8px; color: var(--text-faint); font-size: .85em; }
/* De zware lijn hoort tussen de lessen en de voet, en dus op de eerste regel
   daarvan — of dat nu een subtotaal is of meteen het totaal. */
.planning tfoot tr:first-child td { border-top: 2px solid var(--border-strong); }
/* In een kolomkop mag de eenheid rustiger staan dan de kop zelf. */
.planning th .eenheid { font-weight: 400; text-transform: none; letter-spacing: 0; }

/* Weggevinkte kolommen.
   Drie klassen tegen twee (`.planning td` in de kaartweergave), dus deze
   regel wint op specificiteit — ook onder 720px, waar cellen blokken worden.
   Daarom is hier geen !important nodig. */
.planning--zonder-voorbereiding .col-voorbereiding,
.planning--zonder-totaal .col-totaal { display: none; }

/* Op papier geen grijze balken: een vlak van 30% grijs over de hele breedte
   kost toner en leest slechter dan een lijn. Dit blok staat bewust hier en
   niet bij de andere printregels hierboven — het moet ná de schermregels
   komen om ze te kunnen overschrijven. */
@media print {
    .planning tfoot tr:first-child td { border-top: 1px solid #000; }
    .planning .subtotaalrij td { border-top: 1px solid #999; background: none; color: #333; }
    .planning .totaalrij td { border-top: 1px solid #000; background: none; }
}

/* De balk met de vinkjes: een rustige regel onder de periodeknoppen. */
.kolomkeuze {
    display: flex; align-items: center; gap: 8px 18px; flex-wrap: wrap;
    padding-top: 14px; padding-bottom: 14px;
    border-top: 1px solid var(--border);
}
.kolomkeuze__label {
    font-size: .78rem; font-weight: 600; color: var(--text-soft);
    text-transform: uppercase; letter-spacing: .03em;
}
.kolomkeuze__opties { display: flex; gap: 18px; flex-wrap: wrap; }
.kolomkeuze .checkbox { font-size: .88rem; }

@media (max-width: 720px) {
    .planning,
    .planning tbody,
    .planning tfoot,
    .planning tr,
    .planning td { display: block; width: 100%; }

    .planning thead { display: none; }

    .planning tbody tr {
        padding: 14px 18px 16px;
        border-bottom: 1px solid var(--border);
    }
    /* De globale tabelregel zet white-space op nowrap. In een kaart mag niets
       meer nowrap zijn: een cursusnaam van negentig tekens duwt de pagina
       anders breder dan het scherm. Alleen de getallen blijven bij elkaar. */
    .planning td { border: 0; padding: 0 0 6px; text-align: left !important;
                   white-space: normal; overflow-wrap: anywhere; }
    .planning .col-getal { white-space: nowrap; overflow-wrap: normal; }

    /* De datum is de kop van de kaart; de tijd komt er als eerste veld onder. */
    .planning .col-datum { padding-bottom: 4px; font-size: 1rem; min-width: 0; }
    .planning .col-tijd { padding-bottom: 10px; }

    /* De labels die op desktop de kolomkop zijn, komen hier bij de waarde. */
    .planning .veld-mobiel__label {
        display: inline-block; min-width: 120px; margin: 0;
        font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
        font-weight: 700; color: var(--text-faint);
    }

    .planning .totaalrij td,
    .planning .subtotaalrij td { padding: 3px 18px; }
    .planning .totaalrij td:first-child,
    .planning .subtotaalrij td:first-child { padding-top: 12px; }
}

/* ---------- Bijladen van een tabel ---------- */
.laden {
    display: flex; flex-direction: column; align-items: center;
    gap: 10px; padding: 56px 24px; text-align: center;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: var(--shadow-sm);
}
.laden p { margin: 0; font-weight: 550; color: var(--text-soft); }
.laden small { color: var(--text-faint); }
.laden__spin {
    width: 26px; height: 26px; border-radius: 50%;
    border: 3px solid var(--border);
    border-top-color: var(--brand);
    animation: laden-draai .8s linear infinite;
}
@keyframes laden-draai { to { transform: rotate(360deg); } }

/* Wie liever geen beweging ziet, krijgt een rustige puls. */
@media (prefers-reduced-motion: reduce) {
    .laden__spin { animation: laden-puls 1.4s ease-in-out infinite; }
    @keyframes laden-puls { 50% { opacity: .35; } }
}

/* Staat de loader al binnen een paneel -- de planning bij een project, die na
   het openen wordt bijgeladen -- dan heeft hij zijn eigen kader niet nodig;
   anders staat er een lijst in een lijst. */
.panel > [data-notionvlak] > .laden {
    background: none; border: 0; box-shadow: none;
}

/* Een keuzelijst die zijn opties nog moet krijgen: dat mag je zien, en er mag
   nog niet in gekozen worden alsof de lijst compleet is. */
.select[aria-busy="true"] { color: var(--text-faint); font-style: italic; }

/* Tijdens het verversen blijft de oude inhoud staan, maar wat vager, zodat
   de pagina niet opspringt bij het wisselen van een filter. */
.tabel-doel[aria-busy="true"] .panel { opacity: .55; transition: opacity .15s; }

@media print { .laden { display: none; } }

/* ---------- Detailvenster (individuele les) ---------- */
.venster {
    width: min(1180px, calc(100vw - 32px));
    max-height: calc(100vh - 64px);
    padding: 0; border: 1px solid var(--border);
    border-radius: var(--radius); background: var(--surface); color: var(--text);
    box-shadow: 0 18px 50px rgba(0,0,0,.22);
}
.venster::backdrop { background: rgba(15, 18, 25, .45); }
.venster__kop {
    display: flex; align-items: center; justify-content: space-between; gap: 16px;
    padding: 16px 20px; border-bottom: 1px solid var(--border);
    position: sticky; top: 0; background: var(--surface); z-index: 1;
}
.venster__titel { margin: 0; font-size: 1rem; }
.venster__inhoud { padding: 20px; overflow: auto; max-height: calc(100vh - 140px); }

/* De wachtmelding over het venster heen, terwijl een wijziging naar de
   administratie gaat. Het formulier post gewoon door, dus het venster mag niet
   worden leeggemaakt — deze laag gaat er overheen en houdt tegelijk de knoppen
   buiten bereik, zodat niemand er nog een tweede keer op drukt.

   inset werkt tegen het venster zelf: een <dialog> staat van zichzelf al
   absoluut. */
.venster__bezig {
    position: absolute; inset: 0; z-index: 2;
    display: flex; align-items: center; justify-content: center;
    padding: 24px; background: var(--surface);
}
.venster__bezig .laden { border: 0; box-shadow: none; background: none; }

.detail-kolommen { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 28px; }
.detail-blok h3 {
    margin: 0 0 10px; font-size: .78rem; text-transform: uppercase; letter-spacing: .08em;
    color: var(--text-faint);
}

/* De gewone .data-list heeft een labelkolom van 200px plus 24px zijmarge; in
   een venstervak van een kleine 560px gaat daar te veel aan op. Hier dus een
   smaller label, geen zijmarge, en waarden die afbreken in plaats van uit hun
   cel te lopen — een cursusnaam kan lang zijn. */
.detail-blok .data-list > div {
    grid-template-columns: 116px minmax(0, 1fr);
    gap: 12px; padding: 9px 0; align-items: baseline;
}
.detail-blok .data-list dt { font-size: .82rem; }
.detail-blok .data-list dd {
    font-weight: 550; min-width: 0;
    overflow-wrap: anywhere; word-break: break-word;
}
.detail-scroll { max-height: 380px; overflow: auto; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.planningtabel th {
    position: sticky; top: 0; background: var(--surface-2); z-index: 1;
}
.planningtabel td, .planningtabel th { white-space: nowrap; }
.planningtabel th, .planningtabel td { padding: 9px 12px; }

@media (max-width: 720px) {
    /* In een schermvullend venster is 4 kolommen krap; kleiner lettertype en
       minder marge houdt de tabel binnen de breedte in plaats van hem
       horizontaal te laten schuiven. */
    .planningtabel { font-size: .84rem; }
    .planningtabel th, .planningtabel td { padding: 8px 7px; }
    .detail-scroll { max-height: none; }
    .venster__inhoud { padding: 16px; }
    .venster__kop { padding: 13px 16px; }
}

/* Alleen voor wie voorleest: uit beeld, maar wel in de voorleesvolgorde.

   Let op bij een tabel: die wordt nooit smaller dan zijn inhoud, dus deze 1 bij
   1 pixel levert daar een blok van een paar honderd pixels op. Te zien is het
   niet -- clip knipt het weg -- maar het steekt wel buiten het scherm uit, en
   dan kun je de hele pagina naar rechts schuiven. Zet de klasse in dat geval op
   een omhulsel om de tabel heen; zie src/views/urenblok.php. */
.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (max-width: 900px) {
    .detail-kolommen { grid-template-columns: 1fr; gap: 22px; }
}

@media (max-width: 720px) {
    .detail-blok .data-list > div { grid-template-columns: 104px minmax(0, 1fr); gap: 10px; }
    /* dvh houdt rekening met de balken van een mobiele browser; vh doet dat
       niet, waardoor de onderkant van het venster wegvalt. */
    .venster {
        width: 100vw; max-width: 100vw;
        max-height: 100vh; max-height: 100dvh;
        border-radius: 0; border: 0;
    }
    .venster__inhoud {
        max-height: calc(100vh - 62px);
        max-height: calc(100dvh - 62px);
    }
    .cursussen .col-actions { padding-top: 8px; }
    .cursussen .col-actions .btn { width: auto; }
}

@media print { .venster { display: none; } }

/* ---------- Wijzigformulieren in het cursusvenster ---------- */
.wijzigingen { display: grid; gap: 10px; margin-top: 22px; }
.wijzigingen .panel--inklap > summary { padding: 12px 16px; }
.wijzigingen .panel--inklap > summary h2 { font-size: .9rem; }
.wijzigingen .panel__body { padding: 16px; }
.wijzigingen .form { display: grid; gap: 14px; }
.wijzigingen .field__hint { margin-top: 3px; }
.wijzigingen .btn { justify-self: start; }

@media (max-width: 720px) {
    .wijzigingen { margin-top: 18px; }
    .wijzigingen .btn { justify-self: stretch; width: 100%; }
    /* 16px voorkomt inzoomen op iOS bij het aantikken van een veld. */
    .wijzigingen input, .wijzigingen select { font-size: 16px; }
}

/* ---------- Aanstelling ---------- */
.aanstelling__kop { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.aanstelling__jaar {
    font-size: .9rem; font-weight: 700; color: var(--brand);
    background: var(--brand-50); padding: 2px 9px; border-radius: var(--radius-sm);
}
.aanstelling__cijfers {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px;
}
.cijferblok h3 {
    margin: 0 0 8px; font-size: .74rem; text-transform: uppercase; letter-spacing: .08em;
    color: var(--text-faint);
}
.cijferblok .data-list > div {
    grid-template-columns: minmax(0, 1fr) auto; gap: 12px; padding: 7px 0;
}
.cijferblok .data-list dt { font-size: .84rem; }
.cijferblok .data-list dd { font-variant-numeric: tabular-nums; text-align: right; }

.saldo {
    margin: 0; font-size: 2rem; font-weight: 700; line-height: 1.1;
    font-variant-numeric: tabular-nums;
}
.saldo small { font-size: .9rem; font-weight: 550; color: var(--text-faint); margin-left: 6px; }
/* Meer gepland dan de aanstelling toelaat is het geval om op te vallen. */
.saldo--over  { color: var(--error-tx); }
.saldo--onder { color: var(--text); }
.saldo__uitleg { margin: 8px 0 0; font-size: .88rem; color: var(--text-soft); }
.saldo__datum { margin: 10px 0 0; font-size: .78rem; color: var(--text-faint); }

.aanstelling__notities { border: 0; border-top: 1px solid var(--border); border-radius: 0; box-shadow: none; }
/* Geen justify-items:start op het formulier zelf: dan krimpen de velden tot
   hun eigen inhoud en heeft de width:100% van het tekstvak niets om zich aan
   te meten. Alleen de knop hoeft links te blijven staan. */
.notitie-form { display: grid; gap: 10px; margin-bottom: 18px; }
.notitie-form .btn { justify-self: start; }
.notitie-form .field { width: 100%; }
.notitie-form textarea {
    width: 100%; min-width: 0; box-sizing: border-box;
    padding: 10px 12px; font: inherit; font-size: .9rem;
    color: var(--text); background: var(--surface-2);
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm); resize: vertical;
}
.notities { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.notities li {
    padding: 12px 14px; background: var(--surface-2);
    border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.notitie__tekst { font-size: .92rem; line-height: 1.5; }
.notitie__voet {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; margin-top: 8px;
}

@media (max-width: 900px) {
    .aanstelling__cijfers { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 620px) {
    .aanstelling__cijfers { grid-template-columns: 1fr; gap: 18px; }
    .notitie-form .btn { width: 100%; }
    .notitie-form textarea { font-size: 16px; }
}

/* ---------- Accountbeheer op een telefoon ----------
   De beheertabellen hebben vijf of zes kolommen met keuzelijsten en knoppen
   erin; horizontaal door zoiets scrollen is op een telefoon onwerkbaar. Elke
   rij wordt daarom een kaart met de naam als kop en de kolomkop bij de waarde.

   Bewust op .beheer en niet op .admin-table: Cursussen en Planning dragen die
   klasse ook, en die hebben hun eigen kaartweergave. Twee sets regels met
   dezelfde zwaarte zouden elkaar dan overschrijven. */
@media (max-width: 720px) {
    .beheer,
    .beheer tbody,
    .beheer tr,
    .beheer td { display: block; width: 100%; }

    .beheer thead { display: none; }

    .beheer tbody tr {
        position: relative;
        padding: 14px 18px 16px;
        border-bottom: 1px solid var(--border);
    }
    .beheer tbody tr:last-child { border-bottom: none; }
    .beheer tr:hover td { background: none; }

    /* De globale tabelregel zet white-space op nowrap. In een kaart mag niets
       meer nowrap zijn: een e-mailadres van veertig tekens duwt de pagina
       anders breder dan het scherm. */
    .beheer td {
        border: 0; padding: 0 0 6px; text-align: left !important;
        white-space: normal; overflow-wrap: anywhere;
    }
    .beheer .col-hoofd { padding-bottom: 10px; font-size: 1rem; }

    .beheer .veld-mobiel__label {
        display: inline-block; min-width: 118px; margin: 0;
        font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
        font-weight: 700; color: var(--text-faint);
    }

    /* Een keuzelijst naast zo'n label wordt anders een paar tekens breed. */
    .beheer .inline-form { display: block; margin-top: 4px; }
    .beheer .select--sm { width: 100%; }

    /* Het vinkje hoort bij de naam, niet op een eigen regel eronder. Rechts
       bovenin, en groot genoeg om met een duim te raken. */
    .beheer .col-check {
        position: absolute; top: 12px; right: 16px;
        width: auto; padding: 0;
    }
    .beheer .col-check input { width: 22px; height: 22px; }
    /* Met het vinkje rechtsboven mag de naam daar niet onderdoor lopen. */
    .beheer .col-hoofd { padding-right: 40px; }

    /* Knoppen onder elkaar en over de volle breedte: naast elkaar worden het
       raakdoelen van nog geen centimeter. */
    .beheer .col-actions {
        display: flex; flex-direction: column; gap: 8px;
        padding: 10px 0 0;
    }
    .beheer td.col-actions > * { margin: 0; }
    .beheer .col-actions .btn { width: 100%; padding: 10px 14px; }

    /* Het wachtwoordveld naast een knop van een halve regel is niet te typen:
       veld over de volle breedte, knop eronder. */
    .beheer .ww-zelf { display: block; }
    .beheer .ww-zelf__form { flex-wrap: wrap; gap: 8px; }
    .beheer .ww-zelf__form input[type=text] { flex: 1 1 100%; width: auto; }

    /* Onderaan de Tribe-lijst: knop en uitleg onder elkaar. */
    .bulkbar { flex-direction: column; align-items: stretch; gap: 10px; }
    .bulkbar .btn { width: 100%; }
    .pager { flex-wrap: wrap; gap: 10px; }
}

/* ====================== Invoervelden op een telefoon ======================
   iOS Safari zoomt automatisch in zodra je een invoerveld aantikt waarvan de
   letter kleiner is dan 16px — en zoomt daarna niet vanzelf terug, waardoor de
   pagina scheefgetrokken blijft staan. Alles waar je in typt of kiest gaat op
   een smal scherm daarom naar precies 16px.

   De selectors zijn met opzet dezelfde als die van de compacte maten hierboven
   en dit blok staat achteraan: bij gelijke zwaarte wint de laatste regel, dus
   dit hoeft niet met !important. Op desktop blijven de compacte maten staan.
   ======================================================================== */
@media (max-width: 860px) {
    .select,
    .select--sm,
    input[type=email], input[type=password], input[type=text], input[type=tel],
    input[type=datetime-local],
    .form input[type=number], .form input[type=date],
    .form textarea,
    .search input[type=search],
    .periode-datum input[type=date],
    .presentie .col-opmerking input[type=text],
    .ww-zelf__form input[type=text],
    .notitie-form textarea { font-size: 16px; }
}

/* Het codeveld op de tweestapspagina: zes cijfers die je overtikt uit een
   mail. Groot, met ruimte tussen de cijfers, zodat je ziet wat je typt en
   niet halverwege de tel kwijtraakt. */
.codeveld {
    width: 100%; padding: 14px 16px;
    font: inherit; font-size: 1.6rem; font-weight: 650;
    letter-spacing: .38em; text-indent: .38em; text-align: center;
    font-variant-numeric: tabular-nums;
    color: var(--text); background: var(--surface);
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
}
.codeveld::placeholder { color: var(--text-faint); font-weight: 400; }
.codeveld:focus {
    outline: none; border-color: var(--brand);
    box-shadow: 0 0 0 3px var(--brand-50);
}

/* Een adres, een sleutel of een commando om over te nemen. Niet .codeveld:
   dat is het vak voor de zescijferige tweestapscode, en de brede letters die
   daar helpen maken een URL juist onleesbaar. Hier tellen twee andere dingen:
   een vaste breedte per teken, zodat een l en een 1 uit elkaar te houden zijn,
   en netjes afbreken in plaats van de pagina breder maken. */
.kopieerveld {
    display: block; padding: 10px 12px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .82rem; line-height: 1.6;
    color: var(--text); background: var(--bg);
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    overflow-wrap: anywhere; white-space: pre-wrap;
    user-select: all;
}
/* Het logboek: dezelfde opmaak, maar regels breken niet af -- de kolommen
   moeten onder elkaar blijven staan, dus die schuift liever horizontaal. */
.logboek {
    margin: 0; max-height: 22rem; overflow: auto;
    white-space: pre; user-select: text;
}

/* =========================================================================
   Satdesign portal — onderdelen die alleen dit portaal heeft
   ========================================================================= */

/* --- Cijferkaarten op het overzicht ---------------------------------------
   Eén rij met de aantallen die ertoe doen. Alleen "openstaande facturen"
   vraagt om actie, en die is daarom de enige met kleur; zouden ze allemaal
   opvallen, dan valt er niets meer op. */
.kaartrij {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 14px;
    margin-bottom: 22px;
}
.kaart {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 18px 20px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    text-decoration: none;
    color: inherit;
    transition: border-color .15s ease, transform .15s ease;
}
.kaart:hover { border-color: var(--border-strong); transform: translateY(-1px); }
.kaart__getal { font-size: 30px; font-weight: 650; line-height: 1.1; }
.kaart__label { font-size: 13px; color: var(--text-soft); }
.kaart--let-op { border-color: var(--warn-bd); background: var(--warn-bg); }
.kaart--let-op .kaart__getal { color: var(--warn-tx); }
.kaart--let-op .kaart__label { color: var(--warn-tx); }

/* --- Tabellen ------------------------------------------------------------- */
/* Brede tabellen schuiven binnen hun eigen kader; de pagina zelf hoort nooit
   horizontaal mee te schuiven. */
/* De scrollbak om een brede tabel. Dit is tegelijk het tweede ding dat een
   zwevende kop in de weg zit: een bak met overflow is de scrollbak waar sticky
   zich naar richt, en die scrollt hier niet verticaal — dus zou de kop nergens
   aan blijven hangen.

   Vandaar dat tabel-mobiel.js de bak opheft zodra de tabel er gewoon in past;
   dan is de pagina weer de scrollbak en werkt de kop. Past hij niet, dan blijft
   horizontaal schuiven belangrijker dan een zwevende kop. Zonder JavaScript
   verandert er niets: dan is het gewoon een scrollbak, zoals hiervoor. */
.tabel-scroll { overflow-x: auto; }
.tabel-scroll--past { overflow: visible; }
.admin-table td.num, .admin-table th.num { text-align: right; }
.admin-table td.nowrap, .admin-table th.nowrap { white-space: nowrap; }

/* De twee knoppen onder het openstaande bedrag. Ze staan als één regel in de
   lijst met cijfers eronder, dus zonder de kolommen van die lijst. */
.maandstand__acties {
    display: flex; flex-wrap: wrap; gap: 8px;
    padding-top: 4px;
}
.maandstand__acties .btn svg { width: 1em; height: 1em; margin-right: 6px; }

/* Eén factuur in het voorbeeld van de facturenrun: een kop met de klant, het
   bedrag en het vinkje, en de regels eronder. Ze staan als losse panelen onder
   elkaar en niet in een tabel -- een factuur heeft meer dan één regel, en dat
   past niet in een rij. */
.runfactuur { margin-bottom: 12px; }
.runfactuur .panel__body { padding: 14px 20px; }
.runfactuur__kop {
    display: flex; align-items: baseline; gap: 12px; cursor: pointer;
}
.runfactuur__kop input { width: 16px; height: 16px; accent-color: var(--brand); }
.runfactuur__naam { flex: 1; min-width: 0; font-weight: 600; }
.runfactuur__bedrag {
    flex: none; text-align: right; font-weight: 650; font-variant-numeric: tabular-nums;
}
.runfactuur__bedrag small { display: block; font-size: .72rem; font-weight: 400; color: var(--text-faint); }
.runfactuur__regels {
    list-style: none; margin: 8px 0 0 28px; padding: 0;
    font-size: .84rem; color: var(--text-soft);
}
.runfactuur__regels li {
    display: flex; gap: 12px; padding: 2px 0;
}
.runfactuur__regels li span:first-child { flex: 1; min-width: 0; }
.runfactuur__prijs { flex: none; font-variant-numeric: tabular-nums; }

/* De aanvinkkolom bij de bankimport: smal, en het vinkje in het midden. */
.admin-table th.kol-vink, .admin-table td.kol-vink { width: 44px; text-align: center; }
.kol-vink input { width: 16px; height: 16px; accent-color: var(--brand); }
/* Een bedrag dat niet klopt met de factuur; de regel is dan ook niet
   aangevinkt. */
.verschil {
    display: block; font-size: .7rem; font-weight: 600; color: var(--warn-tx);
    text-transform: uppercase; letter-spacing: .04em;
}

/* Een vinkje in het overzicht dat meteen naar de database gaat. Het woord
   ernaast blijft staan: op een kaartje op de telefoon staat het vinkje los van
   zijn kolomkop, en dan zegt "ja" meer dan een vakje. */
.snelvink { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; }
.snelvink input { width: 16px; height: 16px; accent-color: var(--brand); cursor: pointer; }
.snelvink input:disabled { cursor: default; }
.snelvink__tekst { font-size: .82rem; color: var(--text-soft); }
.snelvink__tekst--fout { color: var(--error-tx); }

/* De regels van een factuur in het overzicht: kleiner en grijzer dan de rest,
   want het is een kijkje in de factuur en geen gegeven waarop je zoekt. Eén
   regel, afgekapt waar hij niet past; de hele zin staat in de tooltip. */
.admin-table td.kol-regels { max-width: 22rem; }
.admin-table td.kol-regels span {
    display: block;
    font-size: .82rem; color: var(--text-soft); line-height: 1.35;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.subtekst {
    display: block;
    font-size: 12px;
    line-height: 17px;
    color: var(--text-soft);
    margin-top: 2px;
}
/* Achter de waarde in plaats van eronder — voor een korte toevoeging als
   "per jaar, excl. btw", waar een eigen regel per tabelrij zonde is. */
.subtekst--achter { display: inline; margin-top: 0; }
.rij--open td { background: color-mix(in srgb, var(--warn-bg) 45%, transparent); }

/* --- Statuslabels --------------------------------------------------------- */
.status {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 550;
    white-space: nowrap;
    background: var(--info-bg);
    color: var(--info-tx);
    border: 1px solid var(--info-bd);
}
.status--ok   { background: var(--success-bg); color: var(--success-tx); border-color: var(--success-bd); }
.status--open { background: var(--warn-bg);    color: var(--warn-tx);    border-color: var(--warn-bd); }

/* --- Filterbalk boven een overzicht --------------------------------------- */
.filterbalk {
    display: flex;
    align-items: flex-end;
    /* Zonder wrap kan de regel hieronder — het zoekvak over de volle breedte
       op een telefoon — niet uitpakken: de knoppen blijven dan achter het vak
       op dezelfde regel staan en de laatste valt buiten het scherm. */
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 16px;
}
/* Basis 220px en mag krimpen, maar niet groeien: het zoekvak houdt zijn maat
   op een breed scherm (de knoppen worden door margin-left:auto naar rechts
   geduwd) en wordt smaller in plaats van de balk uit te rekken zodra er geen
   220px meer is. min-width: 0 hoort daarbij — een flexitem weigert anders
   onder de breedte van zijn inhoud te zakken. */
.filterbalk .field { margin: 0; flex: 0 1 220px; min-width: 0; }
/* Het zoekvak even hoog als de knop ernaast: een invoerveld is standaard op
   een formulier gemaakt, waar het ruim mag staan, maar hier staat het op één
   regel naast .btn--sm en stak het daar een kop bovenuit. Dezelfde
   tekstgrootte én dezelfde padding als die knop, zodat de twee gelijk blijven
   als er aan een van beide wordt gedraaid. */
.filterbalk input[type=text] { padding: 6px 12px; font-size: .82rem; }
/* --- De extra filters --------------------------------------------------------
   Vijf keuzelijsten passen niet naast het zoekvak, dus ze krijgen een eigen
   regel onder de balk: flex-basis 100% breekt de regel af en vult hem.

   Het uitklappen gaat met een vinkje en een label en niet met een knop en een
   script: zo werkt het ook zonder JavaScript, en de stand hoeft nergens
   bewaard te worden. Het vinkje zelf staat op sr-only in plaats van hidden —
   het moet met de tab-toets te bereiken en met de spatiebalk te bedienen zijn,
   en dat kan een verborgen veld niet. */
/* Ingeklapt op elk formaat: je opent deze pagina meestal om de lijst te lezen,
   niet om te filteren, en vijf keuzelijsten die er altijd staan duwen de tabel
   naar beneden. Staat er een filter aan, dan zet de view het vinkje op checked
   — anders zou je een gefilterde lijst zien zonder de knoppen die dat deden. */
.filterbalk__extra {
    flex: 0 0 100%; min-width: 0;
    display: none; flex-wrap: wrap; align-items: flex-end; gap: 12px;
}
.filterbalk__schakelaar:checked ~ .filterbalk__extra { display: flex; }
.filterbalk__extra .field { flex: 0 1 190px; min-width: 0; }
/* Zelfde maat als .btn--sm en het zoekvak ernaast; de ruimte rechts blijft voor
   het pijltje, dat als achtergrond op 12px van de rand staat.

   Op de hele balk en niet alleen op het uitklapblok: het logboek van de
   planning heeft zijn keuzelijsten naast het zoekvak staan in plaats van
   eronder, en daar stak een keuzelijst met de maat van een formulierveld een
   kop boven de rest uit. */
.filterbalk .select { padding: 6px 30px 6px 12px; font-size: .82rem; }

/* De twee datumvelden van een eigen periode. Smaller dan een keuzelijst, want
   er staat maar een datum in en anders vullen ze samen de hele regel. */
.filterbalk__extra .field--datum { flex: 0 1 145px; }
/* Dezelfde padding en tekstgrootte als de keuzelijst ernaast. Het pijltje van
   de select vraagt rechts 30px, de kalenderknop hier niet — die staat binnen de
   gewone padding. */
.filterbalk__extra input[type=date],
.beeldbalk__veld input[type=date] {
    width: 100%; padding: 6px 12px;
    font: inherit; font-size: .82rem; color: var(--text);
    /* Wit, net als de keuzelijst ernaast, en niet het --surface-2 van een
       invoerveld op een formulier: hier staan de twee naast elkaar en dan valt
       dat kleurverschil op. */
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    /* Zonder color-scheme tekent de browser het kalendertje en het
       uitklapvenster altijd licht, en dan verdwijnt dat icoontje op een donker
       veld. Zie input[type=datetime-local] achteraan deze stylesheet, waar het
       om dezelfde reden staat. */
    color-scheme: light dark;
    /* Cijfers van gelijke breedte, zodat de datum niet verspringt terwijl je
       hem met de pijltjes bijstelt. */
    font-variant-numeric: tabular-nums;
    transition: border-color .15s, box-shadow .15s, background .15s;
}
.filterbalk__extra input[type=date]::-webkit-calendar-picker-indicator,
.beeldbalk__veld input[type=date]::-webkit-calendar-picker-indicator {
    cursor: pointer; opacity: .55; margin-left: 6px;
}
.filterbalk__extra input[type=date]::-webkit-calendar-picker-indicator:hover,
.beeldbalk__veld input[type=date]::-webkit-calendar-picker-indicator:hover { opacity: 1; }
/* Even hoog als de keuzelijst ernaast, en dat gaat niet vanzelf: een datumveld
   tekent zijn inhoud in een eigen binnenveld (::-webkit-datetime-edit) met
   padding van zichzelf, en de kalenderknop ernaast heeft die ook. Dezelfde
   tekstgrootte en dezelfde padding leverden zo toch een veld op dat een paar
   pixels boven de lijst ernaast uitstak.

   Die padding gaat er dus af, en daarna staat de hoogte van allebei op dezelfde
   som: de regelhoogte van de tekst (1.55, die van body) plus de 6px padding en
   de rand van 1px aan weerskanten. In em en niet in pixels, want op een smal
   scherm gaat de letter naar 16px en dan horen ze samen mee te groeien. */
.filterbalk__extra .select,
.filterbalk__extra input[type=date],
.beeldbalk__veld input[type=date] {
    line-height: 1.55;
    height: calc(1.55em + 14px);
}
.filterbalk__extra input[type=date]::-webkit-datetime-edit,
.beeldbalk__veld input[type=date]::-webkit-datetime-edit { padding: 0; }
/* Op de iPhone was dat nog niet genoeg: Safari op iOS tekent een datumveld als
   eigen bedieningselement (appearance: auto) en zet de datum in een binnenveld
   (::-webkit-date-and-time-value) met een eigen marge. Daarmee werden
   "... vanaf" en "... t/m" hoger dan de keuzelijsten eromheen, ondanks de
   hoogte hierboven. Zonder die eigen weergave geldt de hoogte gewoon; tikken
   opent nog steeds de datumkiezer van iOS. Die binnenmarge gaat er ook af, en
   de datum links, zoals de tekst in de keuzelijst ernaast. Chrome en Safari op
   de Mac tekenen het veld hiermee hetzelfde als eerst. */
.filterbalk__extra input[type=date],
.beeldbalk__veld input[type=date] {
    -webkit-appearance: none;
    appearance: none;
    min-height: 0;
}
.filterbalk__extra input[type=date]::-webkit-date-and-time-value,
.beeldbalk__veld input[type=date]::-webkit-date-and-time-value {
    margin: 0;
    text-align: left;
}

.filterbalk__meer {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 6px 2px; cursor: pointer; user-select: none;
    font-size: .84rem; font-weight: 600; color: var(--text-soft);
    white-space: nowrap;
}
.filterbalk__meer:hover { color: var(--text); }
/* Het driehoekje draait mee met de stand, zoals bij .panel--inklap. */
.filterbalk__meer-ico {
    width: 7px; height: 7px; flex: none;
    border-right: 2px solid var(--text-faint);
    border-bottom: 2px solid var(--text-faint);
    transform: rotate(45deg); transition: transform .15s ease;
}
.filterbalk__schakelaar:checked ~ .filterbalk__meer .filterbalk__meer-ico {
    transform: rotate(-135deg);
}
/* De omlijning hoort om het label heen te vallen en niet om het vinkje, dat
   daar onzichtbaar staat. */
.filterbalk__schakelaar:focus-visible ~ .filterbalk__meer {
    outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 4px;
}

/* Boven 640px hoort de Nieuw-knop rechtsboven, op dezelfde regel als het
   zoekvak. In de bron staat hij ná het uitklapblok — dat is de volgorde op een
   telefoon — dus schuift dat blok hier een plaats op. De knop is daarmee weer
   het laatste item van de eerste regel en zijn margin-left:auto duwt hem naar
   rechts; het blok valt met flex-basis 100% op de regel eronder. */
@media (min-width: 641px) {
    .filterbalk__extra { order: 1; }
}

@media (max-width: 640px) {
    /* 16px voorkomt dat iOS bij het focussen inzoomt: onder die maat schuift
       Safari de hele pagina groter zodra je in een veld tikt, en daar komt hij
       niet vanzelf van terug. Geldt net zo goed voor de keuzelijsten in het
       uitklapblok. */
    .filterbalk input[type=text] { font-size: 16px; }
    .filterbalk__extra .select { font-size: 16px; }
    .filterbalk__extra input[type=date],
    .beeldbalk__veld input[type=date],
    .beeldbalk__veld input[type=number] { font-size: 16px; }
    /* Het zoekvak wordt van die grotere letter ruim vier pixels hoger, en de
       knoppen ernaast staken daar dan onderuit — zie de regel bij
       .filterbalk input[type=text] hierboven, waar die twee op elkaar gezet
       zijn. Meer padding in plaats van een grotere letter, want het opschrift
       hoort niet mee te groeien; als aanraakvlak is het bovendien winst. */
    .filterbalk .btn--sm { padding: 8px 12px; }

    /* Een eigen regel, want naast het zoekvak past er niets meer bij. */
    .filterbalk__meer { flex: 0 0 100%; padding: 8px 0; }
    /* Onder elkaar en over de volle breedte: naast elkaar wordt een keuzelijst
       met vierhonderd klanten erin te smal om te lezen. */
    .filterbalk__extra .field { flex: 1 1 100%; }
    /* Hieronder staat op een telefoon de knop naar een nieuw formulier, en die
       hoort niet als zesde regel van de filterlijst te lezen. Bovenop de 12px
       tussenruimte van de balk zelf, dus een kleine 24px in totaal. Alleen als
       het blok openstaat: ingeklapt staat het op display:none en telt de marge
       niet mee, zodat er dan geen gat onder "Meer filters" valt. */
    .filterbalk__extra { margin-bottom: 11px; }
}

/* Staat de filterbalk bovenin een paneel, dan heeft hij de zijmarge van dat
   paneel nodig: .panel brengt zijn padding niet zelf mee, die zit in
   .panel__head en .panel__body. Zonder dit plakken het zoekvak en de
   Nieuw-knop tegen de rand van het paneel. */
/* De lijn eronder scheidt het zoeken af van de tabel, net als .panel__head bij
   een gewoon paneel; de marge vervalt dan, anders zweeft de kop los. */
.panel > .filterbalk {
    padding: 20px 24px;
    margin-bottom: 0;
    border-bottom: 1px solid var(--border);
}

/* --- Totaalbedrag onder het onderhoud -------------------------------------
   Het bedrag groot, de opbouw eronder klein: de meeste mensen willen alleen
   het bedrag, en wie het niet vertrouwt leest de regel eronder. */
.totaalblok {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px 24px;
    padding: 16px 20px;
    border-top: 1px solid var(--border);
    background: var(--surface-2);
}
.totaalblok__label { display: block; font-size: 12px; color: var(--text-soft); }
.totaalblok__waarde { font-size: 24px; font-weight: 650; }
.totaalblok__sub { font-size: 12px; color: var(--text-soft); margin-left: 4px; }
.totaalblok .note { margin: 0; max-width: 62ch; }

/* --- Acties --------------------------------------------------------------- */
.onder-actie { margin: 18px 0 0; }
/* Staat de knop binnen een paneel in plaats van eronder op de pagina, dan
   heeft hij de zijmarge van het paneel nodig: dat vlak brengt zijn padding
   niet zelf mee, die zit in .panel__head en .panel__body. Zonder dit plakt de
   knop tegen de rand — en tegen de onderrand, want .onder-actie heeft alleen
   een marge aan de bovenkant. Hetzelfde geldt voor een kleine toelichting
   direct onder een tabel. */
.panel > .onder-actie { padding: 0 24px 20px; }
.panel > .note {
    margin: 0; padding: 14px 24px 18px;
    border-top: 1px solid var(--border);
}
.form__acties { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }

/* Een link naar buiten: de naam zelf blijft leesbaar als tekst, het pijltje
   erachter zegt dat je de site verlaat. Het icoon schaalt mee met de tekst en
   is aria-hidden — de sr-only ernaast zegt het met woorden. */
.extern { color: inherit; text-decoration: none; }
.extern:hover { text-decoration: underline; }
.extern__ico {
    width: .8em; height: .8em;
    margin-left: 5px; vertical-align: baseline;
    color: var(--text-faint);
}

/* Een knop die eruitziet als een link, voor een handeling binnen een zin. */
.knop-als-link {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    color: var(--brand);
    text-decoration: underline;
    cursor: pointer;
}
.knop-als-link:hover { color: var(--brand-600); }
.domein-uitslag { display: block; margin-top: 4px; }

/* --- Termijnkeuze bij "incasso activeren" ---------------------------------
   Zit in een <details> zodat de keuze pas verschijnt als iemand hem nodig
   heeft; anders staat er in elke factuurregel een keuzelijst die vrijwel
   nooit gebruikt wordt. */
.incasso-keuze { display: inline-block; text-align: left; }
.incasso-keuze > summary { list-style: none; cursor: pointer; }
.incasso-keuze > summary::-webkit-details-marker { display: none; }
.incasso-keuze__form {
    position: absolute;
    z-index: 20;
    right: 16px;
    margin-top: 8px;
    padding: 16px;
    width: 280px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    text-align: left;
}
.incasso-keuze__form .note { font-size: 12px; line-height: 17px; }

@media (max-width: 680px) {
    .totaalblok { flex-direction: column; align-items: flex-start; }
}

/* --- Logo in plaats van merkletters --------------------------------------
   Het Satdesign-blokje heeft zijn eigen kleur en vorm, dus de gekleurde
   achtergrond en de schaduw eromheen kunnen weg: die vochten met het logo. */
.brand-mark:has(img) {
    background: none;
    box-shadow: none;
    border-radius: 0;
}
.brand-mark img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/* --- Klantkoppelingen in het accountbeheer -------------------------------
   Staan onder het e-mailadres in dezelfde cel: een account heeft er meestal
   maar één, en je wilt hem zien naast het adres waar hij bij hoort. */
.koppelingen { margin-top: 6px; }
.koppel-lijst { list-style: none; margin: 2px 0 6px; padding: 0; }
.koppel-lijst li {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 13px;
    line-height: 19px;
}
.koppel-form { display: flex; gap: 6px; align-items: center; margin-top: 4px; }
.koppel-form .select { max-width: 240px; }

/* Een tekstknop die niet met de echte acties concurreert. */
.knop-als-link--stil {
    color: var(--text-faint);
    font-size: 12px;
    text-decoration: underline;
}
.knop-als-link--stil:hover { color: var(--error-tx); }

/* --- Blokken naast elkaar op het overzicht -------------------------------
   .detail-blok komt uit het docentenportaal, waar het altijd binnen een
   lichtbak stond die zijn eigen witte vlak meebracht. Op de overzichtspagina
   staat het los op de paginakleur, dus krijgt het hier hetzelfde vlak als een
   .panel — anders zweeft de tabel op de grijze achtergrond. */
.detail-blok {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    min-width: 0;
}
.detail-blok > .panel__head { padding: 16px 20px 12px; }
/* Deze kop is geen flexrij zoals .panel__head--split: de knop komt onder de
   titel terecht, en plakte daar zonder marge tegenaan. */
.detail-blok > .panel__head .btn { margin-top: 10px; }
.detail-blok > .empty { padding: 20px; }
.detail-blok > .note { margin: 0; padding: 12px 20px 16px; }

/* --- Lange teksten in overzichtstabellen ---------------------------------
   .admin-table zet alle cellen op nowrap; dat werkt voor het accountbeheer,
   waar elke cel kort is. In de overzichten staat er een omschrijving in
   ("Online overleg met Tim over de nieuwe formulieren…") en die liep dan door
   de kolom ernaast heen. Hier mogen cellen dus afbreken; datums en bedragen
   houden hun nowrap via .nowrap en .num. */
.detail-blok .admin-table th,
.detail-blok .admin-table td,
.tabel-scroll .admin-table th,
.tabel-scroll .admin-table td {
    white-space: normal;
    overflow-wrap: anywhere;
}
.detail-blok .admin-table td.num,
.detail-blok .admin-table th.num,
.detail-blok .admin-table td.nowrap,
.tabel-scroll .admin-table td.num,
.tabel-scroll .admin-table th.num,
.tabel-scroll .admin-table td.nowrap,
.tabel-scroll .admin-table th.nowrap {
    white-space: nowrap;
    overflow-wrap: normal;
}
.detail-blok .admin-table th,
.detail-blok .admin-table td { padding: 10px 20px; }

/* --- Stappenuitleg vóór een handeling met gevolgen ------------------------
   Staat op de incassopagina boven het formulier: er wordt een échte betaling
   gedaan, en dat hoort niet pas bij Mollie duidelijk te worden. */
.stappen {
    margin: 0 0 20px;
    padding-left: 20px;
    color: var(--text-soft);
    font-size: .92rem;
    line-height: 1.55;
}
.stappen li { margin-bottom: 6px; }
.stappen li::marker { color: var(--brand); font-weight: 600; }

/* --- VPS Servers ----------------------------------------------------------
   Een rij knoppen om tussen servers te wisselen: verbruik en firewall kosten
   per server twee aanroepen bij TransIP, dus er staat er telkens één open. */
.serverkiezer { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.serverkiezer__knop {
    display: inline-block;
    padding: 7px 14px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface);
    color: var(--text-soft);
    font-size: .88rem;
    text-decoration: none;
    white-space: nowrap;
}
.serverkiezer__knop:hover { border-color: var(--border-strong); color: var(--text); }
.serverkiezer__knop.is-actief {
    background: var(--brand); border-color: var(--brand); color: var(--brand-ink);
}

.vps-kolommen {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 28px;
}
.blokkop {
    margin: 0 0 6px; font-size: .78rem; text-transform: uppercase;
    letter-spacing: .08em; color: var(--text-faint);
}

/* Het CPU-getal krijgt kleur zodra het de moeite waard is om naar te kijken.
   Onder de 70% blijft het gewone tekst: een groen vinkje bij elk normaal
   getal maakt de pagina druk zonder iets toe te voegen. */
.meter { font-weight: 600; }
.meter--druk { color: var(--warn-tx); }
.meter--max { color: var(--error-tx); }

.ip-lijst { display: flex; flex-direction: column; gap: 8px; }
.ip-veld { font-variant-numeric: tabular-nums; }

/* --- Firewallregel openklappen -------------------------------------------
   Elke poort heeft een eigen IP-lijst. Die staan ingeklapt, want een server
   heeft er al gauw achttien en dan is de pagina één muur van invoervelden. */
.fw-regel { display: inline-block; text-align: left; }
.fw-regel > summary { list-style: none; cursor: pointer; }
.fw-regel > summary::-webkit-details-marker { display: none; }
.fw-regel__paneel {
    position: absolute;
    z-index: 20;
    right: 16px;
    margin-top: 8px;
    padding: 16px;
    width: 320px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    text-align: left;
}
.fw-regel__kop { margin: 0 0 10px; }
.fw-regel__paneel .note { font-size: 12px; line-height: 17px; }

/* "Pas toe op alle IP-lijsten van deze server". Een streep erboven, want dit
   hoort niet bij de lijst erboven maar bij het opslaan eronder — het is een
   keuze over wát er opgeslagen wordt, geen extra veld. */
.fw-overal {
    /* display:flex en niet de inline-flex van .checkbox: een inline element
       staat op een regel, en die regel houdt onder het label ruimte vrij voor
       staartletters. Dat is precies de lege strook die de uitleg eronder te
       ver naar beneden duwde. */
    display: flex;
    margin-top: 12px; padding-top: 12px;
    border-top: 1px solid var(--border);
    align-items: flex-start; font-size: 13px; color: var(--text);
}
.fw-overal input { margin-top: 1px; flex: none; }
/* De waarschuwing hoort onder het vinkje en niet ernaast: hij noemt de poorten
   die overschreven worden, en dat is een zin en geen bijschrift. Uitgelijnd met
   de tekst van het vinkje, zodat het één blokje leest. */
.fw-overal__uitleg { margin: 4px 0 0 24px; }

@media (max-width: 680px) {
}

/* --- VPS-firewall op een telefoon ----------------------------------------
   Vijf kolommen naast elkaar levert onder de 720px kolommen van een paar
   tekens breed op, en dan past er geen IP-adres meer in. Elke regel wordt
   daar een kaartje met de poort als kop; dezelfde aanpak als het
   accountbeheer. */
@media (max-width: 720px) {
    .firewall, .firewall thead, .firewall tbody, .firewall tr, .firewall td {
        display: block; width: auto;
    }
    /* De koprij is overbodig zodra elk veld zijn eigen label heeft. */
    .firewall thead { display: none; }

    .firewall tbody tr {
        position: relative;
        padding: 14px 18px 16px;
        border-bottom: 1px solid var(--border);
    }
    .firewall tbody tr:last-child { border-bottom: none; }
    .firewall tr:hover td { background: none; }

    /* De globale tabelregel zet white-space op nowrap. In een kaart mag niets
       meer nowrap zijn: een poortbereik als 49152-65535 duwt de pagina anders
       breder dan het scherm. */
    .firewall td {
        border: 0; padding: 0 0 6px; text-align: left !important;
        white-space: normal; overflow-wrap: anywhere;
    }

    .firewall .veld-mobiel__label {
        display: inline-block; min-width: 96px; margin: 0;
        font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
        font-weight: 700; color: var(--text-faint);
    }

    /* Poort en omschrijving vormen samen de kop van de kaart: daaraan herken je
       de regel. Het label "Poort" blijft staan als gewone tekst en niet als
       CSS-inhoud, want gegenereerde tekst leest een schermlezer niet voor. De
       omschrijving spreekt voor zich en heeft geen label nodig. */
    .firewall .col-waarvoor .veld-mobiel__label { display: none; }
    .firewall .col-poort { font-size: 1.05rem; padding-bottom: 0; }
    .firewall .col-poort .veld-mobiel__label {
        min-width: 0; margin-right: 6px; font-size: .78rem; letter-spacing: .04em;
    }
    .firewall .col-waarvoor { color: var(--text-soft); padding-bottom: 10px; }

    /* De knop over de volle breedte: naast de tekst wordt het een raakdoel van
       nog geen centimeter. */
    .firewall .col-actie { padding-top: 10px; }
    .firewall .fw-regel { display: block; }
    .firewall .fw-regel > summary { display: block; text-align: center; }
    .firewall .col-actie .subtekst { text-align: left; }

    .firewall .ip-veld { width: 100%; }
    .firewall .form__acties { flex-direction: column; align-items: stretch; }
}

/* --- Uitklappanelen in een tabelcel --------------------------------------
   .admin-table zet white-space op nowrap en dat erft door naar alles wat in
   een cel staat — ook naar een uitklappaneel met lopende tekst erin. Die liep
   daardoor als één regel de viewport uit, op elk schermformaat. Vandaar dat
   deze panelen hun eigen witruimtegedrag terugzetten.

   De actiekolom van de firewall heette eerst .num; dat is opgeruimd, want het
   staat er wel rechts maar er staat geen getal in. */
.col-actie { text-align: right; }
.fw-regel__paneel,
.incasso-keuze__form {
    white-space: normal;
    overflow-wrap: anywhere;
}
.fw-regel__paneel .note,
.fw-regel__paneel .uitleg,
.incasso-keuze__form .note {
    white-space: normal;
}

/* --- Sluitknop op een zwevend uitklappaneel -------------------------------
   Het paneel dekt de knop af waarmee je het opende, dus het heeft een eigen
   uitgang nodig. Op een telefoon staat het paneel in de kaart zelf en is de
   knop weer gewoon zichtbaar; daar mag hij weg. */
.paneel-sluit {
    position: absolute;
    top: 6px;
    right: 8px;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--text-faint);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
}
.paneel-sluit:hover { background: var(--surface-2); color: var(--text); }
.paneel-sluit:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* Ruimte vrijhouden zodat de kop niet onder het kruisje doorloopt. */
.fw-regel__paneel .fw-regel__kop,
.incasso-keuze__form > .field:first-of-type { padding-right: 26px; }

@media (max-width: 720px) {
    .fw-regel__paneel .fw-regel__kop,
    .incasso-keuze__form > .field:first-of-type { padding-right: 0; }
}

/* --- IP-adres met omschrijving -------------------------------------------
   TransIP bewaart alleen het adres; wat het betekent staat in onze eigen
   database. Ze staan naast elkaar zodat je in één blik ziet welk adres van
   kantoor is en welke van een klant. Het paneel is daarvoor breder dan het
   was — met alleen adressen paste 320px, met uitleg erbij niet meer. */
.ip-rij { display: flex; gap: 8px; }
/* Een IPv6-adres met prefix is zo'n veertig tekens; daar is dit op gemaakt.
   De omschrijving krijgt de rest, want die is meestal kort ("kantoor"). */
.ip-rij .ip-veld { flex: 0 0 52%; min-width: 0; }
.ip-rij .ip-omschrijving { flex: 1 1 auto; min-width: 0; }
.ip-omschrijving { font-size: .88rem; }

@media (max-width: 720px) {
    /* Naast elkaar worden het op een telefoon twee velden van een paar tekens.
       Onder elkaar, met een streepje links zodat je ziet dat ze bij elkaar
       horen. */
    .ip-rij {
        flex-direction: column;
        gap: 4px;
        padding-left: 10px;
        border-left: 2px solid var(--border);
    }
    .ip-rij .ip-veld { flex: none; }
    .ip-lijst { gap: 12px; }
}

/* =========================================================================
   Uitklappanelen als dialoogvenster
   =========================================================================
   De IP-lijst per firewallregel en de termijnkeuze bij een factuur stonden
   eerst als zwevend blokje naast de knop. Bij twintig adressen met een
   omschrijving ernaast werd dat te groot om nog ergens naast te passen, en het
   dekte de tabel af waar je net naar keek.

   Nu komen ze in het midden van het scherm, met de pagina erachter gedimd. Dat
   is meteen duidelijker: je bent met één regel bezig, niet met de tabel.

   Sluiten blijft een bewuste handeling — het kruisje of Escape. Een klik op de
   donkere achtergrond doet niets, want in het paneel staat een half ingevulde
   lijst die je niet per ongeluk wilt weggooien. */
.fw-regel[open] > .fw-regel__paneel,
.incasso-keuze[open] > .incasso-keuze__form {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 60;

    width: min(560px, calc(100vw - 32px));
    max-height: calc(100vh - 64px);
    max-height: calc(100dvh - 64px);
    overflow-y: auto;

    margin: 0;
    padding: 22px;
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
}

/* De dimlaag. Als ::before op het <details> zelf, zodat er geen extra element
   in elke tabelrij hoeft te staan. */
.fw-regel[open]::before,
.incasso-keuze[open]::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 55;
    background: rgba(16, 24, 40, .55);
}

/* Zolang er een venster openstaat schuift de pagina erachter niet mee. */
html.paneel-open, html.paneel-open body { overflow: hidden; }

.fw-regel__paneel .fw-regel__kop { margin-top: 0; }
.fw-regel__paneel .ip-lijst { max-height: none; }

/* Bij een lange lijst wil je de knoppen kunnen bereiken zonder eerst helemaal
   naar beneden te scrollen; ze plakken daarom onderaan het venster. */
.fw-regel__paneel .form__acties,
.incasso-keuze__form .form__acties {
    position: sticky;
    bottom: -22px;
    margin-bottom: -22px;
    padding: 12px 0;
    background: var(--surface);
    border-top: 1px solid var(--border);
}

@media (max-width: 720px) {
    .fw-regel[open] > .fw-regel__paneel,
    .incasso-keuze[open] > .incasso-keuze__form {
        width: calc(100vw - 20px);
        padding: 18px;
    }
}

/* Ruimer venster nu er per regel twee velden in staan: een IP-adres en een
   omschrijving naast elkaar hebben allebei genoeg breedte nodig om er iets van
   te kunnen lezen. Boven de 680px wordt het weer een lange leesregel, dus daar
   stopt het. */
.fw-regel[open] > .fw-regel__paneel {
    width: min(680px, calc(100vw - 32px));
}

/* --- Live-aanduiding bij het verbruik -------------------------------------
   Klein stipje naast de kop, zodat je ziet dát de cijfers meelopen. Grijs als
   de verbinding wegvalt; dan staat er iets verouderds op het scherm en dat
   hoort zichtbaar te zijn. */
.live {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-left: 8px;
    font-size: .68rem;
    text-transform: none;
    letter-spacing: 0;
    font-weight: 500;
    color: var(--text-faint);
}
.live::before {
    content: '';
    width: 6px; height: 6px;
    border-radius: 50%;
    background: currentColor;
}
.live--aan { color: var(--success-tx); }
.live--aan::before { animation: live-puls 2s ease-in-out infinite; }
.live--uit { color: var(--text-faint); }

@keyframes live-puls {
    0%, 100% { opacity: 1; }
    50%      { opacity: .25; }
}

/* Wie bewegende beelden liever vermijdt krijgt een stilstaand stipje; de kleur
   vertelt het verhaal ook. */
@media (prefers-reduced-motion: reduce) {
    .live--aan::before { animation: none; }
}

/* ============================================================== Projecten
   De projectgegevens en de planning komen uit Notion. De statuskleuren zijn
   die van Notion zelf: dan ziet de klant dezelfde kleur als wij, ook als er
   later een status bijkomt. Eén variabele per kleur, gebruikt door zowel het
   statuslabel als de stip op de tijdlijn. */
/* Een paneel dat onder een ander paneel staat: anders plakken de twee tabellen
   tegen elkaar en lijkt het één blok. */
.panel--volgend { margin-top: 24px; }
/* Ruimer, voor een paneel dat niet bij het vorige hoort maar er iets anders
   begint: het formulier staat bij een bestaand project onder de panelen waar je
   het project op bekijkt, en die twee horen niet tegen elkaar aan te liggen. */
.panel--verderop { margin-top: 48px; }

/* De terugknop staat boven de inhoud in plaats van eronder, dus de marge zit
   aan de onderkant. */
.terug-actie { margin: 0 0 20px; }

/* Het filter op de planning. Staat binnen het paneel, dus het heeft dezelfde
   zijmarge nodig als een panel__head. */
.planning-filter {
    flex-wrap: wrap;
    padding: 16px 24px;
    margin: 0;
    border-bottom: 1px solid var(--border);
    background: var(--surface-2);
}
.planning-filter .field { min-width: 180px; }
/* Het vinkje en de telling staan naast de keuzelijsten; die hebben een label
   boven zich, dus hier een beetje ruimte onder om op één lijn te komen. */
.planning-filter .checkbox { padding-bottom: 9px; font-size: .88rem; }
.filter-telling { font-size: .82rem; color: var(--text-soft); padding-bottom: 9px; }

@media (max-width: 720px) {
    .planning-filter { padding: 14px 18px; }
    .planning-filter .field { min-width: 0; flex: 1 1 140px; }
}

/* De projectkeuze: één rij per project, en de hele rij is aantikbaar.
   Elke cel wordt daarvoor gevuld met een link — de celpadding verhuist naar
   die link, dus het ziet er hetzelfde uit maar het hele vlak vangt de klik.
   Bewust geen ::after over de rij heen: zo'n vlak zoekt het eerste element
   erboven dat zelf gepositioneerd is, en een <tr> telt daar niet overal voor
   mee. Dan ligt de link ineens over de hele pagina. */
.projectkeuze tbody td { padding: 0; }
.projectkeuze__vlak {
    display: block; padding: 12px 14px;
    color: inherit; text-decoration: none;
}
.projectkeuze__naam { font-weight: 600; }
.projectkeuze tbody tr:hover .projectkeuze__naam { text-decoration: underline; }

/* De rij oplichten bij toetsenbordfocus: de link is niet meer dan de naam,
   dus een randje daaromheen zegt te weinig over wat je opent. */
.projectkeuze tbody tr:focus-within td { background: var(--surface-2); }
.projectkeuze__naam:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* De planning is een lange lijst met korte regels; daar past een compactere
   rij bij dan bij de beheertabellen. De datumkolom krijgt de ruimte om
   "1 sep 2026 t/m 20 sep 2026" op één regel kwijt te kunnen. */
.planning-tabel th, .planning-tabel td { padding: 8px 14px; }
.planning-tabel .col-datum { width: 1%; }
.planning-tabel .datum-tot { color: var(--text-soft); }
.planning-tabel .subtekst { margin-top: 2px; }
/* De naam van de stap is waar je op scant; 700 is in deze tabel te zwaar. */
.planning-tabel strong { font-weight: 600; }

/* Het eerste label van een stap bepaalt de kleur van de balk op de tijdlijn.
   Het stipje maakt dat zichtbaar, zodat de legenda onder de tijdlijn en deze
   kolom over hetzelfde gaan. */
.badge--label::before {
    content: ""; display: inline-block;
    width: 7px; height: 7px; border-radius: 50%;
    margin-right: 6px; vertical-align: 1px;
    background: var(--notion-kleur, var(--text-faint));
}

.notion-status--default, .notion-vlak--default { --notion-kleur: #8b93a1; }
.notion-status--gray,    .notion-vlak--gray    { --notion-kleur: #9aa1ad; }
.notion-status--brown,   .notion-vlak--brown   { --notion-kleur: #a1785c; }
.notion-status--orange,  .notion-vlak--orange  { --notion-kleur: #e08a3c; }
.notion-status--yellow,  .notion-vlak--yellow  { --notion-kleur: #d3a52f; }
.notion-status--green,   .notion-vlak--green   { --notion-kleur: #2ea36a; }
.notion-status--blue,    .notion-vlak--blue    { --notion-kleur: #3f7fe0; }
.notion-status--purple,  .notion-vlak--purple  { --notion-kleur: #8b6ae0; }
.notion-status--pink,    .notion-vlak--pink    { --notion-kleur: #d961a8; }
.notion-status--red,     .notion-vlak--red     { --notion-kleur: #d9534f; }

.notion-status {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 3px 10px; border-radius: 999px;
    font-size: .78rem; font-weight: 600; white-space: nowrap;
    background: var(--surface-2); color: var(--text-soft);
    border: 1px solid var(--border);
}
.notion-status::before {
    content: ""; width: 8px; height: 8px; border-radius: 50%;
    background: var(--notion-kleur, var(--text-faint));
}

/* Twee weergaven van dezelfde planning. */
.schakelaar {
    display: inline-flex; gap: 2px; padding: 2px;
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: 999px;
}
.schakelaar__knop {
    padding: 5px 14px; border: 0; border-radius: 999px;
    font: inherit; font-size: .82rem; font-weight: 600;
    color: var(--text-soft); background: none; cursor: pointer;
}
.schakelaar__knop:hover { color: var(--text); }
.schakelaar__knop.is-actief {
    background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm);
}

/* In het klantportaal staat deze schakelaar in de kop van het paneel. In het
   CRM zit de planning onder een formulier en komt hij na het laden binnen --
   met de kop erbuiten, want die verwisselt niet mee bij het filteren. Dus
   staat hij hier boven de lijst, op dezelfde marge als de filterbalk erboven. */
.planning-schakelaar { margin: 14px 24px 0; }

/* Tijdens het verversen van het filter blijft de oude planning staan, maar wat
   vager: zo springt de pagina niet en zie je dat er iets gebeurt. */
[data-notionvlak][aria-busy="true"] { opacity: .55; transition: opacity .15s; }

@media (max-width: 720px) {
    .planning-schakelaar { margin: 12px 18px 0; }
}

/* De tijdlijn. Geen naamkolom: de naam staat achter de balk, op de plek waar
   de stap valt. Dat geeft de as de volle breedte, en bij een weekraster is dat
   precies wat je nodig hebt. */
.tijdlijn-scroll { overflow-x: auto; }
.tijdlijn { min-width: var(--tijdlijn-breedte, 560px); }

.tijdlijn__rij, .tijdlijn__kop { display: flex; align-items: stretch; }
.tijdlijn__rij { border-top: 1px solid var(--border); }
.tijdlijn__kop { border-bottom: 1px solid var(--border); }

/* De kop is twee stroken boven elkaar: maanden ter oriëntatie, weken als het
   eigenlijke raster. Beide even breed als de as eronder. */
.tijdlijn__askop { flex: 1 1 auto; min-width: var(--tijdlijn-breedte, 560px); }
.tijdlijn__maandbalk, .tijdlijn__weekbalk { display: flex; }

.tijdlijn__maand {
    padding: 8px 8px 4px; font-size: .74rem; font-weight: 600;
    color: var(--text-soft); text-transform: lowercase;
    border-left: 1px solid var(--border-strong);
    overflow: hidden; white-space: nowrap;
}
/* De eerste maand begint halverwege en heeft dus geen grens om te tonen. */
.tijdlijn__maand:first-child { border-left: 0; }

.tijdlijn__week {
    padding: 2px 0 5px; font-size: .68rem;
    color: var(--text-faint); text-align: center;
    border-left: 1px solid var(--border);
    overflow: hidden; white-space: nowrap;
}
.tijdlijn__week:first-child { border-left: 0; }

/* Het raster zelf: een lijn per week en een tint over zaterdag en zondag.
   Als achtergrond en niet als elementen — bij een half jaar planning zijn dat
   anders zesentwintig lege blokjes per rij. --week en --werkweek komen uit de
   view; die weet hoeveel procent van de as één week beslaat. */
.tijdlijn__as {
    position: relative;
    flex: 1 1 auto; min-width: var(--tijdlijn-breedte, 560px);
    min-height: 34px;
}
/* De weekendtint loopt door tot in de kop, zodat de kolommen één geheel zijn.
   De weeklijnen staan daar al als rand op de kolommen zelf. */
.tijdlijn__as, .tijdlijn__weekbalk {
    background-image: repeating-linear-gradient(to right,
        transparent 0, transparent var(--werkweek, 100%),
        var(--weekend) var(--werkweek, 100%), var(--weekend) var(--week, 100%));
}
.tijdlijn__as {
    background-image:
        repeating-linear-gradient(to right,
            var(--border) 0, var(--border) 1px,
            transparent 1px, transparent var(--week, 100%)),
        repeating-linear-gradient(to right,
            transparent 0, transparent var(--werkweek, 100%),
            var(--weekend) var(--werkweek, 100%), var(--weekend) var(--week, 100%));
}

.tijdlijn__maandlijn {
    position: absolute; top: 0; bottom: 0;
    border-left: 1px solid var(--border-strong);
}

.tijdlijn__punt {
    position: absolute; top: 50%;
    width: 11px; height: 11px; border-radius: 50%;
    transform: translate(-50%, -50%);
    background: var(--notion-kleur, var(--brand));
}
/* Een balk is tien pixels hoog en een punt elf breed; daar precies op mikken
   met de muis is te veel gevraagd. Dit onzichtbare vlak eromheen vangt het op,
   zodat het kaartje al verschijnt als je in de buurt komt. */
.tijdlijn__punt::after {
    content: ""; position: absolute; inset: -9px -6px;
}
.tijdlijn__punt--balk {
    height: 10px; min-width: 8px; border-radius: 999px;
    transform: translateY(-50%);
}

/* De naam hangt aan de balk en schuift dus mee. Buiten de balk en niet erin:
   een balk van één dag is elf pixels breed en daar past geen woord in. */
.tijdlijn__titel {
    position: absolute; left: 100%; top: 50%;
    transform: translateY(-50%);
    margin-left: 10px;
    font-size: .84rem; font-weight: 600; line-height: 1.3; color: var(--text);
    white-space: nowrap;
}
.tijdlijn__titel--links {
    left: auto; right: 100%;
    margin-left: 0; margin-right: 10px;
}
/* Wie de stap doet, achter de titel. Dunner en lichter: de titel moet de
   regel blijven dragen, dit is de aanvulling erop. Binnen dezelfde span als de
   titel, dus het schuift vanzelf mee naar links als de naam aan de andere kant
   van de balk komt te staan. */
.tijdlijn__wie {
    margin-left: 7px;
    font-weight: 400; color: var(--text-soft);
}
.tijdlijn__vandaag {
    position: absolute; top: 0; bottom: 0;
    border-left: 2px dashed var(--brand); opacity: .55;
}

/* De legenda onder de as: welk label heeft welke kleur? Loopt door op een
   smal scherm in plaats van mee te schuiven met de as — hij hoort bij de
   tijdlijn als geheel, niet bij een plek erop. */
.tijdlijn-legenda {
    display: flex; flex-wrap: wrap; gap: 8px 18px;
    margin: 0; padding: 14px 20px 2px;
    list-style: none;
    border-top: 1px solid var(--border);
}
.tijdlijn-legenda__item {
    display: inline-flex; align-items: center; gap: 7px;
    font-size: .8rem; color: var(--text-soft);
}
.tijdlijn-legenda__item::before {
    content: ""; width: 14px; height: 8px; border-radius: 999px;
    background: var(--notion-kleur, var(--text-faint));
}
/* Vandaag is een streep en geen balk; het bolletje zou liegen. */
.tijdlijn-legenda__item--vandaag::before {
    width: 0; height: 12px; border-radius: 0;
    background: none; border-left: 2px dashed var(--brand); opacity: .55;
}

/* Het kaartje bij een balk. Staat fixed en aan de body, want binnen de
   tijdlijn zou de scrollbak het afknippen; planning.js zet de plek. */
.kaartje {
    position: fixed; z-index: 95;
    max-width: 280px;
    padding: 12px 14px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    font-size: .84rem; line-height: 1.45;
    pointer-events: none;
}
.kaartje__titel {
    margin: 0 0 8px; font-size: .9rem; font-weight: 600; line-height: 1.35;
}
/* De samenvatting is lopende tekst en staat daarom boven de kenmerken, niet
   ertussen. Een scheidingslijn eronder houdt de twee uit elkaar. */
.kaartje__tekst {
    margin: -2px 0 9px; padding-bottom: 9px;
    color: var(--text-soft);
    border-bottom: 1px solid var(--border);
}
/* Twee kolommen: het kopje links, de waarde rechts. Bij een lange tag mag de
   waarde afbreken; het kopje nooit. */
.kaartje__lijst {
    display: grid; grid-template-columns: auto 1fr; gap: 5px 12px;
    margin: 0;
}
.kaartje__lijst dt {
    font-size: .76rem; font-weight: 600; color: var(--text-soft);
    white-space: nowrap;
}
.kaartje__lijst dd { margin: 0; }

/* De regels van een factuur in een kaartje: hier is de omschrijving het lange
   deel en het bedrag het korte, dus precies andersom dan bij de planning. De
   omschrijving mag afbreken, het bedrag staat rechts en blijft op één regel. */
.kaartje--regels { max-width: 380px; }
.kaartje--regels .kaartje__lijst { grid-template-columns: minmax(0, 1fr) auto; gap: 7px 16px; }
.kaartje--regels .kaartje__lijst dt {
    white-space: normal; font-weight: 500; font-size: .84rem; color: var(--text);
}
.kaartje--regels .kaartje__lijst dd {
    text-align: right; white-space: nowrap; font-size: .84rem;
    font-variant-numeric: tabular-nums; color: var(--text-soft);
}

.tijdlijn-rest { padding: 16px 20px 4px; border-top: 1px solid var(--border); }
.tijdlijn-rest h3 { font-size: .95rem; margin: 0 0 8px; }
.opsomming { margin: 0; padding-left: 18px; }
.opsomming li { margin-bottom: 6px; font-size: .9rem; }
.tekst-zacht { color: var(--text-faint); }

@media (max-width: 860px) {
    .tijdlijn__titel { font-size: .8rem; margin-left: 8px; }
    .tijdlijn__titel--links { margin-left: 0; margin-right: 8px; }
}

/* ================================================= Tabellen op een telefoon
   Boven 640px zijn het gewone tabellen, en past er iets niet, dan schuift de
   scrollbak eromheen. Daaronder houdt dat op te werken: bij vijf kolommen ben
   je meer aan het vegen dan aan het lezen, en zonder kop in beeld weet je niet
   meer waar je naar kijkt.

   Elke rij wordt daarom een kaartje met de kolomkop vóór elke waarde. Het
   label komt uit data-label, dat tabel-mobiel.js per cel zet; zonder
   JavaScript krijgt de tabel de klasse tabel--mobiel niet en blijft alles bij
   het oude.

   De kop wordt absoluut gepositioneerd en niet als flex- of grid-item: een cel
   met meer dan één kind (een titel met een toelichting eronder) zou anders
   uiteenvallen in losse kolommen. */
@media (max-width: 640px) {
    /* .beheer en .firewall hebben hierboven al een eigen kaartweergave met
       labels uit de view zelf; die krijgen de klasse tabel--mobiel niet, maar
       de uitzondering staat er ook hier voor het geval dat. */
    .tabel--mobiel:not(.beheer):not(.firewall) thead {
        position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
        overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
    }

    .tabel--mobiel:not(.beheer):not(.firewall) tbody tr {
        display: block;
        padding: 12px 16px;
        border-bottom: 1px solid var(--border);
    }
    .tabel--mobiel:not(.beheer):not(.firewall) tbody tr:last-child { border-bottom: 0; }
    .tabel--mobiel:not(.beheer):not(.firewall) tr:hover td { background: none; }

    .tabel--mobiel:not(.beheer):not(.firewall) tbody td {
        display: block;
        position: relative;
        padding: 5px 0 5px 38%;
        border-bottom: 0;
        text-align: left;
        white-space: normal;
        overflow-wrap: anywhere;
    }
    .tabel--mobiel:not(.beheer):not(.firewall) tbody td[data-label]::before {
        content: attr(data-label);
        position: absolute; left: 0; top: 5px; width: 34%;
        font-size: .72rem; font-weight: 600; line-height: 1.35;
        text-transform: uppercase; letter-spacing: .03em;
        color: var(--text-soft);
        overflow-wrap: anywhere;
    }
    /* Cellen zonder kop — een knoppenkolom, een vinkje, een melding over de
       volle breedte — nemen de hele regel. */
    .tabel--mobiel:not(.beheer):not(.firewall) tbody td:not([data-label]) { padding-left: 0; }

    /* De datumkolom krimpt op een brede tabel tot precies haar inhoud; als
       blok in een kaartje zou die 1% er letterlijk 1% van maken. */
    .tabel--mobiel .col-datum { width: auto; }

    /* In een kaartje heeft de cel zelf al padding; de link erin vult hem nog
       steeds helemaal, dus de rij blijft aantikbaar. */
    .projectkeuze .projectkeuze__vlak { padding: 0; }

    /* De scrollbak heeft niets meer te doen zodra er niets meer uitsteekt. */
    .tabel-scroll:has(.tabel--mobiel) { overflow-x: visible; }
}

/* =====================================================================
   CRM — wat het beheerscherm bovenop de portaalstijl nodig heeft.

   Dit bestand begon als een kopie van de stylesheet van mijn.satdesign.nl,
   zodat de twee omgevingen als één product aanvoelen. Alles wat hieronder
   staat is van het CRM alleen; verandert er iets aan de gedeelde stijl
   hierboven, dan moet dat voorlopig met de hand mee.
   ===================================================================== */

/* Zoals in het logo: de naam rood, de regel eronder blauw. */
.crm-merk { display: block; font-weight: 700; letter-spacing: -.02em; color: var(--accent); }
.crm-merk__sub {
    display: block; font-size: .72rem; text-transform: uppercase;
    letter-spacing: .1em; color: var(--brand);
}
.auth-merk { text-align: center; margin-bottom: 18px; }
.auth-merk img { display: block; margin: 0 auto 8px; }
/* Hetzelfde logo als het klantportaal, met "administratie" eronder in plaats
   van de portaalnaam ernaast. */
.crm .brand-link { text-decoration: none; color: inherit; }
.crm .sidebar__brand { padding-bottom: 18px; align-items: flex-start; }
.crm .brand-name { line-height: 1.15; }

/* Ingeklapt staat de merkregel als kolom en is de uitlijning hierboven de
   verkeerde: align-items regelt in een kolom de horizontale kant, dus met
   flex-start plakten het logo en de inklapknop tegen de linkerrand in plaats
   van in het midden van de smalle balk.

   Die uitlijning staat er niet voor niets — uitgeklapt hoort "Satdesign /
   administratie" van twee regels links uit te lijnen met het logo — dus hij
   wordt hier teruggezet in plaats van weggehaald. Drie klassen, want
   .crm .sidebar__brand staat verderop in dit bestand en zou bij gelijke
   zwaarte winnen; .menu-ingeklapt staat op <html> en .crm op <body>. */
@media (min-width: 861px) {
    .menu-ingeklapt .crm .sidebar__brand { align-items: center; }
}

/* De pictogrammen in het menu zijn tekeningen en geen letters. .nav-ico is op
   een teken gemaakt — 20px breed en gecentreerd via text-align — dus de
   tekening krijgt hier zelf een maat en een eigen uitlijning. flex: none, want
   naast een lang label mag het icoon niet meekrimpen. */
.crm .sidebar__nav .nav-ico {
    display: flex; flex: none;
    align-items: center; justify-content: center;
}
.crm .nav-ico svg { width: 18px; height: 18px; }

/* Dezelfde voet als het klantportaal — wie ben je, en eruit — met de link naar
   het portaal ertussen. Dat is hier geen menu-onderdeel maar een uitstapje naar
   een andere omgeving, en die horen onderaan bij elkaar. */
.sidebar__voet {
    margin-top: auto; padding-top: 16px;
    display: flex; flex-direction: column; gap: 8px;
    border-top: 1px solid var(--border);
}
/* De marge van .user-chip komt uit het portaal, waar de voet geen gap heeft;
   hier zou hij er bovenop komen. */
.sidebar__voet .user-chip { margin-bottom: 4px; }
/* Een <form> is geen knop: zonder dit staat de uitlogknop in een blok dat zelf
   geen breedte meebrengt. */
.sidebar__voet form { display: contents; }
/* Het pictogram verschijnt pas in de smalle balk, net als in het portaal: naast
   het woord voegt het niets toe, zonder het woord is het het enige houvast. */
.crm .uitlog .nav-ico svg { width: 18px; height: 18px; }

/* De kolomkop blijft staan zodra hij uit beeld zou lopen. Bij veertig regels
   ben je anders halverwege kwijt of de vierde kolom nu minuten of prijs is.

   Een eigen achtergrond is geen opsmuk maar noodzaak: een sticky element laat
   zien wat eronder doorschuift, en zonder vlak lees je de kop dwars door de
   rijen heen. De lijn eronder komt uit .admin-table en blijft mee zweven. */
.crm-tabel thead th {
    position: sticky; top: 0; z-index: 2;
    background: var(--surface);
    /* De lijn als schaduw en niet alleen als rand: bij border-collapse hoort de
       rand bij de tabel en niet bij de cel, en dan blijft hij bij het scrollen
       achter terwijl de kop meebeweegt. Zo is er altijd een lijn onder. */
    box-shadow: inset 0 -1px 0 var(--border);
}

/* Een lijst in het CRM is lang en de regels zijn kort; daar past een/* Een lijst in het CRM is lang en de regels zijn kort; daar past een
   compactere rij bij dan bij de beheertabellen van het portaal. */
.crm-tabel th, .crm-tabel td { padding: 8px 14px; }

/* De buitenste cellen lijnen uit met de filterbalk erboven en de bladerbalk
   eronder: die staan allebei 24px van de rand. Alleen de eerste en de laatste,
   want tussen de kolommen is 14px genoeg — 24px overal zou de tabel uit elkaar
   trekken in plaats van hem netjes in het paneel zetten. */
.crm-tabel th:first-child, .crm-tabel td:first-child { padding-left: 24px; }
.crm-tabel th:last-child,  .crm-tabel td:last-child  { padding-right: 24px; }
/* Onder 640px is de tabel een rij kaartjes en zit de marge op de kaart zelf;
   die regels staan verderop en zijn zwaarder, dus die winnen daar vanzelf. */

/* Een sorteerbare kolomkop. De pijl komt uit de CSS en niet uit de HTML: de
   kaartweergave op een telefoon zet de kop vóór elke waarde, en dan zou de pijl
   daar in elk kaartje meelopen. */
.crm-sorteer { color: inherit; text-decoration: none; white-space: nowrap; }
.crm-sorteer:hover { color: var(--text); text-decoration: underline; }
.crm-sorteer.is-actief { color: var(--text); }
.crm-sorteer.is-asc::after  { content: ' \2191'; }
.crm-sorteer.is-desc::after { content: ' \2193'; }

/* De naamkolom opent de regel. */
.crm-rijlink { color: inherit; text-decoration: none; }
.admin-table tr:hover .crm-rijlink { text-decoration: underline; }

/* De klant bij een factuur, het domein bij een abonnement: door naar waar die
   regel over gaat. Ingetogen — de naam in de eerste kolom blijft de link die je
   het eerst ziet, deze is er voor als je iets anders zoekt. */
.crm-relatielink { color: inherit; text-decoration: none; }
.crm-relatielink:hover,
.crm-relatielink:focus-visible { color: var(--brand); text-decoration: underline; }

/* Dupliceren, helemaal rechts op de regel. Ingetogen tot je erbij bent: het is
   een handeling die je zelden doet, en een rij knoppen onder elkaar trekt meer
   aandacht dan de gegevens ernaast. Het aanraakvlak is 32 bij 32 en dus groter
   dan het pictogram van 16. */
.crm-kopie {
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; border-radius: var(--radius-sm);
    color: var(--text-faint);
    /* Deze klasse zat eerst alleen op links. De knop naar Yuki is een <button>,
       en die brengt zijn eigen rand en vlak van de browser mee -- dat was het
       opgeplakte randje. */
    border: 0; background: none; padding: 0; font: inherit; cursor: pointer;
    transition: color .15s, background .15s;
}
.crm-kopie svg { width: 16px; height: 16px; }
/* Een handeling die al gedaan is: het pictogram blijft staan als aantekening,
   maar het is geen knop meer. Groen, want dit is het enige plekje in de rij
   waar "klaar" iets betekent. */
.crm-kopie--klaar { color: var(--success-tx); cursor: default; }

/* En de tegenhanger: dit moet nog gebeuren. De andere pictogrammen in de rij
   zijn grijs tot je erbij bent -- dit is het enige dat om iets vraagt, dus krijgt
   het kleur. Alleen kleur: een vlakje met een randje eromheen maakt er een knop
   in een knop van, en dat viel op als een opgeplakt vakje. */
.crm-kopie--yuki,
.crm-kopie--todo { color: var(--attentie); }

.crm-kopie:hover { color: var(--brand); background: var(--brand-50); }

/* Bij aanwijzen dezelfde kleur met een vlakje eronder -- zo blijft de kleur van
   de handeling kloppen.

   Deze twee stonden eerst vóór de regel hierboven. Ze zijn even zwaar, dus won
   de laatste en werd elk pictogram bij het aanwijzen alsnog indigo -- inclusief
   het groene vinkje, dat helemaal geen knop is. */
.crm-kopie--yuki:hover,
.crm-kopie--todo:hover {
    color: var(--attentie);
    background: color-mix(in srgb, var(--attentie) 12%, transparent);
}
.crm-kopie--klaar:hover { color: var(--success-tx); background: none; }
.crm-kopie:focus-visible {
    outline: 2px solid var(--brand); outline-offset: 2px; color: var(--brand);
}

/* Wat er vandaag bij is gekomen, licht getint. Bij het bijwerken van een lange
   lijst wil je in één blik zien wat van vandaag is en wat er al stond.

   Een tint van de merkkleur en niet groen of geel: het zegt "nieuw" en niet
   "goed" of "let op". Met color-mix duidelijk lichter dan --brand-50 zelf, want
   dat is de kleur van het actieve menu-item en die is voor een hele rij te
   nadrukkelijk. De kleur staat nergens alleen voor: de datum staat in dezelfde
   regel, dus wie het verschil niet ziet leest het gewoon. */
.crm-tabel tr.crm-vandaag td {
    background: color-mix(in srgb, var(--brand-50) 55%, var(--surface));
}
/* Zweven moet ook op een getinte regel iets doen, anders lijkt hij dood. */
.admin-table tr.crm-vandaag:hover td {
    background: color-mix(in srgb, var(--brand-50) 90%, var(--surface));
}

/* De streep links van een regel, in de kleur die de module bij die rij noemt
   -- bij onderhoud die van het label (Module::$rijkleur). De kleur komt als
   --rijkleur op de rij mee.

   Een schaduw aan de binnenkant van de eerste cel en geen border-left: een
   rand maakt de cel breder, en dan staat de eerste kolom van een regel mét
   label vier pixels verder dan die van een regel zonder. Op de rij zelf kan
   het niet, want een tabelrij tekent geen schaduw. */
.crm-tabel tr.crm-rijkleur td:first-child {
    box-shadow: inset 4px 0 0 var(--rijkleur);
}

/* Het staaltje in de kolom Kleur bij de labels. Met een dun randje, anders
   verdwijnt een heel lichte kleur tegen de achtergrond. */
.kleurstaal {
    display: inline-block; width: .95em; height: .95em; margin-right: 8px;
    vertical-align: -.12em;
    background: var(--staal);
    border-radius: 4px;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--text) 18%, transparent);
}

/* Het hexveld met de kiezer ernaast (Veld::KLEUR). De kiezer is een vierkant
   ter hoogte van het invoerveld; zonder JavaScript staat hij op hidden en vult
   het hexveld de hele breedte. */
.kleurveld { display: flex; gap: 8px; align-items: stretch; }
.kleurveld input[type=text] { flex: 1 1 auto; min-width: 0; }
.kleurveld__kiezer {
    flex: none; width: 42px; min-height: 38px; padding: 3px;
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.kleurveld__kiezer::-webkit-color-swatch-wrapper { padding: 0; }
.kleurveld__kiezer::-webkit-color-swatch { border: 0; border-radius: 6px; }
.kleurveld__kiezer::-moz-color-swatch { border: 0; border-radius: 6px; }

/* De datumkolom houdt haar eigen maat. De tabel staat afbreken toe omdat er
   omschrijvingen in staan die anders door de kolom ernaast lopen, maar een
   datum is één ondeelbaar ding: die brak af tot "04-09-" met "2026" eronder.

   Kolommen met een eigen breedte uit de moduledefinitie (Veld::$lijstbreedte)
   staan op de kop; hun cellen erven die vanzelf. */
.crm-tabel th.kol-datum,
.crm-tabel td.kol-datum { width: 7rem; }

.filterbalk .crm-nieuw { margin-left: auto; }
@media (max-width: 640px) {
    /* Zoeken hoort bij het vak ernaast, dus die twee blijven één regel; de
       knop naar een nieuw formulier is iets anders en krijgt zijn eigen regel
       eronder. flex-basis 100% doet allebei tegelijk: hij breekt de regel af
       én vult hem, wat op een telefoon meteen een fatsoenlijk aanraakvlak
       oplevert. */
    .filterbalk .crm-nieuw { flex: 0 0 100%; margin-left: 0; }
}

/* Een veld met een afgekeurde waarde. De melding staat eronder, op de plek
   waar anders de toelichting staat. */
.field--fout input, .field--fout textarea, .field--fout .select {
    border-color: var(--error-bd); background: var(--error-bg);
}
.field__fout { font-size: .78rem; color: var(--error-tx); line-height: 1.4; }

/* Een keuze die zichzelf zojuist heeft ingevuld licht even op. Zonder dat
   verandert er iets in beeld zonder dat je ziet dát er iets veranderde --
   en dan vertrouw je het de volgende keer niet meer. */
.select.is-afgeleid {
    border-color: var(--brand);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent);
    transition: box-shadow .25s, border-color .25s;
}

/* Een link naast een veld: "open dit elders". Klein en terughoudend — het is
   geen handeling in dit formulier maar een uitstapje. Staat als rasteritem
   onder zijn veld en niet ernaast, want de kolom is al smal. */
.veld-elders {
    display: inline-flex; align-items: center; gap: 6px;
    margin-top: -8px; font-size: .8rem; color: var(--text-soft);
    text-decoration: none;
}
.veld-elders:hover { color: var(--brand); text-decoration: underline; }
.veld-elders svg { width: .85em; height: .85em; flex: none; }
.veld-elders:focus-visible {
    outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 3px;
}

/* --- Het ingeklapte deel van een formulier -------------------------------
   Voor velden die er nog zijn maar niet meer worden ingevuld. Als rasteritem
   over alle kolommen, want het is een blok en geen veld. */
.form--raster > .veld-meer { grid-column: 1 / -1; }
.veld-meer {
    border-top: 1px solid var(--border);
    padding-top: 14px; margin-top: 4px;
}
.veld-meer > summary {
    display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
    cursor: pointer; user-select: none; list-style: none;
    font-size: .86rem; font-weight: 600; color: var(--text-soft);
}
.veld-meer > summary::-webkit-details-marker { display: none; }
.veld-meer > summary::before {
    content: ''; flex: none; width: 7px; height: 7px; align-self: center;
    border-right: 2px solid var(--text-faint); border-bottom: 2px solid var(--text-faint);
    transform: rotate(-45deg); transition: transform .15s ease;
}
.veld-meer[open] > summary::before { transform: rotate(45deg); }
.veld-meer > summary:hover { color: var(--text); }
.veld-meer > summary:focus-visible {
    outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 4px;
}
.veld-meer__bij { font-weight: 400; font-size: .8rem; color: var(--text-faint); }

/* Binnenin hetzelfde raster als het formulier eromheen, zodat de velden onder
   en boven de vouw op dezelfde kolommen vallen. */
.veld-meer__raster { display: flex; flex-direction: column; gap: 16px; margin-top: 16px; }
@media (min-width: 760px) {
    .veld-meer__raster {
        display: grid; align-items: start;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 16px 22px;
    }
    .veld-meer__raster > .field--breed { grid-column: 1 / -1; }
    .veld-meer__raster > .checkbox { align-self: end; padding-bottom: 9px; }
}
@media (min-width: 1240px) {
    .veld-meer__raster { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* --- Het kruimelpad -------------------------------------------------------
   Staat op de plek van de bovenregel die er eerst stond. Klein en grijs: het is
   oriëntatie en geen inhoud, en de kop eronder is waar je naar kijkt.

   De scheidingstekens komen uit de CSS en niet uit de HTML: als tekst zouden ze
   worden voorgelezen en meegekopieerd. */
.kruimels { margin-bottom: 10px; }
.kruimels ol {
    display: flex; flex-wrap: wrap; align-items: center; gap: 2px 0;
    margin: 0; padding: 0; list-style: none;
    font-size: .78rem; line-height: 1.4;
}
.kruimels li { display: inline-flex; align-items: center; min-width: 0; }
.kruimels li + li::before {
    content: ''; flex: none;
    width: 5px; height: 5px; margin: 0 9px;
    border-right: 1.5px solid var(--text-faint); border-top: 1.5px solid var(--text-faint);
    transform: rotate(45deg);
}
.kruimels a { color: var(--text-soft); text-decoration: none; }
.kruimels a:hover { color: var(--brand); text-decoration: underline; }
.kruimels a:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 3px; }
/* De laatste kruimel is waar je bent: donkerder, en hij kapt af in plaats van
   de regel te laten omvallen — een projecttitel kan lang zijn. */
.kruimels [aria-current="page"] {
    color: var(--text); font-weight: 600;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Een losse knop in een tabelcel zit in een eigen formulier — anders zou hij
   binnen het formulier eromheen vallen en dat versturen. Dat formulier mag
   verder niets doen met de opmaak van de rij. */
.koppel-af { display: inline; margin: 0; }

/* --- De kleine editor bij de factuurteksten ------------------------------
   Eén omlijning om de knoppenbalk en het schrijfvlak samen, zodat het als één
   veld leest en niet als een balk met een tekstvak eronder. */
.editor {
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    overflow: hidden;
}
.editor:focus-within {
    border-color: var(--brand);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent);
}

.editor__balk {
    display: flex; flex-wrap: wrap; gap: 2px;
    padding: 5px 6px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
}
.editor__knop {
    min-width: 30px; height: 28px; padding: 0 7px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: 1px solid transparent; border-radius: 7px;
    color: var(--text-soft); font: inherit; font-size: .84rem; cursor: pointer;
    transition: background .12s, color .12s;
}
.editor__knop:hover { background: var(--surface); color: var(--text); }
.editor__knop:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.editor__knop.is-actief { background: var(--brand-50); color: var(--brand); }
/* De broncodeknop hoort niet bij de opmaakknoppen: die schuift naar rechts. */
.editor__knop--rechts { margin-left: auto; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

.editor__vlak {
    min-height: 150px; max-height: 420px; overflow-y: auto;
    padding: 12px 14px;
    font: inherit; color: var(--text); line-height: 1.55;
}
.editor__vlak:focus { outline: none; }
/* De mailtekst is geen webpagina: alinea's mogen niet de marges van dit CRM
   erven, anders lijkt hij in de editor anders dan in de mailbox. */
.editor__vlak p { margin: 0 0 1em; }
.editor__vlak p:last-child { margin-bottom: 0; }
.editor__vlak a { color: var(--brand); }
.editor__vlak ul { margin: 0 0 1em; padding-left: 22px; }

/* Het tekstvak eronder is de broncode: vaste breedte, want daar lees je tags. */
.editor textarea {
    display: block; width: 100%; border: 0; border-radius: 0;
    background: var(--surface); min-height: 170px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .84rem;
}
.editor textarea:focus { outline: none; box-shadow: none; }

/* --- De regels op een factuur -------------------------------------------
   Vier kolommen die op elke regel op dezelfde plek staan, dus geen tabel met
   losse velden maar één raster waar de koprij en de rijen allebei in vallen.
   De omschrijving krijgt wat er overblijft; de drie getallen zijn zo breed als
   ze moeten zijn en niet breder — anders staat er een invoerveld van dertig
   tekens waar "1" in komt. */
.factuurregels {
    --regelkolommen: minmax(0, 1fr) 5.5rem 7.5rem minmax(0, 17rem);
    display: flex; flex-direction: column; gap: 6px;
}
.factuurregels__kop,
.factuurregels__rij {
    display: grid; gap: 8px;
    grid-template-columns: var(--regelkolommen);
    align-items: center;
}
.factuurregels__kop {
    font-size: .78rem; color: var(--text-faint);
    padding: 0 2px;
}
/* Getallen rechts uitlijnen: zo staan de komma's onder elkaar en zie je in één
   oogopslag of er een nul te veel in staat. */
.factuurregels__rij input:not(:first-child) { text-align: right; }
/* De omschrijving bij een grootboekrekening is lang ("8020: Omzet vrijgestelde
   prestaties 0%"). Hij mag afgekapt worden — het nummer staat vooraan en dat is
   waar je op zoekt. */
.factuurregels__rij .select { min-width: 0; }
.factuurregels .form__acties { margin-top: 4px; align-items: center; }
.factuurregels [data-regeltotaal] { margin-left: auto; }

@media (max-width: 640px) {
    /* Naast elkaar is dit op een telefoon vier velden van vijftig pixels. Onder
       elkaar met een kopje per veld leest beter; de koprij vervalt dan. */
    .factuurregels { gap: 14px; }
    .factuurregels__kop { display: none; }
    .factuurregels__rij { grid-template-columns: 1fr 1fr; gap: 6px; }
    .factuurregels__rij input:first-child,
    .factuurregels__rij .select { grid-column: 1 / -1; }
    .factuurregels__rij input:not(:first-child) { text-align: left; }
    /* Een streep tussen de regels, want zonder koprij is anders niet te zien
       waar de ene regel ophoudt en de volgende begint. */
    .factuurregels__rij + .factuurregels__rij {
        border-top: 1px solid var(--border); padding-top: 14px;
    }
}

/* --- Balans: een staaf per maand -----------------------------------------
   Honderdvijfentwintig maanden en er komen er elke maand bij. De staven delen
   de breedte, met een ondergrens per maand: op een telefoon wordt het anders
   een grijze waas van haarlijnen. Onder die grens schuift het venster.

   Alles in HTML en CSS, zoals de grafieken onder Onderhoud: de browser verdeelt
   de breedte en meet de tekst, en dat is precies wat de server niet kan. */
.balans__venster {
    overflow-x: auto; overflow-y: hidden; padding-bottom: 4px;
    /* De grafiek is breder dan het scherm en begint in 2003, terwijl je hier
       voor de laatste maanden komt. Met rtl op het venster en ltr op de inhoud
       staat hij bij het openen meteen aan het einde -- zonder JavaScript, en
       zonder dat er iets aan de volgorde van de staven verandert. */
    direction: rtl;
}
.balans__venster > * { direction: ltr; }

.balans__vlak {
    position: relative;
    /* Acht pixels per maand plus de aslabels links. Bij bijna driehonderd
       maanden is dat ruim twee schermen breed, en dus schuift het venster --
       maar een staaf van zes pixels is wél een staaf, en op vier pixels werd
       het een streepjescode. */
    min-width: calc(var(--maanden) * 8px + 60px);
    padding-left: 58px;
}

/* ---- Het raster met de bedragen erbij ---- */
.balans__raster {
    position: absolute; inset: 0 0 34px 58px;
    display: flex; flex-direction: column; justify-content: space-between;
}
.balans__lijn {
    position: relative;
    border-top: 1px solid var(--border);
    height: 0;
}
/* De onderste lijn is de nullijn en mag steviger. */
.balans__lijn:last-child { border-top-color: var(--border-strong); }
.balans__aslabel {
    position: absolute; left: -58px; top: -.6em;
    width: 50px; text-align: right;
    font-size: .68rem; color: var(--text-faint);
    font-variant-numeric: tabular-nums;
}

/* ---- De staven ---- */
.balans__staven {
    position: relative;
    display: flex; align-items: flex-end;
    height: 260px; margin: 0; padding: 0; list-style: none;
}
/* De tussenruimte zit in de maand zelf en niet in een gap: alleen dan is elke
   maand exact even breed, en valt de rij jaartallen eronder precies onder de
   staven waar hij bij hoort. Met een gap houdt die rij ruimte over die de
   staven al hebben opgemaakt, en schuiven de jaren mee. */
.balans__maand { flex: 1 1 0; min-width: 6px; height: 100%; padding-right: 2px;
    display: flex; align-items: flex-end; }
/* Een streepje bij januari: zonder scheiding is een reeks van tien jaar één
   lange rij en zie je niet waar een jaar begint. */
.balans__maand--jaar { box-shadow: inset 1px 0 0 var(--border); }

.balans__staaf {
    display: block; width: 100%; min-height: 1px;
    border-radius: 2px 2px 0 0;
    background: var(--brand);
}
/* De lopende maand is nog niet af. Lichter, zodat die laatste lagere staaf niet
   als een terugval leest. */
.balans__staaf--loopt {
    background: color-mix(in srgb, var(--brand) 38%, var(--surface));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 55%, var(--surface));
}

/* ---- Wat er deze maand nog gefactureerd wordt ----
   Op het dashboard krijgt de staaf van deze maand er een blok bovenop: de
   planning uit Notion, zonder de abonnementen -- die staan er al als
   gefactureerd in. Zie Crm\Factureren en src/views/dashboard.php.

   De maand wordt daarvoor een kolom die vanaf de onderkant vult, met het
   gefactureerde deel onderaan en het voornemen erboven. Alleen op de maand die
   zo'n blok heeft: een kolom van één staaf zou hetzelfde opleveren, maar dan
   staat er opmaak voor tweehonderd maanden die niets doet. */
.balans__maand--stapel { flex-direction: column; justify-content: flex-end; }
/* De ronde bovenkant hoort bij wat er bovenaan staat; het gefactureerde deel
   zit daar nu tegenaan en sluit dus recht af. Op :last-child en niet op alle
   staven in de kolom: anders raakt het blok erboven zijn ronde hoeken kwijt --
   die regel is met twee klassen sterker dan de zijne. */
.balans__maand--stapel .balans__staaf:last-child { border-radius: 0; }

/* Het onderhoud van deze maand dat op de laatste werkdag gefactureerd wordt.
   Direct op het gefactureerde deel en niet in de arcering: het is gedaan werk
   dat vastligt, geen voornemen. Dezelfde rand als de lopende maand, maar een
   stap lichter, zodat je ziet waar de facturen ophouden. Staat er geen
   planning boven, dan is dit de bovenkant en krijgt het de ronde hoeken. */
.balans__staaf--onderhoud {
    border-radius: 0;
    background: color-mix(in srgb, var(--brand) 16%, var(--surface));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 55%, var(--surface));
}
.balans__maand--stapel .balans__staaf--onderhoud:first-child { border-radius: 2px 2px 0 0; }

/* Gearceerd en niet een kleur erbij. Dit bedrag is een voornemen en geen
   omzet, en dat verschil hoort te zien te zijn zonder legenda -- een derde
   volle kleur zou het net zo hard laten meetellen als de rest van de grafiek.
   Dezelfde arcering als de weekenden onder Onderhoud, in de merkkleur, dus hij
   volgt vanzelf de donkere stand. */
.balans__staaf--verwacht {
    border-radius: 2px 2px 0 0;
    background: repeating-linear-gradient(
        -45deg,
        color-mix(in srgb, var(--brand) 55%, var(--surface)) 0 2px,
        color-mix(in srgb, var(--brand) 10%, var(--surface)) 2px 5px
    );
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 40%, var(--surface));
}

/* ---- De jaartallen eronder ---- */
.balans__jaren {
    display: flex; margin: 6px 0 0; padding: 0; list-style: none;
    height: 28px;
}
.balans__jaar {
    flex: var(--breedte) 1 0; min-width: 0;
    border-top: 1px solid var(--border);
    padding: 5px 2px 0 0;
    font-size: .72rem; color: var(--text-faint);
    text-align: center;
    overflow: hidden; white-space: nowrap;
}

.balans__voet {
    margin-top: 14px; font-size: .8rem; color: var(--text-faint); line-height: 1.5;
}

/* De jaarregel in de tabel eronder: een kop en geen maand. */
.balans__jaarrij th,
.balans__jaarrij td {
    background: var(--surface-2); font-weight: 650; color: var(--text);
    border-top: 1px solid var(--border);
}
.admin-table .balans__jaarrij:hover td { background: var(--surface-2); }

/* --- De timer bij onderhoud ----------------------------------------------
   Een klok die loopt moet je zien lopen, ook als je met iets anders bezig bent.
   Vandaar de stip die pulseert en de tijd die doortelt: stilstaande cijfers
   lezen als "dit is gebeurd" en niet als "dit gebeurt nu". */
.timerknop {
    display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
    margin-bottom: 16px;
}
.timerknop .btn svg { width: 1.05em; height: 1.05em; margin-right: 7px; }
.timerknop__tijd {
    font-size: 1.15rem; font-weight: 650; font-variant-numeric: tabular-nums;
    letter-spacing: .01em;
}
.timerknop__uitleg { flex: 1 1 220px; }
/* Op pauze: de cijfers blijven staan maar treden terug, want er gebeurt niets. */
.timerknop__tijd--pauze { opacity: .6; }
.timerknop--pauze .timerknop__stip { animation: none; }
.timerknop__melding {
    flex-basis: 100%; font-size: .8rem; color: var(--success-tx);
}
.timerknop__melding--fout { color: var(--error-tx); }

/* De stopknop draagt de waarschuwkleur: hij doet iets onomkeerbaars (de tijd
   wordt opgeteld) en hij hoort op te vallen tussen de rest. */
.btn--stop {
    background: var(--warn-tx); color: var(--warn-bg); border-color: var(--warn-tx);
}
.btn--stop:hover { filter: brightness(1.08); }
.timerknop__stip, .lopend__stip {
    display: inline-block; width: 8px; height: 8px; margin-right: 8px;
    border-radius: 50%; background: currentColor;
    animation: timerpuls 1.6s ease-in-out infinite;
}
@keyframes timerpuls {
    0%, 100% { opacity: 1; }
    50%      { opacity: .3; }
}
/* Wie liever geen beweging ziet, krijgt een stip die gewoon staat. */
@media (prefers-reduced-motion: reduce) {
    .timerknop__stip, .lopend__stip { animation: none; }
}

/* De lopende timer in de mobiele kopbalk: alleen de langstlopende, met een
   plusje als er meer zijn. De hele lijst staat in het menu erachter. */
.mobiel-timer {
    display: inline-flex; align-items: center; gap: 6px; margin-left: auto; margin-right: 10px;
    padding: 5px 10px; border-radius: 999px;
    border: 1px solid var(--warn-bd); background: var(--warn-bg); color: var(--warn-tx);
    font-size: .8rem; font-weight: 650; font-variant-numeric: tabular-nums;
    text-decoration: none;
}
.mobiel-timer .lopend__stip { margin-right: 0; }
.mobiel-timer__meer { opacity: .75; font-weight: 550; }

/* ---- De klokken groot, boven het onderhoudsoverzicht ----
   Naast elkaar zolang er ruimte is, en anders onder elkaar. Er lopen er zelden
   meer dan twee of drie tegelijk; de kaart mag daarom breed zijn en de tijd
   groot. Dit is het scherm waar je tussendoor terugkomt om iets te stoppen. */
.timerkaarten {
    display: grid; gap: 14px; margin-bottom: 22px;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.timerkaart {
    display: flex; flex-direction: column; gap: 4px; min-width: 0;
    padding: 16px 18px 14px;
    border: 1px solid var(--warn-bd); border-radius: var(--radius);
    background: var(--warn-bg); color: var(--warn-tx);
    box-shadow: var(--shadow-sm);
}
/* Op pauze: dezelfde kaart, maar rustig. Er gebeurt niets, dus er hoeft ook
   niets te trekken -- alleen blijven staan zodat je hem niet vergeet. */
.timerkaart--pauze {
    border-color: var(--border); background: var(--surface-2); color: var(--text-soft);
}

.timerkaart__klant {
    margin: 0; font-size: .72rem; font-weight: 650;
    text-transform: uppercase; letter-spacing: .04em;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.timerkaart__titel { margin: 0; font-size: .92rem; font-weight: 600; line-height: 1.35; }
.timerkaart__titel a { color: var(--text); text-decoration: none; }
.timerkaart__titel a:hover { text-decoration: underline; }
/* Hooguit twee regels: een titel als "Blog: Een rolluik of screen: wat is de
   beste keuze voor je dakkapel?" zou de kaart anders twee keer zo hoog maken
   als zijn buurman. */
.timerkaart__titel a {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
}

.timerkaart__klok {
    margin: 6px 0 10px;
    font-size: 2.1rem; font-weight: 650; line-height: 1;
    font-variant-numeric: tabular-nums; letter-spacing: .01em;
    color: var(--text);
}
.timerkaart__klok--pauze { color: var(--text-soft); opacity: .75; }

.timerkaart__knoppen { display: flex; flex-wrap: wrap; gap: 8px; margin-top: auto; }
.timerkaart__knoppen .btn svg { width: 1em; height: 1em; margin-right: 5px; }

/* ---- Wat er nu loopt, in de zijbalk ---- */
.lopend {
    /* Alleen ruimte eronder: de zijbalk heeft zijn eigen marge al, en het blok
       hoort op één lijn te staan met de menu-items erboven. */
    margin: 0 0 20px; padding: 10px 12px;
    border: 1px solid var(--warn-bd); border-radius: var(--radius-sm);
    background: var(--warn-bg); color: var(--warn-tx);
}
.lopend__kop {
    display: flex; align-items: center; gap: 2px; margin: 0 0 8px;
    font-size: .76rem; font-weight: 650; text-transform: uppercase; letter-spacing: .04em;
}
/* Het getal is er voor de ingeklapte balk; daar past de tekst niet. */
.lopend__aantal { display: none; }
.lopend__lijst { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
/* min-width: 0 hoort erbij en is geen sierlijkheid. Een regel in dit lijstje is
   een rasteritem, en die staan van zichzelf op `min-width: auto`: ze mogen niet
   smaller worden dan hun inhoud. De titel eronder staat op `nowrap`, dus die
   inhoud is de hele titel op één regel — bij "Blog: Een rolluik of screen: wat
   is de beste keuze voor je dakkapel?" werd de li 470px breed in een zijbalk
   van 264, en dan liep de titel het kader uit in plaats van dat hij afbrak op
   een beletselteken. Hetzelfde als bij .field .select verderop. */
.lopend__lijst li { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.lopend__lijst a { flex: 1; min-width: 0; text-decoration: none; color: inherit; }
.lopend__titel {
    display: block; font-size: .8rem; font-weight: 550; color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lopend__lijst a:hover .lopend__titel { text-decoration: underline; }
.lopend__klant {
    display: block; font-size: .72rem; opacity: .85;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lopend__tijd {
    flex: none; font-size: .8rem; font-weight: 650; font-variant-numeric: tabular-nums;
}
.lopend__tijd--pauze { opacity: .55; }
/* Staat alles op pauze, dan houdt de stip op met knipperen en wordt het blok
   rustiger: er loopt niets, er hoeft ook niets te trekken. */
.lopend--stil { border-color: var(--border); background: var(--surface-2); color: var(--text-soft); }
.lopend--stil .lopend__stip { animation: none; opacity: .5; }
.mobiel-timer--pauze { border-color: var(--border); background: var(--surface-2); color: var(--text-soft); }
.mobiel-timer--pauze .lopend__stip { animation: none; opacity: .5; }

/* --- Dashboard -----------------------------------------------------------
   De eerste pagina na het inloggen. Opgebouwd uit wat er al is -- de
   cijferkaarten van het portaal, de staven van Balans, de gewone tabel -- met
   hier alleen de lijm ertussen. Een dashboard met een eigen vormtaal is een
   tweede product om te onderhouden. */
.dash-kop {
    display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
    gap: 14px; margin-bottom: 22px;
}
.dash-kop h1 { font-size: 1.6rem; font-weight: 680; line-height: 1.2; margin: 0; }
.dash-kop__datum { margin: 4px 0 0; color: var(--text-soft); font-size: .92rem; }
.dash-kop .btn svg { width: 1em; height: 1em; margin-right: 6px; }

/* De vier cijfers bovenaan zijn links; ze mogen er niet als links uitzien,
   maar wel als aanklikbaar aanvoelen. */
.dash-cijfers .kaart { text-decoration: none; color: inherit; }
.dash-cijfers .kaart:hover { border-color: var(--brand); }
.kaart__sub {
    margin-top: 2px; font-size: .78rem; color: var(--text-faint); line-height: 1.45;
}
/* Het accentstreepje bovenaan een kaart: genoeg om de rij ritme te geven
   zonder vier kleuren die allemaal iets willen zeggen. */
.dash-cijfers .kaart { position: relative; overflow: hidden; }
.dash-cijfers .kaart::before {
    content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
    background: var(--brand); opacity: .55;
}
.dash-cijfers .kaart--let-op::before { background: var(--warn-tx); opacity: .5; }

/* "€ 228.456,99" past op dertig punten niet in een kaart van anderhalve duim.
   De maat hangt daarom aan de kaart en niet aan het scherm: die vier kaarten
   staan afhankelijk van de breedte alleen, met z'n tweeën of met z'n vieren
   naast elkaar, en dan zegt de schermbreedte niets over de ruimte die er in één
   kaart over is. Een kaart die alleen staat op een smalle telefoon heeft plek
   zat; twee naast elkaar op datzelfde scherm niet.

   Browsers zonder containervragen houden de dertig punten; dat is de stand van
   voor deze regel en dus geen verslechtering. */
.dash-cijfers .kaart { container-type: inline-size; }

@container (max-width: 260px) {
    .dash-cijfers .kaart__getal { font-size: 1.5rem; }
}
@container (max-width: 210px) {
    .dash-cijfers .kaart__getal { font-size: 1.2rem; }
    .dash-cijfers .kaart__label { font-size: 12px; }
    .dash-cijfers .kaart__sub   { font-size: .74rem; }
    /* Ook de marges wat krapper: dat scheelt acht pixels, en precies die acht
       maken het verschil tussen één regel en een afgebroken bedrag. */
    .dash-cijfers .kaart { padding: 14px 16px 13px; }
}

/* Het verschil met vorig jaar. Groen en rood, maar met een teken erbij: kleur
   alleen is voor wie hem niet ziet geen verschil. */
.delta {
    display: inline-block; padding: 1px 7px; border-radius: 999px;
    font-size: .74rem; font-weight: 650;
    background: var(--surface-2); color: var(--text-soft);
}
.delta--op   { background: var(--success-bg); color: var(--success-tx); }
.delta--neer { background: var(--error-bg);   color: var(--error-tx); }

.dash-kopje {
    display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
    margin: 0 0 14px; font-size: .95rem; font-weight: 650;
}
.dash-meer { font-size: .78rem; font-weight: 500; color: var(--text-faint); text-decoration: none; }
a.dash-meer:hover { color: var(--brand); text-decoration: underline; }
.dash-voet { margin: 10px 0 0; font-size: .76rem; color: var(--text-faint); }
.dash-leeg { margin: 0; color: var(--text-faint); font-size: .9rem; }

/* ---- Wat aandacht vraagt ---- */
.dash-aandacht { margin-bottom: 22px; }
.aandacht { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.aandacht__punt a {
    display: flex; align-items: center; gap: 12px;
    padding: 11px 14px; border-radius: var(--radius-sm);
    border: 1px solid var(--border); background: var(--surface-2);
    color: inherit; text-decoration: none;
    transition: border-color .12s, background .12s;
}
.aandacht__punt a:hover { border-color: var(--brand); background: var(--surface); }
.aandacht__getal {
    min-width: 34px; text-align: center;
    font-size: 1.05rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.aandacht__tekst { flex: 1; font-size: .9rem; }
.aandacht__pijl { width: 16px; height: 16px; flex: none; color: var(--text-faint); }
.aandacht__punt a:hover .aandacht__pijl { color: var(--brand); }
/* Iets dat kán, en iets dat in de weg staat: dat tweede krijgt de waarschuwkleur. */
.aandacht__punt--doen .aandacht__getal   { color: var(--brand); }
.aandacht__punt--let-op a {
    border-color: var(--warn-bd); background: var(--warn-bg);
}
.aandacht__punt--let-op .aandacht__getal { color: var(--warn-tx); }
/* En een storing: die staat bovenaan en krijgt zijn eigen roze, zodat je aan
   de kleur al ziet dat dit iets anders is dan een factuur die blijft liggen. */
.aandacht__punt--storing a {
    border-color: var(--storing-bd); background: var(--storing-bg);
}
.aandacht__punt--storing a:hover { border-color: var(--storing-tx); }
.aandacht__punt--storing .aandacht__getal { color: var(--storing-tx); }
.aandacht__punt--storing .aandacht__tekst { color: var(--storing-tx); }
.aandacht__punt--storing a:hover .aandacht__pijl { color: var(--storing-tx); }

/* ---- De twee panelen eronder ---- */
.dash-raster { display: grid; gap: 18px; margin-bottom: 22px; }
@media (min-width: 960px) {
    /* De grafiek krijgt de ruimte; het lijstje ernaast is een kolom tekst. */
    .dash-raster { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); }
}

/* De twee panelen worden even hoog (dat doet het raster vanzelf) en de grafiek
   rekt mee met wat er overblijft: het lijstje ernaast is hoger, en onder een
   grafiek met een vaste hoogte stond anders een half paneel wit.

   De min-width: 0 is hierbij geen franje maar de reden dat het goed gaat.
   Zonder dat krimpt een rastercel niet onder de breedte van zijn inhoud, en
   duwt één lange klantnaam de hele kolom voorbij de rand van het scherm. */
.dash-raster > .panel { min-width: 0; display: flex; flex-direction: column; }
.dash-raster > .panel > .panel__body {
    min-width: 0; overflow: hidden;
    flex: 1; display: flex; flex-direction: column;
}

/* De staven van Balans, maar zonder as: het is hier een vorm en geen
   meetinstrument. Voor het precieze getal is er het kaartje en Balans zelf. */
.dash-staven { flex: 1; height: auto; min-height: 200px; min-width: 0; }
.dash-staven .balans__maand { min-width: 4px; padding-right: 3px; }

/* ---- Laatst gedaan ---- */
.dash-lijst { list-style: none; margin: 0; padding: 0; min-width: 0; }
.dash-lijst li {
    display: flex; align-items: baseline; gap: 12px; min-width: 0;
    padding: 9px 0; border-bottom: 1px solid var(--border);
}
.dash-lijst li:last-child { border-bottom: none; padding-bottom: 0; }
.dash-lijst__link { flex: 1; min-width: 0; text-decoration: none; color: inherit; }
.dash-lijst__titel {
    display: block; font-size: .88rem; font-weight: 550;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dash-lijst__link:hover .dash-lijst__titel { color: var(--brand); }
.dash-lijst__meta {
    display: block; margin-top: 2px; font-size: .76rem; color: var(--text-faint);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dash-lijst__waarde {
    flex: none; text-align: right; font-size: .82rem; font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.dash-lijst__waarde small { display: block; font-size: .72rem; color: var(--text-faint); }

/* Het soort achter de naam in de verlengingentabel. */
.dash-soort { margin-left: 8px; font-size: .72rem; color: var(--text-faint); font-weight: 400; }

/* --- De lichtbak ---------------------------------------------------------
   Een pagina als venster over het overzicht heen, nu voor het mailen van een
   factuur. Een <dialog> en geen eigen constructie: die regelt de aandacht, de
   Escape-toets en het afsluiten van de pagina eronder zelf. */
.lichtbak {
    width: min(760px, calc(100vw - 32px));
    max-height: calc(100vh - 64px);
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--text);
    box-shadow: 0 24px 60px rgb(0 0 0 / 28%);
    overflow: hidden;
}
.lichtbak::backdrop { background: rgb(15 18 24 / 55%); }

.lichtbak__kop {
    display: flex; align-items: center; gap: 12px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--border);
    background: var(--surface-2);
}
.lichtbak__titel { margin: 0; font-size: .98rem; font-weight: 600; }
/* Het kruisje helemaal rechts; .paneel-sluit heeft de vorm al, maar staat daar
   absoluut in de hoek van een uitklappaneel. Hier is het gewoon het laatste
   ding op de regel. */
.lichtbak__kop .paneel-sluit { position: static; margin-left: auto; flex: none; }

/* Het venster scrollt vanbinnen en niet vanbuiten: de knoppenrij onderaan moet
   bereikbaar blijven op een laag scherm. */
.lichtbak__body {
    padding: 16px;
    max-height: calc(100vh - 140px);
    overflow-y: auto;
}
.lichtbak__laden { margin: 0; color: var(--text-faint); }

/* De pagina erachter mag niet meescrollen zolang het venster open staat. */
body.lichtbak-open { overflow: hidden; }

/* --- De tegels op de instellingenpagina ----------------------------------
   Twee stuks nu, dus geen raster dat op tien is berekend: ze staan onder elkaar
   en gaan naast elkaar zodra er ruimte is. Een hele tegel is de link en niet
   alleen de titel — een doel van tweehonderd pixels breed mis je niet. */
.tegels {
    display: grid; gap: 14px;
    grid-template-columns: 1fr;
    max-width: 760px;
}
@media (min-width: 720px) {
    .tegels { grid-template-columns: 1fr 1fr; }
}

.tegel {
    display: flex; align-items: flex-start; gap: 14px;
    padding: 18px 20px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: var(--shadow-sm);
    color: inherit; text-decoration: none;
    transition: border-color .15s, box-shadow .15s, transform .05s;
}
.tegel:hover { border-color: var(--brand); box-shadow: var(--shadow-md); }
.tegel:active { transform: translateY(1px); }
.tegel:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.tegel__ico {
    flex: none; width: 38px; height: 38px; border-radius: 11px;
    display: grid; place-items: center;
    background: var(--brand-50); color: var(--brand);
}
.tegel__ico svg { width: 19px; height: 19px; }

.tegel__tekst { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.tegel__tekst strong { font-size: 1rem; }
.tegel__tekst small { color: var(--text-soft); font-size: .84rem; line-height: 1.45; }

/* Het pijltje rechts zegt "hier ga je heen". Het schuift een haartje mee bij
   het zweven; genoeg om te merken, te weinig om af te leiden. */
.tegel__pijl {
    flex: none; align-self: center; margin-left: auto;
    width: 8px; height: 8px;
    border-right: 2px solid var(--text-faint); border-top: 2px solid var(--text-faint);
    transform: rotate(45deg); transition: transform .15s ease, border-color .15s;
}
.tegel:hover .tegel__pijl {
    border-color: var(--brand); transform: rotate(45deg) translate(2px, -2px);
}

/* --- Omzet en kosten onder een project -----------------------------------
   Vier getallen naast elkaar, met de brutowinst er nadrukkelijker uit. Geen
   tabel: het zijn vier losse feiten en geen rijen die je met elkaar vergelijkt. */
.projectgeld {
    display: grid; gap: 2px 24px; margin: 0;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    padding: 16px 24px 20px;
    border-top: 1px solid var(--border);
    background: var(--surface-2);
}
.projectgeld dt {
    font-size: .74rem; font-weight: 600; color: var(--text-soft);
    text-transform: uppercase; letter-spacing: .04em;
}
.projectgeld dd {
    margin: 2px 0 0; font-size: 1.05rem; font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.projectgeld--uit dd { font-size: 1.3rem; color: var(--brand); }
.projectgeld__sub { font-size: .78rem; font-weight: 400; color: var(--text-soft); }

/* De taart bij Uitgevoerd: waar de uren van een project heen gingen, per
   label (src/views/taart.php). Links van de tabel, die meteen de legenda is;
   op een smal scherm erboven.

   De stukken hebben de kleur van hun label. Zonder kleur is het een grijs, en
   "Overige" een ander grijs -- allebei tokens, zodat ze in de donkere stand
   meegaan. Tussen de stukken een rand in de kleur van het vlak: zo blijven
   twee buren met een vergelijkbare kleur toch twee stukken. */
.uitgevoerd--taart {
    display: flex; flex-wrap: wrap; align-items: center;
}
.uitgevoerd--taart > .tabel-scroll { flex: 1 1 320px; min-width: 0; }
.taart {
    --taart-leeg:   var(--text-faint);
    --taart-overig: var(--border-strong);
    flex: 0 0 176px; margin: 20px 0 20px 24px;
}
.taart__vlak { display: block; width: 100%; height: auto; overflow: visible; }
.taart__stuk {
    fill: var(--stuk);
    stroke: var(--surface); stroke-width: 2; stroke-linejoin: round;
    transition: opacity .15s;
    outline: none;
}
/* Over een stuk gaan zet de andere een stap terug, zodat je ziet welk stuk
   het kaartje bedoelt. */
.taart__vlak:has(.taart__stuk:hover, .taart__stuk:focus-visible)
    .taart__stuk:not(:hover):not(:focus-visible) { opacity: .4; }
.taart__stuk:focus-visible { stroke: var(--text); }
.taart__totaal {
    fill: var(--text); font-size: 17px; font-weight: 600;
    text-anchor: middle; font-variant-numeric: tabular-nums;
}
.taart__eenheid { fill: var(--text-soft); font-size: 9px; text-anchor: middle; }

@media (max-width: 560px) {
    .taart { flex-basis: 150px; margin: 18px auto 6px; }
}

/* Een veld dat je niet kunt wijzigen: dezelfde plek als een invoerveld, maar
   zonder de rand en de achtergrond die zeggen "hier kun je typen". */
.veld-vast { padding: 6px 0; font-size: .92rem; }

@media (max-width: 480px) {
    .projectgeld { padding: 14px 18px 16px; }
}

/* --- De staafdiagrammen onder het onderhoud -------------------------------
   Twee gerangschikte lijsten met een balk erbij: waar zitten de uren, per klant
   en per label. Geen SVG en geen grafiekbibliotheek — het is een lijst met een
   balk, en dat kan de browser zelf.

   Elke balk heeft dezelfde kleur. De lengte zegt al hoe groot iets is; dat ook
   in de kleur herhalen kost de enige vrije aanwijzing die er nog is, en maakt
   van een rij namen zonder volgorde ten onrechte een rangschikking. De kleur
   komt uit --brand en volgt dus vanzelf de donkere stand. */
.grafieken {
    display: grid; gap: 22px; margin-top: 22px;
    grid-template-columns: 1fr;
}
@media (min-width: 900px) {
    .grafieken { grid-template-columns: 1fr 1fr; }
}

.grafiek {
    margin: 0; min-width: 0;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: var(--shadow-sm);
}
.grafiek__kop { padding: 18px 24px 14px; border-bottom: 1px solid var(--border); }
.grafiek__titel { font-size: 1rem; margin: 0; }
.grafiek__bij { margin: 4px 0 0; font-size: .82rem; color: var(--text-soft); }
.grafiek__leeg { margin: 0; padding: 24px; color: var(--text-soft); font-size: .88rem; }

.grafiek__lijst { margin: 0; padding: 14px 24px 20px; list-style: none; }
.grafiek__regel {
    display: grid; align-items: center; gap: 4px 14px;
    grid-template-columns: minmax(0, 11rem) minmax(0, 1fr) auto;
    padding: 5px 0;
}
.grafiek__naam {
    font-size: .84rem; color: var(--text-soft);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Het spoor is geen tweede balk maar alleen de ruimte waarin de balk staat;
   een grijze achterkant erbij zou net zo hard meelezen als de meting zelf. */
.grafiek__spoor { display: block; min-width: 0; }
.grafiek__staaf {
    display: block; height: 10px; min-width: 2px;
    background: var(--brand);
    /* Vierkant waar hij begint, rond waar hij eindigt: de nul is een lijn en
       geen vorm, het uiteinde is de meting. */
    border-radius: 0 4px 4px 0;
}
.grafiek__waarde {
    font-size: .82rem; font-variant-numeric: tabular-nums;
    color: var(--text); white-space: nowrap;
}

@media (max-width: 560px) {
    /* Te smal voor drie kolommen: de naam gaat boven de balk staan en het
       getal ernaast, zodat de balk zelf ruimte houdt om iets te zeggen. */
    .grafiek__regel {
        grid-template-columns: minmax(0, 1fr) auto;
        padding: 7px 0;
    }
    .grafiek__naam { grid-column: 1; }
    .grafiek__waarde { grid-column: 2; grid-row: 1; }
    .grafiek__spoor { grid-column: 1 / -1; }
    .grafiek__kop { padding: 16px 18px 12px; }
    .grafiek__lijst { padding: 12px 18px 16px; }
}

/* --- Uren per week en per dag, per medewerker -----------------------------
   Onder de twee lijsten hierboven: dezelfde kaart, maar staande staven. Dit is
   een reeks in de tijd en tijd loopt van links naar rechts; liggend zou de
   volgorde van de vakken verticaal komen te staan en dat leest niemand zo.

   Eén opmaak voor beide blokken -- de weken en de dagen eronder -- want het is
   dezelfde grafiek met andere vakken erin. Zie src/views/tijdgrafiek.php.

   De schaal is voor alle medewerkers in hetzelfde blok dezelfde -- die komt van
   buiten mee -- dus een staaf in de ene grafiek is even hoog als dezelfde uren
   in de andere. Dat is de hele reden om ze naast elkaar te zetten. Tussen de
   twee blokken verschilt de schaal juist wel: acht uur op een dag en veertig op
   een week passen niet op één liniaal. */
.urenblok { margin-top: 30px; }
.urenblok__titel { font-size: 1rem; margin: 0 0 5px; }
.urenblok__bij {
    margin: 0 0 16px; max-width: 74ch;
    font-size: .82rem; line-height: 1.5; color: var(--text-soft);
}
.urenblok .grafieken { margin-top: 0; }

.tijdgrafiek__vlak { padding: 18px 22px 16px 46px; }

/* Het vlak waar de staven in staan; het raster ligt er precies overheen. */
.tijdgrafiek__plot { position: relative; height: 170px; }

.tijdgrafiek__raster {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; justify-content: space-between;
}
.tijdgrafiek__lijn { position: relative; height: 0; border-top: 1px solid var(--border); }
/* De onderste lijn is de nullijn en mag steviger. */
.tijdgrafiek__lijn:last-child { border-top-color: var(--border-strong); }
.tijdgrafiek__aslabel {
    position: absolute; left: -46px; top: -.62em;
    width: 38px; text-align: right;
    font-size: .68rem; color: var(--text-faint);
    font-variant-numeric: tabular-nums;
}

.tijdgrafiek__staven {
    position: absolute; inset: 0;
    display: flex; align-items: flex-end;
    margin: 0; padding: 0; list-style: none;
}
/* De hele kolom is het doel van de muis en niet alleen de staaf: een week
   zonder uren heeft geen staaf om op te mikken, en juist dan wil je lezen dat
   er niets staat. De tussenruimte zit in de kolom zelf, zodat elk vak exact
   even breed blijft en het nummer eronder precies onder zijn eigen staaf
   staat. */
.tijdgrafiek__vak {
    position: relative;
    flex: 1 1 0; min-width: 0; height: 100%; padding: 0 3px;
    display: flex; align-items: flex-end;
}
/* Het weekend, over de volle hoogte gearceerd. Zaterdag en zondag zijn bijna
   altijd leeg, en zonder die arcering leest zo'n gat als een dag waarop er niet
   geschreven is in plaats van een dag waarop er niet gewerkt is.

   Streepjes en geen vlak: een grijs vlak achter de staven verandert de kleur
   van de staaf die er toevallig in staat, en juist een zaterdag met uren erop
   wil je onveranderd zien. De arcering ligt binnen dezelfde 3 pixels marge als
   de staaf, zodat hij precies zo breed is en de rasterlijnen er tussen de
   streepjes doorheen blijven lopen. */
.tijdgrafiek__vak--stil::before {
    content: ""; position: absolute; top: 0; bottom: 0; left: 3px; right: 3px;
    border-radius: 3px;
    background: repeating-linear-gradient(
        -45deg,
        color-mix(in srgb, var(--border) 70%, transparent) 0 1px,
        transparent 1px 7px
    );
}
/* Boven de arcering: een staaf op zaterdag hoort niet half weg te vallen. */
.tijdgrafiek__staaf {
    position: relative;
    display: block; width: 100%; min-height: 2px;
    border-radius: 4px 4px 0 0;
    background: var(--brand);
}
/* De lopende week -- of vandaag -- is nog niet af. Lichter, zodat die laatste
   lagere staaf niet als een terugval leest. */
.tijdgrafiek__staaf--loopt {
    background: color-mix(in srgb, var(--brand) 38%, var(--surface));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 55%, var(--surface));
}

/* ---- De week- en dagnummers eronder ---- */
.tijdgrafiek__labels {
    display: flex; margin: 7px 0 0; padding: 0; list-style: none;
}
.tijdgrafiek__label {
    flex: 1 1 0; min-width: 0; padding: 0 3px;
    font-size: .68rem; color: var(--text-faint);
    font-variant-numeric: tabular-nums;
    text-align: center; overflow: hidden;
}
/* De lopende week of vandaag: hetzelfde getal, maar je moet hem kunnen
   terugvinden. */
.tijdgrafiek__label--nu { color: var(--text-soft); font-weight: 650; }
/* Zaterdag en zondag. Een lege staaf op zondag is de gewone gang van zaken en
   geen gemiste dag; dat moet je onder de grafiek kunnen zien zonder te tellen. */
.tijdgrafiek__label--stil { opacity: .45; }

@media (max-width: 560px) {
    .tijdgrafiek__vlak { padding: 16px 14px 14px 40px; }
    .tijdgrafiek__aslabel { left: -40px; width: 32px; }
}

/* --- De maandstand boven het onderhoud ------------------------------------
   Eén getal groot, de opbouw eronder klein. Wie deze pagina opent wil weten
   waar de maand staat; de verdeling per medewerker is de controlevraag daarna.
   Dezelfde verhouding als .totaalblok onder het onderhoud in het portaal. */
.maandstand {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: var(--shadow-sm);
    margin-bottom: 22px; overflow: hidden;
}
.maandstand__kop { padding: 18px 24px; }
.maandstand__label {
    margin: 0; font-size: .78rem; font-weight: 600;
    text-transform: uppercase; letter-spacing: .04em; color: var(--text-soft);
}
.maandstand__bedrag { margin: 4px 0 0; font-size: 1.9rem; font-weight: 650; line-height: 1.1; }
.maandstand__sub { margin: 4px 0 0; font-size: .84rem; color: var(--text-soft); }

.maandstand__wie {
    margin: 0; padding: 12px 24px 16px; list-style: none;
    border-top: 1px solid var(--border); background: var(--surface-2);
}
.maandstand__wie li {
    display: flex; align-items: baseline; gap: 10px;
    padding: 4px 0; font-size: .88rem;
}
/* De naam duwt de cijfers naar rechts; die staan daardoor onder elkaar en zijn
   te vergelijken zonder te lezen. */
.maandstand__naam { flex: 1; min-width: 0; }
.maandstand__geld { font-variant-numeric: tabular-nums; font-weight: 600; }
.maandstand__uren {
    font-variant-numeric: tabular-nums; color: var(--text-soft);
    min-width: 9ch; text-align: right;
}

/* Naam, bedrag en tijd vlak bij elkaar in plaats van uitgesmeerd over de hele
   breedte. Bij twee medewerkers onder een blok van bijna een meter breed staat
   het bedrag anders een halve pagina van de naam vandaan, en dan lees je niet
   meer wat bij wie hoort.

   De kolommen zijn zo breed als hun inhoud (max-content) en het rijtje begint
   links. De <li> is display: contents, zodat de drie spans zelf de rastercellen
   zijn: zo staan de bedragen van beide regels alsnog onder elkaar -- daar ging
   het bij het uitsmeren om -- zonder dat ze naar de rand geduwd worden.

   Alleen hier: bij Openstaand staat er een rij knoppen in dezelfde lijst, en
   bij Balans staan er maanden in plaats van uren. Die houden hun eigen regel. */
.maandstand__wie--dicht {
    display: grid;
    grid-template-columns: minmax(0, max-content) max-content max-content;
    justify-content: start; align-items: baseline;
    column-gap: 20px;
}
.maandstand__wie--dicht li { display: contents; }
.maandstand__wie--dicht li > span { padding: 4px 0; }
.maandstand__wie--dicht .maandstand__geld,
.maandstand__wie--dicht .maandstand__uren { text-align: right; }
.maandstand__wie--dicht .maandstand__uren { min-width: 0; }

@media (max-width: 480px) {
    .maandstand__kop { padding: 16px 18px; }
    .maandstand__wie { padding: 10px 18px 14px; }
    .maandstand__bedrag { font-size: 1.6rem; }
}

/* --- Het formulier in kolommen -------------------------------------------
   Een klant heeft twintig velden. Onder elkaar is dat een pagina van bijna
   twee schermen, waarin je moet scrollen om van de bedrijfsnaam naar het
   uurtarief te komen; naast elkaar past het in één blik.

   Alleen hier en niet op .form in het algemeen: de inlogschermen zijn smalle
   kaarten waar een tweede kolom niet in past.

   auto-fit met een ondergrens zou zichzelf regelen, maar .content--breed laat
   de breedte helemaal los — op een groot scherm werden dat vier kolommen van
   een halve meter. Vandaar vaste stappen en een bovengrens op het raster
   zelf. */
.form--raster { max-width: 1180px; }

/* Het mailvenster bij een factuur: vier velden, waarvan twee naast elkaar
   passen. Een derde kolom zou een gat opleveren, en in de lichtbak is de
   breedte toch al beperkt. */
.mailform { max-width: 760px; }
.form--raster > .alert,
.form--raster > .mailform__voet { grid-column: 1 / -1; }

@media (min-width: 760px) {
    .form--raster {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-items: start;
        gap: 16px 22px;
    }
    /* Het tekstvak en de knoppenrij lopen over alle kolommen. */
    .form--raster > .field--breed,
    .form--raster > .form__acties { grid-column: 1 / -1; }

    /* Een vinkje heeft geen label erboven, dus het zou tegen de bovenkant van
       de rij plakken terwijl de velden ernaast daar hun kopje hebben staan.
       Onderaan uitlijnen zet het op één lijn met de invoervelden. */
    .form--raster > .checkbox { align-self: end; padding-bottom: 9px; }
}

@media (min-width: 1240px) {
    .form--raster { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .mailform.form--raster { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Verwijderen is geen tegenhanger van opslaan; het krijgt daarom geen
   opvallende knop maar wel een duidelijke kleur zodra je er bent. */
.crm-verwijder { color: var(--error-tx); }
.crm-verwijder:hover { background: var(--error-bg); border-color: var(--error-bd); }

/* ---- De lijst op een telefoon --------------------------------------------
   Onder 640px wordt elke rij een kaartje met de kolomkop vóór elke waarde;
   dat regelt tabel-mobiel.js samen met .tabel--mobiel in de stijl hierboven.
   Wat hier staat is wat een CRM-lijst daar nog extra nodig heeft. */
@media (max-width: 640px) {
    /* De naam is de kop van het kaartje: groter, zonder label ervoor, en over
       de volle breedte. Zonder dit staat er "Bedrijfsnaam" voor de naam, en dat
       is precies de regel die je juist wilt kunnen scannen.

       De selector draagt hier :not(.beheer):not(.firewall) mee, niet omdat het
       CRM die klassen kent maar om boven de algemene kaartregels uit te komen.
       Die staan op .tabel--mobiel:not(.beheer):not(.firewall) td[data-label] en
       zijn daarmee specifieker dan een gewone .crm-tabel-regel: het label bleef
       anders links staan en de naam liep er dwars overheen.

       Het label weghalen in de HTML kan niet: tabel-mobiel.js zet data-label op
       elke cel waarvan de kolomkop tekst heeft, en die kop is op een breed
       scherm juist nodig.

       Het gaat om .crm-naam en niet om de eerste cel: bij hosting, domeinen en
       abonnementen staat daar de startdatum, en de naam — het domein, de
       omschrijving — een paar kolommen verderop. Zie Module::naamveld(). */
    .crm-tabel.tabel--mobiel:not(.beheer):not(.firewall) tbody td.crm-naam {
        padding-left: 0; padding-bottom: 8px;
        font-size: 1rem; line-height: 1.35;
    }
    .crm-tabel.tabel--mobiel:not(.beheer):not(.firewall) tbody td.crm-naam::before {
        content: none;
    }

    /* En de kop hoort bovenaan het kaartje te staan, ook als die cel in de
       tabel verderop komt. Een kolom als flexrij zetten kan hier zonder
       gevolgen: de cellen zijn al blokken, en het label ernaast staat absoluut
       binnen zijn eigen cel. */
    .crm-tabel.tabel--mobiel:not(.beheer):not(.firewall) tbody tr {
        display: flex; flex-direction: column;
    }
    .crm-tabel.tabel--mobiel:not(.beheer):not(.firewall) tbody td.crm-naam {
        order: -1;
    }
    /* Op een kaartje staat elke cel op een eigen regel; een vaste kolombreedte
       zou daar de waarde afknijpen. */
    .crm-tabel.tabel--mobiel:not(.beheer):not(.firewall) tbody td.kol-datum { width: auto; }

    /* Hier stond de knop Bewerken, die op een kaartje niets toevoegde naast de
       naam erboven. Dupliceren doet dat wel, dus die cel blijft nu staan — met
       het pictogram rechts op de regel in plaats van onder de gegevens.

       Op de cel met de knop erin en niet op de laatste cel: niet elke tabel
       eindigt met een knop. Bij de VPS-lijst staat daar "Zichtbaar voor", en
       die kwam zo boven op de naam terecht. */
    .crm-tabel.tabel--mobiel:not(.beheer):not(.firewall) tbody td.crm-actie {
        position: absolute; top: 8px; right: 12px;
        padding: 0; width: auto;
    }
    .crm-tabel.tabel--mobiel:not(.beheer):not(.firewall) tbody tr { position: relative; }
    /* Op een kaartje draagt de rij zelf de achtergrond: de cellen zijn daar
       blokken zonder eigen vlak, dus een tint op de cel zou niet doorlopen. */
    .crm-tabel.tabel--mobiel:not(.beheer):not(.firewall) tbody tr.crm-vandaag {
        background: color-mix(in srgb, var(--brand-50) 55%, var(--surface));
    }
    .crm-tabel.tabel--mobiel:not(.beheer):not(.firewall) tbody tr.crm-vandaag td {
        background: none;
    }
    /* De streep van het label hoort hier langs het hele kaartje en niet langs
       de eerste cel: die is op een kaartje één regel ergens in het midden. */
    .crm-tabel.tabel--mobiel:not(.beheer):not(.firewall) tbody tr.crm-rijkleur {
        box-shadow: inset 4px 0 0 var(--rijkleur);
    }
    .crm-tabel.tabel--mobiel:not(.beheer):not(.firewall) tbody tr.crm-rijkleur td:first-child {
        box-shadow: none;
    }
    /* Ruimte vrijhouden, anders loopt een lange naam onder het pictogram door —
       maar alleen in een rij die zo'n pictogram heeft. */
    .crm-tabel.tabel--mobiel:not(.beheer):not(.firewall) tbody tr:has(td.crm-actie) td.crm-naam {
        padding-right: 40px;
    }

    /* Het zoekvak vult wat er naast de zoekknop overblijft. Geen 100%: dan
       staat de knop op een eigen regel, en die twee horen bij elkaar. */
    .filterbalk .field { flex: 1 1 auto; }
}

/* Bladeren onder een lange lijst: op een telefoon mogen de knoppen afbreken.
   De woorden zijn daar al weg (zie .pager__woord), dus meestal past het op één
   regel; deze regel is er voor als dat net niet lukt. */
@media (max-width: 480px) {
    .pager { flex-wrap: wrap; gap: 8px; }
}

/* ------------------------------------------------------------------ *
 * De verzendrun: alle facturen die nog niet gemaild zijn achter elkaar.
 * ------------------------------------------------------------------ */

/* Een rij waar iets aan mankeert: die doet niet mee en hoort op te vallen
   zonder dat je hem eerst hoeft te lezen. Ook de bankimport gebruikt dit. */
.rij--let-op > td { background: var(--warn-bg); }

/* Een cel met een gegeven dat er wel bij hoort maar waar je niet naar zoekt:
   het adres waar de mail heen gaat, de bijlagen die eraan komen te hangen. */
.crm-zacht { color: var(--text-soft); font-size: .86rem; }

/* Het voorbeeld van de mailtekst, met [AANHEF] en [payurl] er nog in. Even
   breed als een echte mail ongeveer is, zodat de regellengte klopt. */
.mailrun__proef {
    margin: 14px 0 0; padding: 14px 16px; max-width: 62ch;
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    background: var(--surface-2); color: var(--text-soft);
    font-size: .88rem; line-height: 1.55;
}
.mailrun__proef > :first-child { margin-top: 0; }
.mailrun__proef > :last-child { margin-bottom: 0; }

/* De statuskolom. Breed genoeg voor "Gemaild naar naam@voorbeeld.nl · in Yuki",
   want dat is de regel die er straks in de meeste rijen staat. */
.admin-table th.kol-stand, .admin-table td.kol-stand { min-width: 210px; }

.stand {
    display: inline-flex; align-items: center; gap: 7px;
    font-size: .82rem; line-height: 1.35;
}

/* Een rondje vóór de tekst: op een halfvolle tabel zie je aan de kolom rondjes
   hoe ver de run is, zonder de meldingen te lezen. */
.stand::before {
    content: ''; flex: none; width: 8px; height: 8px; border-radius: 50%;
    background: currentColor;
}
.stand--wacht { color: var(--text-faint); }
.stand--bezig { color: var(--brand); }
.stand--ja    { color: var(--success-tx); }
.stand--nee   { color: var(--error-tx); }
.stand--half  { color: var(--warn-tx); }

/* Bezig: een rondje dat pulseert. Niet meer dan dat -- er staan er dertig onder
   elkaar en dertig draaiende dingen is een kermis. */
.stand--bezig::before { animation: standpuls 1s ease-in-out infinite; }
@keyframes standpuls { 50% { opacity: .25; } }

@media (prefers-reduced-motion: reduce) {
    .stand--bezig::before { animation: none; }
}

/* ------------------------------------------------------------------ *
 * De tarieven voor hosting en domeinen (Instellingen).
 * ------------------------------------------------------------------ */

/* Twee smalle kolommen met een invoerveld erin. De bedragkolom is smal genoeg
   dat de bedragen onder elkaar staan en je ze kunt vergelijken -- daar kijk je
   op deze pagina naar. */
.admin-table td.kol-bedrag    { width: 130px; }
.admin-table td.kol-extensie  { width: 180px; }
.kol-bedrag input, .kol-extensie input { width: 100%; }

/* Een bedrag rechts uitgelijnd en met cijfers van gelijke breedte: anders
   springen de kommas van regel tot regel en telt het oog niet mee. */
.bedragveld { text-align: right; font-variant-numeric: tabular-nums; }

/* Het minimumtarief en de ondergrens: twee velden die bij elkaar horen en
   samen op één regel passen zolang er ruimte is. */
.prijzen__paar {
    display: grid; gap: 18px; margin-top: 20px;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

/* ---- De twee tabellen met invoervelden op een telefoon -------------------
   Onder 640px wordt elke rij een kaartje met de kolomkop links ernaast, op 34%
   van de breedte. Voor een cel met tekst klopt dat; voor een invoerveld niet --
   dan blijft er een derde van een telefoonscherm over om in te typen, en de
   vaste kolombreedtes hierboven knijpen het veld daarbinnen nog eens af.

   Dus: het kopje bóven het veld, en het veld over de volle breedte. De
   :not()-en staan er om boven de algemene kaartregels uit te komen; zie de
   toelichting daar. */
@media (max-width: 640px) {
    .tabel--velden.tabel--mobiel:not(.beheer):not(.firewall) tbody td.kol-bedrag,
    .tabel--velden.tabel--mobiel:not(.beheer):not(.firewall) tbody td.kol-extensie {
        width: auto;
        padding: 20px 0 6px;
    }
    .tabel--velden.tabel--mobiel:not(.beheer):not(.firewall) tbody td.kol-bedrag::before,
    .tabel--velden.tabel--mobiel:not(.beheer):not(.firewall) tbody td.kol-extensie::before {
        top: 0; width: auto;
    }

    /* Een bedrag rechts uitlijnen heeft alleen zin als de bedragen onder elkaar
       staan. In een kaartje staat er één, en dan hoort hij links te beginnen
       net als elk ander veld. */
    .tabel--velden .bedragveld { text-align: left; }

    /* Een lege cel is op een breed scherm een lege cel; op een kaartje zou hij
       een kopje krijgen met niets erachter. */
    .tabel--velden.tabel--mobiel:not(.beheer):not(.firewall) tbody td:empty { display: none; }

    /* 16px, want daaronder zoomt Safari op iOS bij het aantikken van een veld
       in en komt de pagina scheef te staan. */
    .prijzen input { font-size: 16px; }

    /* Op een telefoon staan de knoppen onder elkaar over de volle breedte:
       naast elkaar vallen "Tarieven opslaan" en "Terug naar standaard" anders
       half af, en die tweede wil je niet per ongeluk raken. */
    .prijzen .pager { flex-direction: column; align-items: stretch; }
    .prijzen .pager .btn { width: 100%; justify-content: center; }
}

/* De volgorde waarin een domeinprijs wordt bepaald. Genummerd, want het is een
   volgorde en geen opsomming: de eerste die iets oplevert wint. */
.prijzen__volgorde {
    margin: 0 0 16px; padding-left: 22px;
    color: var(--text-soft); font-size: .92rem; line-height: 1.6;
}
.prijzen__volgorde li { margin-bottom: 4px; }
.prijzen__volgorde strong { color: var(--text); font-weight: 600; }

/* ========================= Storingen ======================================
   Boven de lijst met onze eigen meldingen staat wat HetrixTools ziet. Alleen
   wat eruit ligt krijgt een kaartje: twintig monitors die het gewoon doen zijn
   geen nieuws en zouden het ene dat stuk is wegdrukken. */

/* Rood naast de bestaande groen (--ok) en oranje (--open). Bij een storing is
   dat het verschil tussen "let op" en "stuk". */
.status--fout { background: var(--error-bg); color: var(--error-tx); border-color: var(--error-bd); }

/* Deze klasse werd al gebruikt — bij de firewall, de planning en de
   herinneringen — maar was nooit gedefinieerd; zo'n melding kwam er dus uit als
   een gewone blauwe. */
.alert--warn { background: var(--warn-bg); color: var(--warn-tx); border-color: var(--warn-bd); }

.monitorstand { margin-bottom: 20px; }

.monitorlijst { display: flex; flex-direction: column; gap: 12px; }

.monitor {
    border: 1px solid var(--border);
    border-left: 3px solid var(--error-tx);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
}
/* Verborgen voor de klant: wel te zien, maar duidelijk terzijde. */
.monitor--verborgen { border-left-color: var(--border); opacity: .72; }

.monitor__kop { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 4px; }
.monitor__kop h3 { margin: 0; font-size: .98rem; }

.monitor__meta   { margin: 0; color: var(--text-soft); font-size: .86rem; }
/* Het adres van een server is een technisch gegeven; cijfers van gelijke
   breedte lezen dan prettiger, net als bij een bedrag. */
.monitor__doel   { font-variant-numeric: tabular-nums; }
.monitor__fouten { margin: 6px 0 0; color: var(--text-faint); font-size: .82rem; display: block; }
.monitor__rustig { margin: 0; color: var(--text-soft); }
.monitor__knop   { margin-top: 10px; }
/* In een tabelcel staat de knop al op zijn plek; daar hoort geen extra ruimte
   boven. */
.crm-tabel .monitor__knop { margin-top: 0; }

/* Een regel die de klant niet ziet, zakt naar de achtergrond zonder te
   verdwijnen: hij hoort nog wel in het logboek. */
.rij--flauw td { opacity: .6; }

.monitor-log { margin: 0 24px 20px; }

/* ---- Het datum- en tijdveld ---- */
/* De maten komen van het gewone invoerveld hierboven; hier staat alleen wat
   een native kiezer nodig heeft en een tekstveld niet.

   color-scheme is het belangrijkste: zonder die regel tekent de browser het
   kalendertje, de klok en het uitklapvenster altijd in lichte kleuren. Op een
   donker veld is dat een zwart icoontje dat je niet ziet en een wit
   kalendervenster dat uit de toon valt. Met "light dark" volgt de kiezer
   dezelfde voorkeur als de rest van deze stylesheet -- zie de
   prefers-color-scheme-blokken bovenaan. */
input[type=datetime-local] {
    color-scheme: light dark;
    /* Cijfers van gelijke breedte: anders verspringt "06-09-2026 09:20" bij
       elke minuut die je omhoog klikt. Net als bij een bedrag. */
    font-variant-numeric: tabular-nums;
}
/* Het kalendertje rechts in het veld is een knop; laat de muisaanwijzer dat ook
   zeggen, en geef hem dezelfde ruimte als de tekst ernaast. */
input[type=datetime-local]::-webkit-calendar-picker-indicator {
    cursor: pointer;
    opacity: .55;
    padding: 2px;
    margin-left: 6px;
}
input[type=datetime-local]::-webkit-calendar-picker-indicator:hover { opacity: 1; }
/* Het veldje waar de aandacht in staat (de dag, de maand, het uur) krijgt de
   merkkleur in plaats van het grijsblauw van het besturingssysteem. */
input[type=datetime-local]::-webkit-datetime-edit-day-field:focus,
input[type=datetime-local]::-webkit-datetime-edit-month-field:focus,
input[type=datetime-local]::-webkit-datetime-edit-year-field:focus,
input[type=datetime-local]::-webkit-datetime-edit-hour-field:focus,
input[type=datetime-local]::-webkit-datetime-edit-minute-field:focus {
    background: var(--brand);
    color: var(--brand-ink);
    border-radius: 3px;
    outline: none;
}

/* --- Cronjobs en koppelingen (processen.php) -----------------------------
   Drie tabellen met veel korte technische regels erin: een cronnotatie, een
   pad, een klassenaam, de laatste regel uit een log. Die horen kleiner en
   zachter dan de tekst eromheen -- het is naslag, geen leestekst -- en een pad
   mag afbreken, anders duwt /var/www/vhosts/... de kolom uit het scherm. */
.proces-klein {
    display: inline-block;
    font-size: .78rem;
    color: var(--text-faint);
    line-height: 1.45;
    overflow-wrap: anywhere;
}
/* Een cronnotatie leest alleen als de vijf velden even breed zijn. */
code.proces-klein { font-variant-numeric: tabular-nums; }

/* --- Chatgesprekken (chatgesprekken.php, views/chatgesprek.php) -----------
   Hetzelfde beeld als het venster op de site: de bezoeker rechts, de bot
   links. Zo lees je het gesprek zoals de bezoeker het zag. Wat de bot opzocht
   en waar het misging staat klein boven en onder het antwoord: dat is de
   reden dat je hier kijkt als een antwoord niet deugde, maar het is niet het
   gesprek zelf. */
.chatlog { display: flex; flex-direction: column; gap: 14px; padding: 20px 24px 24px; }

.chatlog__bericht { max-width: min(46rem, 88%); }
.chatlog__bericht--bezoeker { align-self: flex-end; }
.chatlog__bericht--bot      { align-self: flex-start; }

.chatlog__meta {
    margin: 0 0 4px;
    font-size: .78rem;
    color: var(--text-faint);
}
.chatlog__bericht--bezoeker .chatlog__meta { text-align: right; }

/* pre-wrap: de bot schrijft opsommingen en alinea's met gewone regeleinden,
   en die horen te blijven staan. */
.chatlog__tekst {
    padding: 10px 14px;
    border-radius: var(--radius-sm);
    line-height: 1.5;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    background: var(--surface-2);
    border: 1px solid var(--border);
}
.chatlog__bericht--bezoeker .chatlog__tekst {
    background: var(--brand-50);
    border-color: transparent;
}

.chatlog__hulp {
    margin: 0 0 6px;
    padding: 0;
    list-style: none;
    font-size: .8rem;
    color: var(--text-soft);
}
.chatlog__hulp li::before { content: "↳ "; color: var(--text-faint); }

.chatlog__fout {
    margin: 6px 0 0;
    font-size: .84rem;
    color: var(--error-tx);
}

@media (max-width: 640px) {
    .chatlog { padding: 16px; }
    .chatlog__bericht { max-width: 100%; }
}

/* =====================================================================
   De planning: de schakelaar en het raster

   Een rij per monteur, een kolom per dag. De naamkolom en de dagenkop
   blijven staan bij het schuiven; met achtentwintig rijen en eenendertig
   kolommen ben je anders kwijt naar wie of naar welke dag je kijkt.
   ===================================================================== */

.beeldbalk {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
    margin: 18px 0 14px;
}

/* De drie standen als één knop met drie vakken: zo lees je in één blik
   welke er aanstaat en wat de andere twee zijn. */
.beeldkeuze {
    display: inline-flex; gap: 2px; padding: 3px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 999px; box-shadow: var(--shadow-sm);
}
.beeldkeuze__knop {
    padding: 6px 15px; border-radius: 999px; text-decoration: none;
    font-size: .86rem; font-weight: 550; color: var(--text-soft);
    transition: background .15s ease, color .15s ease;
}
.beeldkeuze__knop:hover { background: var(--surface-2); color: var(--text); }
.beeldkeuze__knop.is-actief { background: var(--brand); color: var(--brand-ink); }
.beeldkeuze__knop:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.beeldbalk__bladeren { display: inline-flex; gap: 6px; }

/* Het logboek staat uiterst rechts, los van de knoppen waarmee je door de
   weken bladert: het is geen andere kijk op dezelfde planning maar een andere
   pagina. Boven de lijst -- waar de titel met zijn flex ontbreekt -- duwt
   deze marge hem daar ook naartoe. */
.beeldbalk__logboek { margin-left: auto; }
.beeldbalk__titel { margin: 0; flex: 1 1 auto; font-weight: 600; font-size: 1rem; }

/* De sprong naar een dag of een weeknummer. Het opschrift staat vóór het veld
   en niet erboven zoals op een formulier: dan is de hele balk één regel hoog
   en staan de velden, de knoppen en de titel op dezelfde lijn. */
.beeldbalk__spring { display: flex; align-items: center; gap: 10px; }
.beeldbalk__veld {
    display: inline-flex; align-items: center; gap: 7px;
    font-size: .82rem; font-weight: 600; color: var(--text-soft);
    white-space: nowrap;
}

/* Het weeknummer: dezelfde maten als het datumveld ernaast en als .btn--sm,
   zodat de drie even hoog zijn. Breed genoeg voor twee cijfers plus het
   pijltje dat de browser erbij zet. */
.beeldbalk__veld input[type=number] {
    width: 5rem; padding: 6px 10px;
    font: inherit; font-size: .82rem; line-height: 1.55; height: calc(1.55em + 14px);
    color: var(--text); background: var(--surface);
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
    font-variant-numeric: tabular-nums;
    transition: border-color .15s, box-shadow .15s, background .15s;
}
.beeldbalk__veld input[type=date] { width: 9.5rem; }

/* Het raster scrollt in beide richtingen binnen het paneel: horizontaal
   omdat een maand eenendertig kolommen heeft, verticaal omdat de kop
   anders nergens aan kan blijven hangen -- sticky richt zich naar de
   dichtstbijzijnde bak die scrollt. */
/* De hoogte komt van assets/js/raster.js, dat meet hoeveel er onder de balk
   overblijft tot de onderkant van het venster -- zo staat de legenda er net
   boven en scrollt de pagina zelf niet. Zonder dat script is het deze
   schatting, en dan scrollt de pagina gewoon zoals elke andere. */
.raster-scroll {
    overflow: auto; overscroll-behavior: contain;
    max-height: var(--rasterhoogte, min(74vh, 880px));
}

.raster {
    border-collapse: separate; border-spacing: 0; width: 100%;
    font-size: .8rem; background: var(--surface);
}
/* Een week heeft acht kolommen en hoeft geen breed scherm vol te maken: op
   een monitor van tweeduizend pixels wordt een dagkolom anders zo breed dat
   je hem niet meer als dag leest. De maand kan juist niet smal genoeg en
   schuift. */
.raster--week  { max-width: 1400px; }
.raster--maand { min-width: 2200px; }

.raster th, .raster td {
    border-right: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
    vertical-align: top;
}
.raster tr > *:last-child { border-right: 0; }

.raster thead th {
    position: sticky; top: 0; z-index: 3;
    background: var(--surface-2); color: var(--text-soft);
    padding: 7px 6px; font-weight: 600; text-align: center;
    white-space: nowrap;
}
.raster__dagnaam { display: block; font-size: .72rem; text-transform: uppercase;
                   letter-spacing: .04em; color: var(--text-faint); }
.raster__dagnr   { display: block; font-size: .95rem; color: var(--text); }

/* De hoek hoort bij allebei en moet dus boven allebei liggen. */
.raster__hoek { left: 0; z-index: 4; text-align: left !important; }

.raster__naam {
    position: sticky; left: 0; z-index: 2;
    background: var(--surface); text-align: left; font-weight: 550;
    padding: 6px 10px 6px 12px; width: 12rem; min-width: 12rem;
    border-right: 1px solid var(--border-strong);
}
.raster__hoek, .raster__naam { box-shadow: 1px 0 0 var(--border); }
.raster__naam a { color: var(--text); text-decoration: none; }
.raster__naam a:hover { color: var(--brand); text-decoration: underline; }
.raster__rij--uit .raster__naam { color: var(--text-faint); font-weight: 450; }
.raster__uit {
    display: block; font-size: .7rem; color: var(--text-faint); font-weight: 450;
}

.raster__vak {
    padding: 3px; min-width: 9.5rem; position: relative;
    background: var(--surface);
}
.raster--maand .raster__vak { min-width: 6.5rem; }

.raster .is-weekend { background: var(--weekend); }
.raster thead .is-vandaag { background: var(--brand); color: var(--brand-ink); }
.raster thead .is-vandaag .raster__dagnaam,
.raster thead .is-vandaag .raster__dagnr { color: var(--brand-ink); }
.raster tbody .is-vandaag { background: var(--brand-50); }

/* Eén klus in een vakje. De kleur komt van de status en staat als waarde in
   het style-attribuut; de letterkleur is erbij gekozen, want lichtgeel en
   donkergroen kunnen niet dezelfde hebben. Zie Veld::inkt(). */
.raster__punt {
    display: block; margin-bottom: 3px; padding: 3px 6px;
    border-radius: 6px; text-decoration: none;
    background: var(--punt, var(--surface-2));
    color: var(--punt-ink, var(--text));
    line-height: 1.25; overflow-wrap: anywhere;
}
.raster__punt:last-of-type { margin-bottom: 0; }
.raster__punt:hover { filter: brightness(.94); }
.raster__punt:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* Loopt een klus door buiten deze dag, dan lopen de blokjes tegen elkaar
   aan: vijf losse afgeronde blokken lezen als vijf klussen. */
.raster__punt.is-door-links  { border-top-left-radius: 0;  border-bottom-left-radius: 0;
                               margin-left: -3px;  padding-left: 6px; }
.raster__punt.is-door-rechts { border-top-right-radius: 0; border-bottom-right-radius: 0;
                               margin-right: -3px; padding-right: 6px; }

.raster__merk {
    display: inline-block; margin-left: 4px; padding: 0 4px;
    border-radius: 4px; background: rgba(0, 0, 0, .16);
    font-size: .68rem; font-weight: 700; vertical-align: text-bottom;
}

/* De plek waar nog niets staat is een uitnodiging. Op een muis pas bij het
   erover gaan -- anders staat het raster vol plusjes; op een aanraakscherm
   altijd, want daar is "erover gaan" niets. */
.raster__plus {
    display: block; margin-top: 2px; padding: 1px 0;
    border-radius: 6px; border: 1px dashed var(--border-strong);
    color: var(--text-faint); text-align: center; text-decoration: none;
    font-size: .8rem; line-height: 1.3;
}
.raster__plus:hover { border-color: var(--brand); color: var(--brand); }
@media (hover: hover) {
    .raster__plus { opacity: 0; }
    .raster__vak:hover .raster__plus,
    .raster__plus:focus-visible { opacity: 1; }
}

.raster-legenda {
    display: flex; flex-wrap: wrap; gap: 6px 16px;
    margin: 12px 2px 0; font-size: .8rem; color: var(--text-soft);
}
.raster-legenda__item { display: inline-flex; align-items: center; gap: 6px; }
.raster-legenda__staal {
    width: 12px; height: 12px; border-radius: 3px;
    background: var(--staal, var(--surface-2));
    border: 1px solid rgba(0, 0, 0, .15);
}
.raster-legenda__letter {
    padding: 0 4px; border-radius: 4px; background: var(--surface-2);
    border: 1px solid var(--border); font-size: .7rem; font-weight: 700;
}

@media (max-width: 860px) {
    /* Op een telefoon is het raster een schuifbaar vlak en niet een tabel
       die zich opvouwt: een week met een rij per monteur ís breed, en er
       staat een lijstweergave naast die wél smal is. */
    .raster__naam { width: 8.5rem; min-width: 8.5rem; padding-left: 8px; }
    .raster__vak { min-width: 7.5rem; }
    .beeldbalk__titel { flex: 1 1 100%; order: 3; }
    .beeldbalk__spring { flex: 1 1 100%; order: 4; }
}

/* Terwijl het volgende raster onderweg is. Alleen dimmen en niets verschuiven:
   een week ophalen duurt tientallen milliseconden, en een blok dat in die tijd
   van plaats wisselt leest als een storing. */
.is-laden { opacity: .55; transition: opacity .12s ease; cursor: progress; }
.is-laden a { pointer-events: none; }

.beeldbalk__bij { color: var(--text-faint); font-weight: 450; }
.beeldbalk__bij::before { content: "· "; }

/* Hoeveel tekst er in een blok past.

   In de week drie regels, in de maand één: een kolom van zes rem met
   "CTVrede Amsterdam · IGUS ondersteuningen met Reda Rhiana" erin zou
   anders tien regels hoog worden, en dan is een maand niet meer te
   overzien. De rest staat in het zweefkaartje. */
.raster--week .raster__punt {
    display: -webkit-box; -webkit-box-orient: vertical;
    -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden;
}
.raster--maand .raster__punt {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* De lichtbak: het planningformulier over het raster heen.

   Een <dialog>, dus de achtergrond, Escape en de aandacht die binnen het
   venster blijft komen van de browser. Wat hier staat is alleen hoe hij
   eruitziet. Zie assets/js/raster.js. */
.lichtbak {
    width: min(920px, calc(100vw - 32px));
    max-height: calc(100dvh - 48px);
    padding: 0; border: 1px solid var(--border);
    border-radius: var(--radius); background: var(--surface); color: var(--text);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}
.lichtbak::backdrop { background: rgba(10, 13, 20, .5); }

.lichtbak__vak {
    max-height: calc(100dvh - 48px); overflow: auto;
    padding: 24px 26px 26px;
}
.lichtbak__vak.is-laden { opacity: .55; cursor: progress; }
.lichtbak__laden { color: var(--text-soft); margin: 0; padding: 28px 0; text-align: center; }

.lichtbak__sluit {
    position: absolute; top: 10px; right: 12px; z-index: 2;
    width: 32px; height: 32px; padding: 0;
    border: 0; border-radius: 8px; background: transparent;
    color: var(--text-faint); font-size: 1.5rem; line-height: 1; cursor: pointer;
}
.lichtbak__sluit:hover { background: var(--surface-2); color: var(--text); }
.lichtbak__sluit:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* In de lichtbak staat de kop dichter op het formulier: het venster zegt al
   dat dit iets op zichzelf is, dus de lucht van een paginakop is te veel. */
.page-head--lichtbak { margin-bottom: 16px; padding-right: 32px; }
.page-head--lichtbak h2 { font-size: 1.25rem; font-weight: 700; margin: 0; }

/* De panelen in de lichtbak hoeven geen eigen rand: het venster is de rand. */
.lichtbak .panel {
    border: 0; box-shadow: none; background: transparent; border-radius: 0;
}
.lichtbak .panel__body { padding: 0; }
.lichtbak .panel--volgend {
    margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--border);
}

@media (max-width: 640px) {
    .lichtbak { width: 100vw; max-width: 100vw; max-height: 100dvh; border-radius: 0; border: 0; }
    .lichtbak__vak { max-height: 100dvh; padding: 20px 16px 24px; }
}

/* Slepen in het raster: een blokje overnemen naar een andere dag of monteur.

   Het blokje dat je vasthebt blijft op zijn plek staan en wordt bleek -- er
   gaat niets weg, er komt iets bij. Het vakje waar je boven zweeft krijgt een
   rand, want zonder dat is bij zeven smalle kolommen niet te zien waar je hem
   loslaat. Zie assets/js/raster.js. */
.raster__punt[draggable="true"] { cursor: grab; }
.raster__punt.is-sleept { opacity: .4; cursor: grabbing; }

.raster__vak.is-doel {
    outline: 2px dashed var(--brand); outline-offset: -2px;
    background: var(--brand-50);
}

/* ========================================================== Geschiedenis

   Wie heeft wat en wanneer aan een regel veranderd. Een lijn met stippen en
   niet een tabel: de vraag is "wat is hier gebeurd", en dat leest van boven
   naar beneden. Een tabel zou vier kolommen beloven die overal hetzelfde
   betekenen, en dat doen ze niet -- bij een verplaatsing staat er een zin, bij
   een wijziging een rijtje velden, bij een verwijdering de hele regel.

   Zie src/views/geschiedenis.php. */
.verloop { list-style: none; margin: 0; padding: 0; }

.verloop__punt {
    position: relative;
    padding: 0 0 20px 24px;
    border-left: 2px solid var(--border);
}
/* De lijn stopt bij de laatste stip; doorlopen zou een vervolg beloven. */
.verloop__punt:last-child { border-left-color: transparent; padding-bottom: 0; }

.verloop__punt::before {
    content: ''; position: absolute; left: -8px; top: 3px;
    width: 14px; height: 14px; border-radius: 50%;
    background: var(--surface); border: 3px solid var(--border-strong);
}
/* De kleur van de stip zegt wat voor soort wijziging het was, zodat je in een
   lange lijst de verplaatsingen eruit pikt zonder te lezen. */
.verloop__punt--aangemaakt::before,
.verloop__punt--gekopieerd::before { border-color: var(--success-tx); }
.verloop__punt--verplaatst::before { border-color: var(--brand); }
.verloop__punt--verwijderd::before { border-color: var(--error-tx); }
.verloop__punt--begin::before      { border-color: var(--border-strong); background: var(--border); }

.verloop__kop {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
    margin: 0 0 4px;
}
.verloop__actie { font-weight: 640; font-size: .94rem; }
.verloop__wie   { color: var(--text-soft); font-size: .9rem; }
.verloop__moment { color: var(--text-faint); font-size: .84rem; }
.verloop__bron {
    font-size: .78rem; color: var(--text-soft);
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: 999px; padding: 1px 8px;
}

.verloop__zin { margin: 0 0 6px; font-size: .9rem; color: var(--text-soft); }

.verloop__velden { list-style: none; margin: 0; padding: 0; font-size: .88rem; }
.verloop__velden li {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px;
    padding: 3px 0; border-top: 1px solid var(--border);
}
.verloop__velden li:first-child { border-top: 0; }
/* Een vaste kolom voor de veldnaam: dan staan de waarden onder elkaar en is
   in één blik te zien wát er veranderde. Onder de 520px valt dat weg -- daar
   is er geen ruimte voor twee kolommen en leest het als zinnen. */
.verloop__veld { flex: 0 0 9rem; color: var(--text-soft); }
.verloop__oud  { color: var(--text-faint); text-decoration: line-through; }
.verloop__pijl { color: var(--text-faint); }
.verloop__nieuw { font-weight: 560; }

@media (max-width: 520px) {
    .verloop__veld { flex: 1 0 100%; }
}

/* De naam van de regel in het logboekoverzicht. Een regel die weg is krijgt
   geen link maar blijft wel staan: dat hij er niet meer is, is juist wat je
   hier komt halen. */
.verloop__regel { font-weight: 560; color: var(--brand); text-decoration: none; }
.verloop__regel:hover { text-decoration: underline; }
.verloop__regel--weg { color: var(--text-soft); text-decoration: line-through; }

.verloop__meer > summary {
    cursor: pointer; font-size: .84rem; color: var(--text-soft);
    padding: 2px 0; width: fit-content;
}
.verloop__meer > summary:hover { color: var(--text); }
.verloop__meer[open] > summary { margin-bottom: 4px; }

/* ======================================================= Herhaalde planning

   Twee schermen in één blok onder het planningformulier: bij een losse regel
   de vraag óf hij zich herhaalt, bij een regel uit een reeks de vraag wat een
   wijziging raakt. Zie src/views/planning-herhaling.php. */
.form--raster > .herhaal-blok { grid-column: 1 / -1; }

.herhaal-blok {
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    background: var(--surface-2); padding: 14px 16px;
}
.herhaal-blok__kop {
    display: flex; align-items: center; gap: 8px;
    margin: 0 0 4px; font-weight: 600; font-size: .94rem;
}
/* Het pijltjesrondje: hetzelfde merkje dat in het weekoverzicht op een blok
   staat dat bij een reeks hoort. */
.herhaal-merk { display: inline-flex; width: 18px; height: 18px; color: var(--brand); }
.herhaal-merk svg { width: 100%; height: 100%; }

.herhaal-keuze { border: 0; margin: 0; padding: 0; display: grid; gap: 8px; }
.herhaal-keuze > legend {
    padding: 0; margin: 0 0 6px;
    font-size: .8rem; font-weight: 600; color: var(--text-soft);
    text-transform: uppercase; letter-spacing: .04em;
}

.radio { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; }
.radio input { width: 16px; height: 16px; margin-top: 2px; accent-color: var(--brand); }
.radio > span { display: flex; flex-direction: column; font-size: .92rem; }
/* De uitleg onder de keuze: wat er gebeurt als je hem neemt. Dat hoort erbij
   -- "deze en de volgende" zegt niet uit zichzelf dat de datums blijven
   staan. */
.radio small { color: var(--text-faint); font-size: .8rem; }

/* Een veld dat niet bij de gekozen soort herhaling hoort. [hidden] verliest
   het van de display van .field, dus dat moet hier expliciet. */
.veld-meer__raster > [hidden] { display: none; }

/* Het merkje op een blok in het weekoverzicht dat bij een reeks hoort. */
.raster__herhaal {
    display: inline-block; width: 11px; height: 11px;
    vertical-align: -1px; margin-right: 3px; opacity: .75;
}
.raster__herhaal svg { width: 100%; height: 100%; display: block; }

/* ===================================== Het formulier in de lichtbak

   Eén kolom, met het opschrift links en de waarde of het invoerveld erachter.
   Het venster is 760px breed en gaat over één planningregel; twee kolommen
   velden lezen daar als een formulier dat alle kanten op staat, terwijl je
   hier meestal één ding komt wijzigen en de rest alleen naloopt.

   Hetzelfde raster voor een invoerveld als voor een waarde die je alleen mag
   inzien (.veld-vast), zodat de twee standen van dit scherm er hetzelfde
   uitzien. Zie src/views/formulier.php en views/veld.php. */
.form--regels { display: flex; flex-direction: column; gap: 3px; }

.form--regels > .field {
    display: grid;
    grid-template-columns: 10.5rem minmax(0, 1fr);
    align-items: baseline;
    gap: 0 18px;
}

/* Strak op elkaar. Dit is een lijst om te lezen met hier en daar iets te
   wijzigen, en niet een formulier dat je van boven naar beneden invult: dan
   telt dat er veel regels tegelijk in beeld passen, en niet dat elk veld lucht
   om zich heen heeft. De velden zelf zijn daarom ook wat lager dan op een
   volle pagina. */
.form--regels .field input,
.form--regels .field textarea,
.form--regels .field .select { padding-top: 6px; padding-bottom: 6px; }
.form--regels .veld-vast { padding: 3px 0; }
.form--regels > .field > .field__label { padding-top: 6px; }
/* De uitleg onder een veld blijft hier weg. Op een volle pagina helpt ze -- daar
   vul je een regel van nul in -- maar in dit venster kom je één ding wijzigen
   aan iets dat er al staat, en dan is het bij elk veld een alinea die je al
   kent. De foutmelding blijft wél staan: die gaat over wat je net deed.

   Weg en niet grijs: een tekst die je toch niet leest, leest niemand beter als
   hij er staat. Zie views/veld.php en views/planning-herhaling.php -- die
   schrijven hem voor het gewone scherm. */
.form--regels .field__hint { display: none; }

/* De foutmelding hoort bij de waarde en niet bij het opschrift: die begint dus
   in de tweede kolom. */
.form--regels > .field > .field__fout { grid-column: 2; }

/* Een vinkje heeft zijn opschrift ernaast in plaats van erboven. Het staat op
   de plek van de waarde, zodat de rij uitlijnt met de velden eromheen. */
.form--regels > .checkbox { margin-left: calc(10.5rem + 18px); }

/* Een tekstvak mag hoger zijn dan de regel eromheen; het opschrift blijft
   bovenaan staan. */
.form--regels > .field textarea { min-height: 4.5rem; }

/* Wat over de volle breedte gaat: de ingeklapte blokken, de knoppenrij en de
   foutbalk bovenaan. */
.form--regels > .veld-meer,
.form--regels > .herhaal-blok,
.form--regels > .alert,
.form--regels > .form__acties { margin-left: 0; }

.form--regels > .form__acties {
    display: flex; gap: 10px; margin-top: 4px;
    padding-top: 12px; border-top: 1px solid var(--border);
}

/* Binnen "Meer velden" en "Herhalen" hetzelfde beeld: onder elkaar, opschrift
   ernaast. Anders zit er middenin het venster ineens weer een raster. */
.form--regels .veld-meer__raster {
    display: flex; flex-direction: column; gap: 3px;
}
.form--regels .veld-meer__raster > .field {
    display: grid; grid-template-columns: 10.5rem minmax(0, 1fr);
    align-items: baseline; gap: 0 18px;
}
.form--regels .veld-meer__raster > .field > .field__label { padding-top: 6px; }

.form--regels .veld-meer__raster > .checkbox { margin-left: calc(10.5rem + 18px); }

@media (max-width: 560px) {
    /* Geen ruimte voor twee kolommen: dan weer opschrift boven waarde, zoals
       op elk ander smal scherm. */
    .form--regels > .field,
    .form--regels .veld-meer__raster > .field { grid-template-columns: minmax(0, 1fr); }
    .form--regels > .field > .field__label { padding-top: 0; }
    .form--regels > .field > .field__fout { grid-column: 1; }
    .form--regels > .checkbox,
    .form--regels .veld-meer__raster > .checkbox { margin-left: 0; }
}
