/**
 * Purpose:    Every reusable UI component: buttons, fields, cards, badges, and
 *             so on.
 * Depends on: tokens.css
 * Used by:    Linked fourth, after layout.css, in views/layout/app.php and
 *             views/layout/guest.php. Every view that uses a component class
 *             depends on this file being loaded.
 * UI rules:   Two rules apply here without exception, see
 *             _docs/design-system.md section 4 for the full explanation:
 *               1. Class selectors only. A rule like "form input { ... }"
 *                  would win over a component class depending on where it's
 *                  nested, making the same field look different in two places.
 *               2. Semantic tokens only (--bg-*, --text-*, --accent-*). No
 *                  color ramp value and no hex code directly in a component
 *                  rule, or dark mode breaks for that component.
 *             New variant added here? It must also appear on /styleguide (see
 *             views/dev/styleguide.php) — see the rule in the project's
 *             CLAUDE.md.
 */

/* ============================================================
   Button
   Eine Familie: .btn + Groesse + Variante. Breite ist NIE
   voreingestellt, volle Breite nur ueber .btn--block.
   ============================================================ */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: var(--control-h);
    padding: 0 var(--space-4);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--text-default);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    line-height: var(--leading-tight);
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

.btn:hover {
    text-decoration: none;
}

.btn:disabled,
.btn.is-disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn--sm {
    min-height: var(--control-h-sm);
    padding: 0 var(--space-3);
    font-size: var(--text-sm);
}

.btn--lg {
    min-height: var(--control-h-lg);
    padding: 0 var(--space-6);
    font-size: var(--text-base);
}

.btn--block {
    width: 100%;
}

.btn--primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
}

.btn--primary:not(:disabled):hover {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
}

.btn--secondary {
    background: var(--bg-surface);
    border-color: var(--border-default);
    color: var(--text-default);
}

.btn--secondary:not(:disabled):hover {
    background: var(--bg-subtle);
    border-color: var(--border-strong);
    color: var(--text-strong);
}

.btn--ghost {
    color: var(--text-muted);
}

.btn--ghost:not(:disabled):hover {
    background: var(--bg-subtle);
    color: var(--text-strong);
}

.btn--danger {
    background: var(--bg-surface);
    border-color: var(--danger-border);
    color: var(--danger-text);
}

.btn--danger:not(:disabled):hover {
    background: var(--danger-bg);
    border-color: var(--danger-text);
}

/* Tertiaer: sieht aus wie ein Link, verhaelt sich wie ein Button (fuer
   <button>-Elemente in Formularen, z.B. Abmelden). */
.btn--link {
    min-height: 0;
    padding: 0;
    background: none;
    border: none;
    color: var(--text-muted);
    font-weight: var(--weight-medium);
}

.btn--link:not(:disabled):hover {
    color: var(--accent-text);
    text-decoration: underline;
}

/* ============================================================
   Segmented Control
   Zusammenhaengende Auswahlgruppe (Rollenumschalter, Theme,
   Typ-/Plan-Auswahl). Ersetzt .admin-type-switch, .button-solid
   und .leitner-tab-active als Zustandsmittel.
   ============================================================ */

.segmented {
    display: inline-flex;
    align-items: stretch;
    flex-wrap: wrap;
    gap: 2px;
    padding: 2px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-subtle);
}

.segmented__item {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: calc(var(--control-h-sm) - 4px);
    padding: 0 var(--space-3);
    border: none;
    border-radius: calc(var(--radius-md) - 3px);
    background: transparent;
    color: var(--text-muted);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

.segmented__item:hover {
    background: var(--bg-surface);
    color: var(--text-strong);
    text-decoration: none;
}

.segmented__item.is-selected {
    background: var(--accent);
    color: var(--text-on-accent);
    font-weight: var(--weight-semibold);
}

.segmented__item.is-selected:hover {
    background: var(--accent-hover);
    color: var(--text-on-accent);
}

/* Icon-Variante (Theme-Umschalter): quadratisch, ohne Textbreite. */
.segmented--icon .segmented__item {
    width: calc(var(--control-h-sm) - 4px);
    padding: 0;
}

/* Verteilt die Optionen gleichmaessig ueber die volle Zeilenbreite. Bewusst
   Opt-in: bei wenigen kurzen Optionen wirkt eine auseinandergezogene Zeile
   unruhig, deshalb ist inhaltsbreit der Standard. */
.segmented--stretch {
    display: flex;
    width: 100%;
}

.segmented--stretch .segmented__item {
    flex: 1;
}

/* Jede Option ist ein eigenes <form> (POST). Damit die Formulare die
   Segmentbreite nicht aufbrechen, verhalten sie sich wie das Element selbst. */
.segmented > .form--inline {
    display: inline-flex;
}

.segmented--stretch > .form--inline {
    flex: 1;
}

.segmented > .form--inline .segmented__item {
    width: 100%;
}

/* ============================================================
   Formular
   ============================================================ */

.form--inline {
    display: inline;
}

/* Aktionsleiste am Formularende. Ersetzt das feste margin-top, das frueher
   in der Button-Klasse selbst steckte. */
.form__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-top: var(--space-6);
}

