/*
 * Components. Everything here reads from tokens.css and hard-codes no colour,
 * size or radius of its own — that is what makes the later reskin a one-file job.
 */

*, *::before, *::after { box-sizing: border-box; }

html {
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    background: var(--achtergrond);
    color: var(--tekst);
    font-family: var(--lettertype);
    font-size: var(--tekst-m);
    line-height: var(--regelhoogte);
}

h1, h2, h3, h4 {
    font-family: var(--lettertype-kop);
    line-height: var(--regelhoogte-kop);
    font-weight: var(--gewicht-vet);
    margin: 0 0 var(--ruimte-3);
    letter-spacing: -0.01em;
}

h1 { font-size: var(--tekst-2xl); }
h2 { font-size: var(--tekst-l); }
/* Een kop bínnen een paneel. Stond op de standaard van de browser, wat toevallig
   vlak bij h2 uitkwam -- en dus geen trap was. */
h3 { font-size: var(--tekst-m); }
/* Een keuze binnen een paneel: zo groot als de kop erboven, de inzet zegt de rest. */
h4 { font-size: var(--tekst-m); }

p { margin: 0 0 var(--ruimte-3); }

a { color: var(--link); text-underline-offset: 0.2em; }
a:hover { color: var(--link-donker); }

code { font-family: var(--lettertype-mono); font-size: 0.9em; }

/* The parts of the page the browser draws. Left alone they belong to no design
   system at all: a blue selection band and a black caret on a warm green
   ground are the two places where the brand simply stops. */
::selection { color: var(--op-selectie); background: var(--selectie); }
input, textarea, select { caret-color: var(--accent); }

/* A visible focus ring everywhere: on a touch screen it is rarely seen, and for
   keyboard users it is the only way to know where they are. */
:focus-visible {
    outline: 3px solid var(--focusring);
    outline-offset: 2px;
    border-radius: var(--radius-s);
}

.skip-link {
    position: absolute;
    left: -9999px;
}
.skip-link:focus {
    left: var(--ruimte-3);
    top: var(--ruimte-3);
    z-index: 10;
    background: var(--vlak-rustig);
    padding: var(--ruimte-2) var(--ruimte-3);
    border-radius: var(--radius-s);
    box-shadow: var(--schaduw-m);
}

/* ---- Layout ------------------------------------------------------------ */

/*
 * The shell: a bar down the left, the page beside it.
 *
 * Two things decide the shape of this. The bar is permanent from 60rem, which
 * is where there is room for it beside a screen that is itself a grid of
 * targets; below that it slides in over the page, because a market stall is a
 * phone held in one hand and 15rem of it spent on navigation is 15rem not
 * spent on products.
 *
 * The drawer half only exists once <html data-js> is set, which the inline
 * script in the layout does before the first paint. Without script the same
 * markup is a plain list of links at the top of the page: reachable, ugly, and
 * far better than a menu whose only handle does nothing.
 */

.schil {
    flex: 1;
    display: grid;
    /* minmax(0, 1fr) and not 1fr: a grid track sized 1fr still refuses to go
       below its content, so one wide thing on one screen -- a table, a long
       unbroken word -- would widen the whole shell and scroll the menu off the
       side of a phone. */
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
}

/* Dezelfde kleuren als de zijbalk: op een telefoon is dit dezelfde balk, alleen
   dan liggend en met de lade erachter. */
.mobielbalk {
    display: flex;
    align-items: center;
    gap: var(--ruimte-2);
    padding: var(--ruimte-2) var(--ruimte-3);
    padding-top: max(var(--ruimte-2), env(safe-area-inset-top));
    color: var(--op-zijbalk);
    background: var(--zijbalk);
    border-bottom: 1px solid var(--zijbalk-rand);
    /* Blijft staan bij het scrollen: onderaan een lange voorraadlijst is het
       menu anders een heel eind terug omhoog. Onder de mandbalk (14) en de
       waas (15), zodat een open lade of mand er gewoon overheen valt. */
    position: sticky;
    top: 0;
    z-index: 12;
}

/* Wat bovenaan vastzit en er dus niet onder hoort te schuiven -- de mand op de
   kassa, een veld dat de focus krijgt -- houdt deze hoogte vrij. */
:root {
    --mobielbalk-hoogte: calc(var(--raakvlak) + var(--ruimte-2) + max(var(--ruimte-2), env(safe-area-inset-top)) + 1px);
}

.mobielbalk__knop {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--raakvlak);
    height: var(--raakvlak);
    flex: none;
    color: inherit;
    background: none;
    border: 0;
    border-radius: var(--radius-m);
    cursor: pointer;
}
.mobielbalk__knop:hover { background: var(--zijbalk-actief); }

/* Het woordmerk draagt de app: dit is het enige merk dat een handelaar de hele
   dag ziet, en op 1.75rem verdween het naast een menuknop van dezelfde hoogte.
   De drie maten hieronder -- telefoonbalk, zijbalk, uitgelogde topbalk --
   bewegen samen, want het is één merk op drie plekken. Sinds oktober 2026 is
   het het gestapelde logo, en dat heeft meer hoogte nodig voor dezelfde
   lettergrootte dan het liggende woordmerk ervoor. Op de telefoon blijft het
   onder de 48px van de menuknop, zodat de balk niet hoger wordt. */
.mobielbalk__merk { display: flex; align-items: center; }
.mobielbalk__logo { display: block; width: auto; height: 2.75rem; }

/* Line icons, one size, coloured by whatever they sit in. */
.icoon {
    width: 20px;
    height: 20px;
    flex: none;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* ---- The bar ----------------------------------------------------------- */

.zijbalk {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-4);
    padding: var(--ruimte-4) var(--ruimte-3);
    color: var(--op-zijbalk);
    background: var(--zijbalk);
    border-right: 1px solid var(--zijbalk-rand);
}

.zijbalk__kop {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-2);
    padding: var(--ruimte-2) var(--ruimte-3) 0;
}
.zijbalk__merk { display: flex; align-items: center; }
.zijbalk__logo { display: block; width: auto; height: 3.5rem; }

.zijbalk__sluit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    flex: none;
    color: var(--op-zijbalk-zacht);
    background: none;
    border: 0;
    border-radius: var(--radius-m);
    cursor: pointer;
}
.zijbalk__sluit:hover { color: var(--op-zijbalk); background: var(--zijbalk-actief); }

/* Scrolls on its own, so a long menu never takes the page with it and the
   account block below it stays put. */
.zijbalk__rol {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.zijbalk__groep {
    margin: var(--ruimte-3) 0 var(--ruimte-1);
    padding-inline: var(--ruimte-3);
    font-size: var(--tekst-xs);
    font-weight: var(--gewicht-medium);
    color: var(--op-zijbalk-zacht);
}
.zijbalk__rol > .zijbalk__groep:first-child { margin-top: 0; }

.zijbalk__streep {
    margin: var(--ruimte-3) var(--ruimte-3);
    border: 0;
    border-top: 1px solid var(--zijbalk-rand);
}

.zijbalk__item {
    display: flex;
    align-items: center;
    gap: var(--ruimte-3);
    min-height: var(--raakvlak);
    padding: var(--ruimte-2) var(--ruimte-3);
    font-size: var(--tekst-s);
    font-weight: var(--gewicht-medium);
    color: var(--op-zijbalk-zacht);
    text-decoration: none;
    border-radius: var(--radius-m);
}
.zijbalk__item:hover { color: var(--op-zijbalk); background: var(--zijbalk-actief); }

/* Where you are: a chip a shade lighter than the bar, with the icon in the
   brand's light green. The bar itself is quiet enough that this needs no more
   than that -- filling it with the accent would put a second primary button on
   every screen. */
.zijbalk__item--hier {
    color: var(--op-zijbalk);
    background: var(--zijbalk-actief);
}
.zijbalk__item--hier .icoon { color: var(--zijbalk-accent); }

.zijbalk__item--knop {
    width: 100%;
    font-family: inherit;
    background: none;
    border: 0;
    cursor: pointer;
    text-align: left;
}

.zijbalk__voet {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-1);
    padding-top: var(--ruimte-3);
    border-top: 1px solid var(--zijbalk-rand);
}

/* Filled in by menu.js from the stored context, never rendered by PHP. */
.rekening {
    display: flex;
    align-items: center;
    gap: var(--ruimte-3);
    padding: var(--ruimte-2) var(--ruimte-3);
    background: var(--zijbalk-actief);
    border-radius: var(--radius-m);
}

.rekening__cirkel {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    flex: none;
    font-size: var(--tekst-xs);
    font-weight: var(--gewicht-vet);
    color: var(--op-selectie);
    background: var(--selectie);
    border-radius: 50%;
}

.rekening__tekst { min-width: 0; font-size: var(--tekst-s); line-height: 1.3; }
.rekening__tekst strong {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rekening__plan { font-size: var(--tekst-xs); color: var(--op-zijbalk-zacht); }

/* ---- The drawer, on a phone -------------------------------------------- */

@media (max-width: 59.999rem) {
    [data-js] .zijbalk {
        position: fixed;
        inset: 0 auto 0 0;
        z-index: 20;
        width: min(17rem, 82vw);
        padding-top: max(var(--ruimte-4), env(safe-area-inset-top));
        transform: translateX(-100%);
        visibility: hidden;
        box-shadow: var(--schaduw-m);
        transition: transform 0.18s ease, visibility 0.18s;
    }
    [data-js] .zijbalk[data-open] { transform: none; visibility: visible; }

    [data-js] .waas {
        position: fixed;
        inset: 0;
        z-index: 15;
        background: var(--waas);
    }

    /* Nothing behind the drawer scrolls while it is over the page. */
    body.geen-rol { overflow: hidden; }

    html[data-js] { scroll-padding-top: var(--mobielbalk-hoogte); }

    /* No script: the bar is simply the top of the page, and it carries the
       wordmark itself -- so the phone bar, whose only jobs are the logo and a
       handle that would now do nothing, goes away entirely. */
    html:not([data-js]) .zijbalk { border-right: 0; border-bottom: 1px solid var(--rand); }
    html:not([data-js]) .mobielbalk,
    html:not([data-js]) .zijbalk__sluit { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    [data-js] .zijbalk,
    html[data-js] body:has(.kassa) .zijbalk { transition: none; }
}

/* ---- From 60rem: the bar is furniture ---------------------------------- */

@media (min-width: 60rem) {
    .mobielbalk { display: none; }
    .waas { display: none; }

    .schil { grid-template-columns: 15.5rem minmax(0, 1fr); }

    .zijbalk {
        position: sticky;
        top: 0;
        height: 100dvh;
        padding-block: var(--ruimte-4);
    }
    /* Nothing to close: the bar is furniture from here up. */
    .zijbalk__sluit { display: none; }
}

/* ---- Behalve op de kassa, tot 80rem ------------------------------------ *
 *
 * Een liggende tablet is 64rem breed, en op de kassa staan daar drie dingen
 * naast elkaar: de balk, het raster met artikelen en de mand. Dat is krap, en
 * van de drie is de balk degene die je tijdens het afrekenen niet gebruikt. Dus
 * blijft hij daar een lade tot er echt ruimte is -- dezelfde lade als op een
 * telefoon, letterlijk dezelfde opmaak en dezelfde knop, alleen tot een andere
 * breedte. Zonder script gebeurt er niets: dan blijft de balk staan, want een
 * lade die niet open kan is erger dan een krap scherm. menu.js kent dezelfde
 * grens.
 */
@media (min-width: 60rem) and (max-width: 79.999rem) {
    html[data-js] body:has(.kassa) .mobielbalk { display: flex; }

    html[data-js] body:has(.kassa) .schil { grid-template-columns: minmax(0, 1fr); }

    html[data-js] body:has(.kassa) .zijbalk {
        position: fixed;
        inset: 0 auto 0 0;
        z-index: 20;
        width: min(17rem, 82vw);
        height: auto;
        padding-top: max(var(--ruimte-4), env(safe-area-inset-top));
        transform: translateX(-100%);
        visibility: hidden;
        box-shadow: var(--schaduw-m);
        transition: transform 0.18s ease, visibility 0.18s;
    }
    html[data-js] body:has(.kassa) .zijbalk[data-open] { transform: none; visibility: visible; }

    html[data-js] body:has(.kassa) .zijbalk__sluit { display: inline-flex; }

    html[data-js] body:has(.kassa) .waas {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 15;
        background: var(--waas);
    }
}

/* ---- The page beside it ------------------------------------------------ */

.topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-3);
    padding: var(--ruimte-3) var(--ruimte-4);
    padding-top: max(var(--ruimte-3), env(safe-area-inset-top));
    background: var(--vlak);
    border-bottom: 1px solid var(--rand);
}

.topbar__merk { display: flex; align-items: center; text-decoration: none; }
.topbar__merk img { display: block; width: auto; height: 3.5rem; }

/*
 * The work surface.
 *
 * Wide, because what sits on it is a wall of products or a table of sales, and
 * because the bar on the left already spends 15rem. Pages that are read rather
 * than scanned pull themselves back to a column with .pagina--lezen; a form
 * does the same with .formulier--lezen. Width is decided per page, not once.
 */
.inhoud {
    flex: 1;
    width: 100%;
    max-width: var(--breedte-werkblad);
    margin: 0 auto;
    padding: var(--ruimte-6) var(--ruimte-5);
    padding-bottom: max(var(--ruimte-6), env(safe-area-inset-bottom));
}

@media (max-width: 47.999rem) {
    .inhoud { padding: var(--ruimte-4) var(--ruimte-3); }
}

/* A document rather than a screen. The invoice is printed and filed, so it
   keeps the reading width the work surfaces gave up. */
.factuur .inhoud { max-width: var(--breedte-inhoud); }

