/*
 * Vereinsfarben: alle Farben der Anwendung sind hier als CSS Custom
 * Properties definiert (Issue #1). Palette abgeleitet aus Grau (neutrale
 * Flächen/Text), leichtem Grün (Primär/Aktionen) und leichtem Gelb
 * (Akzent/Hervorhebung). --color-accent-bg/--color-warning-bg/
 * --color-danger-bg und --color-on-accent sind bewusst modus-unabhängig,
 * da sie ausschließlich mit fester weißer Schrift kombiniert werden
 * (Buttons, Icon, Banner); alle übrigen Farben sind je Modus auf
 * WCAG-AA-Kontrast (>= 4.5:1 Text, >= 3:1 Rahmen/große Schrift) gegen
 * --color-bg abgestimmt, siehe PR-Beschreibung für die gemessenen Werte.
 * --color-danger-bg (#a82d24) gegen --color-on-accent (#ffffff): 6.8:1,
 * damit über AA (4.5:1) unabhängig vom Modus (Issue #68).
 */
:root {
    --color-accent-bg: #328551;
    --color-warning-bg: #8d651b;
    --color-danger-bg: #a82d24;
    --color-on-accent: #ffffff;

    --color-bg: #f4f6f4;
    --color-text: #131b15;
    --color-text-muted: #495a4b;
    --color-accent: #27683f;
    --color-warning: #8d651b;
    --color-danger: #a82d24;
    --color-info: #276591;

    /* FullCalendar theming (vendor bundle injects its own :root defaults
       at an unpredictable time relative to this stylesheet, so !important
       is needed to make our palette win deterministically). */
    --fc-page-bg-color: var(--color-bg) !important;
    --fc-neutral-bg-color: color-mix(in srgb, var(--color-text) 8%, var(--color-bg)) !important;
    --fc-neutral-text-color: var(--color-text-muted) !important;
    --fc-border-color: color-mix(in srgb, var(--color-text) 20%, transparent) !important;
    --fc-button-text-color: var(--color-on-accent) !important;
    --fc-button-bg-color: var(--color-accent-bg) !important;
    --fc-button-border-color: var(--color-accent-bg) !important;
    --fc-button-hover-bg-color: var(--color-accent) !important;
    --fc-button-hover-border-color: var(--color-accent) !important;
    --fc-button-active-bg-color: var(--color-accent) !important;
    --fc-button-active-border-color: var(--color-accent) !important;
    --fc-event-bg-color: var(--color-accent-bg) !important;
    --fc-event-border-color: var(--color-accent-bg) !important;
    --fc-event-text-color: var(--color-on-accent) !important;
    --fc-event-selected-overlay-color: color-mix(in srgb, var(--color-text) 25%, transparent) !important;
    --fc-more-link-bg-color: color-mix(in srgb, var(--color-text) 15%, var(--color-bg)) !important;
    --fc-non-business-color: color-mix(in srgb, var(--color-text) 8%, transparent) !important;
    --fc-bg-event-color: var(--color-accent) !important;
    --fc-highlight-color: color-mix(in srgb, var(--color-accent) 30%, transparent) !important;
    --fc-today-bg-color: color-mix(in srgb, var(--color-warning) 15%, transparent) !important;
    --fc-now-indicator-color: var(--color-danger) !important;
    --fc-list-event-hover-bg-color: color-mix(in srgb, var(--color-text) 6%, var(--color-bg)) !important;
}

@media (prefers-color-scheme: dark) {
    :root {
        --color-bg: #131b15;
        --color-text: #e6eae7;
        --color-text-muted: #819283;
        --color-accent: #95d0ab;
        --color-warning: #e7ca74;
        --color-danger: #e0736c;
        --color-info: #88badd;
    }
}

* {
    box-sizing: border-box;
}

/* Issue #83: without this, any element with an author "display: flex/grid"
   rule (.field-row, .segmented, ...) stays visible despite [hidden] - a
   normal author declaration always wins over the UA stylesheet's
   "[hidden]{display:none}" regardless of specificity (origin is compared
   before specificity in the cascade). Surfaced by #booking-gueltig-feld
   (.field-row) staying visible while toggled hidden for the "nur dieser"/
   Einzeltermin booking UI; !important restores the attribute's actual
   contract ("hide, full stop") for every future case too. */
[hidden] {
    display: none !important;
}

body {
    margin: 0;
    font-family: system-ui, sans-serif;
    line-height: 1.5;
    background: var(--color-bg);
    color: var(--color-text);
}

.site-header {
    padding: 1rem clamp(1rem, 4vw, 2rem);
    border-bottom: 2px solid var(--color-accent);
}

.site-header h1 {
    margin: 0;
    font-size: 1.25rem;
}

main {
    padding: clamp(1rem, 4vw, 2rem);
}

/* --- Public --- */

.brand {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    color: inherit;
    text-decoration: none;
}

.brand-logo {
    height: 1.75rem;
    width: 1.75rem;
    object-fit: contain;
}

.main-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1rem;
    margin-top: 0.5rem;
}

.main-nav a {
    color: var(--color-accent);
    text-decoration: none;
    font-weight: 600;
}

.main-nav a:hover {
    text-decoration: underline;
}

/* Issue #66: /abonnieren needs a live network (feed URLs), so its nav
   link is disabled instead of leading to a confusing offline error. */
.main-nav a.nav-disabled {
    color: var(--color-text);
    opacity: 0.5;
    cursor: not-allowed;
    text-decoration: none;
}

.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 1rem;
}

.card {
    display: block;
    padding: 1rem;
    border: 1px solid color-mix(in srgb, var(--color-text) 25%, transparent);
    border-radius: 8px;
    color: inherit;
    text-decoration: none;
}

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

/*
 * Kalender-Überschrift + Zeitraum-Anzeige (Issue #53): die Zeitraum-Anzeige
 * saß vorher in FullCalendars eigener Toolbar (`.fc-toolbar-title`, von
 * Preact verwaltet) - ein direktes `textContent`-Schreiben dort (Terminliste,
 * Issue #31/#52) und Preacts eigene Re-Renders beim View-Wechsel gerieten
 * sich in die Quere (siehe kalender-titel.js). Jetzt ein eigenes Element
 * neben der Überschrift, außerhalb von FullCalendars DOM. Feste min-height
 * verhindert einen Layoutsprung, wenn der Text beim Darstellungswechsel
 * kürzer/länger wird.
 */
.kalender-titelzeile {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 0.75rem;
    row-gap: 0.15rem;
}

.kalender-titelzeile h2 {
    margin: 0;
}