.field {
    display: block;
    margin-bottom: var(--space-4);
}

.field:last-child {
    margin-bottom: 0;
}

.field--search {
    max-width: 20rem;
}

.field__label {
    display: block;
    margin-bottom: var(--space-1);
    color: var(--text-strong);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
}

.field__hint {
    margin: var(--space-1) 0 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.field__error {
    margin: var(--space-1) 0 0;
    color: var(--danger-text);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
}

/* Eingabefelder teilen Hoehe, Radius, Rahmen und Flaeche mit den Buttons.
   Flaeche ist --bg-surface, nicht --bg-canvas: Felder sollen nicht dunkler
   wirken als die Karte, auf der sie liegen. */
.input,
.select,
.textarea {
    display: block;
    width: 100%;
    min-height: var(--control-h);
    padding: 0 var(--space-3);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
    color: var(--text-strong);
    font-size: var(--text-base);
    line-height: var(--leading-normal);
    transition: border-color var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out);
}

.textarea {
    min-height: calc(var(--control-h) * 2);
    padding: var(--space-2) var(--space-3);
    resize: vertical;
}

.input:hover,
.select:hover,
.textarea:hover {
    border-color: var(--border-strong);
}

.input:focus,
.select:focus,
.textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 4px var(--ring);
}

.input:disabled,
.select:disabled,
.textarea:disabled {
    background: var(--bg-subtle);
    color: var(--text-muted);
    cursor: not-allowed;
}

.select {
    /* Platz fuer den nativen Pfeil, sonst klebt der Text daran. */
    padding-right: var(--space-8);
    cursor: pointer;
}

.input--sm,
.select--sm {
    min-height: var(--control-h-sm);
    padding-inline: var(--space-2);
    font-size: var(--text-sm);
}

.select--sm {
    padding-right: var(--space-6);
}

/* Breite an den Inhalt statt an den Container — fuer Felder, die in einer
   Zeile neben anderen Elementen stehen. */
.input--auto,
.select--auto {
    width: auto;
}

/* Zahlenfelder (Reihenfolge, Wochenlimit, neue Karten pro Tag). Ersetzt die
   frueheren Inline-style="width:4rem". */
.input--narrow {
    width: 5rem;
    min-width: 5rem;
}

/* Niveau-Auswahl ("A1.2"): sehr kurzer Inhalt, feste kompakte Breite, damit
   sie in jedem Kontext gleich knapp aussieht. */
.select--narrow {
    width: 7rem;
    min-width: 7rem;
}

/* Textfelder in Tabellenzellen: ohne Mindestbreite schrumpfen sie bei vielen
   Spalten auf wenige Zeichen zusammen. Die Tabelle scrollt stattdessen
   horizontal (.table__wrap). */
.table .input {
    min-width: 9rem;
}

.table .input--narrow {
    min-width: 5rem;
}

/* --- Checkbox / Radio --- */

.checkbox,
.radio {
    width: 1.05rem;
    height: 1.05rem;
    margin: 0;
    accent-color: var(--accent);
    cursor: pointer;
    flex-shrink: 0;
}

/* --- Auswahlzeilen (frueher .radio-option) --- */

.choice-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.choice {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
    color: var(--text-default);
    font-size: var(--text-base);
    font-weight: var(--weight-normal);
    cursor: pointer;
    transition: background var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out);
}

.choice:hover {
    border-color: var(--border-strong);
    background: var(--bg-subtle);
}