/* ---- A page: a heading on the ground, panels laid on it ---------------- *
 *
 * The old shape was one card holding the whole screen, with more cards inside
 * it. A card inside a card says nothing -- both are "this belongs together",
 * and the outer one already said it about everything. So the page itself is
 * transparent now: the title sits on the ground, and each block that really is
 * one thing gets a panel of its own. The gap between them is the grouping.   */

.pagina {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-5);
    width: 100%;
    max-width: var(--breedte-pagina);
    margin-inline: auto;
}

/* Same reason as the shell above: a flex item is sized by its content unless
   it is told it may shrink. Without this, one panel holding something wide
   takes the whole page sideways with it. */
.pagina > * { min-width: 0; }

/* Pages that are read rather than scanned. */
.pagina--lezen { max-width: var(--breedte-inhoud); }

/* The till, and nothing else: a wall of products with a basket beside it is
   the one screen that earns every pixel the window has. */
.pagina--werkblad { max-width: var(--breedte-werkblad); }

/* The gap already spaced these; their own bottom margin would double it. */
.pagina > p,
.pagina > ul,
.pagina > ol { margin-bottom: 0; }
.pagina > .melding { margin: 0; }

/* A pill is as wide as its words. A flex column would otherwise stretch it
   across the whole work surface, which is the shape this rewrite got rid of. */
.pagina > .sync { align-self: flex-start; }

.paginakop {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-3) var(--ruimte-4);
}

.paginakop h1 { margin: 0; }

.paginakop__tekst { flex: 1 1 18rem; min-width: 0; }

.paginakop__sub {
    max-width: 62ch;
    margin: var(--ruimte-1) 0 0;
    font-size: var(--tekst-s);
    color: var(--tekst-zacht);
}

/* Pushed right even when the heading is long enough to wrap them onto a line
   of their own — a row of buttons drifting to the left edge reads as a second
   heading. */
.paginakop__acties {
    margin-left: auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ruimte-2);
}

.voetregel {
    padding: var(--ruimte-4);
    padding-bottom: max(var(--ruimte-4), env(safe-area-inset-bottom));
    text-align: center;
    color: var(--tekst-zacht);
    font-size: var(--tekst-xs);
}
.voetregel p { margin: 0; }

.page-centered {
    display: grid;
    place-items: center;
    min-height: 100dvh;
    padding: var(--ruimte-4);
}

/* ---- Card and panels --------------------------------------------------- */

/*
 * .kaart is the card a page IS: a login form, a confirmation, one setting with
 * its explanation. It centres itself and stays at reading width. Inside the
 * application, where a screen holds several blocks, the page is a .pagina and
 * the blocks are .paneel -- see the note above .pagina.
 */
.kaart, .card {
    background: var(--vlak);
    border: 1px solid var(--paneel-rand);
    border-radius: var(--radius-xl);
    padding: var(--ruimte-5);
    box-shadow: var(--schaduw-paneel);
}

.kaart--smal {
    max-width: var(--breedte-smal);
    margin: 0 auto;
}

/* Een instellingenpagina is een formulier met uitleg, geen inlogvenster: op
   26rem stond er een kolom van een derde met een halve pagina wit ernaast, en
   een adresveld past er niet fatsoenlijk in. Leesbreedte, dezelfde die
   .pagina--lezen en .formulier--lezen aanhouden -- breder dan dit wordt een
   invoerveld iets dat je niet meer in één blik overziet. */
.kaart--lezen {
    max-width: var(--breedte-inhoud);
    margin: 0 auto;
}

/* The start screen: one question, one card, centred on an empty page. */
.startkaart {
    max-width: var(--breedte-inhoud);
    margin: 0 auto;
}
.startkaart h1 { margin-top: 0; }

/*
 * De wizard bij de eerste keer inloggen. Eén kolom, een kop die zegt waar je
 * bent, en de knoppen onderaan in beeld -- ook boven het toetsenbord van een
 * telefoon, want sticky volgt de zichtbare ruimte.
 */
.wizardkop {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-3);
    margin-bottom: var(--ruimte-4);
}
.wizardkop__waar { flex: 1; }
.wizardkop__stap { margin: 0 0 var(--ruimte-2); color: var(--tekst-zacht); }

.voortgang { display: flex; gap: var(--ruimte-2); max-width: 16rem; }
.voortgang__deel {
    flex: 1;
    height: 6px;
    border-radius: 3px;
    background: var(--accent-zacht);
}
.voortgang__deel--gedaan { background: var(--accent); }

/* Volgende staat als eerste in de HTML (Enter slaat op), maar rechts in beeld. */
.wizardknoppen {
    position: sticky;
    bottom: 0;
    display: flex;
    flex-direction: row-reverse;
    flex-wrap: wrap;
    gap: var(--ruimte-3);
    margin-top: var(--ruimte-5);
    padding: var(--ruimte-3) 0;
    padding-bottom: max(var(--ruimte-3), env(safe-area-inset-bottom));
    background: var(--achtergrond);
    border-top: 1px solid var(--rand);
}
.wizardknoppen__terug { margin-right: auto; }

/* Een groep knoppen in een stap: naast elkaar zolang ze passen, anders onder
   elkaar -- met dezelfde ruimte tussen de regels als tussen de knoppen. */
.wizardacties {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ruimte-3);
}

/* In het kader van het inlogscherm: geen kaarten op de achtergrond, alleen een
   lijn tussen twee groepen. Dezelfde partials staan op de instellingenschermen
   als paneel, dus het hangt aan .wizard en niet aan het partial.

   De instellingen en Abonnement doen hetzelfde, zodat ze net zo strak zijn.
   Daar blijft een paneel ín een paneel wel lichtgrijs: dat zijn keuzes naast
   elkaar binnen één onderdeel, en dat moet je blijven zien. En de kaart om
   een hele instellingenpagina valt ook weg, anders stond alles nog in een vak. */
.instellingen.kaart,
.wizard .paneel,
.instellingen .paneel:not(.paneel .paneel) {
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
}
.wizard .paneel + .paneel,
.instellingen .paneel:not(.paneel .paneel) + .paneel {
    padding-top: var(--ruimte-5);
    border-top: 1px solid var(--rand);
}
/* En evenveel ruimte boven de lijn: zonder rand heeft het paneel erboven geen
   binnenruimte meer, en dan lag de lijn tegen de onderkant van zijn knop. */
.wizard .paneel:has(+ .paneel),
.instellingen .paneel:not(.paneel .paneel):has(+ .paneel) { padding-bottom: var(--ruimte-5); }
/* Het blokje binnen een onderdeel stond op --vlak-rustig, en dat is overdag
   wit: op een pagina zonder kaarten wit op wit. --vlak is in allebei de
   standen net anders dan de grond. */
.instellingen .paneel .paneel { background: var(--vlak); }

/* Het hele scherm: één kleur, één kolom in het midden, zoals de formulierkant
   van het inlogscherm. De bovenbalk en de voetregel die de layout om een kaal
   scherm zet, vallen weg -- het woordmerk staat boven de kolom. */
body:has(.wizardscherm) .inhoud {
    max-width: none;
    padding: 0;
}
body:has(.wizardscherm) .topbar,
body:has(.wizardscherm) .voetregel { display: none; }

.wizardscherm {
    display: flex;
    min-height: 100dvh;
    padding: var(--ruimte-6) var(--ruimte-5) 0;
    background: var(--achtergrond);
}

/* margin: auto centreert in beide richtingen, en snijdt anders dan een
   gecentreerd raster de bovenkant niet af als de stap hoger is dan het scherm. */
/* De leesbreedte van de rest van de applicatie, dezelfde als .kaart--lezen en
   .formulier--lezen: breder dan dit is een veld niet meer in één blik te
   overzien. */
.wizardscherm__kolom {
    width: 100%;
    max-width: var(--breedte-inhoud);
    margin: auto;
}
/* Onderaan geen ruimte op het scherm zelf: de knoppenbalk plakt daar en draagt
   zijn eigen. Een stap zonder die balk (de eerste en de laatste) eindigde
   daardoor met een knop tegen de rand van het scherm. */
.wizardscherm__kolom:not(:has(.wizardknoppen)) {
    padding-bottom: max(var(--ruimte-6), env(safe-area-inset-bottom));
}
.wizardscherm__logo {
    display: block;
    width: auto;
    height: 3.5rem;
    margin-bottom: var(--ruimte-6);
}

/* Dezelfde marges als .inhoud, op dezelfde breekpunten. */
@media (max-width: 47.999rem) {
    .wizardscherm { padding: var(--ruimte-4) var(--ruimte-3) 0; }
    .wizardscherm__logo { margin-bottom: var(--ruimte-5); }
}

/* ---- Het startscherm zodra er verkocht wordt ---------------------------
 *
 * Vijf korte antwoorden in plaats van één kaart. Eén kolom op een telefoon,
 * twee op een breed scherm: vandaag, aandacht en de laatste verkopen over de
 * hele breedte, de marktdagen en de toplijst naast elkaar.
 */
.start {
    display: grid;
    gap: var(--ruimte-4);
    max-width: var(--breedte-pagina);
    margin: 0 auto;
}

@media (min-width: 60rem) {
    .start { grid-template-columns: 1fr 1fr; align-items: start; }
    .startblok--breed { grid-column: 1 / -1; }
}

.startblok h1,
.startblok h2 { margin: 0; }
.startblok > .kop { margin-bottom: var(--ruimte-3); }

.startblok__bedrag {
    margin: 0;
    font-size: var(--tekst-2xl);
    font-weight: var(--gewicht-vet);
    font-variant-numeric: tabular-nums;
}
.startblok__regel { margin: var(--ruimte-1) 0 0; }
.startblok__zacht { color: var(--tekst-zacht); }
.startblok__verschil--meer { color: var(--goed); }

.startlijst { margin: 0; padding: 0; list-style: none; }
.startlijst > li { border-bottom: 1px solid var(--rand); }
.startlijst > li:last-child { border-bottom: 0; }

.startlijst__link,
.startlijst__rij {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-3);
    min-height: var(--raakvlak);
    padding: var(--ruimte-2) 0;
    font-variant-numeric: tabular-nums;
}
.startlijst__link { color: inherit; text-decoration: none; }
.startlijst__link:hover span:first-child { text-decoration: underline; }

/* Een marktdag: datum, balk, bedrag. De balk krijgt wat er overblijft. */
.startdag { display: grid; grid-template-columns: 6.5rem 1fr auto; }
.startdag__balk {
    height: 0.6rem;
    background: var(--vlak-rustig);
    border-radius: var(--radius-s);
    overflow: hidden;
}
.startdag__vulling { display: block; height: 100%; background: var(--accent); }
.startdag__bedrag { text-align: right; }

.kaart__intro { color: var(--tekst-zacht); }

.kaart__voet {
    margin: var(--ruimte-4) 0 0;
    padding-top: var(--ruimte-4);
    border-top: 1px solid var(--rand);
    font-size: var(--tekst-s);
    color: var(--tekst-zacht);
}

/* One block of a page: a sheet lying on the ground. No margin of its own --
   .pagina's gap decides the distance, so two panels are never a different
   distance apart on two screens. */
.paneel {
    padding: var(--ruimte-5);
    background: var(--vlak);
    border: 1px solid var(--paneel-rand);
    border-radius: var(--radius-xl);
    box-shadow: var(--schaduw-paneel);
}
.paneel > :last-child { margin-bottom: 0; }
.paneel > :first-child { margin-top: 0; }

/* Twee panelen onder elkaar in een kaart raakten elkaar: staan ze in een
   .formulier of een .pagina, dan zorgt de gap daar voor de ruimte, maar los in
   een kaart is er niets dat dat doet. */
.kaart > .paneel + .paneel { margin-top: var(--ruimte-5); }
.paneel > .paneel + .paneel { margin-top: var(--ruimte-3); }
.paneel--leeg { color: var(--tekst-zacht); }

/* Quiet: an inset within a panel or a card, not a sheet of its own. White on
   white would be a nested card, which is the thing this rewrite removed. */
.paneel--rustig,
.kaart .paneel,
.kaart .kerncijfer,
.paneel .paneel {
    background: var(--vlak-rustig);
    border-color: transparent;
    border-radius: var(--radius-m);
    box-shadow: none;
}

/* For a panel whose content brings its own edges: a table, a full-width list. */
.paneel--strak { padding: 0; overflow: hidden; }

/* The heading row inside a panel. Same shape as .paginakop one level down, so
   "title left, actions right" looks the same wherever it appears. */
.paneel__kop {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-2) var(--ruimte-4);
    margin-bottom: var(--ruimte-4);
}
.paneel__kop h2 { margin: 0; }
.paneel__kop .veld__hulp { margin: 0; }

/* An action that closes a panel off, under a hairline: "doe dit met alles wat
   hierboven staat". Zonder die lijn leest de knop als nog een regel in de
   lijst, en dat is precies de verwarring die je bij uitloggen niet wilt. */
.paneel__actie {
    margin-top: var(--ruimte-4);
    padding-top: var(--ruimte-4);
    border-top: 1px solid var(--rand);
}
.paneel__actie .veld__hulp { margin-bottom: 0; }

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

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

/* A form inside a wide panel. Fields that run the full width of a work surface
   are fields nobody can tell apart at a glance. */
.formulier--lezen { max-width: var(--breedte-inhoud); }

.veld { display: block; }

/* Velden die los in een paneel staan in plaats van in een .formulier: zonder
   dit raakt het label van het volgende veld het invoerveld erboven. Het paneel
   zet zijn eerste kind al op nul. */
.paneel > .veld,
.paneel > .veldpaar { margin-top: var(--ruimte-4); }

/* Twee velden die samen één regel zijn -- postcode en plaats, voorvoegsel en
   volgnummer. Een postcode van 44rem breed is geen betere postcode. Passen ze
   niet naast elkaar, dan vallen ze vanzelf onder elkaar. */
