/* ============================================================
   KlasVizier UI Components
   ------------------------------------------------------------
   Generieke, herbruikbare UI-componenten — offcanvas, modal,
   dropdown, toast, forms, alerts, pills, cards, buttons,
   tables, empty-state, tabs, inline-edit, en onderaan het
   paginaskelet van de leerkracht-app (ui-page, ui-toolbar,
   ui-subbar, ui-panel, ui-loading, ui-actionbar).

   Gebruikt door:
   - admin-shell (beheer-pagina's)   — klasse-namen .admin-*
   - leerkracht-app (dashboard,      — klasse-namen .ui-*
     voortgang, etc.)

   Selectoren zijn gedubbeld: `.ui-x, .admin-x` zodat beide
   contexten dezelfde CSS delen. Oud klasnamen blijven werken;
   nieuwe code kan `.ui-*` gebruiken.

   Accent-kleuren binnen .admin-shell (amber/blauw per hub)
   worden via var(--admin-accent, var(--primary-color)) gevolgd
   — binnen admin-shell gebruikt het de hub-kleur, daarbuiten
   valt het terug op de KlasVizier primary (#2563eb).

   Vereist: style.css vooraf geladen (voor CSS-vars).
   ============================================================ */

/* ============================================================
   OFFCANVAS — Alpine-driven slide-out edit panel
   ============================================================ */
/* z-index 1200 — moet boven sticky leerkracht-header (z-index 1100
   in header.css) én boven Bootstrap modals (1055) komen. Bootstrap
   modals worden gefaseerd vervangen door deze offcanvas. Admin-shell
   topbar heeft z-index 30, dus 1200 werkt daar ook. */
.ui-offcanvas,
.admin-offcanvas {
    position: fixed;
    inset: 0;
    z-index: 1200;
    pointer-events: none;
}

.ui-offcanvas[x-cloak],
.admin-offcanvas[x-cloak] { display: none !important; }

.ui-offcanvas__backdrop,
.admin-offcanvas__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(31, 41, 55, 0.45);
    backdrop-filter: blur(2px);
    pointer-events: auto;
}

.ui-offcanvas__panel,
.admin-offcanvas__panel {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(480px, 100%);
    background: var(--surface-color);
    box-shadow: -8px 0 32px rgba(31, 41, 55, 0.16);
    display: flex;
    flex-direction: column;
    pointer-events: auto;
    transition: transform var(--transition-base);
}

/* --stacked (UX-3b): een tweede drawer die bovenóp een al geopende drawer komt.
   Alle offcanvas zitten standaard op 1200; een panel dat vanuit een ander panel
   geopend wordt (bv. materiaalkiezer vanuit de planning-drawer) heeft een eigen
   laag nodig, anders valt het eronder of vecht het met de backdrop.
   1205 zit boven de gewone offcanvas (1200) en onder .ui-modal (1210), zodat
   showConfirmDialog() óók bovenop een gestapelde drawer blijft werken. */
.ui-offcanvas--stacked,
.admin-offcanvas--stacked { z-index: 1205; }

/* Breedte-modifiers — leerkracht-app gebruikt deze om offcanvas op maat te zetten */
.ui-offcanvas--sm .ui-offcanvas__panel { width: min(380px, 100%); }
.ui-offcanvas--md .ui-offcanvas__panel { width: clamp(420px, 40vw, 600px); }
.ui-offcanvas--lg .ui-offcanvas__panel { width: clamp(480px, 50vw, 720px); }
.ui-offcanvas--xl .ui-offcanvas__panel { width: clamp(560px, 60vw, 880px); }

@media (max-width: 640px) {
    .ui-offcanvas--sm .ui-offcanvas__panel,
    .ui-offcanvas--md .ui-offcanvas__panel,
    .ui-offcanvas--lg .ui-offcanvas__panel,
    .ui-offcanvas--xl .ui-offcanvas__panel { width: 100%; }
}

.ui-offcanvas__panel--enter-start,
.admin-offcanvas__panel--enter-start {
    transform: translateX(100%);
}

.ui-offcanvas__panel--enter-end,
.admin-offcanvas__panel--enter-end {
    transform: translateX(0);
}

.ui-offcanvas__header,
.admin-offcanvas__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1.1rem 1.4rem;
    border-bottom: 1px solid var(--border-color);
    background: #fafbfc;
    flex-shrink: 0;
}

