/* =====================================================================
   eticketskopen.nl - basisstijl
   Fase 2 van de huisstijl-feature (hoofdstuk 4.6.5): platform-restyling
   op basis van de door de klant aangeleverde designcollage ("Eticketskopen
   Collage", Claude Design, 2026-08-02) - paars (#5D5DFF) als enige
   interactiekleur, geel (#FFC80A) als signaalaccent, "glazen" panelen op
   een zacht crème/lavendel-verloop, ronde "pil"-vormige knoppen/badges/
   navigatie. Dit bestand bevat de FUNDAMENT-laag (kleuren, lettertypes,
   randen/schaduwen, basiscomponenten als .card/.button/.badge) - de
   grotere structurele stukken uit de collage (het donkere "glaspaneel"-
   dashboardschild, de zwevende pil-navigatie, het scanscherm in twee
   varianten, de white-label ticketshop) volgen in een latere ronde.

   Lettertypes ('Plus Jakarta Sans' voor tekst, 'JetBrains Mono' voor
   labels/codes) komen uit diezelfde collage - bewust ZELF gehost onder
   assets/fonts/ (uit de collage's eigen ingebedde woff2-bestanden
   gehaald) i.p.v. een Google Fonts-CDN, zodat het platform geen externe
   lettertype-afhankelijkheid krijgt (past bij de rest van het platform).
   Beide zijn variabele fonts - één bestand dekt het hele gewichtenbereik.
   ===================================================================== */

@font-face {
    font-family: "Plus Jakarta Sans";
    src: url("../fonts/PlusJakartaSans-variable.woff2") format("woff2");
    font-weight: 400 800;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "JetBrains Mono";
    src: url("../fonts/JetBrainsMono-variable.woff2") format("woff2");
    font-weight: 400 700;
    font-style: normal;
    font-display: swap;
}

/* Suez One (Google Font, OFL-licentie) - alleen voor de koppen op het
   nieuwe flyerontwerp van het printbare ticket (klantbeslissing
   2026-08-09), niet voor de rest van het platform. Eén statisch gewicht
   i.p.v. een variabel font zoals de twee hierboven, vandaar
   "-Regular" i.p.v. "-variable" in de bestandsnaam. Zolang het
   bronbestand nog niet is omgezet naar woff2 laadt deze regel simpelweg
   niets en valt --font-display (hieronder in :root) terug op Georgia. */
@font-face {
    font-family: "Suez One";
    src: url("../fonts/SuezOne-Regular.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

:root {
    --brand: #5d5dff;
    --brand-dark: #101014;
    --brand-light: #eeeaff;
    --accent: #ffc80a;
    --accent-ink: #8a6800;
    --ink: #101014;
    --muted: #7c7c89;
    --surface: #ffffff;
    --surface-alt: #f6f6ff;
    --border: #dcdce2;
    --danger: #b3352b;
    --danger-bg: #fbeceb;
    --success: #1f7a5a;
    --success-bg: #e7f5ef;
    --radius: 10px;
    --radius-lg: 16px;
    --radius-panel: 22px;
    --radius-pill: 999px;
    --font-sans: "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Consolas, "Liberation Mono", monospace;
    --font-display: "Suez One", Georgia, "Times New Roman", serif;
    --shadow-card: 0 10px 30px rgba(20, 20, 60, 0.05);
    --shadow-pill: 0 6px 16px rgba(20, 20, 60, 0.08);
}

* {
    box-sizing: border-box;
}

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

body {
    margin: 0;
    /* Zonder dit blijft het canvasverloop/dashboardschild (.canvas-body/
       .dashboard-body hieronder) beperkt tot de hoogte van de content -
       op een korte pagina (bijv. inloggen) stopt de gradient dan
       halverwege het scherm i.p.v. de hele viewport te vullen. */
    min-height: 100vh;
    /* "Sticky footer"-patroon: body is een flex-kolom van de volle
       viewporthoogte, .page (hieronder) krimpt niet mee maar groeit juist
       mee (flex-grow) om de resterende ruimte op te vullen - de voettekst
       (site-footer) wordt daardoor altijd tegen de onderkant van het
       scherm geduwd, ook als de pagina-inhoud zelf te kort is om de
       viewport te vullen. Zonder dit stapelt de voettekst gewoon direct
       onder de content, met lege ruimte er nog onder. */
    display: flex;
    flex-direction: column;
    font-family: var(--font-sans);
    color: var(--ink);
    background: var(--surface-alt);
    line-height: 1.55;
    font-size: 16px;
    /* Voorkomt in-/uitzoomen bij dubbeltikken (o.a. scan-app-inlog- en
       evenementkeuzepagina's) - de viewport-meta (user-scalable=no)
       wordt door moderne iOS Safari genegeerd voor dit specifieke
       gebaar, touch-action is de betrouwbare aanpak. Pinch-zoom blijft
       gewoon werken (manipulation staat dat toe), alleen de dubbeltik-
       snelkoppeling wordt uitgeschakeld. */
    touch-action: manipulation;
}

/* Voorkomt de andere vorm van "inzoomen" op mobiel (o.a. ticketshop-
   checkout): dubbeltikken op een <label> (bijv. een tickettype-kaart of
   een aanvinkvakje-tekst) selecteert anders de tekst, en iOS Safari
   toont daarbij een vergrotende selectie-loep + keuzemenu - dat voelt
   hetzelfde aan als inzoomen maar wordt NIET door touch-action hierboven
   tegengehouden (dat blokkeert alleen de dubbeltik-zoom-snelkoppeling
   op de pagina zelf, niet native tekstselectie). input/textarea blijven
   bewust wel gewoon selecteerbaar, anders kun je niet meer bewerken. */
label,
.button,
button,
.badge {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
}

input,
textarea,
select {
    -webkit-user-select: text;
    user-select: text;
}

h1, h2, h3 {
    font-family: var(--font-sans);
    color: var(--brand-dark);
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1.2;
    margin: 0 0 0.6em;
}

h1 {
    font-size: 1.9rem;
}

/* Paginatitel + optionele "Terug naar ..."-knop (klantbeslissing
   2026-08-05, View::render()'s $backLink) op dezelfde regel i.p.v. de
   knop ergens linksonder de pagina - de ruimte die h1's eigen
   margin-bottom normaal gaf, staat nu op .page-header zelf. */
.page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 0.6em;
}

.page-header h1 {
    margin-bottom: 0;
}

.page-header__back {
    flex: none;
}

h2 {
    font-size: 1.4rem;
}

p {
    margin: 0 0 1em;
}

a {
    color: var(--brand);
    text-decoration: underline;
    text-underline-offset: 2px;
}

code {
    font-family: var(--font-mono);
    background: var(--surface-alt);
    padding: 0.15em 0.4em;
    border-radius: 4px;
    font-size: 0.9em;
    overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------------
   Kopieerbare link (deel-links, hoofdstuk 4.4) - de link zelf mag
   afbreken (overflow-wrap hierboven), het knopje blijft op zijn eigen
   regel staan i.p.v. mee te breken.
   --------------------------------------------------------------------- */

.copy-link {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 1em;
}

.copy-link__value {
    flex: 1 1 auto;
    min-width: 0;
}

.copy-link__btn {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--surface-alt);
    color: var(--ink);
    font-size: 0.82rem;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
}

.copy-link__btn:hover {
    background: var(--brand-light);
}

.copy-link__btn svg {
    width: 14px;
    height: 14px;
    flex: none;
}

/* ---------------------------------------------------------------------
   Layout chrome: header met het "ticketreep"-beeldmerk, en een simpele
   centrale container. Mobile-first: header stapelt niet, blijft compact.
   --------------------------------------------------------------------- */

/* Sticky (klantverzoek 2026-08-06) + geen rechter padding: de
   logo-pil hieronder bleedt tot de ware rechterrand van de viewport,
   zelfde foutbestendige aanpak als .dashboard-topbar (geen padding om
   een negatieve marge tegen te hoeven spiegelen). Bewust GEEN eigen
   achtergrond (meer) - klantbeslissing 2026-08-08: alleen de witte
   logo-pil zelf is zichtbaar, de rest van de balk is transparant en
   toont de hero-afbeelding/pagina-achtergrond erdoorheen. Ruimere
   padding-top dan onder (klantverzoek 2026-08-08, "meer marge boven
   het kader") - onder is nog steeds compact, dit was nooit een volle
   balk om voor te reserveren. */
.site-header {
    padding: 24px 0 8px 20px;
    position: sticky;
    top: 0;
    z-index: 30;
}

/* Op de ticketshop-hoofdpagina (met een hero-afbeelding, EventStorefront-
   Controller::renderEventHero() - altijd het eerste kind van <main>)
   mag de logo-pil bovenop de hero beginnen i.p.v. er ruimte boven te
   reserveren (klantverzoek 2026-08-08: "die mag al op de hero
   afbeelding beginnen en dan de hero afbeelding iets eerder [beginnen]").
   position:fixed i.p.v. sticky: dat haalt de balk volledig uit de
   documentflow (de hero start dan vanzelf bovenaan de pagina, geen
   handmatige negatieve marge nodig om een geschat balkhoogte te
   compenseren) en blijft, net als sticky, tijdens het scrollen in
   beeld. Alleen deze specifieke combinatie - elke andere pagina (zonder
   hero als eerste paginakind) houdt gewoon de gewone sticky-balk. */
.site-header:has(+ main.page > .event-hero:first-child) {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
}

/* Geen max-width/margin:auto hier (zelfde bug als .dashboard-topbar
   ooit had): dat zou op een scherm breder dan 1600px een gat overlaten
   tussen de pil en de ware rechterrand - de pil moet tot de rand van de
   viewport bleeden, niet tot de rand van de 1600px-brede inhoud. */
.site-header__inner {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
}

/* Organisatielogo als witte pil rechts, "uit de zijkant komend"
   (klantverzoek 2026-08-06, View.php $publicHeaderLogoHtml) - het
   spiegelbeeld van .dashboard-topbar__logo: hier links afgerond i.p.v.
   rechts, want de pil bleedt naar RECHTS toe. */
.site-header__logo-pill {
    display: flex;
    align-items: center;
    padding: 8px 20px 8px 18px;
    border-radius: var(--radius-pill) 0 0 var(--radius-pill);
    background: #ffffff;
    box-shadow: var(--shadow-pill);
    box-sizing: border-box;
}

.site-header__logo-pill img {
    display: block;
    max-height: 36px;
    max-width: 160px;
}

/* Groter op desktop (klantverzoek 2026-08-08) - alleen max-height/
   max-width, geen object-fit en geen losse HTML width/height-attributen
   op de <img>, dus de browser schaalt het logo altijd naar verhouding:
   werkt voor liggende, staande én vierkante logo's zonder vervorming -
   een staand/vierkant logo loopt gewoon eerder tegen max-height aan
   (en blijft dus smaller dan max-width), een liggend logo eerder tegen
   max-width. Op mobiel blijft de kleinere basisregel hierboven staan
   (weinig ruimte naast de rest van de pagina-inhoud). */
@media (min-width: 900px) {
    .site-header__logo-pill img {
        max-height: 52px;
        max-width: 240px;
    }
}

.site-header__logo-pill .logo-mark--dark span {
    background: var(--ink);
}

.site-header__logo-pill .logo-mark {
    height: 16px;
}

.site-header__logo-pill .logo-text--dark {
    color: var(--ink);
}

.logo-mark {
    display: flex;
    align-items: flex-end;
    gap: 3px;
    height: 22px;
}

.logo-mark span {
    display: block;
    width: 5px;
    background: var(--surface);
    border-radius: 1px;
}

.logo-mark span:nth-child(1) { height: 60%; }
.logo-mark span:nth-child(2) { height: 100%; }
.logo-mark span:nth-child(3) { height: 45%; }
.logo-mark span:nth-child(4) { height: 80%; }

.logo-text {
    color: var(--surface);
    font-weight: 700;
    font-size: 1.05rem;
    letter-spacing: -0.01em;
}

.logo-text .dot {
    color: var(--accent);
}

.page {
    max-width: 1600px;
    margin: 0 auto;
    padding: 32px 20px 64px;
    /* Groeit mee met de resterende ruimte in de flex-kolom van body
       hierboven ("sticky footer"-patroon) - duwt de voettekst altijd
       tegen de onderkant van het scherm, ook op korte pagina's. */
    flex: 1 0 auto;
    width: 100%;
    box-sizing: border-box;
    /* display:flex/column (klantbeslissing 2026-08-05, i.p.v. het
       eerdere gewone block-element) - alleen zodat .tab-dock hieronder
       `margin-top: auto` kan gebruiken om zichzelf naar de onderkant van
       .page te duwen op een korte pagina (inhoud vult het scherm niet).
       Verandert verder niets aan de opmaak: elk kind hier krijgt van
       CSS-grid/flex-column standaard dezelfde volle breedte als voorheen
       in normale block-flow, en margin-collapsing speelt in de praktijk
       toch geen rol - kaarten/koppen/paragrafen in dit project gebruiken
       uitsluitend margin-bottom, nooit margin-top, dus er was hiervoor
       ook al nooit een aangrenzende marge die kon "collapsen". */
    display: flex;
    flex-direction: column;
}

.page--narrow {
    max-width: 440px;
}

/* Tussenmaat voor de aanmaakwizard (klantverzoek 2026-08-13) - geeft
   .field-grid (minmax(260px,1fr)) ruimte voor 2-3 kolommen op desktop,
   zonder de wizard even breed te maken als de platformbrede .page
   (1600px, bedoeld voor lijstpagina's). */
.page--medium {
    max-width: 900px;
}

/* Geen eigen padding (i.p.v. een eigen, nog bredere max-width - .page
   hierboven is nu al platformbreed 1600px) - alleen voor de publieke
   ticketshop-hoofdpagina (collage "05 Ticketshop"), zodat de hero zelf
   tot de randen van de pagina loopt. De rest van de inhoud krijgt zijn
   eigen ademruimte terug via .page-inner hieronder. */
.page--full {
    padding: 0 0 64px;
}

.page-inner {
    padding: 24px 24px 0;
}

@media (min-width: 900px) {
    .page-inner {
        padding: 32px 32px 0;
    }
}

/* Platform-restyling fase 2 (hoofdstuk 4.6.5, "01 Backend - organisator"
   uit de designcollage): het dashboardschild voor ingelogde organisatie-
   schermen - zacht crème/lavendel-verloop i.p.v. de platte
   --surface-alt-achtergrond, met een zwevende "pil"-navigatie bovenaan
   i.p.v. de simpele donkere site-header. Wordt toegepast wanneer
   View::useDashboardChrome()/useAdminChrome() is aangeroepen (de
   ingelogde organisatie-/adminschermen).

   .canvas-body (restyle-opdracht, Stijling/RESTYLE_PROMPT.md - "Canvas-
   kleur: zacht paars-crème verloop, Paginabackground, geen effen wit"):
   ZELFDE verloop, maar voor de eigen platformpagina's zonder dashboard-
   chrome - inloggen/2FA/activeren, adminsetup en de marketingsite. De
   wit-label klantpagina's (ticketshop/checkout/e-ticket/deel-links, waar
   de organisatie een eigen merkkleur/logo kan hebben) krijgen dit
   BEWUST niet - zie de toelichting bij View::render(). */
body.dashboard-body,
body.canvas-body {
    background: linear-gradient(138deg, #fdfcf8 0%, #f7f7ff 30%, #ecebff 62%, #fff9e8 100%);
}

/* Grid i.p.v. flex met justify-content:space-between - dat zou de
   pil-navigatie tegen de linkerrand duwen (er staat niets links
   ernaast). Twee gelijke 1fr-kolommen aan weerszijden van de
   auto-brede navigatie houden die daadwerkelijk gecentreerd, ongeacht
   de breedte van het gebruikersbadge rechts. */
.dashboard-topbar {
    /* Bewust GEEN max-width/margin:auto (klantbeslissing 2026-08-06, na
       feedback dat de logo-/accountpil nog niet echt helemaal links/
       rechts tegen de paginarand stonden): met een max-width van 1600px
       zoals .page hieronder wel heeft, zou dat op een breder scherm dan
       1600px alsnog een zichtbare marge tussen de pil en de ware
       viewportrand overlaten. De pil-navigatie in het midden blijft
       gewoon horizontaal gecentreerd op de viewport (grid 1fr-auto-1fr),
       dus die staat nog steeds in lijn met de wél op 1600px begrensde
       .page-inhoud eronder. Ook bewust GEEN horizontale padding: met
       padding hier zou de negatieve marge op de pillen hieronder altijd
       exact het paddingcijfer moeten spiegelen om de ware rand te raken -
       foutgevoelig zodra een van beide ooit wijzigt. */
    padding: 20px 0 0;
    /* Sticky (klantbeslissing 2026-08-06): blijft bovenin staan tijdens
       het scrollen, paginainhoud schuift er gewoon achter/onderdoor -
       er zit bewust geen eigen achtergrond op .dashboard-topbar zelf
       (alleen de 3 pillen hebben er een), dus dat "doorschuiven" is ook
       zichtbaar in de gaten tussen de pillen. z-index laag genoeg om
       onder modals/overlays (900+) te blijven, hoog genoeg voor gewone
       paginainhoud. */
    position: sticky;
    top: 0;
    z-index: 30;
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    /* stretch i.p.v. center (klantbeslissing 2026-08-05): logo- en
       accountpil moeten precies zo hoog worden als het menu, dat zelf de
       hoogte van de rij bepaalt (.pill-nav is de hoogste van de drie). */
    align-items: stretch;
    gap: 16px;
}

.dashboard-topbar .pill-nav {
    grid-column: 2;
    justify-self: center;
}

.dashboard-topbar .dashboard-topbar__user {
    grid-column: 3;
    justify-self: end;
}

/* Platformlogo linksboven in de dashboardschild-topbar (klantbeslissing
   2026-08-05). Witte pil (zelfde als de accountnaam-pil rechts) met het
   gekleurde platformlogo i.p.v. de witte variant - deze pil zit niet op
   een donkere achtergrond zoals de header/voettekst, dus het gewone
   logo is hier juist beter leesbaar. Komt "uit de zijkant": vlak
   (niet afgerond) aan de linkerkant, alleen rechts afgerond, en met een
   negatieve marge tot tegen de linkerrand van de topbar-container
   getrokken - het spiegelbeeld van de accountpil rechts hieronder. */
.dashboard-topbar__logo {
    grid-column: 1;
    justify-self: start;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 18px 0 20px;
    border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
    background: #ffffff;
    box-shadow: var(--shadow-pill);
    box-sizing: border-box;
}

/* Zie .site-footer__logo hieronder - zelfde reden voor de ruimere
   max-height (was 24px); .dashboard-topbar__logo stretcht toch al mee
   met de hoogte van de pil-navigatie ernaast, dus er is ruim plek. */
.dashboard-topbar__logo img {
    display: block;
    max-height: 36px;
    max-width: 140px;
}

.dashboard-topbar__logo .logo-mark--dark span {
    background: var(--ink);
}

.dashboard-topbar__logo .logo-mark {
    height: 16px;
}

.dashboard-topbar__logo .logo-text--dark {
    color: var(--ink);
}

.pill-nav {
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 6px;
    border-radius: var(--radius-pill);
    background: rgba(16, 16, 22, 0.92);
    box-shadow: 0 12px 30px rgba(16, 16, 40, 0.28);
    overflow-x: auto;
    max-width: 100%;
}

.pill-nav__item {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 11px 18px;
    border-radius: var(--radius-pill);
    font-size: 0.88rem;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.66);
    text-decoration: none;
    white-space: nowrap;
    flex: none;
}

.pill-nav__item:hover {
    color: #fff;
}

.pill-nav__item--active {
    background: #fff;
    color: var(--ink);
    font-weight: 700;
}

.pill-nav__item--active .pill-nav__dot {
    background: var(--brand);
}

.pill-nav__dot {
    width: 9px;
    height: 9px;
    border-radius: 3px;
    background: transparent;
    flex: none;
}

/* Spiegelbeeld van .dashboard-topbar__logo hierboven (klantbeslissing
   2026-08-05): alleen links afgerond, vlak/tegen de rechterrand van de
   topbar-container getrokken met een negatieve marge. */
.dashboard-topbar__user {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 20px 0 18px;
    border-radius: var(--radius-pill) 0 0 var(--radius-pill);
    background: #fff;
    box-shadow: var(--shadow-pill);
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--ink);
    white-space: nowrap;
    box-sizing: border-box;
}