.veldpaar {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    gap: var(--ruimte-4);
}

.veld__label {
    display: block;
    margin-bottom: var(--ruimte-1);
    font-size: var(--tekst-s);
    font-weight: var(--gewicht-medium);
}

.veld__optioneel { color: var(--tekst-zacht); font-weight: var(--gewicht-normaal); }

.veld__invoer {
    width: 100%;
    min-height: var(--raakvlak);
    padding: var(--ruimte-2) var(--ruimte-3);
    font: inherit;
    color: var(--tekst);
    background: var(--invoer);
    border: 1px solid var(--rand-sterk);
    border-radius: var(--radius-m);
}
.veld__invoer:focus-visible { border-color: var(--accent); }

/* iOS geeft een datumveld een eigen breedte die width: 100% negeert, en met
   een waarde erin steekt het dan voorbij de velden eronder. Zonder de eigen
   weergave volgt het de regel hierboven; de tekst blijft links staan, zoals
   in elk ander veld, in plaats van gecentreerd. */
.veld__invoer[type="date"] {
    -webkit-appearance: none;
    appearance: none;
    min-width: 0;
    display: block;
}
.veld__invoer[type="date"]::-webkit-date-and-time-value { text-align: left; }

.veld__invoer--code {
    font-family: var(--lettertype-mono);
    font-size: var(--tekst-xl);
    letter-spacing: 0.35em;
    text-align: center;
}

.veld__hulp {
    display: block;
    margin-top: var(--ruimte-1);
    font-size: var(--tekst-xs);
    color: var(--tekst-zacht);
}

/* ---- Uitleg achter een i ------------------------------------------------ *
 *
 * De instellingenschermen legden elk veld onder dat veld uit. Los is elke regel
 * nuttig, bij elkaar werd het een muur waarin de instelling zelf verdween. Wat
 * je moet weten vóór je klikt blijft staan -- een gevolg dat je niet
 * terugdraait, het formaat van een veld terwijl je het invult -- en de rest
 * komt hierachter vandaan.
 *
 * Zonder script. De tekst staat gewoon in de pagina; dit laat hem zien bij
 * hover én bij focus, en dat tweede is de hele truc: op een tablet bestaat
 * hover niet, maar een aanraking zet de focus op de knop. Tikken naast de knop
 * haalt de focus weg en sluit hem weer.                                      */

.hint {
    display: inline-block;
    /* De knop staat naast een label en mag de regelhoogte niet oprekken. */
    vertical-align: middle;
    line-height: 1;
}

/* De uitleg hangt aan de regel waar hij bij hoort en niet aan het icoontje.
   Dat is geen smaak: op een smalle telefoon staat dat icoontje rechts op de
   regel, en een kaartje dat dáár begint loopt het scherm uit. Vanaf de linker-
   kant van de regel is er altijd plek, en breder dan de regel wordt hij niet. */
.veld:has(.hint),
.veld__label:has(.hint),
.schakelaar:has(.hint),
.kaart__intro:has(.hint),
h2:has(.hint),
h3:has(.hint),
h4:has(.hint) {
    position: relative;
}

.hint__knop {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
    padding: 0;
    border: 1px solid var(--rand);
    border-radius: 50%;
    background: transparent;
    color: var(--tekst-zacht);
    font: inherit;
    font-size: var(--tekst-xs);
    font-style: italic;
    line-height: 1;
    /* Geen pointer: er gebeurt niets als je klikt, er verschijnt alleen iets. */
    cursor: help;
}

/* Het cirkeltje is klein omdat het naast een label staat; de vinger die ernaar
   wijst is dat niet. Dit maakt het raakvlak groter zonder de tekening te
   veranderen -- en bewust niet de volle --raakvlak, want links ernaast staat
   vaak het label van een schakelaar, en dat mag een tik niet inpikken. */
.hint__knop::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 2rem;
    height: 2rem;
    transform: translate(-50%, -50%);
}

.hint__knop:hover,
.hint__knop:focus-visible {
    border-color: var(--accent);
    color: var(--tekst);
}

.hint__tekst {
    position: absolute;
    z-index: 5;
    top: calc(100% + var(--ruimte-1));
    left: 0;
    width: max-content;
    max-width: min(22rem, calc(100vw - 2 * var(--ruimte-5) - 2 * var(--ruimte-3)));
    padding: var(--ruimte-3) var(--ruimte-4);
    border: 1px solid var(--paneel-rand);
    border-radius: var(--radius-m);
    background: var(--vlak);
    box-shadow: var(--schaduw-paneel);
    font-size: var(--tekst-s);
    font-style: normal;
    font-weight: var(--gewicht-normaal);
    line-height: 1.45;
    color: var(--tekst);
    text-align: left;
    /* visibility en niet display: dan blijft de tekst in de toegankelijkheids-
       boom staan voor `aria-describedby`. */
    visibility: hidden;
    opacity: 0;
    transition: opacity 0.12s ease;
}

.hint:hover .hint__tekst,
.hint__knop:focus .hint__tekst,
.hint__knop:focus + .hint__tekst {
    visibility: visible;
    opacity: 1;
}

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

/* ---- Buttons ----------------------------------------------------------- */

.knop {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--raakvlak);
    padding: var(--ruimte-2) var(--ruimte-5);
    font: inherit;
    font-weight: var(--gewicht-medium);
    text-decoration: none;
    color: var(--tekst);
    /* Een stap richting het licht, net als een inzetje: een knop die dezelfde
       kleur heeft als het paneel waar hij op ligt, is geen knop. */
    background: var(--vlak-rustig);
    border: 1px solid var(--rand);
    border-radius: var(--radius-m);
    cursor: pointer;
}
.knop:hover { background: var(--vlak); }

.knop--primair {
    color: var(--op-accent);
    background: var(--accent);
    border-color: var(--accent);
}
.knop--primair:hover { background: var(--accent-donker); border-color: var(--accent-donker); }

.knop--rand {
    color: var(--accent);
    background: transparent;
    border-color: var(--accent);
}

.knop--stil {
    color: var(--tekst-zacht);
    background: transparent;
    border-color: transparent;
    padding-inline: var(--ruimte-3);
}
.knop--stil:hover { color: var(--tekst); background: var(--vlak-rustig); }

/* Voor wat niet terug te draaien is. De tekst in de kleur van het vlak, zodat
   hij in licht en donker leesbaar blijft op de foutkleur. */
.knop--gevaar {
    color: var(--vlak);
    background: var(--fout);
    border-color: var(--fout);
}

.knop[disabled] { opacity: 0.55; cursor: default; }

/* ---- Messages ---------------------------------------------------------- */

.melding {
    margin: var(--ruimte-4) 0;
    padding: var(--ruimte-3) var(--ruimte-4);
    border-radius: var(--radius-m);
    font-size: var(--tekst-s);
}
.melding > :last-child { margin-bottom: 0; }
.melding--fout    { background: var(--fout-zacht); color: var(--fout); }
.melding--success { background: var(--goed-zacht); color: var(--goed); }
.melding--info    { background: var(--accent-zacht); color: var(--tekst); }
.melding--waarschuwing { background: var(--waarschuwing-zacht); color: var(--waarschuwing); }

/* Een betaald pakket dat nog openstaat, bovenaan elk scherm. Tekst en knop
   naast elkaar zolang dat past; op een telefoon de knop eronder. */
.betaalbalk {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-3) var(--ruimte-4);
    /* Zo breed als de pagina eronder, niet als het hele werkvlak. */
    max-width: var(--breedte-pagina);
    margin: 0 auto var(--ruimte-5);
}
.betaalbalk p { flex: 1 1 20rem; margin: 0; color: var(--tekst); }
/* De knop erin hoort bij de melding, niet bij de pagina eronder. */
.melding--waarschuwing .knop--stil { color: inherit; }
.melding--waarschuwing .knop--stil:hover { color: inherit; background: var(--vlak-rustig); }

/* De volle catalogus op Artikelen: één keer een venster, daarna deze balk. */
.catalogusvol {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-2);
    margin-bottom: var(--ruimte-4);
    font-size: var(--tekst-s);
}
.catalogusvol__venster { max-width: 34rem; }
.catalogusvol__venster h2 { margin-top: 0; }
.catalogusvol__keuzes { padding-left: 1.25rem; display: grid; gap: var(--ruimte-2); }

.status { font-size: var(--tekst-s); }
.status--aan { color: var(--goed); }
.status--uit { color: var(--tekst-zacht); }

/* ---- Lists and data ---------------------------------------------------- */

.lijst { list-style: none; margin: 0 0 var(--ruimte-4); padding: 0; }

.lijst__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-3);
    padding: var(--ruimte-3) 0;
    border-bottom: 1px solid var(--rand);
}
.lijst__item:last-child { border-bottom: 0; }
.lijst__detail { display: block; font-size: var(--tekst-xs); color: var(--tekst-zacht); }

/* Hoeveel er terugkomt bij een tegenboeking: een getal, geen zin. */
.lijst__aantal { width: 5rem; flex: none; text-align: center; }

.gegevens {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--ruimte-2) var(--ruimte-4);
    margin: 0 0 var(--ruimte-4);
    font-size: var(--tekst-s);
}
.gegevens dt { color: var(--tekst-zacht); }
.gegevens dd { margin: 0; }

.stappen { padding-left: 1.25em; color: var(--tekst-zacht); }
.stappen li { margin-bottom: var(--ruimte-2); }

.qr {
    display: block;
    margin: var(--ruimte-4) auto;
    background: var(--kleur-wit);
    padding: var(--ruimte-3);
    border-radius: var(--radius-m);
}

.sleutel { font-size: var(--tekst-l); letter-spacing: 0.08em; text-align: center; }

.uitklap { margin-bottom: var(--ruimte-4); font-size: var(--tekst-s); }
.uitklap summary { cursor: pointer; min-height: var(--raakvlak); display: flex; align-items: center; }

/* ---- Passkey block ----------------------------------------------------- */

.passkey { margin-top: var(--ruimte-4); }
.passkey .knop { width: 100%; }

.scheiding {
    display: flex;
    align-items: center;
    gap: var(--ruimte-3);
    margin: var(--ruimte-4) 0;
    color: var(--tekst-zacht);
    font-size: var(--tekst-xs);
}
.scheiding::before, .scheiding::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--rand);
}

[hidden] { display: none !important; }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ---- Inventory (phase 2) ----------------------------------------------- */

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

/* The sync indicator. Colour carries meaning, but never alone: the text says
   the same thing, because a colour-blind trader in daylight has only the words.
 *
 * A pill in the page heading rather than a bar across the screen. What it says
 * on a normal day is "Alles opgeslagen", and a sentence that is true all day
 * should not be the widest thing on the page. When something is actually wrong
 * it grows a colour and a button, and then it is worth the room. */
.sync {
    display: inline-flex;
    align-items: center;
    gap: var(--ruimte-2);
    max-width: 100%;
    margin: 0;
    padding: var(--ruimte-1) var(--ruimte-3);
    min-height: 2.25rem;
    background: var(--vlak);
    border: 1px solid var(--paneel-rand);
    border-radius: 999px;
    box-shadow: var(--schaduw-paneel);
    font-size: var(--tekst-xs);
    color: var(--tekst-zacht);
}

.sync__stip {
    width: 0.6rem;
    height: 0.6rem;
    flex: none;
    border-radius: 50%;
    background: var(--tekst-zacht);
}
.sync[data-toestand="gesynct"] .sync__stip { background: var(--goed); }
.sync[data-toestand="wacht"] .sync__stip,
.sync[data-toestand="bezig"] .sync__stip { background: var(--accent); }
.sync[data-toestand="fout"] .sync__stip { background: var(--fout); }
.sync[data-toestand="fout"] {
    color: var(--fout);
    background: var(--fout-zacht);
    border-color: transparent;
    box-shadow: none;
}
.sync__knop { min-height: 2rem; padding-inline: var(--ruimte-2); font-size: var(--tekst-xs); }

.filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--ruimte-3);
    margin-bottom: var(--ruimte-4);
}
/* Bedoeld voor een rij: naast elkaar is 12rem een breedte. In een .formulier,
   dat een kolom is, is de hoofdas verticaal en betekent diezelfde regel 12rem
   hóógte -- een datumveld met een handbreed lucht eronder. Daar is "klein" een
   breedte en verder niets. */
.veld--klein { flex: 1 1 12rem; min-width: 0; }

.formulier > .veld--klein {
    flex: 0 0 auto;
    max-width: 12rem;
}

.schakelaar {
    display: flex;
    align-items: center;
    gap: var(--ruimte-2);
    min-height: var(--raakvlak);
    font-size: var(--tekst-s);
}
.schakelaar input { width: 1.15rem; height: 1.15rem; }

/* Een keuzelijst waarin meer dan één ding aan kan: de categorieën van een
   product. Dicht oogt hij als een select; open zweeft de lijst over de velden
   eronder, zoals het menu van een select, in plaats van ze weg te duwen. Met
   veel categorieën liep het venster anders ver door naar beneden. De lijst
   heeft een vaste hoogte en scrollt zelf. */
