/* Brand tokens -- docs/specs/00_algemene_opzet.md §4.1-4.3 (colors) + §4.4-4.5
   (radius/typography). Do NOT source colors from docs/specs/DESIGN-TOKENS.md
   -- that file is structure-only reference from an unrelated project. */

@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/static/fonts/inter/inter-latin-400-normal.woff2') format('woff2');
}
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('/static/fonts/inter/inter-latin-500-normal.woff2') format('woff2');
}
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('/static/fonts/inter/inter-latin-600-normal.woff2') format('woff2');
}
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('/static/fonts/inter/inter-latin-700-normal.woff2') format('woff2');
}

:root {
    --color-black: #000000;
    /* Module 0 vervolg §A.1: renamed from --color-cream (same hex, same
       brand-mandated value per docs/specs/00_algemene_opzet.md §4.1) --
       marks "this is deliberately THE accent colour" (CTA's/actieve staat)
       at every call site, now that most other beige-tinted surfaces have
       moved to the neutral greys below. */
    --color-accent: #EEE8DD;
    /* v1.2 ('Nieuwe klant'-verfijning): hover-tint van --color-accent --
       herbruikbaar overal waar een accent-achtergrond een merkbaar
       donkerder :hover nodig heeft (nu: "Annuleren"-knop). */
    --color-accent-hover: #DDD2BE;
    --color-bg: #F3F4F6;
    --color-card-border: #E5E7EB;
    --color-table-hover: #F9FAFB;
    --color-text: #1A1A1A;
    --color-text-muted: #6B6B63;

    --color-success: #6B8F71;
    --color-warning: #C98A3E;
    --color-danger: #B5533C;
    /* v2.2 (klanten-afronding, finale fixes): de "Klant verwijderen"-knop
       wordt een volle, prominente danger-knop (was outline/subtiel) --
       een aparte hover-tint van --color-danger zelf, zelfde opbouw als
       --color-accent-hover t.o.v. --color-accent hierboven (1 vaste,
       merkbaar donkerdere tint, geen berekende kleur). */
    --color-danger-hover: #9C4631;
    --color-info: #6B7A8F;

    /* v2.2 (klanten-afronding, finale fixes) Deel 1.2: definitieve,
       gescoped kleurtokens voor de Type-badge (Zakelijk/Particulier) --
       vervangen de tijdelijke hergebruikte --color-accent/--color-accent-hover
       van v1.x. Bewust NIET dezelfde tokens als --color-success/--color-danger/
       --color-warning (dit is geen status, geen fout/waarschuwing -- een
       eigen, herkenbare kleurpaar per klanttype, zelfde pastel-bg +
       verzadigde-tekst-opbouw als de bestaande statusbadges). */
    --color-badge-zakelijk-bg: #DCE8F5;
    --color-badge-zakelijk-text: #2A5C8A;
    --color-badge-particulier-bg: #E9E1F5;
    --color-badge-particulier-text: #6B4A9E;

    /* v1.4 (klanten-afronding): "Opgelet!"-kaart (.client-alert, 'Klant
       bewerken' se "Klant verwijderen"-kadertje) -- zelfde opzet als
       --color-tip-bg/--color-tip-icon hieronder (2 tokens: 1 achtergrond,
       1 die zowel het icoon als de titel kleurt via currentColor), nu
       voor een "opgelet" i.p.v. "tip"-variant. --color-alert-icon
       hergebruikt --color-danger zelf (var(), geen losse hex) -- dit IS
       gewoon de bestaande foutkleur, geen nieuwe kleur nodig, enkel een
       eigen naam voor deze component-context. --color-alert-bg is
       dezelfde lichte tint als .status-badge--danger/.btn-danger:hover
       hun achtergrond (#fbeeeb), nu als hergebruikbaar token i.p.v. een
       3de plek die diezelfde hex-literal herhaalt. */
    --color-alert-bg: #FBEEEB;
    --color-alert-icon: var(--color-danger);

    /* v2.1 (klanten-afronding, laatste polishing): een 3de, nóg subtielere
       grijstint boven op --color-text-muted (#6B6B63) -- nodig zodra v2.1
       meerdere de-emphasized elementen tegelijk introduceerde die zelfs
       lichter dan --color-text-muted moeten ogen dan de gewone hint-/
       label-tekst elders (contactpersoon-/leveringsadres-"+ Toevoegen",
       "Standaardadres"/"Instellen als standaard"). 1 nieuw token i.p.v. per
       instantie een eigen letterlijke waarde. */
    --color-text-faint: #9CA3AF;

    /* Interactiestaten -- focus (projectbreed, zie DESIGN-TOKENS.md
       "Interactiestaten"). Bewust geen --color-black: #BBBBBB is zacht
       genoeg om zowel op een lichte (wit veld) als donkere (zwarte
       sidebar) achtergrond voldoende contrast te geven, dus 1 token
       volstaat -- geen aparte lichte/donkere variant nodig. */
    --color-focus-border: #BBBBBB;

    --radius-soft: 4px;
    --radius-tight: 4px;

    /* Auth-schermenfamilie enkel -- zie DESIGN-TOKENS.md "Auth-visuele
       correctie t.o.v. referentiesite". Niet een herziening van
       --radius-soft/--radius-tight zelf (die blijven gelden voor de rest
       van de app, zie docs/specs/00_algemene_opzet.md §4.4) -- op de
       referentiesite (mijn.besteloplossing.be) delen knop en invoerveld
       wél 1 radius, dus binnen .auth-body wordt dat hier bewust gelijkgetrokken. */
    --radius-auth: 12px;

    /* Exacte randkleur van de referentiesite (devtools computed style op
       .field-input input: rgb(233,234,240)) -- kouder/lichter dan onze
       algemene --color-card-border (#E5E0D6, warmer/crème-tint). Enkel
       binnen .auth-body toegepast, zie DESIGN-TOKENS.md "Randkleur
       formuliervelden" (3de correctieronde). */
    --color-card-border-auth: #E9EAF0;

    /* Punt 2 (v1.2): 1 icoon-groottetoken voor topbar-iconen (hamburger/
       pijltje, zoekveld) en 1 voor inline/kolom-iconen (potlood,
       sorteer-chevron, dropdown-pijltjes) -- voorheen kreeg elk icoon zijn
       eigen, los-van-elkaar-gekozen width/height, wat zichtbaar
       inconsistente visuele "zwaarte" gaf. */
    --icon-size-topbar: 20px;
    --icon-size-inline: 16px;

    /* 'Nieuwe klant' v1.1 punt 1: alle H1-paginatitels (Klantoverzicht,
       Nieuwe klant, ...) op 1 gedeeld token i.p.v. losse, per-pagina
       font-size-waarden. */
    --font-size-page-title: 1.25rem;

    /* 'Nieuwe klant' v1.1 punt 8: 1 gedeeld token voor de tekstgrootte van
       elk gewoon formulierveld/knop/dropdown -- de "Annuleren"/"Klant
       opslaan"-inconsistentie en het te grote Maatschappelijke-naam-veld
       kwamen allebei van eenzelfde patroon (font: inherit zonder een
       eigen font-size, waardoor de omgevende, ambient font-size lekte).
       Elk form-control/knop hieronder verwijst nu naar dit ene token --
       geen enkele losse 0.9rem-literal meer op die selectors. */
    --font-size-control: 0.9rem;

    /* 'Nieuwe klant' v1.1 §5.2: het voortgangsvinkje is BEWUST een
       eigen, frissere/vollere groentint dan het gedempte --color-success
       (#6B8F71) -- enkel voor dit ene element, de rest van de bestaande
       statuskleuren (badges, meldingen) blijft op het gedempte palet. */
    --color-progress-complete: #3FA34D;

    /* 'Nieuwe klant' v1.1 §5.3: eigen, apart genaamd tokenpaar voor de
       "Tip"-kaart -- semantisch los van --color-warning, ook al is
       --color-tip-icon toevallig dezelfde hex-waarde. */
    --color-tip-bg: #FDF6E3;
    --color-tip-icon: #C98A3E;

    /* v1.2 punt 4.2: 1 gedeeld token voor ELKE sectietitel binnen een
       kaart, ongeacht of die met een <h2> (kaarttitel, bv. "Facturatie")
       of een <h3> (sub-sectietitel binnen die kaart, bv.
       "Facturatiecontact") gemarkeerd is -- voorheen kreeg enkel h2 een
       expliciete font-size (1rem), h3 erfde de grotere browserdefault
       (~1.17em), vandaar "Facturatiecontact" zichtbaar groter dan
       "Facturatie" erboven. */
    --font-size-section-title: 1rem;
    /* v1.2 punt 4.1: 1 gedeelde marge tussen een titel (paginatitel of
       sectietitel) en zijn subtitel/omschrijving -- voorheen verschilde
       dit per instantie (.page-header__subtitle vs. de losse <p
       class="form-hint"> onder "Eerste contactpersoon"). */
    --spacing-title-subtitle: 0.25rem;
    /* v1.3 (klanten-afronding), punt 2: het bovenstaande token bleek te
       algemeen -- correct voor een titel gevolgd door een ECHTE subtitel
       (bv. "Eerste contactpersoon" -> "Ontvangt de bestelbevestiging..."),
       maar veel te krap voor een titel gevolgd door een formulierveld of
       gewone doorlopende tekst (bv. "Klanttype" -> de dropdown, "Tip" ->
       "Extra contactpersonen..."). Dit is nu het DEFAULT voor elke
       .console-card h2/h3 (zie hieronder); --spacing-title-subtitle blijft
       de opt-in uitzondering voor het echte titel+subtitel-geval. */
    --spacing-title-content: 1rem;
    /* v1.3 (klanten-afronding), punt 1: dezelfde krappe marge als
       .field-error (validatietekst onder een invoerveld) -- hergebruikt
       voor elke korte hint/waarschuwing die visueel bij het element
       erboven hoort, i.p.v. een losse, per-instantie margin-waarde
       (of een negatieve-margin-hack) per geval. */
    --spacing-hint: 0.5rem;

    /* 'Nieuwe klant' v1.1 §5.1: gemeten effectieve hoogte van
       .app-shell__topbar (getBoundingClientRect in de browser, met de
       huidige padding/icoongrootte) -- de sticky Voortgang/Tip-kaarten
       gebruiken dit als top-offset zodat ze niet onder de sticky topbar
       verdwijnen. 1 token i.p.v. dezelfde meting op 2 plaatsen te
       hardcoderen. */
    --layout-topbar-height: 58px;

    /* v2.0 (klanten-afronding, definitief) Deel 1.1: de content/sidebar-
       breedteverhouding van .client-page-layout (create/edit/klantenfiche)
       als eigen, gedeeld tokenpaar -- was voorheen een letterlijke
       "1fr 22rem" enkel op die ene grid-regel. Nu 1 plek die de KPI-rij
       (binnen de contentkolom) en de rechterkolom gegarandeerd laat
       uitlijnen, ongeacht welke pagina de layout hergebruikt. */
    --layout-content-width: 1fr;
    --layout-sidebar-width: 22rem;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    /* 'Nieuwe klant' v1.1 punt 12: iets ruimer dan de browserdefault
       (~1.15-1.2), projectbreed -- geen lokale override per formulier. */
    line-height: 1.5;
    background: var(--color-bg);
    color: var(--color-text);
    -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

/* Auth pages (login, wachtwoord-vergeten, MFA) */

.auth-body {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    /* Referentiesite toont geen enkele afscheiding tussen "pagina" en
       "kaart" (vlak wit, geen border/schaduw) -- zie DESIGN-TOKENS.md
       "Kader/achtergrondkleur rond de kaart". Enkel deze klasse, niet
       --color-bg zelf (blijft gelden voor console/tablet-home elders). */
    background: #ffffff;
}

.auth-shell {
    flex: 1;
    display: flex;
}

.auth-main {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2.5rem 1rem;
}

.auth-column {
    width: 100%;
    max-width: 430px;
    display: flex;
    flex-direction: column;
    align-items: center;
}

/* Achtergrondafbeelding rechts, enkel Desktop (>=1024px) -- zie de
   @media-blok onderaan dit bestand en DESIGN-TOKENS.md. Bevat sinds de
   2de correctieronde ook het logo (rechtsonder overlay, zie
   .auth-bg__logo) -- .auth-logo (boven de kaart) en .auth-footer
   (zwarte balk) zijn dan verwijderd, zie DESIGN-TOKENS.md "Logo
   verhuist naar de achtergrondafbeelding" / "Footer volledig verwijderd". */
.auth-bg {
    display: none;
}

/* §4.7: het echte Besteloplossing.be-logo (logo_white.svg, van
   mijn.besteloplossing.be -- dit project valt voorlopig onder dezelfde
   naam/branding), niet meer de "Teeltmanager"-tekst-placeholder. Wit op
   de (donkere, grijstinten) achtergrondafbeelding -- geen filter nodig,
   dit is al de juiste context voor de witte SVG. */
.auth-bg__logo {
    position: absolute;
    right: 2rem;
    bottom: 2rem;
    height: 32px;
    width: auto;
}

/* Referentiesite toont geen enkele afscheiding tussen "pagina" en
   "kaart" -- geen border, geen schaduw, geen apart wit vlak op een
   getinte pagina-achtergrond. .auth-card wordt daarom een neutrale
   layout-wrapper (zie DESIGN-TOKENS.md "Kader/achtergrondkleur rond de
   kaart"); dit overschrijft de eerdere "bewust behouden"-beslissing uit
   de vorige correctieronde, op basis van rechtstreekse screenshot-
   vergelijking. --color-bg zelf en .console-card (regiecentrum) blijven
   ongewijzigd -- deze override is specifiek voor .auth-card. */
.auth-card {
    width: 100%;
    /* 430px, exact breedte gemeten via devtools op de referentiesite
       (form/veld-rect width: 430px) -- was 380px, zie DESIGN-TOKENS.md
       "Formuliervelden (en de kaart) te smal" (3de correctieronde). */
    max-width: 430px;
}

.auth-card h1 {
    /* 32px, exacte referentiewaarde (devtools computed h1.fontSize) --
       was 1.4rem/22.4px. Zie DESIGN-TOKENS.md "Lettergrootte". */
    font-size: 2rem;
    font-weight: 700;
    margin: 0 0 0.35rem;
}

.auth-card .auth-subtitle {
    color: var(--color-text-muted);
    margin: 0 0 1.5rem;
    /* 16px, exacte referentiewaarde -- was 0.9rem/14.4px. */
    font-size: 1rem;
    line-height: 1.4;
}

.field { margin-bottom: 1.1rem; }

/* CSS-conventie (CLAUDE.md): korte, ondersteunende hulptekst onder een
   sectietitel -- voorheen leende de contactpersoon-intro op 'Nieuwe klant'
   hiervoor de class .auth-subtitle (puur qua uiterlijk, .auth-subtitle is
   verder een auth-schermenfamilie-specifieke class), wat zonder een
   .auth-card-voorouder geen enkel effect had (onopgemerkt, want de tekst
   oogde toevallig al passend als kale <p>). Eigen, generieke class i.p.v.
   een 2de kopie van "toevallig gelijkaardig uitziende class overnemen". */
.form-hint { color: var(--color-text-muted); font-size: 0.85rem; margin: 0 0 1rem; }

/* Placeholder-only formuliervelden (zie DESIGN-TOKENS.md) -- het
   <label> blijft in de DOM voor screenreaders (accessibele naam), maar
   is visueel verborgen; de zichtbare "labeltekst" staat als placeholder
   in het veld zelf (accounts/forms.py), zoals de referentiesite.

   Categorieën-verfijning, 3de vervolgsessie, grondoorzaak van de
   horizontale scrollbalk op de bewerkpagina: `.file-input input.sr-only`
   (het verborgen bestandsveld, zie layout/_file_input_field.html) verloor
   zijn `width: 1px` van `.client-form .field input { width: 100% }` (2
   classes + 1 element = hogere specificiteit dan deze ene class) --
   `clip`/`overflow` hielden het element wel onzichtbaar, maar de
   layout-BOX zelf bleef 100% van zijn (brede) ouder, wat de hele pagina
   breder maakte dan de viewport. `!important` hier is bewust: dit is een
   universele, "moet-altijd-gelden"-hulpklasse (verberg dit element
   visueel, zonder uitzondering) -- geen enkele andere regel hoort dit
   ooit te overschrijven, dus `!important` is hier de juiste, generieke
   fix voor élk huidig en toekomstig gebruik van `.sr-only`, niet enkel
   een lokale patch voor dit ene bestandsveld. */
.sr-only {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
}

.field-input { position: relative; }

.field-input .icon {
    position: absolute;
    left: 0.75rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--color-text-muted);
    display: flex;
    pointer-events: none;
}