.dashboard-topbar__user-badge {
    width: 30px;
    height: 30px;
    border-radius: var(--radius-pill);
    background: var(--brand);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.8rem;
    font-weight: 700;
    flex: none;
}

@media (max-width: 720px) {
    .dashboard-topbar {
        grid-template-columns: 1fr;
        align-items: stretch;
        justify-items: stretch;
        /* Gestapeld hebben de pillen weer gewone ademruimte tot de rand
           nodig (ze zijn dan rondom afgerond, niet meer "uit de zijkant
           komend") - de desktop-variant hierboven heeft hier bewust geen
           padding, zie de toelichting daar. */
        padding-left: 20px;
        padding-right: 20px;
    }

    /* Op één kolom gestapeld heeft "uit de zijkant komen" geen betekenis
       meer (er is geen duidelijke linker-/rechterrand-relatie tussen de
       pillen) - dus hier gewoon een normale, volledig afgeronde pil. */
    .dashboard-topbar__logo,
    .dashboard-topbar .dashboard-topbar__user {
        grid-column: 1;
        justify-self: start;
        border-radius: var(--radius-pill);
    }

    .dashboard-topbar .pill-nav {
        grid-column: 1;
        justify-self: stretch;
    }
}

/* Telefoonspecifieke polish bovenop het gestapelde @720px-blok hierboven
   (klantmelding 2026-08-14: "de weergave van o.a. de logo's menu balk en
   klantnaam niet goed weergegeven"). De klantnaam stond als kale tekst
   zonder eigen element (zie renderTopbar() in View.php - nu wél een
   <span>), dus kon nooit worden afgekapt: bij een lange organisatie-/
   gebruikersnaam werd de pil breder dan het scherm. */
@media (max-width: 480px) {
    .dashboard-topbar__user-name {
        max-width: 45vw;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* Vloeiender scrollen op iOS + iets compactere pillen zodat er meer
       tabs in beeld passen zonder te hoeven scrollen. */
    .pill-nav {
        -webkit-overflow-scrolling: touch;
    }

    .pill-nav__item {
        padding: 10px 14px;
        font-size: 0.84rem;
    }
}

/* Donkere, schermbrede voettekst uit de designcollage (klantbeslissing
   2026-08-02, View::renderSiteFooter()) - vervangt de eerdere lichte,
   gecentreerde voettekst (alleen een logo). Ronde bovenhoek net als een
   paneel (--radius-panel), net als de zwevende pil-navigatie een eigen
   "afgesloten" vlak i.p.v. een vlakke balk die de hele viewportbreedte
   pakt. */
/* position:relative + z-index hoger dan .checkout-summary-toggle
   (klantverzoek 2026-08-08: de voettekst-links vielen op mobiel achter
   die vaste "Bestelling bekijken"-balk) - position:static-elementen
   (de standaardwaarde) negeren z-index volledig, dus zonder relative
   zou dit niets doen. .checkout-summary-toggle is fixed en toont
   daardoor normaal altijd bovenop de rest van de pagina, ook bovenop de
   voettekst zodra die aan het einde van de pagina in beeld scrollt -
   deze regel laat de voettekst daar juist overheen tekenen (heeft zelf
   toch al een eigen ondoorzichtige achtergrond, dus de balk verdwijnt
   daar simpelweg achter, precies op het moment dat hij toch niets meer
   te bedienen heeft). */
.site-footer {
    position: relative;
    z-index: 90;
    background: var(--brand-dark);
    border-radius: var(--radius-panel) var(--radius-panel) 0 0;
    padding: 28px 32px;
    margin-top: 40px;
}

.site-footer__inner {
    max-width: 1600px;
    margin: 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

/* max-height/max-width samen (i.p.v. een vaste hoogte) laten een
   <img> zonder eigen width/height-attributen altijd zijn eigen
   beeldverhouding behouden - een liggend logo wordt dus door
   max-width begrensd, een staand/vierkant logo door max-height, geen
   van beide raakt ooit vervormd. max-height bewust ruimer dan
   voorheen (klantverzoek 2026-08-06, was 28px) - anders werd een
   staand/vierkant logo tot een piepklein duimnagel-formaat
   teruggeschaald terwijl er in de voettekst ruim plek voor is. De
   header heeft sinds klantverzoek 2026-08-06 zijn eigen witte
   logo-pil (.site-header__logo-pill hierboven) i.p.v. dit logo
   rechtstreeks op de donkere achtergrond te tonen - de voettekst blijft
   wél ALTIJD het platformlogo/-beeldmerk (nooit het tenant-logo - geen
   instelling die de klant kan overschrijven), dus .logo-mark/.logo-text
   hier gewoon hun eigen witte basisstijl houden (bedoeld voor een
   donkere achtergrond) i.p.v. de eerdere donker-op-licht-override die
   bij de oude lichte voettekst hoorde. */
.site-footer__logo {
    max-height: 40px;
    max-width: 200px;
}

.site-footer__brand {
    display: flex;
    align-items: center;
    gap: 12px;
}

.site-footer__brand-label {
    color: var(--muted);
    font-size: 0.82rem;
}

.site-footer__links {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 24px;
}

.site-footer__links a {
    color: var(--surface);
    text-decoration: none;
    font-size: 0.85rem;
    font-weight: 600;
}

.site-footer__links a:hover {
    color: var(--accent);
}

/* Kleiner op mobiel (klantverzoek 2026-08-08) - minder padding en een
   kleiner logo/label/links, past bij hoe compact de rest van de mobiele
   pagina's al zijn i.p.v. dezelfde ruime desktopmaten te hergebruiken.
   Gecentreerd (klantverzoek 2026-08-09, was flex-start) - .site-footer__
   brand/__links zijn zelf ook flex-rijen, dus align-items:center hier
   centreert zowel de twee rijen zelf (t.o.v. elkaar) als hun eigen
   inhoud (logo+label, resp. de losse links) t.o.v. het midden. */
@media (max-width: 640px) {
    .site-footer {
        padding: 18px 20px;
    }

    .site-footer__inner {
        flex-direction: column;
        align-items: center;
        text-align: center;
        gap: 12px;
    }

    .site-footer__links {
        justify-content: center;
    }

    .site-footer__logo {
        max-height: 28px;
        max-width: 140px;
    }

    .site-footer__brand-label {
        font-size: 0.75rem;
    }

    .site-footer__links {
        gap: 16px;
    }

    .site-footer__links a {
        font-size: 0.8rem;
    }
}

/* Homepage-hoofddomein (hoofdstuk 4.15) - bewust EEN gecentreerd
   koppositie i.p.v. de eerdere stapel van 6 losse featurekaarten: één
   duidelijke boodschap + één call-to-action, met een kort rijtje
   kernpunten eronder in plaats van volle kaarten (klantbeslissing
   2026-08-05, "veel simpeler en mooier"). Zit al op het bestaande
   .canvas-body-verloop, geen eigen achtergrond nodig. */
.marketing-hero {
    text-align: center;
    padding: 64px 24px 8px;
}

.marketing-hero__eyebrow {
    display: inline-block;
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--brand);
    margin-bottom: 16px;
}

.marketing-hero h1 {
    font-size: 2.6rem;
    max-width: 720px;
    margin: 0 auto 18px;
}

.marketing-hero p {
    font-size: 1.08rem;
    color: var(--muted);
    max-width: 540px;
    margin: 0 auto 30px;
}

.marketing-highlights {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 28px;
    max-width: 1000px;
    margin: 0 auto;
    padding: 8px 24px 40px;
    text-align: left;
}

.marketing-highlights__item h3 {
    font-size: 1.05rem;
    margin: 0 0 6px;
}

.marketing-highlights__item p {
    margin: 0;
    color: var(--muted);
    font-size: 0.92rem;
}

@media (max-width: 720px) {
    .marketing-hero h1 {
        font-size: 2rem;
    }

    .marketing-highlights {
        grid-template-columns: 1fr;
    }
}

/* Hero-banner boven de publieke evenementpagina (hoofdstuk 4.6.5,
   huisstijl + collage "05 Ticketshop") - optioneel, per evenement (geen
   headerafbeelding geüpload = helemaal geen hero, geen lege banner). */
.event-hero {
    position: relative;
    width: 100%;
    aspect-ratio: 3.5 / 1;
    overflow: hidden;
    margin-bottom: 20px;
    color: #ffffff;
}

.event-hero__image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: var(--hero-crop, center center);
    display: block;
}

/* "Passend"-stand, apart instelbaar voor desktop/mobiel (klantverzoek
   2026-08-20, events.header_image_fit_desktop/_mobile) - volledige
   afbeelding zichtbaar, geen bijsnijden. Achtergrond: de merkkleur van
   de klant (var(--brand), altijd al gezet door View::render()), of een
   per-evenement afwijkende kleur via --hero-bg (events.header_image_bg_color,
   inline gezet door EventStorefrontController::renderEventHero()).
   Media-query-gated i.p.v. 1 gedeelde klasse: desktop en mobiel kunnen nu
   onafhankelijk "uitvullen"/"passend" zijn, dus de contain-regel mag
   alleen op de bijbehorende breedte gelden - .event-hero zelf wisselt
   immers ook van beeldverhouding op dezelfde 640px-grens hieronder. */
@media (min-width: 641px) {
    .event-hero--desktop-contain {
        background: var(--hero-bg, var(--brand));
    }

    .event-hero--desktop-contain .event-hero__image {
        object-fit: contain;
    }
}

.event-hero__scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(16, 16, 30, 0.35) 0%, rgba(16, 16, 30, 0.1) 40%, rgba(16, 16, 30, 0.85) 100%);
}

.event-hero__content {
    position: relative;
    height: 100%;
    box-sizing: border-box;
    padding: 24px;
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
}

.event-hero__title {
    color: #ffffff;
    margin: 0 0 6px;
    font-size: clamp(1.5rem, 4vw, 2.3rem);
    letter-spacing: -0.02em;
}

.event-hero__meta {
    font-size: 0.95rem;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.85);
}

@media (max-width: 640px) {
    .event-hero {
        aspect-ratio: 4 / 3;
    }

    .event-hero--mobile-contain {
        background: var(--hero-bg, var(--brand));
    }

    .event-hero--mobile-contain .event-hero__image {
        object-fit: contain;
    }

    /* Bugfix (klantverzoek 2026-08-08): met maar 1 kind (.event-hero__text)
       zette de overgeërfde justify-content:space-between van de basisregel
       de tekst bovenin i.p.v. onderin - space-between met precies 1 item
       lijnt dat item uit op de MAIN-start, en in column-richting is dat
       de bovenkant. flex-end lost dat op, zodat de titel/datum net als op
       desktop onderin blijft staan, boven op de donkerste rand van
       .event-hero__scrim (leesbaarder tegen de achtergrondafbeelding). */
    .event-hero__content {
        align-items: stretch;
        justify-content: flex-end;
        flex-direction: column;
    }
}