.meerkeuze { position: relative; width: 100%; }
.meerkeuze__knop {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-2);
    cursor: pointer;
    list-style: none;
}
.meerkeuze__knop::-webkit-details-marker { display: none; }
.meerkeuze__knop > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.meerkeuze__knop::after {
    content: "";
    flex: 0 0 auto;
    width: 0.5rem;
    height: 0.5rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: translateY(-25%) rotate(45deg);
}
.meerkeuze[open] .meerkeuze__knop { border-color: var(--accent); }
.meerkeuze[open] .meerkeuze__knop::after { transform: translateY(25%) rotate(-135deg); }
.meerkeuze__lijst {
    position: absolute;
    min-width: 14rem;
    /* Boven de knoppenbalk onderin het venster, die sticky en dus ook een laag is. */
    z-index: 2;
    top: calc(100% + var(--ruimte-1));
    left: 0;
    right: 0;
    max-height: min(15rem, 45dvh);
    overflow-y: auto;
    overscroll-behavior: contain;
    box-shadow: 0 8px 24px rgb(0 0 0 / 35%);
    padding: var(--ruimte-1) var(--ruimte-3);
    background: var(--invoer);
    border: 1px solid var(--rand-sterk);
    border-radius: var(--radius-m);
}
.meerkeuze__lijst .schakelaar { width: 100%; }

/* Het vinkje "voor mijn bedrijf" in een betaalformulier (ADR-0026). Twee
   regels tekst naast een vakje: het vakje bovenaan, niet in het midden. */
.plan__zakelijk {
    align-items: flex-start;
    margin-bottom: var(--ruimte-2);
    font-size: var(--tekst-s);
    text-align: left;
}
.plan__zakelijk input { margin-top: 0.2em; }

/* ---- De abonnementspagina ---------------------------------------------- */

.abonnementstatus__pakket {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--ruimte-1) var(--ruimte-3);
    margin: 0;
}
.abonnementstatus__pakket strong { font-size: var(--tekst-l); }
.abonnementstatus__pakket span { color: var(--tekst-zacht); }

/* Wat je koos of wat er gepland staat, onder een lijn in hetzelfde blok. */
.abonnementstatus__keuze {
    margin-top: var(--ruimte-4);
    padding-top: var(--ruimte-4);
    border-top: 1px solid var(--rand);
}
.abonnementstatus__keuze h3 { margin-top: 0; }

/* Het vinkje in het venster: wat ruimte boven de knoppen. */
.venster .plan__zakelijk { margin-top: var(--ruimte-4); }
.venster .plan__zakelijk + .formulier__acties { margin-top: 0; }

/* ---- Het logo dat bij de stand past ------------------------------------- *
 *
 * Twee afbeeldingen, waarvan er één zichtbaar is. De omhulling doet niet mee
 * aan de opmaak (display: contents), zodat de afbeelding zelf haar eigen
 * display houdt. Dezelfde twee voorwaarden als het donkere blok in tokens.css. */
.merk { display: contents; }
.merk--donker { display: none; }
@media (prefers-color-scheme: dark) {
    :root:not([data-thema="licht"]) .merk--licht  { display: none; }
    :root:not([data-thema="licht"]) .merk--donker { display: contents; }
}
:root[data-thema="donker"] .merk--licht  { display: none; }
:root[data-thema="donker"] .merk--donker { display: contents; }

/* ---- Dichtgeklapt ------------------------------------------------------- *
 *
 * Een <details> met de kop als knop, en rechts hetzelfde pijltje dat Snel
 * toevoegen bij Producten heeft. Twee onder elkaar krijgen een lijn ertussen. */

.klap > summary {
    display: flex;
    align-items: center;
    gap: var(--ruimte-3);
    min-height: var(--raakvlak);
    font-weight: var(--gewicht-medium);
    cursor: pointer;
    list-style: none;
}
.klap > summary::-webkit-details-marker { display: none; }
.klap > summary h3 { margin: 0; }
.klap > summary::after {
    content: "";
    flex: none;
    margin-left: auto;
    width: 0.5rem;
    height: 0.5rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: translateY(-0.15rem) rotate(45deg);
    transition: transform 120ms ease;
    opacity: 0.7;
}
.klap[open] > summary::after { transform: translateY(0.1rem) rotate(-135deg); }
.klap[open] > summary { margin-bottom: var(--ruimte-3); }
.klap + .klap { border-top: 1px solid var(--rand); }
/* Open: de knop onderaan krijgt ruimte boven de lijn naar het volgende blok. */
.klap[open]:has(+ .klap) { padding-bottom: var(--ruimte-4); }
.klap > p:first-of-type { margin-top: 0; }

@media (prefers-reduced-motion: reduce) {
    .klap > summary::after { transition: none; }
}

/* Betalen en nee zeggen naast elkaar, zolang ze passen. */
.plan__knoppen {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ruimte-3);
}
.meerkeuze__leeg { margin: 0; padding: var(--ruimte-2) 0; font-size: var(--tekst-s); color: var(--tekst-zacht); }

.lijst__telling {
    margin: 0 0 var(--ruimte-2);
    font-size: var(--tekst-xs);
    color: var(--tekst-zacht);
}

.leeg {
    flex: 1;
    margin: 0;
    padding: var(--ruimte-5) var(--ruimte-4);
    text-align: center;
    text-wrap: balance;
    color: var(--tekst-zacht);
    font-size: var(--tekst-s);
}

.lijst--producten { margin: 0; }

.product {
    display: flex;
    align-items: center;
    gap: var(--ruimte-3);
    padding: var(--ruimte-3) 0;
    border-bottom: 1px solid var(--rand);
}
.product:last-child { border-bottom: 0; }

.product__foto {
    width: 3rem;
    height: 3rem;
    flex: none;
    object-fit: cover;
    border-radius: var(--radius-s);
    background: var(--vlak-rustig);
}
/* No photo: the product's initial, for the reason in .tegel__foto--leeg. */
.product__foto--leeg {
    display: grid;
    place-items: center;
    font-size: var(--tekst-m);
    font-weight: var(--gewicht-vet);
    line-height: 1;
    color: var(--accent);
    background: var(--accent-zacht);
}
/* See the note in voorraad.js: the slot exists only when something fills it. */
.lijst--producten:not(.lijst--fotos) .product__foto--leeg { display: none; }

.product__tekst { flex: 1; min-width: 0; }
.product__naam { display: block; }

.product__detail {
    display: block;
    font-size: var(--tekst-xs);
    color: var(--tekst-zacht);
}

/* A record still in the outbox. Deliberately understated: the change IS saved,
   locally, and the work is not at risk — only the trip to the server is pending. */
.product__wacht {
    display: inline-block;
    margin-top: var(--ruimte-1);
    padding: 0 var(--ruimte-2);
    font-size: var(--tekst-xs);
    color: var(--accent);
    background: var(--accent-zacht);
    border-radius: var(--radius-s);
}

.product__acties { display: flex; flex: none; gap: var(--ruimte-1); }

/* Voorbij de grens van het pakket (ADR-0025): gedimd, maar leesbaar. */
.product--vergrendeld .product__foto,
.product--vergrendeld .product__tekst { opacity: 0.55; }
.product__slot {
    display: inline-block;
    margin-top: var(--ruimte-1);
    padding: 0 var(--ruimte-2);
    font-size: var(--tekst-xs);
    color: var(--tekst-zacht);
    box-shadow: inset 0 0 0 1px var(--rand-sterk);
    border-radius: var(--radius-s);
}

[data-wacht] .product__naam::after { content: ""; }

/* Snel toevoegen: één regel op een laptop, gestapeld op een telefoon. Een
   paneel van zichzelf, want het is een andere handeling dan de lijst eronder,
   en dichtgeklapt tot er iets toegevoegd moet worden -- zes velden boven de
   lijst zijn zes velden die je op een marktdag niet nodig hebt. */
.snel { margin: 0; }

/* De hele balk is de knop, want dat is wat je aanwijst als hij dicht is. */
.snel__kop {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--ruimte-2) var(--ruimte-3);
    cursor: pointer;
    /* Het eigen driehoekje weg: er komt er hieronder een die meedraait en die
       rechts staat, waar de rest van deze app zijn open-en-dicht ook heeft. */
    list-style: none;
}

.snel__kop::-webkit-details-marker { display: none; }

.snel__kop-titel { font-weight: var(--gewicht-vet); }
.snel__kop-hint { color: var(--tekst-zacht); font-size: var(--tekst-s); }

.snel__kop::after {
    content: "";
    margin-left: auto;
    width: 0.5rem;
    height: 0.5rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: translateY(-0.15rem) rotate(45deg);
    transition: transform 120ms ease;
    opacity: 0.7;
}

.snel[open] .snel__kop::after { transform: translateY(0.1rem) rotate(-135deg); }

/* Alleen als hij openstaat: dicht is de balk niets meer dan zijn kop. */
.snel[open] .snel__kop { margin-bottom: var(--ruimte-4); }

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

.snel__velden {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ruimte-3);
    align-items: end;
}

.snel__velden .veld--breed { flex: 1 1 14rem; grid-column: auto; }
.snel__velden .veld--klein { flex: 0 1 7rem; }
/* Breder dan de kleine velden, want het label past anders niet op één regel en
   een artikelnummer is langer dan een prijs. */
.snel__velden .veld--nummer { flex: 0 1 10rem; }
/* Breed genoeg voor twee categorienamen in het dichte veld. */
.snel__velden .veld--categorieen { flex: 0 1 12rem; }
/* Rechts uitgelijnd: dit veld staat aan het eind van de rij, en een lijst die
   breder is dan het veld liep anders buiten het paneel. */
.snel__velden .meerkeuze__lijst { left: auto; right: 0; }
.snel__velden .knop { flex: 0 0 auto; }

.veld__hulp--fout { color: var(--fout); }

/* The edit form a list screen opens. Capped at reading width: a row of seven
   fields spread across a work surface is seven fields nobody can tell apart.
   Gecentreerd, want de lijst erboven loopt wél door tot de rand: een smaller
   blok dat links blijft plakken leest als een uitlijnfout in plaats van als
   een keuze. */
.formulier-blok {
    max-width: var(--breedte-inhoud);
    margin-top: var(--ruimte-5);
    margin-inline: auto;
}

.formulier--raster {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--ruimte-4);
}
.veld--breed { grid-column: 1 / -1; }

.formulier__acties {
    display: flex;
    gap: var(--ruimte-3);
    margin-top: var(--ruimte-5);
}

/* Out of sight, still in the accessibility tree and still focusable -- which
   `hidden` and `display: none` are not. The file inputs live inside their
   labels, so the label is the button and the input is the mechanism. */
.visueel-verborgen {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.foto { margin-top: var(--ruimte-5); }
.foto__knoppen {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ruimte-2);
    margin: var(--ruimte-2) 0;
}
/* The label carries the focus ring, because the input it wraps cannot show
   one from a 1px box in the corner of the screen. */
.foto__knoppen .knop:focus-within {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
/* The frame the × hangs on. Inline-block so it is exactly as wide as the
   photo, which is what puts the button on the corner instead of somewhere off
   to the right. */
.foto__houder {
    position: relative;
    display: inline-block;
    margin: var(--ruimte-2) 0;
}
.foto__voorbeeld {
    display: block;
    width: 7.5rem;
    height: 7.5rem;
    object-fit: cover;
    border-radius: var(--radius-m);
    background: var(--vlak-rustig);
}
/* Sits on the corner rather than beside it: a photo on a phone should not have
   to share its row with a control. Sized like the basket steppers, which is
   the smallest thing in this app anyone is asked to hit with a thumb. */
.foto__weg {
    position: absolute;
    top: calc(var(--ruimte-2) * -1);
    right: calc(var(--ruimte-2) * -1);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    font: inherit;
    font-size: var(--tekst-l);
    line-height: 1;
    color: var(--tekst);
    background: var(--invoer);
    border: 1px solid var(--rand);
    border-radius: 50%;
    cursor: pointer;
}
.foto__weg:hover { color: var(--fout); border-color: var(--fout); }
/* The circle is smaller than a thumb on purpose -- a 48px disc on a 120px
   photo is a button with a picture behind it. The target underneath is the
   full --raakvlak, so what it looks like and what it catches are two separate
   questions. */
.foto__weg::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--raakvlak);
    height: var(--raakvlak);
    transform: translate(-50%, -50%);
}

@media (max-width: 34rem) {
    .product { flex-wrap: wrap; }
    .product__acties { width: 100%; justify-content: flex-end; }
}

/* ---- Selling screen (phase 3) ------------------------------------------ */

/*
 * The till: a catalogue on the left, the basket beside it.
 *
 * The basket only earns its own column once there is room for both. Below that
 * it drops under the grid, where a thumb reaches it — and it stops being
 * sticky, because a panel pinned to the bottom of a phone is a panel covering
 * the products.
 */
.kassa {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--ruimte-5);
}

.kassa__catalogus {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-4);
    min-width: 0;
}

/*
 * Drie stappen, want de mand vraagt breedte die de balk links al opgemaakt
 * heeft. Vanaf 48rem past hij ernaast -- een tablet in portret houdt dan nog
 * twee kolommen tegels over, en de balk links is daar nog een lade -- en hij
 * groeit mee zodra er ruimte bij komt.
 *
 * Onder 48rem past er niets naast en staat de mand als blad onder aan het
 * scherm; zie .mandbalk.
 */
@media (min-width: 48rem) {
    .kassa { grid-template-columns: minmax(0, 1fr) 17rem; align-items: start; }
    .kassa__mand { position: sticky; top: var(--ruimte-5); }
}

/* Tot 80rem staat op de kassa de telefoonbalk bovenaan, en die blijft staan. */
@media (min-width: 48rem) and (max-width: 79.999rem) {
    html[data-js] .kassa__mand { top: calc(var(--mobielbalk-hoogte) + var(--ruimte-5)); }
}

@media (min-width: 64rem) {
    .kassa { grid-template-columns: minmax(0, 1fr) 19rem; }
}

@media (min-width: 82rem) {
    .kassa { grid-template-columns: minmax(0, 1fr) 23rem; }
}