.choice:has(input:checked) {
    border-color: var(--accent);
    background: var(--accent-subtle);
    color: var(--text-strong);
    font-weight: var(--weight-medium);
}

.choice__meta {
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* Kompakte Inline-Variante fuer Rollen-Checkboxen in Tabellenzellen. */
.choice--inline {
    display: inline-flex;
    padding: 0;
    border: none;
    background: none;
    white-space: nowrap;
    font-size: var(--text-sm);
}

.choice--inline:hover {
    background: none;
}

.choice--inline:has(input:checked) {
    background: none;
    border: none;
}

/* Lange Personenlisten: scrollbarer Block mit buendigen Zeilen statt
   einzelner Pillen mit Abstand. */
.choice-list--scroll {
    max-height: 13rem;
    overflow-y: auto;
    gap: 0;
    padding: 2px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
}

.choice-list--scroll .choice {
    position: relative;
    padding: var(--space-2);
    border: none;
    border-radius: var(--radius-sm);
    border-bottom: 1px solid var(--border-subtle);
}

.choice-list--scroll .choice:last-child {
    border-bottom: none;
}

/* Der Auswahlpunkt selbst ist hier redundant — die gewaehlte Zeile ist bereits
   farblich hervorgehoben. Visuell versteckt, aber weiterhin fokussier- und
   per Tastatur bedienbar (kein display:none). */
.choice-list--scroll .choice input[type="radio"] {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    opacity: 0;
}

.choice-list--scroll .choice:has(input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* ============================================================
   Seitenaufbau
   ============================================================ */

.page {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.page__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-3);
}

.page__title {
    margin: 0;
    color: var(--text-strong);
    font-size: var(--text-2xl);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    line-height: var(--leading-snug);
}

.page__lead {
    margin: var(--space-2) 0 0;
    max-width: 60ch;
    color: var(--text-muted);
}

.page__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.page__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-8);
}

.section {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

/* Trennlinie ist Opt-in. Frueher zog JEDE h2 automatisch eine border-top —
   auch die erste direkt unter dem Seitentitel, wo sie den Titel optisch vom
   eigenen Inhalt abgeschnitten hat. */
.section--divided {
    padding-top: var(--space-6);
    border-top: 1px solid var(--border-subtle);
}

.section__title {
    margin: 0;
    color: var(--text-strong);
    font-size: var(--text-lg);
    font-weight: var(--weight-semibold);
}

.section__hint {
    margin: 0;
    max-width: 70ch;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* ============================================================
   Karte
   ============================================================ */

.card {
    padding: var(--space-6);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
}

/* Im Dark Theme uebernimmt der Rahmen die Trennwirkung — Schatten tragen auf
   dunklem Grund kaum (die Schatten-Tokens sind dort entsprechend flacher). */
.card--raised {
    box-shadow: var(--shadow-md);
}

.card--flush {
    padding: 0;
    overflow: hidden;
}

/* Schmale Karte fuer einzelne Formulare (Konto bearbeiten). Bewusst linksbuendig
   statt zentriert — sie steht unter einem linksbuendigen Seitentitel. */
.card--narrow {
    max-width: var(--container-narrow);
}

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

.card__title {
    margin: 0;
    font-size: var(--text-xl);
    font-weight: var(--weight-semibold);
    color: var(--text-strong);
}

@media (min-width: 640px) {
    .card {
        padding: var(--space-8);
    }
}

/* Sekundaere Links am Fuss einer Gast-Karte (Passwort vergessen, Registrieren,
   zurueck zum Login). Frueher drei lose Absaetze mit vollem Absatzabstand. */
.auth-links {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding-top: var(--space-4);
    border-top: 1px solid var(--border-subtle);
    color: var(--text-muted);
    font-size: var(--text-sm);
    text-align: center;
}

.auth-links > * {
    margin: 0;
}

/* ============================================================
   Startseite und Fehlerseiten
   ============================================================ */

.hero {
    text-align: center;
}

.hero__title {
    margin: 0 0 var(--space-3);
    font-size: var(--text-4xl);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    color: var(--text-strong);
}

.hero__lead {
    margin: 0 0 var(--space-6);
    color: var(--text-muted);
    font-size: var(--text-lg);
}

@media (min-width: 640px) {
    .hero__title {
        font-size: var(--text-5xl);
    }
}

.error__code {
    margin: 0 0 var(--space-2);
    color: var(--text-subtle);
    font-size: var(--text-4xl);
    font-weight: var(--weight-bold);
    font-variant-numeric: tabular-nums;
    letter-spacing: var(--tracking-tight);
    line-height: var(--leading-tight);
}

/* ============================================================
   Tabelle
   ============================================================ */

.table__wrap {
    overflow-x: auto;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
}

/* Long tables (teacher/leitner's full vocabulary list): a fixed height with
   its own vertical scrollbar instead of stretching the page. 28.5rem holds
   roughly 10-12 .table--compact rows plus the sticky header — halved from
   the original 57rem, which left the box tall enough to push the rest of
   the page (filters, action buttons) out of the viewport. */
.table__wrap--scroll {
    max-height: 28.5rem;
    overflow-y: auto;
}

.table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
}

.table th,
.table td {
    padding: var(--space-3) var(--space-4);
    text-align: left;
    vertical-align: top;
    border-bottom: 1px solid var(--border-subtle);
}

/* Klebende Kopfzeile: bei den langen Admin-Listen ging beim Scrollen sonst
   der Spaltenbezug verloren. */
.table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--bg-subtle);
    /* Bewusst --text-default statt --text-muted: die Kopfzeile ist 12px in
       Versalien, und --text-muted auf --bg-subtle kommt im hellen Theme nur
       auf 4.34:1 — unter der AA-Schwelle von 4.5 fuer normalgrossen Text. */
    color: var(--text-default);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    white-space: nowrap;
    border-bottom: 1px solid var(--border-default);
}

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