/* Tickettype-keuze (hoofdstuk 4.5, collage "05 Ticketshop") - losse
   kaart-rijen i.p.v. een <table class="data-table">: die klasse wordt
   platformbreed hergebruikt in de admin-overzichten, dus hier een eigen
   set klassen i.p.v. daaraan te sleutelen. */
.ticket-select-heading {
    margin: 0 0 14px;
}

.ticket-select-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-bottom: 20px;
}

/* Groep tickettypes van 1 (sub)evenement (klantverzoek 2026-08-06) - het
   omlijnde --bordered-blok wordt alleen toegepast als er meerdere groepen
   op de pagina staan (EventStorefrontController::renderGroup(), bijv.
   een hoofdevenement met meerdere voorstellingsdagen): dan moet de
   bezoeker onmiskenbaar zien voor welke dag hij tickets kiest. Bij één
   los evenement (de meeste gevallen) blijft dit ongewijzigd - .event-group
   zonder modifier heeft geen eigen opmaak. */
.event-group--bordered {
    background: rgba(255, 255, 255, 0.6);
    border: 1px solid var(--border);
    border-radius: var(--radius-panel);
    padding: 18px 18px 4px;
    margin-bottom: 20px;
}

.event-group__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 14px;
}

.event-group__header h3 {
    margin: 0;
}

.event-group__date-badge {
    display: inline-flex;
    align-items: center;
    padding: 5px 14px;
    border-radius: var(--radius-pill);
    background: var(--brand);
    color: #ffffff;
    font-size: 0.82rem;
    font-weight: 700;
    white-space: nowrap;
}

.ticket-select-row {
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
    background: rgba(255, 255, 255, 0.82);
    border: 1px solid rgba(255, 255, 255, 0.9);
    border-radius: var(--radius-panel);
    box-shadow: var(--shadow-card);
    padding: 18px 22px;
}

.ticket-select-row__info {
    flex: 1;
    min-width: 160px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.ticket-select-row__name {
    font-size: 1.05rem;
    font-weight: 800;
}

.ticket-select-row__description {
    font-size: 0.88rem;
    color: var(--muted);
}

.ticket-select-row__price {
    font-size: 1.1rem;
    font-weight: 800;
    letter-spacing: -0.01em;
}

.ticket-select-row__action {
    flex-shrink: 0;
}

/* ---------------------------------------------------------------------
   Kaart - voor formulieren (inloggen, 2FA, instellingen)
   --------------------------------------------------------------------- */

.card {
    background: rgba(255, 255, 255, 0.82);
    border: 1px solid rgba(255, 255, 255, 0.9);
    border-radius: var(--radius-panel);
    box-shadow: var(--shadow-card);
    padding: 28px 24px;
    margin-bottom: 20px;
}

/* Ruimte na een kaart geldt ook als er geen tweede kaart op volgt (bijv.
   een los verzendknop-element direct na een kaart) - margin-bottom op de
   kaart zelf i.p.v. een aangrenzende-kaart-selector, zodat dit nooit
   per ongeluk 0 wordt. */
.card:last-child {
    margin-bottom: 0;
}

/* Meerdere kaarten van sterk uiteenlopende hoogte naast elkaar
   (bijv. het grote evenementgegevens-formulier naast de kleine
   Deurverkoop-kaart) - CSS-kolommen i.p.v. CSS Grid, want Grid rekt de
   hele rij op naar de hoogste kaart erin (lege witruimte naast korte
   kaarten); kolommen laten elke kaart z'n eigen hoogte houden en vullen
   vanzelf top-naar-onder, dan door naar de volgende kolom. */
.card-columns {
    column-gap: 20px;
    column-count: 1;
}

@media (min-width: 700px) {
    .card-columns {
        column-count: 2;
    }
}

@media (min-width: 900px) {
    .card-columns {
        column-count: 3;
    }
}

/* Klantdetailpagina (admin, klantbeslissing 2026-08-06): hier blijft het
   op 2 kolommen staan, ook op brede schermen - bij 3 kolommen viel de
   (nu langere) Facturatie-kaart uit de pas met de kortere kaarten
   ernaast. */
.card-columns--max2 {
    column-count: 1;
}

@media (min-width: 700px) {
    .card-columns--max2 {
        column-count: 2;
    }
}

.card-columns > .card {
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
    display: inline-block;
    width: 100%;
}

/* Bovenste rij op de Gegevens-tab (klantverzoek 2026-08-13): "Algemeen"
   moet altijd de eerste 2 kolommen beslaan en de subevenementenkaart
   altijd de 3e - dat kan de .card-columns-masonry hierboven niet
   garanderen (die vult puur op volgorde/hoogte), vandaar een aparte,
   echte CSS-grid. Op smalle schermen gewoon 1 kolom (allebei vol-breed
   onder elkaar), pas vanaf 900px de 3-koloms indeling. */
.gegevens-top-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 20px;
    margin-bottom: 20px;
}

@media (min-width: 900px) {
    .gegevens-top-grid {
        grid-template-columns: repeat(3, 1fr);
    }

    .gegevens-top-grid__algemeen {
        grid-column: span 2;
    }
}

/* Aanmaakformulier achter een knop i.p.v. altijd uitgeklapt (klantverzoek
   2026-08-13, tickettypes-tab) - bewust <details>/<summary> i.p.v. een
   los JS-toggle: zit binnen 1 tab-paneel-<div>, dus onaangeraakt door
   renderTabDockScript() (die bij elke tab-wissel `display` opnieuw zet
   op ALLE elementen met hetzelfde data-tab-panel - een los getoggeld
   2e element zou daardoor steeds terugklappen naar zichtbaar). */
.details-toggle summary.button {
    list-style: none;
    cursor: pointer;
    width: fit-content;
}

.details-toggle summary.button::-webkit-details-marker {
    display: none;
}

.details-toggle summary.button::marker {
    content: '';
}

.details-toggle__body {
    margin-top: 16px;
}

/* Factuurdetail (admin): vaste 1/3-2/3 verdeling i.p.v. de vloeiende
   card-columns-masonry - hier moet de samenvattingskaart altijd smal
   links staan en de specificatietabel breed rechts. */
.split-columns {
    display: grid;
    grid-template-columns: 1fr;
    gap: 20px;
    align-items: start;
}

@media (min-width: 900px) {
    .split-columns {
        grid-template-columns: 1fr 2fr;
    }
}

/* Bestellingspagina (klantverzoek 2026-08-09): 2 gelijke, gegarandeerd
   naast-elkaar-staande kaarten (deel-link + tips) - een echte grid i.p.v.
   .card-columns (CSS-multicolumn) hierboven, omdat "balance" bij 2
   ongelijk-hoge kaarten soms alles alsnog in 1 kolom laat vallen. */
.two-col-cards {
    display: grid;
    grid-template-columns: 1fr;
    gap: 20px;
    align-items: start;
}

@media (min-width: 700px) {
    .two-col-cards {
        grid-template-columns: 1fr 1fr;
    }
}

/* Tips-kaart op de bestellingspagina krijgt de merkkleur van de
   organisatie als achtergrond (klantverzoek 2026-08-09) - achtergrond +
   basistekstkleur staan al inline (Tenant::contrastTextColor() bepaalt
   wit/donker), maar de algemene "h1,h2,h3{color:var(--brand-dark)}"-
   regel elders in dit bestand wint anders alsnog van die overgeërfde
   kleur voor de kop - color:inherit hier laat 'm gewoon meebewegen. */
.entry-tips-card h2 {
    color: inherit;
}

/* Bestellingspagina (klantverzoek 2026-08-09): duidelijk meer ruimte
   boven de "Tickets"-subtitel dan de standaard kop-marge, zodat het
   eerste gedeelte (bevestiging + tips/deel-link) en de tickets-sectie
   zich echt als 2 losse onderdelen van de pagina lezen. */
.tickets-section-heading {
    margin-top: 40px;
}

/* Tickets in een echte grid i.p.v. .card-columns (CSS-multicolumn)
   hierboven - "balance" liet de kaarten bij ongelijke hoogtes soms
   allemaal in kolom 1 vallen, zichtbaar als "geen 3 kolommen op desktop"
   (klantmelding 2026-08-09). Zelfde breedte-drempels als .card-columns
   (700px/900px) voor visuele consistentie met de rest van het platform.
   .event-grid (hoofdticketshop van een klant, klantverzoek 2026-08-20)
   deelt dezelfde regels - zelfde "niet netjes uitgelijnd"-klacht.
   align-items: stretch (i.p.v. start, klantverzoek 2026-08-20) - de
   tickets in een bestelling (OrderStatusController::renderTicketRow())
   hebben ongelijke inhoud (wel/niet gepersonaliseerd, wel/geen
   bestaande deel-link erbij), waardoor de kaarten in dezelfde rij
   zonder stretch ongelijk hoog oogden. */
.ticket-grid,
.event-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 20px;
    align-items: stretch;
}

/* Bugfix (klantmelding 2026-08-20): .card:last-child { margin-bottom:0 }
   hierboven geldt maar voor de LAATSTE kaart van de hele grid (DOM-volgorde),
   niet per rij - bij bijv. 5 kaarten in 3 kolommen (rij van 3 + rij van 2)
   kreeg alleen kaart 5 geen margin-bottom, waardoor die zichtbaar hoger
   oogde dan kaart 4 ernaast, ondanks align-items:stretch (die maakt de
   GRID CELL gelijk, de marge erbinnen at nog steeds ongelijk zichtbare
   ruimte weg). .gap hierboven regelt de tussenruimte al, dus de
   kaart-marge kan hierbinnen gewoon altijd 0 zijn. */
.ticket-grid > .card,
.event-grid > .card {
    margin-bottom: 0;
}

@media (min-width: 700px) {
    .ticket-grid,
    .event-grid {
        grid-template-columns: 1fr 1fr;
    }
}

@media (min-width: 900px) {
    .ticket-grid,
    .event-grid {
        grid-template-columns: 1fr 1fr 1fr;
    }
}

/* Evenementkaart op de hoofdticketshop van een klant (klantverzoek
   2026-08-20) - de afbeelding bleedt tot de kaartranden (negatieve
   marge t.o.v. .card's eigen padding) en staat boven de titel. */
.event-card__image {
    display: block;
    width: calc(100% + 48px);
    aspect-ratio: 16 / 9;
    object-fit: cover;
    margin: -28px -24px 16px;
    border-radius: var(--radius-panel) var(--radius-panel) 0 0;
}

/* ---------------------------------------------------------------------
   Dashboard-hoofdscherm: stat-cards + aankomende-evenementenlijst
   (platform-restyling, dashboard-content-ronde)
   --------------------------------------------------------------------- */

.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 16px;
    margin-bottom: 20px;
}

.stat-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    background: rgba(255, 255, 255, 0.82);
    border: 1px solid rgba(255, 255, 255, 0.9);
    border-radius: var(--radius-panel);
    box-shadow: var(--shadow-card);
    padding: 20px 22px;
    text-decoration: none;
    color: inherit;
    transition: border-color 0.15s ease;
}

.stat-card:hover {
    border-color: var(--brand);
}

.stat-card__label {
    font-size: 0.78rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.stat-card__value {
    font-size: 1.8rem;
    font-weight: 800;
    color: var(--brand-dark);
    letter-spacing: -0.01em;
}

.stat-card__sub {
    font-size: 0.78rem;
    color: var(--muted);
}

.dashboard-event-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 0;
    border-bottom: 1px solid var(--border);
    text-decoration: none;
    color: inherit;
}

.dashboard-event-row:last-of-type {
    border-bottom: none;
}

.dashboard-event-row__name {
    font-weight: 700;
}

.dashboard-event-row:hover .dashboard-event-row__name {
    color: var(--brand);
}

.dashboard-event-row__date {
    font-size: 0.85rem;
    color: var(--muted);
    white-space: nowrap;
}

.quick-links {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

/* ---------------------------------------------------------------------
   Tabbladen - scan-app-inlogpagina (code vs. account, hoofdstuk 4.7)
   --------------------------------------------------------------------- */

.login-tabs {
    display: flex;
    gap: 8px;
    margin-bottom: 16px;
}

.login-tabs__tab {
    flex: 1;
    padding: 10px 14px;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--surface-alt);
    color: var(--ink);
    font-weight: 600;
    cursor: pointer;
}

.login-tabs__tab--active {
    background: var(--brand);
    color: #ffffff;
    border-color: var(--brand);
}

/* Sticky tabbladen-balk onderin de pagina (klantbeslissing 2026-08-05,
   View::renderTabDock()/renderTabDockScript(), gebruikt door o.a.
   InvoiceController::overview() en EventController::showDetail()) -
   verving het eerdere `.detail-tabs`-patroon (een rij bovenaan de
   pagina, zelfde opmaak als .login-tabs hierboven) volledig, ook op het
   evenement-detailscherm waar het oorspronkelijk voor gebouwd was.
   Zelfde donkere-pil-uiterlijk als de scan-app-tabbalk (.scan-tab-bar),
   maar bewust position:sticky i.p.v. position:fixed. Als gewoon block-element binnen
   .page "plakt" deze aan de onderkant van het scherm tijdens het scrollen
   door de tabinhoud, maar laat hij vanzelf los zodra .page's eigen
   onderkant (net boven de voettekst) in beeld komt - position:fixed zou
   daarentegen altijd over de voettekst heen blijven zweven. */
.tab-dock {
    position: sticky;
    bottom: 16px;
    z-index: 40;
    display: flex;
    justify-content: center;
    /* margin-top: auto (klantbeslissing 2026-08-05) - duwt de balk naar
       de onderkant van .page (nu display:flex/column, zie hierboven) op
       een korte pagina waar de inhoud het scherm niet vult; op een lange
       pagina heeft dit geen effect (geen "over" ruimte om te vullen) en
       doet position:sticky hierboven gewoon zijn werk tijdens het
       scrollen. */
    margin: auto 0 0;
    padding-top: 28px;
    pointer-events: none;
}

.tab-dock__bar {
    display: flex;
    gap: 4px;
    padding: 6px;
    border-radius: 999px;
    background: rgba(16, 16, 22, 0.92);
    box-shadow: 0 12px 30px rgba(16, 16, 40, 0.28);
    max-width: calc(100vw - 24px);
    overflow-x: auto;
    pointer-events: auto;
}

.tab-dock__item {
    display: flex;
    align-items: center;
    white-space: nowrap;
    padding: 10px 16px;
    border-radius: 999px;
    border: none;
    background: transparent;
    color: rgba(255, 255, 255, 0.66);
    font-family: inherit;
    font-weight: 600;
    font-size: 0.85rem;
    cursor: pointer;
}

.tab-dock__item--active {
    background: #ffffff;
    color: #101014;
}

/* ---------------------------------------------------------------------
   Formulieren
   --------------------------------------------------------------------- */

.field {
    margin-bottom: 18px;
}

.field label {
    display: block;
    font-weight: 600;
    font-size: 0.9rem;
    margin-bottom: 6px;
}

/* Los van een formulierveld (bijv. de dashboard-welkomsttekst, een
   SEPA-mandaatstatuszin) - dezelfde gedempte stijl, alleen niet
   genest onder .field. */
.hint {
    display: block;
    font-weight: 400;
    color: var(--muted);
    font-size: 0.82rem;
}

.field .hint {
    margin-top: 4px;
}