.kalender-zeitraum {
    display: inline-block;
    min-height: 1.3em;
    font-size: clamp(0.85rem, 3vw, 1.05rem);
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

/* "Jetzt gerade" (aktuell laufende Termine ganz oben): nur sichtbar, wenn
   wirklich etwas läuft (sonst hidden, unveränderte Seite). Card-Optik wie
   .warning-message weiter unten, aber in Akzent- statt Warnfarbe - kein
   Warnzustand, reiner Status. */
.kalender-laufend {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.35rem 0.75rem;
    padding: 0.5rem 0.9rem;
    margin-bottom: 0.75rem;
    border-left: 4px solid var(--color-accent);
    background: color-mix(in srgb, var(--color-accent) 12%, var(--color-bg));
    border-radius: 4px;
}

.kalender-laufend-label {
    font-weight: 600;
    color: var(--color-accent);
    flex-shrink: 0;
}

.kalender-laufend-liste {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 0.5rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Touch-Ziel >= 44px (CLAUDE.md Abschnitt 8), analog .chip-toggle. */
.kalender-laufend-item {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    min-height: 44px;
    padding: 0.2rem 0.6rem;
    border: 1px solid color-mix(in srgb, var(--color-text) 30%, transparent);
    border-radius: 999px;
    background: var(--color-bg);
    color: inherit;
    font: inherit;
    font-size: 0.9rem;
    cursor: pointer;
}

/*
 * FullCalendar's own header toolbar (prev/next/today/view switcher - der
 * Titel wurde mit Issue #53 daraus entfernt, s.o.) - nicht die custom
 * filter .toolbar weiter unten. Issue #3/#53: kompakt, KEIN horizontaler
 * Scrollbalken mehr auf 360-430px - stattdessen umbricht die Toolbar sauber
 * in eine zweite Zeile, Touch-Ziele bleiben >= 44px via min-height/min-width.
 */
.fc .fc-header-toolbar {
    flex-wrap: wrap;
    row-gap: 0.35rem;
    column-gap: 0.35rem;
    margin-bottom: 0.75rem;
}

.fc .fc-toolbar-chunk {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
}

.fc .fc-button-group {
    gap: 2px;
}

.fc .fc-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    min-width: 44px;
    padding: 0.3rem 0.55rem;
    font-size: 0.85rem;
    line-height: 1;
}

.fc .fc-today-button {
    padding: 0.3rem 0.5rem;
    font-size: 1rem;
}

@media (max-width: 430px) {
    .fc .fc-button {
        padding: 0.25rem 0.4rem;
        font-size: 0.75rem;
    }
}

.toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: 0.75rem 1rem;
    margin-bottom: 1rem;
}

/* Woche-Navigation der Verfügbarkeitsansicht (prev/heute/next), optisch
   analog FullCalendars eigener .fc-button-Gruppe im Kalender-Toolbar
   (kompakt, Touch-Ziel >= 44px, s. .fc .fc-button oben). */
.week-nav {
    display: inline-flex;
    gap: 2px;
}

.week-nav-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    min-width: 44px;
    padding: 0.3rem 0.55rem;
    font-size: 1rem;
    line-height: 1;
}

.toolbar .filter {
    margin: 0;
    font-size: 0.85rem;
}

/* Platz-Auswahl (Issues #6/#7): Dropdown nur unterhalb der Desktop-Sidebar-
   Schwelle (CLAUDE.md Abschnitt 9, ~1100px) - darüber bleibt die bisherige
   Spalten- bzw. Untereinander-Darstellung ohne Auswahl bestehen. Sichtbarkeit
   wird per JS anhand derselben einmalig ausgewerteten matchMedia-Schwelle
   gesteuert wie die Filter-/Farblogik selbst (keine eigene @media-Regel),
   damit Auswahl und Darstellung bei einer Fenster-Größenänderung nie
   auseinanderlaufen können - ein Reload wendet die neue Breite an. */
.filter-narrow-hidden {
    display: none;
}

/* Terminliste-Nachladen (Issue #4): Ladeindikator am Listenende während
   ein weiterer Batch nachgeladen wird. */
.liste-lade-indikator {
    text-align: center;
    padding: 0.75rem;
    color: var(--color-text-muted);
    font-size: 0.9rem;
}

/* Issue #81: Vergangenheits-Batches wachsen die Dokumenthöhe OBERHALB der
   Scrollposition ein - Chromes/Firefoxs native CSS-Scroll-Anchoring
   ("overflow-anchor", per Default an) korrigiert das zwar ebenfalls, tut das
   aber GLEICHZEITIG mit unserer eigenen, getesteten Korrektur
   (scrollAnkerZiel() in nachlade.js) und verfälscht sie dadurch
   unvorhersehbar (beobachtet: erwartete Zielposition ~96px, tatsächlich
   834px). `overflow-anchor: none` schaltet den Browser-Mechanismus ab, damit
   ausschließlich unsere deterministische Berechnung greift. Betrifft nur das
   Nachladen nach unten (Issue #4/#31) nicht, das unterhalb der Scrollposition
   anhängt - dort greift natives Scroll-Anchoring ohnehin nie ein. */
html {
    overflow-anchor: none;
}

/* Issue #81: Schalter "Vergangenheit anzeigen" oberhalb der Terminliste -
   Touch-Ziel >= 44px (CLAUDE.md Abschnitt 8), analog .segmented label. */
.liste-vergangenheit-leiste {
    margin-bottom: 0.5rem;
}

.liste-vergangenheit-schalter {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 44px;
    padding: 0.25rem 0.25rem;
    cursor: pointer;
}

dialog.sheet {
    border: none;
    border-radius: 12px;
    padding: 1.25rem;
    max-width: 26rem;
    width: calc(100% - 2rem);
    background: var(--color-bg);
    color: var(--color-text);
}

dialog.sheet::backdrop {
    background: rgb(0 0 0 / 0.4);
}

@media (max-width: 767px) {
    dialog.sheet {
        margin: auto 0 0;
        width: 100%;
        max-width: none;
        border-radius: 12px 12px 0 0;
    }
}

.dialog-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-top: 0.75rem;
}

.field-row {
    display: flex;
    gap: 0.75rem;
}

.field-row label {
    flex: 1;
}

/* Echter Callout statt bloß eingefärbtem Text (Doppelbelegung/Restriktions-
   Warnungen sollen unübersehbar sein) - dieselbe Technik wie der bisherige
   .hint-Block (Heimspiel-ohne-Platz/Vermietungs-Hinweise in der
   Verfügbarkeit), hier zusammengeführt statt eines zweiten, identischen
   Regelsatzes. */
.warning-message {
    padding: 0.5rem 0.75rem;
    border-left: 4px solid var(--color-warning);
    background: color-mix(in srgb, var(--color-warning) 12%, var(--color-bg));
    color: var(--color-warning);
}

/* Gesperrter Platz im Detail-Dialog: derselbe Callout in Rot statt Gelb -
   dieselbe Farbtrennung wie am Termin selbst und im Verfügbarkeits-Zeitstrahl
   (gesperrt = --color-danger, eingeschränkt = --color-warning). */
.warning-message-danger {
    border-left-color: var(--color-danger);
    background: color-mix(in srgb, var(--color-danger) 12%, var(--color-bg));
    color: var(--color-danger);
}

.warn-titel {
    display: block;
    margin-bottom: 0.35rem;
    font-size: 1.05rem;
}