/* ---- De mand op een telefoon ------------------------------------------- *
 *
 * Onder 48rem staat de mand niet naast het raster maar eronder, en dat is bij
 * veertig artikelen vijf schermen scrollen -- heen om hem te zien, en terug om
 * verder te kunnen aanslaan. Met een klant voor de kraam is dat geen ongemak
 * maar een blokkade.
 *
 * Dus: een balk onder aan het scherm met het aantal en het totaal, altijd in
 * beeld, en één tik zet dezelfde mand als blad over het raster heen. Dezelfde
 * mand, letterlijk -- het is hetzelfde stuk opmaak dat op een tablet naast het
 * raster staat, alleen anders neergezet. Twee kopieën van een mand is één
 * kopie te veel, en de tweede is degene die achterloopt.
 *
 * Alleen met script, net als de lade van het menu: zonder script blijft de
 * mand gewoon onder het raster staan, waar hij bereikbaar is en lelijk, in
 * plaats van een blad dat niets opent.                                       */

@media (max-width: 47.999rem) {
    html[data-js] .mandbalk {
        position: fixed;
        inset: auto 0 0 0;
        z-index: 14;
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--ruimte-3);
        width: 100%;
        min-height: var(--raakvlak);
        padding: var(--ruimte-3) var(--ruimte-4);
        padding-bottom: max(var(--ruimte-3), env(safe-area-inset-bottom));
        font: inherit;
        font-size: var(--tekst-s);
        color: var(--op-accent);
        background: var(--accent);
        border: 0;
        cursor: pointer;
    }

    .mandbalk__telling { font-weight: var(--gewicht-medium); }
    .mandbalk__rechts {
        display: flex;
        align-items: center;
        gap: var(--ruimte-2);
    }
    .mandbalk__totaal {
        font-size: var(--tekst-l);
        font-weight: var(--gewicht-vet);
        font-variant-numeric: tabular-nums;
    }

    /* Het laatste rijtje tegels hoort niet onder de balk te verdwijnen. */
    html[data-js] body:has(.mandbalk) .inhoud {
        padding-bottom: calc(var(--raakvlak) + var(--ruimte-6) + env(safe-area-inset-bottom));
    }

    /* De mand zelf, als blad. */
    html[data-js] .kassa__mand {
        position: fixed;
        inset: auto 0 0 0;
        z-index: 20;
        max-height: 85dvh;
        overflow-y: auto;
        overscroll-behavior: contain;
        border-radius: var(--radius-xl) var(--radius-xl) 0 0;
        padding-bottom: max(var(--ruimte-5), env(safe-area-inset-bottom));
        box-shadow: var(--schaduw-m);
        transform: translateY(100%);
        visibility: hidden;
        /* visibility springt, hij loopt niet mee: bij het sluiten pas ná het
           wegschuiven, bij het openen meteen. Dat laatste is wat het blad
           focusbaar maakt op het moment dat het opengaat -- een element dat nog
           `hidden` is kun je niet focussen, en een halve seconde wachten om dat
           te omzeilen is een timing-aanname die ooit niet meer klopt. */
        transition: transform 0.22s ease, visibility 0s 0.22s;
    }
    html[data-js] .kassa__mand[data-open] {
        transform: none;
        visibility: visible;
        transition: transform 0.22s ease, visibility 0s;
    }

    html[data-js] .mandwaas {
        position: fixed;
        inset: 0;
        z-index: 19;
        background: var(--waas);
    }
}

/* Alleen op de telefoon, waar de mand een blad is dat je dicht wilt kunnen
   doen. Ernaast heeft hij geen sluitknop nodig, want hij gaat nooit dicht. */
.mandkop__sluit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    flex: none;
    color: var(--tekst-zacht);
    background: none;
    border: 0;
    border-radius: 50%;
    cursor: pointer;
}
.mandkop__sluit:hover { color: var(--tekst); background: var(--vlak-rustig); }

/* Vanaf 48rem staat de mand er gewoon; dan is er niets om te openen. */
@media (min-width: 48rem) {
    .mandbalk,
    .mandwaas,
    .mandkop__sluit { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    html[data-js] .kassa__mand { transition: none; }
}

/* ---- Searching --------------------------------------------------------- *
 * No label above it: a field with a magnifier in it and "Zoek een artikel"
 * inside says what it is, and the row it saves is a row of products.        */

.zoekbalk {
    position: relative;
    display: flex;
    align-items: center;
}

.zoekbalk__icoon {
    position: absolute;
    left: var(--ruimte-4);
    color: var(--tekst-zacht);
    pointer-events: none;
}

.zoekbalk__invoer {
    width: 100%;
    min-height: 3.25rem;
    padding: var(--ruimte-2) var(--ruimte-5);
    padding-left: calc(var(--ruimte-4) * 2 + 20px);
    font: inherit;
    color: var(--tekst);
    background: var(--invoer);
    border: 1px solid var(--paneel-rand);
    border-radius: 999px;
    box-shadow: var(--schaduw-paneel);
}
.zoekbalk__invoer::placeholder { color: var(--tekst-zacht); }

/* ---- Categories -------------------------------------------------------- *
 *
 * Cards rather than pills, and each one says how much is behind it: on a stall
 * "Kaas · 12 artikelen" is the difference between tapping and guessing. They
 * scroll sideways rather than wrap, so the wall of products never gets pushed
 * off the screen by four rows of category names.
 *
 * The mark is the category's initial, not an icon. Categories are the trader's
 * own words — Kaas, Noten, Kado's — and no icon set knows what they mean; a
 * drawn-on guess would be wrong on the third one.                            */

/*
 * A flex row, not a grid.
 *
 * This was an implicit grid -- one row, a column track per category -- and it
 * inherited two things from that. It took its width from whatever space it had,
 * so four categories on a tablet became four cards the width of a hand. And a
 * grid row stretches: the page column above this one is handed a definite
 * height (body is a flex column of 100dvh, .schil takes the rest, and a grid
 * hands that height straight down its auto row), and where that reached this
 * row every card grew into a full-height panel with its name stranded at the
 * bottom. On an iPad that meant scrolling down to see one category.
 *
 * A row of fixed-width cards that scrolls sideways is what flexbox is for, and
 * neither of those two things can happen to it: the cards state their own
 * width, and flex-start means a card is as tall as a card wherever it is laid.
 */
.categorierij {
    display: flex;
    flex-wrap: nowrap;
    align-items: flex-start;
    scroll-snap-type: x proximity;
    gap: var(--ruimte-3);
    overflow-x: auto;
    padding-bottom: var(--ruimte-2);
    scrollbar-color: var(--rand) transparent;
    scrollbar-width: thin;
}

.categoriekaart {
    /* Never grow to fill the row, never shrink to fit it: the row scrolls
       instead. Same width as the narrowest a product tile is allowed to be. */
    flex: 0 0 9.5rem;
    /* A flex item refuses to go below its own content by default, so a long
       category name would widen its card past the 9.5rem it just asked for --
       the name is an ellipsis, not a reason to be wider. */
    min-width: 0;
    scroll-snap-align: start;
    display: flex;
    flex-direction: column;
    /* Spelled out, not left to the default. A <button> that is a flex container
       lines its children up on the centre in WebKit rather than stretching
       them, and a child that is not stretched is a child sized by its own
       content -- which is how a long category name walked out of its card. */
    align-items: stretch;
    gap: var(--ruimte-2);
    min-height: 6.25rem;
    /* And whatever happens in there, it happens inside the rounded box. */
    overflow: hidden;
    padding: var(--ruimte-3);
    font: inherit;
    text-align: left;
    color: var(--tekst);
    background: var(--vlak);
    border: 1px solid var(--paneel-rand);
    border-radius: var(--radius-l);
    box-shadow: var(--schaduw-paneel);
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}
.categoriekaart:hover { border-color: var(--selectie-rand); }

.categoriekaart--actief {
    color: var(--op-selectie);
    background: var(--selectie);
    border-color: var(--selectie-rand);
    box-shadow: none;
}

.categoriekaart__merk {
    display: grid;
    place-items: center;
    width: 2.25rem;
    height: 2.25rem;
    /* Same reason as .rekening__cirkel: a square that may shrink is not a
       square. It is a flex item now, so it has to say so. */
    flex: none;
    font-size: var(--tekst-s);
    font-weight: var(--gewicht-vet);
    color: var(--accent);
    background: var(--accent-zacht);
    border-radius: var(--radius-m);
}
.categoriekaart--actief .categoriekaart__merk { color: var(--accent); background: var(--invoer); }
.categoriekaart__merk .icoon { width: 18px; height: 18px; }

/* display and width stated rather than inherited from the flex layout: an
   ellipsis needs a block box with a width to be an ellipsis at all, and on a
   button neither of those is a given. */
.categoriekaart__naam {
    display: block;
    width: 100%;
    margin-top: auto;
    font-size: var(--tekst-s);
    font-weight: var(--gewicht-medium);
    line-height: 1.25;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.categoriekaart__aantal {
    display: block;
    width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--tekst-xs);
    color: var(--tekst-zacht);
}
.categoriekaart--actief .categoriekaart__aantal { color: var(--op-selectie); opacity: 0.8; }

/* ---- The wall of products ---------------------------------------------- *
 * Big targets: this is tapped with cold hands, outdoors, in a hurry.        */

.raster {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
    gap: var(--ruimte-4);
}

.tegel {
    position: relative;
    display: flex;
    flex-direction: column;
    padding: var(--ruimte-2);
    background: var(--vlak);
    border: 1px solid var(--paneel-rand);
    border-radius: var(--radius-l);
    box-shadow: var(--schaduw-paneel);
    transition: border-color 0.15s ease;
}

/* Already in the basket. The ring is the answer to "did that tap land", which
   on a busy stall is asked about twice a minute. */
.tegel--gekozen {
    border-color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent), var(--schaduw-paneel);
}

/* The card itself is the button. Everything above the footer is one target,
   because a product tile with a small "add" button on it is a small button. */
.tegel__kies {
    display: flex;
    flex-direction: column;
    /* Same reason as .categoriekaart: on a button this is not the default it
       looks like, and everything below it is sized off the tile, not off itself. */
    align-items: stretch;
    gap: var(--ruimte-2);
    flex: 1;
    padding: 0;
    font: inherit;
    text-align: left;
    color: inherit;
    background: none;
    border: 0;
    cursor: pointer;
}

.tegel__foto {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: var(--radius-m);
    background: var(--vlak-rustig);
}
/* Only when at least one product in view actually has a picture. A grid of
   picture slots on a catalogue with no pictures is worse than no slots at all. */
.raster:not(.raster--fotos) .tegel__foto { display: none; }

/* This product has no photo but its neighbour does. It gets the same mark a
   category card gets -- its own initial -- because that tells two photoless
   tiles apart at arm's length, and a grey rectangle does not. */
.tegel__foto--leeg {
    display: grid;
    place-items: center;
    font-size: var(--tekst-2xl);
    font-weight: var(--gewicht-vet);
    line-height: 1;
    color: var(--accent);
    background: var(--accent-zacht);
}

.tegel__naam {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    font-size: var(--tekst-s);
    font-weight: var(--gewicht-medium);
    line-height: 1.3;
}

.tegel__prijs {
    margin-top: auto;
    font-size: var(--tekst-m);
    font-weight: var(--gewicht-vet);
    color: var(--accent);
    font-variant-numeric: tabular-nums;
}

/* Not a button: it is the bottom of the button above it, drawn so the tile
   says what tapping it does. */
.tegel__knop {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 2.5rem;
    /* Room around the word. It is centred in a full-width button, so this is
       invisible there -- but where the button shrinks to its label, this is the
       difference between a button and a word with a border round it. */
    padding-inline: var(--ruimte-4);
    margin-top: var(--ruimte-1);
    font-size: var(--tekst-s);
    font-weight: var(--gewicht-medium);
    color: var(--accent);
    background: var(--accent-zacht);
    border-radius: var(--radius-m);
    transition: background-color 0.15s ease, color 0.15s ease;
}
.tegel__kies:hover .tegel__knop,
.tegel__kies:focus-visible .tegel__knop {
    color: var(--op-selectie);
    background: var(--selectie);
}

/* Shown when the count is at or below zero. It never blocks the sale — the
   customer is holding the item — it just says the count needs attention. */
.tegel__op {
    align-self: flex-start;
    padding: 0 var(--ruimte-2);
    font-size: var(--tekst-xs);
    color: var(--fout);
    background: var(--fout-zacht);
    border-radius: var(--radius-s);
}

/* Voorbij de grens van het pakket (ADR-0025). Gedimd en niet doorgestreept:
   het artikel bestaat nog, het is alleen niet aan te slaan. De tik blijft
   werken, want die legt uit waarom. */
.tegel--vergrendeld .tegel__foto,
.tegel--vergrendeld .tegel__naam,
.tegel--vergrendeld .tegel__prijs { opacity: 0.5; }
.tegel--vergrendeld .tegel__foto { filter: grayscale(1); }

.tegel__slot,
.tegel__kies:hover .tegel__slot,
.tegel__kies:focus-visible .tegel__slot {
    gap: var(--ruimte-1);
    color: var(--tekst-zacht);
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--rand-sterk);
}
.tegel__slot .icoon { width: 1em; height: 1em; }

.slotvenster h2 { margin-top: 0; }

/* ---- The basket -------------------------------------------------------- */

.kassa__mand {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-4);
}

.mandkop {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--ruimte-3);
}
.mandkop h2 { margin: 0; font-size: var(--tekst-l); }
.mandkop__telling { font-size: var(--tekst-xs); color: var(--tekst-zacht); }

.mandlijst {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-2);
}

/*
 * A basket line reads like a till roll: what it is, what one costs, how many,
 * and what that comes to.
 *
 * Rows rather than columns. The panel beside the grid is 19rem on a laptop and
 * 23rem on a wide screen, and any layout that puts a name, a price, a stepper
 * and a total on one line gives the name whatever is left over -- which is the
 * one thing on the line the trader reads back to the customer.
 */