/* 2-koloms veldgrid (klantverzoek 2026-08-09, wizard-sub-stappen) - elke
   wizard-sub-stap wrapt zijn zichtbare velden hierin i.p.v. alles onder
   elkaar. Simpele velden (tekst/datum/select) nemen automatisch een halve
   rij in; brede blokken (textarea, afbeeldingsvoorbeelden, radiogroepen
   met een lange hint, de fee-payer-matrix) worden gewrapt in
   .field--full en nemen de volle breedte. renderEventForm() (bewerken/
   nieuw subevenement) gebruikt dit bewust niet - blijft op de bestaande
   enkele kolom. */
.field-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 0 24px;
}

.field--full {
    grid-column: 1 / -1;
}

/* Live voorbeeld bij een upload (headerafbeelding EN losse
   ticketafbeelding, klantverzoek 2026-08-06) - basisverhouding 16:9,
   generiek genoeg voor beide velden. De headerafbeelding-voorbeelden
   hieronder (desktop/mobiel) overschrijven dit met hun eigen,
   hero-accurate verhoudingen via de --hero/--mobile-varianten. */
.header-image-preview {
    width: 100%;
    aspect-ratio: 16 / 9;
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    margin-bottom: 12px;
}

.header-image-preview img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center center;
}

/* Headerafbeelding-voorbeelden (klantverzoek 2026-08-20) - zelfde 2
   beeldverhoudingen als de daadwerkelijke hero op de publieke
   evenementpagina (.event-hero hieronder, 3.5:1 op desktop, 4:3 op
   mobiel via de @media-regel), zodat het voorbeeld realistisch is. Het
   mobiele voorbeeld staat smaller weergegeven zodat het ook visueel als
   "mobiel" oogt naast het brede desktop-voorbeeld erboven. */
.header-image-preview--hero {
    aspect-ratio: 3.5 / 1;
}

.header-image-preview--mobile {
    aspect-ratio: 4 / 3;
    max-width: 260px;
}

/* "Passend"-stand (klantverzoek 2026-08-20): de volledige afbeelding
   blijft zichtbaar i.p.v. bijgesneden te worden - letterboxen op de
   merkkleur van de klant (of een per-evenement afwijkende kleur via
   --hero-bg, zie renderHeaderImagePreviewScript()), zodat dit voorbeeld
   overeenkomt met de daadwerkelijke hero (.event-hero--desktop-contain/
   --mobile-contain hierboven). */
.header-image-preview--contain {
    background: var(--hero-bg, var(--brand));
}

.header-image-preview--contain img {
    object-fit: contain;
}

/* Plattegrond op de Praktisch-kaart (klantverzoek 2026-08-06,
   EventStorefrontController::renderPracticalInfoCard()/TicketViewController::
   renderPracticalInfoSection()) - bewust GEEN object-fit:cover zoals
   .header-image-preview hierboven: een plattegrond mag nooit bijgesneden
   worden, dus altijd de volledige afbeelding tonen op zijn eigen
   hoogte/breedte-verhouding (Uploads::storeMapImage() schaalt al naar
   binnen i.p.v. te croppen). */
.practical-info-map {
    margin-top: 12px;
}

.practical-info-map img {
    display: block;
    max-width: 100%;
    height: auto;
    border-radius: var(--radius);
    border: 1px solid var(--border);
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
select,
textarea {
    display: block;
    width: 100%;
    margin-top: 6px;
    padding: 10px 12px;
    font-size: 1rem;
    font-family: inherit;
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

textarea {
    resize: vertical;
}

input[type="text"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
input[type="number"]:focus,
select:focus,
textarea:focus {
    outline: none;
    border-color: var(--brand);
    box-shadow: 0 0 0 3px var(--brand-light);
}

/* align-items: flex-start i.p.v. center - bij een lange labeltekst die
   op meerdere regels afbreekt (bijv. de voorwaarden/privacy-checkbox op
   mobiel) drukte center het vinkje verticaal midden tussen alle regels.
   flex-start lijnt het vinkje netjes uit met de EERSTE regel; voor de
   vele kortere, 1-regelige labels elders (checkbox-instellingen) is het
   verschil met center visueel verwaarloosbaar.
   De eigenlijke oorzaak van het "in elkaar gedrukte" uiterlijk
   (klantverzoek 2026-08-09) zat in de HTML, niet in deze regel: met
   display:flex hier wordt ELK direct kind van <label> een eigen flex-
   item. Bij platte tekst met inline links (renderDetailsStep(), "Ik ga
   akkoord met de <a>algemene voorwaarden</a> en de <a>privacy-
   verklaring</a>") werd daardoor niet alleen het vinkje, maar ook ELK
   tekstfragment/losse link een eigen flex-item - die braken dan
   onafhankelijk van elkaar af i.p.v. gewoon als lopende tekst mee te
   wrappen, en de links leken zo in hun "eigen kolom" te staan. De fix
   staat in de HTML: de hele zin (incl. links) in één <span> na het
   vinkje, zodat <label> maar 2 flex-items heeft (vinkje + span) en de
   tekst BINNEN die span gewoon normaal, aaneengesloten kan doorlopen. */
.checkbox-field label {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    font-weight: 400;
    font-size: 0.92rem;
    line-height: 1.5;
}

.checkbox-field input {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    margin-top: 3px;
}

/* ---------------------------------------------------------------------
   Aantal-stepper (hoofdstuk 4.5, ticketkeuze) - +/- knoppen naast het
   aantalveld, progressive enhancement bovenop het gewone number-input
   (dat blijft ook zonder JavaScript gewoon werken).
   --------------------------------------------------------------------- */

.qty-stepper {
    display: flex;
    align-items: center;
    gap: 6px;
}

.qty-stepper input[type="number"] {
    width: 56px;
    flex: none;
    margin: 0;
    padding: 8px 4px;
    text-align: center;
    -moz-appearance: textfield;
}

.qty-stepper input[type="number"]::-webkit-outer-spin-button,
.qty-stepper input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.qty-stepper__btn {
    flex: none;
    width: 32px;
    height: 32px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-pill, 999px);
    border: 1px solid var(--border);
    background: var(--surface-alt);
    color: var(--ink);
    font-size: 1.15rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
}

.qty-stepper__btn:hover {
    background: var(--brand-light);
}

/* Plus-knop gevuld met de merkkleur i.p.v. dezelfde outline als de
   min-knop (collage) - data-step bestaat al op elke qty-stepper-knop
   in de hele app (checkout, wachtlijst, deurverkoop), dus dit werkt
   overal zonder losse markup-wijziging. */
.qty-stepper__btn[data-step="1"] {
    background: var(--brand);
    border-color: var(--brand);
    color: #ffffff;
}

.qty-stepper__btn[data-step="1"]:hover {
    filter: brightness(0.92);
}

.qty-stepper__btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* ---------------------------------------------------------------------
   Stap-indicator (hoofdstuk 4.5, bestelproces) - toont waar de koper
   zich in het bestelproces bevindt.
   --------------------------------------------------------------------- */

.step-indicator {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
    gap: 8px;
    margin-bottom: 24px;
    font-size: 0.82rem;
}

.step-indicator__item {
    display: block;
    width: 100%;
    padding: 8px 6px;
    margin: 0;
    border-radius: var(--radius);
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--muted);
    font: inherit;
    font-weight: 600;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.step-indicator__item--done {
    background: var(--brand-light);
    border-color: var(--brand-light);
    color: var(--brand-dark);
}

.step-indicator__item--active {
    background: var(--brand);
    border-color: var(--brand);
    color: #ffffff;
}

/* De "done" stap-badges zijn soms een <button> in een eigen <form>
   (renderStepNavForm()) om er rechtstreeks naartoe terug te kunnen
   springen - display:contents op het formulier zorgt dat de knop zelf
   het grid-item blijft, zodat de gelijke kolombreedte van
   .step-indicator intact blijft. */
.step-indicator__form {
    display: contents;
}

button.step-indicator__item {
    cursor: pointer;
}

button.step-indicator__item--done:hover {
    background: var(--brand);
    border-color: var(--brand);
    color: #ffffff;
}

/* .step-indicator__num (cirkel met alleen het stapnummer) staat verder
   altijd in de HTML mee (renderStepIndicator()), maar blijft hier
   verborgen - pas in de mobiele media query hieronder wordt hij zichtbaar
   voor niet-actieve stappen. */
.step-indicator__num {
    display: none;
}

/* Compacte stap-indicator op mobiel (klantverzoek 2026-08-07): i.p.v.
   alle stappen voluit naast elkaar (te veel tekst op een smal scherm)
   toont alleen de actieve stap zijn volledige label; de overige stappen
   worden kleine genummerde cirkels - gevuld met de merkkleur zodra ze al
   afgerond zijn, zodat in één oogopslag duidelijk is hoever de koper is. */
@media (max-width: 640px) {
    .step-indicator {
        display: flex;
        align-items: center;
        gap: 8px;
    }

    .step-indicator__item {
        flex: 0 0 auto;
        width: 32px;
        height: 32px;
        padding: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 50%;
    }

    .step-indicator__label {
        display: none;
    }

    .step-indicator__num {
        display: block;
    }

    .step-indicator__item--active {
        flex: 1 1 auto;
        width: auto;
        height: auto;
        padding: 8px 10px;
        border-radius: var(--radius);
    }

    .step-indicator__item--active .step-indicator__num {
        display: none;
    }

    .step-indicator__item--active .step-indicator__label {
        display: block;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
}

/* ---------------------------------------------------------------------
   Wizard-type-tegels (evenement-aanmaakwizard, stap 0) - kies-tegels
   per evenementsoort (EventController::renderWizardTypePicker()).
   --------------------------------------------------------------------- */

.wizard-type-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 16px;
    margin-bottom: 20px;
}

.wizard-type-tile {
    display: block;
    background: rgba(255, 255, 255, 0.82);
    border: 1px solid rgba(255, 255, 255, 0.9);
    border-radius: var(--radius-panel);
    box-shadow: var(--shadow-card);
    padding: 24px 20px;
    text-decoration: none;
    color: inherit;
    transition: border-color 0.15s ease, transform 0.15s ease;
}

.wizard-type-tile:hover {
    border-color: var(--brand);
    transform: translateY(-2px);
}

.wizard-type-tile h3 {
    margin: 0;
    color: var(--brand-dark);
}

.checkout-countdown {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 8px 14px;
    margin-bottom: 16px;
    border-radius: var(--radius);
    background: var(--brand-light);
    color: var(--brand-dark);
    font-size: 0.85rem;
}

.checkout-countdown__label {
    font-weight: 500;
}

.checkout-countdown__time {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------
   Betaalmethode kiezen (hoofdstuk 4.9) - elke optie toont meteen de
   totaalprijs mét eventuele transactiekosten voor die methode.
   --------------------------------------------------------------------- */

.payment-method-option {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px;
    margin-bottom: 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    cursor: pointer;
}

.payment-method-option:has(input:checked) {
    border-color: var(--brand);
    background: var(--brand-light);
}

.payment-method-option__name {
    flex: 1;
    font-weight: 600;
}

.payment-method-option__total {
    font-weight: 700;
    white-space: nowrap;
}

/* Kleine "?"-badge met een CSS-only tooltip-bubble (klantverzoek
   2026-08-20, "Tickets op naam zetten"-keuze) - geen JS-library nodig,
   :hover/:focus-within volstaan. tabindex="0" op de badge zelf houdt 'm
   ook met toetsenbord/touch bereikbaar (tikken = focus = bubble toont). */
.info-tooltip {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    margin-left: 6px;
    border-radius: 50%;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    color: var(--muted);
    font-size: 0.75rem;
    font-weight: 700;
    cursor: help;
    flex-shrink: 0;
}

.info-tooltip__bubble {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    width: 240px;
    padding: 10px 12px;
    background: var(--ink);
    color: #ffffff;
    font-size: 0.8rem;
    font-weight: 400;
    line-height: 1.4;
    text-align: left;
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s ease;
    z-index: 20;
    pointer-events: none;
}

.info-tooltip:hover .info-tooltip__bubble,
.info-tooltip:focus .info-tooltip__bubble,
.info-tooltip:focus-within .info-tooltip__bubble {
    opacity: 1;
    visibility: visible;
}

/* ---------------------------------------------------------------------
   Bestelling-samenvatting tijdens het bestelproces (hoofdstuk 4.5) - op
   desktop een vaste (sticky) kaart naast het formulier, op mobiel
   ingeklapt achter een knop die een popup opent (dezelfde bottom-sheet-
   aanpak als .scan-modal, maar losstaand: deze pagina's laden niet via
   het scan-app-JS).
   --------------------------------------------------------------------- */

.checkout-layout {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.checkout-layout__main {
    min-width: 0;
}

.checkout-layout__sidebar {
    display: none;
}

.checkout-summary-row {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 6px 0;
    font-size: 0.92rem;
}

.checkout-summary-row--total {
    margin-top: 6px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
    font-weight: 700;
    align-items: baseline;
}

.checkout-summary-row--total .js-summary-total {
    font-size: 1.5rem;
    font-weight: 800;
    letter-spacing: -0.02em;
}

/* Sticky onderin het scherm op mobiel (i.p.v. gewoon in de tekstflow),
   zodat de knop altijd bereikbaar is tijdens het scrollen door de
   vervolgstappen - main.page:has(...) geeft de pagina evenveel
   onderruimte terug zodat de vaste knop geen inhoud verbergt. */
.checkout-summary-toggle {
    display: block;
    width: 100%;
    margin: 0;
    padding: 14px 16px calc(14px + env(safe-area-inset-bottom));
    border-radius: 0;
    border: 1px solid var(--border);
    border-left: none;
    border-right: none;
    border-bottom: none;
    background: var(--surface);
    color: var(--ink);
    font-weight: 600;
    font-size: 0.95rem;
    cursor: pointer;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 80;
    box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.08);
}

main.page:has(.checkout-summary-toggle) {
    padding-bottom: calc(70px + env(safe-area-inset-bottom));
}

/* "Terug" en de knop voor de volgende stap horen samen in één kaart
   (stap 1 t/m 4) - het zijn twee losse <form>-elementen (kunnen niet in
   elkaar genest worden), maar de knoppen staan hier via het HTML5
   form="..."-attribuut toch samen. margin-left: auto op de primaire
   knop houdt hem rechts uitgelijnd, ook als de terug-knop een keer
   ontbreekt.
   Sticky onderin is alleen voor desktop (klantverzoek 2026-08-08: "op
   mobiel hoeft het sticky gedeelte niet") - zie de
   @media (min-width:900px)-regel hieronder. Op mobiel dus gewoon
   normale paginaflow. */
.checkout-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    /* De kaart hierboven staat in een eigen <form> en is daarbinnen
       .card:last-child, dus verliest zijn margin-bottom (bedoeld voor
       een kaart zonder iets erna) - maar hier volgt deze balk wél nog,
       dus altijd een eigen marge boven i.p.v. op de vorige kaart te
       vertrouwen. */
    margin-top: 20px;
}

.checkout-actions .button--primary {
    margin-left: auto;
}

/* Op mobiel valt de admin-instelbare stapnaam weg (klantverzoek
   2026-08-08: "← Terug naar Personaliseren" e.d. duwde de twee knoppen
   onder elkaar) - alleen "← Terug"/"Volgende" blijft over
   (EventStorefrontController::renderStepActionLabel()). Vanaf 640px is
   er weer genoeg breedte voor de volledige tekst. */
@media (max-width: 640px) {
    .checkout-actions__step-name {
        display: none;
    }
}

/* Laadindicator op de stap-navigatieknoppen (klantverzoek 2026-08-08) -
   EventStorefrontController::renderCheckoutLoadingScript() zet deze
   klasse + spinner-span erin zodra de koper op "Volgende"/"← Terug"/
   "Bestellen & betalen" klikt, zodat duidelijk is dat de pagina aan het
   laden is terwijl de browser naar de volgende stap (of Mollie)
   navigeert. Hergebruikt @keyframes scan-spin (hierboven, deurverkoop-
   QR-preloader) i.p.v. een eigen animatie te definiëren. */
.checkout-actions button.is-loading {
    opacity: 0.85;
    cursor: default;
}

.checkout-actions__spinner {
    display: inline-block;
    width: 16px;
    height: 16px;
    margin-right: 8px;
    vertical-align: -3px;
    border: 2px solid rgba(255, 255, 255, 0.45);
    border-top-color: #ffffff;
    border-radius: 50%;
    animation: scan-spin 0.8s linear infinite;
}

/* .button--secondary (de "← Terug"-knop) heeft een lichte achtergrond -
   een witte spinner zou daar bijna onzichtbaar zijn, dus hier de
   omgekeerde kleuren (donkere baan, merkkleur als draaiende rand). */
.checkout-actions .button--secondary.is-loading .checkout-actions__spinner {
    border-color: rgba(0, 0, 0, 0.15);
    border-top-color: var(--brand, #5d5dff);
}

.checkout-summary-modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 900;
}

.checkout-summary-modal--open {
    display: block;
}

.checkout-summary-modal__backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
}

.checkout-summary-modal__panel {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    max-height: 80vh;
    overflow-y: auto;
    background: var(--surface);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    padding: 20px;
    box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.25);
}

