:root {
  /* DLRG-Rot – exakt aus dem Vereinslogo (static/img/dlrg-logo.png)
     entnommen (#e30613), damit Header und Logo nahtlos ineinander
     übergehen. Für den Status "Entschuldigt" gibt es bewusst eine eigene
     Farbe (--status-entschuldigt), damit er sich weiterhin klar von
     "Abwesend"/"Urlaub" unterscheidet, statt auch rot zu werden. */
  --brand: #e30613;
  --brand-dark: #a80410;
  --status-entschuldigt: #0057a3;
  --green: #1c8a4c;
  --red: #c0392b;
  --amber: #b8860b;
  /* Kräftiger als die pastelligen Badge-Farben oben, damit die Markierung
     auf der Live-Anzeige/dem Kiosk auch aus einigen Metern Entfernung noch
     klar als "hervorgehoben" erkennbar ist, nicht nur als leichter Tonwert. */
  --ziel-erreicht-bg: #bdeecb;
  --schwimmt-bg: #fff0a3;
  --bg: #f4f6f8;
  --card: #ffffff;
  --text: #1a2733;
  --muted: #63737f;
  --border: #dde3e8;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  /* Seit v250 (Torstens Rückmeldung: "auf dem Handy ist alles etwas
     außerhalb und zu groß"): overflow-wrap ist eine vererbte Eigenschaft
     (siehe MDN) – einmal hier gesetzt, gilt sie für JEDE Überschrift/
     jeden Text auf jeder Seite, ohne jede einzelne h1/h2/Karten-Überschrift
     separat behandeln zu müssen. Betroffen war konkret z.B. die
     Karten-Überschrift "Einverständnis-/Gesundheitserklärung"
     (#erklaerung-card in eltern.html) – ein einzelnes, zu langes Wort ohne
     Umbruchstelle drückte dort (wie zuvor schon in der Kopfzeile, siehe
     header.app-header h1 oben) die ganze Karte über den Bildschirmrand
     hinaus. Bewusst zusätzlich zur bereits bestehenden, spezifischeren
     Regel für die Kopfzeile (die bleibt als Dokumentation des
     ursprünglich gefundenen Falls stehen). */
  overflow-wrap: break-word;
}

header.app-header {
  background: var(--brand);
  color: white;
  padding: 14px 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Seit v310 (Torstens Wunsch: "kannst du den zurück Button in den roten
     Kasten immer nach rechts unten setzen") – Bezugsrahmen für die
     absolute Positionierung von #kompakt-header-zurueck-btn weiter unten,
     damit dieser IMMER an derselben Ecke der Kopfzeile klebt, egal wie
     hoch sie gerade ist (z.B. bei langem Kursnamen in #header-ansicht
     oder umgebrochener Marke auf schmalen Handys). */
  position: relative;
  /* Seit v250 (Torstens Rückmeldung: "auf dem Handy ist alles etwas
     außerhalb und zu groß"): ohne dieses min-width:0 verweigert sich ein
     Flex-Item standardmäßig, unter die Breite seines eigenen Inhalts zu
     schrumpfen ("min-width: auto") – bei einem schmalen Handy-Bildschirm
     drückte das lange Wort "Gesundheitserklärung" (siehe .brand-Kette
     unten) dadurch die GANZE Kopfzeile über den sichtbaren Rand hinaus,
     statt in der zweiten Zeile umzubrechen. Betrifft dieselbe
     .brand/<a>/<div>-Verschachtelung in allen Seiten mit dieser Kopfzeile
     (siehe grep auf class="brand" – mcc.html, eltern.html,
     erklaerung.html, erklaerung-neu.html, kiosk.html, anzeige.html,
     event.html, mein-profil.html, personen-detail.html,
     personen-profil.html, start.html), daher hier zentral statt in jeder
     Datei einzeln behoben. */
  min-width: 0;
}

header.app-header .brand,
/* Die Marke ist teils zusätzlich in ein <a href="/"> mit demselben
   inline display:flex gepackt (Klick aufs Logo führt zur Startseite,
   siehe Kommentar in mcc.html) – dieselbe min-width:0-Kette muss dann
   auch für dieses <a> gelten, sonst wirkt sie nur auf .brand selbst und
   das Problem bleibt eine Ebene tiefer bestehen. */
header.app-header .brand > a {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

header.app-header .brand > div,
header.app-header .brand > a > div {
  min-width: 0;
}

header.app-header .logo {
  height: 44px;
  width: auto;
  display: block;
  border-radius: 4px;
  /* Ein Flex-Item schrumpft sonst zuerst gleichmäßig mit den anderen
     Items statt zuletzt – ohne flex-shrink:0 würde das Logo bei sehr
     schmalen Bildschirmen mitgequetscht, bevor der eigentlich lange Text
     daneben umbricht. */
  flex-shrink: 0;
}

header.app-header h1 {
  font-size: 1.05rem;
  margin: 0;
  font-weight: 600;
  /* Bricht auch ein einzelnes, sehr langes Wort (z.B.
     "Gesundheitserklärung") innerhalb der Zeile um, statt es unverändert
     über den Rand hinausragen zu lassen – das eigentliche Gegenstück zum
     min-width:0 oben, das dafür nur den PLATZ dafür freimacht. */
  overflow-wrap: break-word;
}

header.app-header .sub {
  font-size: 0.8rem;
  opacity: 0.85;
  overflow-wrap: break-word;
}

/* Seit v265 (Torstens Rückmeldung, mit Fotos vom iPhone 17 Pro Max: "die
   Ansicht beim Benutzer Login im MCC auf einen iPhone 17 Pro max ist
   nicht schön") – auf den Fotos war der Markenblock ("MCC (Mönsheim
   Control Center)") auf eine einstellige Zeichenbreite zusammengequetscht
   und das Logo dadurch mittig in der dadurch sehr hohen Spalte
   "versunken". Ursache: header.app-header ist EIN einziges,
   nicht-umbrechendes Flex-Row (kein flex-wrap), und .brand hat seit v250
   bewusst min-width:0 (siehe dortiger Kommentar), damit ein einzelnes
   langes Wort nicht die ganze Kopfzeile über den Rand schiebt. Genau das
   erlaubt es dem Flex-Algorithmus aber auch, .brand fast auf 0 Breite zu
   quetschen, sobald die rechte Seite (bei einer Admin-Anmeldung mit
   "angemeldet als …", Versionslink, "Als App speichern", "Mein Profil",
   "abmelden" UND der Nav-Zeile darunter) mehr Platz beansprucht, als auf
   einem Handy übrig bleibt – h1 bricht dann (overflow-wrap:break-word)
   buchstabenweise um, statt normal an Wortgrenzen.
   Fix: unterhalb dieser Breite wird die Kopfzeile zur Spalte – Marke oben
   in voller Breite (h1 bricht dort wieder normal an Wortgrenzen um), die
   rechte Seite darunter, ebenfalls linksbündig und mit flex-wrap, damit
   ihre Buttons/Links sauber untereinander/nebeneinander umbrechen statt
   die Marke zu verdrängen. Derselbe Header-Aufbau (.app-header/.brand)
   steckt unverändert in allen Seiten mit dieser Kopfzeile (siehe
   Kommentar bei .brand oben) – die Regel hier wirkt daher zentral überall
   mit, ohne dass jede Datei einzeln angefasst werden muss. */
@media (max-width: 700px) {
  header.app-header {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }
  header.app-header > .brand {
    width: 100%;
  }
  header.app-header .header-actions {
    width: 100%;
    align-items: flex-start;
  }
  header.app-header .header-meta-row,
  header.app-header .header-nav-row {
    justify-content: flex-start;
  }
}

/* Seit v313 (Torstens Wunsch: "in der Handy/Tablet Ansicht muss die
   Titelleiste oben verankert werden") – auf Handy & Tablet (derselbe
   1024px-Schwellwert wie KOMPAKT_BREITE in mcc.js, siehe
   istKompaktBreite()/aktualisiereBereichsNavigation() dort; reines CSS
   kann diese JS-Konstante nicht direkt lesen, daher hier als eigener,
   inhaltlich synchron zu haltender Media-Query-Wert) bleibt die rote
   Kopfzeile beim Scrollen oben am Bildschirmrand kleben, statt mit dem
   übrigen Seiteninhalt wegzuscrollen – u.a. bleibt so der
   "← Zurück"-Knopf (#kompakt-header-zurueck-btn oben) auf einer langen,
   tief scrollenden Seite jederzeit erreichbar. Auf dem Desktop
   (>1024px) bleibt die Kopfzeile bewusst wie bisher normal im
   Textfluss. Derselbe Header-Aufbau steckt unverändert in allen Seiten
   mit dieser Kopfzeile (siehe Kommentar bei .brand oben) – die Regel
   wirkt daher zentral überall mit. z-index deutlich unter den
   Modal-Overlays (1000, siehe .modal-overlay weiter unten), damit ein
   offenes Modal die Kopfzeile weiterhin zuverlässig überdeckt. */
@media (max-width: 1024px) {
  header.app-header {
    position: sticky;
    top: 0;
    z-index: 500;
  }
}

/* Seit v265: löst die bisher direkt inline gestylten Buttons/Links rechts
   in der Kopfzeile (mcc.html) in wiederverwendbare Klassen auf – vor
   allem, damit .header-meta-row/.header-nav-row per flex-wrap sauber
   umbrechen können (statt sich als reiner Fließtext krumm zu stapeln) und
   die obige Media-Query sie gezielt links ausrichten kann. Rein optisch
   unverändert auf breiten Bildschirmen (dieselben Farben/Abstände wie
   zuvor). */
.header-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}
.header-meta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  column-gap: 8px;
  row-gap: 4px;
  color: white;
  font-size: 0.8rem;
}
.header-pill {
  background: rgba(255, 255, 255, 0.15);
  color: white;
  border-color: rgba(255, 255, 255, 0.6);
}
/* Seit v310 (Torstens Wunsch: "kannst du den zurück Button in den roten
   Kasten immer nach rechts unten setzen") – bis v309 saß der Knopf als
   normales Flex-Item ganz oben in .header-actions (siehe deren
   flex-direction:column), damit wanderte er auf breiten Bildschirmen
   optisch nach oben statt unten. Absolut positioniert relativ zu
   header.app-header (siehe dessen position:relative oben) bleibt er
   IMMER an der rechten unteren Ecke der Kopfzeile kleben – unabhängig
   davon, wie hoch die Kopfzeile gerade ist (z.B. bei zweizeiliger Marke
   auf schmalen Handys oder langem Kursnamen in #header-ansicht) und ohne
   den Flex-Ablauf von .header-actions/.header-meta-row zu stören. Seit
   v311 (siehe aktualisiereBereichsNavigation() in mcc.js) ist dieser Knopf
   der allgemeine Zurück-Knopf der gesamten Kompakt-Navigation, nicht mehr
   nur von "Prüfungskarten abhaken" – daher der generische Name. */
#kompakt-header-zurueck-btn {
  position: absolute;
  right: 20px;
  bottom: 14px;
}
/* Seit v311 (Torstens Rückmeldung zum Desktop-Layout: "nur auf dem
   Desktop muss unter dem zurück etwas verschoben werden") – auf einem
   breiten Bildschirm ist die Kopfzeile nur eine einzige, kurze Zeile hoch
   (Marke und .header-meta-row passen nebeneinander), ohne zusätzlichen
   Platz läge der oben absolut positionierte Zurück-Knopf dann auf
   derselben Höhe wie "angemeldet als/Mein Profil/abmelden" und drängte
   sich optisch mit ihnen. Diese Klasse (von
   aktualisiereBereichsNavigation() in mcc.js nur dann gesetzt, wenn der
   Zurück-Knopf gerade sichtbar ist) schafft zusätzlichen Platz UNTER
   dieser Zeile, in den der Knopf sauber hineinpasst, ohne die Kopfzeile
   bei jedem anderen Tab unnötig höher zu machen. */