/* Gruppierte Konfliktanzeige (Issue #9): eine Zeile je Verursacher. */
.konflikt-liste {
    list-style: none;
    margin: 0.5rem 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.konflikt-toggle {
    font-size: 0.85rem;
}

.konflikt-termine {
    list-style: none;
    margin: 0.3rem 0 0 0.5rem;
    padding: 0;
    font-size: 0.85rem;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.ev-abgesagt {
    text-decoration: line-through;
    opacity: 0.6;
}

/* Team- und Spielstättenfarbe gleichzeitig statt Umschalter (Issue #39):
   eigener eventContent in kalender.js liefert diese Struktur für jeden
   Termin. Die Punkte nutzen zwei Ringe statt eines einzelnen Rahmens -
   ein Ring in --color-bg trennt die (mitunter helle) Punktfarbe optisch
   vom Untergrund, ein zweiter Ring in currentColor sorgt für Kontrast
   sowohl im farbigen Termin-Block (dort ist currentColor die weiße
   Event-Textfarbe) als auch in der Listen-/Monatsansicht (dort die normale
   Textfarbe) - unabhängig davon, wie hell oder dunkel Team-/Spielstätten-
   farbe selbst ausfallen. Team = Kreis, Spielstätte = Quadrat: auch ohne
   Farbunterscheidung (Farbfehlsichtigkeit) klar auseinanderzuhalten.
   Sperrungen haben kein Team und bekommen keine Punkte (eventPunkte()
   liefert dafür null), sie bleiben bei ihrer Art-Farbe (gesperrt/
   eingeschränkt) - nur für sie bleibt FullCalendars eigener Listen-/
   Monats-Punkt sichtbar. */
.ev-inhalt {
    display: flex;
    /* Issue #58 - Fund der eigentlichen Ursache des gemeldeten Überlaufs:
       FullCalendars eigenes `.fc-v-event .fc-event-main-frame{flex-direction:
       column;height:100%}` (Tag/Woche, zwei Klassen = Spezifität 0,2,0) schlug
       unser einfaches `.ev-inhalt{display:flex}` (eine Klasse = 0,1,0) und
       kippte die Zeile in eine SPALTE - ohne Haupt-Achse in der Breite
       schrumpft dort nichts mehr, jedes Kind (Uhrzeit/Punkte/Titel) nimmt
       seine natürliche Breite und ragt über den Block hinaus, unabhängig von
       jeder Ellipsis-Regel auf den Kindern. Monat/Liste (kein `.fc-v-event`)
       bleiben bei dieser Zeilen-Basis - Issue #75 unten kippt die Zeile für
       Tag/Woche bewusst wieder in eine Spalte, diesmal mit breitenbegrenzten,
       ellipsierenden Kindern statt FCs ungebremster `column`-Regel. */
    flex-direction: row;
    align-items: center;
    gap: 0.3rem;
    min-width: 0;
    /* beschneiden statt überlaufen - die letzte Sicherung, falls ein Kind
       trotz eigener Regeln (s. u.) einmal breiter ausfällt. */
    overflow: hidden;
    /* Der Kontrast-Ring der Farbpunkte (.ev-punkt box-shadow, bis zu 2px
       außerhalb der eigenen Box) UND der Spielstätten-Ring
       (.ev-punkt-venue::before/::after, inset:-25%) ragen über die
       Punkt-Box hinaus. Ohne dieses Polster liegt der erste Punkt (Team)
       flush an der linken Kante von .ev-inhalt - sein Ring wird dann vom
       overflow:hidden oben mit abgeschnitten (in Tag/Woche/Liste sichtbar,
       da dort keine ausreichende Polsterung von außen kommt). Reines
       Innenpolster statt weniger overflow:hidden zu verwenden - die
       #58/#67/#75-Kürzungsregeln bleiben unberührt. */
    padding-left: 0.2rem;
}

/* Issue #75: Tag/Woche (`.fc-v-event`, timeGrid-Views) stapeln Uhrzeit /
   Farbpunkte / Team vertikal statt in einer Zeile - der ursprüngliche
   Auftrag der App, s. Issue-Text. Das scheint der #58-Zeilen-Regel oben zu
   widersprechen, ist es aber nicht: #58 verhinderte Überlauf, weil FCs
   erzwungene `column`-Kippung KEINE Breitenbegrenzung auf die Kinder legte
   (jedes Kind nimmt seine natürliche nowrap-Breite). Hier ist es umgekehrt -
   die Spalte ist bewusst gewählt, UND jedes gestapelte Kind bleibt über
   `min-width:0`+`overflow:hidden`+Ellipsis (Uhrzeit oben, `.ev-titel` unten)
   auf die definite Blockbreite begrenzt, die die Grid-Geometrie liefert. Die
   #58-Kürzungsreihenfolge (Präfix schrumpft nach dem Titel) bleibt dabei
   unangetastet, weil `.ev-titel` selbst weiter eine interne Row-Flex-Zeile
   ist - nur die drei Blöcke Uhrzeit/Punkte/Titel stapeln sich außen. Höhere
   Spezifität (0,3,0 durch die zusätzliche `.fc`-Klasse) statt `!important`
   genügt hier, um FCs `.fc-v-event .fc-event-main-frame` (0,2,0) zu
   schlagen - anders als bei den `--fc-event-*`-Variablen oben ist das kein
   Wettrüsten gegen viele künftige Selektoren, sondern eine einzelne, stabile
   FC-Klasse. Monat (`.fc-daygrid`, Dot-Events) und Liste (`listNachlade`,
   #67-Tabellenlayout) sind keine `.fc-v-event` und bleiben unberührt. */
.fc .fc-v-event .ev-inhalt {
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
}

/* Die Uhrzeit soll in der Spalte nie vertikal schrumpfen (kompaktestes
   Signal neben den Punkten, Issue #58-Prinzip) - nur der Titel (unten) gibt
   bei zu wenig Höhe zuerst nach; der Container-`overflow:hidden` oben klippt
   ihn dann sauber weg, voller Text bleibt über title/aria-label und den
   Detail-Dialog erreichbar (kalender.js). .ev-punkte trägt bereits global
   flex-shrink:0 (oben) - das gilt achsenunabhängig für Zeile UND Spalte,
   braucht hier keine eigene Regel. */
.fc .fc-v-event .ev-inhalt > .fc-event-time {
    flex-shrink: 0;
}

/* Issue #58: Uhrzeit ist nicht Teil der festgelegten Kürzungsreihenfolge
   (die gilt für Präfix/Titel, s. .ev-titel unten) - sie kann als
   Start-Ende-Bereich ("17:00 - 18:30") selbst breiter sein als eine schmale
   geteilte Spalte. flex-shrink:0 allein hätte hier hart am Elementrand
   abgeschnitten (kein Ellipsis, s. Issue) statt zu beschneiden; stattdessen
   wie Präfix/Titel: schrumpfen mit Ellipsis statt überlaufen. Die Breiten-
   Regeln hier (min-width/overflow/ellipsis/nowrap) gelten unverändert in
   JEDER Darstellung. Issue #75 überschreibt für `.fc-v-event` (Tag/Woche)
   nur `flex-shrink` auf 0 - dort ist die Hauptachse nach der Spalten-Kippung
   vertikal, das betrifft also die HÖHE (Uhrzeit soll nie vertikal
   schrumpfen), nicht die hier geregelte horizontale Ellipsis-Kürzung. */
.ev-inhalt .fc-event-time {
    flex-shrink: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ev-punkte {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    flex-shrink: 0;
}

.ev-punkt {
    display: inline-block;
    width: 0.6rem;
    height: 0.6rem;
    flex-shrink: 0;
    box-shadow:
        0 0 0 1px var(--color-bg),
        0 0 0 2px color-mix(in srgb, currentColor 55%, transparent);
}

.ev-punkt-team {
    border-radius: 50%;
}

/* Spielstätte = Dreieck, unterscheidbar von Team (Kreis), Platz (Quadrat)
   und Sportheim (Raute). box-shadow folgt der BOX, nicht einer geclippten
   Form - die zwei Kontrast-Ringe der übrigen Punkte lassen sich hier also
   nicht per box-shadow nachbilden. Stattdessen zwei zusätzliche,
   größer werdende Dreieck-Ebenen (::before/::after) UNTER einem echten
   Kind-Element (.ev-punkt-venue-fill, von punkt() in kalender.js erzeugt)
   - dieselben zwei Farbtöne (var(--color-bg), color-mix mit currentColor)
   wie der box-shadow-Ring, nur als Flächen statt als Schatten. Das äußere
   Element selbst bleibt ohne eigenen Hintergrund/Schatten (nur Träger für
   role/aria-label/title, s. punkt()).
   Test: alle vier Formen + beide Größen (ev-punkt/legende-punkt) sowohl mit
   einer dunklen (#0969da) als auch einer hellen Farbe (#f1e05a) auf hellem
   UND dunklem Hintergrund gerendert - der Ring bleibt in jeder Kombination
   sichtbar (dieselbe Prüfung wie im ursprünglichen Kommentar zu
   .legende-punkt oben). */
.ev-punkt-venue {
    position: relative;
    box-shadow: none;
}

.ev-punkt-venue::before,
.ev-punkt-venue::after {
    content: '';
    position: absolute;
    clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

.ev-punkt-venue::before {
    inset: -25%;
    background: color-mix(in srgb, currentColor 55%, transparent);
    z-index: 1;
}

.ev-punkt-venue::after {
    inset: -10%;
    background: var(--color-bg);
    z-index: 2;
}

.ev-punkt-venue-fill {
    position: relative;
    z-index: 3;
    display: block;
    width: 100%;
    height: 100%;
    clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

/* Issue #57: Platz-Punkt in der Monatsansicht (dort kommt ein Hintergrund in
   Platzfarbe nicht an - dayGridMonth rendert Dot-Events ohne Block-Fläche).
   Quadrat, passend zur Legende (Platz = Quadrat, unterscheidbar vom
   dreieckigen Spielstätten-Punkt oben). */
.ev-punkt-pitch {
    border-radius: 2px;
}

/* Sportheim-Punkt im Detail-Dialog (kalender.js showDetail): Raute wie
   .legende-punkt-heim - unterscheidbar von Kreis (Team), Quadrat (Platz) und
   Dreieck (Spielstätte), in der Farbe von dessen Spielstätte (Sportheime
   haben noch keine eigene, Issue #36/#47). Nur im Detail-Dialog verwendet,
   nicht am Termin selbst. */
.ev-punkt-heim {
    border-radius: 2px;
    transform: rotate(45deg);
}

/* Issue #58: Termininhalte dürfen nie über den Block hinausragen - beschnitten
   statt übergelaufen. Kürzungsreihenfolge (bewusst, s. Issue): Farbpunkte
   bleiben immer sichtbar (.ev-punkte, flex-shrink:0 oben) - das kompakteste
   Signal. Titel und Präfix teilen sich den Rest über einen stark
   unterschiedlichen flex-shrink (9999 vs. 1 - Standard-Trick für eine
   Schrumpf-REIHENFOLGE statt eines proportionalen Verhältnisses): der Titel
   gibt praktisch die gesamte Schrumpfung zuerst her, bis er bei 0 ankommt
   (min-width:0) - erst danach greift überhaupt etwas vom Präfix-Anteil.
   Ausprobiert wurde zunächst `container-type: inline-size` +
   `@container`-Regel, um den Präfix unterhalb einer festen Breite ganz
   auszublenden: das kollidiert aber mit Flexbox-Containment - ein
   size-contained Flex-Kind mit flex-grow ignoriert dabei die verfügbare
   Breite und bleibt bei 0 hängen, obwohl Platz da wäre (reproduziert im
   Monatsraster: Event 211px breit, Titel blieb trotzdem 0px). Der
   flex-shrink-Ansatz braucht keine Breitenmessung und bricht daher an
   dieser Stelle nicht. Der Präfix bekommt dieselbe
   Ellipsis-Behandlung wie der Titel (nicht flex-shrink:0) - für den
   Extremfall eines sehr langen Platznamen-Fallbacks (Alt-Plätze ohne
   Kürzel, CLAUDE.md Abschnitt 3) beschneidet auch er sich selbst statt hart
   am Elementrand überzulaufen. Der vollständige Text bleibt in jedem Fall
   über title/aria-label (in kalender.js gesetzt) und den Detail-Dialog
   erreichbar. */
.ev-titel {
    display: flex;
    align-items: baseline;
    gap: 0.2rem;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
}

.ev-praefix {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ev-titel-text {
    flex: 1 9999 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Issue #67: die Kürzungsreihenfolge aus #58 (oben) setzt eine BEGRENZTE
   Breite des Termin-Blocks voraus - flex-shrink kann nur schrumpfen, wenn
   es eine Vorgabe gibt, gegen die geschrumpft wird. In Tag/Woche/Monat
   liefert die Grid-Geometrie diese Vorgabe (der Block bekommt seine Breite
   aus Spalte/Zelle), in der Terminliste NICHT: FullCalendar rendert sie als
   `table.fc-list-table` mit `table-layout: auto`, und dort sizen sich die
   Spalten am INHALT. Das `white-space: nowrap` von .ev-titel-text/.ev-praefix
   macht deren min-content-Breite gleich der vollen Textlänge; diese Breite
   ist für eine Auto-Layout-Tabelle eine harte Untergrenze und wandert
   ungebremst nach oben: Zelle → Spalte → Tabelle. `overflow: hidden` auf
   den Kindern verhindert das nicht - es begrenzt die Darstellung, nicht den
   Platzbedarf, den die Zelle nach oben meldet. Die Tabelle wurde so breiter
   als ihr `.fc-scroller` (der nur vertikal scrollt, overflow-x: visible),
   der Überschuss lief bis zu body/html durch und erzeugte den gemeldeten
   horizontalen Scrollbalken AM DOKUMENT - nicht am Termin und nicht am
   Scroller. Die #58-Regeln greifen dabei durchaus (flex-direction: row,
   min-width: 0 sind aktiv), sie laufen nur ins Leere.
   `max-width: 0` auf der Titel-Zelle deckelt deren Beitrag zur
   Spaltenbreite: die Spalte kann keine Breite mehr aus ihrem Inhalt
   ableiten und bekommt nur noch den Platz, der nach den beiden
   Inhalts-Spalten (Uhrzeit/Grafik, von FullCalendar per `width: 1px` +
   nowrap bewusst schmal gehalten) übrig ist. Damit hat der Flex-Container
   endlich eine definite Breite - die Kürzungsreihenfolge aus #58 wirkt und
   der Titel bekommt seine Ellipsis. Bewusst NICHT `table-layout: fixed`:
   das würde die Spaltenbreiten aus der ersten Zeile ziehen, und
   FullCalendars `<thead>` hängt per `position: absolute; left: -10000px`
   ausserhalb des Flusses - fixed fiele auf die erste Body-Zeile zurück
   (`.fc-list-day` mit colspan=3, also EINE Zelle) und teilte alle drei
   Spalten gleich breit auf; Uhrzeit-Spalte und Titel würden beide
   unbrauchbar. `max-width: 0` lässt FullCalendars eigene Spalten-Logik
   unangetastet. Der volle Text bleibt wie in #58 über title/aria-label am
   .ev-titel und den Detail-Dialog erreichbar. */
.fc .fc-list-table td.fc-list-event-title {
    max-width: 0;
}

.ev-belegung .fc-list-event-dot,
.ev-spiel .fc-list-event-dot,
.ev-vermietung .fc-list-event-dot {
    display: none;
}

/* Issue #67: mit ausgeblendetem Punkt (Regel darüber - die Farbpunkte stehen
   bei uns im Titel) bleibt FullCalendars Grafik-Spalte als reine Polsterung
   übrig. Seit die Titel-Spalte nur noch den ÜBRIGEN Platz bekommt (max-width
   oben), geht dieser Rest direkt vom Titel ab - bei 360 px waren das 14 von
   188 px, also gut 7 % der Titelbreite für eine leere Zelle. Sperrungen
   fehlen in der Liste bewusst (wie schon bei der Punkt-Regel darüber) - sie
   behalten FullCalendars eigenen Punkt und brauchen die Spalte weiterhin.
   Die Selektorlänge ist Absicht: FullCalendars `.fc .fc-list-table td
   {padding:8px 14px}` (0,2,1) und `.fc-direction-ltr .fc-list-table
   .fc-list-event-graphic` (0,3,0) schlagen eine kurze Fassung. */
.fc .fc-list-table tr:is(.ev-belegung, .ev-spiel, .ev-vermietung) td.fc-list-event-graphic {
    padding: 0;
}

/* Der Wochentag (listDaySideFormat) soll direkt neben dem Datum stehen
   ("28. Juli 2026 - Dienstag"), nicht rechtsbündig am anderen Zeilenende:
   FullCalendar positioniert .fc-list-day-side-text per float:right dorthin.
   float:none lässt beide <a>-Elemente stattdessen inline hintereinander
   fließen; das :before liefert den Trenner. Ohne Floats greift wieder der
   Browser-Default für <th> (zentriert) auf .fc-list-day-cushion - explizit
   linksbündig, wie der Rest der Liste. */
.fc.fc-direction-ltr .fc-list-day-cushion {
    text-align: left;
}
.fc.fc-direction-ltr .fc-list-day-text,
.fc.fc-direction-ltr .fc-list-day-side-text {
    float: none;
}
.fc.fc-direction-ltr .fc-list-day-side-text::before {
    content: ' - ';
}

/* Issue #36: Vermietung eigener, gedeckter Look statt Platzfarbe (die sie
   nicht hat) - Text-Label ("Vermietung: …") bleibt das eigentliche Signal. */
.ev-vermietung {
    background-color: color-mix(in srgb, var(--color-text) 12%, var(--color-bg));
    font-style: italic;
}

/* 🏠-Indikator am Termin, wenn dessen Platz zu einem gerade vermieteten
   Sportheim gehört (Issue #36) - nur ein dezenter Hinweis, der volle Text
   steht im Detail-Dialog. */
.ev-vermietung-hinweis {
    margin-left: 0.25rem;
    flex: 0 0 auto;
}

/* Doppelbelegung (CLAUDE.md Abschnitt 3): anders als das dezente
   🏠-Vermietungs-Symbol muss dieses Signal unübersehbar sein - der Auftrag
   lautet ausdrücklich so. ⚠ steht deshalb VOR den Farbpunkten (s.
   eventContent) und behält immer Platz (flex-shrink: 0 wie .ev-punkte). */
.ev-doppelbelegung-hinweis {
    margin-right: 0.25rem;
    flex: 0 0 auto;
    color: var(--color-warning);
}

/* Zusätzlich zum ⚠ ein Rahmen + Schraffur auf dem Terminblock selbst -
   dieselbe repeating-linear-gradient-Technik wie .eingeschraenkt-layer im
   Verfügbarkeits-Zeitstrahl, hier als ::after statt eines eigenen
   Layer-Elements (eventDidMount setzt nur eine Klasse, kein Zusatz-DOM).
   outline statt border, damit die von eventDidMount inline gesetzte
   Platzfarbe (Issue #57, background-color/border-color) unangetastet
   bleibt. :not(.fc-list-event) grenzt die Terminliste aus - deren Zeile ist
   ein <tr>, wo Rahmen/absolut positioniertes Overlay nicht zuverlässig
   greifen; dort übernimmt die eigene Regel unten. */
.ev-doppelbelegung:not(.fc-list-event) {
    position: relative;
    outline: 2px solid var(--color-warning);
    outline-offset: -2px;
}

.ev-doppelbelegung:not(.fc-list-event)::after {
    content: '';
    position: absolute;
    inset: 0;
    background: repeating-linear-gradient(
        45deg,
        color-mix(in srgb, var(--color-warning) 35%, transparent),
        color-mix(in srgb, var(--color-warning) 35%, transparent) 6px,
        transparent 6px,
        transparent 12px
    );
    pointer-events: none;
}

/* Terminliste (Issue #67: table-layout, die Zeile ist ein <tr>): statt
   Rahmen+Schraffur ein linker Farbbalken auf der Titel-Zelle - das ⚠ im
   Titel trägt dort ohnehin die Hauptlast des Signals. */
.fc .fc-list-table tr.ev-doppelbelegung td.fc-list-event-title {
    box-shadow: inset 4px 0 0 var(--color-warning);
}

/* Platzsperrung/-einschränkung am betroffenen Termin (CLAUDE.md Abschnitt 3):
   ⛔ bzw. 🚧 vor den Farbpunkten, dieselbe Mechanik wie das
   Doppelbelegungs-⚠. Die beiden Zeichen sind schon ohne Farbe eindeutig
   ("Farbe ist nie das einzige Signal") - ⚠ wäre es nicht, das trägt bereits
   die Doppelbelegung. Als Emoji bringen sie ihre eigene Farbe mit; `color`
   greift nur, wo sie in Text-Präsentation gerendert werden, und stimmt dann
   mit der Rahmenfarbe überein. */
.ev-platzsperrung-hinweis {
    margin-right: 0.25rem;
    flex: 0 0 auto;
}

.ev-platzsperrung-hinweis-gesperrt {
    color: var(--color-danger);
}

.ev-platzsperrung-hinweis-eingeschraenkt {
    color: var(--color-warning);
}

/* Rahmen am Terminblock, Technik 1:1 von .ev-doppelbelegung (outline statt
   border, damit die inline gesetzte Platzfarbe unangetastet bleibt;
   :not(.fc-list-event) grenzt die Terminliste aus).

   Diese drei Blöcke stehen bewusst NACH .ev-doppelbelegung und in der
   Reihenfolge eingeschränkt -> gesperrt: es gibt nur EINEN outline und EIN
   ::after je Element, bei mehreren zutreffenden Markierungen gewinnt so das
   stärkste Signal. Die Zeichen im Titel bleiben additiv, es geht also keine
   Information verloren.

   Eingeschränkt trägt bewusst nur einen GESTRICHELTEN Rahmen ohne Schraffur:
   es teilt sich die Warnfarbe mit der Doppelbelegung und wäre sonst von ihr
   nicht zu unterscheiden. */
.ev-platz-eingeschraenkt:not(.fc-list-event) {
    position: relative;
    outline: 2px dashed var(--color-warning);
    outline-offset: -2px;
}

.ev-platz-eingeschraenkt:not(.fc-list-event)::after {
    content: none;
}

.ev-platz-gesperrt:not(.fc-list-event) {
    position: relative;
    outline: 2px solid var(--color-danger);
    outline-offset: -2px;
}

.ev-platz-gesperrt:not(.fc-list-event)::after {
    content: '';
    position: absolute;
    inset: 0;
    background: repeating-linear-gradient(
        45deg,
        color-mix(in srgb, var(--color-danger) 35%, transparent),
        color-mix(in srgb, var(--color-danger) 35%, transparent) 6px,
        transparent 6px,
        transparent 12px
    );
    pointer-events: none;
}

.fc .fc-list-table tr.ev-platz-eingeschraenkt td.fc-list-event-title {
    box-shadow: inset 4px 0 0 var(--color-warning);
}

.fc .fc-list-table tr.ev-platz-gesperrt td.fc-list-event-title {
    box-shadow: inset 4px 0 0 var(--color-danger);
}

/* Maps-Link im Termindetail (Issue #5): Icon + Text, nie Farbe allein */
.maps-link {
    color: var(--color-accent);
    text-decoration: underline;
    font-weight: 600;
}

.site-footer {
    padding: 1rem clamp(1rem, 4vw, 2rem);
    border-top: 1px solid color-mix(in srgb, var(--color-text) 20%, transparent);
    display: flex;
    gap: 1rem;
    font-size: 0.9rem;
}

.site-footer a {
    color: inherit;
}

.footer-brand {
    font-weight: 600;
    margin-right: auto;
}

.offline-banner {
    background: var(--color-warning-bg);
    color: var(--color-on-accent);
    text-align: center;
    padding: 0.75rem 1rem;
    font-weight: 600;
    border-bottom: 3px solid color-mix(in srgb, var(--color-on-accent) 40%, transparent);
}

.offline-banner div {
    margin-top: 0.25rem;
    font-weight: 400;
    font-size: 0.9rem;
}

.bell {
    font-size: 1.2rem;
    text-decoration: none;
}

.hint {
    padding: 0.5rem 0.75rem;
    border-left: 4px solid var(--color-warning);
    background: color-mix(in srgb, var(--color-warning) 12%, var(--color-bg));
}

.content-page h1 {
    margin-top: 0;
}

textarea {
    display: block;
    width: 100%;
    padding: 0.5rem;
    margin-top: 0.25rem;
    border: 1px solid var(--color-text);
    border-radius: 4px;
    background: var(--color-bg);
    color: var(--color-text);
    font: inherit;
}

/* --- Dashboard --- */

.monitor {
    padding: 0.75rem 1rem;
    border-radius: 6px;
    font-weight: 600;
}

.monitor-gruen { background: color-mix(in srgb, var(--color-accent) 20%, var(--color-bg)); }
.monitor-gelb { background: color-mix(in srgb, var(--color-warning) 25%, var(--color-bg)); }
.monitor-rot { background: color-mix(in srgb, var(--color-danger) 25%, var(--color-bg)); }
.monitor-grau { background: color-mix(in srgb, var(--color-text) 12%, var(--color-bg)); }

.bars {
    display: flex;
    align-items: flex-end;
    gap: 4px;
    height: 8rem;
    max-width: 30rem;
}

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

.bar-fill {
    width: 100%;
    background: var(--color-accent-bg);
    border-radius: 3px 3px 0 0;
    min-height: 2px;
}

.bar-label {
    font-size: 0.65rem;
    opacity: 0.7;
}

.dashboard-columns {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 1rem;
}

.filter-form,
.mass-form {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: 0.5rem 1rem;
    margin-bottom: 1rem;
}

.filter-form label,
.mass-form label {
    margin: 0;
    font-size: 0.85rem;
}

tr.ausgeschlossen {
    opacity: 0.5;
    text-decoration: line-through;
}

pre.payload {
    background: color-mix(in srgb, var(--color-text) 8%, var(--color-bg));
    padding: 0.75rem;
    border-radius: 6px;
    overflow-x: auto;
}

/* --- Availability view --- */

.legend {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.chip {
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
    font-size: 0.85rem;
    border: 1px solid color-mix(in srgb, var(--color-text) 30%, transparent);
}

.chip-frei { background: color-mix(in srgb, var(--color-accent) 15%, var(--color-bg)); }
.chip-belegt { background: color-mix(in srgb, var(--color-info) 25%, var(--color-bg)); }
.chip-eingeschraenkt { background: color-mix(in srgb, var(--color-warning) 30%, var(--color-bg)); }
.chip-gesperrt { background: color-mix(in srgb, var(--color-danger) 30%, var(--color-bg)); }

/* Aktive-Filter-Chips (Issue #8): nur Abweichungen vom Default, horizontal
   scrollbar auf schmalen Bildschirmen statt umzubrechen. */
.chip-row {
    display: flex;
    flex-wrap: nowrap;
    gap: 0.5rem;
    list-style: none;
    margin: 0.5rem 0 0;
    padding: 0 0 0.25rem;
    overflow-x: auto;
}

.chip-row .chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    white-space: nowrap;
    background: color-mix(in srgb, var(--color-accent) 15%, var(--color-bg));
}

.chip-remove {
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    color: inherit;
    font: inherit;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
}

/* Mehrfach-Auswahl-Chips im Filter-Sheet (Issue #63: Arten der Sportheim-
   Termine). Anders als .chip-row sind das echte Buttons mit Zustand; der
   aktive Zustand hängt an aria-pressed, damit Screenreader ihn ohne
   Zusatztext bekommen (Abnahmeregel "Farbe ist nie das einzige Signal").
   Touch-Ziel >= 44px (CLAUDE.md Abschnitt 8). */
.chip-toggle-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.25rem;
}

.chip-toggle {
    min-height: 44px;
    padding: 0.35rem 0.8rem;
    border-radius: 999px;
    border: 1px solid color-mix(in srgb, var(--color-text) 30%, transparent);
    background: var(--color-bg);
    color: inherit;
    font: inherit;
    font-size: 0.9rem;
    cursor: pointer;
}

.chip-toggle[aria-pressed="true"] {
    background: color-mix(in srgb, var(--color-accent) 25%, var(--color-bg));
    border-color: var(--color-accent);
    font-weight: 600;
}

.chip-toggle[aria-pressed="true"]::before {
    content: "✓ ";
}

/* Segmented Control im Formular (Issue #63: Art eines Sportheim-Termins) -
   Radios ohne eigene Optik, das Label trägt den Zustand. */
.segmented {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    border: none;
    padding: 0;
    margin: 0 0 0.75rem;
}

.segmented legend {
    padding: 0;
    margin-bottom: 0.25rem;
}

.segmented label {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    min-height: 44px;
    padding: 0.25rem 0.75rem;
    border-radius: 999px;
    border: 1px solid color-mix(in srgb, var(--color-text) 30%, transparent);
    cursor: pointer;
}

.segmented label:has(input:checked) {
    background: color-mix(in srgb, var(--color-accent) 25%, var(--color-bg));
    border-color: var(--color-accent);
    font-weight: 600;
}

/* Filter-Button + Badge (Issue #8): der Button öffnet Panel/Bottom-Sheet
   (dialog.filter-sheet, wiederverwendet dieselbe .sheet-Mechanik wie die
   übrigen Dialoge - mobil Bottom-Sheet, Desktop zentriertes Panel). */
.filter-button {
    position: relative;
}

.badge {
    display: inline-block;
    min-width: 1.1rem;
    padding: 0 0.35rem;
    border-radius: 999px;
    background: var(--color-warning-bg);
    color: var(--color-on-accent);
    font-size: 0.75rem;
    font-weight: 700;
    line-height: 1.1rem;
    text-align: center;
}

dialog.filter-sheet .filter {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    margin-bottom: 0.75rem;
}

.venue-block {
    margin-bottom: 2rem;
}

.venue-block .address {
    margin-top: -0.5rem;
    font-size: 0.9rem;
    opacity: 0.8;
}

.pitch-block {
    margin-bottom: 1rem;
}

.pitch-block h4 {
    margin: 0.5rem 0 0.25rem;
}

.day-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 3px;
}

.day-label {
    flex: 0 0 5.5rem;
    font-size: 0.8rem;
}

.timeline {
    position: relative;
    flex: 1;
    height: 2rem;
    background: color-mix(in srgb, var(--color-accent) 10%, var(--color-bg));
    border: 1px solid color-mix(in srgb, var(--color-text) 20%, transparent);
    border-radius: 4px;
    overflow: hidden;
}

.segment,
.segment-layer {
    position: absolute;
    top: 0;
    bottom: 0;
}

.segment {
    border: none;
    border-radius: 0;
    padding: 0 0.3rem;
    font-size: 0.7rem;
    color: var(--color-text);
    background: transparent;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    text-align: left;
    min-height: 0;
    font-weight: 400;
    cursor: pointer;
}

.segment-belegt {
    background: color-mix(in srgb, var(--color-info) 35%, var(--color-bg));
}

.segment-eingeschraenkt {
    background: color-mix(in srgb, var(--color-warning) 40%, var(--color-bg));
}

.segment-gesperrt {
    background: color-mix(in srgb, var(--color-danger) 40%, var(--color-bg));
}

/* Doppelbelegung (CLAUDE.md Abschnitt 3): zusätzlich zur belegt-Farbe ein
   Schraffur-Overlay + Rahmen, dieselbe Technik wie .eingeschraenkt-layer -
   hier als background-image (Longhand) statt background-Shorthand, damit
   die von .segment-belegt gesetzte Hintergrundfarbe erhalten bleibt. */
.segment-doppelbelegung {
    background-image: repeating-linear-gradient(
        45deg,
        color-mix(in srgb, var(--color-warning) 45%, transparent),
        color-mix(in srgb, var(--color-warning) 45%, transparent) 6px,
        transparent 6px,
        transparent 12px
    );
    outline: 2px solid var(--color-warning);
    outline-offset: -2px;
    font-weight: 600;
}

.chip-doppelbelegung {
    background: color-mix(in srgb, var(--color-warning) 45%, var(--color-bg));
    font-weight: 600;
}

.eingeschraenkt-layer {
    background: repeating-linear-gradient(
        45deg,
        color-mix(in srgb, var(--color-warning) 35%, transparent),
        color-mix(in srgb, var(--color-warning) 35%, transparent) 6px,
        transparent 6px,
        transparent 12px
    );
    pointer-events: none;
}

/* --- Admin --- */

.admin-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1rem;
    margin-top: 0.5rem;
}

.admin-nav a {
    color: var(--color-accent);
    text-decoration: none;
    font-weight: 600;
}

.admin-nav a:hover {
    text-decoration: underline;
}

.narrow {
    max-width: 34rem;
}

form label {
    display: block;
    margin-bottom: 0.75rem;
}

form label input[type="text"],
form label input[type="password"],
form label input[type="number"],
form label select {
    display: block;
    width: 100%;
    padding: 0.5rem;
    margin-top: 0.25rem;
    border: 1px solid var(--color-text);
    border-radius: 4px;
    background: var(--color-bg);
    color: var(--color-text);
    font: inherit;
}

form label.checkbox input {
    width: auto;
    margin-right: 0.4rem;
}

fieldset {
    border: 1px solid var(--color-text);
    border-radius: 4px;
    margin: 0 0 0.75rem;
}

/* team/weekday multi-select in the booking dialog (touch targets >= 44px) */
.checkbox-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 0.75rem;
}

.checkbox-list label {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    min-height: 44px;
    white-space: nowrap;
}

.checkbox-list input[type="checkbox"] {
    width: 1.15rem;
    height: 1.15rem;
}

.dialog-actions.vertical {
    flex-direction: column;
    align-items: stretch;
}

.dialog-actions.vertical .button {
    width: 100%;
}

button,
.button {
    display: inline-block;
    padding: 0.5rem 1rem;
    border: none;
    border-radius: 4px;
    background: var(--color-accent-bg);
    color: var(--color-on-accent);
    font: inherit;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}

button.linklike {
    background: none;
    color: var(--color-accent);
    padding: 0;
    text-decoration: underline;
    font-weight: 600;
}

button.linklike.danger {
    color: var(--color-danger);
}

/* Issue #68: gemeinsame Button-Leiste im Termin-Detaildialog (alle
   Termintypen) - "Schließen" und Lösch-Aktionen sind echte, farblich
   abgesetzte Buttons statt linklike-Text, damit sie als Button erkennbar
   sind und ein Touch-Ziel >= 44px haben (min-height unten). Destruktive
   Aktionen bleiben zusätzlich über ihr Textlabel erkennbar, nicht nur über
   Farbe (CLAUDE.md Abschnitt 8: "Farbe ist nie das einzige Signal"). */
.button.secondary {
    background: color-mix(in srgb, var(--color-text) 12%, var(--color-bg));
    color: var(--color-text);
}

.button.danger {
    background: var(--color-danger-bg);
    color: var(--color-on-accent);
}

.termin-actions .button {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

@media (max-width: 767px) {
    .termin-actions {
        flex-direction: column;
        align-items: stretch;
    }

    .termin-actions .button {
        width: 100%;
    }
}

.inline-form {
    display: inline;
}

table {
    border-collapse: collapse;
    width: 100%;
    margin-bottom: 1rem;
}

th, td {
    text-align: left;
    padding: 0.5rem;
    border-bottom: 1px solid color-mix(in srgb, var(--color-text) 25%, transparent);
}

.swatch {
    display: inline-block;
    width: 1.25rem;
    height: 1.25rem;
    border-radius: 4px;
    vertical-align: middle;
    border: 1px solid color-mix(in srgb, var(--color-text) 30%, transparent);
}

/* Drag&Drop-Sortierung (Issue #27): Handle-Spalte in Bereiche-/Teams-/
   Plätze-/Spielstätten-Listen; Touch-Ziel >=44px (CLAUDE.md Abschnitt 8). */
.drag-handle {
    width: 2.75rem;
    min-width: 2.75rem;
    height: 2.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: grab;
    touch-action: none;
    font-size: 1.25rem;
    color: color-mix(in srgb, var(--color-text) 50%, transparent);
    user-select: none;
}

tr.dragging {
    opacity: 0.5;
}

.palette {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding: 0.5rem;
}

.palette-option {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    margin: 0;
}

.palette-label {
    font-size: 0.85rem;
}

.flash {
    padding: 0.75rem 1rem;
    border-radius: 4px;
    background: color-mix(in srgb, var(--color-accent) 15%, var(--color-bg));
    border: 1px solid var(--color-accent);
}

/* Maintenance banner in the admin layout: shown on EVERY admin page while
   the flag is set, because both a crashed update and an abandoned rebuild
   can leave it behind and the way out has to be reachable from anywhere.
   Deliberately loud - while it is up, the public site is a 503. */
.wartung-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1rem;
    padding: 0.75rem 1rem;
    margin-bottom: 1rem;
    border-radius: 4px;
    background: color-mix(in srgb, var(--color-danger) 12%, var(--color-bg));
    border: 1px solid var(--color-danger);
}

.wartung-banner p {
    margin: 0;
    flex: 1 1 20rem;
}

/* Explanatory paragraph before a destructive or long-running admin action. */
.hinweis {
    padding: 0.75rem 1rem;
    border-radius: 4px;
    background: color-mix(in srgb, var(--color-text) 6%, var(--color-bg));
    border-left: 3px solid color-mix(in srgb, var(--color-text) 35%, var(--color-bg));
    font-size: 0.95rem;
}

.error-message,
.field-error {
    color: var(--color-danger);
}

.field-error {
    display: block;
    font-size: 0.85rem;
    margin-top: 0.2rem;
}

.stat-list {
    list-style: none;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}

.stat-list li {
    padding: 1rem;
    border: 1px solid color-mix(in srgb, var(--color-text) 25%, transparent);
    border-radius: 8px;
}

progress {
    width: 100%;
}

/* --- Legende (Issue #38) --- */

/* [data-legende]-Mount, von public/js/legende.js gefüllt (Startseite,
   /legende, Kalender-Overlay - dieselbe Komponente an allen drei Stellen).
   Ein Raster statt einer festen Spaltenzahl: auf breiten Bildschirmen
   (Startseite/Legende-Seite) liegen Spielstätten/Plätze/Teams nebeneinander,
   im schmaleren Overlay-Panel/Bottom-Sheet fällt es von selbst auf eine
   Spalte zurück. */
.legende {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 1.25rem 2rem;
    align-items: start;
}

.legende-abschnitt h4 {
    margin: 0 0 0.5rem;
}

.legende-abschnitt h5 {
    margin: 0.75rem 0 0.25rem;
    font-size: 0.9rem;
    color: var(--color-text-muted);
}

.legende-liste {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.legende-eintrag {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* Zwei Ringe wie .ev-punkt (Issue #39): funktioniert unabhängig davon, wie
   hell oder dunkel die jeweilige Team-/Platzfarbe ausfällt - Kontrast in
   hell UND dunkel geprüft, ohne die Palette selbst je Modus anzupassen.
   Spielstätte (Dreieck, s. .legende-punkt-venue unten) braucht dafür eine
   eigene, clip-path-basierte Umsetzung statt dieses box-shadow-Rings. */
.legende-punkt {
    display: inline-block;
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
    box-shadow:
        0 0 0 1px var(--color-bg),
        0 0 0 2px color-mix(in srgb, var(--color-text) 55%, transparent);
}

.legende-punkt-team {
    border-radius: 50%;
}

.legende-punkt-pitch {
    border-radius: 3px;
}

/* Spielstätte = Dreieck (dieselbe Form wie .ev-punkt-venue am Termin, s.
   dort für die ausführliche Begründung): box-shadow folgt der Box, nicht
   der geclippten Form, daher zwei zusätzliche Dreieck-Ebenen statt eines
   Rings; legende.js::punkt() erzeugt für form='venue' zusätzlich das
   Kind-Element .legende-punkt-venue-fill mit der eigentlichen Farbe. */
.legende-punkt-venue {
    position: relative;
}

.legende-punkt-venue::before,
.legende-punkt-venue::after {
    content: '';
    position: absolute;
    clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

.legende-punkt-venue::before {
    inset: -25%;
    background: color-mix(in srgb, var(--color-text) 55%, transparent);
    z-index: 1;
}

.legende-punkt-venue::after {
    inset: -10%;
    background: var(--color-bg);
    z-index: 2;
}

.legende-punkt-venue-fill {
    position: relative;
    z-index: 3;
    display: block;
    width: 100%;
    height: 100%;
    clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

/* Issue #47: Sportheim/Raum bekommen eine vierte, von Kreis (Team), Quadrat
   (Platz) und Dreieck (Spielstätte) klar unterscheidbare Form - eine Raute
   (gedrehtes Quadrat), in der Farbe der zugehörigen Spielstätte, da
   Sportheime noch keine eigene Farbe haben (Issue #36, bewusst nicht Teil
   dieses Issues). */
.legende-punkt-heim {
    border-radius: 2px;
    transform: rotate(45deg);
}

.legende-eintrag-heim {
    flex-direction: column;
    align-items: stretch;
    gap: 0.35rem;
}

.legende-eintrag-kopf {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.legende-liste-raeume {
    margin-left: 1.5rem;
    font-size: 0.9rem;
}

.legende-symbol-erklaerung {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    margin: 0 0 0.5rem;
}

.legende-symbol-erklaerung .legende-punkt {
    margin-top: 0.2rem;
}

.legende-symbol-erklaerung:last-child {
    margin-bottom: 0;
}

.legende-compact summary {
    cursor: pointer;
    font-weight: 600;
    padding: 0.5rem 0;
}

.legende-compact[open] summary {
    margin-bottom: 0.75rem;
}

.legende-sheet {
    max-width: 40rem;
    max-height: 85vh;
    overflow-y: auto;
}