.checkout-summary-modal__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

.checkout-summary-modal__header h2 {
    margin: 0;
}

.checkout-summary-modal__close {
    width: 32px;
    height: 32px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--surface-alt);
    color: var(--ink);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
}

@media (min-width: 900px) {
    .checkout-layout {
        flex-direction: row;
        align-items: flex-start;
    }

    .checkout-layout__main {
        flex: 1 1 0;
    }

    .checkout-layout__sidebar {
        display: block;
        flex: 0 0 320px;
        position: sticky;
        top: 24px;
    }

    .checkout-summary-toggle {
        display: none;
    }

    /* Sticky onderin is bewust ALLEEN voor desktop (klantverzoek
       2026-08-08: "op mobiel hoeft het sticky gedeelte niet") - blijft
       in beeld terwijl de koper door een lange ticketlijst/
       personalisatieformulier scrolt, en "laat los" zodra de kaart
       z'n eigen plek in de paginaflow bereikt (standaardgedrag van
       position:sticky, geen JS nodig). Geen mobiele
       .checkout-summary-toggle-balk om rekening mee te houden op deze
       breedte (hierboven al verborgen), dus gewoon tegen de onderkant. */
    .checkout-actions {
        position: sticky;
        bottom: 0;
        z-index: 40;
    }
}

/* ---------------------------------------------------------------------
   Knoppen
   --------------------------------------------------------------------- */

.button {
    display: inline-block;
    font-family: inherit;
    font-size: 0.95rem;
    font-weight: 700;
    padding: 11px 22px;
    border-radius: var(--radius-pill);
    border: 1px solid transparent;
    cursor: pointer;
    text-decoration: none;
    transition: transform 0.05s ease, background 0.15s ease;
}

.button:active {
    transform: scale(0.98);
}

.button--primary {
    background: var(--brand);
    color: var(--surface);
}

.button--primary:hover {
    background: #4747d1;
}

.button--secondary {
    background: transparent;
    color: var(--ink);
    border-color: var(--border);
}

.button--secondary:hover {
    background: var(--surface-alt);
}

/* Evenementkeuze in de scan-app (klantverzoek 2026-08-10): titel en
   datum/tijd onder elkaar i.p.v. op 1 regel achter een streepje, titel
   groter zodat die als eerste opvalt in de lijst. */
.event-picker-row {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
    text-align: left;
    padding-top: 14px;
    padding-bottom: 14px;
}

.event-picker-row__name {
    font-size: 1.1rem;
    font-weight: 700;
}

.event-picker-row__date {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--muted);
}

/* Pil-vormig statuslabel (bijv. "Betaald"/"Open"/"Actief" in tabellen) -
   zie de collage: kleine ronde badges i.p.v. platte tekst. Nieuw
   onderdeel van het fundament; de tabellen zelf worden in een latere
   ronde omgezet om deze klasse te gebruiken. */
.badge {
    display: inline-flex;
    align-items: center;
    padding: 5px 12px;
    border-radius: var(--radius-pill);
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.badge--success {
    background: var(--success-bg);
    color: var(--success);
}

.badge--danger {
    background: var(--danger-bg);
    color: var(--danger);
}

.badge--accent {
    background: var(--accent);
    color: var(--accent-ink);
}

.badge--neutral {
    background: var(--surface-alt);
    color: var(--muted);
}

/* Zichtbare focusring voor toetsenbordgebruik, ook op knoppen/links */
a:focus-visible,
.button:focus-visible,
input:focus-visible {
    outline: 3px solid var(--brand-light);
    outline-offset: 2px;
}

/* ---------------------------------------------------------------------
   Meldingen
   --------------------------------------------------------------------- */

.alert {
    padding: 12px 16px;
    border-radius: var(--radius);
    font-size: 0.92rem;
    margin-bottom: 20px;
}

.alert--danger {
    background: var(--danger-bg);
    color: var(--danger);
}

.alert--success {
    background: var(--success-bg);
    color: var(--success);
}

/* Neutrale, informatieve melding (bijv. TenantLegalController: duidelijk
   maken dat een getoonde tekst van het platform komt, niet van de
   organisatie zelf, klantbeslissing 2026-08-05) - geen foutmelding, dus
   bewust niet .alert--danger/--success. */
.alert--info {
    background: var(--brand-light);
    color: var(--brand-dark);
}

/* Openstaand betaalverzoek bij een ticketomzetting (klantverzoek
   2026-08-17: "de melding niet duidelijk... kader omheen zodat het
   opvalt") - bovenop de gewone .alert--info-styling een duidelijk
   zichtbaar kader, zodat dit niet tussen de rest van de kaart verdwijnt.
   Alleen een extra klasse i.c.m. .alert--info, geen wijziging aan die
   basisklasse zelf (die wordt ook elders al gebruikt). */
.pending-conversion-notice {
    border: 2px solid var(--brand);
}

.pending-conversion-notice p {
    margin: 4px 0;
}

.pending-conversion-notice p:first-child {
    margin-top: 0;
}

.pending-conversion-notice p:last-child {
    margin-bottom: 0;
}

/* ---------------------------------------------------------------------
   Tabellen (klanten-/gebruikersoverzichten in de adminomgeving)
   --------------------------------------------------------------------- */

.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.92rem;
}

.data-table th,
.data-table td {
    text-align: left;
    padding: 10px 12px;
    border-bottom: 1px solid var(--border);
}

.data-table th {
    color: var(--muted);
    font-weight: 600;
    font-size: 0.82rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.data-table tbody tr:last-child td {
    border-bottom: none;
}

.data-table .button {
    padding: 6px 12px;
    font-size: 0.85rem;
}

/* Groepskop per (sub)evenement in de tickettabel van een bestelling
   (klantverzoek 2026-08-21) - alleen aanwezig als de bestelling tickets
   voor meerdere evenementen bevat (OrderListController::renderOrderDetail()),
   dus bewust geen aparte toggle/instelling nodig. */
.data-table__group-row td {
    padding-top: 18px;
    font-weight: 700;
    color: var(--brand-dark);
    border-bottom: 2px solid var(--border);
}

.data-table__group-row:first-child td {
    padding-top: 10px;
}

/* Naam/e-mail per ticket pas bewerkbaar na een potlood-icoon (klantverzoek
   2026-08-20, OrderListController::renderOrderDetail()) - i.p.v. standaard
   een open tekstveld. Herzien dezelfde dag: geen potlood meer per cel (dat
   maakte bulk-bewerken onmogelijk), maar 1 potlood boven de tabel
   (.js-bulk-edit-toggle) dat alle rijen tegelijk omzet, plus een losse
   "Naam/e-mail bewerken"-actie per rij in het "⋮"-menu hieronder.
   .editable-field__input krijgt verder geen eigen opmaak (het generieke
   input[type="text"] hierboven in dit bestand geldt al), alleen de kleine
   editknop. */
.editable-field {
    white-space: nowrap;
}

.editable-field__edit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    margin-left: 6px;
    padding: 0;
    border: none;
    background: none;
    color: var(--muted);
    font-size: 0.85rem;
    cursor: pointer;
    border-radius: var(--radius);
}

.editable-field__edit:hover {
    background: var(--surface-alt);
    color: var(--ink);
}

.editable-field__input {
    display: inline-block;
    width: auto;
    min-width: 140px;
    margin-top: 0;
}

/* "⋮"-acties-menu per ticket (klantverzoek 2026-08-20: "Bekijken" +
   "Omzetten" + "Opnieuw versturen" achter 1 knop i.p.v. steeds meer
   losse links). <details>/<summary> voor de open/dicht-staat, maar de
   lijst zelf wordt position:fixed en krijgt zijn top/right via JS
   (rowMenuScript() in OrderListController) - klantfeedback 2026-08-20
   dat de eerdere in-place uitklap de pagina liet "verspringen". Fixed
   i.p.v. absolute ontsnapt ook meteen aan de overflow-x:auto hieronder
   op .card:has(table.data-table), die in de meeste browsers impliciet
   ook verticaal clipt. */
.row-menu__toggle {
    cursor: pointer;
    list-style: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--muted);
    font-weight: 700;
}

.row-menu__toggle::-webkit-details-marker {
    display: none;
}

.row-menu__toggle:hover {
    background: var(--surface-alt);
    color: var(--ink);
}

.row-menu__list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 6px;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
    min-width: 160px;
    /* Altijd position:fixed, niet pas via JS bij het openen (klantmelding
       2026-08-21: de popup "verspringt" nog steeds) - <details>'s native
       toggle-event vuurt VOLGENS SPEC asynchroon (een aparte taak, niet
       synchroon binnen dezelfde klik), dus de browser tekende de net
       geopende <details> eerst nog kort IN-FLOW (de tabelrij duwde open,
       vandaar de sprong) voordat rowMenuScript() 'm alsnog naar fixed
       omzette. Nu al standaard fixed, dus nooit meer in-flow, ongeacht
       wanneer die JS draait - rowMenuScript() (OrderListController)
       positioneert 'm nu bovendien synchroon in de click-handler zelf,
       vóór de browser de <details> daadwerkelijk opent. */
    position: fixed;
    z-index: 50;
}

.row-menu__list a,
.row-menu__button {
    display: block;
    width: 100%;
    text-align: left;
    padding: 6px 8px;
    border: none;
    border-radius: var(--radius);
    background: none;
    font: inherit;
    font-size: 0.88rem;
    color: var(--ink);
    cursor: pointer;
}

.row-menu__list a:hover,
.row-menu__button:hover {
    background: var(--surface);
}

/* Generieke mobiele redding voor ALLE .data-table's (klantmelding
   2026-08-14: "de weergave van o.a. bestellingen niet geschikt voor op
   mobiele telefoons") - een brede tabel scrollt horizontaal binnen zijn
   eigen kaart i.p.v. de hele pagina breder te maken. Descendant-selector
   (geen `>`) omdat sommige tabellen dieper genest zitten dan direct in
   .card. Nul HTML-wijzigingen nodig, werkt voor alle bestaande tabellen. */
.card:has(table.data-table) {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Bestellingenoverzicht op mobiel: kaartjes i.p.v. een (horizontaal
   scrollende) tabel (klantverzoek 2026-08-14, "app-ervaring") - bekende
   CSS-only responsive-table-truc: thead verbergen, elke rij een eigen
   afgeronde kaart, elke cel een label/waarde-regel via het data-label-
   attribuut (OrderListController::renderOrderRow()). Alleen op de
   .data-table--stack-tabellen, niet op alle 33 .data-table-gebruiken -
   dat zou een veel grotere, ongeteste HTML-operatie zijn geweest voor
   pagina's die niemand als probleem heeft genoemd. */
@media (max-width: 640px) {
    .data-table--stack {
        border: none;
    }

    .data-table--stack thead {
        display: none;
    }

    .data-table--stack tbody {
        display: block;
    }

    .data-table--stack tr {
        display: block;
        border: 1px solid var(--border);
        border-radius: var(--radius);
        margin-bottom: 12px;
        padding: 4px 12px;
    }

    .data-table--stack tr:last-child {
        margin-bottom: 0;
    }

    .data-table--stack td {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        text-align: right;
        padding: 8px 0;
    }

    .data-table--stack td:not(:last-child) {
        border-bottom: 1px solid var(--border);
    }

    .data-table--stack td[data-label=""] {
        justify-content: flex-end;
    }

    .data-table--stack td::before {
        content: attr(data-label);
        font-weight: 600;
        color: var(--muted);
        font-size: 0.82rem;
        text-align: left;
        flex: none;
    }

    .data-table--stack td[data-label=""]::before {
        content: none;
    }
}

/* Wachtlijst-aanmeldformulier bij een uitverkocht tickettype (hoofdstuk
   4.2, EventStorefrontController::renderWaitlistModal()) - een gedeelde
   modal (net als .scan-modal: mobiel beeldvullend, vanaf 640px een
   gecentreerde kaart) i.p.v. een inline rij, want een <form> nesten
   binnen de buitenste #quantities-form is ongeldige HTML. */
.waitlist-modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 950;
}

.waitlist-modal--open {
    display: block;
}

.waitlist-modal__backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
}

.waitlist-modal__panel {
    position: fixed;
    inset: 0;
    overflow-y: auto;
    background: var(--surface);
    padding: 20px;
}

.waitlist-modal__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

.waitlist-modal__header h2 {
    margin: 0;
}

.waitlist-modal__close {
    width: 32px;
    height: 32px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--surface-alt);
    color: var(--ink);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
}

@media (min-width: 640px) {
    .waitlist-modal__panel {
        inset: auto;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 90%;
        max-width: 420px;
        max-height: 85vh;
        border-radius: var(--radius-lg);
        box-shadow: 0 10px 40px rgba(0, 0, 0, 0.25);
    }
}


/* ---------------------------------------------------------------------
   Ticketkaart (hoofdstuk 4.13) - mobiele kaart op scherm, A4-achtig bij
   het printen (@media print hieronder). Volgt op scherm de systeem-
   instelling licht/donker; bij printen altijd een vaste lichte
   achtergrond (voorkomt onnodig inktverbruik en blijft leesbaar).
   --------------------------------------------------------------------- */

.ticket-page {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 16px;
    align-items: start;
}

.ticket-page__nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 16px;
}

/* Alleen op mobiel relevant - op tablet/desktop staan de tickets toch al
   naast elkaar in de grid hierboven, dus is de losse knop overbodig. */
.tickets-link--mobile-only {
    display: none;
}

@media (max-width: 640px) {
    .tickets-link--mobile-only {
        display: block;
    }
}

/* Krijgt de merkkleur van de organisatie (button--primary, klantbeslissing
   2026-08-06 - stond eerst op button--secondary) + gecentreerde tekst: de
   knoptekst is lang genoeg om op de meeste telefoons over 2 regels te
   lopen, en .button is display:inline-block (dus standaard links
   uitgelijnde tekst) i.p.v. het automatisch centrerende gedrag van een
   volle-breedte block-knop. */
.tickets-link__button {
    display: block;
    text-align: center;
}