header.app-header.hat-kompakt-zurueck {
  padding-bottom: 46px;
}
.header-version-link {
  color: white;
  text-decoration: underline;
  opacity: 0.85;
}
.header-nav-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  column-gap: 14px;
  row-gap: 4px;
}
.header-nav-row a {
  color: white;
  font-size: 0.8rem;
}

main {
  max-width: 780px;
  margin: 0 auto;
  padding: 16px;
}

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px;
  margin-bottom: 16px;
}

button {
  font: inherit;
  cursor: pointer;
  border: none;
  border-radius: 8px;
  padding: 10px 16px;
  background: var(--brand);
  color: white;
}

button.secondary {
  background: white;
  color: var(--brand);
  border: 1px solid var(--brand);
}

button:disabled { opacity: 0.5; cursor: not-allowed; }

input, select {
  font: inherit;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  width: 100%;
}

/* Checkboxen/Radios sollen NICHT die width:100% von oben erben – sonst
   blähen sie sich (v.a. innerhalb eines <label>) auf die volle Breite des
   Elternelements auf und schieben den danebenstehenden Beschriftungstext
   in die nächste Zeile bzw. verdrängen ihn ganz (Bug in v85/v86 gemeldet:
   Checkbox "Trainer pro Kind" ohne sichtbare Beschriftung). */
input[type="checkbox"], input[type="radio"] {
  width: auto;
  flex: 0 0 auto;
}

label { font-size: 0.85rem; color: var(--muted); display: block; margin-bottom: 4px; }

.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 8px 6px; border-bottom: 1px solid var(--border); font-size: 0.92rem; }
th { color: var(--muted); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.02em; }