.mandregel {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--ruimte-1) var(--ruimte-2);
    padding: var(--ruimte-3);
    background: var(--vlak-rustig);
    border-radius: var(--radius-m);
}
.mandregel__naam {
    grid-area: 1 / 1;
    font-size: var(--tekst-s);
    font-weight: var(--gewicht-medium);
    line-height: 1.3;
}
.mandregel__detail {
    grid-area: 2 / 1 / 3 / 3;
    font-size: var(--tekst-xs);
    color: var(--tekst-zacht);
}
.mandregel__stapper { grid-area: 3 / 1; justify-self: start; }
.mandregel__totaal {
    grid-area: 3 / 2;
    justify-self: end;
    font-size: var(--tekst-m);
    font-weight: var(--gewicht-vet);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Remove. Quiet on purpose: stepping down to zero is the ordinary way out of
   a line, and this is for the trader who wants it gone in one tap. */
.mandregel__weg {
    grid-area: 1 / 2;
    justify-self: end;
    display: grid;
    place-items: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    font: inherit;
    line-height: 1;
    color: var(--tekst-zacht);
    background: none;
    border: 0;
    border-radius: 50%;
    cursor: pointer;
}
.mandregel__weg:hover { color: var(--fout); background: var(--fout-zacht); }
.mandregel__weg .icoon { width: 16px; height: 16px; }

/* The price in a basket line is a control, not a label: tapping it opens the
   haggling. Dotted underline rather than a button chrome -- it has to read as
   part of the line, and a stall has no room for a button per price. */
.mandregel__prijs {
    padding: var(--ruimte-1) 0;
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    border-bottom: 1px dotted var(--tekst-zacht);
    cursor: pointer;
    font-variant-numeric: tabular-nums;
}
.mandregel__prijs:hover { color: var(--link); border-bottom-color: var(--link); }

/* Sized to the price it replaces, so the line does not jump when it opens. */
.mandregel__prijsveld {
    width: 5.5rem;
    padding: var(--ruimte-1);
    font: inherit;
    font-size: var(--tekst-xs);
    color: var(--tekst);
    background: var(--invoer);
    border: 1px solid var(--accent);
    border-radius: var(--radius-s);
    font-variant-numeric: tabular-nums;
}

/* What the catalogue says, struck through, and the way back to it. */
.mandregel__was {
    padding: 0;
    font: inherit;
    color: var(--tekst-zacht);
    text-decoration: line-through;
    background: none;
    border: 0;
    cursor: pointer;
}
.mandregel__was:hover { color: var(--link); }

.stapper {
    display: flex;
    align-items: center;
    gap: var(--ruimte-1);
    padding: 2px;
    background: var(--vlak);
    border-radius: 999px;
}
.stapper__knop {
    display: grid;
    place-items: center;
    width: 2.25rem;
    height: 2.25rem;
    font: inherit;
    font-size: var(--tekst-l);
    line-height: 1;
    color: var(--accent);
    background: var(--accent-zacht);
    border: 0;
    border-radius: 50%;
    cursor: pointer;
}
.stapper__knop:hover { color: var(--op-selectie); background: var(--selectie); }
.stapper__aantal {
    min-width: 1.75rem;
    text-align: center;
    font-size: var(--tekst-s);
    font-weight: var(--gewicht-vet);
    font-variant-numeric: tabular-nums;
}

/* The same stepper on a product tile, where it replaces the "Toevoegen" line
   and therefore has to be exactly as wide as it was. */
.stapper--tegel {
    justify-content: space-between;
    margin-top: var(--ruimte-1);
    padding: 0;
    background: var(--accent-zacht);
    border-radius: var(--radius-m);
}
.stapper--tegel .stapper__knop { background: none; }

.vrij {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-3);
    padding: var(--ruimte-3);
    background: var(--vlak-rustig);
    border-radius: var(--radius-m);
}

/*
 * What it comes to.
 *
 * Separated from the lines above by a dashed rule, the way a till roll does
 * it: everything above is what the customer picked, everything below is the
 * arithmetic. The end total is the biggest number on the screen, because it is
 * the one that gets read out loud.
 */
.totalen {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--ruimte-2);
    align-items: center;
    margin: 0;
    padding-top: var(--ruimte-4);
    border-top: 1px dashed var(--rand);
    font-size: var(--tekst-s);
    color: var(--tekst-zacht);
}
.totalen dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
.totalen__korting { display: block; font-size: var(--tekst-xs); color: var(--tekst-zacht); }
.totalen__eind {
    padding-top: var(--ruimte-2);
    font-family: var(--lettertype);
    font-size: var(--tekst-xl);
    font-weight: var(--gewicht-vet);
    color: var(--tekst);
}

.veld__invoer--kort {
    display: inline-block;
    width: 5.5rem;
    min-height: 2.5rem;
    padding: var(--ruimte-1) var(--ruimte-2);
    text-align: right;
}

/* One way to pay per row. Two full-width targets beat two half-width ones on a
   stall, and the list grows by itself when a method is added. */
.betaalrij {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-2);
}
.betaalrij .knop {
    width: 100%;
    gap: var(--ruimte-2);
    min-height: 3.25rem;
}

.mandvoet {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--ruimte-2);
}
.mandvoet .knop { padding-inline: var(--ruimte-2); font-size: var(--tekst-s); }

.wachtend { margin-bottom: var(--ruimte-4); }

.bon {
    padding: var(--ruimte-5);
    text-align: center;
    background: var(--goed-zacht);
    border-radius: var(--radius-xl);
}
.bon h2 { color: var(--goed); }
.bon__totaal {
    margin: 0;
    font-size: var(--tekst-2xl);
    font-weight: var(--gewicht-vet);
    font-variant-numeric: tabular-nums;
}
.bon__detail { margin: 0; font-size: var(--tekst-s); color: var(--tekst-zacht); }

/* ---- Reporting (phase 4) ----------------------------------------------- */

/* Flex rather than a grid: auto-fit would cut a wide page into six columns and
   then leave four of them empty, and what these figures want is to share
   whatever row they land on. */
.kerncijfers {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ruimte-4);
    margin: 0;
}

.kerncijfer { flex: 1 1 13rem; }

/* A figure is a panel of its own on the page: the numbers a trader wants from
   across the stall should not be sharing a box with the paragraph under them. */
.kerncijfer {
    padding: var(--ruimte-5);
    background: var(--vlak);
    border: 1px solid var(--paneel-rand);
    border-radius: var(--radius-xl);
    box-shadow: var(--schaduw-paneel);
}

/* The day's takings is the one number a trader looks for from across the
   stall, so it gets the room. */
.kerncijfer--groot { flex-basis: 100%; }
.kerncijfer--groot .kerncijfer__waarde { font-size: var(--tekst-2xl); }

/* Inside a panel — the VAT breakdown — where a second white sheet would be a
   card in a card. */
.kerncijfer--klein {
    padding: var(--ruimte-4);
    background: var(--vlak-rustig);
    border-color: transparent;
    border-radius: var(--radius-m);
    box-shadow: none;
}

.kerncijfer__label {
    display: block;
    font-size: var(--tekst-xs);
    color: var(--tekst-zacht);
}

.kerncijfer__waarde {
    display: block;
    font-size: var(--tekst-xl);
    font-variant-numeric: tabular-nums;
    line-height: 1.2;
}

.kerncijfer__detail {
    display: block;
    font-size: var(--tekst-xs);
    color: var(--tekst-zacht);
}

.tabel {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: var(--ruimte-4);
    font-size: var(--tekst-s);
}
.tabel th, .tabel td { padding: var(--ruimte-2); border-bottom: 1px solid var(--rand); text-align: left; }
.tabel th { font-weight: var(--gewicht-medium); color: var(--tekst-zacht); }
/* Amounts line up on the decimal, so columns can be scanned rather than read. */
.tabel--getal { text-align: right; font-variant-numeric: tabular-nums; }
.tabel__totaal td { font-weight: var(--gewicht-vet); border-top: 2px solid var(--rand); }

.lijst--boekingen { margin-top: var(--ruimte-3); }

.boeking { border-bottom: 1px solid var(--rand); }
.boeking:last-child { border-bottom: 0; }

.boeking__kop {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-3);
    width: 100%;
    min-height: var(--raakvlak);
    padding: var(--ruimte-3) 0;
    font: inherit;
    color: var(--tekst);
    text-align: left;
    background: none;
    border: 0;
    cursor: pointer;
}

.boeking__detail { display: block; font-size: var(--tekst-xs); color: var(--tekst-zacht); }
.boeking__bedrag { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* A reversal is a negative amount; the colour is a hint, the minus sign is the
   actual signal. */
.boeking--tegen .boeking__bedrag { color: var(--fout); }

.boeking__detailblok {
    padding: 0 0 var(--ruimte-4);
}

/* ------------------------------------------------------------------ slot

   The lock on the device.

   Hiding happens on the html element, set by a small synchronous script in the
   head, so the screen behind it is never painted at all -- a flash of somebody
   else's takings would defeat the point. The lock screen itself is in the
   markup from the start rather than built by a script, so there is no blank
   moment between the two. */

/* De hele schil gaat weg, niet alleen de inhoud: de navigatiebalk stond naast
   het pincodescherm (en op een telefoon eronder), en een menu dat je kunt
   aantikken terwijl het slot op staat is geen slot. */
html[data-vergrendeld] .topbar,
html[data-vergrendeld] .mobielbalk,
html[data-vergrendeld] .schil,
html[data-vergrendeld] .inhoud,
html[data-vergrendeld] .voetregel,
html[data-vergrendeld] .skip-link { display: none; }

.slotscherm {
    display: none;
    min-height: 100dvh;
    padding: var(--ruimte-5) var(--ruimte-4);
    place-content: center;
}

html[data-vergrendeld] .slotscherm { display: grid; }

.slotscherm__kaart {
    width: 100%;
    max-width: 22rem;
    margin: 0 auto;
    text-align: center;
}

.slotscherm__pin {
    width: 100%;
    padding: var(--ruimte-3);
    font-size: var(--tekst-l);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.4em;
    text-align: center;
}

/* De knop plakte tegen het pinveld. Een grid met een gat zet ook de foutmelding
   los zodra die er is, en kost niets zolang die verborgen blijft. */
.slotscherm__kaart form {
    display: grid;
    gap: var(--ruimte-4);
}

.slotscherm__uitleg {
    margin-top: var(--ruimte-4);
    color: var(--tekst-zacht);
    font-size: var(--tekst-xs);
}

/* ---- The price list ---------------------------------------------------- *
 *
 * Two screens draw this -- the subscription page and the first step of signing
 * up -- from one partial, so there is one price list and not two that drift.
 *
 * The shape is the one a price list has had for twenty years, on purpose: a
 * row of cards, one of them lifted, and the full comparison underneath. The
 * old version put all thirteen comparison rows inside every card, which turned
 * four plans into four screens of scrolling and made the one question a
 * customer actually has -- which of these is for me -- the hardest one to
 * answer.
 *
 * The month/year switch is CSS, not script. Two radios sit as direct children
 * of .prijzen and everything after them reacts to which one is on, so it works
 * before any module has loaded, and on the signup screen the same radio is the
 * field that gets submitted.                                                 */

.prijzen {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-5);
}

/* A flex item does not shrink below its content unless it is told to, and the
   comparison table is deliberately wider than a phone. Without this the table
   pushes the whole page sideways instead of scrolling inside its own box —
   which is how a phone ends up scrolling the menu off the screen. */
.prijzen > *,
.prijzen__blok > * { min-width: 0; }

.prijzen__blok {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-5);
}

.prijzen__kop { text-align: center; }
.prijzen__kop h2 { margin: 0 0 var(--ruimte-2); }

.prijzen__intro {
    max-width: 52ch;
    margin: 0 auto;
    font-size: var(--tekst-s);
    color: var(--tekst-zacht);
}

.prijzen__voet { text-align: center; font-size: var(--tekst-s); }

/* ---- Per maand or per year -------------------------------------------- */

.intervalkeuze {
    display: inline-flex;
    align-self: center;
    gap: var(--ruimte-1);
    padding: var(--ruimte-1);
    background: var(--vlak-rustig);
    border-radius: 999px;
}

.intervalkeuze__optie {
    display: inline-flex;
    align-items: center;
    gap: var(--ruimte-2);
    min-height: 2.75rem;
    padding: 0 var(--ruimte-4);
    font-size: var(--tekst-s);
    font-weight: var(--gewicht-medium);
    color: var(--tekst-zacht);
    border-radius: 999px;
    cursor: pointer;
}

/* Which half is lit follows the radio, so the control tells the truth even
   when somebody changes it with the keyboard. */
#prijs-maand:checked ~ .intervalkeuze [for="prijs-maand"],
#prijs-jaar:checked ~ .intervalkeuze [for="prijs-jaar"] {
    color: var(--tekst);
    background: var(--vlak);
    box-shadow: var(--schaduw-s);
}

/* De zelfde schakelaar met de radio ín het label, zoals de themakeuze op
   Instellingen: dan kan het oplichten aan het label zelf hangen. */
.intervalkeuze__optie:has(:checked) {
    color: var(--tekst);
    background: var(--vlak);
    box-shadow: var(--schaduw-s);
}
.intervalkeuze__optie:has(:focus-visible) {
    outline: 3px solid var(--focusring);
    outline-offset: 2px;
}
.themakeuze .intervalkeuze { align-self: flex-start; }

/* The input is off-screen, so the focus ring has to be drawn on its label. */
#prijs-maand:focus-visible ~ .intervalkeuze [for="prijs-maand"],
#prijs-jaar:focus-visible ~ .intervalkeuze [for="prijs-jaar"] {
    outline: 3px solid var(--focusring);
    outline-offset: 2px;
}