.ticket-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    overflow: hidden;
}

/* Eigen merkkleur van de organisatie (var(--brand), per-tenant
   overschreven in View.php) i.p.v. een vaste kleur - matcht de
   verloopkleur-ticketkaart uit de collage (klantbeslissing 2026-08-05).
   color-mix() i.p.v. een tweede vaste kleur, zodat het verloop met de
   merkkleur meebeweegt in plaats van een eigen losstaande tint te zijn. */
.ticket-card__header {
    background: linear-gradient(135deg, var(--brand) 0%, color-mix(in srgb, var(--brand) 65%, #140b2e) 100%);
    color: #ffffff;
    padding: 28px 26px 24px;
}

.ticket-card__location {
    margin: 0 0 4px;
    opacity: 0.85;
    font-size: 0.9rem;
}

/* Datum/Aanvang + Type/Order als 2x2-rooster (collage) i.p.v. losse
   regels onder elkaar. */
.ticket-card__info-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px 20px;
    margin: 18px 0 0;
}

.ticket-card__info-label {
    display: block;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    opacity: 0.7;
    margin-bottom: 3px;
}

.ticket-card__info-value {
    display: block;
    font-weight: 700;
    font-size: 1.02rem;
}

/* Tickettype krijgt de volle breedte i.p.v. een halve cel (klantbeslissing
   2026-08-05, na het weghalen van de Order-cel ernaast) - meer ruimte
   voor een langere tickettypenaam. */
.ticket-card__info-item--wide {
    grid-column: 1 / -1;
}

.ticket-card__eyebrow {
    margin: 0 0 8px;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.7);
}

.ticket-card__header h2 {
    color: #ffffff;
    margin: 0 0 6px;
}

.ticket-card__header p {
    margin: 0 0 2px;
    opacity: 0.85;
}

.ticket-card__holder {
    margin: 16px 0 0 !important;
    padding-top: 14px;
    border-top: 1px solid rgba(255, 255, 255, 0.25);
    font-size: 0.88rem;
    opacity: 0.85 !important;
}

/* Geperforeerde scheidingslijn tussen de gekleurde kop en het
   QR-gedeelte - visuele "ticketstub"-associatie (collage), puur
   decoratief. De twee "inkepingen" zijn cirkels die half buiten de
   kaart vallen; .ticket-card's overflow:hidden knipt ze af tot een
   halve maan, alsof er een rond gaatje in de kaartrand geponst is. */
.ticket-card__divider {
    position: relative;
    height: 0;
    border-top: 2px dashed var(--border);
}

.ticket-card__notch {
    position: absolute;
    top: -12px;
    width: 24px;
    height: 24px;
    border-radius: 999px;
    background: var(--surface);
}

.ticket-card__notch--left {
    left: -12px;
}

.ticket-card__notch--right {
    right: -12px;
}

.ticket-card__body {
    padding: 24px;
    text-align: center;
}

.ticket-card__qr-panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-panel, 16px);
    box-shadow: var(--shadow-card);
    padding: 20px;
    margin: 0 0 16px;
}

/* Volle-breedte donkere pil, zelfde gewicht als de actieknop onderin
   de e-ticket-kaart uit de collage - blijft gewoon de bestaande
   "print"-actie, geen Wallet-koppeling (die bestaat niet). */
.ticket-card__print-btn {
    display: block;
    width: 100%;
    box-sizing: border-box;
    background: var(--ink);
    color: #ffffff;
    border-color: var(--ink);
}

.ticket-card__print-btn:hover {
    filter: brightness(1.15);
}

.ticket-card__qr {
    width: 200px;
    margin: 12px auto;
}

.ticket-card__qr svg,
.ticket-stub__qr svg {
    display: block;
    width: 100%;
    height: auto;
}

.ticket-card__code {
    font-family: var(--font-mono);
    font-size: 1.05rem;
    letter-spacing: 0.04em;
}

.ticket-card__practical {
    padding: 20px 24px;
    border-top: 1px solid var(--border);
    text-align: left;
}

.ticket-stub {
    display: none;
}

/* Liggend formaat op tablet/desktop (klantbeslissing 2026-08-06): de
   staande kaart (kop bovenop QR-gedeelte) werd op een breed scherm zo
   hoog dat 'ie buiten beeld viel, terwijl er ruimschoots breedte
   overbleef. Kop en QR-gedeelte komen hier naast elkaar te staan i.p.v.
   onder elkaar - de kaart wordt daardoor veel korter. .ticket-page's
   kolombreedte gaat mee omhoog zodat er op een breed scherm 2 van deze
   liggende kaarten naast elkaar passen i.p.v. 3-4 smalle staande. De
   optionele "Jouw gegevens"/"Praktische informatie"-blokken (variabele,
   soms lange tekst) blijven bewust wél de volle breedte onder de
   kop/QR-rij innemen (flex-basis:100%) i.p.v. mee in de zijkolom te
   gaan - daar is geen ruimte voor doorlopende tekst. Alleen op scherm;
   @media print hierboven regelt zijn eigen (compacte) A4-opmaak. */
@media (min-width: 620px) {
    .ticket-page {
        grid-template-columns: repeat(auto-fill, minmax(660px, 1fr));
    }

    .ticket-card {
        display: flex;
        flex-wrap: wrap;
        align-items: stretch;
        /* Bij precies 1 ticket (bijv. de losse "ticket bekijken"-pagina,
           klantbeslissing 2026-08-06) maakt auto-fill hierboven van de
           enige kolom een volle-breedte 1fr-track - zonder deze grens
           zou de liggende kaart dan over de hele paginabreedte
           uitrekken. Bij 2+ tickets blijft de 2-koloms-indeling gewoon
           werken, dit begrenst alleen de kaart zelf, niet het rooster. */
        max-width: 760px;
    }

    .ticket-card__header {
        flex: 1 1 320px;
        display: flex;
        flex-direction: column;
        justify-content: center;
    }

    /* Was een horizontale streep met inkepingen links/rechts aan de
       kaartrand - wordt nu een verticale streep tussen kop en QR. De
       inkepingen zaten qua "geponst gaatje"-effect specifiek op de
       buitenrand van de kaart (zie .ticket-card__notch hierboven); in
       het midden van een liggende kaart heeft dat geen betekenis meer,
       dus die verbergen we hier gewoon i.p.v. ze te herpositioneren. */
    .ticket-card__divider {
        flex: 0 0 auto;
        align-self: stretch;
        width: 0;
        height: auto;
        border-top: none;
        border-left: 2px dashed var(--border);
        margin: 0 4px;
    }

    .ticket-card__notch {
        display: none;
    }

    .ticket-card__body {
        flex: 0 0 300px;
        display: flex;
        flex-direction: column;
        justify-content: center;
    }

    .ticket-card__practical {
        flex-basis: 100%;
    }
}

@media (prefers-color-scheme: dark) {
    .ticket-page {
        color: #ece9fc;
    }

    .ticket-page .ticket-card {
        background: #201c3a;
        border-color: #3a3364;
    }

    .ticket-page .ticket-card__practical {
        border-color: #3a3364;
    }

    /* Bugfix: de algemene "h1,h2,h3{color:var(--brand-dark)}"-regel
       (bijna zwart) wint van deze donkere-modus tekstkleur op
       .ticket-page, omdat een expliciete elementregel altijd voorgaat
       op overerving - de "Praktische informatie"/"Jouw gegevens"-titel
       werd daardoor onleesbaar (bijna zwart op donkerpaars). */
    .ticket-page .ticket-card__practical h3 {
        color: #ece9fc;
    }

    /* De inkeping-cirkels moeten de kaart-achtergrond matchen, niet de
       vaste --surface (wit) - anders blijven het witte stipjes i.p.v.
       een "geponst gaatje"-effect. */
    .ticket-page .ticket-card__notch,
    .ticket-page .ticket-card__qr-panel {
        background: #201c3a;
        border-color: #3a3364;
    }
}

/* Standaard verborgen op scherm - moet VOOR @media print hieronder staan
   zodat de "display: block" daarbinnen bij gelijke specificiteit wint
   (CSS-cascade: bij een gelijkspel wint de regel die het laatst in het
   bestand staat). Zie ook de uitgebreide toelichting bij .print-ticket
   verderop in dit bestand. */
.print-only {
    display: none;
}

@media print {
    .no-print {
        display: none !important;
    }

    .site-header,
    .site-footer {
        display: none !important;
    }

    /* Bugfix (klantmelding 2026-08-09, "3 pagina's i.p.v. 1"): body/.page
       gebruiken op scherm een "sticky footer"-flex-truc
       (min-height:100vh + flex:1 0 auto, zie hun basisregels hierboven)
       zodat de voettekst altijd onderaan het scherm blijft plakken. Dat
       botst bij het printen met .print-ticket hieronder (die zelf ook
       een vaste hoogte krijgt): de twee "min-height"-eisen stapelen dan
       op, plus .page's eigen padding (32px+64px) komt er nog eens
       bovenop - samen ruim genoeg om over te lopen naar 2-3 pagina's.
       Bij het printen is die scherm-truc sowieso zinloos (er is geen
       viewport), dus gewoon helemaal uitzetten. */
    body {
        background: #ffffff !important;
        color: #101014 !important;
        display: block;
        min-height: 0;
    }

    .page {
        display: block;
        padding: 0;
        max-width: none;
        flex: none;
    }

    .page-header {
        display: none;
    }

    .ticket-page {
        display: block;
        grid-template-columns: 1fr;
        margin: 0;
    }

    /* Flyer-herontwerp (klantbeslissing 2026-08-09): het printblok
       (.print-ticket, hieronder na dit @media print-blok) vervangt de
       schermkaart bij het printen - nooit allebei tegelijk zichtbaar.
       De kleur-strip-regels verderop in dit blok voor .ticket-card__*
       worden hierdoor dode code (die elementen zijn al helemaal
       verborgen), maar blijven bewust staan i.p.v. opgeruimd - geen
       risico, en scheelt een grotere diff. */
    .ticket-card {
        display: none !important;
    }

    .print-only {
        display: block;
    }

    /* Site-navigatie/-voettekst/pagina-titel weg bij printen (niet
       alleen voor het ticket - overal zinloos op papier), nodig zodat
       .print-ticket hieronder de volledige pagina kan vullen i.p.v. te
       moeten delen met kopregel/voettekst/titel eromheen.
       Vaste mm-hoogte i.p.v. min-height:100vh (bugfix, zie hierboven bij
       body/.page) - 297mm (A4) min 2x de @page-marge (12mm, zie
       helemaal onderaan dit bestand) = exact één pagina hoog, ongeacht
       hoe de browser vh tijdens printen interpreteert.
       Bugfix (klantmelding 2026-08-09, PDF-test): display:flex +
       margin-top:auto om de strook onderin te duwen werkte op scherm
       prima, maar printpaginering van een flex-container is browser-
       ondersteuning die niet goed gespecificeerd is - zodra de totale
       hoogte ook maar iets over de paginagrens ging, verhuisde niet de
       overtollige tekst maar het HELE laatste flex-kind (de
       strook+logo) in zijn geheel naar een 2e, verder lege pagina.
       position:relative + de strook/logo hieronder absoluut
       gepositioneerd (.print-ticket__bottom) i.p.v. flex is stabieler:
       dat is één ondeelbaar geplakt vlak, geen "content die ergens moet
       breken". Bewust min-height (geen vaste height + overflow:hidden,
       dat was een eerdere tussenstap) - bij een uitzonderlijk lange
       praktische-informatietekst mag de kaart best over 297mm heen
       groeien en eventueel op een 2e pagina doorlopen, want een vaste
       hoogte met overflow:hidden knipt bij te veel inhoud stilletjes
       zowel het einde van de tekst als de hele strook+QR+logo eraf -
       een lege plek waar de organisatie niets van merkt totdat een
       bezoeker met een niet-scanbaar ticket voor de deur staat. Content
       nooit verliezen weegt zwaarder dan altijd exact 1 pagina.
       padding-bottom reserveert vaste ruimte voor .print-ticket__bottom
       (absoluut gepositioneerd, telt zelf niet mee in de flow-hoogte) -
       zonder deze reservering overlapt de strook+logo de praktische
       informatie zodra die net te lang is om nog in de resterende
       ruimte onder min-height te passen (gemeten: strook+logo ~52mm
       hoog bij 1 regel evenementnaam, 58mm geeft marge voor een langere
       naam die naar 2 regels omslaat). */
    .print-ticket {
        position: relative;
        min-height: 273mm;
        padding-bottom: 58mm;
    }

    .print-ticket__bottom {
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
    }

    .ticket-card {
        box-shadow: none;
        border: 1px solid #101014;
        background: #ffffff !important;
    }

    /* Geen gekleurde vlakken op papier - onnodig inktverbruik, en de
       merkkleur van de organisatie is niet altijd goed leesbaar/afdrukbaar. */
    .ticket-card__header {
        background: #ffffff !important;
        color: #101014 !important;
        border-bottom: 1px solid #101014;
    }

    .ticket-card__header h2 {
        color: #101014 !important;
    }

    .ticket-card__holder {
        border-top-color: #101014 !important;
    }

    .ticket-card__divider {
        display: none;
    }

    .ticket-card__qr-panel {
        box-shadow: none;
        border-color: #101014 !important;
    }

    .ticket-card__practical {
        border-color: #101014 !important;
    }

    .ticket-card__qr {
        width: 260px;
    }

    .ticket-stub {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 16px;
        margin-top: 12px;
        border-top: 2px dashed #101014;
        padding: 16px 24px;
    }

    .ticket-stub__qr {
        width: 90px;
        flex-shrink: 0;
    }
}

/* ---------------------------------------------------------------------
   Printbaar ticket - flyerontwerp (klantbeslissing 2026-08-09, op basis
   van een aangeleverde referentieafbeelding). Losstaand van .ticket-card
   hierboven (die blijft de mobiele/scherm-kaart, ongewijzigd) -
   .print-only zorgt dat dit blok alleen bij het printen zichtbaar wordt,
   .ticket-card wordt dan juist verborgen (@media print hierboven), zodat
   er nooit twee varianten tegelijk op papier komen. De basisregel
   ".print-only { display: none; }" staat bewust VOOR het @media
   print-blok hierboven (i.p.v. hier) - bugfix (klantmelding 2026-08-09):
   met gelijke specificiteit wint bij het printen de regel die het laatst
   in het bestand staat, en die stond eerst per ongeluk hier, ná de
   "display: block" binnen @media print, waardoor het printblok nooit
   zichtbaar werd.
   --------------------------------------------------------------------- */

.print-ticket {
    font-family: var(--font-sans);
    color: var(--ink);
    background: #ffffff;
    border: 2px solid var(--brand);
    border-radius: var(--radius-lg);
    overflow: hidden;
    break-inside: avoid;
    page-break-inside: avoid;
    /* Forceert kleurvlakken/achtergrondafbeeldingen bij het printen, ook
       als de browserinstelling "Achtergrondafbeeldingen" uitstaat -
       zonder dit printen de meeste browsers de paarse tips-vlak
       hieronder gewoon blanco/wit. De foto zelf heeft dit niet nodig
       (dat is een echte <img>, geen CSS-achtergrond - die print altijd
       mee), maar deze regel op de hele kaart dekt ook de paarse
       tips-box en de lichte stub-achtergrond in één keer af. */
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
}