.table tbody tr:hover td {
    background: var(--bg-subtle);
}

/* Marks a row that deviates from a known-good baseline (e.g. Leitner config vs. its default). */
.table__row--warning td {
    background: var(--warning-bg);
}

.table--compact th,
.table--compact td {
    padding: var(--space-2) var(--space-3);
}

/* Feste Spaltenbreiten fuer die Matrizen (Plan x Modul, Plan x Sprache):
   bei 'auto' haengt die Breite am Inhalt der jeweils EIGENEN Tabelle, und
   zwei Matrizen mit gleicher Spaltenzahl laufen optisch auseinander. */
.table--matrix {
    table-layout: fixed;
}

.table--matrix th:first-child,
.table--matrix td:first-child {
    width: 14rem;
}

.table--matrix td {
    text-align: center;
}

.table--matrix td:first-child {
    text-align: left;
}

/* ============================================================
   Liste
   Ein Baustein fuer alle Zeilenlisten: Kontodaten, Verknuepfungen,
   Leitner-Fortschritt, Zuweisungen, Suchergebnisse.
   ============================================================ */

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

.list__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
}

.list__item--stacked {
    flex-direction: column;
    align-items: stretch;
}

.list__main {
    min-width: 0;
    flex: 1 1 14rem;
}

/* Kleines Versal-Label ueber einem Wert (Kontoseite). */
.list__label {
    display: block;
    margin-bottom: 2px;
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
}

.list__title {
    color: var(--text-strong);
    font-weight: var(--weight-semibold);
}

.list__value {
    color: var(--text-strong);
}

.list__meta {
    margin: 2px 0 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.list__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    flex-shrink: 0;
}

/* Zeilen mit mehreren Aktionen brauchen auf schmalen Screens die volle
   Breite, sonst bleibt vom Flex-Rest zu wenig uebrig. */
@media (max-width: 639px) {
    .list__actions {
        flex-basis: 100%;
    }
}

/* ============================================================
   Badge
   ============================================================ */

.badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 2px var(--space-2);
    border: 1px solid transparent;
    border-radius: var(--radius-full);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    white-space: nowrap;
}

.badge--neutral {
    background: var(--bg-subtle);
    border-color: var(--border-subtle);
    color: var(--text-muted);
}

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

.badge--warning {
    background: var(--warning-bg);
    border-color: var(--warning-border);
    color: var(--warning-text);
}

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

.badge--info {
    background: var(--info-bg);
    border-color: var(--info-border);
    color: var(--info-text);
}