/* The claim on the switch. A percentage no plan beats, rounded down, worked
   out from the prices themselves — see Planoverzicht. */
.intervalkeuze__winst {
    padding: 0 var(--ruimte-2);
    font-size: var(--tekst-xs);
    font-weight: var(--gewicht-medium);
    color: var(--op-selectie);
    background: var(--selectie);
    border-radius: 999px;
    white-space: nowrap;
}

/* Exactly one of the two prices is on the screen. Both are in the markup;
   the switch decides, and nothing has to be recalculated to change it. */
#prijs-maand:checked ~ .plannen .plan__jaar,
#prijs-jaar:checked ~ .plannen .plan__maand { display: none; }

/* ---- The cards --------------------------------------------------------- */

.plannen {
    display: grid;
    /* 15rem rather than 13: two cards side by side on a phone leaves each of
       them too narrow for "Volledige export met journaalpost" to be a line
       rather than a column of words. */
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: var(--ruimte-4);
    align-items: stretch;
}

.plan {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-2);
    padding: var(--ruimte-5);
    background: var(--vlak);
    border: 1px solid var(--paneel-rand);
    border-radius: var(--radius-xl);
    box-shadow: var(--schaduw-paneel);
}

.plan--kiesbaar { cursor: pointer; }
.plan--kiesbaar:hover { border-color: var(--selectie-rand); }

/* The one card that is filled rather than outlined. Which one differs per
   screen: signing up asks "what do you recommend", the subscription page asks
   "where am I now", and each passes its own answer to the partial. */
.plan--uitgelicht {
    color: var(--op-accent);
    background: var(--accent);
    border-color: var(--accent);
}
.plan--uitgelicht .plan__grondslag,
.plan--uitgelicht .plan__per,
.plan--uitgelicht .plan__vanaf,
.plan--uitgelicht .veld__hulp,
.plan--uitgelicht .status { color: inherit; opacity: 0.82; }
.plan--uitgelicht .plan__vink { color: inherit; }
.plan--uitgelicht .plan__winst,
.plan--uitgelicht .plan__kies {
    color: var(--accent);
    background: var(--op-accent);
}
.plan--uitgelicht .knop--primair {
    color: var(--accent);
    background: var(--op-accent);
    border-color: var(--op-accent);
}
.plan--uitgelicht .knop--primair:hover { opacity: 0.9; }

.plan__naam {
    margin: 0;
    font-family: var(--lettertype-kop);
    font-size: var(--tekst-l);
    font-weight: var(--gewicht-vet);
    line-height: var(--regelhoogte-kop);
}

.plan__bedrag {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--ruimte-1);
    margin: 0;
}
.plan__bedrag strong {
    font-size: var(--tekst-2xl);
    font-weight: var(--gewicht-vet);
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}
.plan__per { font-size: var(--tekst-s); color: var(--tekst-zacht); }

/* Its own line above the amount. Beside it, "vanaf € 32,50 p/m" is wide enough
   to push the "p/m" onto a second line, and a price that wraps mid-phrase is a
   price that has to be read twice. */
.plan__vanaf {
    flex: 0 0 100%;
    font-size: var(--tekst-xs);
    color: var(--tekst-zacht);
}

/* Same place as the saving, different claim: nothing is being discounted here,
   the amount simply is not settled yet. */
.plan__nader {
    display: block;
    margin-top: var(--ruimte-1);
}

.plan__grondslag {
    margin: 0;
    font-size: var(--tekst-xs);
    color: var(--tekst-zacht);
}

/* What a year actually saves, in euros, on the card it belongs to. A
   percentage is abstract; this is the amount that stays in the drawer. */
.plan__winst {
    display: inline-block;
    margin-top: var(--ruimte-1);
    padding: 0 var(--ruimte-2);
    font-weight: var(--gewicht-medium);
    color: var(--op-selectie);
    background: var(--selectie);
    border-radius: 999px;
}

.plan__actie {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-2);
    margin-top: var(--ruimte-2);
}
.plan__actie .knop { width: 100%; }
.plan__actie > .veld__hulp,
.plan__actie > .status { margin: 0; }

/* The card is the control on the signup screen, so the pill is a picture of
   the state rather than a button of its own -- two things to press for one
   choice is one too many. */
.plan__kies {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--ruimte-2);
    min-height: var(--raakvlak);
    padding: 0 var(--ruimte-4);
    font-size: var(--tekst-s);
    font-weight: var(--gewicht-medium);
    color: var(--accent);
    background: var(--accent-zacht);
    border-radius: var(--radius-m);
}
.plan__kies .plan__vink,
.plan__kies-aan { display: none; }
.plan__radio:checked ~ .plan__actie .plan__kies .plan__vink,
.plan__radio:checked ~ .plan__actie .plan__kies-aan { display: inline-flex; }
.plan__radio:checked ~ .plan__actie .plan__kies-uit { display: none; }

/* Chosen: the card is lit the way a selected chip is, never filled with the
   button colour -- a page of ten primary buttons is a page with none. */
.plan__radio:checked ~ .plan__naam { color: inherit; }
.plan--kiesbaar:has(.plan__radio:checked) {
    border-color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent), var(--schaduw-paneel);
}
.plan__radio:focus-visible ~ .plan__actie .plan__kies {
    outline: 3px solid var(--focusring);
    outline-offset: 2px;
}

.plan__punten {
    list-style: none;
    margin: var(--ruimte-2) 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-2);
    font-size: var(--tekst-s);
}
.plan__punten li {
    display: flex;
    align-items: flex-start;
    gap: var(--ruimte-2);
    line-height: 1.35;
}
.plan__vink { color: var(--accent); margin-top: 0.15em; }

/* ---- Everything side by side ------------------------------------------- */

/* A table this wide scrolls inside its own box rather than taking the page
   with it: a horizontal scrollbar under the whole screen is how a phone loses
   the menu. */
.tabelrol {
    /* Positioned so it becomes the containing block for what is inside it.
       The table's cells carry .visueel-verborgen labels, which are absolutely
       positioned -- without an anchor here they land against the page at the
       table's full width and give the whole document 60-odd pixels of
       sideways scroll that nothing on the screen explains. */
    position: relative;
    overflow-x: auto;
    scrollbar-color: var(--rand) transparent;
    scrollbar-width: thin;
}

.vergelijktabel { margin: 0; min-width: 34rem; }
.vergelijktabel th[scope="col"] {
    font-family: var(--lettertype-kop);
    font-size: var(--tekst-m);
    color: var(--tekst);
    text-align: center;
}
.vergelijktabel th[scope="row"] {
    font-weight: var(--gewicht-normaal);
    color: var(--tekst);
    text-align: left;
}
.vergelijktabel td { text-align: center; }

/* The column you are on, carried through from the card above it. */
.vergelijktabel__uit { background: var(--accent-zacht); }

.vergelijking__ja { color: var(--accent); }
.vergelijking__nee { color: var(--tekst-zacht); }

.vergelijking .veld__hulp { margin: var(--ruimte-4) 0 0; }

/* ---- Centred headings, for a page that is a price list ----------------- */

.paginakop--midden { justify-content: center; text-align: center; }
.paginakop--midden .paginakop__tekst { flex: 0 1 auto; }
.paginakop--midden .paginakop__sub { margin-inline: auto; }
.formulier__acties--midden { justify-content: center; }

/* ---- The way in --------------------------------------------------------- *
 *
 * The portal: logging in, creating an account, and everything else somebody
 * does before they are inside. Not a card floating on a page but the screen
 * itself, split in two -- a coloured half carrying the brand and a half
 * carrying the form -- the same shape the application has once you are in,
 * where the bar down the side is furniture and the work fills the rest.
 *
 * The coloured half always invites you to the side that is closed, and
 * switching walks it across to the other side.
 *
 * The switch is two radios and a transform. No script, for the same reason as
 * the price list's month/year switch: this is the front door, and a front door
 * that needs a module to load before the second half of it exists is a front
 * door that is sometimes broken.
 *
 * The slide works because both halves are exactly 50% wide, so moving one a
 * full width to the right and the other a full width to the left swaps them
 * without either leaving the screen. They keep their place in the grid, which
 * is what keeps each half's height honest -- absolutely positioning them, the
 * usual way this is built, forces one fixed height on a short form and a tall
 * one.                                                                       */

/* Full bleed. The layout gives every page a padded, capped column; this is the
   one screen that is the page. */
body:has(.toegang) .inhoud {
    max-width: none;
    padding: 0;
}

/* The two things the shell puts around a page, both of which the portal says
   itself: the wordmark, and the name in the footer. */
body:has(.toegang) .topbar,
body:has(.toegang) .voetregel { display: none; }

.toegang { min-height: 100dvh; }

.toegang__kaart {
    position: relative;
    overflow: hidden;
    min-height: 100dvh;
    background: var(--achtergrond);
}

/* ---- The coloured half -------------------------------------------------- */

.toegang__merk {
    display: flex;
    padding: var(--ruimte-6) var(--ruimte-5);
    color: var(--op-toegangvlak);
    background: var(--toegangvlak);
}

/* Both halves put their content in a column of the same width, so the brand
   and the form line up across the seam instead of drifting apart as the window
   grows. */
.toegang__merkinhoud {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-4);
    width: 100%;
    max-width: 26rem;
    margin-inline: auto;
}

/* align-self, want de gekleurde helft is een flex-kolom: zonder dit rekt het
   merk mee tot de volle breedte en klopt zijn verhouding niet meer. */
.toegang__logo {
    display: block;
    align-self: flex-start;
    width: auto;
    height: 4.5rem;
}

/* Gecentreerd in wat er onder het merk overblijft: gelijke marge boven en
   onder. Onderaan geplakt hangt hij op het inlogscherm -- dat korter is dan het
   aanmeldscherm -- onder een half leeg vlak. */
.toegang__boodschap {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--ruimte-3);
    margin-block: auto;
}
.toegang__boodschap h2 { margin: 0; color: inherit; }
.toegang__boodschap p { margin: 0; }

/* A label rather than a button: the control is the radio it points at, so the
   switch keeps working with no script and the browser keeps the state. */
.knop--omgekeerd {
    color: var(--toegangvlak);
    background: var(--op-toegangvlak);
    border-color: var(--op-toegangvlak);
}
.knop--omgekeerd:hover { opacity: 0.9; }

/* The plan somebody picked on /pakketten, carried in on the query string. */
/* Staat bij de aanmeldvelden en niet meer op het gekleurde vlak, dus de rand
   is een gewone rand en niet currentColor. */
.toegang__plan {
    padding: var(--ruimte-3) var(--ruimte-4);
    border: 1px solid var(--rand);
    border-radius: var(--radius-m);
}
.toegang__plan p { margin: 0; }
.toegang__plan-kop { font-size: var(--tekst-s); }
.toegang__plan-prijs {
    font-size: var(--tekst-l);
    font-weight: var(--gewicht-vet);
    font-variant-numeric: tabular-nums;
}
.toegang__plan-uitleg { font-size: var(--tekst-xs); color: var(--tekst-zacht); }
.toegang__merk a { color: inherit; }

/* ---- The half with the form --------------------------------------------- */

.toegang__vlak {
    display: grid;
    align-content: center;
    padding: var(--ruimte-6) var(--ruimte-5);
}

/* Both panels in one grid cell, so the half is as tall as whichever is open
   and the closed one is out of the accessibility tree entirely. */
.toegang__paneel {
    grid-area: 1 / 1;
    width: 100%;
    max-width: 26rem;
    margin-inline: auto;
}
.toegang__paneel h1 { margin-bottom: var(--ruimte-2); }
.toegang__intro { color: var(--tekst-zacht); font-size: var(--tekst-s); }
.toegang__voet { margin: var(--ruimte-4) 0 0; font-size: var(--tekst-s); }

#kant-inloggen:checked ~ .toegang__kaart .toegang__paneel--aanmelden,
#kant-inloggen:checked ~ .toegang__kaart .toegang__boodschap--bij-aanmelden,
#kant-aanmelden:checked ~ .toegang__kaart .toegang__paneel--inloggen,
#kant-aanmelden:checked ~ .toegang__kaart .toegang__boodschap--bij-inloggen { display: none; }

/* The focus ring belongs on the label: the radio it belongs to is off-screen. */
#kant-inloggen:focus-visible ~ .toegang__kaart [for="kant-inloggen"],
#kant-aanmelden:focus-visible ~ .toegang__kaart [for="kant-aanmelden"] {
    outline: 3px solid var(--focusring);
    outline-offset: 2px;
}

/* ---- Side by side, and the slide --------------------------------------- */

@media (min-width: 52rem) {
    .toegang__kaart {
        display: grid;
        grid-template-columns: 1fr 1fr;
        align-items: stretch;
    }

    .toegang__merk,
    .toegang__vlak {
        grid-row: 1;
        transition: transform 0.5s cubic-bezier(0.66, 0, 0.2, 1);
    }
    .toegang__merk { grid-column: 1; }
    .toegang__vlak { grid-column: 2; }

    /* Half a screen each, so one width right and one width left trades places. */
    #kant-aanmelden:checked ~ .toegang__kaart .toegang__merk { transform: translateX(100%); }
    #kant-aanmelden:checked ~ .toegang__kaart .toegang__vlak { transform: translateX(-100%); }

    .toegang__merk,
    .toegang__vlak { padding: var(--ruimte-7) var(--ruimte-6); }

    /* Het merk in de bovenhoek, zoals in de zijbalk van de app, en de tekst in
       het midden van het hele vlak. Uit de stroom gehaald, want anders staat de
       tekst gecentreerd in wat er ónder het merk overblijft en hangt hij lager
       dan het formulier ernaast. */
    .toegang__merkinhoud {
        position: relative;
        justify-content: center;
    }
    .toegang__logo {
        position: absolute;
        top: 0;
        left: 0;
        height: 5.5rem;
    }
    .toegang__boodschap { margin-block: 0; }
}