.print-ticket__photo {
    position: relative;
    height: 60mm;
    background: linear-gradient(135deg, var(--brand) 0%, color-mix(in srgb, var(--brand) 65%, #140b2e) 100%);
}

.print-ticket__photo-img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: saturate(0.55) sepia(0.35) hue-rotate(200deg);
}

/* Zelfde scrim-verloop als .event-hero__scrim op de ticketshop
   (hierboven in dit bestand) - donker onderin voor leesbare witte
   tekst, lichter bovenin zodat de foto zelf nog zichtbaar blijft. */
.print-ticket__photo-scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(16, 16, 30, 0.15) 0%, rgba(16, 16, 30, 0.1) 35%, rgba(16, 16, 30, 0.85) 100%);
}

.print-ticket__photo-content {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 6mm 40mm 5mm 8mm;
    color: #ffffff;
    box-sizing: border-box;
}

.print-ticket__photo-title {
    margin: 0 0 2mm;
    font-family: var(--font-display);
    font-size: 1.5rem;
    line-height: 1.1;
}

.print-ticket__photo-meta {
    margin: 0;
    font-size: 0.92rem;
    font-weight: 600;
    opacity: 0.9;
}

.print-ticket__qr-box {
    position: absolute;
    top: 50%;
    right: 8mm;
    transform: translateY(-50%);
    width: 30mm;
    height: 30mm;
    background: #ffffff;
    border-radius: var(--radius-lg);
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 3mm;
    box-sizing: border-box;
}

.print-ticket__qr-box svg {
    display: block;
    width: 100%;
    height: auto;
}

.print-ticket__body {
    padding: 8mm 10mm;
    border-left: 4px solid var(--accent);
    margin: 6mm 8mm 0;
}

.print-ticket__visitor-name {
    margin: 0 0 3mm;
    font-family: var(--font-display);
    font-size: 1.7rem;
    line-height: 1.15;
}

.print-ticket__visitor-type {
    margin: 0;
    font-size: 0.88rem;
    color: var(--muted);
}

/* Meer lucht tussen het goudaccent-blok (naam/type) en de daaropvolgende
   Praktische informatie/Jouw gegevens-sectie, en de dunne lijn ertussen
   mag weg (klantverzoek 2026-08-09) - alleen binnen .print-ticket:
   .ticket-card__practical is een gedeelde class met de schermkaart, die
   blijft daar bewust ongewijzigd (heeft de lijn nog wel nodig). */
.print-ticket .ticket-card__practical {
    border-top: none;
    margin-top: 10mm;
}

.print-ticket__stub {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin: 0;
    border-top: 2px dashed #101014;
    padding: 6mm 8mm;
}

.print-ticket__stub-qr {
    width: 24mm;
    flex-shrink: 0;
}

.print-ticket__stub-qr svg {
    display: block;
    width: 100%;
    height: auto;
}

.print-ticket__footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding: 4mm 8mm 8mm;
}

.print-ticket__footer-logo {
    max-height: 28px;
    max-width: 160px;
}

/* .logo-mark/.logo-text zijn standaard voor een gekleurde/donkere
   achtergrond gebouwd (wit) - net als .site-header__logo-pill en
   .dashboard-topbar__logo elders in dit bestand hun eigen donkere
   variant scopen, doet de flyer-footer (witte achtergrond) dat hier
   ook, anders zijn de logo-balkjes onzichtbaar (wit-op-wit). */
.print-ticket__footer .logo-mark--dark span {
    background: var(--ink);
}

.print-ticket__footer .logo-mark {
    height: 16px;
}

.print-ticket__footer .logo-text--dark {
    color: var(--ink);
    font-size: 0.85rem;
}

@page {
    margin: 12mm;
}

/* ---------------------------------------------------------------------
   Sleutel-weergave (2FA-secret, ticketcodes)
   --------------------------------------------------------------------- */

.secret-key {
    display: inline-block;
    font-family: var(--font-mono);
    background: var(--surface-alt);
    border: 1px dashed var(--border);
    padding: 10px 14px;
    border-radius: var(--radius);
    letter-spacing: 0.04em;
    word-break: break-all;
}

@media (min-width: 640px) {
    .page {
        padding-top: 48px;
    }

    h1 {
        font-size: 2.2rem;
    }
}

/* ---------------------------------------------------------------------
   Scanscherm (hoofdstuk 4.7) - losse, minimale pagina (geen site-chrome),
   achtergrond in klantkleur. Bewust hoog contrast en grote knoppen/tekst:
   dit scherm wordt op een tablet/telefoon bij de deur gebruikt, vaak in
   fel licht en onder tijdsdruk.
   --------------------------------------------------------------------- */

.scan-body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    font-family: var(--font-sans);
    /* Ondoorzichtige achtergrond i.p.v. de standaard (lichte)
       body-achtergrond hierboven laten doorschemeren (klantmelding
       2026-08-21: de bovenkant van het scanscherm oogde mintgroen/te
       licht) - de kind-elementen hieronder mixen hun merkkleurtint tegen
       DEZE donkere kleur i.p.v. tegen transparent, maar dit is het
       vangnet voor elk stukje dat toch nog blootligt (bijv. tijdens de
       overgang tussen tabs). */
    background: #0f0f16;
    /* touch-action: manipulation staat al op de algemene body-regel. */
}

/* Scannen-tab (04 Scanapp - collage): donkere achtergrond die de eigen
   merkkleur van de organisatie behoudt via color-mix() i.p.v. platte
   achtergrondkleur (was: background-color: var(--scan-color) direct op
   body). Herzien 2026-08-21: color-mix(...,  transparent) liet de LICHTE
   standaard-bodyachtergrond erdoorheen schemeren (klantmelding "nog niet
   donker genoeg"/"mintgroen") - nu mixt elke stop tegen de eigen donkere
   basiskleur i.p.v. tegen transparent, dus altijd 100% dekkend.
   flex: 1 i.p.v. min-height:100vh (was hard vastgezet, waardoor de
   voettekst hieronder altijd een extra volledige schermhoogte verderop
   stond) - binnen .scan-body (flex-kolom, min-height:100vh) vult dit nu
   automatisch de ruimte BOVEN de voettekst, en op mobiel (voettekst
   display:none) nog steeds de volledige hoogte, want het is dan het
   enige zichtbare flex-kind. */
.scan-tab-scannen {
    width: 100%;
    flex: 1;
    position: relative;
    background: linear-gradient(
        180deg,
        color-mix(in srgb, var(--scan-color, var(--brand)) 28%, #0f0f16) 0%,
        color-mix(in srgb, var(--scan-color, var(--brand)) 6%, #0f0f16) 55%,
        #0f0f16 100%
    );
    transition: background 0.2s ease;
}

.scan-screen {
    width: 100%;
    max-width: 480px;
    min-height: 100%;
    margin: 0 auto;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 20px 20px 110px;
    color: #ffffff;
}

.scan-screen__top {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 0.95rem;
    margin-bottom: 16px;
}

.scan-screen__tenant {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Klantlogo rechtsboven, letterlijk dezelfde pil-conventie als
   .site-header__logo-pill (klantmelding 2026-08-21: stond niet op
   dezelfde plek/grootte als de ticketshop) - position:absolute t.o.v.
   .scan-tab-scannen (de volle breedte, niet de 480px-kolom .scan-screen)
   zodat de pil net als op de ticketshop tot de ECHTE rand van het
   scherm "bleedt" (rounded alleen links, plat rechts, geen padding-
   right) i.p.v. binnen de kolom met ruimte aan weerszijden te zweven.
   Zelfde basismaat (36px) als .site-header__logo-pill img. */
.scan-screen__logo-pill {
    display: flex;
    align-items: center;
    position: absolute;
    top: 20px;
    right: 0;
    padding: 8px 20px 8px 18px;
    border-radius: var(--radius-pill, 999px) 0 0 var(--radius-pill, 999px);
    background: #ffffff;
    box-shadow: var(--shadow-pill, 0 6px 16px rgba(0, 0, 0, 0.2));
    box-sizing: border-box;
    z-index: 2;
}

.scan-screen__logo {
    display: block;
    max-height: 36px;
    max-width: 160px;
}

/* Sobere voettekst, alleen op desktopbreedte (klantverzoek 2026-08-20:
   "onderin bij de desktop moet de footer ook in beeld zijn, alleen dan
   niet de linkjes naar de algemene voorwaarden") - .scan-body wordt
   hieronder een kolom i.p.v. een enkele gecentreerde rij, zodat deze
   footer gewoon als volgend blok ONDER .scan-tab-scannen verschijnt.
   Verborgen op mobiel: daar is dit scherm bewust 1 beeldvullende
   kiosk-pagina en heeft de zwevende tabbalk al de onderkant in gebruik. */
.scan-footer {
    display: none;
    width: 100%;
    box-sizing: border-box;
    background: #101014;
    padding: 20px 32px;
}

@media (min-width: 900px) {
    .scan-footer {
        display: block;
    }
}

.scan-footer__inner {
    max-width: 1600px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    gap: 12px;
}

.scan-footer__label {
    color: rgba(255, 255, 255, 0.6);
    font-size: 0.82rem;
}

.scan-footer__logo {
    display: block;
    max-height: 32px;
    max-width: 180px;
}

/* QR-kader (collage "04 Scanapp") - visuele scan-affordance. Op mobiel
   verschijnt hierin ook het live camerabeeld zodra de camera aan staat
   (zie .scan-viewfinder__video); op desktop/tablet is er geen camera
   (isMobilePhone() in scan.js) en verdwijnt het kader zelf via de
   --plain-variant hieronder, want een leeg decoratief kader zonder
   functie oogt daar als een gat in de pagina. De resultaatkaart valt
   hier middenin, zowel in de wachtstand als na een scan. */
.scan-viewfinder {
    position: relative;
    width: 100%;
    max-width: 260px;
    aspect-ratio: 1 / 1;
    margin: 20px auto;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-radius: 24px;
}

.scan-viewfinder--plain {
    aspect-ratio: auto;
    max-width: 320px;
    overflow: visible;
}

.scan-viewfinder--plain .scan-viewfinder__corner,
.scan-viewfinder--plain .scan-viewfinder__scanline {
    display: none;
}

/* Zonder camera (desktop/tablet) heeft de idle-tekst geen kaart nodig
   om op te vallen - het scanresultaat zelf komt toch als schermvullende
   overlay (.scan-result-overlay hieronder), dus dit is puur de
   wachtstand-tekst. */
.scan-viewfinder--plain .scan-screen__result {
    background: none;
    box-shadow: none;
    padding: 0;
    max-width: none;
    color: rgba(255, 255, 255, 0.85);
}

.scan-viewfinder__video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: 0;
}

.scan-viewfinder__corner {
    position: absolute;
    z-index: 2;
    width: 46px;
    height: 46px;
    border: 4px solid var(--scan-color, var(--brand));
    opacity: 0.85;
}

.scan-viewfinder__corner--tl {
    top: 0;
    left: 0;
    border-right: none;
    border-bottom: none;
    border-radius: 16px 0 0 0;
}

.scan-viewfinder__corner--tr {
    top: 0;
    right: 0;
    border-left: none;
    border-bottom: none;
    border-radius: 0 16px 0 0;
}

.scan-viewfinder__corner--bl {
    bottom: 0;
    left: 0;
    border-right: none;
    border-top: none;
    border-radius: 0 0 0 16px;
}

.scan-viewfinder__corner--br {
    bottom: 0;
    right: 0;
    border-left: none;
    border-top: none;
    border-radius: 0 0 16px 0;
}

.scan-viewfinder__scanline {
    position: absolute;
    z-index: 2;
    left: 8%;
    right: 8%;
    top: 50%;
    height: 2px;
    background: var(--accent, #ffc80a);
    box-shadow: 0 0 22px rgba(255, 200, 10, 0.7);
    animation: scan-line-move 2.4s ease-in-out infinite;
}

@keyframes scan-line-move {
    0%, 100% { top: 18%; }
    50% { top: 82%; }
}

.scan-screen__result {
    position: relative;
    z-index: 2;
    width: 100%;
    max-width: 220px;
    box-sizing: border-box;
    background: rgba(255, 255, 255, 0.96);
    color: var(--ink);
    border-radius: 26px;
    box-shadow: 0 12px 30px rgba(0, 0, 0, 0.28);
    padding: 22px 18px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    font-size: 1.05rem;
    gap: 4px;
}

#scan-input {
    width: 100%;
    box-sizing: border-box;
    font-size: 1.4rem;
    padding: 16px;
    border-radius: var(--radius-pill, 999px);
    border: none;
    text-align: center;
    margin-bottom: 12px;
}

.scan-screen__quickactions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 12px;
}

.scan-screen__quickactions button {
    flex: 1 1 calc(50% - 5px);
    padding: 14px 10px;
    border-radius: var(--radius-pill, 999px);
    border: 1px solid rgba(255, 255, 255, 0.3);
    background: rgba(255, 255, 255, 0.1);
    color: #ffffff;
    font-weight: 600;
    font-size: 0.95rem;
    cursor: pointer;
    backdrop-filter: blur(10px);
}

.scan-screen__logout {
    text-align: center;
    margin-top: 8px;
}

.scan-screen__logout button {
    background: transparent;
    border: none;
    color: rgba(255, 255, 255, 0.7);
    text-decoration: underline;
    cursor: pointer;
    padding: 8px;
    font-size: 0.9rem;
}

/* Floating pil-navigatiebalk onderin (collage: "balk onderin = zelfde
   pil als de header, alleen onderaan") - zelfde vorm/gedrag als
   .pill-nav in het backend-dashboardschild (View.php), hier los
   gehouden omdat het scanscherm zijn eigen HTML-document is (geen
   gedeeld dashboardschild). --glass op de donkere Scannen-tab,
   --solid (effen donkere pil) op de lichte Bezoekers/Deurverkoop/
   Stats-tabs. */
.scan-tab-bar {
    position: fixed;
    left: 50%;
    bottom: 18px;
    transform: translateX(-50%);
    z-index: 960;
    display: flex;
    gap: 4px;
    padding: 6px;
    border-radius: 999px;
    max-width: calc(100vw - 24px);
    overflow-x: auto;
}

.scan-tab-bar--glass {
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.16);
    backdrop-filter: blur(18px);
    box-shadow: 0 12px 30px rgba(16, 16, 40, 0.28);
}

.scan-tab-bar--solid {
    background: rgba(16, 16, 22, 0.92);
    box-shadow: 0 12px 30px rgba(16, 16, 40, 0.28);
}

.scan-tab-bar__item {
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
    padding: 10px 16px;
    border-radius: 999px;
    border: none;
    background: transparent;
    color: rgba(255, 255, 255, 0.66);
    font-weight: 600;
    font-size: 0.85rem;
    cursor: pointer;
}

.scan-tab-bar__item--active {
    background: #ffffff;
    color: #101014;
}