/* ============================================================
   Status Toggle
   A 3-way admin review control (approved/in_review/blocked): colored pills
   in badge colors, the row's CURRENT status shown solid+non-interactive
   (a <span>, not a real radio -- it must not be re-selectable), the other
   two real radio inputs, neutral until checked then solid in their own
   target color. Picking one + a required reason + a Save button changes
   the status. Not specific to vocabulary: any future admin list with the
   same 3-state review pattern (e.g. word-to-word translation links) should
   reuse this instead of a new component. First use: admin/vocabulary-content.
   ============================================================ */

.status-toggle {
    display: inline-flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.status-toggle__option {
    position: relative;
    display: inline-flex;
    align-items: center;
    padding: 2px var(--space-3);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-full);
    background: var(--bg-subtle);
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

.status-toggle__option input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    opacity: 0;
}

.status-toggle__option:not(.is-current):not(:has(input:checked)):hover {
    background: var(--bg-surface);
    color: var(--text-strong);
}

.status-toggle__option:has(input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.status-toggle__option.is-current {
    cursor: default;
}

.status-toggle__option--approved.is-current,
.status-toggle__option--approved:has(input:checked) {
    background: var(--success-bg);
    border-color: var(--success-border);
    color: var(--success-text);
}

.status-toggle__option--in_review.is-current,
.status-toggle__option--in_review:has(input:checked) {
    background: var(--warning-bg);
    border-color: var(--warning-border);
    color: var(--warning-text);
}

.status-toggle__option--blocked.is-current,
.status-toggle__option--blocked:has(input:checked) {
    background: var(--danger-bg);
    border-color: var(--danger-border);
    color: var(--danger-text);
}

/* ============================================================
   Code / Kennungen
   ============================================================ */

.code {
    display: inline-block;
    padding: 1px var(--space-2);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    background: var(--bg-subtle);
    color: var(--text-strong);
    font-family: var(--font-mono);
    font-size: var(--text-sm);
}

/* Hervorgehobene Fassung fuer den Referral-Code. */
.code--lg {
    padding: var(--space-2) var(--space-4);
    border: 1px dashed var(--accent-border);
    border-radius: var(--radius-md);
    background: var(--accent-subtle);
    color: var(--accent-text);
    font-size: var(--text-lg);
    font-weight: var(--weight-bold);
    letter-spacing: 0.08em;
}

/* ============================================================
   Alert
   ============================================================ */

.alert {
    padding: var(--space-3) var(--space-4);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
}

.alert + .alert {
    margin-top: var(--space-2);
}

.alert--info {
    background: var(--info-bg);
    border-color: var(--info-border);
    color: var(--info-text);
}

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

.alert--warning {
    background: var(--warning-bg);
    border-color: var(--warning-border);
    color: var(--warning-text);
}

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

/* ============================================================
   Leerzustand
   Ein Muster statt der frueheren vier (p / p.hint / span.hint /
   alert-error). Ein Leerzustand ist kein Fehler und wird deshalb
   nicht in Fehlerfarbe gesetzt.
   ============================================================ */

.empty {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-6);
    border: 1px dashed var(--border-default);
    border-radius: var(--radius-lg);
    background: var(--bg-inset);
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.empty__text {
    margin: 0;
}

.empty__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

/* ============================================================
   Leitner
   ============================================================ */

.practice-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
}

.practice-bar__day {
    color: var(--text-strong);
    font-weight: var(--weight-semibold);
}

.boxes {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.boxes__label {
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.box {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border: 2px solid var(--border-default);
    border-radius: var(--radius-full);
    background: var(--bg-surface);
    color: var(--text-muted);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
}

.box--due {
    border-color: var(--accent);
    background: var(--accent);
    color: var(--text-on-accent);
}

.flashcard {
    display: flex;
    flex-direction: column;
    justify-content: center;
    max-width: 34rem;
    min-height: 17rem;
    margin-inline: auto;
    padding: var(--space-8);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-xl);
    background: var(--bg-surface);
    box-shadow: var(--shadow-md);
    text-align: center;
}

/* Full width on mobile, 80% (centered) once there is room — see design-system.md 4.8.
   Overrides .flashcard's max-width: 34rem, which would otherwise cap the card below
   80% on most screens and make the width rule invisible. */
.flashcard--wide {
    width: 100%;
    max-width: none;
}

@media (min-width: 900px) {
    .flashcard--wide {
        width: 80%;
    }
}

.flashcard__meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* "Report this card" panel (student/leitner.php, free/subscription box
   only) — collapsed by default, expanded on click via leitner.js. Without
   JS it stays visible (progressive enhancement), see the view's UI rules. */
.flashcard__report {
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border-subtle);
    text-align: left;
}