.ui-offcanvas__title,
.admin-offcanvas__title {
    font-family: var(--font-display);
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.ui-offcanvas__title i,
.admin-offcanvas__title i {
    color: var(--admin-accent, var(--primary-color));
    font-size: 1rem;
}

.ui-offcanvas__close,
.admin-offcanvas__close {
    background: transparent;
    border: 0;
    color: var(--text-secondary);
    font-size: 1.05rem;
    cursor: pointer;
    padding: 0.4rem 0.6rem;
    border-radius: var(--border-radius);
    transition: all var(--transition-fast);
}

.ui-offcanvas__close:hover,
.admin-offcanvas__close:hover {
    background: #f3f4f6;
    color: var(--primary-color);
}

.ui-offcanvas__close--danger {
    color: var(--danger-color, #dc3545);
}

.ui-offcanvas__close--danger:hover {
    background: rgba(220, 53, 69, 0.08);
    color: var(--danger-color, #dc3545);
}

/* Offcanvas body: container is NIET scrollend zelf — de inner form-grid scrollt.
   Dit voorkomt elastic-bounce glitches met sticky footers op macOS/iOS. */
.ui-offcanvas__body,
.admin-offcanvas__body {
    padding: 1.4rem;
    flex: 1;
    min-height: 0;             /* cruciaal voor flex + overflow in descendant */
    display: flex;
    flex-direction: column;
    overflow: hidden;          /* Scroll gebeurt op .ui-form-grid child, niet hier */
}

/* ============================================================
   WIZARD DOTS — stap-indicator in offcanvas header
   ------------------------------------------------------------
   Gebruik in combinatie met `uiWizard` Alpine factory. Plaats
   binnen `.ui-offcanvas__header` naast de titel. Elke dot is
   clickable om direct naar die stap te springen.
   ============================================================ */
.ui-wizard-dots {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-left: auto;
    margin-right: 0.75rem;
}
.ui-wizard-dots__dot {
    width: 28px;
    height: 28px;
    border-radius: 999px;
    border: 1.5px solid var(--border-color);
    background: var(--surface-color);
    color: var(--text-secondary);
    font-family: var(--font-primary);
    font-size: 0.78rem;
    font-weight: 700;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: all var(--transition-fast);
}
.ui-wizard-dots__dot:hover:not(:disabled) {
    border-color: var(--primary-light);
    color: var(--primary-color);
}
.ui-wizard-dots__dot.is-active {
    background: var(--primary-color);
    border-color: var(--primary-color);
    color: #fff;
}
.ui-wizard-dots__dot:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}
.ui-wizard-dots__connector {
    width: 20px;
    height: 2px;
    background: var(--border-color);
    border-radius: 1px;
}

/* De form binnen de body is zelf flex-column zodat grid en actions als siblings
   gelayout worden: grid scrollt, actions blijven onderaan. */
.ui-offcanvas__body > form,
.admin-offcanvas__body > form {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

/* Form-grid is het échte scroll-gebied — altijd zichtbare scrollbar + contain.
   Strekt over de volle breedte van de offcanvas body via negatieve horizontal
   margins, zodat de scroll-shadow (zie hieronder) doorloopt tot aan de panel-rand.

   Scroll-shadow (Lea Verou trick, CSS-only):
     1. "Cover": gradient surface-color → transparent, LOCAL attachment — scrollt
        mee met de content en dekt de shadow af bij de bodem.
     2. "Shadow": donkere gradient, SCROLL attachment — fixed tegen de viewport,
        altijd zichtbaar tot de cover hem afdekt.
   Effect: shadow is alleen zichtbaar als er nog content onder de viewport is. */
.ui-offcanvas__body .ui-form-grid,
.ui-offcanvas__body .admin-form-grid,
.admin-offcanvas__body .ui-form-grid,
.admin-offcanvas__body .admin-form-grid {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-gutter: stable;

    /* Rijen bovenaan uitlijnen i.p.v. uitrekken om beschikbare hoogte te vullen */
    align-content: start;

    /* Strek over volle breedte van de body — scroll-shadow loopt tot panel-rand */
    margin-left: -1.4rem;
    margin-right: -1.4rem;
    padding-left: 1.4rem;
    padding-right: 1.4rem;

    background:
        /* 1. Cover onderaan (local: scrollt mee) — dekt de shadow af als je aan de bodem bent */
        linear-gradient(to top, var(--surface-color) 0, rgba(255, 255, 255, 0) 28px) bottom / 100% 28px no-repeat local,
        /* 2. Shadow onderaan (scroll: fixed tegen de viewport) — hint "er is meer" */
        linear-gradient(to top, rgba(31, 41, 55, 0.14), rgba(31, 41, 55, 0) 22px) bottom / 100% 22px no-repeat scroll;
    background-color: var(--surface-color);
}

/* Form-actions zijn nu een normale flex-sibling onderaan — geen sticky meer.
   Strekt over de volle breedte van de body door negatieve horizontale margin. */
.ui-offcanvas__body .ui-form-actions,
.ui-offcanvas__body .admin-form-actions,
.admin-offcanvas__body .ui-form-actions,
.admin-offcanvas__body .admin-form-actions {
    flex-shrink: 0;
    margin: 1rem -1.4rem -1.4rem;
    padding: 0.9rem 1.4rem;
    background: var(--surface-color);
    border-top: 1px solid var(--border-color);
    box-shadow: 0 -8px 12px -8px rgba(31, 41, 55, 0.1);
}

.ui-offcanvas__footer,
.admin-offcanvas__footer {
    display: flex;
    justify-content: flex-end;
    gap: 0.55rem;
    padding: 1rem 1.4rem;
    background: #fafbfc;
    border-top: 1px solid var(--border-color);
    flex-shrink: 0;
}

/* ============================================================
   MODAL — Alpine-driven centered dialog (kleinere variant)
   ============================================================ */
.ui-modal,
.admin-modal {
    position: fixed;
    inset: 0;
    /* z-index 1210 — boven offcanvas (1200) zodat confirm-dialogs OP een sidebar mogelijk zijn */
    z-index: 1210;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.ui-modal[x-cloak],
.admin-modal[x-cloak] { display: none !important; }

.ui-modal__backdrop,
.admin-modal__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(31, 41, 55, 0.55);
    backdrop-filter: blur(2px);
}

.ui-modal__panel,
.admin-modal__panel {
    position: relative;
    background: var(--surface-color);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-xl);
    width: 100%;
    max-width: 460px;
    max-height: 90vh;
    overflow-y: auto;
}

.ui-modal__header,
.admin-modal__header {
    padding: 1.1rem 1.4rem 0.4rem;
}

.ui-modal__title,
.admin-modal__title {
    font-family: var(--font-display);
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
    display: flex;
    align-items: center;
    gap: 0.55rem;
}

.ui-modal__body,
.admin-modal__body {
    padding: 0.5rem 1.4rem 1.1rem;
    color: var(--text-primary);
    font-size: 0.92rem;
    line-height: 1.5;
}

.ui-modal__footer,
.admin-modal__footer {
    display: flex;
    justify-content: flex-end;
    gap: 0.55rem;
    padding: 1rem 1.4rem;
    background: #fafbfc;
    border-top: 1px solid var(--border-color);
    border-radius: 0 0 var(--border-radius-lg) var(--border-radius-lg);
}

/* Variant icons in modal */
.ui-modal__icon,
.admin-modal__icon {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.3rem;
    margin: 0 auto 0.85rem;
}

.ui-modal__icon--danger,  .admin-modal__icon--danger  { background: #fee2e2; color: #991b1b; }
.ui-modal__icon--warn,    .admin-modal__icon--warn    { background: #fef3c7; color: #92400e; }
.ui-modal__icon--info,    .admin-modal__icon--info    { background: #dbeafe; color: #1e40af; }
.ui-modal__icon--success, .admin-modal__icon--success { background: #d1fae5; color: #065f46; }

/* ============================================================
   DROPDOWN — Alpine-driven menu
   ============================================================ */
.ui-dropdown,
.admin-dropdown {
    position: relative;
    display: inline-block;
}

.ui-dropdown__menu,
.admin-dropdown__menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    min-width: 200px;
    background: var(--surface-color);
    border: 1px solid var(--border-color);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 0.4rem;
    z-index: 50;
}

.ui-dropdown__menu[x-cloak],
.admin-dropdown__menu[x-cloak] { display: none !important; }

.ui-dropdown__item,
.admin-dropdown__item {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    width: 100%;
    padding: 0.5rem 0.75rem;
    color: var(--text-primary);
    background: transparent;
    border: 0;
    border-radius: var(--border-radius);
    font-size: 0.88rem;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    text-align: left;
    transition: all var(--transition-fast);
}

.ui-dropdown__item:hover,
.admin-dropdown__item:hover {
    background: var(--admin-accent-soft, #eff6ff);
    color: var(--admin-accent-dark, var(--primary-dark));
    text-decoration: none;
}

.ui-dropdown__item i,
.admin-dropdown__item i {
    width: 1rem;
    color: var(--text-secondary);
    text-align: center;
}

.ui-dropdown__item:hover i,
.admin-dropdown__item:hover i { color: var(--admin-accent-dark, var(--primary-dark)); }

.ui-dropdown__divider,
.admin-dropdown__divider {
    height: 1px;
    background: var(--border-color);
    margin: 0.35rem 0;
}

/* ============================================================
   TOAST — fixed-bottom meldingen
   ============================================================ */
.ui-toast,
.admin-toast {
    position: fixed;
    bottom: 1.5rem;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 0.85rem;
    padding: 0.9rem 1.1rem 0.9rem 1.25rem;
    background: var(--text-primary);
    color: #fff;
    border-radius: 999px;
    box-shadow: 0 8px 32px rgba(31, 41, 55, 0.25);
    font-family: var(--font-primary);
    font-size: 0.88rem;
    z-index: 120;
    min-width: 320px;
    max-width: min(560px, calc(100% - 2rem));
}

.ui-toast[x-cloak],
.admin-toast[x-cloak] { display: none !important; }

.ui-toast__icon,
.admin-toast__icon {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.15);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    font-size: 0.85rem;
}

.ui-toast__body,
.admin-toast__body {
    flex: 1;
    line-height: 1.35;
}

.ui-toast__body strong,
.admin-toast__body strong { font-weight: 600; }

.ui-toast__action,
.admin-toast__action {
    background: rgba(255, 255, 255, 0.2);
    border: 0;
    color: #fff;
    padding: 0.45rem 0.9rem;
    border-radius: 999px;
    font-family: inherit;
    font-size: 0.82rem;
    font-weight: 600;
    cursor: pointer;
    transition: background var(--transition-fast);
    white-space: nowrap;
}

.ui-toast__action:hover,
.admin-toast__action:hover {
    background: rgba(255, 255, 255, 0.3);
}

.ui-toast__close,
.admin-toast__close {
    background: transparent;
    border: 0;
    color: rgba(255, 255, 255, 0.7);
    font-size: 0.9rem;
    cursor: pointer;
    padding: 0.2rem 0.4rem;
    transition: color var(--transition-fast);
}

.ui-toast__close:hover,
.admin-toast__close:hover { color: #fff; }

.ui-toast__timer,
.admin-toast__timer {
    font-size: 0.72rem;
    color: rgba(255, 255, 255, 0.6);
    font-variant-numeric: tabular-nums;
}

/* ============================================================
   INLINE EDIT (click-to-edit fields)
   ============================================================ */
.ui-inline-edit,
.admin-inline-edit {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    position: relative;
}

.ui-inline-edit__display,
.admin-inline-edit__display {
    cursor: text;
    padding: 0.2rem 0.4rem;
    border-radius: var(--border-radius-sm);
    border: 1px solid transparent;
    transition: all var(--transition-fast);
}

.ui-inline-edit__display:hover,
.admin-inline-edit__display:hover {
    background: #f3f4f6;
    border-color: var(--border-color);
}

.ui-inline-edit__input,
.admin-inline-edit__input {
    padding: 0.2rem 0.4rem;
    border: 1px solid var(--primary-light);
    border-radius: var(--border-radius-sm);
    font-family: inherit;
    font-size: inherit;
    background: var(--surface-color);
    box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12);
}

.ui-inline-edit__saved,
.admin-inline-edit__saved {
    color: var(--success-color);
    font-size: 0.78rem;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
}

/* ============================================================
   CARDS — content-wrapper met header/body
   ============================================================ */
.ui-card,
.admin-card {
    background: var(--surface-color);
    border: 1px solid var(--border-color);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    transition: box-shadow var(--transition-base);
}

.ui-card:hover,
.admin-card:hover {
    box-shadow: var(--shadow);
}

.ui-card__header,
.admin-card__header {
    padding: 1.1rem 1.4rem;
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    background: #fafbfc;
}

.ui-card__title,
.admin-card__title {
    font-family: var(--font-display);
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
    display: flex;
    align-items: center;
    gap: 0.55rem;
}

.ui-card__title i,
.admin-card__title i {
    color: var(--admin-accent, var(--primary-color));
    font-size: 1rem;
}

.ui-card__body,
.admin-card__body {
    padding: 1.25rem 1.4rem;
}

/* ============================================================
   BUTTONS — KlasVizier patroon
   ============================================================ */
.ui-btn,
.admin-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.55rem 1.1rem;
    border-radius: var(--border-radius);
    font-family: var(--font-primary);
    font-size: 0.875rem;
    font-weight: 600;
    border: 1px solid transparent;
    cursor: pointer;
    text-decoration: none;
    transition: all var(--transition-fast);
    line-height: 1.2;
}

.ui-btn--primary,
.admin-btn--primary {
    background: var(--primary-color);
    color: #fff;
    border-color: var(--primary-color);
    box-shadow: var(--shadow-xs);
}

.ui-btn--primary:hover,
.admin-btn--primary:hover {
    background: var(--primary-dark);
    border-color: var(--primary-dark);
    color: #fff;
    text-decoration: none;
    transform: translateY(-1px);
    box-shadow: var(--shadow-sm);
}

/* Accent-variant — gebruikt hub-accent binnen admin-shell, primary daarbuiten */
.ui-btn--accent,
.admin-btn--accent {
    background: var(--admin-accent, var(--primary-color));
    color: #fff;
    border-color: var(--admin-accent, var(--primary-color));
    box-shadow: var(--shadow-xs);
}

.ui-btn--accent:hover,
.admin-btn--accent:hover {
    background: var(--admin-accent-dark, var(--primary-dark));
    border-color: var(--admin-accent-dark, var(--primary-dark));
    color: #fff;
    text-decoration: none;
    transform: translateY(-1px);
    box-shadow: var(--shadow-sm);
}

.ui-btn--secondary,
.admin-btn--secondary {
    background: var(--surface-color);
    color: var(--text-primary);
    border-color: var(--border-color);
}

.ui-btn--secondary:hover,
.admin-btn--secondary:hover {
    background: #f9fafb;
    border-color: var(--primary-light);
    color: var(--primary-color);
    text-decoration: none;
    transform: translateY(-1px);
    box-shadow: var(--shadow-xs);
}

.ui-btn--ghost,
.admin-btn--ghost {
    background: transparent;
    color: var(--text-secondary);
}

.ui-btn--ghost:hover,
.admin-btn--ghost:hover {
    background: #f3f4f6;
    color: var(--primary-color);
    text-decoration: none;
}

.ui-btn--danger,
.admin-btn--danger {
    background: var(--surface-color);
    color: var(--danger-color);
    border-color: #fecaca;
}

.ui-btn--danger:hover,
.admin-btn--danger:hover {
    background: #fef2f2;
    border-color: var(--danger-color);
    color: var(--danger-color);
    text-decoration: none;
}

/* Statusvarianten, zelfde opzet als --danger: lichte achtergrond, gekleurde
   tekst, zachte rand; op hover een getinte achtergrond en vollere randkleur.
   De tekstkleuren zijn dezelfde tinten die de pills en alerts verderop in dit
   bestand gebruiken, zodat groen, amber en blauw overal hetzelfde spreken. */
.ui-btn--success,
.admin-btn--success {
    background: var(--surface-color);
    color: #065f46;
    border-color: #a7f3d0;
}

.ui-btn--success:hover,
.admin-btn--success:hover {
    background: #ecfdf5;
    border-color: var(--success-color);
    color: #065f46;
    text-decoration: none;
}

.ui-btn--warning,
.admin-btn--warning {
    background: var(--surface-color);
    color: #92400e;
    border-color: #fde68a;
}

.ui-btn--warning:hover,
.admin-btn--warning:hover {
    background: #fffbeb;
    border-color: var(--warning-color);
    color: #92400e;
    text-decoration: none;
}

.ui-btn--info,
.admin-btn--info {
    background: var(--surface-color);
    color: #1e40af;
    border-color: #bfdbfe;
}

.ui-btn--info:hover,
.admin-btn--info:hover {
    background: #eff6ff;
    border-color: var(--info-color);
    color: #1e40af;
    text-decoration: none;
}

/* Actieve staat voor keuzegroepen. Twee ingangen: een Bootstrap btn-check-radio
   die aan een ui-btn-label gekoppeld is, en de klasse .is-active die je zelf
   zet (Alpine, Vue of los JS). Beide zien er in ruststand uit als --primary,
   zodat de gekozen optie in elke groep hetzelfde spreekt. */
.btn-check:checked + .ui-btn,
.btn-check:checked + .admin-btn,
.ui-btn.is-active,
.admin-btn.is-active {
    background: var(--primary-color);
    color: #fff;
    border-color: var(--primary-color);
    box-shadow: var(--shadow-xs);
}

.btn-check:checked + .ui-btn:hover,
.btn-check:checked + .admin-btn:hover,
.ui-btn.is-active:hover,
.admin-btn.is-active:hover {
    background: var(--primary-dark);
    border-color: var(--primary-dark);
    color: #fff;
    text-decoration: none;
}

.btn-check:checked + .ui-btn i,
.btn-check:checked + .admin-btn i,
.ui-btn.is-active i,
.admin-btn.is-active i {
    color: #fff;
}

.ui-btn--sm,
.admin-btn--sm {
    padding: 0.35rem 0.7rem;
    font-size: 0.78rem;
}

.ui-btn--lg,
.admin-btn--lg {
    padding: 0.7rem 1.4rem;
    font-size: 0.95rem;
}

.ui-btn i,
.admin-btn i { font-size: 0.9rem; }

.ui-btn:disabled,
.ui-btn[disabled],
.admin-btn:disabled,
.admin-btn[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
    box-shadow: none;
    pointer-events: none;
}

/* ============================================================
   TABLE
   ============================================================ */
.ui-table,
.admin-table {
    width: 100%;
    border-collapse: collapse;
    font-family: var(--font-primary);
    font-size: 0.88rem;
    overflow-anchor: none;
}

.ui-table tbody,
.admin-table tbody { overflow-anchor: none; }
.ui-table tr,
.admin-table tr { overflow-anchor: none; }

.ui-table thead th,
.admin-table thead th {
    text-align: left;
    padding: 0.85rem 1.4rem;
    background: #fafbfc;
    border-bottom: 1px solid var(--border-color);
    font-family: var(--font-primary);
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.ui-table tbody td,
.admin-table tbody td {
    padding: 0.95rem 1.4rem;
    border-bottom: 1px solid var(--border-color);
    color: var(--text-primary);
    vertical-align: middle;
}

.ui-table tbody tr:last-child td,
.admin-table tbody tr:last-child td { border-bottom: none; }

/* ------------------------------------------------------------
   Responsive horizontaal scroll voor tables
   ------------------------------------------------------------
   Expliciete utility-class + automatische fallback via :has()
   zodat elke admin-card met een directe tabel automatisch
   horizontaal scrollt op smalle schermen — geen inline styles.
*/
.ui-table-wrap,
.admin-table-wrap {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.ui-table-wrap .ui-table,
.ui-table-wrap .admin-table,
.admin-table-wrap .ui-table,
.admin-table-wrap .admin-table {
    margin-bottom: 0;
}

/* Auto-scroll: elke card-body met een directe tabel-kind */
.ui-card__body:has(> .ui-table),
.ui-card__body:has(> .admin-table),
.admin-card__body:has(> .ui-table),
.admin-card__body:has(> .admin-table) {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding: 0;
}

/* Voorkom dat flex/grid-parents de card laten krimpen beneden tabel-breedte */
.ui-card,
.admin-card {
    min-width: 0;
}

.ui-table tbody tr,
.admin-table tbody tr {
    transition: background var(--transition-fast);
}

.ui-table tbody tr:hover,
.admin-table tbody tr:hover {
    background: #f9fafb;
}

.ui-table__name,
.admin-table__name {
    font-family: var(--font-display);
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-primary);
    line-height: 1.2;
    display: block;
}

.ui-table__sub,
.admin-table__sub {
    font-size: 0.76rem;
    color: var(--text-secondary);
    margin-top: 0.15rem;
}

.ui-table__actions,
.admin-table__actions {
    display: flex;
    gap: 0.35rem;
    justify-content: flex-end;
}

/* Klikbare rijen voor drill-down */
.ui-table--clickable tbody tr,
.admin-table--clickable tbody tr {
    cursor: pointer;
}
.ui-table--clickable tbody tr:hover,
.admin-table--clickable tbody tr:hover {
    background: #eff6ff;
}
.ui-table--clickable tbody tr:hover .ui-table__chevron,
.ui-table--clickable tbody tr:hover .admin-table__chevron,
.admin-table--clickable tbody tr:hover .ui-table__chevron,
.admin-table--clickable tbody tr:hover .admin-table__chevron {
    color: var(--primary-color);
    transform: translateX(2px);
}
.ui-table__chevron,
.admin-table__chevron {
    color: #9ca3af;
    transition: color var(--transition-fast), transform var(--transition-fast);
}

/* ============================================================
   PILLS / BADGES
   ============================================================ */
.ui-pill,
.admin-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    border: 1px solid transparent;
}

.ui-pill--success,  .admin-pill--success { background: #d1fae5; color: #065f46; border-color: #a7f3d0; }
.ui-pill--info,     .admin-pill--info    { background: #dbeafe; color: #1e40af; border-color: #bfdbfe; }
.ui-pill--warn,     .admin-pill--warn    { background: #fef3c7; color: #92400e; border-color: #fde68a; }
.ui-pill--danger,   .admin-pill--danger  { background: #fee2e2; color: #991b1b; border-color: #fecaca; }
.ui-pill--neutral,  .admin-pill--neutral { background: #f3f4f6; color: #4b5563; border-color: #e5e7eb; }

/* ============================================================
   CHIP TOGGLES + GRADE TAG
   Gedeeld filter-idioom (jaargroep-chips in beoordelen en
   doelenplanning), afgeleid van .vg-chip in voortgang.css.
   Markup: <div class="ui-chip-row"><button type="button"
   class="ui-chip-toggle is-active" aria-pressed="true">Groep 8
   <span class="ui-chip-toggle__count">148</span></button></div>
   ============================================================ */
.ui-chip-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.ui-chip-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border-radius: 999px;
    border: 1px solid var(--border-color);
    background: #f9fafb;
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    transition: all var(--transition-fast);
}

.ui-chip-toggle:hover { border-color: var(--primary-light); color: var(--text-primary); }
.ui-chip-toggle:focus-visible { outline: 2px solid var(--primary-color); outline-offset: 2px; }
.ui-chip-toggle:disabled { opacity: 0.5; cursor: not-allowed; }

.ui-chip-toggle.is-active {
    background: #eff6ff;
    border-color: var(--primary-light);
    color: var(--primary-dark);
}

/* Eigen jaargroep: bullseye als merkicoon, tooltip via title="Jouw groep" */
.ui-chip-toggle--own .fa-bullseye { color: var(--primary-color); font-size: 0.7rem; }

.ui-chip-toggle__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5em;
    padding: 0 0.4em;
    border-radius: 999px;
    background: #e5e7eb;
    color: #4b5563;
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1.5;
}

.ui-chip-toggle.is-active .ui-chip-toggle__count { background: #dbeafe; color: #1e40af; }

/* Klein jaargroep-label op een doel dat niet van de eigen jaargroep is */
.ui-grade-tag {
    display: inline-flex;
    align-items: center;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    border: 1px solid #e5e7eb;
    background: #f3f4f6;
    color: #4b5563;
    font-size: 0.7rem;
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
    vertical-align: middle;
}

/* ============================================================
   TABS — KlasVizier-stijl, Alpine-driven
   ============================================================ */
.ui-tabs,
.admin-tabs {
    display: flex;
    gap: 0.25rem;
    background: var(--surface-color);
    border: 1px solid var(--border-color);
    border-radius: var(--border-radius-lg);
    padding: 0.4rem;
    margin-bottom: 1.5rem;
    box-shadow: var(--shadow-xs);
    overflow-x: auto;
}

.ui-tab,
.admin-tab {
    flex: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.7rem 1rem;
    border-radius: var(--border-radius);
    color: var(--text-secondary);
    background: transparent;
    border: 0;
    font-family: var(--font-primary);
    font-size: 0.88rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: all var(--transition-fast);
    white-space: nowrap;
    min-width: max-content;
}

.ui-tab:hover,
.admin-tab:hover {
    background: #f3f4f6;
    color: var(--text-primary);
    text-decoration: none;
}

.ui-tab.is-active,
.admin-tab.is-active {
    background: var(--admin-accent-soft, #eff6ff);
    color: var(--admin-accent-dark, var(--primary-dark));
}

.ui-tab.is-active i,
.admin-tab.is-active i { color: var(--admin-accent, var(--primary-color)); }

.ui-tab i,
.admin-tab i {
    font-size: 0.92rem;
    color: var(--text-muted);
}

.ui-tab__count,
.admin-tab__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 0.45rem;
    background: #f3f4f6;
    color: var(--text-secondary);
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 700;
}

.ui-tab.is-active .ui-tab__count,
.ui-tab.is-active .admin-tab__count,
.admin-tab.is-active .ui-tab__count,
.admin-tab.is-active .admin-tab__count {
    background: var(--admin-accent, var(--primary-color));
    color: #fff;
}

@media (max-width: 767.98px) {
    .ui-tabs,
    .admin-tabs { flex-wrap: wrap; }
    .ui-tab,
    .admin-tab { flex: 1 1 calc(50% - 0.25rem); }
}

/* ============================================================
   FORMS
   ============================================================ */
.ui-form-grid,
.admin-form-grid {
    display: grid;
    gap: 1rem;
}

.ui-form-grid--2col,
.admin-form-grid--2col {
    grid-template-columns: repeat(2, 1fr);
}

.ui-form-row,
.admin-form-row {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

/* ui-form-row--fill — laat een form-row de volle beschikbare hoogte pakken
   binnen een offcanvas-body. Bedoeld voor wizard-stappen of panels met een
   dominante lijst die moet meeademen met dynamische geselecteerd-panelen of
   status-blokken eronder.

   Gedrag:
   - De form-grid die deze row bevat krijgt `align-content: stretch` zodat
     de row verticaal mag groeien.
   - De row zelf pakt `flex: 1` met `min-height: 0` (cruciaal voor nested scroll).
   - Elke `.ui-scrollable-list` binnen de row verliest z'n max-height cap en
     wordt zelf de flex-child die de rest van de ruimte opvult. Zo krimpt de
     lijst automatisch als er onder 'm iets verschijnt (bijv. een
     "Geselecteerd"-box) en groeit hij terug als dat weer verdwijnt. */
.ui-offcanvas__body .ui-form-grid:has(> .ui-form-row--fill),
.admin-offcanvas__body .ui-form-grid:has(> .ui-form-row--fill) {
    align-content: stretch;
}
.ui-form-row--fill {
    flex: 1;
    min-height: 0;
}
.ui-form-row--fill .ui-scrollable-list {
    flex: 1;
    min-height: 0;
    max-height: none;
}

.ui-form-label,
.admin-form-label {
    font-family: var(--font-primary);
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-primary);
    display: flex;
    align-items: center;
    gap: 0.35rem;
}

.ui-form-label .req,
.admin-form-label .req {
    color: var(--danger-color);
    font-weight: 700;
}

.ui-form-input,
.ui-form-textarea,
.ui-form-select,
.admin-form-input,
.admin-form-textarea,
.admin-form-select {
    width: 100%;
    padding: 0.6rem 0.85rem;
    border: 1px solid var(--border-color);
    border-radius: var(--border-radius);
    background: var(--surface-color);
    color: var(--text-primary);
    font-family: var(--font-primary);
    font-size: 0.9rem;
    line-height: 1.4;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.ui-form-input:focus,
.ui-form-textarea:focus,
.ui-form-select:focus,
.admin-form-input:focus,
.admin-form-textarea:focus,
.admin-form-select:focus {
    outline: none;
    border-color: var(--primary-light);
    box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12);
}

.ui-form-textarea,
.admin-form-textarea {
    resize: vertical;
    min-height: 90px;
}

.ui-form-help,
.admin-form-help {
    font-size: 0.78rem;
    color: var(--text-secondary);
}

.ui-form-error,
.admin-form-error {
    font-size: 0.78rem;
    color: var(--danger-color);
    font-weight: 500;
}

.ui-form-actions,
.admin-form-actions {
    display: flex;
    gap: 0.55rem;
    align-items: center;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border-color);
}

@media (max-width: 575.98px) {
    .ui-form-grid--2col,
    .admin-form-grid--2col { grid-template-columns: 1fr; }
}

/* ============================================================
   ALERTS
   ============================================================ */
.ui-alert,
.admin-alert {
    display: flex;
    align-items: flex-start;
    gap: 0.7rem;
    padding: 0.85rem 1rem;
    border-radius: var(--border-radius-lg);
    border: 1px solid transparent;
    font-size: 0.88rem;
    line-height: 1.5;
    margin-bottom: 1rem;
}

.ui-alert__icon,
.admin-alert__icon {
    font-size: 1rem;
    flex-shrink: 0;
    margin-top: 0.15rem;
}

.ui-alert__body,
.admin-alert__body {
    flex: 1;
    color: inherit;
}

.ui-alert__close,
.admin-alert__close {
    background: transparent;
    border: 0;
    color: inherit;
    opacity: 0.6;
    cursor: pointer;
    padding: 0.15rem 0.35rem;
    border-radius: var(--border-radius-sm);
    transition: opacity var(--transition-fast);
}

.ui-alert__close:hover,
.admin-alert__close:hover { opacity: 1; }

.ui-alert--success, .admin-alert--success { background: #d1fae5; color: #065f46; border-color: #a7f3d0; }
.ui-alert--info,    .admin-alert--info    { background: #dbeafe; color: #1e40af; border-color: #bfdbfe; }
.ui-alert--warn,    .admin-alert--warn    { background: #fef3c7; color: #92400e; border-color: #fde68a; }
.ui-alert--danger,  .admin-alert--danger  { background: #fee2e2; color: #991b1b; border-color: #fecaca; }

/* ============================================================
   EMPTY STATE
   ============================================================ */
.ui-empty-state,
.admin-empty-state {
    text-align: center;
    padding: 3rem 1.5rem;
    background: var(--surface-color);
    border: 2px dashed var(--border-color);
    border-radius: var(--border-radius-lg);
}

.ui-empty-state__icon,
.admin-empty-state__icon {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: var(--admin-accent-soft, #eff6ff);
    color: var(--admin-accent-dark, var(--primary-dark));
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.6rem;
    margin-bottom: 1rem;
}

.ui-empty-state__title,
.admin-empty-state__title {
    font-family: var(--font-display);
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--text-primary);
    margin: 0 0 0.4rem;
}

.ui-empty-state__desc,
.admin-empty-state__desc {
    color: var(--text-secondary);
    font-size: 0.92rem;
    max-width: 50ch;
    margin: 0 auto 1.25rem;
    line-height: 1.5;
}

/* ============================================================
   ACTION BUTTONS (table row actions)
   ============================================================ */
.ui-action-buttons,
.admin-action-buttons {
    display: inline-flex;
    gap: 0.3rem;
    align-items: center;
    justify-content: flex-end;
}

.ui-action-btn,
.admin-action-btn {
    padding: 0.4rem 0.55rem;
    min-width: auto;
}

/* ============================================================
   GRID HELPERS
   ============================================================ */
.ui-grid-2,
.admin-grid-2 {
    display: grid;
    grid-template-columns: 1.5fr 1fr;
    gap: 1.25rem;
}

.ui-grid-3,
.admin-grid-3 {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1.25rem;
}

.ui-section-spacer,
.admin-section-spacer { margin-bottom: 1.75rem; }

.ui-quick-actions,
.admin-quick-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

@media (max-width: 991.98px) {
    .ui-grid-2,
    .ui-grid-3,
    .admin-grid-2,
    .admin-grid-3 { grid-template-columns: 1fr; }
}

/* ============================================================
   PAGINASKELET LEERKRACHT-APP (PLAN-LAYOUT-CONSISTENT.md, fase 0)
   ------------------------------------------------------------
   Eén opbouw voor elke leerkrachtpagina, afgeleid van de maatstaf
   (weektaak-detailoverzicht). Alleen ui-*-klassen, geen admin-*:
   de beheer-shell heeft zijn eigen opbouw.

   Opbouw van een pagina:
     <div class="ui-page">                  wortel, size-container
       <div class="ui-toolbar">             werkbalk: titel, status, kerncijfers links, knoppen rechts
         <span class="ui-toolbar__title">
         <span class="ui-toolbar__meta">
         <div class="ui-toolbar__actions">  knoplabels in <span class="ui-btn__label">
       <div class="ui-subbar">              optionele tweede balk (weeknavigatie, filters)
       <div class="ui-panel">               de inhoudskaart
       <div class="ui-empty-state">         de enige lege stand
       <div class="ui-loading">             de enige laadstand
       <div class="ui-actionbar">           optionele sticky balk onderaan (opslaan, verder)

   Overlays (ui-offcanvas, ui-modal) staan BUITEN .ui-page: de
   size-container kan in sommige browsers een containing block worden
   voor position: fixed, en dan zou een lade aan de pagina vastzitten.

   Geen kopjes (h1/h2) in de inhoud: de topbalk draagt de paginatitel.
   ============================================================ */

/* --- Tokens: het kaartrecept en de vaste maten van de maatstaf --- */
:root {
    --kv-card-radius: 12px;
    --kv-card-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
    --kv-card-border: 1px solid var(--border-color);
    --kv-bar-gap: 6px;        /* werkbalk naar subbalk */
    --kv-block-gap: 8px;      /* tussen de blokken eronder */
    --kv-page-inset: 24px;    /* inzet van de pagina; nu nog de padding van .container-fluid */
    /* --kv-topbar-h (68px) staat in header.css */

    /* Maatschaal van de leerkracht-app (ronde 3). Gebruik deze tokens in
       pagina-CSS in plaats van eigen rem-waarden; nergens kleiner dan xs. */
    --kv-fs-xs: 0.6875rem;    /* 11px: kolomkoppen, bijschriften, chips in een matrix */
    --kv-fs-sm: 0.75rem;      /* 12px: meta, pills, hulptekst, formulierlabels */
    --kv-fs-base: 0.8125rem;  /* 13px: gewone tekst, tabelcellen, invoervelden, knoplabels */
    --kv-fs-md: 0.875rem;     /* 14px: namen in lijsten en matrices, kaartkoppen */
    --kv-fs-title: 1rem;      /* 16px: alleen de werkbalktitel en de titel van een lege stand */
    --kv-btn-h: 28px;         /* hoogte van een knop (ui-btn), ook de icoonknop van 28x28 */
    --kv-control-h: 32px;     /* hoogte van invoervelden en selects */
}

/* --- Wortel ---
   Size-container, zodat werkbalken op hun eigen breedte reageren (de
   zijbalk van 240 of 60px en Windows-schaling tellen vanzelf mee).
   Geen eigen padding: de inzet komt van .container-fluid. */
.ui-page {
    container-type: inline-size;
    min-width: 0;
}

/* --- Werkbalk en subbalk: het kaartrecept --- */
.ui-toolbar,
.ui-subbar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    background: var(--surface-color);
    border: var(--kv-card-border);
    border-radius: var(--kv-card-radius);
    box-shadow: var(--kv-card-shadow);
}

/* De werkbalk is één regel: titel en status links mogen krimpen en omlopen,
   de knoppen rechts houden hun breedte. Onder 1024px vouwt hij wel om, zodat
   de knoppen altijd zichtbaar blijven (UI-wet 1). */
.ui-toolbar {
    flex-wrap: nowrap;
    min-height: 50px;
}

/* De subbalk (weeknavigatie, filters) mag altijd omvouwen: daar staan vaak
   meer groepjes naast elkaar dan op één regel passen. Geen min-hoogte, hij
   is de smallere balk. */
.ui-subbar {
    flex-wrap: wrap;
    margin-top: var(--kv-bar-gap);
}

/* Titel: mag omlopen, wordt nooit afgekapt (UI-wet 4). */
.ui-toolbar__title {
    min-width: 0;
    font-family: var(--font-display);
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.3;
    color: var(--text-primary);
    overflow-wrap: break-word;
}

/* Kerncijfers en status naast de titel: compacte tekst, geen kaart */
.ui-toolbar__meta {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--text-secondary);
    white-space: nowrap;
}

.ui-toolbar__meta i {
    color: var(--primary-color);
    font-size: 0.8rem;
}

.ui-toolbar__actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 0 0 auto;
    margin-left: auto;
}

/* Voor pagina's met veel filters in de werkbalk: altijd omvouwen. */
.ui-toolbar--wrap {
    flex-wrap: wrap;
    row-gap: 6px;
}

@media (max-width: 1023.98px) {
    .ui-toolbar {
        flex-wrap: wrap;
        row-gap: 6px;
    }
}

/* Plakkende werkbalk voor pagina's die zelf scrollen. Hij sluit precies aan
   op de topbalk; het ::before-vlak dekt de strook erboven af in de
   paginakleur, zodat de inhoud daar niet zichtbaar onderdoor schuift (zelfde
   aanpak als de filterbalken op beoordelen en cijfers). z-index 20: boven de
   inhoud, ruim onder de topbalk (1040), de zijbalk (1045) en de lades (1200).
   De werkbalk staat bovenaan de pagina; het vlak dekt 90px erboven af. */
.ui-toolbar--sticky {
    position: sticky;
    top: var(--kv-topbar-h, 68px);
    z-index: 20;
}

.ui-toolbar--sticky::before {
    content: '';
    position: absolute;
    left: -4px;
    right: -4px;
    /* +1px: bottom 100% rekent vanaf de binnenkant van de rand en zou de
       bovenrand van de balk afdekken. */
    bottom: calc(100% + 1px);
    height: 90px;
    background: var(--surface-color);
    pointer-events: none;
}

@media (max-width: 767.98px) {
    .ui-toolbar--sticky {
        position: static;
    }

    .ui-toolbar--sticky::before {
        display: none;
    }
}

/* Knoplabels inklappen is opt-in (alles past altijd, UI-wet 1): alleen een
   werkbalk of subbalk met veel knoppen krijgt --labels-auto. Een balk met
   een paar knoppen houdt zijn labels; daar is ruimte genoeg.
   Met de modifier gaat het in twee stappen, net als de maatstaf: onder 1180px
   containerbreedte verdwijnen de labels in de werkbalk, behalve die met
   .ui-btn__label--keep (de hoofdactie); onder 860px alle labels, ook in een
   subbalk met --labels-auto. Het icoon blijft; geef de knop daarom altijd een
   title of aria-label. */
@container (max-width: 1180px) {
    .ui-toolbar--labels-auto .ui-btn__label:not(.ui-btn__label--keep) {
        display: none;
    }
}

@container (max-width: 860px) {
    .ui-toolbar--labels-auto .ui-btn__label,
    .ui-subbar--labels-auto .ui-btn__label {
        display: none;
    }
}

/* --- Inhoudskaart ---
   isolation: isolate houdt de z-index van alles binnenin (plakkende
   tabelkoppen, vaste kolommen) binnen de kaart, zodat een ui-dropdown uit
   de werkbalk er altijd overheen valt. */
.ui-panel {
    margin-top: var(--kv-block-gap);
    background: var(--surface-color);
    border: var(--kv-card-border);
    border-radius: var(--kv-card-radius);
    box-shadow: var(--kv-card-shadow);
    overflow: hidden;
    isolation: isolate;
}

/* Eigen scrollvak, alleen voor een matrix met vaste kop en vaste eerste
   kolom. De pagina zet --ui-panel-max-h (meestal vanuit JS: de resterende
   hoogte van het venster); zonder waarde groeit de kaart gewoon mee. */
.ui-panel--scroll {
    max-height: var(--ui-panel-max-h, none);
    overflow: auto;
    overscroll-behavior: contain;
}

/* Lege stand en laadstand op paginaniveau houden dezelfde afstand als een
   inhoudskaart. */
.ui-page > .ui-empty-state,
.ui-page > .ui-loading {
    margin-top: var(--kv-block-gap);
}

/* --- Lege stand ---
   .ui-empty-state staat hierboven (sectie EMPTY STATE). In de leerkracht-app
   (alles binnen .main-container, de schil van templates/header.php) volgt hij
   het kaartrecept in plaats van de gestippelde rand. De beheer-shell heeft
   geen .main-container en houdt de gestippelde rand, ook voor de
   ui-empty-states die ui_empty_state() daar rendert. */
.main-container .ui-empty-state {
    border: var(--kv-card-border);
    border-radius: var(--kv-card-radius);
    box-shadow: var(--kv-card-shadow);
}

/* Geen extra witruimte onder de tekst als er geen knop meer volgt */
.main-container .ui-empty-state__desc:last-child {
    margin-bottom: 0;
}

/* --- Laadstand ---
   Markup: <div class="ui-loading" role="status"><i class="fas fa-circle-notch
   fa-spin" aria-hidden="true"></i><span>Laden...</span></div> */
.ui-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    padding: 3rem;
    color: var(--text-secondary);
    font-size: 0.9rem;
    font-weight: 600;
    text-align: center;
}

.ui-loading i {
    color: var(--primary-color);
    font-size: 1.1rem;
}

/* --- Actiebalk onderaan ---
   Voor pagina's met een opslaan- of verderknop. Kaartrecept zonder ronding
   onderaan; de schaduw is die van de kaart, maar naar boven gericht. */
.ui-actionbar {
    position: sticky;
    bottom: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    min-height: 48px;
    margin-top: var(--kv-block-gap);
    padding: 6px 12px;
    background: var(--surface-color);
    border: var(--kv-card-border);
    border-bottom: 0;
    border-radius: var(--kv-card-radius) var(--kv-card-radius) 0 0;
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.08);
}

/* ============================================================
   MAATSCHAAL LEERKRACHT-APP (PLAN-LAYOUT-CONSISTENT.md, ronde 3)
   ------------------------------------------------------------
   Eén compacte maat voor knoppen, invoer, tabs, tabellen en lege
   standen in de leerkracht-app; de tokens staan bij :root hierboven.

   Alles hieronder dat een bestaande klasse aanpast, staat onder
   :where(.main-container). Alleen de leerkracht-shell
   (templates/header.php) heeft die wrapper, dus de beheer-shell, de
   loginpagina's en de dialogen van showConfirmDialog (die in <body>
   hangen) houden hun maten.

   Waarom :where(): de specificiteit blijft gelijk aan die van de
   klasse zelf (.ui-btn telt als 0,1,0). Daardoor
   - wint deze regel van de basisregel en van --sm en --lg hierboven,
     omdat hij later in dit bestand staat;
   - winnen pagina-CSS (laadt na dit bestand) en de aanraakregel in
     mobile-responsive.css (min-height 44px bij pointer: coarse) nog
     steeds, net als voor deze ronde. Eigen knop- en invoermaten op
     pagina's ruimen de pagina's zelf op.

   De nieuwe klassen (ui-panel__title, ui-filter-row, ui-panel--table,
   ui-tabs--many, ui-tabs__select) bestaan alleen in de leerkracht-app
   en staan daarom zonder scope, net als de rest van het skelet.
   ============================================================ */

/* --- Knoppen ---
   ui-btn zonder modifier is de compacte knop van 28px (letter 0.78rem).
   ui-btn--sm blijft bestaan voor bestaande markup en is hier gelijk aan de
   standaard; zet hem niet meer bij. ui-btn--lg is de oude standaardmaat
   (36px), alleen voor de ene hoofdknop in een lege stand. */
:where(.main-container) .ui-btn,
:where(.main-container) .ui-btn--sm {
    padding: 0.35rem 0.7rem;
    font-size: 0.78rem;
    line-height: 1.2;
    min-height: var(--kv-btn-h);
}

:where(.main-container) .ui-btn--lg {
    padding: 0.55rem 1.1rem;
    font-size: 0.875rem;
}

/* Knop met alleen een icoon: vierkant van 28 bij 28. Geef hem altijd een
   title of aria-label. Op een aanraakscherm maakt mobile-responsive.css elke
   knop 44px hoog; de breedte gaat dan mee, zodat hij vierkant blijft. */
:where(.main-container) .ui-btn--icon {
    justify-content: center;
    gap: 0;
    width: var(--kv-btn-h);
    height: var(--kv-btn-h);
    padding: 0;
    flex-shrink: 0;
}

@media (pointer: coarse) {
    :where(.main-container) .ui-btn--icon {
        width: var(--mobile-touch-target, 44px);
        height: var(--mobile-touch-target, 44px);
    }
}

/* --- Formulieren ---
   Invoer en select 32px met letter 13px, label en hulptekst 12px. De
   textarea houdt de min-height van 90px uit de basisregel. Zoekvelden in een
   werkbalk zijn gewoon een ui-form-input en dus ook 32px. */
:where(.main-container) .ui-form-input,
:where(.main-container) .ui-form-select,
:where(.main-container) .ui-form-textarea {
    padding: 0.35rem 0.6rem;
    font-size: var(--kv-fs-base);
}

:where(.main-container) .ui-form-input,
:where(.main-container) .ui-form-select {
    min-height: var(--kv-control-h);
}

:where(.main-container) .ui-form-label,
:where(.main-container) .ui-form-help {
    font-size: var(--kv-fs-sm);
}

/* --- Tabs ---
   Een tab is even hoog als een knop (28px), ook met een telling erin. De
   hoogte komt uit min-height en align-items center, niet uit verticale
   padding: met 0.4rem padding zou een tab met een telling van 18px op 31px
   uitkomen. Zonder telling geeft de regelhoogte van 1.2 ruim 15px tekst. */
:where(.main-container) .ui-tabs {
    padding: 0.3rem;
}

:where(.main-container) .ui-tab {
    min-height: var(--kv-btn-h);
    padding: 0 0.8rem;
    font-size: 0.8rem;
    line-height: 1.2;
}

:where(.main-container) .ui-tab__count {
    min-width: 18px;
    height: 18px;
    padding: 0 0.35rem;
    font-size: var(--kv-fs-xs);
}

/* Meer dan vier tabs (telefoonregel 2): op telefoon een keuzelijst met
   dezelfde keuzes in plaats van de tabs. Tot en met vier tabs is niets nodig,
   die staan op telefoon twee per regel (sectie TABS hierboven).
   Markup: <div class="ui-tabs ui-tabs--many">...</div> en ernaast
   <select class="ui-form-select ui-tabs__select" aria-label="...">. */
.ui-tabs__select {
    display: none;
}

/* --- Tabel ---
   Alleen de lettermaat; kleuren, randen en padding blijven. Kolomkoppen in
   thead 11px 700 in hoofdletters, cellen 13px, namen 14px. Een rijkop
   (th scope="row" in tbody, zoals de naamkolom van cijfers) erft de maat van
   de tabel en krijgt geen hoofdletters. */
:where(.main-container) .ui-table thead th {
    font-size: var(--kv-fs-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

:where(.main-container) .ui-table tbody td {
    font-size: var(--kv-fs-base);
}

:where(.main-container) .ui-table__name {
    font-size: var(--kv-fs-md);
}

:where(.main-container) .ui-table__sub {
    font-size: var(--kv-fs-sm);
}

/* --- Lege stand: titel 16px, uitleg 13px. De ene knop erin mag
   ui-btn--lg zijn. */
:where(.main-container) .ui-empty-state__title {
    font-size: var(--kv-fs-title);
}

:where(.main-container) .ui-empty-state__desc {
    font-size: var(--kv-fs-base);
}

/* --- Kaartkop in een inhoudskaart ---
   De variant van .ui-card__title (sectie CARDS) voor het paginaskelet:
   14px 700 in plaats van 16,8px 600, geen grijze kopbalk maar een lijn
   eronder. Mag een h2 of h3 zijn; de marges van Bootstrap gaan eraf.
   Markup: <h3 class="ui-panel__title"><i class="fas fa-book"></i> Vakken
           <span class="ui-panel__title-actions">knoppen</span></h3>
   Past de tekst niet naast de knoppen, dan vouwt de kop om; de tekst wordt
   nooit afgekapt (UI-wet 4). Alleen het directe icoon wordt blauw, niet de
   iconen in de knoppen. */
.ui-panel__title {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin: 0;
    padding: 10px 14px;
    border-bottom: var(--kv-card-border);
    font-family: var(--font-display);
    font-size: var(--kv-fs-md);
    font-weight: 700;
    line-height: 1.3;
    color: var(--text-primary);
    overflow-wrap: break-word;
}

.ui-panel__title > i {
    color: var(--primary-color);
    font-size: 0.85rem;
}

.ui-panel__title-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-left: auto;
}

/* --- Filterrij (telefoonregel 3) ---
   Selects en zoekvelden naast elkaar; op desktop op één regel (vouwt om als
   het niet past), op telefoon in twee kolommen. Zet het label als eerste
   optie ("Alle vakken") in plaats van een los label ervoor. */
.ui-filter-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

.ui-filter-row > .ui-form-select,
.ui-filter-row > .ui-form-input {
    width: auto;
    min-width: 140px;
    flex: 1 1 140px;
}

/* --- Tabelkaart (telefoonregel 4) ---
   Een tabel die breder is dan de kaart scrolt horizontaal binnen de kaart in
   plaats van afgekapt te worden. Deze klasse levert alleen de
   scrollcontainer; of de tabel een minimale breedte heeft, bepaalt de pagina.
   Een vaste naamkolom komt ook van de pagina zelf, met position: sticky;
   left: 0 op de eerste cel (zoals cijfers). Let op: een plakkende tabelkop
   plakt in deze kaart niet meer aan het venster, want de kaart is nu zelf de
   scrollcontainer. */
.ui-panel--table {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* --- Telefoon (tot 768px) --- */
@media (max-width: 767.98px) {
    /* Werkbalk en subbalk (telefoonregel 1): minder binnenruimte, titel 15px.
       De knoppen krijgen een eigen regel over de volle breedte en vouwen
       daar om, zodat ze nooit buiten de kaart vallen (UI-wet 1 en 4). */
    :where(.main-container) .ui-toolbar,
    :where(.main-container) .ui-subbar {
        padding: 8px 10px;
        gap: 6px;
    }

    :where(.main-container) .ui-toolbar__title {
        font-size: 0.9375rem;
    }

    :where(.main-container) .ui-toolbar__actions {
        flex-wrap: wrap;
        margin-left: 0;
        width: 100%;
    }

    /* Tabs als keuzelijst (telefoonregel 2) */
    .ui-tabs--many {
        display: none;
    }

    .ui-tabs__select {
        display: block;
        margin-bottom: 1.5rem;
    }

    /* Filterrij in twee kolommen (telefoonregel 3). minmax(0, 1fr) in plaats
       van 1fr, zodat een lange optietekst de kolom niet breder duwt dan het
       scherm. */
    .ui-filter-row {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: 6px;
    }

    .ui-filter-row > .ui-form-select,
    .ui-filter-row > .ui-form-input {
        width: 100%;
        min-width: 0;
    }
}

/* ============================================================
   KIEZER (ui-kiezer) (PLAN-LAYOUT-CONSISTENT.md, ronde 5)
   ------------------------------------------------------------
   Eén dropdown voor vak, jaargroep en periode: een witte knop met
   een vaste grijze voortekst ("Vak"), de gekozen waarde en een
   pijltje; een klik opent een lijst links onder de knop. Overgenomen
   uit cijfers (daar heette het cf-keuze*), dat hem nu ook gebruikt.

   Markup:
   <div class="ui-dropdown ui-kiezer">
     <button type="button" class="ui-kiezer__knop" aria-haspopup="listbox" aria-expanded="false">
       <span class="ui-kiezer__label">Vak</span>
       <span class="ui-kiezer__stip" style="background: #f59e0b"></span>   (optioneel)
       <span class="ui-kiezer__waarde">Rekenen</span>
       <i class="fas fa-chevron-down ui-kiezer__pijl" aria-hidden="true"></i>
     </button>
     <div class="ui-dropdown__menu ui-kiezer__lijst" role="listbox">   (.is-twee boven 8, .is-lang boven 30)
       <div class="ui-kiezer__kolommen" style="--ui-kiezer-rijen: 6">
         <button type="button" class="ui-dropdown__item ui-kiezer__item is-gekozen" role="option" aria-selected="true">
           <i class="fas fa-check ui-kiezer__vink" aria-hidden="true"></i>
           <span class="ui-kiezer__stip"></span>                               (optioneel)
           <i class="fas fa-bullseye ui-kiezer__eigen" aria-hidden="true"></i> (optioneel)
           <span class="ui-kiezer__naam">Rekenen</span>
           <span class="ui-kiezer__aantal">12</span>                           (optioneel)
         </button>
       </div>
       <div class="ui-dropdown__divider"></div>                                (optioneel)
       <button type="button" class="ui-dropdown__item ui-kiezer__item ui-kiezer__extra">...</button>
     </div>
   </div>

   De helper window.kvKiezer (ui-components.js) rendert dit en regelt
   muis en toetsenbord; cijfers schrijft de markup met de hand (Alpine).
   Meerkeuze (aria-multiselectable op de lijst) ziet er hetzelfde uit:
   elk gekozen item krijgt het vinkje.

   Scope :where(.main-container), net als de maatschaal hierboven: de
   specificiteit blijft die van de klasse, dus pagina-CSS wint nog.
   Waar een basisregel van ui-dropdown__item zwaarder weegt (iconen,
   hover), staat de kiezerregel met een klasse extra.
   ============================================================ */

:where(.main-container) .ui-kiezer {
    position: relative;
}

:where(.main-container) .ui-kiezer [hidden] {
    display: none !important;
}

/* --- Knop ---
   Zelfde maat en vorm als een ui-btn--secondary binnen de leerkracht-app:
   28px hoog, letter 0.78rem. min-height in plaats van height: past een
   lange waarde niet naast de andere knoppen, dan loopt hij om naar een
   tweede regel in plaats van uit de balk te steken (UI-wet 1 en 4). Op
   één regel is de knop precies 28px. */
:where(.main-container) .ui-kiezer__knop {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    max-width: 100%;
    min-height: var(--kv-btn-h, 28px);
    padding: 0.2rem 0.7rem;
    border: 1px solid var(--border-color);
    border-radius: var(--border-radius);
    background: var(--surface-color);
    color: var(--text-primary);
    font-family: var(--font-primary);
    font-size: 0.78rem;
    line-height: 1.2;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
    transition: border-color var(--transition-fast), background var(--transition-fast), box-shadow var(--transition-fast);
}

:where(.main-container) .ui-kiezer__knop:hover {
    background: #f9fafb;
    border-color: var(--primary-light);
}

:where(.main-container) .ui-kiezer__knop.is-open {
    border-color: var(--primary-color);
    box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12);
}

:where(.main-container) .ui-kiezer__knop:focus-visible {
    outline: 2px solid var(--primary-color);
    outline-offset: 2px;
}

:where(.main-container) .ui-kiezer__label {
    font-weight: 500;
    color: var(--text-secondary);
}

:where(.main-container) .ui-kiezer__waarde {
    font-weight: 700;
    color: var(--text-primary);
    white-space: normal;
}

/* Nog niets gekozen (kvKiezer zonder waarde en zonder leeg-item): de
   knoptekst ("Kies een vak", standaard "Geen") grijs en niet vet. */
:where(.main-container) .ui-kiezer__knop.is-leeg .ui-kiezer__waarde {
    font-weight: 500;
    color: var(--text-secondary);
}

:where(.main-container) .ui-kiezer__pijl {
    margin-left: 0.15rem;
    font-size: 0.65rem;
    color: var(--text-secondary);
    transition: transform var(--transition-fast);
}

:where(.main-container) .ui-kiezer__knop:hover .ui-kiezer__pijl,
:where(.main-container) .ui-kiezer__knop.is-open .ui-kiezer__pijl {
    color: var(--primary-color);
}

:where(.main-container) .ui-kiezer__knop.is-open .ui-kiezer__pijl {
    transform: rotate(180deg);
}

/* Kleurstip van een vak, in de knop en in de lijst. De kleur komt inline. */
:where(.main-container) .ui-kiezer__stip {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}

/* --- Lijst ---
   Links onder de knop, zo breed als de langste naam. Geen eigen scroll
   tot 30 items (collega's zien niet dat ze moeten scrollen). Schuift naar
   links als hij rechts buiten het venster valt (kvKiezer en cijfers.js).
   z-index 1000: boven plakkende werkbalken (20), plakkende tabelkoppen en
   de knoppen van het dashboard (100), onder de topbalk (1040), de zijbalk
   (1045) en de lades (1200). In een plakkende werkbalk telt hij binnen
   die balk, en de balk zelf ligt al boven de inhoud. */
:where(.main-container) .ui-kiezer__lijst {
    left: 0;
    right: auto;
    /* Expliciet max-content: de lijst hangt absoluut aan de smalle knop en
       zou anders zijn breedte daarop afstemmen, waardoor lange namen
       uitsteken. */
    width: max-content;
    min-width: 13rem;
    max-width: calc(100vw - 1.5rem);
    z-index: 1000;
}

/* Meer dan 8 items: twee kolommen, kolom 1 van boven naar beneden en dan
   kolom 2 (grid-auto-flow: column met --ui-kiezer-rijen rijen, gezet door
   de markup). Elke kolom minstens 12rem en zo breed als de langste naam.
   Extra acties ("+ Vak") staan buiten dit raster, dus over de volle
   breedte. */
:where(.main-container) .ui-kiezer__lijst.is-twee .ui-kiezer__kolommen {
    display: grid;
    grid-auto-flow: column;
    grid-template-rows: repeat(var(--ui-kiezer-rijen, 1), auto);
    grid-template-columns: repeat(2, minmax(12rem, max-content));
    column-gap: 0.25rem;
}

/* Pas boven 30 items een eigen scroll, binnen 60vh. */
:where(.main-container) .ui-kiezer__lijst.is-lang {
    max-height: 60vh;
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* Te smal voor twee kolommen (telefoon, of een lade): één kolom, en een
   naam die dan nog niet past loopt om in plaats van buiten beeld te
   vallen. Op telefoon via de mediaquery; kvKiezer zet .is-smal ook zelf
   als de lijst niet in zijn venster of lade past. */
:where(.main-container) .ui-kiezer__lijst.is-smal {
    min-width: 0;
}

:where(.main-container) .ui-kiezer__lijst.is-smal .ui-kiezer__kolommen {
    display: block;
}

@media (max-width: 479.98px) {
    :where(.main-container) .ui-kiezer__lijst.is-twee .ui-kiezer__kolommen {
        display: block;
    }
}

/* --- Item --- */
:where(.main-container) .ui-kiezer__item {
    white-space: nowrap;
}

/* Alleen als de lijst smaller is dan de naam loopt die om; in een lijst
   met width: max-content gebeurt dat nooit. */
:where(.main-container) .ui-kiezer__naam {
    white-space: normal;
}

:where(.main-container) .ui-kiezer__item:focus {
    outline: none;
    background: #eff6ff;
}

:where(.main-container) .ui-kiezer__item:focus-visible {
    outline: 2px solid var(--primary-color);
    outline-offset: -2px;
}

/* ui-dropdown__item zet iconen op 1rem breed; het vinkje houdt die plek
   ook als het onzichtbaar is, zodat de namen onder elkaar blijven. */
:where(.main-container) .ui-kiezer__vink {
    visibility: hidden;
}

:where(.main-container) .ui-kiezer__item.is-gekozen {
    font-weight: 700;
    color: var(--primary-dark);
}

:where(.main-container) .ui-kiezer__item.is-gekozen .ui-kiezer__vink {
    visibility: visible;
    color: var(--primary-color);
}

/* Het vizier bij de eigen jaargroep, zoals in .ui-chip-toggle--own. Ook bij
   hover in de primaire kleur (ui-dropdown__item:hover i weegt anders zwaarder). */
:where(.main-container) .ui-kiezer__item .ui-kiezer__eigen,
:where(.main-container) .ui-kiezer__item:hover .ui-kiezer__eigen {
    width: auto;
    color: var(--primary-color);
    font-size: 0.7rem;
}

/* Aantal (of "nu" bij de periode): grijze pil rechts in de regel. */
:where(.main-container) .ui-kiezer__aantal {
    margin-left: auto;
    padding: 0 0.4rem;
    border: 1px solid var(--border-color);
    border-radius: 999px;
    background: #f3f4f6;
    color: var(--text-secondary);
    font-size: var(--kv-fs-xs, 0.6875rem);
    font-weight: 600;
    line-height: 1.5;
}

/* Extra actie onder de streep ("+ Vak"): grijs, ook bij hover. */
:where(.main-container) .ui-kiezer__item.ui-kiezer__extra {
    color: var(--text-secondary);
}