.scan-tab-bar__dot {
    display: none;
    width: 9px;
    height: 9px;
    border-radius: 3px;
    background: var(--brand, #5d5dff);
}

.scan-tab-bar__item--active .scan-tab-bar__dot {
    display: inline-block;
}

/* Bezoekerslijst/statistieken/deurverkoop als volwaardige, beeldvullende
   tabs (collage "04 Scanapp") i.p.v. een los pop-up-venster: dezelfde
   .scan-modal-elementen als voorheen, nu altijd beeldvullend zodat ze
   samen met de vaste tabbalk hierboven als "tab" aanvoelen. De
   backdrop-klik-logica (ScanApp.requestCloseModal) blijft functioneel
   bestaan maar is nooit meer zichtbaar, omdat het paneel er altijd
   overheen ligt. */
.scan-modal-backdrop {
    position: fixed;
    inset: 0;
    background: transparent;
    z-index: 900;
}

.scan-modal {
    position: fixed;
    inset: 0;
    z-index: 950;
    color: #ffffff;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    padding: 20px 20px 110px;
    overflow-y: auto;
}

/* Koppen erven color:#fff hierboven NIET (klantmelding 2026-08-21: titel
   bleef zwart) - de generieke h1,h2,h3-regel elders in dit bestand zet
   color:var(--brand-dark) rechtstreeks op het element zelf, en een
   expliciete regel wint altijd van een geërfde waarde, ongeacht de
   specificiteit van de ouder-selector. Dekt elke kop binnen elk
   scan-modal-paneel (Bezoekerslijst/Statistieken/Deurverkoop, incl. de
   kwitantie) in 1 keer af. */
.scan-modal h2,
.scan-modal h3 {
    color: #ffffff;
}

/* Echte donkere modus (klantmelding 2026-08-20: de eerdere lichte,
   pastelkleurige achtergrond was "niet mooi gedaan... eigenlijk te
   licht") - zelfde donkere basis als .scan-tab-scannen (de merkkleur
   vervaagt naar bijna-zwart), zodat Bezoekers/Statistieken/Deurverkoop
   er als 1 samenhangende, donkere app uitzien i.p.v. 1 donkere tab
   tussen 3 lichte pop-ups. De bestaande witte/lichttransparante kaarten
   (.scan-search-result, #scan-search-input, .scan-stats-table, ...)
   waren getuned op een lichte achtergrond met te weinig contrast - op
   deze donkere achtergrond springen ze er vanzelf juist wél uit, dus
   die kaart-CSS blijft ongewijzigd. Herzien 2026-08-21 (klantmelding: de
   Scannen-tab schemerde zichtbaar door de bovenkant van deze panelen
   heen) - color-mix(..., transparent) is NOOT 100% dekkend, dus wat
   eronder ligt (.scan-tab-scannen, altijd in de DOM, nooit verwijderd)
   bleef zichtbaar door de rand van dit paneel heen ondanks position:fixed
   + hogere z-index. Elke stop mixt nu tegen de eigen donkere basiskleur
   i.p.v. tegen transparent, dus altijd volledig dekkend. */
.scan-modal--bezoekers {
    background: linear-gradient(
        180deg,
        color-mix(in srgb, var(--scan-color, var(--brand)) 24%, #0f0f16) 0%,
        color-mix(in srgb, var(--scan-color, var(--brand)) 5%, #0f0f16) 55%,
        #0f0f16 100%
    );
}

.scan-modal--stats {
    background: linear-gradient(
        180deg,
        color-mix(in srgb, var(--scan-color, var(--brand)) 24%, #0f0f16) 0%,
        color-mix(in srgb, var(--scan-color, var(--brand)) 5%, #0f0f16) 55%,
        #0f0f16 100%
    );
}

.scan-modal--deurverkoop {
    background: linear-gradient(
        180deg,
        color-mix(in srgb, var(--scan-color, var(--brand)) 24%, #0f0f16) 0%,
        color-mix(in srgb, var(--scan-color, var(--brand)) 5%, #0f0f16) 55%,
        #0f0f16 100%
    );
}

@media (min-width: 640px) {
    .scan-modal {
        padding-left: max(20px, calc(50% - 260px));
        padding-right: max(20px, calc(50% - 260px));
    }
}

.scan-modal__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

.scan-modal__header h2 {
    margin: 0;
    font-size: 1.1rem;
    /* Expliciet i.p.v. inheritance (klantmelding 2026-08-21: titel bleef
       zwart) - de generieke h1,h2,h3-regel elders in dit bestand zet
       color:var(--brand-dark) rechtstreeks op het element zelf, en een
       expliciete regel wint altijd van een geërfde waarde, ongeacht de
       specificiteit van de ouder-selector (.scan-modal { color:#fff }
       hierboven kwam dus nooit aan bod). */
    color: #ffffff;
}

.scan-modal__header-actions {
    display: flex;
    gap: 8px;
}

.scan-modal__header-actions button {
    padding: 8px 12px;
    border-radius: var(--radius-pill, 999px);
    border: 1px solid var(--border);
    background: rgba(255, 255, 255, 0.7);
    color: var(--ink);
    cursor: pointer;
    font-weight: 600;
    font-size: 0.85rem;
}

.scan-modal__body {
    flex: 1;
    overflow-y: auto;
}

#scan-search-input {
    width: 100%;
    box-sizing: border-box;
    padding: 12px 16px;
    margin-bottom: 8px;
    border-radius: var(--radius-pill, 999px);
    border: 1px solid var(--border);
    background: rgba(255, 255, 255, 0.9);
    font-size: 1rem;
}

/* Overlay-melding bovenop de bezoekerslijst na het inchecken (i.p.v. de
   pop-up te sluiten - zo kun je meteen doorgaan met de rest van je
   zoekopdracht, hoofdstuk 4.7). */
.scan-toast {
    position: absolute;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 24px;
    font-size: 1.15rem;
    font-weight: 700;
    color: #ffffff;
    z-index: 5;
}

.scan-toast--success {
    background: rgba(41, 115, 79, 0.96);
}

.scan-toast--duplicate {
    background: rgba(179, 53, 43, 0.96);
}

/* Kleine laadindicator (deurverkoop, QR-betaling starten - hoofdstuk 4.8). */
.scan-spinner {
    display: inline-block;
    width: 18px;
    height: 18px;
    border: 3px solid rgba(0, 0, 0, 0.15);
    border-top-color: var(--brand, #5d5dff);
    border-radius: 50%;
    animation: scan-spin 0.8s linear infinite;
    vertical-align: middle;
    margin-right: 8px;
}

@keyframes scan-spin {
    to {
        transform: rotate(360deg);
    }
}

/* Handgeschreven SVG-grafieken (statistieken - hoofdstuk 4.9). */
.stats-chart {
    width: 100%;
    height: auto;
    margin-top: 8px;
}

.stats-chart__label {
    font-size: 11px;
    fill: var(--text-muted, #6b7280);
}

.stats-chart__legend {
    display: flex;
    gap: 16px;
    margin-top: 8px;
    font-size: 13px;
}

.stats-chart__legend-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.stats-chart__legend-swatch {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 2px;
}

.stats-chart rect,
.stats-chart circle {
    cursor: default;
}

.stats-chart rect:hover,
.stats-chart circle:hover {
    opacity: 0.75;
}

.stats-table-toggle {
    margin-top: 12px;
}

.stats-table-toggle summary {
    cursor: pointer;
    color: var(--brand, #5d5dff);
    font-size: 0.9rem;
}

.stats-table-toggle table {
    margin-top: 8px;
}

/* Eigen hover-tooltip voor de grafieken (ChartRenderer::tooltipScript())
   i.p.v. het native SVG <title>-element - dat heeft de eerste keer per
   element een trage OS-vertraging, deze verschijnt meteen. */
.stats-chart-tooltip {
    position: fixed;
    z-index: 100;
    transform: translate(-50%, calc(-100% - 10px));
    background: #101014;
    color: #fff;
    padding: 6px 10px;
    border-radius: 6px;
    font-size: 0.8rem;
    line-height: 1.3;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
}

.stats-chart-tooltip--visible {
    opacity: 1;
    visibility: visible;
}

.scan-search-result {
    padding: 10px 14px;
    margin-bottom: 8px;
    border-radius: var(--radius-panel, 16px);
    background: rgba(255, 255, 255, 0.82);
    box-shadow: var(--shadow-card, 0 10px 30px rgba(20, 20, 60, 0.05));
    font-size: 0.9rem;
    /* Expliciet i.p.v. leunen op inheritance (klantmelding 2026-08-21:
       onleesbaar witte tekst op deze lichte kaart) - .scan-modal
       hierboven zet color:#fff voor de DONKERE modal-achtergrond zelf,
       maar deze kaart heeft zijn eigen lichte achtergrond en heeft dus
       zijn eigen (donkere) tekstkleur nodig, ongeacht wat de ouder zet. */
    color: var(--ink);
}

/* Klik op de naam/rij toont pas de actieknop (inchecken of ongedaan
   maken) - voorkomt een per ongeluk gemiste of dubbele tik, hoofdstuk 4.7. */
.scan-search-result__toggle {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 4px 0;
    border: none;
    background: none;
    text-align: left;
    cursor: pointer;
    font: inherit;
    color: inherit;
}

.scan-search-result__avatar {
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    border-radius: 999px;
    background: var(--brand-light, #eeeaff);
    color: var(--brand, #5d5dff);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 0.8rem;
}

.scan-search-result__name {
    flex: 1;
}

.scan-search-result__actions {
    display: flex;
    gap: 8px;
    padding-top: 8px;
}

.scan-search-result__actions button {
    padding: 8px 14px;
    border-radius: var(--radius);
    border: none;
    cursor: pointer;
    font-weight: 600;
    flex-shrink: 0;
}

.scan-search-result__actions button[data-action="checkin"] {
    background: var(--brand);
    color: #ffffff;
}

.scan-search-result__actions button[data-action="revert"] {
    background: #b3352b;
    color: #ffffff;
}

.scan-search-result__actions button[data-action="cancel"] {
    background: var(--surface-alt);
    color: var(--ink);
}

/* Totaalkaart deurverkoop-formulier (collage: donkere samenvattingskaart
   boven de Contant/QR-knoppen). */
.scan-doorsale-total {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: #101014;
    color: #ffffff;
    border-radius: var(--radius-panel, 16px);
    padding: 16px 18px;
    margin: 12px 0;
}

.scan-doorsale-total__label {
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.66);
}

.scan-doorsale-total__amount {
    font-size: 1.4rem;
    font-weight: 700;
}

/* Staafdiagram incheck-verloop op het statistieken-tabblad van de
   scan-app (klantverzoek 2026-08-20: per 15/30/60 minuten, met knoppen om
   te schakelen) - bewust simpele HTML/CSS-staven i.p.v. de SVG-
   ChartRenderer van de organisatie-statistiekenpagina's (.stats-chart
   hierboven): dit scherm is al volledig JS-gedreven, geen server-render-
   rondje nodig voor zoiets eenvoudigs. Zelfde "witte kaart op donkere
   achtergrond"-aanpak als .scan-stats-table hieronder. */
.scan-stats-chart__title {
    margin: 0 0 10px;
    font-size: 1rem;
    /* Zelfde inheritance-bug als .scan-modal__header h2 hierboven - deze
       h3 zit op de donkere modal-achtergrond zelf (niet op de lichte
       kaart eronder), dus moet ook expliciet wit zijn. */
    color: #ffffff;
}

.scan-stats-interval-toggle {
    display: flex;
    gap: 4px;
    margin-bottom: 12px;
    padding: 4px;
    background: rgba(255, 255, 255, 0.1);
    border-radius: var(--radius-pill, 999px);
    width: fit-content;
}

.scan-stats-interval-toggle__item {
    padding: 6px 14px;
    border: none;
    border-radius: var(--radius-pill, 999px);
    background: transparent;
    color: rgba(255, 255, 255, 0.7);
    font-weight: 600;
    font-size: 0.82rem;
    cursor: pointer;
}

.scan-stats-interval-toggle__item--active {
    background: #ffffff;
    color: #101014;
}

.scan-stats-chart {
    background: rgba(255, 255, 255, 0.85);
    border-radius: var(--radius-panel, 16px);
    box-shadow: var(--shadow-card, 0 10px 30px rgba(20, 20, 60, 0.05));
    padding: 16px 14px 10px;
    margin-bottom: 20px;
}

.scan-stats-chart__bars {
    display: flex;
    align-items: flex-end;
    gap: 3px;
    height: 120px;
}

.scan-stats-chart__bar-wrap {
    flex: 1;
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: center;
}

.scan-stats-chart__bar {
    width: 100%;
    min-height: 0;
    background: var(--brand, #5d5dff);
    border-radius: 3px 3px 0 0;
}

/* Aantal incheckingen zichtbaar boven de staaf (klantverzoek 2026-08-21) -
   niet alleen als hover-tooltip, die is op een touchscreen sowieso niet
   bruikbaar. */
.scan-stats-chart__bar-count {
    font-size: 0.68rem;
    font-weight: 700;
    color: var(--ink);
    margin-bottom: 2px;
    line-height: 1;
}

.scan-stats-chart__labels {
    display: flex;
    justify-content: space-between;
    margin-top: 8px;
    font-size: 0.75rem;
    color: var(--muted);
}

.scan-stats-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
    background: rgba(255, 255, 255, 0.82);
    border-radius: var(--radius-panel, 16px);
    box-shadow: var(--shadow-card, 0 10px 30px rgba(20, 20, 60, 0.05));
    overflow: hidden;
    /* Zelfde reden als .scan-search-result hierboven - expliciet i.p.v.
       de witte tekstkleur van .scan-modal te erven op deze lichte kaart. */
    color: var(--ink);
}

.scan-stats-table th,
.scan-stats-table td {
    text-align: left;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
}

.scan-stats-table th:not(:first-child),
.scan-stats-table td:not(:first-child) {
    text-align: right;
}

/* Schermvullende scanresultaat-overlay (hoofdstuk 4.7) - gedeeld door
   zowel het typte/hardware-scanner-pad als het camera-scanpad (zie
   ScanApp.showResult() in scan.js): één volledig kleurvlak over de
   HELE viewport (los van de 480px-brede .scan-screen-kolom, ook op
   desktop) is nodig omdat een kleursignaal op de resultaatkaart of de
   achtergrond van de tab bleek te subtiel te zijn voor deurpersoneel
   dat in een oogopslag, snel achter elkaar, moet zien of een scan goed
   of fout was. Staat boven de tabbalk (z-index 960) en alle panelen. */
.scan-result-overlay {
    position: fixed;
    inset: 0;
    z-index: 980;
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 28px;
    padding: 40px 24px;
    text-align: center;
    color: #ffffff;
    animation: scan-result-pop 0.18s ease-out;
}

.scan-result-overlay--success {
    background: var(--success, #1f7a5a);
}

.scan-result-overlay--duplicate,
.scan-result-overlay--invalid {
    background: #b3352b;
}

.scan-result-overlay__content {
    font-size: 1.6rem;
    font-weight: 700;
    line-height: 1.35;
}

.scan-result-overlay__content p {
    margin: 0 0 6px;
}

.scan-result-overlay__continue {
    padding: 16px 40px;
    border-radius: var(--radius-pill, 999px);
    border: none;
    background: rgba(255, 255, 255, 0.18);
    color: #ffffff;
    font-weight: 700;
    font-size: 1.05rem;
    cursor: pointer;
}

@keyframes scan-result-pop {
    from {
        transform: scale(0.94);
        opacity: 0;
    }
    to {
        transform: scale(1);
        opacity: 1;
    }
}

/* Camerabeeld (optioneel, naast typen/hardware-scanner) - leeft IN het
   viewfinder-kader zelf (.scan-viewfinder__video hierboven) i.p.v. een
   aparte fullscreen zwarte overlay, zodat de rest van het scherm
   (tabbalk, kop) zichtbaar blijft terwijl de camera aanstaat. Het
   scanresultaat zelf (geldig/dubbel/ongeldig) verschijnt via de
   gedeelde .scan-result-overlay hierboven. */
.scan-camera-controls {
    width: 100%;
    max-width: 260px;
    margin: 10px auto 0;
    padding: 0;
    display: flex;
    gap: 10px;
    justify-content: center;
}

.scan-camera-controls button {
    padding: 12px 24px;
    border-radius: var(--radius-pill, 999px);
    border: none;
    background: rgba(255, 255, 255, 0.92);
    color: var(--ink);
    font-weight: 600;
    cursor: pointer;
}