.field-input input {
    width: 100%;
    padding: 0.6rem 0.85rem 0.6rem 2.5rem;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    font: inherit;
    font-size: var(--font-size-control);
    background: #ffffff;
    color: var(--color-text);
}

.field-input.has-toggle input { padding-right: 2.5rem; }

/* v2.1 (klanten-afronding, laatste polishing) punt 2: het browser-eigen
   kruisje van <input type="search"> (klantoverzicht-zoekveld) had de
   OS-accentkleur (blauw op deze Windows-installatie) -- vervangen door
   dezelfde subtiele, lichtgrijze iconkleur (--color-text-muted) als de rest
   van de app, via een eigen SVG (zelfde vinkje-stijl/stroke-breedte als
   .custom-select__check hierboven). Projectbreed op elk
   <input type="search"> (nu enkel het klantoverzicht-zoekveld, geldt gratis
   voor een toekomstig zoekveld elders). */
input[type="search"]::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
    width: 14px;
    height: 14px;
    cursor: pointer;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236B6B63' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3C/svg%3E") no-repeat center / contain;
}

.field-input .toggle {
    position: absolute;
    right: 0.6rem;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    /* Transparante rand i.p.v. border: 0 -- zelfde breedte in rust- en
       focus-status (enkel de kleur wisselt bij :focus, zie
       DESIGN-TOKENS.md "Interactiestaten"), dus geen verspringen. */
    border: 1px solid transparent;
    border-radius: var(--radius-tight);
    cursor: pointer;
    color: var(--color-text-muted);
    display: flex;
    padding: 0.25rem;
}

.field-checkbox {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 1.1rem;
    /* Geen referentiewaarde beschikbaar (de referentiescreenshots tonen
       geen vertrouwd-toestel-checkbox) -- proportioneel opgeschaald
       t.o.v. de nu grotere titel/subtekst, was 0.85rem. */
    font-size: 0.9rem;
    color: var(--color-text);
}

/* Vinkje-kleur (het vinkje zelf, niet de rand) -- browserstandaard is
   blauw, moet onze eigen zwart zijn. accent-color is de moderne,
   onderhoudsvriendelijke aanpak (breed ondersteund, geen volledig
   custom checkbox-component nodig). Projectbreed, niet enkel
   .field-checkbox -- elke toekomstige checkbox erft dit gratis. */
input[type="checkbox"] {
    accent-color: var(--color-black);
    /* Punt 8 (v1.2): browsers geven een checkbox standaard een eigen
       marge (~3-4px) los van de td-padding -- zonder deze reset was de
       ruimte links van het selectievakje in de eerste kolom (td-padding +
       checkbox-marge) niet exact gelijk aan de ruimte rechts van het
       potlood in de Acties-kolom (enkel td-padding, geen extra marge). */
    margin: 0;
}

.field-error {
    color: var(--color-danger);
    font-size: 0.8rem;
    margin-top: var(--spacing-hint);
}

.forgot-link {
    display: block;
    text-align: right;
    font-size: 0.875rem;
    margin: 0.4rem 0 1.4rem;
}

.forgot-link a {
    color: var(--color-text-muted);
    text-decoration: none;
    /* Transparante rand i.p.v. geen rand -- zelfde breedte in rust- en
       focus-status, zie DESIGN-TOKENS.md "Interactiestaten". */
    border: 1px solid transparent;
    border-radius: var(--radius-tight);
}
.forgot-link a:hover { text-decoration: underline; }

.back-link a { color: var(--color-text); text-decoration: underline; }

.btn-primary {
    width: 100%;
    padding: 0.55rem 1rem;
    /* Transparante rand i.p.v. border: 0 -- zelfde breedte in rust- en
       focus-status, zie DESIGN-TOKENS.md "Interactiestaten". */
    border: 1px solid transparent;
    border-radius: var(--radius-soft);
    background: var(--color-black);
    color: #ffffff;
    font: inherit;
    font-size: var(--font-size-control);
    font-weight: 600;
    cursor: pointer;
    /* Punt 4 (v1.2): sommige .btn-primary-instanties zijn een <a> (bv.
       "+ Nieuwe klant") -- zonder deze reset toont de browser-default
       underline op knoptekst, wat niet bij een knop-uiterlijk past. */
    text-decoration: none;
    transition: background-color 0.2s ease, color 0.2s ease;
}

/* Radius knop = radius invoerveld op de auth-schermen (referentiesite
   gebruikt overal 12px) -- uitzondering specifiek voor .auth-body, geen
   herziening van --radius-soft zelf. Zie DESIGN-TOKENS.md. */
.auth-body .btn-primary,
.auth-body .field-input input,
.auth-body .tenant-search input,
.auth-body .tenant-list__item button,
.auth-body .form-errors,
.auth-body .auth-qr img {
    border-radius: var(--radius-auth);
}

.auth-body .field-input input,
.auth-body .tenant-search input {
    border-color: var(--color-card-border-auth);
}

/* Punt 2 (v1.1): .btn-primary/.field-input input werden globaal
   compacter voor de SaaS-context (Manager/Admin-schermen) -- de
   auth-schermen behouden expliciet hun oude, grotere afmetingen, zelfde
   uitzonderingsprincipe als de radius/border-color-overrides hierboven. */
.auth-body .btn-primary {
    padding: 0.75rem;
    font-size: 1rem;
}
.auth-body .field-input input,
.auth-body .tenant-search input {
    padding: 0.9375rem 1rem 0.9375rem 2.9rem;
    font-size: 1rem;
}

/* Interactiestaten -- focus (projectbreed, CANONIEKE regel, zie
   DESIGN-TOKENS.md "Interactiestaten"): een :focus-status wijzigt
   UITSLUITEND border-color (naar --color-focus-border) -- nooit
   border-width/radius/padding/outline/box-shadow erbovenop, dat doet
   het element zichtbaar "verspringen"/groeien. 1 token voor elke
   achtergrond (licht of donker, zie de tokendefinitie hierboven) --
   geen aparte wit/zwart-varianten meer nodig, ook niet op de zwarte
   sidebar. Geplaatst NA de border-color-override hierboven: gelijke
   specificiteit (2 classes + element), dus bronvolgorde beslist --
   :focus-visible moet hier winnen, ook op de auth-schermen.
   .field-input .toggle (het oog-icoon) hoort hier ook bij: kreeg een
   permanente, transparante rand (zie de regel hierboven) net om deze
   kleurwissel zonder verspringen mogelijk te maken -- zonder eigen
   rand in rust-status zou een focus-rand het element alsnog doen
   groeien. button.status-badge (v1.1 punt 4) krijgt dezelfde
   permanente transparante rand hieronder, om dezelfde reden.

   v1.3 punt 5, grondoorzaak-fix: dit was al de bedoelde, ene canonieke
   regel -- de regressie kwam van nieuwe componenten die er nooit aan
   toegevoegd werden (.filter-select in v1.2, de status-badge-knop had
   zelfs een eigen, afwijkende :focus-visible/outline-regel verderop in
   dit bestand, zie de doorverwijzing bij button.status-badge hieronder)
   en dus terugvielen op de dikke, zwarte browserstandaard-outline.
   Elke nieuwe interactieve klasse moet hier toegevoegd worden, nooit een
   eigen :focus/:focus-visible-regel elders definiëren. :focus (niet
   -visible) is hier bewust vervangen door :focus-visible: toont de rand
   enkel bij toetsenbord-focus, niet bij een muisklik -- exact wat de
   status-badge-knop hieronder al deed, nu projectbreed consistent. */
.field-input input:focus-visible,
.tenant-search input:focus-visible,
.tenant-list__item button:focus-visible,
.console-create-form input:focus-visible,
.field-input .toggle:focus-visible,
.forgot-link a:focus-visible,
.btn-primary:focus-visible,
.btn-secondary:focus-visible,
.btn-danger:focus-visible,
.client-form .field input:focus-visible,
.client-form .field select:focus-visible,
.modal__cancel:focus-visible,
.custom-select__toggle:focus-visible,
.phone-input .iti input.iti__tel-input:focus-visible,
button.status-badge:focus-visible {
    border-color: var(--color-focus-border);
    outline: none;
}

/* Checkbox is een uitzondering op de regel hierboven: een native
   (niet-appearance:none) checkbox negeert `border` volledig -- zelfs
   een inline `border` heeft geen enkel effect, getest en bevestigd.
   `outline` is hier dus de ENIGE bruikbare haak, niet `border-color`.
   Enkel `outline-color` wijzigen (niet `outline-style`/`-width`) is
   het equivalent van "enkel de kleur wijzigt": de browser bepaalt zelf
   nog steeds of/wanneer er een outline getekend wordt (bv. wél bij
   toetsenbord-focus, niet bij muisklik), enkel de kleur ervan wordt
   overschreven. Zie DESIGN-TOKENS.md "Interactiestaten". */
input[type="checkbox"]:focus,
input[type="radio"]:focus {
    outline-color: var(--color-focus-border);
}

.btn-primary:hover { opacity: 0.9; }

/* Uitzondering specifiek voor .btn-primary BINNEN .auth-card (geen
   wijziging van de globale .btn-primary-regel hierboven, die nog
   steeds zwart is voor Manager/Admin-schermen elders in de app --
   §4.4-motivatie "te weinig contrast op de lichte paginabackground"
   geldt daar onverkort). Op de auth-schermen volgt de knop nu bewust de
   referentiesite: lichtgrijs/beige in rust, zwart bij hover, vloeiende
   overgang via de transition op .btn-primary zelf. Zie DESIGN-TOKENS.md
   "Aanmelden-knop -- kleur" (3de correctieronde). */
.auth-card .btn-primary {
    background: var(--color-accent);
    color: var(--color-text);
}

.auth-card .btn-primary:hover {
    background: var(--color-black);
    color: #ffffff;
    opacity: 1;
}

.back-link {
    text-align: center;
    margin-top: 1.25rem;
    font-size: 0.875rem;
    color: var(--color-text-muted);
}

.form-errors {
    background: #fbeeeb;
    border: 1px solid var(--color-danger);
    color: var(--color-danger);
    border-radius: var(--radius-tight);
    padding: 0.6rem 0.75rem;
    margin-bottom: 1rem;
    font-size: 0.85rem;
}

.auth-help {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    line-height: 1.5;
    margin-bottom: 1.25rem;
}

.auth-qr {
    display: flex;
    justify-content: center;
    margin-bottom: 1rem;
}

.auth-qr img {
    width: 180px;
    height: 180px;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    padding: 0.5rem;
    background: #fff;
}

.auth-secret {
    text-align: center;
    font-family: 'SFMono-Regular', Consolas, monospace;
    font-size: 0.85rem;
    letter-spacing: 0.05em;
    color: var(--color-text-muted);
    margin-bottom: 1.25rem;
    word-break: break-all;
}

/* Achtergrondafbeelding (docs/background.jpeg) -- enkel Desktop
   (>=1024px), zie DESIGN-TOKENS.md voor de motivatie van elke keuze
   hieronder (grijstinten via CSS-filter, linksboven, cover).
   flex: 0 0 50% (geen grow/shrink) i.p.v. flex: 1 1 50% -- met grow:1
   op zowel dit paneel als .auth-main (ook flex:1) verdeelde de vorige
   versie de restruimte gelijk over BEIDE (elk +25%), wat dit paneel op
   75% i.p.v. de bedoelde 50% bracht (zie DESIGN-TOKENS.md "Achtergrond-
   afbeelding max. 50%"). Met een vaste basis van 50% en geen grow blijft
   dit paneel exact op 50%, en .auth-main (flex:1) vult de andere 50%. */
@media (min-width: 1024px) {
    .auth-bg {
        display: block;
        position: relative;
        flex: 0 0 50%;
        max-width: 50%;
        background-image: url('/static/img/background.jpeg');
        background-size: cover;
        background-position: left top;
        background-repeat: no-repeat;
        filter: grayscale(100%);
    }
}

/* Authenticated app shell (nav/layout) -- §3.
   Breakpoints (not specified exactly in §3.5, chosen here as the project
   default -- keep these consistent in every future module):
   smartphone < 768px, tablet 768-1023px, desktop >= 1024px. */

.app-body {
    margin: 0;
    background: var(--color-bg);
}

.impersonation-banner {
    /* Module 0 vervolg §A.1: moved off the accent colour -- this is a
       "subtiele, niet-hinderlijke balk" (§3.1), not a CTA/actieve staat,
       so it takes a plain neutral surface now instead of the beige accent. */
    background: var(--color-card-border);
    color: var(--color-text);
    font-size: 0.85rem;
    padding: 0.5rem 1rem;
    display: flex;
    gap: 0.75rem;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
}

.impersonation-banner a { text-decoration: underline; font-weight: 600; }

.app-shell { display: flex; min-height: 100vh; }

/* Desktop sidebar -- §3.1: black, text+icon items, active = bold, no
   colored background, sub-items collapsible in place. */
.app-shell__sidebar {
    display: none;
    flex-direction: column;
    width: 240px;
    flex-shrink: 0;
    background: var(--color-black);
    color: #ffffff;
    padding: 1.5rem 1rem;
}

.app-shell__sidebar-logo {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    color: #ffffff;
    font-weight: 700;
    font-size: 1.05rem;
    margin-bottom: 1.5rem;
    padding: 0 0.5rem;
}
.app-shell__sidebar-logo .app-shell__sidebar-logo-mark { display: none; }
.app-shell__sidebar-logo-word { white-space: nowrap; }

/* Sidebar collapse-to-icon-only (§A.3) -- toggled on <html>, not <body>:
   the state-restoring inline script in base_shell.html's <head> runs
   before <body> exists. */