.badge {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
}
.badge.unbekannt { background: #eceff1; color: var(--muted); }
.badge.anwesend { background: #e2f4e9; color: var(--green); }
.badge.abwesend_manuell { background: #fdeceb; color: var(--red); }
.badge.entschuldigt { background: #eaf1fb; color: var(--status-entschuldigt); }

.btn-mini { padding: 5px 9px; font-size: 0.78rem; border-radius: 6px; }

/* Eltern-Ansicht: Liste der künftigen Kurstage */
.kt-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
}
.kt-row:last-child { border-bottom: none; }
.kt-datum { flex: 1 1 160px; font-weight: 600; color: var(--text); font-size: 0.92rem; }
.kt-grund { flex: 1 1 140px; max-width: 200px; }

/* Seit v267 (Torstens Wunsch: "um den Voraussetzungsblock sollte ein
   Rahmen sein oder eine andere Art der Kenntlichmachung damit man das
   sofort erkennt dass das benötigt wird") – umschließt im Tab "Teilnehmer"
   (renderDetailsPanel() in mcc.js) die drei Blöcke Mitgliedschaft/
   Erklärung/Eintrittsgeld sichtbar als EINEN "das hier muss erfüllt
   sein"-Bereich, statt nur durch die dünnen gestrichelten Trennlinien
   zwischen den einzelnen Blöcken erkennbar zu sein. Amber statt Rot, weil
   der Rahmen selbst kein Fehlerzustand ist (der ist bereits, wo zutreffend,
   an den einzelnen Chips/Zeilen selbst rot markiert, siehe z.B.
   renderMitgliedschaftChip()) – der Rahmen markiert nur "hier hinschauen",
   nicht "hier fehlt etwas". */
.kt-voraussetzungen {
  margin-top: 12px;
  border: 1px solid var(--amber);
  border-radius: 8px;
  padding: 2px 14px 12px 14px;
  background: #fffaf0;
}
.kt-voraussetzungen-titel {
  margin-top: 10px;
  font-weight: 600;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--amber);
}

/* Kursziel erreicht / schwimmt – dieselben Markierungsfarben wie auf
   Live-Anzeige/Kiosk (siehe --ziel-erreicht-bg/--schwimmt-bg oben), hier
   zusätzlich in der internen Übersicht (Matrix), der Teilnehmer-Liste und
   (seit v296) der Schwimmcamp-Trainer-Übersicht (table.ta-matrix, siehe
   matrixHtml() in mcc.js). */
.kt-row.ziel-erreicht, .matrix td.person-col.ziel-erreicht, #roster-body tr.ziel-erreicht, table.ta-matrix td.ziel-erreicht { background: var(--ziel-erreicht-bg); }
.kt-row.schwimmt-status, .matrix td.person-col.schwimmt-status, #roster-body tr.schwimmt-status, table.ta-matrix td.schwimmt-status { background: var(--schwimmt-bg); }
.btn-mini.schwimmt-active { background: var(--schwimmt-bg); color: var(--text); border-color: var(--amber); }
.btn-mini.ziel-active { background: var(--ziel-erreicht-bg); color: var(--text); border-color: var(--green); }

/* Erreichte Kursziele als kleine Chips (seit v67 mehrere pro Teilnehmer
   möglich statt eines einzelnen Häkchens, siehe ParticipantKursziel) –
   dieselbe grüne Markierungsfarbe wie zuvor der "Kursziel erreicht"-Button. */
.kursziel-chips { display: inline-flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.chip.ziel-chip {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--ziel-erreicht-bg); border: 1px solid var(--green);
  border-radius: 999px; padding: 2px 4px 2px 9px; font-size: 0.78rem; color: var(--text);
}
.chip.ziel-chip .chip-remove {
  cursor: pointer; color: var(--muted); font-weight: 700; padding: 0 5px;
  border-radius: 50%; line-height: 1.4;
}
.chip.ziel-chip .chip-remove:hover { background: rgba(0,0,0,0.08); color: var(--red); }

#reader { width: 100%; max-width: 420px; margin: 0 auto; }

.hint { color: var(--muted); font-size: 0.85rem; }

/* Seit v266: admin-editierbare Hinweistexte (siehe [data-text-key] in
   mcc.html sowie ladeTextInhalteUndAnwenden() in mcc.js). Das Stift-Symbol
   sitzt inline direkt hinter dem Text, dezent und nur bei Hover/Fokus des
   umgebenden Absatzes deutlich sichtbar, damit es im normalen Lesefluss
   nicht ablenkt. */
.text-edit-wrapper { position: relative; }
.text-edit-stift {
  border: none;
  background: none;
  cursor: pointer;
  font-size: 0.85em;
  opacity: 0.35;
  padding: 0 0 0 6px;
  vertical-align: middle;
}
.text-edit-wrapper:hover .text-edit-stift,
.text-edit-stift:focus { opacity: 1; }
/* Werkzeugleiste (Fett/Schriftgröße) über dem Bearbeiten-Textfeld, seit
   v328 (Torstens Wunsch: "die Textformatierung muss anpassbar sein
   (Schriftgröße und Fett)"), siehe textBearbeitenStarten() in mcc.js. */
.text-edit-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
  font-size: 0.85rem;
}
.text-edit-toolbar select { font: inherit; padding: 2px 4px; }
.text-edit-toolbar .hint { color: var(--muted, #777); font-size: 0.8em; }
.text-edit-textarea {
  width: 100%;
  min-height: 70px;
  font: inherit;
  color: inherit;
  padding: 6px 8px;
  border: 1px solid var(--border, #ccc);
  border-radius: 6px;
  box-sizing: border-box;
  resize: vertical;
}
.text-edit-msg { color: var(--red); font-size: 0.85rem; margin-top: 4px; }

.flash { padding: 10px 14px; border-radius: 8px; margin-bottom: 12px; font-size: 0.9rem; }
.flash.ok { background: #e2f4e9; color: var(--green); }
.flash.error { background: #fdeceb; color: var(--red); }

.center { text-align: center; }

/* Kursübersicht: Matrix Teilnehmer × Kurstage */
.matrix-wrap { overflow-x: auto; }
table.matrix { border-collapse: collapse; }
table.matrix th, table.matrix td { padding: 6px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
table.matrix thead th.date-col { background: var(--brand); color: white; text-align: center; font-weight: 600; }
table.matrix thead th.date-col .hint { color: rgba(255,255,255,0.85); }
.person-col { position: sticky; left: 0; background: var(--card); text-align: left; z-index: 1; box-shadow: 1px 0 0 var(--border); }
thead .person-col { background: var(--brand); color: white; z-index: 2; }

/* Seit v280: Trainer-Übersicht im Tab "Events"/"Schwimmcamp" – Zeit×Platz-
   Matrix, dieselbe Datengrundlage wie der Excel-Export (siehe matrixHtml()
   in mcc.js), mit editierbaren Trainer-Feldern direkt in der Zelle. Torstens
   Meldung 02.09.: "Die Zuordnung der Trainer für das Schwimmcamp ist zu
   unübersichtlich wir müssen das so wie auf dem Export hinbekommen". Feste
   Rahmenlinien statt der sonst üblichen reinen Unterstriche, damit die
   Spaltenblöcke je Stufe optisch genauso klar abgegrenzt sind wie im Excel. */
table.ta-matrix { border-collapse: collapse; font-size: 0.85rem; }
table.ta-matrix th, table.ta-matrix td {
  border: 1px solid var(--border);
  padding: 4px 6px;
  vertical-align: top;
  min-width: 110px;
}
table.ta-matrix thead th { text-transform: none; letter-spacing: normal; text-align: center; color: var(--text); }
table.ta-matrix tbody td:first-child, table.ta-matrix tbody td:nth-child(2) {
  text-align: center;
  font-weight: 600;
  white-space: nowrap;
  min-width: unset;
}
.ta-matrix-name { font-weight: 600; margin-bottom: 2px; }
.ta-matrix-trainer { width: 100%; font-size: 0.85rem; }
/* Seit v315 (Torstens Wunsch: "in der Trainer-Übersicht des Schwimmcamps
   sollen die Trainer Felder rot sein, wenn ein Termin gebucht ist aber
   noch kein Trainer eingetragen ist") – dieselbe Rot-Palette wie bei
   .ta-pille.voll/.slot-belegung.voll (#f5dcd7-Hintergrund + var(--red)),
   hier auf das <select>-Feld selbst statt auf die ganze Zelle angewendet,
   damit es sich klar von der bestehenden grün/gelb-Zellmarkierung
   (.ziel-erreicht/.schwimmt-status, siehe weiter unten) unterscheidet. Wird
   nur gesetzt, wenn matrixHtml() in mcc.js einen belegten Platz/Zeitslot
   ohne eingetragenen Trainer erkennt (siehe eventTrainerSelectHtml()). */
.ta-matrix-trainer.ta-matrix-trainer-fehlt { background: #f5dcd7; border-color: var(--red); color: var(--red); }
.ta-matrix-pause { text-align: center; font-style: italic; color: var(--muted); background: var(--card); }

/* Seit v204 (Torstens Wunsch: "können wir dann auch Badges für ungelesene
   Nachrichten erscheinen lassen?") – ein einziger runder, roter
   Ungelesen-Badge-Stil für alle Einsatzorte: der Nav-Tab/die Kachel
   "💬 Nachrichten" (nav-badge, siehe renderHubTiles()/renderBereichNav() in
   mcc.js), die Chat-Liste (ersetzt die bisherige reine "(3)"-Textanzeige in
   renderNachrichtenChatsListe()) sowie identisch im Eltern-Chat
   (renderElternChat(), eltern.js). Absichtlich derselbe Klassenname/
   dieselbe Optik an allen Stellen statt eigener Varianten, damit "Badge
   für ungelesene Nachrichten" im ganzen Tool einheitlich aussieht. */
.unread-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
  background: var(--red); color: white; font-size: 0.72rem; font-weight: 700;
  line-height: 18px;
}
.nav-badge { margin-left: 6px; }

.count-badges { display: flex; gap: 3px; justify-content: center; margin-top: 4px; }
.count-badge { min-width: 18px; padding: 1px 5px; border-radius: 5px; font-size: 0.7rem; font-weight: 700; color: white; text-align: center; }
.count-badge.anwesend { background: var(--green); }
.count-badge.abwesend_manuell { background: var(--red); }

.pill-group { display: flex; gap: 3px; }
.pill {
  border: 1px solid var(--border);
  background: white;
  font-size: 0.68rem;
  padding: 4px 7px;
  border-radius: 5px;
  cursor: pointer;
  color: var(--muted);
}
.pill.active.anwesend { background: var(--green); border-color: var(--green); color: white; }
.pill.active.abwesend_manuell { background: var(--red); border-color: var(--red); color: white; }
.pill.active.entschuldigt { background: var(--status-entschuldigt); border-color: var(--status-entschuldigt); color: white; }

/* Tab-Navigation */
nav.tabs { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
nav.tabs .tab-btn {
  background: white;
  color: var(--brand);
  border: 1px solid var(--brand);
  padding: 10px 16px;
  border-radius: 8px;
  font-weight: 600;
  font-size: 0.9rem;
}
nav.tabs .tab-btn.active { background: var(--brand); color: white; }

/* Seit v132: "in Vorbereitung"-Platzhalter innerhalb der Bereichs-Nav bzw.
   von .subtabs (siehe #bereich-nav/#einsatz-subtabs in mcc.html) –
   analog zur bestehenden .hub-tile.disabled auf der Startseite: sichtbar,
   aber ausgegraut und nicht klickbar (pointer-events:none, statt gar kein
   onclick zu setzen, damit ein versehentlich doch gesetztes onclick nie
   greifen kann). */
nav.tabs .tab-btn.disabled, .subtabs .subtab-btn.disabled {
  opacity: 0.55;
  cursor: default;
  pointer-events: none;
}

/* Statistik: Auswertung gezielt wählen statt alle Karten gleichzeitig
   untereinander anzuzeigen – bewusst kleiner/zurückhaltender als die
   Haupt-Tabs oben (nav.tabs), damit auf einen Blick klar bleibt, dass das
   eine Auswahl INNERHALB von "Statistik" ist, keine eigene App-Ebene. */
.subtabs { display: flex; gap: 6px; margin-bottom: 4px; flex-wrap: wrap; }
.subtabs .subtab-btn {
  background: var(--card);
  color: var(--text);
  border: 1px solid var(--border);
  padding: 6px 13px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.82rem;
}
.subtabs .subtab-btn.active { background: var(--brand); color: white; border-color: var(--brand); }

/* Seit v255 (ToDo-Liste, Torstens Wunsch: "die unterkategorien hätte ich
   gerne auch als buttons, wenn ich auf eine hauptkategorie klicke gehen die
   unterkategorien auf") – dieselbe Buttonreihen-Optik wie .subtabs oben,
   nur etwas kleiner/zurückhaltender, damit auf einen Blick klar bleibt,
   dass dies eine Auswahl INNERHALB der oben gewählten Hauptkategorie ist,
   keine gleichrangige zweite Tab-Ebene. */
.subtabs.subtabs-unter .subtab-btn {
  padding: 4px 11px;
  font-size: 0.76rem;
}

/* /anzeige und /kiosk laufen auf fest eingerichteten Bildschirmen ohne Maus/
   Touch – ein Scrollbalken wäre dort schlicht unbedienbar. fitAnzeigeLists()
   in anzeige.js/kiosk.js sorgt bereits aktiv dafür, dass alle Namenslisten
   in die verfügbare Höhe passen; dieses overflow:hidden ist zusätzlich ein
   harter Sicherheitsnetz-Fallback, falls trotzdem mal ein paar Pixel Rand
   (z.B. Abstände zwischen Kurskarten) über den sichtbaren Bereich hinausgehen
   – dann wird lieber ein winziger Rand unsichtbar abgeschnitten, als dass
   ein Scrollbalken erscheint. Wichtig: NUR auf diesen beiden Kiosk-Seiten
   (Klasse no-scroll-page), nicht global – die Trainer-Ansicht etc. sollen
   ganz normal scrollen können. */
html:has(body.no-scroll-page), body.no-scroll-page { overflow: hidden; height: 100%; }

/* Live-Anzeige (zweiter Monitor) */
/* ALLE heute sichtbaren Kurse (egal ob zeitgleich oder nur zeitlich
   überlappend sichtbar, siehe ANZEIGE_SICHTBAR_MINUTEN_NACH_START im
   Backend) landen nebeneinander in einer Reihe statt untereinander
   gestapelt – so bekommt jeder Kurs die volle Bildschirmhöhe statt sich
   diese mit anderen Kursen teilen zu müssen, was gerade auf schmalen/
   hochkant montierten Bildschirmen deutlich lesbarer ist. flex: 1 1 300px
   teilt die verfügbare Breite gleichmäßig auf, lässt Karten dabei aber
   nicht unter eine noch gut lesbare Mindestbreite schrumpfen. Reicht die
   Breite für alle Kurse in einer Zeile nicht mehr aus (viele Kurse
   gleichzeitig sichtbar), bricht der Rest per flex-wrap automatisch in
   eine weitere Zeile um – fitAnzeigeLists() in anzeige.js/kiosk.js
   erkennt das anhand der tatsächlich gerenderten Position jeder Karte und
   reserviert für nachfolgende Zeilen ausreichend Platz, damit auch die
   letzte Zeile nicht über den Bildschirm hinausragt. */
.anzeige-time-row { display: flex; flex-wrap: wrap; gap: 20px; align-items: flex-start; margin-bottom: 28px; }
.anzeige-time-row .anzeige-course {
  flex: 1 1 300px;
  min-width: 260px;
  margin-bottom: 0;
  border: 2px solid var(--brand-dark);
  border-radius: 16px;
  padding: 16px 16px 18px;
  background: #eef1f5;
}
.anzeige-course h2 { margin-bottom: 4px; }
/* Trainer-Name im Kiosk-Display (".kiosk-trainer-name", siehe kiosk.js):
   bewusst genauso groß wie der Kursname direkt darüber, damit er von
   Eltern auch auf Distanz am Beckenrand gut lesbar ist – 1.5em relativ
   zum selben Eltern-Element (.anzeige-course) wie h2 (kein eigener fester
   Wert), damit beide IMMER exakt gleich groß bleiben, auch wenn sich die
   Größe von h2 mal ändert. Nur im Kiosk, NICHT in der Live-Anzeige
   (dort weiterhin die kleine ".hint"-Zeile) – dort wurde nur die Größe
   im Kiosk gewünscht. */
.anzeige-course .kiosk-trainer-name {
  font-size: 1.5em;
  font-weight: 700;
  color: var(--text);
  margin: -4px 0 10px;
}
/* Bewusst IMMER genau 2 Spalten nebeneinander (nicht auto-fit/minmax), auch
   wenn die Kurskarte durch einen parallel laufenden zweiten Kurs sehr schmal
   wird: mit auto-fit würden die Spalten bei zu wenig Breite untereinander
   stapeln, was die Karte deutlich höher macht, als auf den Bildschirm passt.
   Bei wenig Breite werden die Namenslisten stattdessen automatisch schmaler
   und – falls nötig – von fitAnzeigeLists() (anzeige.js/kiosk.js) auf
   kleinere Schrift/mehr Spalten umgestellt. minmax(0, 1fr) statt minmax(min-content,1fr)
   verhindert, dass eine sehr lange Kurskarte die Grid-Spalten wieder aufbläht. */
.anzeige-columns { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin-top: 14px; }
.anzeige-col { border-radius: 12px; padding: 14px; background: var(--card); border: 2px solid var(--border); }
.anzeige-col h3 { margin: 0 0 10px 0; font-size: 1.1rem; display:flex; justify-content:space-between; }
.anzeige-col.unbekannt { border-color: var(--border); }
.anzeige-col.anwesend { border-color: var(--green); }
.anzeige-col.entschuldigt { border-color: var(--status-entschuldigt); }
.anzeige-col.abwesend_manuell { border-color: var(--red); }
/* Live-Anzeige & Kiosk: die Anwesend-/Noch-nicht-da-/Entschuldigt-Summen
   sind auf Distanz (zweiter Monitor, Kiosk-Bildschirm) mit der sonst
   überall verwendeten kleinen .badge-Größe schwer ablesbar – hier deutlich
   größer, unabhängig von den (bewusst kleinen) Status-Badges in der
   Trainer-Ansicht. */
.anzeige-col h3 .badge, .anzeige-subdivider h4 .badge {
  font-size: 1.4rem;
  padding: 3px 15px;
  min-width: 2ch;
}
/* Namenslisten: Standardmäßig eine einfache Liste. Passt eine Liste nicht
   mehr ohne Scrollbalken in die verfügbare Höhe, rechnet fitAnzeigeLists()
   in anzeige.js/kiosk.js live aus, wie viele Spalten nötig sind, und setzt
   dafür per JS display:grid + grid-auto-flow:column direkt auf dem <ul>
   (siehe dort) – CSS column-width wurde bewusst NICHT verwendet, weil es
   nur auf verfügbare BREITE reagiert, nicht auf Höhe, und bei knapper Höhe
   Teilnehmer unbemerkt "verschluckt" statt sie in weitere Spalten zu
   verteilen. .list-compact/.list-tiny sind Ausweichstufen mit kleinerer
   Schrift, die fitAnzeigeLists() zuschaltet, wenn selbst mehrspaltig bei
   normaler Schriftgröße nicht genug Platz in der Breite ist. Die Karte
   selbst wächst in die Breite, wenn Platz ist (z.B. wenn nur ein Kurs
   läuft), siehe .anzeige-time-row .anzeige-course. */
.anzeige-col ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* white-space: nowrap + text-overflow: ellipsis ist hier keine Kosmetik,
   sondern PFLICHT für die JS-Einpassung (fitAnzeigeLists() in
   anzeige.js/kiosk.js): die rechnet mit einer einheitlichen Zeilenhöhe pro
   Liste. Dürfte ein langer Vor-/Nachname (z.B. "Sareena Raveendralingam")
   in einer schmalen Spalte umbrechen, wäre diese eine Zeile plötzlich
   doppelt so hoch wie angenommen – die ganze Höhenrechnung würde nicht mehr
   stimmen und Namen könnten wieder (unsichtbar) über den Rand hinausragen.
   Ein zu langer Name wird stattdessen mit „…" abgeschnitten – klar lesbar
   schlechter als der volle Name, aber niemals unsichtbar. */
.anzeige-col li {
  padding: 5px 0;
  font-size: 1.1rem;
  border-bottom: 1px solid var(--border);
  break-inside: avoid;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.anzeige-col li:last-child { border-bottom: none; }
.anzeige-col ul.list-compact li { padding: 3px 0; font-size: 0.88rem; }
.anzeige-col ul.list-tiny li { padding: 1px 0; font-size: 0.74rem; }
/* Kursziel erreicht / schwimmt – reine Hintergrundfarbe, verändert
   bewusst keine Höhe/Breite/Schrift, damit die JS-Einpassung oben
   (einheitliche Zeilenhöhe pro Liste) davon unberührt bleibt. */
.anzeige-col li.ziel-erreicht { background: var(--ziel-erreicht-bg); }
.anzeige-col li.schwimmt-status { background: var(--schwimmt-bg); }
.anzeige-empty { color: var(--muted); font-size: 1rem; }
.anzeige-clock { font-size: 1rem; opacity: 0.85; }

/* Trennlinie innerhalb der "Anwesend"-Spalte, darunter die entschuldigten/
   Urlaub-Kinder – zusammengelegt statt eigener Spalte. */
.anzeige-subdivider {
  border-top: 2px dashed var(--border);
  margin: 12px 0 8px;
  padding-top: 8px;
}
.anzeige-subdivider h4 { margin: 0; font-size: 0.95rem; color: var(--status-entschuldigt); display: flex; justify-content: space-between; }

/* Scan-Popup auf der Live-Anzeige: großer Hinweis bei jedem Scan */
#scan-popup {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 40px;
  font-size: 6rem;
  font-weight: 800;
  color: white;
  z-index: 1000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}
#scan-popup.show { opacity: 1; }
#scan-popup.ok { background: var(--green); }
#scan-popup.error { background: var(--red); font-size: 3.2rem; }
/* Seit v161 (Torstens Wunsch: "eine der Pflicht Sachen fehlt ... rot
   leuchten und neben dem Namen anzeigen welche Sachen fehlen") – zwei
   Zeilen statt des sonst einzelnen Textknotens, daher #scan-popup oben auf
   flex-direction:column umgestellt (ändert am einzeiligen Fall nichts). */
#scan-popup .scan-popup-missing {
  font-size: 0.4em;
  font-weight: 600;
  margin-top: 16px;
}

@media (max-width: 900px) {
  #scan-popup.ok { font-size: 3.5rem; }
  #scan-popup.error { font-size: 2rem; }
}

/* Scan-Leiste unten fest auf der Kiosk-Seite (/kiosk) – schlanker Streifen
   mit nur dem Scan-Feld, damit der Bildschirm überwiegend die große
   Live-Übersicht zeigt statt der vollen Trainer-Bedienoberfläche. */
#kiosk-scan-bar {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  align-items: center;
  gap: 16px;
  background: var(--card);
  border-top: 3px solid var(--brand);
  padding: 14px 24px;
  box-shadow: 0 -4px 14px rgba(0, 0, 0, 0.12);
  z-index: 400;
}
#kiosk-scan-bar label { width: auto; flex-shrink: 0; color: var(--muted); font-size: 0.9rem; }
#kiosk-scan-bar input {
  flex: 1;
  font-size: 1.3rem;
  padding: 12px 16px;
}
#kiosk-scan-bar span {
  flex-shrink: 0;
  font-size: 1.05rem;
  min-width: 220px;
  text-align: right;
}

@media (max-width: 700px) {
  #kiosk-scan-bar { flex-wrap: wrap; }
  #kiosk-scan-bar label { display: none; }
  #kiosk-scan-bar span { width: 100%; text-align: left; min-width: 0; }
}

/* Seit v201 (Torstens Wunsch: "das ganze sollte zweiseitig aufgebaut
   werden ... zumindest auf dem Smartphone auf dem Desktop / Laptop passt
   es auch so wie bisher") – "💬 Nachrichten" (#nachrichten-layout in
   mcc.html) zeigt auf schmalen Bildschirmen immer nur EINE der beiden
   Spalten: die Chat-Liste (#nachrichten-spalte-liste) ODER den Inhalt
   (#nachrichten-spalte-inhalt, Thread/Neue-Nachricht/Mitteilung), je
   nachdem ob #nachrichten-layout die Klasse "nachrichten-zeigt-inhalt"
   trägt (siehe nachrichtenMobilZeigeInhalt()/nachrichtenMobilZeigeListe()
   in mcc.js). Der "← Zurück"-Button (#nachrichten-zurueck-btn) existiert
   nur für diese Mobil-Ansicht und bleibt auf breiteren Bildschirmen
   unsichtbar. Über 700px (Desktop/Laptop) bleibt alles exakt wie seit
   v193: beide Spalten permanent nebeneinander sichtbar. */
#nachrichten-zurueck-btn { display: none; }
@media (max-width: 700px) {
  #nachrichten-layout { flex-direction: column; }
  #nachrichten-spalte-liste { max-width: none; border-right: none; border-bottom: 1px solid var(--border); }
  /* !important nötig, weil beide Spalten ihr display:flex per Inline-
     style (mcc.html) mitbringen, das sonst jede externe Regel schlägt. */
  #nachrichten-layout:not(.nachrichten-zeigt-inhalt) #nachrichten-spalte-inhalt { display: none !important; }
  #nachrichten-layout.nachrichten-zeigt-inhalt #nachrichten-spalte-liste { display: none !important; }
  #nachrichten-zurueck-btn { display: inline-block; }
}

/* ===========================================================================
   Zeitslot-Kacheln auf der öffentlichen Anmeldeseite (seit 27.08.2026, auf
   Torstens Wunsch "so soll es aussehen") – ein Kärtchen-Raster je Stufe mit
   farbiger Belegungs-Ampel. Ein Klick auf ein freies Kärtchen öffnet (seit
   v88, Torstens Wunsch "Popup nach wählen eines Zeitslots zur Dateneingabe
   bzw. scan") direkt das Anmelde-Popup statt (wie zuvor) eine Radio-Auswahl
   zu markieren – die Kärtchen sind daher jetzt normale <button>-Elemente,
   kein <label>/<input type="radio"> mehr. Siehe renderStufen() und
   oeffneAnmeldeModal() in event.js.
   ========================================================================= */
.stufe-gruppe { margin-bottom: 14px; border-bottom: 1px solid var(--border); padding-bottom: 14px; }
.stufe-gruppe:last-child { border-bottom: none; }
/* Seit v88 (Torstens Wunsch "die einzelnen Kurse (Stufen) auf und
   zuklappbar machen"): die Stufenüberschrift ist jetzt ein <button>, der
   den darunterliegenden Termine-Bereich (.stufe-termine) auf-/zuklappt –
   siehe toggleStufe() in event.js. Name + Beschreibung bleiben immer
   sichtbar, nur die Zeitslot-Auswahl steckt hinter dem Klapp-Pfeil. */
.stufe-kopf {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 4px 0;
  margin: 0;
  font: inherit;
  font-weight: 700;
  font-size: 1.15rem;
  color: inherit;
  cursor: pointer;
}
.stufe-kopf:hover .stufe-name { color: var(--brand); }
.stufe-kopf .stufe-name { flex: 1; }
.stufe-kopf .hint { font-weight: 500; font-size: 0.85rem; }
.stufe-chevron {
  display: inline-block;
  flex-shrink: 0;
  font-size: 0.85rem;
  color: var(--muted);
  transition: transform 0.15s ease;
}
.stufe-kopf.aufgeklappt .stufe-chevron,
.ta-collapse-kopf.aufgeklappt .stufe-chevron { transform: rotate(90deg); }
.stufe-beschreibung-text { margin: 2px 0 0; }
.stufe-termine { margin-top: 10px; }
/* Klapp-Köpfe in der Trainer-Ansicht (Event-Einstellungen + je Stufe,
   seit v88, Torstens Wunsch "auf der Events Seite … das auf und zuklappen
   der Stufen … einbauen" und "auch das Event Feld selber zum auf und zu
   klappen") – gleiche Mechanik/Chevron wie .stufe-kopf auf der
   öffentlichen Seite, nur dezenter (kein Fett/große Schrift), da hier
   Kontext (Formularfelder) statt einer reinen Übersicht gezeigt wird.
   Siehe toggleAdminCollapse() in mcc.js. */
.ta-collapse-kopf {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 2px 0;
  margin: 0;
  font: inherit;
  font-weight: 600;
  font-size: 1rem;
  color: inherit;
  cursor: pointer;
}
.ta-collapse-kopf:hover .stufe-name { color: var(--brand); }
.ta-collapse-kopf .stufe-name { flex: 1; }
.slot-raster {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 8px;
  margin-top: 8px;
}
.slot-kachel {
  position: relative;
  display: block;
  width: 100%;
  text-align: left;
  border: 1.5px solid var(--border);
  border-radius: 10px;
  padding: 8px 10px;
  background: var(--card);
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.slot-kachel:hover { border-color: var(--brand); }
.slot-kachel.ausgebucht { opacity: 0.55; cursor: not-allowed; }
.slot-zeit { display: block; font-weight: 600; font-size: 0.9rem; }
.slot-belegung {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: 999px;
  margin-top: 4px;
}
.slot-belegung.frei-viel { background: var(--ziel-erreicht-bg); color: var(--green); }
.slot-belegung.frei-wenig { background: #fdf0d9; color: var(--amber); }
.slot-belegung.voll { background: #f5dcd7; color: var(--red); }

/* ===========================================================================
   Anmelde-Popup (seit v88) – öffnet sich nach Klick auf einen Zeitslot
   (bzw. direkt bei einer Stufe mit nur einem, unsichtbaren Slot) und
   enthält QR-Scan-Button sowie die Namens-/Kontaktfelder, die vorher
   statisch unter allen Stufen standen. Modell: renderModal() im alten
   Node-Anmeldetool (siehe INTEGRATIONSPLAN-Vereinstools.md), an dessen
   Optik sich Torsten wiederholt orientiert hat. Siehe event.html/event.js.
   ========================================================================= */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 26, 31, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  z-index: 1000;
}
.modal-box {
  position: relative;
  background: var(--card);
  border-radius: 12px;
  padding: 22px;
  max-width: 480px;
  width: 100%;
  max-height: 92vh;
  overflow-y: auto;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.28);
}
/* Seit v281 (Torstens Rückmeldung zur Trainer-Übersicht: "sollte sich aber
   noch als Popup öffnen lassen um die ganze Bildschirmgröße zu nutzen") –
   Zusatzklasse für Popups mit breitem Inhalt (z.B. die Zeit×Stufe-Matrix
   in #matrix-modal-overlay), die mit der schmalen 480px-Standardbox oben
   nicht auskommen. Ersetzt max-width, nicht width, damit die Box auf sehr
   breiten Bildschirmen nicht unnötig über den eigentlichen Tabelleninhalt
   hinausragt (die Tabelle selbst bleibt über ihren eigenen .matrix-wrap
   horizontal scrollbar, falls sie trotzdem breiter als 96vw wird). */
.modal-box.modal-box-gross {
  max-width: 96vw;
  max-height: 94vh;
}

.modal-close {
  position: absolute;
  top: 8px;
  right: 10px;
  background: none;
  border: none;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  color: var(--muted);
  padding: 6px;
}
.modal-close:hover { color: var(--text); }
.modal-eyebrow {
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--brand);
  margin: 0 26px 2px 0;
}
.modal-titel {
  margin: 0 26px 16px 0;
  font-size: 1.25rem;
}
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 16px;
}

/* Seit v167 (Torstens Wunsch bei allgemeinen Events wie Hundeschwimmen:
   "ich benötige wie auf der alten Hundeschwimmen Anmeldung ein Fenster wo
   gleich die Badeordnung steht und das Bestätigen dieser dann zu dem
   Anmeldeformular führt") – verwandelt den sonst schwebenden
   Anmelde-Dialog (#anmelde-modal-overlay) in eine normale Karte im
   Seitenfluss, ohne die Formularfelder zu duplizieren (siehe
   initEinfacheAnmeldung() in event.js: setzt diese Klasse zusätzlich zu
   .modal-overlay). Auf einer durchgehenden Seite gibt es nichts zum
   "Schließen", darum werden Schließen-Kreuz und "Abbrechen"-Knöpfe hier
   ausgeblendet. */
.modal-overlay.modal-inline {
  position: static;
  background: none;
  padding: 0;
  z-index: auto;
}
.modal-overlay.modal-inline .modal-box {
  max-width: none;
  box-shadow: none;
  border: 1px solid var(--border);
  border-radius: 8px;
}
.modal-overlay.modal-inline .modal-close,
.modal-overlay.modal-inline .modal-abbrechen-btn {
  display: none;
}

/* ===========================================================================
   Trainer-Zuordnung als Karten-Raster in der Trainer-Ansicht (seit
   27.08.2026, auf Torstens Wunsch "so in der Art soll es aussehen") – ein
   Kärtchen pro Zeitslot, direkt darin die Kinder dieses Slots samt
   Trainer-Feld. Siehe slotCardHtml() in mcc.js.
   ========================================================================= */
.ta-slot-raster {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 10px;
}
.ta-slot-karte {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px;
  background: var(--card);
}
.ta-slot-kopf { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 6px; }
.ta-slot-zeit { font-weight: 600; font-size: 0.9rem; }
.ta-pille {
  display: inline-block;
  white-space: nowrap;
  font-size: 0.7rem;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: 999px;
}
.ta-pille.frei-viel { background: var(--ziel-erreicht-bg); color: var(--green); }
.ta-pille.frei-wenig { background: #fdf0d9; color: var(--amber); }
.ta-pille.voll { background: #f5dcd7; color: var(--red); }
.ta-slot-kapazitaet { width: 100%; box-sizing: border-box; margin-bottom: 6px; font-size: 0.8rem; padding: 4px 6px; }
.ta-kind-zeile { padding: 6px 0; border-top: 1px dashed var(--border); }
.ta-kind-zeile:first-child { border-top: none; padding-top: 0; }
.ta-kind-kopf { display: flex; align-items: center; justify-content: space-between; gap: 6px; flex-wrap: wrap; }
.ta-kind-name { font-weight: 600; font-size: 0.85rem; }
.ta-mail { color: var(--brand); cursor: help; font-weight: 400; margin-left: 2px; }
.ta-kind-aktionen { display: flex; align-items: center; gap: 4px; margin-top: 4px; flex-wrap: wrap; }
.ta-kind-aktionen select { flex: 1 1 100px; font-size: 0.75rem; padding: 3px 4px; }
.ta-icon-btn {
  background: transparent;
  border: 1.5px solid var(--border);
  color: var(--red);
  border-radius: 6px;
  width: 24px;
  height: 24px;
  line-height: 1;
  font-size: 12px;
  flex-shrink: 0;
  padding: 0;
}
.ta-icon-btn:hover { border-color: var(--red); background: #f5dcd7; }
.ta-kind-neu {
  display: flex;
  gap: 4px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
}
.ta-kind-neu input { flex: 1 1 60px; min-width: 0; font-size: 0.75rem; padding: 4px 6px; }
.ta-kind-neu button { flex-shrink: 0; }

/* Hub-Kacheln (Startseite: Verzeichnis der Bereiche + offene Events; seit
   v98 auch der Offene-Events-Hinweis in eltern.html) – ursprünglich nur
   inline in start.html, seit v98 hierher verschoben, damit auch andere
   Seiten dieselbe Optik nutzen können (Torstens Wunsch: "in der Eltern
   Ansicht sollten wir die Event Anmeldung noch integrieren"). */
.hub-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 16px;
}
.hub-tile {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 18px;
  text-decoration: none;
  color: var(--text);
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}
.hub-tile.active:hover {
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.1);
  transform: translateY(-1px);
}
.hub-tile .icon { font-size: 1.8rem; }
.hub-tile h3 { margin: 0; font-size: 1.05rem; }
.hub-tile p { margin: 0; font-size: 0.85rem; color: var(--muted); }
.hub-tile.disabled {
  opacity: 0.6;
  cursor: default;
}
.hub-tile .status {
  margin-top: auto;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.hub-tile.active .status { color: var(--green); }
.hub-tile.disabled .status { color: var(--muted); }
.hub-tile.extern .status { color: var(--amber); }

/* Seit v238 (Torstens Wunsch: "die klicken an wir zahlen 15€ bar dann
   blinkt die Anzeige so lange rot bis wir auf bezahlt klicken") – für eine
   von den Eltern gemeldete, aber noch nicht von einem Trainer/Admin
   bestätigte Eintrittsnachweis-Meldung (siehe renderEintrittsnachweisBlock()
   in mcc.js/personen-profil.js). Bewusst eine dezente Opacity-Animation
   (kein hartes An/Aus) statt einer aggressiven Farb-Invertierung, damit der
   Text dabei lesbar bleibt. */
@keyframes eintrittsnachweis-blinken {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}
.chip-blinkt-rot {
  background: var(--red);
  color: #fff;
  animation: eintrittsnachweis-blinken 1.1s ease-in-out infinite;
}

/* Seit v253 (Torstens Wunsch: "Die Teilnehmerlisten brauchen einen 'nach
   oben' Button sonst scrollt man sich wund") – global schwebender Knopf,
   siehe #nach-oben-btn in mcc.html sowie setupNachObenButton()/
   scrollNachOben() in mcc.js. z-index bewusst deutlich unter den
   Modal-Overlays (1000, siehe .modal-overlay oben), damit ein offenes
   Modal ihn zuverlässig verdeckt statt darüber zu schweben. */
#nach-oben-btn {
  position: fixed;
  right: 20px;
  bottom: 20px;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: var(--brand);
  color: #fff;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
  z-index: 500;
}
#nach-oben-btn:hover { opacity: 0.9; }
@media (max-width: 600px) {
  /* Auf dem Handy weiter oben, damit er nicht mit der PWA-Installations-
     Leiste/dem Scanner-Feld o.ä. am unteren Rand kollidiert. */
  #nach-oben-btn { bottom: 76px; right: 14px; }
}

/* Seit v264 (Torstens Rückmeldung: "der rote 'To Top' Pfeil soll direkt
   neben der A,B,C... Sprungleiste stehen") – kleine, inline sitzende
   Variante desselben roten Knopfs, direkt neben #teilnehmer-az statt frei
   schwebend (siehe #teilnehmer-sticky-leiste in mcc.html). Bewusst
   dieselbe Farbe/Form wie #nach-oben-btn, nur kleiner, damit sie neben den
   A-Z-Buchstaben nicht dominiert. */
.nach-oben-inline {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: none;
  background: var(--brand);
  color: #fff;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
}
.nach-oben-inline:hover { opacity: 0.9; }

/* ===========================================================================
   Chat-Design (seit v336, Torstens Wunsch: "der Nachrichten Block soll
   deutlich schöner werden, angelehnt an das Design und Handling vom
   WhatsApp ... wirklich deutlich schöner, sehr modern und sehr intuitiv")
   – gemeinsames Designsystem für den Tab "💬 Nachrichten" in MCC
   (ladeNachrichtenThreadInhalt()/renderNachrichtenChatsListe() in mcc.js)
   UND den Eltern-Chat (renderElternChat() in eltern.js). Bewusst als
   Klassen hier zentral statt der bisherigen Inline-Styles, damit beide
   Seiten garantiert identisch aussehen. Messenger-Konventionen (eigene
   Nachrichten rechts in zartem Grün, fremde links in Weiß, Schwänzchen an
   der ersten Blase einer Gruppe, Datums-Chips, Zeit klein unten rechts in
   der Blase, runder Senden-Knopf) – ohne fremdes Branding, Farben aus
   einer eigenen Palette. */
:root {
  --chat-bg: #e8e2da;            /* Verlaufs-Hintergrund (warmes Beige) */
  --chat-bg-muster: rgba(0, 0, 0, 0.025);
  --chat-blase-eigen: #d9f4cf;   /* eigene Nachrichten: zartes Grün */
  --chat-blase-fremd: #ffffff;   /* fremde Nachrichten: Weiß */
  --chat-gruen: #1f9d55;         /* Senden-Knopf + Ungelesen-Badge */
  --chat-gruen-dunkel: #177a42;
}

/* --- Verlauf ------------------------------------------------------------ */
.chat-verlauf {
  background:
    radial-gradient(var(--chat-bg-muster) 1px, transparent 1.5px),
    var(--chat-bg);
  background-size: 22px 22px, auto;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  scroll-behavior: smooth;
}
.chat-datum-chip {
  align-self: center;
  background: rgba(255, 255, 255, 0.92);
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: 12px;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.08);
  margin: 10px 0 4px;
}
.chat-nachricht {
  display: flex;
  align-items: flex-end;
  gap: 4px;
  margin: 1px 0;
  /* Bezugsrahmen für .chat-reaktion-picker (siehe unten) – die Zeile ist
     immer so breit wie der ganze Verlauf, der Picker kann daher nie aus
     dem scrollenden Verlauf herausragen und abgeschnitten werden (genau
     das passierte, als er noch am kleinen 🙂-Knopf verankert war). */
  position: relative;
}
.chat-nachricht.eigen { justify-content: flex-end; }
.chat-nachricht.gruppe-start { margin-top: 8px; }

/* --- Sprechblase --------------------------------------------------------- */
.chat-blase {
  position: relative;
  max-width: 78%;
  padding: 6px 9px 5px;
  border-radius: 10px;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.10);
  font-size: 0.95rem;
  line-height: 1.35;
}
.chat-blase.eigen { background: var(--chat-blase-eigen); }
.chat-blase.fremd { background: var(--chat-blase-fremd); }
/* Schwänzchen nur an der ERSTEN Blase einer zusammenhängenden Gruppe
   (Klasse "schwanz", vergeben in ladeNachrichtenThreadInhalt()/
   renderElternChat()) – kleines Dreieck oben außen, in Blasenfarbe. */
.chat-blase.schwanz.eigen { border-top-right-radius: 2px; }
.chat-blase.schwanz.fremd { border-top-left-radius: 2px; }
.chat-blase.schwanz::before {
  content: "";
  position: absolute;
  top: 0;
  width: 0;
  height: 0;
  border-top: 9px solid;
  border-top-color: inherit;
}
.chat-blase.schwanz.eigen::before {
  right: -7px;
  color: var(--chat-blase-eigen);
  border-top-color: var(--chat-blase-eigen);
  border-left: 8px solid transparent;
}
.chat-blase.schwanz.fremd::before {
  left: -7px;
  color: var(--chat-blase-fremd);
  border-top-color: var(--chat-blase-fremd);
  border-right: 8px solid transparent;
}
.chat-absender {
  font-weight: 600;
  font-size: 0.8rem;
  margin-bottom: 1px;
}
.chat-text { white-space: pre-wrap; overflow-wrap: anywhere; }
.chat-meta {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 4px;
  font-size: 0.68rem;
  color: var(--muted);
  margin-top: 1px;
  user-select: none;
}
.chat-blase img { max-width: min(240px, 100%); border-radius: 8px; display: block; }
.chat-anhang { margin-top: 4px; }

/* --- Reaktionen (vergebene Chips immer sichtbar, klein unter der Blase) -- */
.chat-reaktionen {
  display: flex;
  gap: 3px;
  flex-wrap: wrap;
  margin-top: 3px;
}
.chat-reaktion-chip {
  border: 1px solid var(--border);
  background: var(--card);
  border-radius: 11px;
  padding: 0 7px;
  font-size: 0.8rem;
  line-height: 20px;
  cursor: pointer;
}
.chat-reaktion-chip.eigene { background: #e3f0fb; border-color: #9cc3e5; }

/* --- Aktionen (🙂-Picker + 🗑️): erst bei Hover/Antippen sichtbar --------- */
.chat-aktionen {
  display: flex;
  gap: 2px;
  align-items: center;
  opacity: 0;
  transition: opacity 0.15s;
  flex: 0 0 auto;
  padding-bottom: 2px;
}
.chat-nachricht:hover .chat-aktionen,
.chat-nachricht.aktionen-offen .chat-aktionen { opacity: 1; }
.chat-aktion-btn {
  border: none;
  background: rgba(255, 255, 255, 0.85);
  border-radius: 50%;
  width: 26px;
  height: 26px;
  line-height: 1;
  font-size: 0.85rem;
  cursor: pointer;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.12);
  padding: 0;
}
.chat-aktion-btn:hover { background: #fff; }
.chat-reaktion-picker {
  display: none;
  position: absolute;
  /* Verankert an der ganzen .chat-nachricht-Zeile (nicht am 🙂-Knopf) –
     knapp über der Zeile, seitlich per Inline-Style (right/left je nach
     Blasen-Seite) immer nach INNEN wachsend. Die Datums-/Gruppen-Chips
     bzw. die Vorgänger-Nachricht darüber bieten dafür stets Platz. */
  bottom: calc(100% - 4px);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 4px 6px;
  white-space: nowrap;
  z-index: 10;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  font-size: 1.05rem;
}
.chat-reaktion-picker span { cursor: pointer; padding: 2px 4px; }

/* --- Eingabezeile (Composer) --------------------------------------------- */
.chat-eingabe {
  display: flex;
  gap: 6px;
  align-items: flex-end;
  padding: 8px 10px;
  background: var(--card);
  border-top: 1px solid var(--border);
}
.chat-eingabe textarea {
  flex: 1 1 auto;
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 9px 14px;
  font: inherit;
  resize: none;
  max-height: 132px;
  overflow-y: auto;
  min-width: 0;
  background: var(--bg);
}
.chat-eingabe textarea:focus { outline: none; border-color: var(--chat-gruen); background: var(--card); }
.chat-senden-btn {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: none;
  background: var(--chat-gruen);
  color: #fff;
  font-size: 1.15rem;
  line-height: 1;
  cursor: pointer;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 0 0 2px;
}
.chat-senden-btn:hover { background: var(--chat-gruen-dunkel); }
.chat-icon-btn {
  border: none;
  background: none;
  font-size: 1.25rem;
  cursor: pointer;
  padding: 6px 4px;
  line-height: 1;
  flex: 0 0 auto;
  margin: 0;
}
.chat-icon-btn:hover { opacity: 0.7; }

/* --- Chat-Liste (linke Spalte) ------------------------------------------- */
.chat-listen-eintrag {
  display: flex;
  gap: 10px;
  padding: 9px 10px;
  cursor: pointer;
  align-items: center;
  border-bottom: 1px solid var(--border);
  transition: background 0.1s;
}
.chat-listen-eintrag:hover { background: #f2f4f6; }
.chat-listen-eintrag.aktiv { background: #e9eef3; }
.chat-avatar {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 auto;
  border: 1px solid var(--border);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--border);
  font-size: 1.1rem;
  overflow: hidden;
}
.chat-avatar.klein { width: 32px; height: 32px; font-size: 0.95rem; }
.chat-listen-mitte { flex: 1 1 auto; min-width: 0; }
.chat-listen-zeile1 { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.chat-listen-name {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.chat-listen-zeit { font-size: 0.72rem; color: var(--muted); flex: 0 0 auto; }
.chat-listen-zeit.ungelesen { color: var(--chat-gruen); font-weight: 700; }
.chat-listen-zeile2 { display: flex; justify-content: space-between; gap: 8px; align-items: center; margin-top: 1px; }
.chat-listen-vorschau {
  color: var(--muted);
  font-size: 0.85rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.chat-unread-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 19px;
  height: 19px;
  padding: 0 5px;
  border-radius: 10px;
  background: var(--chat-gruen);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  flex: 0 0 auto;
}

/* --- Thread-Kopfzeile ----------------------------------------------------- */
.chat-thread-kopf {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: var(--card);
  border-bottom: 1px solid var(--border);
  min-width: 0;
}
.chat-thread-kopf h3 {
  margin: 0;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 1.02rem;
}

/* ===========================================================================
   Listen & Schalter (seit v337, Torstens Wunsch: "kannst du die Mitglieder
   Verwaltung ähnlich schön gestalten wie die Nachrichten Sektion? ... das
   Handling der Berechtigungsverwaltung ist immer noch etwas altbacken") –
   gemeinsame Bausteine für die modernisierten Personen-Listen (Avatare wie
   in der Chat-Liste, siehe personZeileHtml() in mcc.js) und die neue
   Stufen-Schalter-Bedienung der Berechtigungen (ersetzt die vierstufigen
   <select>-Dropdowns in Rechte-Übersicht, "🔐 Wer ist berechtigt?",
   Login-Berechtigungen und Personen-Profil – siehe stufenSchalterHtml()
   in mcc.js bzw. personen-profil.js). */

/* --- Personen-/Eintrags-Listen ------------------------------------------- */
.listen-eintrag {
  display: flex;
  gap: 10px;
  padding: 8px 6px;
  align-items: center;
  border-bottom: 1px solid var(--border);
  transition: background 0.1s;
}
.listen-eintrag:hover { background: #f2f4f6; }
.listen-eintrag .listen-mitte { flex: 1 1 auto; min-width: 0; }
.listen-eintrag .listen-name { font-weight: 600; }
.listen-eintrag .listen-name a { color: inherit; text-decoration: none; }
.listen-eintrag .listen-name a:hover { text-decoration: underline; }
.listen-eintrag .listen-sub {
  color: var(--muted);
  font-size: 0.82rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.listen-eintrag .listen-aktionen {
  display: flex;
  gap: 6px;
  align-items: center;
  flex: 0 0 auto;
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* --- Stufen-Schalter (Standard / ✅ voll / 👁️ lesen / 🚫 gesperrt) -------- */
.stufen-schalter {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 9px;
  overflow: hidden;
  background: var(--card);
  flex: 0 0 auto;
}
.stufen-schalter button {
  border: none;
  background: none;
  padding: 4px 9px;
  cursor: pointer;
  font-size: 0.82rem;
  line-height: 1.3;
  border-right: 1px solid var(--border);
  color: var(--muted);
  white-space: nowrap;
}
.stufen-schalter button:last-child { border-right: none; }
.stufen-schalter button:hover { background: #f2f4f6; }
.stufen-schalter button.aktiv { color: #fff; font-weight: 600; }
.stufen-schalter button.aktiv.standard { background: var(--muted); }
.stufen-schalter button.aktiv.voll { background: var(--chat-gruen, #1f9d55); }
.stufen-schalter button.aktiv.lesend { background: #0277bd; }
.stufen-schalter button.aktiv.gesperrt { background: var(--red); }

/* --- Moderner Ein/Aus-Schalter (ersetzt Checkboxen, z.B. Rollen-Sicht) --- */
.ui-switch {
  position: relative;
  display: inline-block;
  width: 38px;
  height: 22px;
  flex: 0 0 auto;
  vertical-align: middle;
}
.ui-switch input { opacity: 0; width: 0; height: 0; }
.ui-switch .schieber {
  position: absolute;
  inset: 0;
  background: #c3ccd3;
  border-radius: 11px;
  cursor: pointer;
  transition: background 0.15s;
}
.ui-switch .schieber::before {
  content: "";
  position: absolute;
  width: 18px;
  height: 18px;
  left: 2px;
  top: 2px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition: transform 0.15s;
}
.ui-switch input:checked + .schieber { background: var(--chat-gruen, #1f9d55); }
.ui-switch input:checked + .schieber::before { transform: translateX(16px); }
.ui-switch input:disabled + .schieber { opacity: 0.45; cursor: default; }