/* On a phone the coloured half is a band above the form rather than a column
   beside it: two columns do not fit, and a full-height panel would put the
   first field below the fold on the one screen where somebody is trying to
   get in. */
@media (max-width: 51.999rem) {
    .toegang, .toegang__kaart { min-height: 0; }
    .toegang__merk { padding: var(--ruimte-5) var(--ruimte-4); }
    .toegang__merkinhoud { gap: var(--ruimte-3); }
    .toegang__boodschap { margin-block: 0; }
    .toegang__boodschap p { font-size: var(--tekst-s); }
    .toegang__logo { height: 3.25rem; }
    .toegang__vlak { padding: var(--ruimte-5) var(--ruimte-4); }
}

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

/* ---- The screens without a switch --------------------------------------- *
 * Confirming an address, a forgotten password, a second factor, an invitation.
 * Same two halves, one form, and a coloured half that says something belonging
 * to that particular moment rather than one line for all of them.            */

.toegang--enkel .toegang__boodschap { margin-block: auto; }

/* ---- Rubrieken op een pagina -------------------------------------------- *
 *
 * Een kopje boven een groepje panelen, voor het scherm dat twee dingen tegelijk
 * doet. Beveiliging was vier gelijkwaardige witte blokken: tweestapsverificatie,
 * apparaten, pincode, vingerafdruk. Drie daarvan gaan over je account en reizen
 * met je mee; de pincode is een slot op de la van dít toestel. Zonder kopje ziet
 * niemand dat verschil, en dan lezen vier blokken als vier keer hetzelfde.
 *
 * Hetzelfde uiterlijk als de groepskop in de zijbalk, want het is dezelfde
 * handeling: een stapeltje een naam geven.                                    */

.rubriek {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-4);
}

.rubriek__kop {
    margin: 0;
    font-family: var(--lettertype);
    font-size: var(--tekst-xs);
    font-weight: var(--gewicht-medium);
    color: var(--tekst-zacht);
}

/* Kop en uitleg horen bij elkaar en staan dus dichter op elkaar dan .rubriek's
   gap tussen de panelen. Eén zin die zegt waar het slot zit -- op je account of
   op dit toestel -- want de kop benoemt het verschil wel en legt het niet uit. */
.rubriek__inleiding {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-2);
}

.rubriek__uitleg {
    max-width: 60ch;
    margin: 0;
    font-size: var(--tekst-s);
    color: var(--tekst-zacht);
}

/* ---- De instellingenlijst ----------------------------------------------- *
 * Eén paneel per rubriek in plaats van één per regel: dit is een inhoudsopgave
 * en geen verzameling losse dingen.                                          */

.instellinglijst {
    list-style: none;
    margin: 0;
    padding: 0;
    background: var(--vlak);
    border: 1px solid var(--paneel-rand);
    border-radius: var(--radius-xl);
    box-shadow: var(--schaduw-paneel);
    overflow: hidden;
}

.instelling {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-4);
    min-height: var(--raakvlak);
    padding: var(--ruimte-4) var(--ruimte-5);
    color: var(--tekst);
    text-decoration: none;
    border-bottom: 1px solid var(--rand);
}
.instellinglijst li:last-child .instelling { border-bottom: 0; }
.instelling:hover { color: var(--tekst); background: var(--vlak-rustig); }

.instelling__tekst { min-width: 0; }
.instelling__uitleg {
    display: block;
    margin-top: 2px;
    font-size: var(--tekst-s);
    color: var(--tekst-zacht);
}
.instelling .icoon { color: var(--tekst-zacht); }

/* Op de instellingen geen vak om de lijst en geen klein grijs kopje erboven:
   zonder kaarten is de kop wat de pagina indeelt, dus die krijgt het gewicht
   van een gewone h2. Hulp gebruikt dezelfde lijst en houdt zijn vak. */
.instellingen .instellinglijst {
    background: none;
    border: 0;
    border-top: 1px solid var(--rand);
    border-radius: 0;
    box-shadow: none;
}
.instellingen .instelling { padding-inline: 0; }
.instellingen .instelling:hover { background: none; }
.instellingen .instelling:hover .icoon { color: var(--tekst); }
.instellingen .rubriek__kop {
    font-family: var(--lettertype-kop);
    font-size: var(--tekst-l);
    font-weight: var(--gewicht-vet);
    color: var(--tekst);
}
.instellingen .rubriek + .rubriek { margin-top: var(--ruimte-4); }

/* Het stille woordje in de instellingenlijst: geen melding, wel zichtbaar. */
.instelling__let { color: var(--waarschuwing); font-weight: var(--gewicht-medium); }


/* ---------------------------------------------------------- hulpartikelen
 *
 * Tekst om te lezen terwijl je iets anders doet, dus op volle sterkte: de
 * stappen zijn hier de inhoud en geen bijzaak, anders dan .stappen elders.   */

.hulp__terug { margin: 0; font-size: var(--tekst-s); }
.hulp__terug a { display: inline-flex; align-items: center; gap: var(--ruimte-1); min-height: var(--raakvlak); }

.hulptekst > :first-child { margin-top: 0; }
.hulptekst > :last-child { margin-bottom: 0; }
.hulptekst h2 { margin: var(--ruimte-5) 0 var(--ruimte-2); font-size: var(--tekst-l); }
.hulptekst .stappen,
.hulptekst ul { padding-left: 1.25em; color: var(--tekst); }
.hulptekst li { margin-bottom: var(--ruimte-2); }
.hulptekst pre {
    padding: var(--ruimte-3);
    overflow-x: auto;
    font-family: var(--lettertype-mono);
    font-size: var(--tekst-s);
    background: var(--vlak-rustig);
    border: 1px solid var(--rand);
    border-radius: var(--radius-m);
}

/* Het knopje naast een moeilijk scherm. Stil, want het is hulp en geen
   handeling; met tekst, want een los vraagteken leest niet iedereen. */
.hulpknop { gap: var(--ruimte-2); padding-inline: var(--ruimte-3); font-size: var(--tekst-s); }

/* ---- Het hulppaneel ----------------------------------------------------- *
 *
 * Rechts, vast. Vanaf 60rem schuift de pagina opzij: de body krijgt rechts
 * de ruimte die het paneel inneemt, dus het scherm ernaast blijft helemaal
 * bruikbaar. Daaronder ligt het er overheen. Onder 40rem bestaat het niet:
 * hulppaneel.js laat de link dan gewoon de pagina openen.                  */

:root { --hulppaneel-breedte: clamp(22rem, 33vw, 30rem); }

.hulppaneel {
    position: fixed;
    inset: 0 0 0 auto;
    z-index: 16;
    display: flex;
    flex-direction: column;
    width: min(var(--hulppaneel-breedte), 100vw);
    padding-top: env(safe-area-inset-top);
    background: var(--achtergrond);
    border-left: 1px solid var(--rand);
    box-shadow: var(--schaduw-m);
}

.hulppaneel__kop {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ruimte-3);
    padding: var(--ruimte-3) var(--ruimte-3) var(--ruimte-3) var(--ruimte-5);
    border-bottom: 1px solid var(--rand);
}
.hulppaneel__titel { margin: 0; font-size: var(--tekst-l); }
.hulppaneel__titel:focus { outline: none; }

.hulppaneel__sluit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: var(--raakvlak);
    height: var(--raakvlak);
    color: var(--tekst);
    background: none;
    border: 0;
    border-radius: var(--radius-m);
    cursor: pointer;
}
.hulppaneel__sluit:hover { background: var(--vlak); }

.hulppaneel__inhoud {
    flex: 1;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: var(--ruimte-4) var(--ruimte-5);
}

.hulppaneel__voet {
    display: flex;
    flex-direction: column;
    gap: var(--ruimte-2);
    padding: var(--ruimte-3) var(--ruimte-5);
    padding-bottom: max(var(--ruimte-3), env(safe-area-inset-bottom));
    font-size: var(--tekst-s);
    border-top: 1px solid var(--rand);
}

@media (min-width: 60rem) {
    html[data-hulppaneel] body { padding-right: var(--hulppaneel-breedte); }
    html[data-hulppaneel] .hulppaneel { box-shadow: none; }
}


/* ---------------------------------------------------------- een venster
 *
 * Een handeling die af of afgebroken wordt, over de pagina heen.
 *
 * De breedte volgt de inhoud van de pagina, zodat het raster met velden erin
 * hetzelfde oogt als daarbuiten. De hoogte is begrensd en scrollt: het
 * productformulier met een foto erbij is op een telefoon langer dan het
 * scherm, en een venster dat buiten beeld doorloopt heeft een knop Opslaan
 * die niemand vindt.
 */
.venster {
    width: min(92vw, var(--breedte-inhoud));
    max-height: 85dvh;
    padding: var(--ruimte-5);
    border: 0;
    border-radius: var(--radius-l);
    background: var(--vlak);
    color: var(--tekst);
    overflow: auto;
}

.venster::backdrop { background: rgb(0 0 0 / 60%); }

/* De knoppen blijven staan waar ze staan. Op een telefoon is het
   productformulier langer dan het venster, en een Opslaan die je eerst moet
   zoeken is precies wat een venster hoort op te lossen. */
.venster .formulier__acties {
    position: sticky;
    /* Tot aan de echte onderrand, dwars door de padding van het venster heen:
       blijft de balk op 0 staan, dan scrollt de inhoud zichtbaar onder hem
       langs in die laatste centimeter. */
    bottom: calc(var(--ruimte-5) * -1);
    margin-bottom: calc(var(--ruimte-5) * -1);
    padding-block: var(--ruimte-4) var(--ruimte-5);
    background: var(--vlak);
}


/* -------------------------------------------------------------- de bon-QR
 *
 * Het enige scherm in deze app dat aan iemand anders getoond wordt. Dus groot,
 * wit, en verder leeg: een camera van een halve meter afstand heeft contrast
 * nodig en geen versiering. De code staat in een wit vlak ook als de app in de
 * donkere stand staat -- een omgekeerde QR-code lezen de meeste telefoons niet.
 */
.bonqr {
    max-width: min(92vw, 26rem);
    padding: 1.25rem;
    border: 0;
    border-radius: var(--radius-l);
    background: var(--vlak-rustig);
    color: var(--tekst);
    text-align: center;
}

.bonqr::backdrop { background: rgb(0 0 0 / 60%); }

.bonqr__code {
    padding: 0.75rem;
    background: #fff;
    border-radius: var(--radius-m);
}

.bonqr__code svg { display: block; width: 100%; height: auto; }

.bonqr__uitleg {
    margin: 0.75rem 0 1rem;
    font-size: var(--tekst-s);
    color: var(--tekst-zacht);
}

.lijst__acties { display: flex; align-items: center; gap: var(--ruimte-2); flex-shrink: 0; }

/* Bedragen in een lijst onder elkaar: even brede cijfers, anders danst de komma
   van regel naar regel. */
.lijst__bedrag { font-variant-numeric: tabular-nums; white-space: nowrap; }

.bonqr__mail { margin: 0 0 1rem; text-align: left; }
.bonqr__mail .veld { margin-bottom: 0.5rem; }

.bonqr__factuur { margin: 0 0 1rem; text-align: left; }

/* Het contante geld. De snelknoppen in twee kolommen: vier bedragen naast
   elkaar zijn op een telefoon vier te smalle doelen. */
.kasvenster { width: min(92vw, 26rem); }
.kasvenster .bon__totaal { margin-bottom: var(--ruimte-4); }
.kasvenster .veld__label { text-align: left; }

.kasvenster__snel {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--ruimte-2);
    margin-bottom: var(--ruimte-4);
}

.kasvenster__invoer { font-size: var(--tekst-l); text-align: right; font-variant-numeric: tabular-nums; }

.kasvenster__uitkomst {
    min-height: 1.5em;
    margin: var(--ruimte-3) 0;
    font-size: var(--tekst-l);
    font-weight: var(--gewicht-vet);
    font-variant-numeric: tabular-nums;
}
.kasvenster__uitkomst--tekort { color: var(--fout); }

.kasvenster__acties { display: grid; gap: var(--ruimte-2); }

.bonqr__factuur > summary {
    cursor: pointer;
    padding: var(--ruimte-2) 0;
    font-size: var(--tekst-s);
    color: var(--tekst-zacht);
}

.bonqr__factuur .veld { margin-bottom: var(--ruimte-2); }

/* Postcode en plaats naast elkaar: samen zijn ze één regel op een adres, en
   apart onder elkaar kost dat twee schermregels in een venster dat al vol is. */
.bonqr__paar { display: grid; grid-template-columns: 7rem 1fr; gap: var(--ruimte-2); }

.bon__knop { margin-top: 0.75rem; }

/* Het lijstje onder het wachtwoord bij het aanmelden: een grijs bolletje dat
   groen wordt als de regel gehaald is. De tekst zegt het ook, dus kleur is
   nooit het enige signaal -- alleen het vinkje erbij. */
.wachtwoordtips {
    list-style: none;
    margin: calc(-1 * var(--ruimte-2)) 0 0;
    padding: 0;
    font-size: var(--tekst-xs);
    color: var(--tekst-zacht);
}
.wachtwoordtips li { display: flex; align-items: center; gap: var(--ruimte-2); padding: 2px 0; }
.wachtwoordtips li::before {
    content: "";
    flex: 0 0 auto;
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 50%;
    background: var(--rand-sterk);
    transition: background-color 0.15s;
}
.wachtwoordtips li.is-gehaald { color: var(--tekst); }
.wachtwoordtips li.is-gehaald::before { background: var(--goed); }
.wachtwoordtips__eis { color: var(--tekst-zacht); }