html.sidebar-collapsed .app-shell__sidebar { width: 76px; padding: 1.5rem 0.5rem; }
html.sidebar-collapsed .app-shell__sidebar-logo { justify-content: center; margin-bottom: 1rem; }
html.sidebar-collapsed .app-shell__sidebar-logo-mark { display: flex; }
html.sidebar-collapsed .app-shell__sidebar-logo-word,
html.sidebar-collapsed .sidebar-nav__link span:not(.sidebar-nav__chevron),
html.sidebar-collapsed .sidebar-nav__chevron,
html.sidebar-collapsed .sidebar-nav__subnav {
    display: none;
}
html.sidebar-collapsed .sidebar-nav__link { justify-content: center; }

/* Tooltip (§A.3) -- shown on hover for the collapsed sidebar's icon-only
   nav items (via [data-tooltip], only relevant once labels are hidden)
   and reused for table-cell truncation tooltips + the Acties-kolom
   potlood-icoon (§B.2).
   v1.3 punt 3, grondoorzaak-fix: dit was tot v1.2 een CSS-only
   ::after-pseudo-element, position:absolute t.o.v. het element zelf --
   werd afgeknipt zodra dat element binnen een ouder met overflow !=
   visible zat (.table-scroll, zie punt 9 v1.1), zichtbaar als een lege/
   onvolledige balk. Vervangen door static/js/tooltip.js: 1 gedeelde
   position:fixed-laag t.o.v. de body, positie berekend via
   getBoundingClientRect() -- onafhankelijk van welke scroll-container het
   gehoverde element omsluit. [data-tooltip]-attributen op de templates
   blijven ongewijzigd, enkel het renderingsmechanisme wijzigde. */
.js-tooltip {
    position: fixed;
    top: 0;
    left: 0;
    background: var(--color-black);
    color: #ffffff;
    font-size: 0.75rem;
    font-weight: 400;
    white-space: nowrap;
    padding: 0.3rem 0.6rem;
    border-radius: var(--radius-tight);
    z-index: 1000;
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.1s ease;
}
.js-tooltip.is-visible { opacity: 1; visibility: visible; }

.sidebar-nav { list-style: none; margin: 0; padding: 0; flex: 1; }
.sidebar-nav li { margin-bottom: 0.15rem; }

.sidebar-nav__link {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.6rem 0.5rem;
    border-radius: var(--radius-tight);
    color: rgba(255, 255, 255, 0.85);
    text-decoration: none;
    font-size: 0.9rem;
    cursor: pointer;
    list-style: none;
}
.sidebar-nav__link::-webkit-details-marker { display: none; }
.sidebar-nav__link.is-active { font-weight: 700; color: #ffffff; }
.sidebar-nav__link:hover { color: #ffffff; }
.sidebar-nav__chevron { margin-left: auto; transition: transform 0.15s; }
.sidebar-nav__group[open] > .sidebar-nav__link .sidebar-nav__chevron { transform: rotate(180deg); }

.sidebar-nav__subnav { list-style: none; margin: 0; padding: 0 0 0 2.1rem; }
.sidebar-nav__subnav a {
    display: block;
    padding: 0.4rem 0.5rem;
    color: rgba(255, 255, 255, 0.7);
    text-decoration: none;
    font-size: 0.85rem;
}
.sidebar-nav__subnav a.is-active { font-weight: 700; color: #ffffff; }

/* Tablet icon-rail -- §3.2: compact, Home + module icons, active icon gets
   the accent-stijl round beige badge (§4.4). */
.icon-rail {
    display: none;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    width: 64px;
    flex-shrink: 0;
    background: #ffffff;
    border-right: 1px solid var(--color-card-border);
    padding: 1rem 0;
}

.icon-rail__item {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-soft);
    color: var(--color-text-muted);
    text-decoration: none;
}
.icon-rail__item.is-active { background: var(--color-accent); color: var(--color-text); }

.app-shell__main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
/* Horizontale padding gelijk aan .page-header/.app-shell__topbar (beide
   1.5rem) -- punt 12 (v1.1): lijnt de tabelinhoud uit met de titel.
   Top-padding verkleind -- punt 7 (v1.2, was 2rem -> 1.25rem) en verder
   verkleind in v1.3 punt 7 (1.25rem -> 0.75rem): nog te veel lege ruimte
   tussen de toolbar-rij en de tabel/kaart daaronder. Bottom blijft 2rem
   (footer-afstand, geen klacht daarover). */
.app-shell__content { flex: 1; padding: 0.75rem 1.5rem 2rem; }

.module-placeholder h1 { margin-top: 0; }

/* Tablet Home-scherm -- §3.2 + §4.7 (white logo, so a dark band like the
   sidebar), replaces .app-shell__content while not inside a module. */
.tablet-home { display: none; }

.tablet-home__logo {
    background: var(--color-black);
    color: #ffffff;
    font-weight: 700;
    padding: 1.5rem;
    text-align: center;
}

.tablet-home__grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
    padding: 1.5rem;
}

.tablet-home__card {
    background: var(--color-accent);
    border-radius: var(--radius-soft);
    padding: 1.25rem 0.75rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    text-decoration: none;
    color: var(--color-text);
    font-size: 0.8rem;
    text-align: center;
}


/* Desktop footer -- §3.1, visible on every desktop screen. */
.app-shell__footer {
    display: none;
    padding: 1rem 2rem;
    font-size: 0.75rem;
    color: var(--color-text-muted);
    border-top: 1px solid var(--color-card-border);
}
.app-shell__footer a { color: var(--color-text-muted); text-decoration: underline; }

/* Topbar + titelbalk (Module 0 vervolg §A.2/§A.4) -- sits beside the
   sidebar (not above it).

   'Nieuwe klant' v1.1 punt 1, 2de grondoorzaak-fix: de eerste poging zette
   position:sticky rechtstreeks op .app-shell__topbar, maar liet het
   wrappende .app-shell__header-element (topbar + titelbalk samen) staan --
   een sticky element mag dan wel zelf sticky zijn, zijn "reisruimte" blijft
   begrensd door de doos van zijn eigen ouder (.app-shell__header), die
   maar net zo hoog is als topbar+titelbalk samen. Zodra je verder scrolt
   dan die ~150px is de sticky-ouder zelf al voorbij, en verdwijnt de
   topbar gewoon mee (empirisch bevestigd: getBoundingClientRect().top liep
   ver in het negatieve i.p.v. op 0 te blijven). Fix: .app-shell__header is
   volledig verwijderd uit layout/templates/layout/base_shell.html --
   .app-shell__topbar en .page-header zijn nu rechtstreekse kinderen van
   .app-shell__main (de kolom die de VOLLEDIGE paginahoogte omvat), zodat de
   topbar binnen die kolom kan blijven plakken over de hele scroll. */
.app-shell__topbar {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--color-bg);
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.65rem 1.5rem;
    border-bottom: 1px solid var(--color-card-border);
}

.topbar__hamburger {
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius-tight);
    padding: 0.4rem;
    /* Punt 12 (v1.1): de padding hierboven vergroot enkel het klikvlak --
       zonder correctie duwt ze het icoon zelf 0.4rem verder naar binnen
       dan de titel ernaast. margin-left trekt enkel het icoon terug op
       lijn, het klikvlak blijft even groot. */
    margin-left: -0.4rem;
    display: flex;
    /* v1.3 punt 2: expliciete centrering van de klik-target-box, i.p.v.
       impliciet vertrouwen op "1 kind, dus wel gecentreerd" -- blijft ook
       correct als deze box ooit een tweede kind/badge krijgt. */
    align-items: center;
    justify-content: center;
    color: var(--color-text);
    cursor: pointer;
}
@media (max-width: 1023px) {
    .topbar__hamburger { display: none; }
}

/* Punt 8 (v1.1) / v1.3 punt 2 (grondoorzaak-fix, zie de toelichting bij
   ICON_MENU in layout/nav.py): geen los icon-swap-element, wel een
   kruisvervaging TUSSEN twee kinderen van hetzelfde <svg>-element (zelfde
   viewBox, zelfde overgeërfde stroke-width) -- de 3 hamburgerlijnen
   faden weg, .hamburger-arrow (een standaard chevron-right-pad, exact
   dezelfde armlengte/verhoudingen als elk ander chevron-icoon in de app)
   faded in. Geen rotatie/schaling meer nodig: de eerdere aanpak (lijnen
   ROTEREN tot een pijlvorm) gaf een andere effectieve glyph-grootte dan
   een echt chevron-icoon (16-eenheden lijnen vs. de ~8.49-eenheden armen
   van ICON_CHEVRON) en een niet-gecentreerde vorm (pivot op de rand,
   niet op het viewBox-midden) -- vandaar de twee eerdere, mislukte
   pogingen om dit enkel via her-schalen te verbeteren. */
.topbar__hamburger .hamburger-line,
.topbar__hamburger .hamburger-arrow {
    transition: opacity 0.2s ease;
}
.topbar__hamburger .hamburger-arrow { opacity: 0; }
html.sidebar-collapsed .topbar__hamburger .hamburger-line { opacity: 0; }
html.sidebar-collapsed .topbar__hamburger .hamburger-arrow { opacity: 1; }

.topbar__hamburger svg {
    width: var(--icon-size-topbar);
    height: var(--icon-size-topbar);
}

/* Punt 2 (v1.2): icoon-groottetoken consistent toegepast -- potlood
   (Acties-kolom) en taal-dropdown-pijltje worden allemaal het
   "inline"-formaat, ongeacht welke width/height-attributen de <svg> in
   de template nog draagt. De sorteer-chevron (v1.3 punt 1) heeft een
   eigen, kleinere vaste afmeting -- zie .sort-header__arrow svg
   hieronder, geen deel meer van deze gedeelde inline-groep. */
.icon-btn svg,
.topbar__language svg {
    width: var(--icon-size-inline);
    height: var(--icon-size-inline);
}

.topbar__spacer { flex: 1; }

.topbar__help {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--color-text-muted);
    text-decoration: none;
    font-size: 0.85rem;
    white-space: nowrap;
}
.topbar__help:hover { color: var(--color-text); }

.topbar__separator {
    width: 1px;
    height: 24px;
    background: var(--color-card-border);
}

.topbar__dropdown { position: relative; }

.topbar__language {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius-tight);
    padding: 0.4rem 0.5rem;
    font: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--color-text);
    cursor: pointer;
}

.topbar__avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: 0;
    background: var(--color-accent);
    color: var(--color-text);
    font-weight: 700;
    font-size: 0.85rem;
    cursor: pointer;
}

.topbar__menu {
    display: none;
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    min-width: 200px;
    background: #ffffff;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    box-shadow: 0 5px 10px rgba(30, 32, 37, 0.12);
    padding: 0.4rem;
    z-index: 25;
}
.topbar__dropdown.is-open .topbar__menu { display: block; }
.topbar__menu form { display: block; }
.topbar__menu button,
.topbar__menu a {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: 0;
    padding: 0.5rem 0.6rem;
    border-radius: var(--radius-tight);
    font: inherit;
    font-size: 0.85rem;
    color: var(--color-text);
    text-decoration: none;
    cursor: pointer;
}
.topbar__menu button:hover,
.topbar__menu a:hover { background: var(--color-table-hover); }
.topbar__menu button.is-active { font-weight: 700; }
.topbar__menu-user {
    padding: 0.5rem 0.6rem 0.75rem;
    border-bottom: 1px solid var(--color-card-border);
    margin-bottom: 0.4rem;
}
.topbar__menu-user strong { display: block; font-size: 0.9rem; }
.topbar__menu-user span { display: block; font-size: 0.8rem; color: var(--color-text-muted); }

.topbar__logout button {
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius-tight);
    padding: 0.5rem;
    display: flex;
    color: var(--color-text-muted);
    cursor: pointer;
}
.topbar__logout button:hover { color: var(--color-danger); }

.topbar__back-link {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--color-text-muted);
    text-decoration: none;
    font-size: 0.85rem;
    white-space: nowrap;
}
.topbar__back-link:hover { color: var(--color-text); }

/* 'Nieuwe klant' v1.2 punt 4.3, grondoorzaak-fix: align-items:center
   (punt 4 v1.2 hierboven, ouder) maakte de marge BOVEN de titel afhankelijk
   van de hoogte van de meta-kolom (breadcrumb + acties) ernaast -- op
   Klantoverzicht is die kolom een volledige zoek-/filter-toolbar (hoog),
   op "Nieuwe klant" enkel een korte breadcrumb (laag), waardoor de
   gecentreerde titel op Klantoverzicht zichtbaar verder van de top van de
   rij kwam te staan. flex-start zet de titel altijd op exact dezelfde
   afstand (padding-top hierboven) van de bovenkant van de rij, onafhankelijk
   van wat er in de meta-kolom staat -- dit weegt zwaarder dan de
   esthetische reden voor de eerdere center-keuze (die enkel gold voor
   pagina's met een korte, gestapelde meta-kolom). */
.page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1.5rem;
    /* v1.3 punt 7: 1.25rem -> 1rem -- de titel-kolom kreeg met de nieuwe
       subtitel (zie .page-header__titlegroup) een 2de tekstregel en dus
       meer eigen hoogte; iets minder padding houdt de totale kopband
       ongeveer even hoog als voorheen i.p.v. merkelijk groter. */
    padding: 1rem 1.5rem;
    flex-wrap: wrap;
}
.page-header__title { display: flex; align-items: center; gap: 0.75rem; }
/* v1.3 punt 7: titel + subtitel gestapeld, los van een eventueel
   page_icon-blok ervoor (vandaar deze extra kolom-wrapper i.p.v. de
   stapeling rechtstreeks op .page-header__title te zetten). */
.page-header__titlegroup { display: flex; flex-direction: column; gap: var(--spacing-title-subtitle); }
.page-header__titlegroup h1 { margin: 0; font-size: var(--font-size-page-title); }
.page-header__subtitle { margin: 0; font-size: 0.85rem; color: var(--color-text-muted); }
/* gap 0.6rem -> 0.85rem: iets meer ademruimte tussen breadcrumb en de
   actieknop erdoor (punt 4). */
.page-header__meta { display: flex; flex-direction: column; align-items: flex-end; gap: 0.85rem; }

.breadcrumb {
    list-style: none;
    display: flex;
    gap: 0.4rem;
    margin: 0;
    padding: 0;
    font-size: 0.8rem;
    color: var(--color-text-muted);
}
.breadcrumb li:not(:last-child)::after { content: '/'; margin-left: 0.4rem; }
.breadcrumb a { color: var(--color-text-muted); text-decoration: none; }
.breadcrumb a:hover { text-decoration: underline; }

/* align-items: flex-start -- zonder dit rekt een korte sibling (bv. de
   "+ Nieuwe klant"-knop) zich over de volledige hoogte van een langere
   buur (punt 7's wrappende zoek/filter-toolbar op mobiel) uit, wat een
   veel te hoge knop geeft. */
/* Generieke outline/secundaire knop -- oorspronkelijk enkel gescopet onder
   .page-header__actions; nu ook hergebruikt door .client-form__footer
   ("Annuleren"), vandaar losgetrokken tot een projectbrede class i.p.v.
   een 2de, letterlijke kopie van dezelfde stijlwaarden te schrijven. */