.flashcard__term {
    color: var(--text-strong);
    font-size: var(--text-3xl);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    line-height: var(--leading-tight);
}

.flashcard__translation {
    margin-top: var(--space-3);
    color: var(--text-muted);
    font-size: var(--text-xl);
}

.flashcard__actions {
    display: flex;
    justify-content: center;
    margin-top: var(--space-6);
}

.flashcard__grades {
    display: flex;
    justify-content: center;
    gap: var(--space-3);
}

/* Bewertungsknoepfe tragen ein Emoji statt Text — hier zaehlt die Trefferflaeche,
   nicht die Beschriftungsbreite. */
.flashcard__grades .btn {
    min-width: var(--control-h-lg);
    min-height: var(--control-h-lg);
    font-size: var(--text-xl);
}

/* ============================================================
   Matching
   ============================================================ */

.matching-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-4);
}

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

.matching-grid__col {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

/* Equal-looking tiles on both sides: same min-height, padding, border, centered text. Height can
   still grow past min-height for a long definition — truncating would hide the answer. Reordering
   (matching.js moves matched pairs to the top, see below) animates via this transform transition
   (FLIP technique: matching.js measures old/new position, sets an inverse transform, then clears
   it here). */
.matching-card {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 3.5rem;
    padding: var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
    text-align: center;
    transition: transform 0.25s ease;
}

.matching-card__term {
    color: var(--text-strong);
    font-weight: var(--weight-semibold);
}

/* The real, always-usable answer control (Leitsatz 5) — matching.js hides it (.is-hidden) only
   once JS actually runs; without JS it stays visible right here and works like any other .select. */
.matching-card__select {
    width: 100%;
    margin-top: var(--space-2);
}

/* Right column: draggable/tappable definitions — see matching.js. */
.matching-card--option {
    cursor: grab;
    color: var(--text-default);
}

.matching-card--option:active {
    cursor: grabbing;
}

/* A right card picked by click/tap (touch has no native drag-and-drop), waiting for a left card. */
.matching-card--picked {
    border-color: var(--accent);
    border-style: dashed;
}

/* Both cards of a matched pair — moved to the top of their column, aligned into the same row. */
.matching-card--linked {
    border-color: var(--accent);
    background: var(--accent-subtle);
}

/* Left card currently being dragged over — matching.js toggles this on dragenter/dragleave. */
.matching-card--dragover {
    border-color: var(--accent);
    border-style: dashed;
}

/* Match-order number on a linked pair's two cards — the same number on both sides is what shows
   they belong together once they sit in aligned rows at the top of each column. */
.matching-card__badge {
    position: absolute;
    top: -0.5rem;
    right: -0.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: var(--radius-full);
    background: var(--accent);
    color: var(--text-on-accent);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
}

/* ============================================================
   Modal
   Overlay-Dialog fuer Inhalte, die kurz eingeblendet werden, ohne
   die Seite zu verlassen (Dateivorschau aus Google Drive, siehe
   teacher/student-folder.php). Reine Progressive Enhancement: ohne
   JavaScript bleibt der aufrufende Link ein normaler Link
   (target="_blank") — .is-open wird ausschliesslich von app.js
   gesetzt, es gibt keinen CSS-only Oeffnungsmechanismus dafuer.
   ============================================================ */

.modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 100;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
    background: var(--overlay);
}

.modal.is-open {
    display: flex;
}

.modal__dialog {
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 60rem;
    height: 85vh;
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

.modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-subtle);
}

.modal__title {
    margin: 0;
    color: var(--text-strong);
    font-size: var(--text-base);
    font-weight: var(--weight-semibold);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.modal__body {
    flex: 1;
    background: var(--bg-inset);
}

.modal__body iframe {
    display: block;
    width: 100%;
    height: 100%;
    border: none;
}

/* For a modal filled with server-rendered HTML instead of an iframe (see
   views/admin/vocabulary-content.php's report-history modal) — .modal__body
   alone has no padding/scroll of its own since the iframe case fills 100%
   and scrolls itself. */
.modal__body--padded {
    padding: var(--space-4);
    overflow-y: auto;
    background: var(--bg-surface);
}