.btn-secondary {
    width: auto;
    padding: 0.6rem 1.25rem;
    background: none;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-soft);
    color: var(--color-text);
    font: inherit;
    /* v1.1 punt 8, grondoorzaak: ontbrak hier -- .btn-secondary erfde zo
       de ambient font-size van zijn container i.p.v. dezelfde grootte als
       .btn-primary (die dit al wel expliciet zette), vandaar "Annuleren"
       zichtbaar groter dan "Klant opslaan". */
    font-size: var(--font-size-control);
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
}
.btn-secondary:hover { background: var(--color-table-hover); }
/* v2.1 (klanten-afronding, laatste polishing): de klantfiche se
   "Bewerken"-knop volgt nu dezelfde huisstijl-accentkleur als
   .client-form__footer .btn-secondary ("Annuleren") -- subtiele
   secundaire actie i.p.v. de generieke grijze outline. Modifier, niet een
   herziening van .btn-secondary zelf (die blijft de generieke stijl voor
   elke andere plek die het ongewijzigd gebruikt). */
.btn-secondary--accent { background: var(--color-accent); border-color: transparent; }
.btn-secondary--accent:hover { background: var(--color-accent-hover); }

.page-header__actions { display: flex; align-items: flex-start; gap: 0.75rem; }
.page-header__actions .btn-primary { width: auto; padding: 0.6rem 1.25rem; }

@media (max-width: 767px) {
    .app-shell__topbar { padding: 0.5rem 1rem; }
    .topbar__help span { display: none; }
    .page-header { padding: 1rem; flex-direction: column; align-items: stretch; }
    .page-header__meta { align-items: flex-start; }
}

/* Smartphone bottom-nav -- §3.4: max 4 module items + Meer, fixed B-mark. */
.bottom-nav {
    display: none;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    background: #ffffff;
    border-top: 1px solid var(--color-card-border);
    align-items: stretch;
    z-index: 10;
}

/* Shared "B" brand-mark glyph -- logo placeholder (no brand asset supplied
   yet), reused wherever an icon-only Besteloplossing mark is needed:
   smartphone bottom-nav (always) and the collapsed desktop sidebar (§A.7). */
.brand-mark {
    flex-shrink: 0;
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #ffffff;
    background: var(--color-black);
    font-weight: 700;
    text-decoration: none;
    border-radius: var(--radius-tight);
}

.bottom-nav__brand { margin: 0.4rem 0 0.4rem 0.4rem; }

.bottom-nav__item {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.15rem;
    padding: 0.4rem 0.1rem 0.5rem;
    color: var(--color-text-muted);
    text-decoration: none;
    font-size: 0.65rem;
}
.bottom-nav__item.is-active { color: var(--color-text); }
.bottom-nav__item.is-active svg { background: var(--color-accent); border-radius: 50%; padding: 3px; box-sizing: content-box; }

.more-list { list-style: none; margin: 0 0 1.5rem; padding: 0; }
.more-list li { border-bottom: 1px solid var(--color-card-border); }
.more-list a {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.85rem 0.25rem;
    text-decoration: none;
    color: var(--color-text);
}

@media (min-width: 1024px) {
    .app-shell__sidebar { display: flex; }
    .app-shell__footer { display: block; }
}

@media (min-width: 768px) and (max-width: 1023px) {
    body:not(.in-module) .tablet-home { display: block; }
    body:not(.in-module) .app-shell__content { display: none; }
    body.in-module .icon-rail { display: flex; }
}

@media (max-width: 767px) {
    .bottom-nav { display: flex; }
    .app-shell__content { padding-bottom: 4.5rem; }
}

/* Tenant-choice screen (§3.3) */

.tenant-search { margin-bottom: 1.25rem; }
.tenant-search input {
    width: 100%;
    padding: 0.9375rem 1rem 0.9375rem 2.9rem;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    font: inherit;
    background: #ffffff;
    color: var(--color-text);
}

.tenant-list { list-style: none; margin: 0; padding: 0; max-height: 320px; overflow-y: auto; }
.tenant-list__item + .tenant-list__item { margin-top: 0.5rem; }
.tenant-list__item button {
    width: 100%;
    text-align: left;
    padding: 0.75rem 1rem;
    background: #ffffff;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    font: inherit;
    font-size: var(--font-size-control);
    color: var(--color-text);
    cursor: pointer;
    box-shadow: 0 3px 3px rgba(56, 65, 74, 0.06);
}
.tenant-list__item button:hover { background: var(--color-table-hover); }

/* Flash messages (django.contrib.messages) */

.messages { margin-bottom: 1.5rem; }
.message {
    padding: 0.6rem 0.9rem;
    border-radius: var(--radius-tight);
    font-size: 0.85rem;
    margin-bottom: 0.5rem;
}
.message--success { background: #eef4ef; color: var(--color-success); border: 1px solid var(--color-success); }
.message--error { background: #fbeeeb; color: var(--color-danger); border: 1px solid var(--color-danger); }

/* Impersonation banner "back" control -- looks like the plain link it
   replaced, but is a real POST (stops the impersonation, not just navigates
   away from it). */
.impersonation-banner__form { display: inline; }
.impersonation-banner__form button {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    text-decoration: underline;
    font-weight: 600;
    cursor: pointer;
    color: inherit;
}

/* Regiecentrum (console app) */

.console-card {
    background: #ffffff;
    border-radius: var(--radius-tight);
    box-shadow: 0 3px 3px rgba(56, 65, 74, 0.1);
    padding: 1.5rem;
    margin-bottom: 1.5rem;
}
/* v1.3 (klanten-afronding), punt 2: DEFAULT is nu de grotere
   titel-content-marge -- de meeste sectietitels in dit project worden
   gevolgd door een formulierveld of gewone tekst (content), niet door een
   echte subtitel. .title--subtitle-below hieronder is de opt-in
   uitzondering voor het enige geval waar dat wél zo is (bv. "Eerste
   contactpersoon" -> "Ontvangt de bestelbevestiging..."). */
.console-card h2,
.console-card h3 { margin: 0 0 var(--spacing-title-content); font-size: var(--font-size-section-title); font-weight: 600; }
.console-card h2.title--subtitle-below,
.console-card h3.title--subtitle-below { margin-bottom: var(--spacing-title-subtitle); }

.console-create-form { display: flex; gap: 0.75rem; align-items: flex-start; }
.console-create-form input {
    flex: 1;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    font: inherit;
    font-size: var(--font-size-control);
    background: var(--color-bg);
}
.console-create-form__submit { width: auto; padding: 0.6rem 1.25rem; }

/* border-collapse: separate (i.p.v. het table-default collapse) is punt
   9's vereiste combinatie met position:sticky op thead th -- collapse+
   sticky is een bevestigde, cross-browser renderbeperking waarbij de
   onderrand van de sticky header inconsistent verdwijnt tijdens scrollen. */
.console-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 0.9rem; }
.console-table th {
    text-align: left;
    font-size: 0.75rem;
    text-transform: uppercase;
    color: var(--color-text-muted);
    font-weight: 600;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--color-card-border);
    /* Onvoorwaardelijk aanwezig -- werkt hier omdat .table-scroll (niet de
       pagina) de scrollende ouder is, zie de toelichting daarbij. Wit ==
       .console-card se eigen achtergrond, dus geen visuele wijziging voor
       de kleine tabellen (klantfiche, regiecentrum) die deze regel ook
       meekrijgen maar nooit lang genoeg zijn om te scrollen. */
    position: sticky;
    top: 0;
    z-index: 5;
    background: #ffffff;
}
.console-table td { padding: 0.65rem 0.75rem; border-bottom: 1px solid var(--color-card-border); }
/* Punt 9 (v1.2): geen rand meer onder de laatste rij -- symmetrisch met de
   al afwezige rand boven de kop (thead th heeft nooit een border-top
   gehad). Randen tussen de rijen zelf en tussen kop/body blijven
   ongewijzigd (die staan nog altijd via border-bottom op th/elke td). */
.console-table tbody tr:last-child td { border-bottom: none; }
/* Vervolgsessie (Categorieën-verfijning) punt 9: generieke lege-staat-rij
   (layout/_empty_table_row.html) -- gecentreerd en gedempt i.p.v. de
   standaard links-uitgelijnde, gewone tekstkleur van een normale rij. */
.console-table__empty-row td { text-align: center; color: var(--color-text-muted); padding: 1.5rem 0.75rem; }
.console-table code { font-size: 0.8rem; color: var(--color-text-muted); }
.console-table__actions { display: flex; gap: 0.75rem; }

.status-badge {
    display: inline-block;
    /* Punt 6 (v1.2), bugfix: font-family: inherit (was font: inherit) --
       de shorthand reset button-chrome hieronder herstelde ook font-size
       naar de ge-erfde waarde (0.9rem van .console-table), wat de
       font-size: 0.75rem hierboven onzichtbaar overschreef. Enkel
       font-family moet van het <button>-reset komen; font-size/weight
       blijven expliciet eigendom van deze klasse. Empirisch bevestigd via
       getComputedStyle (was 14.4px effectief, hoort 12px te zijn). */
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.3;
    padding: 0.2rem 0.55rem;
    border-radius: var(--radius-tight);
    /* Punt 4 (v1.1): reset button-chrome zodat de klikbare
       klantoverzicht-variant (<button>) identiek oogt aan de statische
       <span>-variant elders (klantfiche, contactpersonen/adressen). */
    border: 0;
    font-family: inherit;
    cursor: default;
}
/* Punt 6 (v1.2): fellere, contrastrijkere tinten voor de klant-statusbadge
   -- de vorige, "brave" pastels waren visueel te zwak om een statuswissel
   te benadrukken. .status-badge--danger blijft ongewijzigd (een apart,
   los van dit punt bestaand gebruik in het regiecentrum, console/
   tenant_list.html -- geen deel van deze klantoverzicht-verfijning). */
.status-badge--success { background: #DCFCE7; color: #15803D; }
.status-badge--danger { background: #fbeeeb; color: var(--color-danger); }
/* Module 0 vervolg §3.6 / punt 6 (v1.2): "Inactief" is nu ook qua kleur
   een echte statuswissel (rood), niet langer neutraal grijs -- de
   badge is de enige (de)activate-actie (v1.1 punt 4) en moet dus een
   duidelijk andere kleur tonen dan "Actief", niet enkel een andere tekst. */
.status-badge--neutral { background: #FEE2E2; color: #B91C1C; }
/* v2.2 (klanten-afronding, finale fixes) Deel 1.2: definitieve, eigen
   gescoped tokens (v2.1 hergebruikte hier nog --color-accent(-hover),
   maar dat gaf te weinig onderscheid met de eigenlijke accent-kleur
   elders in de app -- zie --color-badge-zakelijk-bg/--color-badge-
   particulier-bg hierboven). */
.status-badge--type-business { background: var(--color-badge-zakelijk-bg); color: var(--color-badge-zakelijk-text); }
.status-badge--type-individual { background: var(--color-badge-particulier-bg); color: var(--color-badge-particulier-text); }

/* Punt 6 (v1.2): enkel de Status-badge (<button>, de (de)activate-actie)
   krijgt cursor:pointer + hover/focus -- de Type-badge (<span>, puur
   informatief) behoudt de cursor:default van de basisklasse hierboven,
   zodat de twee badge-soorten niet allebei "klikbaar" ogen.
   v1.3 punt 5: border ging van 0 naar 1px solid transparent -- dezelfde
   truc als .field-input .toggle hierboven, nodig zodat de canonieke
   focus-visible-regel (border-color-wissel, zie "Interactiestaten"
   hoger in dit bestand) hier iets zichtbaar heeft om te verkleuren
   zonder het element te doen verspringen. De eerdere, losse
   outline-gebaseerde :focus-visible-regel die hier stond is opgegaan in
   die ene canonieke regel. */
button.status-badge {
    border: 1px solid transparent;
    cursor: pointer;
    transition: filter 0.15s ease;
}
button.status-badge:hover { filter: brightness(0.94); }

.btn-link {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    font-size: 0.85rem;
    color: var(--color-text);
    text-decoration: underline;
    cursor: pointer;
}

/* v1.3 (klanten-afronding), punt 2.5: "gevarenzone"-knop -- rode rand/tekst,
   transparante achtergrond in rust, pas bij hover een subtiele rode vulling
   i.p.v. een volle rode vlak -- zichtbaar genoeg om te vinden, niet zo
   prominent dat hij per ongeluk aangeklikt wordt naast de andere knoppen.
   v2.2 maakte dit tijdelijk overal vol/rood (Deel 3 punt 7); v2.3 punt 7
   draait dat terug voor DEZE knop specifiek -- de pagina-eigen "Klant
   verwijderen"-knop (in de sidebar-kaart, buiten de modal) blijft subtiel/
   outline. Enkel de knop BINNEN de bevestigingsmodal wordt vol/rood, zie
   .btn-danger--solid hieronder -- 2 aparte classes i.p.v. 1 gedeelde, want
   ze horen nu bewust verschillend te ogen op 2 verschillende plekken. */
.btn-danger {
    width: auto;
    padding: 0.6rem 1.25rem;
    background: none;
    border: 1px solid var(--color-danger);
    border-radius: var(--radius-soft);
    color: var(--color-danger);
    font: inherit;
    font-size: var(--font-size-control);
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.15s ease;
}
.btn-danger:hover { background: #fbeeeb; }
/* v2.3 punt 7: enkel de bevestigingsknop BINNEN de "Klant verwijderen"-
   modal (confirm-modal.js zet deze modifier erbij wanneer
   data-confirm-variant="danger") -- volle achtergrond, witte tekst,
   --color-danger-hover bij hover (v2.2's token, blijft bruikbaar). */
.btn-danger--solid {
    background: var(--color-danger);
    border-color: var(--color-danger);
    color: #FFFFFF;
}
.btn-danger--solid:hover { background: var(--color-danger-hover); border-color: var(--color-danger-hover); }

/* v1.4 (klanten-afronding): "Opgelet!"-kaart ("Klant verwijderen" in de
   sidebar op 'Klant bewerken') -- zelfde opzet als .client-tip hierboven
   (eigen achtergrondtint, icoon+titel in 1 gedeelde kleur via
   currentColor op de <h2>), nu de "opgelet" i.p.v. "tip"-variant. */
.client-alert { background: var(--color-alert-bg); }
.client-alert h2 { display: flex; align-items: center; gap: 0.5rem; color: var(--color-alert-icon); }
.client-alert__icon { flex: none; }
/* v2.0 Deel 5 punt 1: de aparte, vetgedrukte "Let op: deze actie kan niet
   ongedaan worden gemaakt!"-regel (.client-alert__warning) is verwijderd
   -- die boodschap staat intussen al in de bevestigingsmodal zelf (zie
   .modal--confirm hieronder), dubbel op de kaart hoefde niet meer. */

/* v1.3 (klanten-afronding), punt 2.4: klikbare status-badge naast de
   klantnaam in de titel van 'Klant bewerken' -- .status-badge/
   button.status-badge hierboven leveren al kleur/cursor/hover/focus,
   enkel wat ademruimte t.o.v. de titel-tekst ervoor is hier nodig. */
.page-header__titlegroup h1 .status-badge {
    margin-left: 0.6rem;
    vertical-align: middle;
}

/* Wide content (tables) inside a console-card must scroll on its own,
   never widen the page itself -- §3.5, responsiviteit is een harde vereiste.

   Punt 9 (v1.1) -- sticky header + onafhankelijk horizontaal scrollbare
   tabel kunnen niet allebei tegen de PAGINA zelf plakken: elke
   overflow-x-waarde buiten 'visible' dwingt de browser om overflow-y ook
   als scrollcontainer te behandelen (empirisch bevestigd, ook met
   overflow-y:clip -- Chrome behandelt clip hier alsnog als hidden), wat
   position:sticky binnen déze wrapper opsluit. Enige robuuste oplossing:
   .table-scroll zelf de scrollende ouder maken (eigen max-height +
   overflow-y:auto) -- de kop plakt dan tegen de bovenkant van de tabel
   zelf, niet tegen de topbar. min-height voorkomt een te kleine box op
   erg lage viewports. */
.table-scroll {
    overflow-x: auto;
    overflow-y: auto;
    /* v2.1 (klanten-afronding, laatste polishing) punt 1: de vaste
       min-height is weg -- een gefilterd resultaat met weinig rijen laat nu
       de kaart meekrimpen i.p.v. lege ruimte te reserveren tot 200px.
       max-height blijft (nodig voor de sticky-header-truc, zie punt 9
       hierboven) zodat een LANGE lijst nog steeds op zijn eigen scrollbar
       plakt i.p.v. de hele pagina te doen scrollen. */
    max-height: calc(100vh - 340px);
}

@media (max-width: 767px) {
    .console-create-form { flex-direction: column; align-items: stretch; }
    .console-create-form__submit { width: 100%; }
}

/* Klantenbeheer (clients app) */

.client-detail__fields {
    display: grid;
    grid-template-columns: 12rem 1fr;
    gap: 0.5rem 1rem;
    margin: 0 0 1rem;
}
.client-detail__fields dt {
    color: var(--color-text-muted);
    font-size: 0.85rem;
    text-align: right;
}
/* min-width:0 laat de 1fr-kolom effectief krimpen -- zonder deze regel kan
   lange, ongebroken dd-inhoud (bv. een volledig adres) de grid, en daarmee
   de pagina, breder maken dan de viewport (horizontale scrollbar, punt 1). */
.client-detail__fields dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }

@media (max-width: 767px) {
    .client-detail__fields { grid-template-columns: 1fr; }
    .client-detail__fields dt { margin-top: 0.5rem; text-align: left; }
}

/* v1.4 (klanten-afronding), §2.1: KPI-rij bovenaan de klantenfiche --
   hergebruikt .console-card voor elke tegel (wit/schaduw/radius/padding
   al gedekt), enkel de grid-wrapper en de label/waarde-typografie zijn
   hier nieuw. */
/* v2.2 Deel 3 punt 1 probeerde dit op te lossen door .kpi-row zelf de
   content/sidebar-grid te maken (met een geneste .kpi-row__cards) -- dat
   loste de breedte op, maar gaf een NIEUW probleem (v2.3 punt 5): de
   KPI-rij stond dan nog altijd VOOR .client-page-layout als eigen,
   volledige grid-rij, waardoor de rechterkolom (Klantinfo, ...) pas
   onder de KPI-rij begon i.p.v. ernaast, met lege ruimte rechtsboven tot
   gevolg. Grondoorzaak-fix v2.3: .kpi-row is nu gewoon terug een simpele
   4-koloms-grid, maar staat niet langer los vóór .client-page-layout --
   client_detail.html plaatst 'm nu als EERSTE kind BINNEN
   .client-page-layout__main (vóór "Facturatie"). Omdat .main zelf al de
   juiste content-kolombreedte krijgt van de omvattende
   .client-page-layout-grid, erft de KPI-rij die breedte gratis mee, en
   begint de sidebar ernaast nu vanzelf op exact dezelfde hoogte (rij 1
   van diezelfde grid) -- geen aparte breedte-mirroring-truc meer nodig. */
.kpi-row {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 1.5rem;
    margin-bottom: 1.5rem;
}
.kpi-row .console-card { margin-bottom: 0; }
/* v2.0 Deel 1.2: kleine, gedempte accent-iconen rechts in elke tegel --
   structuur/inhoud van de 4 kaarten blijft ongewijzigd ("geen slanke
   statregel"), enkel deze rij + het icoon zijn nieuw. Achtergrondtint per
   kaart is een eigen, letterlijke waarde (zelfde precedent als
   .status-badge--type-business/--type-individual hierboven -- decoratieve
   1-op-1-kleurtjes, geen projectbreed hergebruikt token nodig), het
   icoon zelf hergebruikt bestaande kleurtokens waar mogelijk. */
.kpi-card__row { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; }
.kpi-card__icon {
    flex: none;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.kpi-card__icon svg { width: var(--icon-size-inline); height: var(--icon-size-inline); }
.kpi-card--revenue .kpi-card__icon { background: #EAF2EC; color: var(--color-success); }
.kpi-card--orders .kpi-card__icon { background: #EAEDF1; color: var(--color-info); }
.kpi-card--lastorder .kpi-card__icon { background: var(--color-tip-bg); color: var(--color-tip-icon); }
.kpi-card--balance .kpi-card__icon { background: #EFEAF3; color: #7A5FA0; }
/* v2.1 (klanten-afronding, laatste polishing): nieuw 3-regelig format --
   titeltje (hoofdletters, klein, grijs) + icoon op de bovenste regel,
   kerngetal groot in het midden, kort tekstje onderaan (klein, grijs).
   .kpi-card__label (v2.0, boven de waarde gestapeld) is hiermee vervangen
   door .kpi-card__title (naast het icoon) + .kpi-card__caption (onder de
   waarde). */
.kpi-card__title {
    display: block;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}
.kpi-card__value { display: block; font-size: 1.5rem; font-weight: 700; margin: 0.5rem 0 0.15rem; }
.kpi-card__caption { display: block; font-size: 0.75rem; color: var(--color-text-muted); }
/* Voorbereidend op de Bestellingen-/Facturatie-module (§2.1, openstaand
   saldo > betalingstermijn): enkel de stijl-hook staat al klaar, de
   effectieve berekening bestaat nog niet -- clients/services.py::
   get_client_kpis geeft `outstanding_overdue` dus nu altijd False terug. */
.kpi-card__value--warning { color: var(--color-warning); }

@media (max-width: 1023px) {
    .kpi-row { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 767px) {
    .kpi-row { grid-template-columns: 1fr; }
}

/* v2.0 Deel 1.3/1.4: "Klantinfo" verliest zijn v1.4-labels volledig (was:
   .client-info__fields dt/dd, label-boven-waarde) -- nu een kale
   lijnenlijst zonder enkel label, gegroepeerd met een kleine verticale
   spatie tussen de 3 groepen uit de spec (Klantnr./Laatst ingelogd --
   Naam/Adres -- Telefoon/E-mail/BTW/Peppol). De titel draagt voortaan ook
   de klikbare statusbadge, rechts uitgelijnd. */
.client-info h2 { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.client-info__group { display: flex; flex-direction: column; gap: 0.15rem; margin-bottom: 0.75rem; font-size: 0.85rem; }
.client-info__group:last-child { margin-bottom: 0; }
.client-info__group p { margin: 0; overflow-wrap: anywhere; }

/* Contactpersonen/Leveringsadressen: een compacte, label-loze lijst in de
   rechterkolom (v2.0 Deel 1.5/1.6) -- zelfde principe als
   .client-info__group hierboven (geen veld-labels, lege regels gewoon
   weggelaten in de template). 1 gedeeld lijst-patroon voor beide secties.
   v2.1 (klanten-afronding, laatste polishing) overschreef een aantal
   details t.o.v. v2.0: geen avatar-cirkel/locatie-icoon meer, naam
   afgekapt+tooltip i.p.v. altijd-volledig, en een hover-onthuld
   potlood-icoon vóór de statusbadge (opent de bewerk-popup). */
.client-contact-list,
.client-address-list {
    list-style: none;
    margin: 0 0 0.75rem;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}
.client-contact-list__item,
.client-address-list__item { font-size: 0.85rem; }
.client-contact-list__row,
.client-address-list__row { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.15rem; }
.client-contact-list__name,
.client-address-list__label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
    color: var(--color-text);
}
.client-contact-list__detail,
.client-address-list__detail { margin: 0; color: var(--color-text-muted); overflow-wrap: anywhere; }
.client-contact-list__row .status-badge,
.client-address-list__row .status-badge { flex: none; }
/* Hover-potlood (v2.1, herzien in v2.2 Deel 3 punt 3, grondoorzaak-fix in
   v2.3 punt 6): staat als EERSTE kind in de rij (vóór de naam, niet na de
   badge) -- neemt in rust geen ruimte in, zodat de statusbadge altijd op
   exact dezelfde rechter-positie blijft staan, ongeacht hover-state. Bij
   hover op de rij schuift het potlood in vanaf links en verschuift de
   naam-tekst ernaast mee naar rechts.
   v2.2 probeerde dit door width:0/overflow:hidden rechtstreeks op de
   <button>.icon-btn zelf te zetten -- dat werkte niet: Chrome geeft
   knop-elementen (form-control-achtige intrinsieke sizing) een eigen
   minimumbreedte die de normale "automatic minimum size is 0 zodra
   overflow != visible"-regel NEGEERT (empirisch bevestigd: een test-
   <button> met width:0 rendert alsnog op 14px, een identiek test-<div>
   wél op 0px). Fix: de width/margin/opacity-animatie zit nu op een
   omhullende <span class="client-contact-list__edit-slot"> (geen
   form-control, dus geen speciale UA-minimumbreedte) -- de <button>
   erbinnen behoudt gewoon zijn normale, vaste .icon-btn-afmeting
   (2rem x 2rem) en wordt enkel door de slot's overflow:hidden
   zichtbaar/onzichtbaar gemaakt. */
.client-contact-list__edit-slot,
.client-address-list__edit-slot {
    flex: none;
    display: block;
    width: 0;
    margin-right: -0.5rem;
    opacity: 0;
    overflow: hidden;
    transition: width 0.15s ease, margin-right 0.15s ease, opacity 0.15s ease;
}
.client-contact-list__row:hover .client-contact-list__edit-slot,
.client-address-list__row:hover .client-address-list__edit-slot {
    width: 2rem;
    margin-right: 0;
    opacity: 1;
}
/* Standaardadres-indicatie (v2.1): platte, lichtgrijze tekst -- geen
   sterretje/icoon meer. "Standaardadres" is louter informatief (geen link),
   "Instellen als standaard" blijft functioneel (confirm + AJAX,
   static/js/set-default-address.js, ongewijzigd). */
.client-address-list__default,
.client-address-list__set-default {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    font-size: 0.75rem;
    color: var(--color-text-faint);
    text-decoration: none;
}
button.client-address-list__set-default { cursor: pointer; }
.client-address-list__set-default:hover { color: var(--color-text-muted); text-decoration: underline; }

/* "+ Toevoegen" onder Contactpersonen/Leveringsadressen (v2.0 Deel 1.5/1.6,
   kleur/uitlijning herzien in v2.1): subtiele, lichtgrijze tekstlink zonder
   knop-styling, nu rechts uitgelijnd i.p.v. links -- vervangt .btn-link
   (onderstreept, donker) op deze 2 plekken; .btn-link zelf blijft
   ongewijzigd voor zijn andere bestaande gebruik elders. */
.client-section-add {
    display: block;
    margin-top: 0.25rem;
    text-align: right;
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    font-size: 0.8rem;
    color: var(--color-text-faint);
    text-decoration: none;
    cursor: pointer;
}
.client-section-add:hover { color: var(--color-text-muted); text-decoration: underline; }

.client-form .field { margin-bottom: 1.1rem; }
.client-form .field label {
    display: block;
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin-bottom: 0.35rem;
}
.client-form .field input,
.client-form .field select {
    width: 100%;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    font: inherit;
    /* v1.1 punt 8, grondoorzaak: ontbrak hier -- deze velden erfden zo de
       ambient (browserdefault) font-size i.p.v. dezelfde grootte als het
       klantoverzicht-zoekveld (.field-input input), vandaar bv. het
       zichtbaar te grote Maatschappelijke-naam-veld. */
    font-size: var(--font-size-control);
    background: #ffffff;
}

/* v1.3 (klanten-afronding), punt 2.2: Klanttype/BTW-nummer op 'Klant
   bewerken' -- vergrendelde velden tonen als een echt disabled-invoerveld
   (i.p.v. kale platte tekst) zodat de vergrendeling ook visueel duidelijk
   is. --color-bg (de paginabackground) is hier bewust hergebruikt als
   "lichtgrijze achtergrond"-tint i.p.v. een nieuw token toe te voegen --
   het contrasteert al voldoende met het witte veld ernaast. */
.client-form .field input:disabled,
.client-form .field select:disabled {
    background: var(--color-bg);
    color: var(--color-text-muted);
    cursor: not-allowed;
}

/* v1.1 punt 4: overrides op de zelf-gehoste intl-tel-input (vendor-CSS
   laadt vóór base.css, zie layout/base_shell.html se {% block extra_css %}
   -- zie DESIGN-TOKENS.md) zodat het telefoonveld dezelfde rand/radius/
   font-size/hoogte krijgt als elk ander formulierveld. Geen open
   vlaggenkeuze (allowDropdown:false in static/js/phone-input.js), dus geen
   hover/pointer-cursor-stijl op de vlag-container nodig. */
/* v2.2 (klanten-afronding, finale fixes) Deel 2 punt 3: expliciete,
   eigen marge boven op de gewone label-marge (.field label se
   margin-bottom hierboven) -- .phone-input kreeg voorheen geen eigen
   marge en leunde volledig op die ene, gedeelde regel; nu een eigen,
   niet-te-missen `margin-top` zodat dit label-widget-gapje niet langer
   afhangt van of die gedeelde regel hier wel effectief doorwerkt. */
.phone-input { margin-top: 0.2rem; }
.phone-input .iti { display: block; width: 100%; }
.phone-input .iti input.iti__tel-input {
    width: 100%;
    padding: 0.6rem 0.75rem 0.6rem 4.6rem;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    font: inherit;
    font-size: var(--font-size-control);
    background: #ffffff;
    color: var(--color-text);
}
/* v2.2 (klanten-afronding, finale fixes) Deel 2 punt 1, grondoorzaak-fix:
   deze regel zette de padding hier vroeger op 0 -- maar .iti__flag-
   container ligt (top:0;left:0;bottom:0, bibliotheek-default) exact over
   de bovenste/linker/onderste randpixel van het invoerveld heen. De
   bibliotheek se eigen padding:1px was net de bedoelde speling die
   voorkwam dat een achtergrondkleur op :hover (.iti__selected-flag
   hieronder) die randpixels zou overschilderen -- door 'm hier naar 0 te
   zetten verdween de rand zichtbaar bij hover op de vlag/belcode-trigger.
   Terug op 1px (i.p.v. helemaal weg te laten), exact gelijk aan de
   rand-dikte van het veld. */
.phone-input .iti__flag-container { padding: 1px; }
.phone-input .iti__selected-flag { padding: 0 0.4rem 0 0.75rem; background: none; }
.phone-input .iti--separate-dial-code .iti__selected-dial-code {
    color: var(--color-text-muted);
    font-size: var(--font-size-control);
}
/* v2.0 Deel 4: de landkeuze-dropdown (klik op vlag/landcode) toonde nog de
   kale library-default -- zelfde visuele taal als .custom-select__menu
   hierboven (kleiner lettertype, --radius-tight, geen zichtbare scrollbar,
   scroll blijft functioneel). Geldt overal in de app waar dit veld
   voorkomt, niet enkel op de klantenfiche.
   Empirisch bevestigd (getComputedStyle): de eigenlijke scroll-container is
   .iti__dropdown-content (overflow-y:scroll, max-height:200px) -- NIET
   .iti__country-list zelf (dat blijft overflow:visible) -- vandaar dat de
   rand/radius/scrollbar-regels op .iti__dropdown-content staan, en enkel
   het lettertype op de lijst/opties zelf. */
.phone-input .iti__dropdown-content {
    border-radius: var(--radius-tight);
    border-color: var(--color-card-border);
    box-shadow: 0 5px 10px rgba(30, 32, 37, 0.12);
    scrollbar-width: none;
    /* v2.3 (klanten-afronding, allerlaatste correcties) punt 2: dezelfde
       marge als .custom-select__menu se top: calc(100% + 0.35rem)
       hierboven -- de bibliotheek positioneert deze lijst zelf via JS
       (geen CSS top/margin-top van zichzelf), dus zonder deze regel plakt
       de lijst rechtstreeks tegen de trigger aan. */
    margin-top: 0.35rem;
}
.phone-input .iti__dropdown-content::-webkit-scrollbar { display: none; }
.phone-input .iti__country-list,
.phone-input .iti__country { font-size: var(--font-size-control); }

/* v2.1 (klanten-afronding, laatste polishing), telefoon-landdropdown --
   verdere afstemming op de .custom-select-referentiestijl.
   1. De trigger (vlag+belcode) had een permanente lichtgrijze achtergrond
      (bibliotheek-default, .iti--separate-dial-code .iti__selected-flag) --
      nu transparant in rust, en enkel bij hover dezelfde lichte tint als de
      optie-hover eronder (i.p.v. de eigen, iets donkerdere
      rgba(0,0,0,.05) van de bibliotheek). */
.phone-input.iti--separate-dial-code .iti__selected-flag,
.phone-input .iti--separate-dial-code .iti__selected-flag { background-color: transparent; }
.phone-input .iti--allow-dropdown .iti__flag-container:hover .iti__selected-flag { background-color: var(--color-table-hover); }
/* 2. Kleine extra ademruimte tussen de belcode en het getypte nummer. */
.phone-input .iti input.iti__tel-input { padding-left: 4.85rem; }
/* 3. Hover op de landen in de lijst zelf: veel lichter dan de
      bibliotheek-default (die duidelijk donkerder grijs is). */
.phone-input .iti__country:hover,
.phone-input .iti__country.iti__highlight { background-color: var(--color-table-hover); }
/* 4. Het huidig geselecteerde land krijgt een vinkje, zoals een
      .custom-select__option -- de bibliotheek zet zelf al .iti__active op
      de <li> van het geselecteerde land (geen eigen JS nodig om dat bij te
      houden), enkel de checkmark zelf is hier nieuw.
      Empirisch bevestigd (outerHTML): .iti__country/.iti__active/
      .iti__highlight staan alle 3 op DEZELFDE <li> (geen los kind-element)
      -- vandaar 1 samengesteld selector i.p.v. een afstammeling-selector. */
/* v2.2 (klanten-afronding, finale fixes) Deel 2 punt 2, grondoorzaak-fix:
   de checkmark-kolom-ruimte (padding-right) stond hier voorheen enkel op
   li.iti__active -- .iti__country-list heeft geen vaste breedte
   (white-space:nowrap, shrink-to-fit op de langste rij), dus de
   dropdown se breedte hing af van WELK land net actief was (het
   "België"-actieve-rij-scenario reserveerde zelf al ruimte, maar zodra
   "Nederland" (langere tekst) actief werd, verschoof de checkmark-positie
   mee met de dan-actuele breedte). Alle rijen reserveren deze ruimte nu
   gelijk, dus de lijst krijgt 1 vaste breedte (incl. checkmark-kolom) en
   het vinkje staat altijd op exact dezelfde rechter-positie, ongeacht
   welk land actief is. */
.phone-input .iti__country-list li.iti__country {
    position: relative;
    padding-right: 2.25rem;
}
.phone-input .iti__country-list li.iti__active {
    background-color: var(--color-table-hover);
}
.phone-input .iti__country-list li.iti__active::after {
    content: '';
    position: absolute;
    right: 0.75rem;
    top: 50%;
    width: 16px;
    height: 16px;
    transform: translateY(-50%);
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231A1A1A' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") no-repeat center / contain;
}
.client-form__grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0 1.5rem;
}
/* 3de vervolgsessie, grondoorzaak van "Verwijderen" die niet uitlijnde met
   de textarea erboven: grid-items krijgen van CSS zelf een impliciete
   `min-width: auto` -- hun minimale breedte is de MIN-CONTENT-breedte van
   hun inhoud, niet 0. Een lange, niet-afbrekende bestandsnaam
   (white-space:nowrap, nodig voor de ellipsis-afkapping) heeft een grote
   intrinsieke minimumbreedte, dus zonder deze regel weigerde de
   Afbeelding-kolom te krimpen tot haar eerlijke 1fr-aandeel en werd de
   hele kolom (en dus "Verwijderen") breder dan haar buurkolommen --
   dezelfde categorie bug als flex-items se automatische minimumgrootte
   (zie .file-input__filename se `min-width:0` hierboven), maar dan op
   grid-niveau. Geldt generiek voor elk huidig/toekomstig veld in dit
   grid, niet enkel het bestandsveld. */
.client-form__grid > * { min-width: 0; }
.client-form__deactivate { margin-top: -0.5rem; }

/* v1.1 punt 9, grondoorzaak-fix: de Adres-kaart had voorheen 4 LOSSE
   .client-form__grid-containers (1 per rij) -- 2 daarvan met een andere
   kolomverdeling (2fr/1fr/1fr vs. 1fr/1fr). Elke losse grid herberekent
   zijn kolombreedtes op basis van zijn EIGEN gap-aftrek, dus "Nr." (rij 1)
   en "Gemeente" (rij 2) kwamen niet exact op dezelfde x-positie uit, ook al
   oogde 2fr/1fr/1fr en 1fr/1fr in de broncode gelijkaardig. Fix: 1 gedeelde
   grid (4 gelijke kolommen) voor de HELE kaart -- elk veld krijgt via
   .client-form__field--half/--quarter een grid-column: span, dus elke rij
   deelt exact dezelfde 4 kolomtracks en -gaps, verticale lijnen kloppen
   overal. */
.client-form__address-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0 1.5rem;
}
.client-form__field--half { grid-column: span 2; }
.client-form__field--quarter { grid-column: span 1; }

/* v1.1 punt 2: subtiele waarschuwing onder de Klanttype-dropdown --
   hergebruikt --color-warning (dit IS functioneel een waarschuwing, geen
   tip, vandaar niet de latere --color-tip-*-tokens). */
.client-form__warning {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    /* v2.0 Deel 6: exacte, opgegeven waarde -- v1.3/v1.4 gebruikten hier
       respectievelijk --spacing-hint (0.5rem) en een letterlijke 0.25rem
       voor margin-TOP (afstand t.o.v. de dropdown erboven); deze ronde
       specificeert de volledige margin-shorthand (incl. margin-bottom)
       expliciet, dus letterlijk overgenomen i.p.v. opnieuw zelf te meten. */
    margin: 0 0 1.1rem 0;
    font-size: 0.85rem;
    color: var(--color-warning);
}
.client-form__warning svg { flex: none; margin-top: 0.1rem; }

/* v1.1 punt 7, grondoorzaak-fix: deze rij omvat nu enkel het invoerveld +
   de zoekknop (niet meer ook het <label> erboven) -- align-items:center
   lijnt de knop dus exact uit met de INPUT, zonder een op het label-hoogte
   gebaseerd magic-number. .client-form__vat-lookup-btn hergebruikt de
   gedeelde .icon-btn-stijl (grijs, donkerder grijs on hover) i.p.v. een
   eigen, zwarte .btn-primary-knop. */
.client-form__vat-lookup { display: flex; gap: 0.5rem; align-items: center; }
.client-form__vat-lookup input { flex: 1; }
.client-form__vat-lookup-btn { flex: none; }

/* Deel B: elke dropdown in dit formulier is voortaan een .custom-select
   (DESIGN-TOKENS.md "Component-patronen" -- nooit een 2de, native-<select>
   dropdown-implementatie ernaast bouwen). De filterdropdowns op het
   klantoverzicht hebben een vaste, smalle breedte; hier moet de dropdown
   net als elk ander formulierveld de volledige grid-cel opvullen. */
/* Hogere specificiteit dan de kale .custom-select-regel (elders in dit
   bestand, breedte 180px voor de klantoverzicht-filterdropdowns) nodig om
   daar betrouwbaar boven te winnen, ongeacht bronvolgorde. */
.client-form .client-form__select { width: 100%; flex: 1 1 auto; }

/* Deel C: hoofdformulier + smalle, meescrollende zijkolom (~25-30% van de
   contentbreedte, cf. de bijgevoegde CRM-referentie) -- align-items:start
   zodat een kortere zijkolom niet uitrekt tot de hoogte van het formulier.
   v1.4 (klanten-afronding): hernoemd van .client-form-layout naar
   .client-page-layout -- de klantenfiche (geen formulier) hergebruikt nu
   dit exacte 2-koloms-plus-sticky-sidebar-patroon (§2.1) net als create/
   edit, dus de oude "form"-specifieke naam klopte niet meer. Zelfde
   klasse, geen 2de parallelle implementatie. */
.client-page-layout {
    display: grid;
    /* v2.0 Deel 1.1: gedeeld breedte-tokenpaar (zie :root) i.p.v. een
       letterlijke "1fr 22rem" hier -- garandeert dat de KPI-rij (klantenfiche)
       en de rechterkolom op elke pagina die deze layout hergebruikt exact
       dezelfde kolombreedtes/uitlijning krijgen. */
    grid-template-columns: var(--layout-content-width) var(--layout-sidebar-width);
    gap: 1.5rem;
    align-items: start;
}
/* v1.1 §5.1: "Voortgang" + "Tip" blijven in beeld tijdens het scrollen --
   1 sticky element voor de hele kolom (i.p.v. elke kaart afzonderlijk
   sticky te maken, wat een eigen top-offset per kaart zou vergen om
   overlap te vermijden). align-items:start op .client-page-layout laat de
   GRID AREA van deze kolom wel de volle rijhoogte behouden (gelijk aan het
   hoofdformulier), dus deze sticky-box heeft binnen die area ruimte om te
   "reizen" en stopt vanzelf zodra het einde van de kolom bereikt is --
   standaard sticky-gedrag, geen JS nodig. */
.client-page-layout__sidebar {
    position: sticky;
    top: calc(var(--layout-topbar-height) + 1rem);
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}
.client-page-layout__sidebar .console-card { margin-bottom: 0; }
/* v2.0 Deel 1.3: op de klantenfiche kan de rechterkolom (Klantinfo +
   Contactpersonen + Leveringsadressen samen) LANGER zijn dan de
   linkerkolom (KPI-rij + Facturatie) -- het omgekeerde van create/edit,
   waar de sidebar juist korter is dan de content. position:sticky op een
   kolom die langer is dan zijn "reisruimte" geeft geen zinvol
   meescroll-effect, enkel verwarrend vastklik-gedrag. Deze modifier (naast
   de basisklasse, niet in de plaats ervan -- zelfde specificiteit, wint
   dankzij bronvolgorde) zet de sidebar terug in de normale documentflow.
   Create/edit blijven ongewijzigd sticky. */
.client-page-layout__sidebar--flow { position: static; top: auto; }

/* v1.1 §5.3: eigen, warme achtergrond (--color-tip-bg) i.p.v. de
   standaard witte kaartachtergrond -- titel + icoon in --color-tip-icon. */
.client-tip { background: var(--color-tip-bg); }
.client-tip h2 { display: flex; align-items: center; gap: 0.5rem; color: var(--color-tip-icon); }
.client-tip__icon { flex: none; }
.client-progress__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.65rem; }
.client-progress__item { display: flex; align-items: center; gap: 0.6rem; font-size: 0.9rem; color: var(--color-text-muted); }
.client-progress__icon { position: relative; width: 16px; height: 16px; flex: none; display: flex; }
.client-progress__circle { color: var(--color-card-border); }
/* v1.1 §5.2: gevulde cirkel (currentColor, --color-progress-complete) MET
   een wit vinkje erin -- geen outline-checkmark meer, en bewust een eigen,
   frissere groentint dan het gedempte --color-success (dat blijft gelden
   voor badges/meldingen elders, ongewijzigd). */
.client-progress__check { position: absolute; inset: 0; color: var(--color-progress-complete); visibility: hidden; }
.client-progress__item.is-done { color: var(--color-text); }
.client-progress__item.is-done .client-progress__circle { visibility: hidden; }
.client-progress__item.is-done .client-progress__check { visibility: visible; }

/* v1.2 punt 3, grondoorzaak-fix: `position: fixed` bleek altijd t.o.v. de
   viewport blijven kleven, ook voorbij het einde van de pagina (over de
   echte footer heen) -- vergde een kunstmatige extra bottom-padding op
   .client-form om ruimte te reserveren. `position: sticky` binnen zijn
   eigen ouder (<form class="client-form">, die stopt vóór
   .app-shell__footer) stopt vanzelf met kleven zodra .client-form se
   eigen einde bereikt is: de knoppen schuiven dan gewoon in hun normale
   flow-positie, geen JS/IntersectionObserver nodig. Enkel een gewone,
   kleine margin-top volstaat dus i.p.v. padding-bottom op de hele kolom. */
.client-form__footer {
    position: sticky;
    /* v1.3 (klanten-afronding), punt 3: was 0 -- plakte zo tegen de
       onderrand van de viewport zonder enige ademruimte. */
    bottom: 1rem;
    margin-top: 1.5rem;
    display: flex;
    justify-content: flex-end;
    gap: 0.75rem;
}
.client-form__footer .btn-primary,
.client-form__footer .btn-secondary {
    width: auto;
    padding: 0.6rem 1.25rem;
}
/* v1.2 punt 2: "Annuleren" volgt de huisstijl-accentkleur i.p.v. de
   generieke outline-stijl van .btn-secondary elders (bv. "Bewerken" op de
   klantfiche) -- expliciet gescoped tot deze footer, geen wijziging van
   de gedeelde .btn-secondary-basisstijl zelf. Geen box-shadow (meer). */
.client-form__footer .btn-secondary {
    background: var(--color-accent);
    border-color: transparent;
    color: var(--color-text);
}
.client-form__footer .btn-secondary:hover { background: var(--color-accent-hover); }

@media (max-width: 767px) {
    /* De onderbalk (.bottom-nav, 4.5rem hoog) staat fixed onderaan --
       zonder deze correctie zou de sticky footer daar middenin/overheen
       kleven zodra ze de viewportbodem raakt. */
    .client-form__footer { bottom: calc(4.5rem + 1rem); }
}

@media (max-width: 1023px) {
    .client-page-layout { grid-template-columns: 1fr; }
    /* Kolom stapelt hier tot 2 losse rijen (main/sidebar) -- de sidebar-rij
       is dan zelf maar zo hoog als zijn eigen inhoud, dus sticky heeft
       geen zinvolle "reisruimte" meer en kan enkel voor een raar
       kortstondig vastplakken zorgen. */
    .client-page-layout__sidebar { position: static; }
}

@media (max-width: 767px) {
    .client-form__grid,
    .client-form__address-grid { grid-template-columns: 1fr; }
    .client-form__field--half,
    .client-form__field--quarter { grid-column: span 1; }
    .client-form__vat-lookup { flex-direction: column; align-items: center; }
    .client-form__vat-lookup input { flex: none; width: 100%; }
}

/* Pop-up modals (native <dialog>, spec §5.3) -- contactpersonen/
   leveringsadressen. No modal component existed anywhere in the project
   before this; built from scratch on the native element rather than a
   hand-rolled div overlay, so focus-trapping/Esc-to-close/::backdrop come
   from the browser for free, in keeping with "vanilla JS only, no
   framework" (CLAUDE.md). */
dialog.modal {
    border: none;
    border-radius: var(--radius-tight);
    padding: 1.5rem;
    max-width: 480px;
    width: calc(100% - 2rem);
    box-shadow: 0 5px 10px rgba(30, 32, 37, 0.12);
    /* v2.2 (klanten-afronding, finale fixes) Deel 3 punt 4: veiligheidsnet
       tegen een horizontale scrollbar in eender welke modal -- geen enkel
       kind-element (nu of in de toekomst) kan de modal breder maken dan
       zijn eigen, vaste breedte hierboven. */
    overflow-x: hidden;
}
dialog.modal::backdrop {
    background: rgba(0, 0, 0, 0.4);
}
/* v2.0 Deel 3 punt 3, grondoorzaak-fix: h2/p binnen een modal hadden geen
   eigen font-size -- erfden zo de browserdefault h2-grootte (~1.5em) resp.
   de ambient paragraph-grootte, merkbaar groter dan overal elders in de
   app. Nu op dezelfde, bestaande tokens als .console-card h2
   (--font-size-section-title) en de gewone formulier-/knoptekstgrootte
   (--font-size-control) -- geen nieuwe tokens, hergebruik van wat er al is. */
dialog.modal h2 { margin: 0 0 var(--spacing-title-content); font-size: var(--font-size-section-title); font-weight: 600; }
dialog.modal p { margin: 0 0 1rem; font-size: var(--font-size-control); color: var(--color-text); }
.modal__actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.75rem;
    margin-top: 1.5rem;
}
.modal__actions .btn-primary { width: auto; padding: 0.6rem 1.25rem; }
/* v2.0 Deel 3 punt 1: "Annuleren" volgt voortaan overal de huisstijl-
   accentkleur -- zelfde stijl als .client-form__footer .btn-secondary
   (create/edit se eigen "Annuleren"), nu projectbreed op deze ene gedeelde
   modal-knop-class i.p.v. een generieke grijze outline. Geldt daarmee
   automatisch voor elke modal die 'm gebruikt: de bevestigingsmodal, en de
   contactpersoon-/leveringsadres-toevoeg-popups. */
.modal__cancel {
    background: var(--color-accent);
    border: 1px solid transparent;
    border-radius: var(--radius-tight);
    padding: 0.6rem 1.25rem;
    font: inherit;
    font-size: var(--font-size-control);
    color: var(--color-text);
    cursor: pointer;
}
.modal__cancel:hover { background: var(--color-accent-hover); }

/* v1.4 (klanten-afronding), punt 1.4: gedeelde, gestileerde
   bevestigingsmodal -- vervangt de kale window.confirm() bij "Klant
   verwijderen" en de status-badge (activeren/deactiveren). 1 gedeelde
   <dialog id="confirm-modal"> in layout/base_shell.html (zie
   layout/templates/layout/_confirm_modal.html), niet een
   pagina-specifieke kopie -- static/js/confirm-modal.js vult titel/
   boodschap/knoptekst/variant in op basis van de data-confirm-*-attributen
   van het element waarop geklikt werd. Hergebruikt dialog.modal/
   .modal__actions/.modal__cancel hierboven -- enkel het icoon en de
   danger-variant zijn hier nieuw. */
/* v2.0 Deel 3 punt 2: icoon + titel op 1 gedeelde regel (was: icoon los
   boven de titel) -- .modal--confirm__header is de nieuwe flex-wrapper om
   beide, dialog.modal h2 hierboven levert de titel-typografie al. */
.modal--confirm__header { display: flex; align-items: center; gap: 0.5rem; margin-bottom: var(--spacing-title-content); }
/* v2.2 (klanten-afronding, finale fixes) Deel 3 punt 6, grondoorzaak-fix:
   dialog.modal h2 erfde tot nu toe de ambient line-height (1.5, zie body
   hierboven) -- op een 1-regelige titel naast een vast-hoge svg (20px)
   geeft dat een lege leegte boven/onder de tekst-glyphs binnen de
   line-box, waardoor align-items:center de twee DOZEN wel centreert maar
   de zichtbare tekst-baseline er toch net naast lijkt te vallen.
   line-height:1 maakt de line-box van de titel exact even hoog als de
   tekst zelf, zodat het verticale midden van de doos ook het verticale
   midden van de zichtbare tekst is. */
/* Vervolgsessie (Categorieën-verfijning) punt 2: line-height:1 hierboven
   centreert de tekst-BASELINE-box correct, maar het driehoekige
   waarschuwings-icoon ernaast heeft door zijn vorm (brede basis onderaan)
   meer optisch gewicht onderaan, waardoor de titel er ondanks
   align-items:center toch iets te hoog naast oogt. Empirisch gemeten
   (getBoundingClientRect op icoon vs. titel): 8px verschil tussen beide
   middens -- vandaar deze expliciete verschuiving i.p.v. een 2de,
   concurrerende align-items-aanpassing.
   Vervolgsessie #2, grondoorzaak van "fix lijkt niet te werken": deze regel
   verloor stil van `dialog.modal h2` hierboven (2 elementen + 1 class =
   hogere specificiteit dan deze regel se 1 class + 1 element), ongeacht
   bronvolgorde -- `margin-top` hier werd dus altijd weer teruggezet naar
   0. Er is precies 1 confirm-modal in de hele app (`#confirm-modal`); een
   ID-selector hier is de robuuste, toekomstvaste fix (wint altijd, ook
   van elke toekomstige generieke `dialog.modal h2`-achtige regel). */
#confirm-modal .modal--confirm__header h2 { margin: 0; margin-top: 0.125rem; line-height: 1; }
.modal--confirm__icon { flex: none; display: flex; color: var(--color-text-muted); }
/* Vervolgsessie (Categorieën-verfijning) punt 2: v2.3 punt 8 had dit
   teruggedraaid naar een zwarte titel (enkel het icoon bleef rood) --
   op expliciet verzoek nu weer teruggedraaid naar v2.2's gedrag: de
   titel zelf kleurt ook rood mee in de danger-variant. */
.modal--confirm.modal--confirm--danger .modal--confirm__header h2,
.modal--confirm.modal--confirm--danger .modal--confirm__icon { color: var(--color-danger); }
/* v2.3 punt 8: `data-confirm-message` is de gewone, niet-opvallende
   vraagzin (ongewijzigde stijl, ook in de danger-variant) -- enkel het
   NIEUWE, aparte `data-confirm-warning`-element krijgt de rode/vetgedrukte
   "Let op!"-opmaak, en enkel in de danger-variant (vandaag de enige
   toepassing: "Klant verwijderen"). */
.modal--confirm.modal--confirm--danger [data-confirm-warning] {
    color: var(--color-danger);
    font-weight: 600;
}
/* Vervolgsessie #2: het gele "Tip!"-blok (data-confirm-tip) hierboven is
   weer verwijderd -- op expliciet verzoek vervangen door de bestaande
   data-confirm-warning-stijl (zelfde rode/vetgedrukte "Let op!"-regel als
   "Klant verwijderen"), niet een 2de, aparte visuele taal voor eenzelfde
   soort boodschap. Was bovendien zelf stuk: `display: flex` op deze class
   versloeg het `hidden`-attribuut (gelijke specificiteit, author-CSS wint
   altijd van de user-agent-stylesheet se `[hidden]{display:none}`), dus
   de box bleef altijd zichtbaar (leeg, wanneer de actieve trigger geen
   tip had) -- zie CLAUDE.md voor de volledige toelichting. Geen vervanging
   nodig: `data-confirm-tip` zelf is ook uit `_confirm_modal.html`/
   `confirm-modal.js` verwijderd, dit was de enige toepassing. */

/* Reusable pagination (Module 0 vervolg §A.5) -- layout/templates/layout/pagination.html */
.pagination {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 1.25rem;
    flex-wrap: wrap;
}
.pagination__link,
.pagination__page {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.25rem;
    height: 2.25rem;
    padding: 0 0.6rem;
    border-radius: var(--radius-tight);
    border: 1px solid var(--color-card-border);
    color: var(--color-text);
    text-decoration: none;
    font-size: 0.85rem;
}
.pagination__link:hover,
.pagination__page:hover { background: var(--color-table-hover); }
.pagination__link.is-disabled { color: var(--color-text-muted); cursor: default; }
.pagination__page.is-active { background: var(--color-black); color: #ffffff; border-color: var(--color-black); }
.pagination__pages { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.pagination__status { color: var(--color-text-muted); font-size: 0.85rem; }

/* Klantoverzicht tabel-rebuild (Module 0 vervolg §B.2/§B.3) */

/* Punt 7 (v1.1): zoekveld + type/status-dropdowns + "+ Nieuwe klant" delen
   nu 1 rij in de titelbalk (page_actions) -- vult meteen de lege ruimte
   die daar voorheen onder "Klanten" stond. */
.client-list__toolbar {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}
/* v2.1 (klanten-afronding, laatste polishing) punt 5: projectbreed
   herbruikbaar patroon, zie layout/_reset_filters_link.html -- vandaar een
   generieke naam, niet client-list-specifiek, ook al is dit vandaag de
   enige toepassing. */
.reset-filters-link {
    color: var(--color-text-muted);
    font-size: 0.85rem;
    text-decoration: none;
    white-space: nowrap;
    /* v2.3 (klanten-afronding, allerlaatste correcties) punt 1: iets meer
       ademruimte t.o.v. het zoekveld ernaast. */
    margin-right: 10px;
}
.reset-filters-link:hover { text-decoration: underline; }
/* Punt 3 (v1.2) / v1.3 punt 4: vaste, gelijke breedte voor zoekveld en
   beide dropdowns (160px -> 180px, nog steeds gelijk aan elkaar). */
.client-list__search { flex: none; width: 180px; margin-bottom: 0; }
/* Vervolgsessie #3: was een vaste `height: 24px` -- korter dan de knoppen/
   dropdowns ernaast (die via padding+font-size hoger uitkomen), dus de
   lijn "zweefde" te kort in het midden i.p.v. even hoog te zijn. De
   ouder (`.page-header__actions`, `display:flex; align-items:flex-start`)
   geeft ieder kind zijn EIGEN hoogte; `align-self: stretch` op de lijn
   zelf laat ze i.p.v. een geraden pixelwaarde altijd exact de hoogte van
   de rij aannemen, ongeacht welke knop/dropdown ernaast staat -- geldt zo
   automatisch op elke pagina die deze class gebruikt (Klanten,
   Categorieën, straks andere lijsten). */
.client-list__toolbar-divider {
    width: 1px;
    align-self: stretch;
    background: var(--color-card-border);
}
/* Punt 3 (v1.2), bugfix: het zoekveld deelde voorheen ook de klasse
   .tenant-search (hergebruikt van het tenant-keuzescherm, §3.3) --
   .tenant-search input (regel ~959) staat verder in dit bestand dan
   .field-input input hierboven en wint dus bij gelijke specificiteit,
   met als gevolg dat het zoekveld stilzwijgend de grotere auth-achtige
   padding/font-size van dat scherm overnam (16px i.p.v. de bedoelde
   14.4px/0.9rem -- empirisch bevestigd via getComputedStyle, exact de
   font-size-mismatch die dit punt meldde). Klasse .tenant-search is
   daarom uit client_list.html verwijderd; dit zoekveld heeft nooit een
   eigen input-styling nodig gehad, .field-input input volstond al. */

/* v1.3 punt 6: custom listbox i.p.v. native <select> -- de open
   optie-lijst van een <select> is browser-chrome en kan niet
   cross-browser gestyled worden. Open/dicht via het al bestaande
   dropdown.js ([data-dropdown]/[data-dropdown-toggle]/.is-open, zelfde
   mechanisme als de taal-/avatar-dropdowns in de topbar) -- enkel de
   select-specifieke laag (optie kiezen, label/hidden-input bijwerken,
   auto-submit) komt uit het nieuwe static/js/custom-select.js.
   .custom-select__toggle hergebruikt bewust dezelfde look als het
   vorige .filter-select (rand/radius/font-size/achtergrond) -- geen
   nieuwe, losse literal-waarden, cf. de CSS-conventie in CLAUDE.md. */
.custom-select { position: relative; flex: none; width: 180px; }
.custom-select__toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    width: 100%;
    padding: 0.6rem 0.85rem;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    font: inherit;
    font-size: var(--font-size-control);
    background-color: #ffffff;
    color: var(--color-text);
    cursor: pointer;
}
/* v2.1 (klanten-afronding, laatste polishing), telefoon-landdropdown-
   afstemming punt 1: de trigger-knop had nog geen hover-staat -- nu
   dezelfde, lichte tint als de optie-hover eronder (--color-table-hover),
   projectbreed op ELKE .custom-select (niet enkel de klantfiltering). */
.custom-select__toggle:hover { background-color: var(--color-table-hover); }
.custom-select__toggle span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.custom-select__chevron { flex: none; color: var(--color-text-muted); transition: transform 0.15s ease; }
.custom-select.is-open .custom-select__chevron { transform: rotate(180deg); }
.custom-select__menu {
    display: none;
    position: absolute;
    top: calc(100% + 0.35rem);
    left: 0;
    min-width: 100%;
    width: max-content;
    max-width: 240px;
    max-height: 260px;
    overflow-y: auto;
    margin: 0;
    padding: 0.3rem;
    list-style: none;
    background: #ffffff;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    box-shadow: 0 5px 10px rgba(30, 32, 37, 0.12);
    z-index: 25;
}
.custom-select.is-open .custom-select__menu { display: block; }
.custom-select__option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    width: 100%;
    background: none;
    border: 0;
    border-radius: var(--radius-tight);
    padding: 0.45rem 0.6rem;
    font: inherit;
    font-size: var(--font-size-control);
    color: var(--color-text);
    text-align: left;
    cursor: pointer;
}
.custom-select__option:hover,
.custom-select__option:focus-visible { background: var(--color-table-hover); outline: none; }
.custom-select__check { visibility: hidden; flex: none; color: var(--color-text); }
[aria-selected="true"] > .custom-select__option .custom-select__check { visibility: visible; }

@media (max-width: 767px) {
    .client-list__toolbar { flex-wrap: wrap; }
    .client-list__search { width: auto; flex: 1 1 100%; }
    .custom-select { width: auto; flex: 1 1 auto; }
}

.sort-header {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: inherit;
    text-decoration: none;
}
.sort-header:hover { color: var(--color-text); }
/* Punt 10 (v1.1) / punt 5 (v1.2) / v1.3 punt 1 (grondoorzaak-herziening):
   eerdere versies wisselden de ICOONVORM zelf tussen "neutraal" (dubbele
   chevron) en "actief" (1 chevron) -- Velzon Grid.js gebruikt in plaats
   daarvan altijd exact hetzelfde icoon (2 gestapelde driehoekjes) en
   wisselt enkel de KLEUR van de actieve richting. Vorm/positie wisselen
   dus nooit meer, wat het vaste-breedte-slot van punt 5 (v1.2) hier
   feitelijk overbodig maakt voor het "niet verspringen"-doel -- de slot
   blijft toch bestaan, nu gewoon exact zo breed als het icoon zelf.
   v1.3-vervolg (bevestigde waarden, verwerkt op deze ene canonieke plek
   -- nooit als losse override elsewhere toevoegen, zie het CSS-conventie-
   patroon in CLAUDE.md): 9x9px, **bewust geen --icon-size-inline (16px)**
   -- dat token is voor de 16px-iconenfamilie (rij-acties, dropdown-
   pijltjes); dit zijn de 2 kleine, gestapelde driehoekjes BINNEN dit ene
   icoon, geen zelfstandig inline-icoon. Niet "corrigeren" naar 16px. */
.sort-header__arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 9px;
}
.sort-header__arrow svg { width: 9px; height: 9px; flex: none; }
/* Elke driehoek heeft zijn EIGEN opacity/fill -- niet de hele span zoals
   voorheen -- zodat enkel de actieve richting donker wordt, de andere
   driehoek blijft altijd subtiel, ongeacht welke richting actief is.
   .sort-arrow.is-active: #999999 is een bevestigde, letterlijke waarde
   (geen bestaand token past -- lichter dan --color-text, donkerder dan
   --color-text-muted), bewust niet vervangen door var(--color-text). */
.sort-arrow { fill: var(--color-text-muted); opacity: 0.35; }
.sort-arrow.is-active { fill: #999999; opacity: 1; }

/* Punt 8 (v1.2): was 5rem -- een leftover uit vóór v1.1 punt 5 (toen hier
   nog het potlood ÉN een "···"-menu naast elkaar stonden). Met nu nog
   maar 1 icoon liet die te brede kolom rechts van het potlood een veel
   grotere ruimte over dan de padding links van het selectievakje in de
   eerste kolom (36px vs. 16px, gemeten) -- 3.5rem is exact icoon (2rem) +
   de gewone td-padding aan beide kanten (2x0.75rem), dezelfde vaste
   padding als elke andere kolom. */
.client-table__actions-col { width: 3.5rem; }
/* Module 2 (Categorieën): gegeneraliseerd van `.client-table td.truncate`
   naar `.console-table td.truncate` -- de afkap-/ellipsis-regel zelf is
   generiek voor elke tabel die op .console-table bouwt (nu ook
   .category-table), niet iets client-tabel-specifieks. Zelfde class,
   1 gedeelde regel i.p.v. een 2de, letterlijke kopie. */
.console-table td.truncate {
    max-width: 180px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.client-table__actions { display: flex; gap: 0.35rem; align-items: center; }

.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: var(--radius-tight);
    border: 1px solid transparent;
    background: none;
    color: var(--color-text-muted);
    cursor: pointer;
    text-decoration: none;
}
.icon-btn:hover { background: var(--color-table-hover); color: var(--color-text); }

/* Vervolgsessie (Categorieën-verfijning) punt 10: Instellingen -- minimale,
   generieke lijst van instellingen-subpagina's (platform_settings app).
   Simpele linklijst, geen kaartgrid nodig voor 1 (en straks een handvol)
   items. */
.settings-list { list-style: none; margin: 0; padding: 0; }
.settings-list__item { border-bottom: 1px solid var(--color-card-border); }
.settings-list__item:last-child { border-bottom: none; }
.settings-list__item a {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    padding: 0.9rem 0.25rem;
    color: inherit;
    text-decoration: none;
}
.settings-list__item a:hover .settings-list__label { text-decoration: underline; }
.settings-list__label { font-weight: 600; }
.settings-list__description { font-size: 0.85rem; color: var(--color-text-muted); }
.settings-list__item--empty { padding: 0.9rem 0.25rem; color: var(--color-text-muted); }

/* Vervolgsessie (Categorieën-verfijning) punt 11: lichtgewicht rich
   text-editor (layout/_richtext_field.html) -- zelfde rand/radius/
   font-size als .client-form .field input hierboven, en een vaste hoogte
   van 7 regels (niet resizeable -- de echte <textarea> blijft hidden,
   enkel de contenteditable-laag is zichtbaar).
   3de vervolgsessie: de losse `max-width: 50%` hier is weer verwijderd --
   Naam/Omschrijving staan nu (net als elk ander veld in dit formulier) in
   een echte `.client-form__grid`-kolom, die zelf al de juiste ~helft-
   breedte geeft. Een EXTRA 50%-beperking hierboven zou dat dubbel
   halveren (~25% i.p.v. dezelfde breedte als de andere velden) -- geen
   aparte override meer nodig, dezelfde gedeelde kolombreedte volstaat. */
.richtext__toolbar { display: flex; align-items: center; gap: 0.25rem; margin-bottom: 0.35rem; }
/* Vervolgsessie #3 punt 3: was `background: none` (enkel op :hover
   zichtbaar) -- op verzoek nu een blijvende, lichtgrijze achtergrond zodat
   deze rij meteen als toolbar leesbaar is, i.p.v. pas bij hover. */
.richtext__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border: 1px solid transparent;
    border-radius: var(--radius-tight);
    background: var(--color-bg);
    color: var(--color-text-muted);
    cursor: pointer;
}
.richtext__btn:hover { background: var(--color-card-border); color: var(--color-text); }
/* Vervolgsessie #3: vrije native <input type="color"> vervangen door een
   beperkt, vooraf samengesteld palet (6 knoppen) -- zie
   layout/_richtext_field.html voor de motivatie. Geen native-widget-
   eigenaardigheden meer (voorheen respecteerde Chrome's ingebouwde
   kleurenkiezer de eigen width/height-CSS niet betrouwbaar). */
.richtext__swatches { display: inline-flex; align-items: center; gap: 0.3rem; margin-left: 0.2rem; padding-left: 0.5rem; border-left: 1px solid var(--color-card-border); }
.richtext__swatch {
    width: 1.1rem;
    height: 1.1rem;
    padding: 0;
    border: 1px solid var(--color-card-border);
    border-radius: 50%;
    cursor: pointer;
}
.richtext__swatch:hover { transform: scale(1.15); }

/* Vervolgsessie #3: herbruikbaar gestileerd bestandskeuzeveld
   (layout/_file_input_field.html) -- de native input is `.sr-only`
   (verborgen maar functioneel), `.file-input__button` is de zichtbare,
   klikbare <label> ervoor. Zelfde rand/radius/achtergrond-opbouw als de
   rest van de knoppen in het project (lichtgrijs, grijze rand, afgeronde
   hoeken), niet de kale browser-default. */
.file-input { display: flex; align-items: center; gap: 0.6rem; }
.file-input__button {
    display: inline-flex;
    align-items: center;
    padding: 0.5rem 1rem;
    background: var(--color-bg);
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    color: var(--color-text);
    font-size: var(--font-size-control);
    font-weight: 600;
    cursor: pointer;
}
.file-input__button:hover { background: var(--color-card-border); }
/* 3de vervolgsessie: `flex:1` + `min-width:0` laat de bestandsnaam de
   resterende ruimte vullen en (via ellipsis) inkrimpen i.p.v. te wrappen
   -- zonder dit duwde een lange bestandsnaam de "Verwijderen"-link naar
   een 2de regel i.p.v. ernaast te blijven staan. */
.file-input__filename {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--font-size-control);
    color: var(--color-text-muted);
}
.file-input__remove { flex: none; color: var(--color-danger); font-size: 0.85rem; text-decoration: none; }
.file-input__remove:hover { text-decoration: underline; }
.richtext__editable {
    height: 10.5rem;
    overflow-y: auto;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    font: inherit;
    font-size: var(--font-size-control);
    background: #ffffff;
}
.richtext__editable:focus-visible { outline: none; border-color: var(--color-focus-border); }

/* Vervolgsessie (Categorieën-verfijning) punt 12: generieke NL/EN-
   tabbladen voor vertaalbare velden (layout/_translated_field_tabs.html)
   -- schaalt naar meer talen zonder herontwerp, niet Categorieën-specifiek. */
.lang-tabs { margin-bottom: 1.1rem; }
.lang-tabs__nav { display: flex; gap: 0.25rem; margin-bottom: 0.5rem; }
.lang-tabs__tab {
    padding: 0.3rem 0.75rem;
    border: 1px solid var(--color-card-border);
    border-radius: var(--radius-tight);
    background: #ffffff;
    color: var(--color-text-muted);
    font: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
}
.lang-tabs__tab.is-active { background: var(--color-accent); color: var(--color-text); border-color: transparent; }
/* 3de vervolgsessie: de losse `max-width: 50%`-patch hier is weer weg --
   category_form.html plaatst Naam/Omschrijving nu in een echte
   `.client-form__grid`-kolom (zelfde patroon als elk ander veld in dit
   formulier), die zelf al de juiste breedte geeft. Zie ook de toelichting
   bij `.richtext` hierboven. */

/* Module 2 -- Categorieën (products app) */

/* Vervolgsessie punt 8: verbreed van 5.5rem -- top-level-rijen tonen nu 3
   iconen (potlood/vuilbak/+) i.p.v. 2. */
.category-table__actions-col { width: 8.5rem; }
/* Vervolgsessie #3, grondoorzaak-fix: `display: flex` stond hier
   voorheen rechtstreeks OP de `<td>` zelf -- dat wijzigt de berekende
   `display`-waarde van een tabelcel van `table-cell` naar (in dit geval)
   een flex-container, waardoor de cel niet langer meedoet aan het
   normale rij-hoogte-/randmodel van de tabel (ze kreeg zo haar EIGEN,
   los berekende hoogte i.p.v. de hoogte van haar rij te delen, wat de
   "kolomrand Zichtbaarheid verspringt tussen 2 rijen"-melding
   veroorzaakte -- in werkelijkheid was het de Naam-cel die te kort was,
   niet Zichtbaarheid, maar het zichtbare "sprongetje" zat toevallig op
   de grens ernaast). De `<td>` zelf blijft nu een gewone tabelcel; enkel
   een NIEUWE, inwendige `.category-table__name-inner`-wrapper krijgt de
   flex-opmaak. */
.category-table__name-inner { display: flex; align-items: center; gap: 0.5rem; }
/* Subcategorie-rijen springen in onder hun hoofdcategorie -- enige visuele
   aanwijzing van de boomstructuur naast de volgorde zelf (spec §1.1: 1
   niveau vandaag, model blijft van nature onbeperkt diep).
   Vervolgsessie #3, grondoorzaak-fix: deze regel gaf voorheen geen
   zichtbaar effect -- `.console-table td`'s `padding: 0.65rem 0.75rem`
   (shorthand, class+element = hogere specificiteit dan deze ene class
   alleen) zette `padding-left` telkens terug naar 0.75rem, ongeacht
   bronvolgorde. Beide classes staan altijd samen op dezelfde `<td>`
   (zie category_list.html), dus de gecombineerde selector hieronder is
   altijd geldig en wint gegarandeerd (2 classes > 1 class + 1 element). */
.category-table__name-cell.category-table__name-cell--sub { padding-left: 1.75rem; }
/* Vervolgsessie punt 4/6: Zichtbaarheid- én Afsluitregel-kolom op
   subcategorie-rijen krijgen bewust hetzelfde kleinere lettertype --
   grondoorzaak-fix van de verticale verspringing uit punt 6: toen had
   enkel Zichtbaarheid dit kleinere font-size (en dus een andere
   line-height dan zijn buren), waardoor de door de browser
   middengeplaatste tekst net niet paste. Een expliciete, met de 0.9rem-
   buren gedeelde line-height (i.p.v. de kleinere ambient normal-waarde die
   0.8rem anders zelf zou berekenen) maakt de tekstbox-hoogte weer gelijk
   aan die van Naam/Status/Acties op dezelfde rij, ongeacht dit kleinere
   font-size. **Vervolgsessie #3**: # Producten kreeg dit ONTERECHT ook
   mee (niet gevraagd) -- terug verwijderd uit de template, blijft nu
   dezelfde grootte als de rest van de rij. */
.category-table__muted,
.category-table__parent-hint { color: var(--color-text-muted); font-size: 0.8rem; line-height: 1.35rem; }
.category-table__parent-hint { display: block; }
/* "Zelfde als bovenliggende categorie"-icoon (punt 5, Lucide
   `corner-down-right`, vervangt het eerdere pijl-omhoog-icoon) i.p.v. de
   zichtbaarheidstekst nogmaals uit te schrijven op een subcategorie-rij --
   enkel in de boomweergave (de gefilterde/platte weergave toont wel de
   tekst, want de hoofdcategorie-rij staat daar niet noodzakelijk ernaast).
   Vervolgsessie #3: nu ook vóór de bestaande "Zelfde regel als ..."-tekst
   in de Afsluitregel-kolom, voor visuele consistentie tussen beide
   kolommen -- daar blijft de tekst zelf wél staan (informatiever, kan ook
   "Eigen regel"/"Standaard" tonen). */
.category-table__inherit-hint { display: inline-flex; flex: none; color: var(--color-text-faint); margin-right: 0.3rem; vertical-align: middle; }
/* Sleep-greep -- puur een visuele affordance (de hele rij is `draggable`,
   zie static/js/category-reorder.js), geen apart klik-doelgebied. */
.category-table__drag-handle {
    display: inline-flex;
    flex: none;
    color: var(--color-text-faint);
    cursor: grab;
}
.category-table__row[data-order-group] { cursor: grab; }
.category-table__row.is-dragging { opacity: 0.4; }
/* Vervolgsessie #3: `display:flex` verhuisd van de `<td>` zelf (zie de
   grondoorzaak-toelichting bij .category-table__name-inner hierboven --
   zelfde risico, hier nog zonder zichtbaar symptoom omdat de iconen se
   eigen hoogte toevallig al bijna gelijk was aan de rij, maar wel
   dezelfde sluimerende bug) naar deze nieuwe, inwendige wrapper. */
.category-table__actions-inner { display: flex; gap: 0.35rem; align-items: center; }

/* Afbeeldingsveld (create/bewerken) -- Category.image is het eerste
   ImageField in het project, geen bestaand crop/preview-patroon om te
   hergebruiken (zie CLAUDE.md "Module 2 — Categorieën"). Enkel een simpele
   preview van de al opgeslagen afbeelding bij het bewerken. */
.category-form__image-preview {
    display: block;
    max-width: 120px;
    max-height: 120px;
    object-fit: cover;
    border-radius: var(--radius-tight);
    margin-bottom: 0.5rem;
}
