/* SAX-MAX Dresden – Basis-Stylesheet
   Systemschriften statt Webfonts: keine externen Requests, kein Datenschutzthema. */

/* Grün bleibt der Grundton – es ist das Blaugrün des Snookertuchs (Farbton ~152°) –,
   aber es ist nicht mehr zugleich die Akzentfarbe. Solange beides grün war, hob sich
   nichts mehr ab: ein grüner Link auf grünlichem Grund neben einer grünen Markierung.
   Der Akzent ist jetzt Kreideblau (~203°), die Farbe der Queue-Kreide – der einzige
   Fremdton am Tisch und damit der einzige, der auf Tuchgrün wirklich auffällt.

   Vier Rollen, vier Mittel, und jedes Mittel nur für seine Rolle:
     ① Handlung       → Blau (--accent). Alles, was anklickbar ist.
     ② „Hier bist du“ → --mark. Fast Schwarz, keine Farbe; Ort ist keine Wertung.
     ③ „Das sind wir“ → Grün (--green, --event-home). Verein, Heimspiel, eigene Zeile.
     ④ Rückmeldung    → Grün/Rot. Gelungen oder misslungen, sonst nichts.
   Wer eine fünfte Bedeutung braucht, braucht kein fünftes Farbmittel, sondern Worte. */
:root {
  --green: #0b6b3f;
  /* Kreideblau. Der dunkle Ton trägt Text (7.3:1 auf Weiß), der helle nur Flächen. */
  --blue: #155b86;
  --blue-light: #1a6f9f;

  --bg: #ffffff;
  /* Die Grautöne haben nur noch einen Hauch Grün (statt deutlich sichtbarem):
     genug, damit sie zum Grundton gehören, zu wenig, um selbst als Farbe zu gelten. */
  --bg-soft: #f2f6f3;
  --bg-header: #ffffff;
  --text: #101820;
  --text-muted: #5c6a64;
  --border: #dce5e0;
  --accent: var(--blue);
  --accent-hover: var(--blue-light);
  --accent-text: #ffffff;
  /* Markierung „hier bist du“. Ein Ort ist weder gut noch schlecht noch anklickbar –
     er verdient Gewicht, aber keine Farbe. Kein reines Schwarz: der Textton selbst war
     als Fläche zu hart. Dies ist ein Grau mit 16 % Sättigung im Farbton des Grundtons
     (155°) – es gehört sichtbar zur Seite, ohne selbst als Farbe zu zählen. */
  --mark: #3d4944;
  --mark-text: #ffffff;
  /* Der Schatten ist grün getönt statt neutralschwarz – sonst wirkt er wie Schmutz
     auf den grünstichigen Flächen. */
  --shadow: 0 1px 2px rgba(11, 107, 63, .10), 0 10px 28px rgba(11, 107, 63, .10);

  /* Termine im Vereinsheim. Alles andere bleibt ungefärbt – zwei Farben bräuchten
     eine Erklärung, eine hervorgehobene und ein Normalfall nicht. Dunkler als das
     frühere #0e8a51, das auf Weiß nur 4.4:1 erreichte und damit zu blass war. */
  --event-home: var(--green);
  /* Das einzige Rot der Seite. Es sagt immer dasselbe – misslungen –, ob die Partie
     verloren ging, das Formular hakt oder ein Import fehlschlug. Gedeckt genug, um
     neben dem Grün nicht nach Alarm auszusehen. */
  --red: #a8322a;

  /* Eine Raumskala, seit 2026-09-19. Vorher standen rund 25 verschiedene rem-Werte in der
     Datei, jeder einzeln gewählt – und Regeln, die dasselbe meinten, sagten Verschiedenes:
     .card stand 1.8 vom Nachbarn, .cols 1.5, .prices wieder 1.8. Das sieht niemand als
     System, aber jeder als Unruhe.

     Sechs Stufen im Verhältnis von etwa 1,5, und jede trifft bei 16 Punkt Grundschrift
     einen ganzen Pixel: 8, 12, 20, 32, 48, 72. Der Faktor ist groß genug, dass zwei
     benachbarte Stufen unterscheidbar sind – zwischen 1.4 und 1.5rem kann niemand eine
     Absicht erkennen, zwischen 20 und 32 Punkt schon.

     Sie gilt für den **Rhythmus** der Seite: Seitenanfang, Überschriften, Absätze,
     Blockabstände, Fuß. Die Feinwerte innerhalb von Bauteilen (Saisonband, Filterleiste,
     Tabellenzellen) bleiben, wo sie sind; dort geht es um das Ausrichten von Kanten und
     nicht um Rhythmus, und eine Skala hätte ihnen nichts zu sagen. */
  --raum-xs: .5rem;
  --raum-s: .75rem;
  --raum-m: 1.25rem;
  --raum-l: 2rem;
  --raum-xl: 3rem;
  --raum-xxl: 4.5rem;

  --wrap: 62rem;
  /* --brand-w ist am 2026-09-14 weggefallen. Die Variable gab es, weil die Logobreite an
     zwei Stellen gebraucht wurde: vom Bild und von der weißen Trägerfläche, die im dunklen
     Design dahinterlag. Das Vektorlogo braucht keine Fläche mehr, also braucht die Breite
     auch niemand außer dem Logo selbst – siehe .logo-svg. */
  /* Eine Staffel statt eines Einheitsradius: kleine Bedienelemente knapp,
     Flächen runder, große Karten und Bilder am rundesten. */
  --radius-sm: 8px;
  --radius: 12px;
  --radius-lg: 18px;
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

[data-theme="dark"] {
  --green: #2fc07c;
  --blue: #5ab3dd;
  --blue-light: #7cc6e6;

  --bg: #0c1411;
  --bg-soft: #142019;
  --bg-header: #101a15;
  --text: #e6efe9;
  --text-muted: #93a79c;
  --border: #24342c;
  --accent: var(--blue);
  --accent-hover: var(--blue-light);
  --accent-text: #06121a;
  /* Dieselbe Zurücknahme wie im hellen Modus, nur andersherum: statt des vollen
     Textweiß eine gedämpfte Fläche, damit die Marke nicht aus der Seite heraussticht. */
  --mark: #b4c4bc;
  --mark-text: #0c1411;
  --event-home: var(--green);
  --red: #ef8d84;
  --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 10px 28px rgba(0, 0, 0, .45);
}

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--text);
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
}

/* Der Seitenrand links und rechts ist zweimal --raum-m und keine eigene Zahl: Er trennt den
   Text vom Fensterrand, und das ist dieselbe Art von Abstand wie zwischen zwei Absätzen. */
.wrap { width: min(100% - var(--raum-m) * 2, var(--wrap)); margin-inline: auto; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--accent-text);
  padding: .7rem 1.1rem; border-radius: 0 0 var(--radius) 0;
}
.skip:focus { left: 0; }

/* ---------- Kopfbereich ---------- */

/* Eine Zeile seit 2026-09-19 (Verein), und sie klebt.

   Vorgeschichte: Bis 2026-09-14 stand alles in einer Leiste, aber als Mischung aus Wörtern
   und Symbolen dreierlei Natur. Danach waren es zwei Zeilen – oben das Wappen allein und
   wegscrollend, darunter die klebende Navigation. Das trennte sauber, hatte aber auf breiten
   Schirmen einen Preis: ein 300 Punkt breites Logo in einem 992 Punkt breiten Streifen, und
   daneben nichts. Ruhig war das nicht, leer schon (Verein).

   Jetzt trägt eine Zeile alles, und leer ist sie nirgends: Schmal stehen drei Dinge darin
   (☰, Wappen, Konto), breit ebenfalls drei (Wappen, die sieben Seiten, Konto). Der klebende
   Teil ist mit 56 Punkt sogar kleiner als die beiden Zeilen zusammen (142) und nur zehn
   Punkt höher als die klebende Leiste von vorher (46).

   `fixed` und nicht `sticky`: Das Logo schrumpft beim Bildlauf (siehe --logo-w unten). Bei
   `sticky` liegt die Leiste im Fluss, und alles, was ihre Höhe ändert, verschiebt den Text
   darunter – man scrollt, und die Seite ruckt einem unter dem Finger weg. `fixed` nimmt sie
   aus dem Fluss; den Platz hält .kopf-platz frei, und der ist immer gleich hoch. */
:root { --kopf-h: 3.5rem; }
.site-kopf {
  position: fixed; top: 0; left: 0; right: 0; z-index: 40;
  background: var(--bg-header);
  border-bottom: 1px solid var(--border);
}
.kopf-platz { height: calc(var(--kopf-h) + 1px); }

/* Der Bearbeitungsmodus, sichtbar gemacht an der Unterkante der Zeile.
   Bis 2026-09-13 färbte er einen ganzen Streifen über dem Kopf ein. Der Streifen ist weg,
   das Signal muss bleiben: Ein Modus, den man übersieht, ist genau die Verwechslung, vor
   der blocks.php:7 warnt – wer nur nachsehen wollte, schreibt sonst aus Versehen.
   Deshalb keine leise Andeutung, sondern die kräftigste Linie der Seite, über die volle
   Breite und an der Kante, an der die Zeile ohnehin endet.

   Drei Punkt dick, aber nicht als dickerer Rand: Der Rand zählt zur Höhe, und die Höhe
   steht im Platzhalter fest. Zwei Punkt Schatten nach innen plus der eine Punkt Rand in
   derselben Farbe ergeben dieselbe Linie, ohne dass die Zeile wächst. */
.site-kopf.ist-bearbeitung {
  border-bottom-color: var(--accent);
  box-shadow: inset 0 -2px 0 var(--accent);
}

/* Das Raster statt `flex`, und zwar wegen der Mitte: Schmal soll das Wappen **mittig**
   stehen, und daneben stehen links 40 Punkt Hamburger und rechts 40 (abgemeldet) oder 49
   (angemeldet) Punkt Konto. Mit `margin: auto` läge es um die halbe Differenz daneben, und
   es wanderte beim Anmelden. Zwei gleich breite 1fr-Spalten außen halten die Mitte fest,
   egal was darin steht.

   Sichtbar sind immer genau drei Kinder – schmal ☰, Wappen, Konto; breit Wappen, Seiten,
   Konto (das ☰ ist dann weg, die Seitenliste vorher). Deshalb genügt die automatische
   Verteilung, und keine Spalte muss benannt werden.

   Breit dreht sich das Verhältnis um: Dort ist das Wappen nicht mehr die Mitte, sondern der
   Anfang, und dehnbar ist die Spalte mit den Seiten. Steht das nicht da, bekommt die
   Wappenspalte nur ihr Drittel der Restbreite – gemessen 101 statt 140 Punkt, denn
   `max-width: 100%` lässt das Wappen schrumpfen, statt die Spalte aufzuhalten. */
.kopf-inner {
  display: grid; grid-template-columns: 1fr auto 1fr;
  align-items: center; gap: var(--raum-s);
  min-height: var(--kopf-h);
}
/* Breit ist die Lücke keine Lücke mehr, sondern eine Grenze: Sie trennt das Wappen von der
   Seitenliste, also zwei Dinge verschiedener Art. Sie muss deshalb deutlich größer sein als
   der Abstand *innerhalb* der Liste, sonst liest sich das Wappen als erster Menüpunkt.
   Nachgemessen am 2026-09-19, vorher: Wort zu Wort 23 Punkt, Wappen zum ersten Wort 19 –
   die Grenze war enger als der Zusammenhalt. Jetzt ist sie mindestens 32 (--raum-l), und
   weil die Liste in ihrer Spalte mittig steht, kommt der freie Rest gleichmäßig dazu. */
@media (min-width: 64em) {
  .kopf-inner { grid-template-columns: auto 1fr auto; gap: var(--raum-l); }
}

/* Das Logo ist seit 2026-09-14 ein Vektor im Quelltext und nimmt die Textfarbe an; die
   weiße Trägerfläche im dunklen Design ist damit ersatzlos weg (siehe logo.php).

   Die Größe steht in der Breite, nicht in der Höhe: Nur so bleibt das Seitenverhältnis
   beim Schrumpfen erhalten. Das Verhältnis ist 4,64:1.

   Zwei Stufen, und beide deckelt dieselbe Grenze – die Zeile. 56 Punkt Höhe und beidseitig
   Luft lassen höchstens 44 Punkt Wappen zu, bei 4,64:1 also 204 Punkt Breite. Daher 190
   schmal (dort steht es zwischen zwei Knöpfen) und 200, sobald Platz daneben ist.

   Bis zum 2026-09-19 waren es vier Stufen, weil die Wortleiste schon bei 58em begann und das
   Wappen sich dort auf 180 ducken musste, um neben sechs Wörtern und dem Konto zu bleiben.
   Diesen Sonderfall gibt es nicht mehr: Die Leiste beginnt erst bei 64em, und ab dort ist
   Platz für beides in voller Größe. Eine Größe weniger zu erklären ist mehr wert als die
   zwanzig Punkt, die das Ducken einbrachte.

   Beim Bildlauf wird es kleiner: `--logo-w-klein`. Die Zeile wird davon nicht flacher –
   sie steht fest –, das Wappen tritt nur zurück, sobald man liest. Wer Bewegung abbestellt
   hat, bekommt den kleineren Stand ohne den Weg dorthin. */
.brand { display: inline-flex; min-width: 0; color: var(--text); }
/* Der Link ums Wappen bringt nichts mit, was man sehen soll: keine Farbe, keine
   Unterstreichung – das Wappen sieht aus wie das Wappen. Sichtbar wird er nur für die
   Tastatur, und der Ring hält Abstand, damit er nicht auf der Zeichnung liegt. */
.logo-link { display: block; color: inherit; text-decoration: none; }
.logo-link:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px;
  border-radius: var(--radius-sm);
}
.kopf-inner { --logo-w: 190px; --logo-w-klein: 150px; }
.logo-svg {
  display: block; width: var(--logo-w); max-width: 100%; height: auto;
  transition: width .2s ease;
}
.ist-gescrollt .logo-svg { width: var(--logo-w-klein); }
@media (prefers-reduced-motion: reduce) {
  .logo-svg { transition: none; }
}
@media (min-width: 40em) {
  .kopf-inner { --logo-w: 200px; --logo-w-klein: 170px; }
}

/* ---------- Die Zeile von innen ---------- */

/* ---------- Knöpfe in der Navigationsleiste ---------- */

/* „Menü“, „Anmelden“ und der eigene Name. Sie sehen aus wie die Links daneben und nicht wie
   Knöpfe: kein Rahmen, keine Fläche, dieselbe Schrift, dasselbe Polster. Das ist Absicht –
   in dieser Zeile ist alles gleichrangig, und ein umrandetes Element darin zöge Blick und
   Bedeutung auf sich, die ihm nicht zustehen. Unterschieden sind sie durch das Pfeilchen,
   und das unterscheidet die richtige Sache: nicht Link gegen Knopf, sondern „führt weg“
   gegen „klappt auf“. */
.nav-knopf {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .45rem .58rem;
  border: 0; background: none;
  color: var(--text-muted); text-decoration: none;
  border-radius: var(--radius-sm);
  font: inherit; font-weight: 600; font-size: .94rem;
  white-space: nowrap; cursor: pointer;
}
.nav-knopf:hover { color: var(--accent); background: var(--bg-soft); }
/* Die Stelle, an der man steht, ist keine Handlung – dieselbe neutrale Markierung wie in
   der Navigation daneben. Trifft „Anmelden“, während man auf /intern/ steht. */
.nav-knopf[aria-current] { color: var(--mark-text); background: var(--mark); }
.nav-knopf:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Der Knopf, auf dem statt eines Wortes ein Bild steht – das Konto. Seitlich weniger
   Polster, damit er quadratisch wirkt und nicht wie ein leeres Schild; nach oben und unten
   dasselbe Maß wie nebenan, sonst wäre er der einzige in der Zeile, der anders hoch ist.
   Nachgemessen: 40 Punkt breit, gegen 91 („Anmelden“) – 51 Punkt, die in der Leiste den
   Lücken zugutekommen statt einem Wort, das ein Bild auch sagt. */
.nav-knopf-symbol { padding: .45rem .5rem; min-width: 2.5rem; justify-content: center; }
/* Angemeldet ist das Symbol ausgefüllt. Der offene Bogen schließt sich dabei zur Sehne –
   aus den Umrissen wird eine Büste, dieselbe Figur, nur massiv. Der Unterschied überlebt
   Graustufen und das dunkle Design, eine Farbe täte das nicht. */
.nav-knopf-symbol .icon.ist-an { fill: currentColor; }

/* Ein gezeichnetes Dreieck und kein ▾ aus der Schrift: Das Zeichen gibt es nicht in jeder
   Schrift, und wo es fehlt, springt der Browser in eine Ersatzschrift und malt es in einer
   anderen Größe. Ein Rahmen ohne Inhalt, dessen obere Kante gefärbt ist, sieht überall
   gleich aus und nimmt über `currentColor` die Farbe des Knopfes an. */
.nav-knopf-pfeil {
  width: 0; height: 0; flex: none;
  border: .3rem solid transparent;
  border-top: .34rem solid currentColor;
  border-bottom: 0;
  transform: translateY(.1em);
}
.site-menu[open] > .nav-knopf { color: var(--accent); background: var(--bg-soft); }
.site-menu[open] > .nav-knopf .nav-knopf-pfeil { transform: translateY(.1em) rotate(180deg); }

/* Die drei Striche – und beim Öffnen das Kreuz daraus.

   Drei <span> und keine Ränder an einem Element: Ränder ließen sich nicht einzeln drehen,
   und genau das ist die Bewegung. Der mittlere Strich verschwindet nicht nach außen, er
   blendet ab – nach außen wäre er über die Kante des Knopfes gelaufen.

   Absolut gesetzt in einem 20-Punkt-Feld: 4, 9, 14 bei 2 Punkt Höhe, also Mitten bei 5, 10
   und 15. Beim Öffnen fahren die äußeren um dieselben 5 Punkt zur Mitte und legen sich
   quer; dass beide Wege gleich lang sind, ist der Grund für die symmetrischen Zahlen. */
.burger { position: relative; display: block; width: 1.25rem; height: 1.25rem; flex: none; }
.burger > span {
  position: absolute; left: 0; right: 0; height: 2px;
  background: currentColor; border-radius: 1px;
  transition: transform .18s ease, opacity .12s ease;
}
.burger > span:nth-child(1) { top: 4px; }
.burger > span:nth-child(2) { top: 9px; }
.burger > span:nth-child(3) { top: 14px; }
.site-menu[open] .burger > span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.site-menu[open] .burger > span:nth-child(2) { opacity: 0; }
.site-menu[open] .burger > span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }
/* Wer Bewegung abbestellt hat, bekommt trotzdem das Kreuz – nur ohne den Weg dorthin. */
@media (prefers-reduced-motion: reduce) {
  .burger > span { transition: none; }
}

/* `relative` wegen der Tafel des Benutzermenüs – siehe dort. `auto` schiebt das Konto an
   den rechten Rand, egal wie viele Seiten links davon stehen. */
.header-tools { position: relative; display: flex; align-items: center; margin-left: auto; }

/* Sichtbar ist immer das Zeichen für das Design, in das der Schalter führt – nicht für das,
   in dem man steckt. Bis 2026-09-14 war es umgekehrt, und solange nur ein Symbol dastand,
   ließ sich das auch nicht entscheiden: ☾ konnte „du bist im Dunkeln“ oder „hier geht's ins
   Dunkle“ heißen. Jetzt steht das Wort daneben, und ein Zeichen, das dem Wort widerspricht,
   wäre ein Fehler statt einer Unklarheit. Im Hellen also ☾ und „Dunkles Design“. */
.icon-sun { display: none; }
[data-theme="dark"] .icon-sun { display: inline; }
[data-theme="dark"] .icon-moon { display: none; }

/* ---------- Die Menütafel ---------- */

/* <details> statt eines Knopfs mit aria-expanded: Das Menü muss ohne JavaScript aufgehen,
   und die CSP verbietet Inline-Skripte. Dieselbe Entscheidung wie beim Kalenderfilter.
   Ab 64em entfällt der Knopf ganz – siehe unten. */
.site-menu { position: relative; }
.site-menu-knopf { list-style: none; }
.site-menu-knopf::-webkit-details-marker { display: none; }

/* Beide Tafeln messen sich an der Leiste und nicht an ihrem Knopf. Am Knopf gemessen liefe
   die des Seitenmenüs rechts aus dem Bild (es steht links) und die des Kontos links (es
   steht rechts) – beide sind 320 Punkt breit und damit breiter als ihr Knopf. An der Leiste
   gemessen enden sie an derselben Kante wie der Inhalt darunter, was sie ohnehin sollten. */
.kopf-inner { position: relative; }
.seiten-menu { position: static; }
.seiten-menu > .site-menu-panel { right: auto; left: 0; }

/* Die Tafel hängt unter der Kopfzeile und schiebt nichts weg – sie liegt darüber.

   Die Höhe ist begrenzt und der Rest scrollt. Es stehen nur sieben Seiten darin, das passt
   auf jedes Telefon – der Deckel bleibt trotzdem: Auf einem quer gehaltenen Gerät sind es
   400 Punkt Höhe, und dann läuft auch weniger aus dem Bild. Abgezogen wird die Zeile selbst
   plus die halbe Zeile Abstand oben und noch einmal so viel Luft unten. */
.site-menu-panel {
  position: absolute; top: calc(100% + .5rem); right: 0; z-index: 50;
  width: min(20rem, calc(100vw - 2rem));
  max-height: calc(100vh - var(--kopf-h) - 1.5rem); overflow-y: auto;
  /* Ringsum gleich viel: Seit im Seitenmenü keine Überschrift mehr steht, ist das
     erste Ding darin ein Eintrag wie jeder andere, und der braucht oben denselben
     Rand wie unten. Die Auskunft im Benutzermenü bringt ihren Abstand nicht mehr mit. */
  padding: .9rem;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow, 0 8px 24px rgb(0 0 0 / .18));
}

.site-menu-gruppe + .site-menu-gruppe {
  margin-top: .6rem; padding-top: .6rem; border-top: 1px solid var(--border);
}
/* Klein gesetzt und in Kapitälchenabstand: Die Überschrift ordnet, sie ist nicht der Inhalt.
   Als h2 im Quelltext, damit die Gruppen auch für Vorlesegeräte Gruppen sind. */
.site-menu-titel {
  margin: .5rem 0 .35rem; font-size: .78rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--text-muted);
}
.site-menu-liste { list-style: none; margin: 0; padding: 0; }
/* Der Abstand aus der Grundregel für li trennt hier zwei Einträge; unter dem letzten
   trennt er nichts und käme nur zum Rand der Tafel hinzu. */
.site-menu-liste li:last-child { margin-bottom: 0; }
.site-menu-liste a {
  display: block; padding: .5rem .6rem; margin: 0 -.3rem;
  color: var(--text); text-decoration: none;
  border-radius: var(--radius-sm); font-weight: 600; font-size: .97rem;
}
.site-menu-liste a:hover { color: var(--accent); background: var(--bg-soft); }
/* Wie in der Kopfzeile: Die Stelle, an der man steht – die Seite oder die Sprache –, ist
   keine Handlung und trägt deshalb die neutrale Markierung statt der Farbe der Links. */
.site-menu-liste a[aria-current] { color: var(--mark-text); background: var(--mark); }
.site-menu-liste .ext { font-size: .75em; margin-left: .25em; vertical-align: super; }

/* ---------- Das Benutzermenü hinter dem eigenen Namen ---------- */

/* Es leiht sich die Tafel vom Seitenmenü – dieselben Klassen, dasselbe Aussehen. Zwei
   Aufklapper in derselben Leiste, die verschieden aussähen, wären zwei Sprachen für
   dieselbe Sache. Eigen ist ihm nur, was darin steht.

   Anders als das Seitenmenü bleibt es auf jeder Breite: Was dahinter liegt, steht nirgends
   sonst. Deshalb trägt das Seitenmenü die Klasse `seiten-menu` und verschwindet ab 64em
   allein – siehe unten. */
.user-menu { position: static; }

/* Die Auskunft, wer angemeldet ist. Sie stand bis 2026-09-13 auf jeder Seite im Streifen
   über dem Kopf; das ist eine Auskunft, die man einmal braucht. Klein und leise: Sie ist
   die Überschrift des Menüs, nicht sein Inhalt. */
.user-menu-wer {
  margin: 0 .3rem .1rem;
  font-size: .82rem; color: var(--text-muted);
}

/* Die Zeilen im Benutzermenü sind Handlungen, keine Seiten – deshalb tragen sie ein
   Symbol. Der Abmelden-Knopf ist ein <button> im Formular (ein Link würde von jedem
   Vorauslader ausgelöst); damit er neben den Links nicht auffällt, bekommt er hier alles
   abgeräumt, was ein Knopf von Haus aus mitbringt.

   Der Selektor führt `.site-menu-liste` mit, weil `.site-menu-liste a` sonst gewinnt:
   ein Klassen- plus ein Elementname schlägt einen Klassennamen allein, und `display: block`
   von dort machte aus dem Flexkasten wieder eine Zeile ohne Symbolspalte. */
.site-menu-liste .user-menu-tat {
  display: flex; align-items: center; gap: .55rem;
  width: calc(100% + .6rem);
  padding: .5rem .6rem; margin: 0 -.3rem;
  border: 0; background: none; text-align: left;
  color: var(--text); text-decoration: none;
  border-radius: var(--radius-sm);
  font: inherit; font-weight: 600; font-size: .97rem;
  cursor: pointer;
}
.site-menu-liste .user-menu-tat:hover { color: var(--accent); background: var(--bg-soft); }
/* Der gedrückte Stift. Dieselbe Markierung wie bei der aktuellen Seite eine Gruppe darüber:
   Der Bearbeitungsmodus ist ein Zustand, in dem man steht, und die Palette hat dafür --mark.
   Das durchgehende Signal trägt die Kante unten an der Navigationsleiste – die sieht man
   auch bei geschlossenem Menü, und die klebt. */
.site-menu-liste .user-menu-tat.ist-an { color: var(--mark-text); background: var(--mark); }
.site-menu-liste .user-menu-tat .icon {
  display: block; width: 1.05rem; height: 1.05rem; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}
.site-menu-liste form { margin: 0; }

/* ---------- Der Anmelde-Kasten ---------- */

/* Ein `<dialog>`, kein Nachbau: Abdunkeln, Fokusfalle, Escape und der Rücksprung auf den
   Knopf, von dem er kam, sind darin eingebaut. Die Farben müssen trotzdem gesetzt werden –
   ein Dialog erbt sie nicht, er beginnt beim Weiß des Browsers, und im dunklen Design stünde
   sonst ein weißer Kasten in der Seite. */
.anmelde-kasten {
  width: min(26rem, calc(100vw - 2rem));
  padding: 1.1rem 1.3rem 1.3rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg); color: var(--text);
  box-shadow: var(--shadow);
}
/* Nachgemessen: Der Kasten wird mit Einleitung, Formular und Cookie-Hinweis 722 Punkt hoch
   und passt damit auf kein Telefon. Der Browser deckelt ihn zwar von selbst, schneidet
   dann aber ab – scrollen muss man ihm sagen. Die Überschrift und das Kreuz bleiben dabei
   stehen: Wer unten im Formular steht, soll den Ausgang nicht suchen müssen.
   Flex erst bei [open], sonst hebelt es das `display: none` des geschlossenen Dialogs aus. */
.anmelde-kasten[open] { display: flex; flex-direction: column; }
.kasten-inhalt { overflow-y: auto; }
/* Dunkler als der übliche Schleier: Der Kasten ist im hellen Design fast so hell wie die
   Seite dahinter, und ohne deutlichen Grund verschwämme seine Kante. */
.anmelde-kasten::backdrop { background: rgba(0, 0, 0, .5); }

.kasten-kopf {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem;
  margin-bottom: .2rem;
}
.kasten-kopf h2 { margin: 0; font-size: 1.3rem; }

/* Das Kreuz sitzt auf der Höhe der Überschrift und nicht in der Ecke des Kastens: Es
   schließt, was unter der Überschrift steht, und gehört damit zu ihr. */
.kasten-zu {
  flex: none; display: inline-flex; padding: .35rem;
  margin: -.2rem -.4rem 0 0;
  border: 0; background: none; color: var(--text-muted);
  border-radius: var(--radius-sm); cursor: pointer;
}
.kasten-zu:hover { color: var(--accent); background: var(--bg-soft); }
.kasten-zu:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.kasten-zu .icon {
  display: block; width: 1.15rem; height: 1.15rem;
  fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;
}

/* Im Kasten ist die Breite schon vom Kasten entschieden; die 34 rem des Fließtextformulars
   wären hier eine zweite Meinung dazu. */
.anmelde-kasten .form { max-width: none; }
.anmelde-kasten .form .button { width: 100%; }
.kasten-inhalt .lead { font-size: .95rem; color: var(--text-muted); }
.kasten-laedt { color: var(--text-muted); }

/* Klein: die Seiten stehen in der Menütafel, weil sie nebeneinander nicht in die Zeile
   passen. Die Regeln hier gelten deshalb für die Fassung *in der Tafel*. */
.site-nav { display: none; }
.site-nav ul {
  display: flex; flex-direction: column; gap: .25rem;
  list-style: none; margin: 0; padding: 0 0 .6rem;
}
/* Der Fließtext gibt jedem <li> 5,6 Punkt Luft nach unten, damit Aufzählungen atmen. Hier
   ist der Abstand `gap`, und die 5,6 Punkt hingen unten an der Liste: Die Leiste war damit
   44,8 statt 39,2 hoch, und die Wörter standen 3 Punkt höher als das Konto am rechten Rand. */
.site-nav li { margin: 0; }
.site-nav a {
  display: block; padding: .6rem .8rem;
  color: var(--text-muted); text-decoration: none;
  border-radius: var(--radius-sm); font-weight: 600; font-size: .97rem;
}
.site-nav a:hover { color: var(--accent); background: var(--bg-soft); }
/* Die aktuelle Seite ist keine Handlung – sie anzuklicken führt nirgendwohin. Deshalb
   trägt sie die neutrale Markierung und nicht das Blau der anklickbaren Punkte. */
.site-nav a[aria-current="page"] { color: var(--mark-text); background: var(--mark); }
/* Das Pfeilchen am externen Ziel hängt sich hoch, ohne die Zeile mitzunehmen. `vertical-align:
   super` täte dasselbe fürs Auge, vergrößerte aber die Zeilenbox – gemessen wuchs die klebende
   Leiste dadurch um 7,6 Punkt, und die Wörter daneben standen 4 Punkt höher als das Konto
   rechts. Ein Pfeil von 11 Punkt Größe darf nicht bestimmen, wie hoch die Navigation ist. */
.site-nav .ext { font-size: .75em; margin-left: .25em; position: relative; top: -.35em; }

/* Ab hier stehen die Seiten nebeneinander in der Leiste, und der Menüknopf verschwindet
   ganz. Ein Knopf, hinter dem nichts steht, was nicht schon zu sehen ist, ist einer zu viel.

   Die Schwelle ist gewandert: 66em, ab 2026-09-14 dann 58em – und am 2026-09-19 auf **64em**
   zurück. Nicht, weil es bei 58em nicht *passte*, sondern weil es nur passte. Nachgerechnet
   für den engen Fall (928 Punkt Fenster sind 913 Inhalt, denn der Bildlaufbalken nimmt 15,
   und nach dem Seitenrand bleiben 873):

     Wappen 200 + sechs Seiten 594 (Deutsch) + Konto 49 = 843 von 873.

   Dreißig Punkt für zwei Lücken, also fünfzehn je Seite – bei 23 Punkt zwischen zwei
   Wörtern. Die Trennung zwischen zwei Gruppen wäre damit enger als der Zusammenhalt
   innerhalb einer, und genau so sah es auch aus: gedrängt (Verein). Ab 64em sind es 969
   Punkt Inhalt und damit 126 für die Lücken – da atmet die Zeile.

   Der Preis steht auch da: Zwischen 928 und 1024 Punkt – kleine Notebooks, Tablets quer –
   führt jetzt der Hamburger zu den Seiten statt der Wortleiste. Das ist derselbe Weg, den
   das Telefon ohnehin geht, und er ist vollständig: In der Menütafel stehen alle sieben
   Seiten, in der Leiste stünden sechs. */
@media (min-width: 64em) {
  /* Nur das Seitenmenü verschwindet. Das Benutzermenü nebenan trägt dieselben Klassen,
     bleibt aber auf jeder Breite: Hinter ihm liegt etwas, was sonst nirgends steht. */
  .seiten-menu { display: none; }
  .site-nav { display: block; min-width: 0; }
  /* Mittig in der eigenen Spalte, nicht am Wappen angeschlagen. Linksbündig sammelte sich
     der ganze freie Platz hinter dem letzten Wort – gemessen 19 Punkt vor „Mitglied werden“
     und 158 hinter „Kontakt“. Das wirkt nicht großzügig, sondern unabsichtlich. */
  .site-nav ul {
    flex-direction: row; flex-wrap: nowrap; justify-content: center; padding-bottom: 0;
  }
  /* Schrift und Polsterung sind dieselben wie am Konto-Knopf nebenan (.nav-knopf): In
     dieser Zeile ist alles gleichrangig, und Gleichrangiges wird gleich gesetzt. */
  .site-nav a { padding: .45rem .58rem; font-size: .94rem; white-space: nowrap; }
}

/* ---------- Header-Bild der Startseite ---------- */

/* Das Bild läuft über die volle Fensterbreite, der Text darauf folgt dem Raster
   des restlichen Inhalts. Der Verlauf hält die weiße Schrift lesbar, egal welcher
   Bildausschnitt bei der jeweiligen Fensterbreite unten liegt. */
.hero { position: relative; line-height: 0; }
/* Die Höhe entscheidet, wie viel vom Bild zu sehen ist: je flacher der Streifen,
   desto enger der Ausschnitt. Deshalb wächst sie mit der Fensterbreite mit – bliebe
   sie fest, zöge ein breiter Schirm das Bild heran, statt mehr davon zu zeigen. Auf
   dem Handy reicht der Streifen an das Seitenverhältnis des Bildes heran, dort steht
   es also fast vollständig da. Der Ausschnitt sitzt leicht oberhalb der Mitte: tiefer
   gelegt schneidet er dem Spieler den Kopf ab, höher steht über dem Tisch nur Wand. */
.hero img {
  display: block; width: 100%;
  height: clamp(13.5rem, 42vw, 31rem);
  object-fit: cover; object-position: 50% 40%;
}
.hero-text {
  position: absolute; left: 0; right: 0; bottom: 0;
  line-height: 1.25; color: #fff;
  padding: 3rem 0 1.2rem;
  background: linear-gradient(to top, rgba(0, 0, 0, .72), rgba(0, 0, 0, 0));
}
.hero-text h1 { margin: 0; color: inherit; text-shadow: 0 1px 3px rgba(0, 0, 0, .55); }
.hero-text p {
  margin: .3rem 0 0; font-size: 1.05rem; font-weight: 600;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .55);
}

/* ---------- Inhalt ---------- */

/* Der Seitenanfang ist auf jeder Breite gleich groß – und größer als jeder Abstand *innerhalb*
   der Seite. Das war er bis zum 2026-09-19 nicht: Gemessen standen zwischen Kopfzeile und
   Titel 29 Punkt (375) bzw. 40 (1280), während eine Zwischenüberschrift mitten im Text 35
   bekam. Der größte Schnitt der Seite hatte damit den kleinsten Abstand, und der Anfang las
   sich nicht wie ein Anfang.

   Nach unten wächst der Abstand mit der Breite: Auf dem Telefon ist Platz knapp und der Fuß
   die nächste Station, auf dem Schirm ist Platz da und ein Text, der direkt am Fuß endet,
   wirkt abgeschnitten. */
main { padding: var(--raum-xl) 0; }
@media (min-width: 46em) { main { padding: var(--raum-xl) 0 var(--raum-xxl); } }

/* h2 und h3 trennen Verschiedenes und bekommen deshalb Verschiedenes: h2 beginnt einen neuen
   Abschnitt, h3 gliedert innerhalb eines. Bis zum 2026-09-19 stand für beide dasselbe Maß
   (2.2rem) – zwei Ebenen, ein Abstand, und damit sah die Gliederung flacher aus, als sie ist.
   Nach unten ist es je eine Stufe weniger: Eine Überschrift gehört zu dem, was unter ihr
   steht, und muss näher daran stehen als an dem, was über ihr endet. */
h1, h2, h3 { line-height: 1.25; font-weight: 700; }
h1 { font-size: clamp(1.8rem, 1.3rem + 2vw, 2.5rem); margin: 0 0 var(--raum-s); }
h2 { font-size: clamp(1.35rem, 1.1rem + 1vw, 1.7rem); margin: var(--raum-l) 0 var(--raum-s); }
h3 { font-size: 1.15rem; margin: var(--raum-m) 0 var(--raum-xs); }

/* Der Seitenkopf: Überschrift links, Stift rechts (2026-09-14, siehe page-title.php).

   `align-items: baseline` statt `center`: Die Überschrift ist bis 2,5 rem groß, der Stift
   knapp 1. Auf Mitte gesetzt schwämme er neben dem Wort; auf der Grundlinie sitzt er auf
   derselben Linie wie der Text, an dem er hängt.

   `flex-wrap` mit `row-gap`, weil „Angaben bearbeiten“ neben „Datenschutzerklärung“ bei
   375 Punkt nicht in eine Zeile passt – gemessen. Es bricht dann unter die Überschrift,
   und der Kasten wächst um eine Zeile statt den Titel zu quetschen. */
.seiten-kopf {
  display: flex; align-items: baseline;
  flex-wrap: wrap; gap: .3rem .9rem;
  /* Derselbe Abstand, den h1 mitbrächte, stünde es allein – der Kasten übernimmt ihn. */
  margin-bottom: var(--raum-s);
}
/* Die Überschrift bringt ihren Abstand selbst mit; im Kasten stünde er doppelt. */
.seiten-kopf > h1 { margin: 0; min-width: 0; }
/* Die Startseite: kein Titel, nur der Stift – ohne Überschrift gibt es nichts, wovon er
   abrücken müsste, deshalb hier weniger Abstand nach unten. */
.seiten-kopf.ohne-titel { margin-bottom: var(--raum-xs); }

/* Der Stift. Er sieht aus wie ein leiser Knopf und nicht wie ein Link: Ein unterstrichenes
   Wort neben einer Überschrift läse sich wie ein Teil von ihr.

   `margin-left: auto` und nicht `justify-content: space-between` am Kasten, und das ist der
   Unterschied zwischen drei Fällen und einer Regel. Der Stift steht **immer rechts**: neben
   einem kurzen Titel, allein auf der Startseite, und auch dann noch, wenn er bei 375 Punkt
   unter einen langen Titel rutscht – dort ist er in seiner Zeile das einzige Element, und
   `space-between` hätte ihn nach links gesetzt. Ein Werkzeug, das je nach Seitenbreite die
   Seite wechselt, muss jedes Mal neu gesucht werden. */
.titel-stift {
  display: inline-flex; align-items: center; gap: .4rem; flex: none;
  margin-left: auto;
  padding: .35rem .6rem;
  border: 1px solid var(--border); border-radius: 999px;
  color: var(--text-muted); text-decoration: none; background: var(--bg);
  font-size: .88rem; font-weight: 600; white-space: nowrap;
}
.titel-stift:hover { color: var(--accent); border-color: var(--accent); }
/* Im Bearbeitungsmodus trägt der Stift dieselbe Markierung wie die Akzentkante unten an der
   Navigationsleiste. Zwei Signale, ein Zustand – und eines davon klebt, ist also auch dann
   noch zu sehen, wenn man weit genug gescrollt hat, dass der Titel oben aus dem Bild ist. */
.titel-stift.ist-an {
  color: var(--mark-text); background: var(--mark); border-color: var(--mark);
}
.titel-stift .icon {
  display: block; width: 1rem; height: 1rem; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}

p { margin: 0 0 var(--raum-m); }
a { color: var(--accent); text-underline-offset: .15em; }
a:hover { text-decoration: none; }

:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

ul, ol { padding-left: 1.3rem; }
li { margin-bottom: .35rem; }
ul.plain { list-style: none; padding: 0; }

img { max-width: 100%; height: auto; }

figure { margin: var(--raum-l) 0; }
figure img { border-radius: var(--radius-lg); box-shadow: var(--shadow); display: block; }
figcaption { margin-top: .6rem; font-size: .9rem; color: var(--text-muted); text-align: center; }

/* Eine Stufe über dem Blockabstand: Der Strich trennt Abschnitte, nicht Absätze. */
hr { border: 0; border-top: 1px solid var(--border); margin: var(--raum-xl) 0; }

.lead { font-size: 1.15rem; color: var(--text-muted); }

.button {
  display: inline-block; padding: .7rem 1.3rem;
  background: var(--accent); color: var(--accent-text);
  border: 0; border-radius: var(--radius-sm);
  font: inherit; font-weight: 600; text-decoration: none; cursor: pointer;
}
.button:hover { background: var(--accent-hover); }

/* Zwei Merkmale auf der Startseite.
   Der Abstand nach außen ist der Blockabstand, der nach innen eine Stufe kleiner:
   So bleiben die Kacheln sichtbar eine Gruppe und fallen nicht in den Text. */
.features {
  display: grid; gap: var(--raum-m);
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  margin: var(--raum-l) 0;
}
.feature {
  text-align: center; padding: var(--raum-m);
  background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.feature img { width: 64px; height: 64px; margin-bottom: .7rem; }
/* Anders als beim Logo ist das Umfärben hier unschädlich: die beiden SVG sind
   einfarbig schwarze Strichzeichnungen, es geht also keine Farbe verloren. */
[data-theme="dark"] .feature img { filter: brightness(0) invert(1) opacity(.85); }
.feature strong { display: block; }

.card {
  padding: var(--raum-m); margin: var(--raum-l) 0;
  background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.card > :first-child { margin-top: 0; }
.card > :last-child { margin-bottom: 0; }

.center { text-align: center; }

/* Spalten aus dem Editor. Auf dem Handy stehen sie untereinander. */
.cols { display: grid; gap: var(--raum-m); margin: var(--raum-l) 0; }
.cols > .col > :first-child { margin-top: 0; }
.cols > .col > :last-child { margin-bottom: 0; }
@media (min-width: 46em) {
  .cols { grid-auto-flow: column; grid-auto-columns: 1fr; }
}

/* Drei Beitragsgruppen als Kacheln. Als Liste mit Trennlinien sah das aus wie eine
   halbe Tabelle – bei drei Werten trägt die Spaltenform nicht. */
.prices {
  list-style: none; padding: 0; margin: var(--raum-l) 0;
  display: grid; gap: var(--raum-m);
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}
.prices li {
  margin: 0; padding: var(--raum-m); text-align: center;
  background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.price-group { display: block; font-weight: 600; }
.price-amount {
  display: block; margin: .2rem 0 .1rem;
  font-size: 1.9rem; font-weight: 700; line-height: 1.1;
  /* Der Preis ist kein Link. Die Größe hebt ihn schon heraus, die Akzentfarbe
     hätte nur versprochen, dass man darauf klicken kann. */
  color: var(--text); font-variant-numeric: tabular-nums;
}
.price-unit { display: block; font-size: .85rem; color: var(--text-muted); }

/* Die Karte steht nur noch im Fuß und wird dort gestaltet – siehe „Anfahrt“ weiter unten. */

/* ---------- Ankündigungen ---------- */

.announce {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--raum-m);
  align-items: start;
}
.announce > h2 { grid-column: 1 / -1; margin-top: 0; }
.announce-media { display: grid; gap: var(--raum-m); align-content: start; }
.announce-media figure { margin: 0; }
.announce-media img { width: 100%; height: auto; border-radius: var(--radius-lg); }
.announce-media .announce-logo img { width: auto; max-width: 9rem; border-radius: 0; }
.announce-media figcaption { font-size: .85rem; color: var(--text-muted); margin-top: .35rem; }
.announce-text > :first-child { margin-top: 0; }
.announce-text > :last-child { margin-bottom: 0; }

/* Ab Tablet Bild und Text nebeneinander, sofern ein Bild da ist. */
@media (min-width: 46em) {
  .announce:not(.announce-plain) {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    /* Nebeneinander braucht die Spalte mehr Luft als die Zeile, sonst laufen zwei
       Textblöcke optisch ineinander – deshalb quer eine Stufe größer. */
    gap: var(--raum-m) var(--raum-l);
  }
}

/* ---------- Liga-Tabellen und Terminliste ---------- */

/* Notausgang für die Tabellen der Verwaltung; die Liga-Tabelle kommt auf dem Handy
   ohne aus, siehe col-wide weiter unten. tabindex macht einen doch scrollenden
   Bereich auch mit der Tastatur bedienbar. */
.table-scroll { overflow-x: auto; margin-bottom: var(--raum-m); }

.liga-tabelle, .admin-liga {
  width: 100%; border-collapse: collapse; font-size: .95rem;
}
.liga-tabelle th, .liga-tabelle td,
.admin-liga th, .admin-liga td {
  padding: .45rem .35rem; white-space: nowrap; text-align: left;
  border-bottom: 1px solid var(--border);
}

/* Zehn Spalten passen auf einem Handy nicht, und eine Tabelle, die man erst zur Seite
   schieben muss, ist schlechter als eine Tabelle mit weniger Spalten. Auf kleinen
   Displays bleiben deshalb Platz, Mannschaft, Punkte, Differenz und Partien – das ist
   die Rangfolge. Spiele, Siege, Unentschieden, Niederlagen und Frames kommen ab
   Tablet-Breite dazu; wer sie auf dem Handy braucht, findet sie im Portal, der Link
   dorthin steht unter der Tabelle. */
.liga-tabelle .col-wide { display: none; }
@media (min-width: 46em) {
  .liga-tabelle .col-wide { display: table-cell; }
  .liga-tabelle th, .liga-tabelle td { padding: .45rem .6rem; }
}
.liga-tabelle thead th, .admin-liga thead th {
  font-size: .85rem; color: var(--text-muted); border-bottom-width: 2px;
}
.liga-tabelle abbr { text-decoration: none; cursor: help; }

/* Zahlen rechts, der Vereinsname links – so stehen die Ziffern untereinander. */
.liga-tabelle td, .liga-tabelle thead th:first-child, .liga-tabelle thead th:nth-child(n+3) {
  text-align: right; font-variant-numeric: tabular-nums;
}
.liga-tabelle tbody th { font-weight: 400; white-space: normal; }

/* Die eigene Zeile ist grün: dasselbe Grün, mit dem auch Heimspiele und die eigene
   Seite der Paarung markiert sind. Grün heißt auf dieser Seite durchgehend „wir“. */
.liga-tabelle tr.liga-eigene th, .liga-tabelle tr.liga-eigene td {
  background: var(--bg-soft); font-weight: 700; color: var(--green);
}

/* Die Prüfseite für den Mailversand (/admin/mail/).

   Der Befund ist eine Tabelle ohne Kopfzeile: Drei Spalten, und alle drei erklären sich
   selbst – Zeichen, Sache, Befund. Eine Kopfzeile darüber sagte dasselbe noch einmal. */
.admin-mail-befund { width: 100%; border-collapse: collapse; margin-bottom: 1.6rem; }
.admin-mail-befund th, .admin-mail-befund td {
  padding: .5rem .5rem; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--border);
}
.admin-mail-befund tbody th { font-weight: 600; }
/* Im Befund stehen Dateinamen und Adressen – „privat/secrets.php“, „info@sax-max.de“ –,
   und die bricht der Browser von sich aus nicht. Bei 375 Punkt schob das die Tabelle
   19 Punkt über den Rand hinaus (gemessen). Lieber mitten im Wort umbrechen als die
   ganze Seite wackeln lassen. */
.admin-mail-befund td { overflow-wrap: anywhere; }
/* Das Zeichen trägt die Farbe, nicht die Zeile: Eine ganze rote Zeile schriee auch dann,
   wenn daneben zwei grüne stehen und die Sache halb in Ordnung ist. */
.admin-mail-befund .stand { width: 1.6rem; font-weight: 700; text-align: center; }
.admin-mail-befund .stand-ok .stand      { color: var(--green); }
.admin-mail-befund .stand-warnung .stand { color: var(--accent); }
.admin-mail-befund .stand-fehler .stand  { color: var(--red); }
.admin-mail-befund .stand-info .stand    { color: var(--text-muted); }
.admin-mail-befund .stand-info th, .admin-mail-befund .stand-info td { color: var(--text-muted); }

.admin-mail-form label { display: block; margin-bottom: .3rem; font-weight: 600; }
/* Eine Adresse ist so lang, wie sie ist – aber ein Feld über die ganze Breite eines
   62-rem-Textes sieht aus, als gehörte ein Satz hinein. */
.admin-mail-form input { width: 100%; max-width: 26rem; }

.admin-mail-ergebnis {
  margin: 1.2rem 0; padding: .8rem 1rem;
  border-left: 4px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-soft);
}
.admin-mail-ergebnis.ist-ok     { border-left-color: var(--green); }
.admin-mail-ergebnis.ist-fehler { border-left-color: var(--red); }

/* Das Gespräch mit dem Mailserver. Feste Schriftbreite, weil der Antwortcode am
   Zeilenanfang steht und untereinander zu lesen sein muss; kein Umbruch, weil eine
   umgebrochene Serverantwort nicht mehr als eine Antwort zu erkennen ist – deshalb darf
   der Kasten scrollen, und tabindex macht das auch mit der Tastatur möglich. */
.admin-mail-log {
  margin: 0 0 1.4rem; padding: .8rem 1rem; overflow-x: auto;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-soft); color: var(--text);
  font-size: .82rem; line-height: 1.5;
}
.admin-mail-log span { display: block; white-space: pre; }
.admin-mail-log .p-client { color: var(--text-muted); }
.admin-mail-log .p-server { color: var(--text); }
.admin-mail-log .p-info   { color: var(--text-muted); font-style: italic; }
.admin-mail-log .p-ok     { color: var(--green); font-weight: 700; }
.admin-mail-log .p-fehler { color: var(--red); font-weight: 700; }

/* Terminliste: links ein Kalenderblatt, rechts Titel und Angaben, gruppiert nach Monat.
   Dieselben Zeilen tragen den Spielplan der Mannschaftsseite, siehe .fixtures weiter unten. */

/* Die Leiste über der Liste: links die Auswahl, rechts, was man mit ihr tun kann –
   Ansicht, Druck, Abo. Auf dem Handy stehen beide Hälften untereinander; nebeneinander
   bliebe für den Filter zu wenig übrig, und der hat mehr Knöpfe. Erst wenn die Zeile es
   hergibt, rücken sie zusammen, und die Handlungen gehen ans rechte Ende.

   position: relative trägt den Abo-Kasten. Er hängt unter der ganzen Leiste und nicht
   unter seinem Knopf: So bleibt er innerhalb der Seitenbreite, egal wo der Knopf nach
   dem Umbruch gerade steht, und nichts läuft über den Rand. */
.event-bar { position: relative; display: grid; gap: .5rem; margin-bottom: 1.4rem; }
/* Die Handlungen dürfen umbrechen. Drei Bedienelemente nebeneinander passen auf ein
   Telefon nicht in eine Zeile. */
.event-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }

/* Der Filter auf schmalen Schirmen: dieselbe Knopfleiste, zusammengefaltet.

   Die Leiste stand hier einmal auch auf dem Telefon und scrollte waagerecht. Das war
   die falsche Bewegung: Ein abgeschnittener Knopf am rechten Rand sieht aus wie das
   Ende der Leiste, nicht wie ihre Fortsetzung, und nach den übrigen Auswahlmöglichkeiten
   wischt dann niemand.

   Zuerst stand hier eine Auswahlliste. Die zeigt zwar alles auf einmal, sieht aber aus
   wie ein Formularfeld und bringt die Gestalt des Betriebssystems mit – in einer Leiste
   aus runden Knöpfen ein Fremdkörper. Jetzt sind es dieselben Knöpfe, nur gefaltet: Die
   zugeklappte Zeile zeigt, was gerade gilt, aufgeklappt stehen alle untereinander.

   Rahmen und Hintergrund sitzen am <details> und nicht an seiner Zeile. Sonst wären es
   zwei Körper – oben eine Pille, darunter ein angeklebter Kasten –, und das sieht aus,
   als erschiene etwas Zweites, statt dass sich eines öffnet. So ist es ein Kasten, der
   seine Form wechselt: zugeklappt die Pille wie überall in dieser Leiste, aufgeklappt
   die Kastenrundung, die das Blatt seinen Bildern und Karten gibt. */
.event-filter-menu {
  margin: 0;
  background: var(--bg-soft);
  border: 1px solid var(--border); border-radius: 999px;
}
.event-filter-menu[open] { border-radius: var(--radius-lg); }
.event-filter-menu > summary {
  display: flex; align-items: center; gap: .5rem;
  padding: 3px 3px 3px .9rem;
  cursor: pointer;
}
/* Das Dreieck des Browsers fällt weg – daneben steht ein eigener Pfeil, und zwei Zeichen
   für dieselbe Auskunft sind eines zu viel. */
.event-filter-menu > summary { list-style: none; }
.event-filter-menu > summary::-webkit-details-marker { display: none; }
.event-filter-menu > summary:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.event-filter-caption {
  font-size: .92rem; font-weight: 600; color: var(--text-muted);
}
/* Die gewählte Auswahl sieht aus wie der markierte Knopf in der offenen Leiste – es ist
   derselbe Zustand, nur in der gefalteten Fassung. */
.event-filter-value {
  padding: .4rem .9rem; border-radius: 999px;
  background: var(--mark); color: var(--mark-text);
  font-size: .92rem; font-weight: 600;
}
/* Aufgeklappt schweigt die Zeile. Eine Aufklapp-Zeile fasst zusammen, was verborgen ist –
   ist nichts mehr verborgen, gibt es nichts zusammenzufassen. Stünde die Pille weiter da,
   wären zwei Felder in der Markierungsfarbe gleichzeitig zu sehen, und das liest sich wie
   eine Mehrfachauswahl. Sichtbar bleibt genau eine Markierung, und zwar die, die man
   anfassen kann.

   visibility und nicht display: Die Pille ist das höchste Kind der Zeile. Nähme display
   sie aus dem Satz, fiele die Zeile beim Aufklappen um ein Dutzend Punkt ein und alles
   darunter ruckte mit; mit einer festen Mindesthöhe dagegen müsste man ihre Höhe als Zahl
   hinschreiben und bei jeder Schriftänderung nachführen. So hält sie den Platz, den sie
   ohnehin hat – die Höhe stimmt von selbst, ohne dass irgendwo eine Zahl steht. Aus dem
   Vorlesegerät ist sie damit ebenso verschwunden wie mit display: none.

   Waagerecht kostet das nichts: Die Beschriftung steht links, der Pfeil wird von
   margin-left: auto an den rechten Rand gedrückt. Dazwischen ist nur Luft, ob die Pille
   dort unsichtbar steht oder gar nicht. */
.event-filter-menu[open] .event-filter-value { visibility: hidden; }
.event-filter-arrow {
  display: inline-flex; margin-left: auto; margin-right: .5rem;
  color: var(--text-muted); transition: transform .15s ease;
}
.event-filter-arrow .icon { width: 1.1rem; height: 1.1rem; }
.event-filter-menu[open] .event-filter-arrow { transform: rotate(180deg); }
/* Auf breiten Schirmen stehen beide Hälften nebeneinander – aber nur, solange sie es
   beide unbeschnitten tun. Wie breit der Filter ist, weiß niemand vorher: Er trägt
   Mannschaftsnamen aus den Daten, und der Verein kann morgen eine dritte Mannschaft
   melden. Ein festes Raster müsste dann etwas wegnehmen, und weggenommen würde vom
   Filter, der dadurch zu scrollen begänne, obwohl die Zeile fast reicht.

   Deshalb eine umbrechende Flex-Zeile: Der Browser legt beide Hälften in ihrer
   natürlichen Breite nebeneinander und schiebt die Handlungen in eine zweite Zeile,
   sobald das nicht aufgeht. So ist es eine Zeile, wenn es eine sein kann, und zwei,
   wenn es zwei sein müssen – ohne dass irgendwo etwas abgeschnitten wird. */
@media (min-width: 58rem) {
  .event-bar { display: flex; flex-wrap: wrap; align-items: center; }
  /* 0 0 auto, nicht 0 1 auto: Die Leiste darf sich nicht zusammendrücken lassen. Seit
     das waagerechte Scrollen weg ist, hat ein zu schmaler Filter keinen Ausweg mehr –
     er würde seine letzten Knöpfe einfach über den Rand schieben. Lieber umbricht die
     Zeile. */
  .event-filter { flex: 0 0 auto; }
  .event-actions { flex: 0 0 auto; flex-wrap: nowrap; margin-left: auto; }
}

/* Beide Schalter sehen gleich aus, weil sie dasselbe tun: aus wenigen festen
   Möglichkeiten eine auswählen. Der Ansichtsumschalter hat nur zwei Knöpfe und passt
   überall; der Filter hat mehr und erscheint erst oben im Breitbild-Block. */
.event-filter, .event-view {
  display: flex; gap: .2rem; padding: 3px;
  background: var(--bg-soft); border: 1px solid var(--border); border-radius: 999px;
}
/* Schmal ist der Aufklapper zuständig, breit die offene Leiste. Es ist immer genau eine
   von beiden sichtbar – die andere steht trotzdem im HTML, damit die Seite ohne
   JavaScript in jeder Breite bedienbar bleibt. */
.event-filter { display: none; }
/* Die Leiste im Aufklapper ist dieselbe, nur umbrechend statt einzeilig: Alle Knöpfe
   stehen da, keiner hängt über dem Rand.

   Rahmen, Hintergrund und Rundung gibt sie ab – die trägt jetzt das <details> um sie
   herum. Bliebe ihr eigener Rahmen stehen, stünde ein Kasten im Kasten. Es bleibt eine
   Trennlinie nach oben: Darüber steht die Auskunft, darunter die Auswahl.

   (Hier stand einmal border-radius: var(--radius-md). Die Variable gibt es nicht – das
   Blatt kennt nur sm, keine, lg. Eine unbekannte Variable macht die Deklaration ungültig,
   und border-radius fiel auf seinen Anfangswert zurück: null. Der Kasten war rechtwinklig,
   gemessene 0px, und das war keine Absicht, sondern ein Vertipper.) */
.event-filter.is-menu {
  display: flex; flex-wrap: wrap; gap: .3rem;
  padding: .5rem;
  background: none;
  border: 0; border-top: 1px solid var(--border);
  border-radius: 0;
}
/* Die Umschaltstelle steht hier unten und nicht oben bei den übrigen Breitbild-Regeln:
   Ein Medienblock gewichtet nicht stärker, er kommt nur irgendwann – und „später im
   Blatt“ gewinnt. Stünde er vor .event-filter { display: none }, bliebe die Leiste in
   jeder Breite verborgen. */
@media (min-width: 58rem) {
  .event-filter { display: flex; }
  .event-filter-menu { display: none; }
}
.event-filter a, .event-view a {
  padding: .4rem .9rem; border-radius: 999px;
  color: var(--text-muted); text-decoration: none;
  font-size: .92rem; font-weight: 600; white-space: nowrap;
}
.event-filter a:hover, .event-view a:hover { color: var(--accent); }
.event-filter a[aria-current="true"], .event-view a[aria-current="true"] {
  background: var(--mark); color: var(--mark-text);
}
/* Der Ansichtsumschalter trägt Symbole statt Wörter und wird dafür quadratisch. Die
   Zeilen-Symbole und das Raster sind das übliche Paar für „Liste oder Übersicht“, und
   welche Ansicht gerade gilt, sagt weiter die Füllung.

   2,3 rem sind so gewählt, dass der Umschalter mit Rahmen und Innenabstand genau so hoch
   wird wie die Filterleiste daneben (45 px) – und die runden Symbolknöpfe mit ihren
   2,8 rem ebenfalls. Drei Bauarten in einer Zeile, drei verschiedene Höhen: Das sähe
   nicht nach Absicht aus, sondern nach Versehen. */
.event-view a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.3rem; height: 2.3rem; padding: 0;
}
/* Der Umschalter bekommt nur so viel Platz, wie seine beiden Wörter brauchen. */
.event-view { flex: none; }

/* Das Saisonband: je Monat eine Zeile, je Woche eine Zelle, je Tag ein Feld. Warum
   Wochen und keine Wochentage, steht bei event_saisonband(); was ein Feld zeigt, in
   event-band.php.

   Fünf Spalten, weil kein Monat mehr als fünf Samstage hat. Welche Spalte eine Woche
   bekommt, sagt PHP über is-w1 bis is-w5 – die angeschnittenen Monate am Anfang und Ende
   der Saison müssen an der richtigen Stelle stehen, nicht am linken Rand. */
/* Mittig, nicht linksbündig. Das Band ist auf 31 rem gedeckelt, alles andere auf der
   Seite nimmt die volle Breite – links angeschlagen hing es zwischen 26 und 56 rem mit
   einem Loch von achtzig Punkt am rechten Rand da, und das sieht nach Versehen aus. Die
   Reihe der Monate ist ohnehin ein Block für sich und keine Textspalte, an deren Kante
   sich das Auge entlangliest. Auf dem Telefon und am Schreibtisch ändert sich nichts:
   Dort füllt das Band die Breite und der Rand fällt auf beiden Seiten weg. */
.eventband {
  display: grid; gap: .2rem;
  max-width: 31rem; margin-inline: auto; margin-bottom: 2.4rem;
}
.band-row {
  display: grid; grid-template-columns: 1.8rem repeat(5, minmax(0, 1fr));
  gap: .2rem; align-items: stretch;
}
.band-month {
  align-self: center;
  font-size: .72rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-muted);
}
.band-week.is-w1 { grid-column: 2; }
.band-week.is-w2 { grid-column: 3; }
.band-week.is-w3 { grid-column: 4; }
.band-week.is-w4 { grid-column: 5; }
.band-week.is-w5 { grid-column: 6; }

/* Die leere Woche wird mitgezeichnet – sie ist die eigentliche Auskunft des Bandes.
   Eine Liste kann nicht zeigen, dass zwischen zwei Terminen drei Wochenenden frei sind. */
.band-week {
  display: flex; align-items: stretch; justify-content: center; gap: .1rem;
  min-height: 1.9rem; padding: .1rem;
  background: var(--bg-soft);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}

/* Die laufende Woche. Am Bildschirm sagt sie, wo man in der Saison steht – auch dann,
   wenn gerade nichts stattfindet und kein Termin das Heute tragen könnte. */
.band-week.is-now { border-color: color-mix(in srgb, var(--mark) 45%, var(--border)); }
.band-week.is-now .band-day.is-frei .band-num { color: var(--text); opacity: 1; }

/* Ein Feld ist ein Tag – belegt oder frei. Samstag und Sonntag teilen sich die Zelle zu
   gleichen Teilen; stünde der freie Tag schmaler da als der belegte, wäre die Zelle mal
   halbiert und mal nicht, je nachdem, ob gespielt wird. */
.band-day {
  display: flex; align-items: center; justify-content: center; gap: .25rem;
  flex: 1 1 0; min-width: 0; padding: .1rem;
  text-decoration: none; color: var(--text);
}
/* Wochentag und Einträge stehen erst auf breiten Bildschirmen da, siehe unten. */
.band-wd, .band-info { display: none; }

/* Die Tageszahl trägt die Färbung, nicht das ganze Feld: Auf dem Telefon ist sie das
   Feld, auf dem Schreibtisch steht sie als Blatt neben dem, was ansteht. So heißt Grün
   an beiden Stellen dasselbe, ohne dass zweierlei gefärbt werden müsste. */
.band-num {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.45rem; padding: .1rem .15rem;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  font-size: .72rem; font-weight: 700; font-variant-numeric: tabular-nums;
  line-height: 1.15;
}

/* Eng gesetzt, solange es sein muss: Auf einem 320 Punkt breiten Telefon bleiben je
   Woche keine fünfzig Punkt, und an einem Wochenende stehen zwei zweistellige Zahlen
   nebeneinander. Sobald die Zeile es hergibt, bekommt alles Luft – ein Raster aus
   Zahlen liest sich nicht, wenn die Zahlen ihre Felder ausfüllen. */
@media (min-width: 26rem) {
  .eventband { gap: .25rem; }
  .band-row { grid-template-columns: 2.2rem repeat(5, minmax(0, 5.5rem)); gap: .25rem; }
  .band-week { gap: .15rem; min-height: 2.2rem; padding: .15rem; }
  .band-num { min-width: 1.6rem; padding: .15rem .25rem; font-size: .8rem; }
}

/* Auf dem Schreibtisch wird aus dem Raster ein Kalender, der mehr sagt als das Datum:
   Das Band nimmt die volle Breite, die Zelle stellt ihre Tage untereinander, und neben
   jedem steht, wer spielt und gegen wen. Das ist die Auskunft, für die man sonst klicken
   müsste – und der Platz dafür ist da, sobald eine Zelle etwa hundertfünfzig Punkt breit
   wird. Darunter bleibt es beim Zahlenraster: Eine halbe Zeile Vereinsname ist keine
   Auskunft, sondern ein Rätsel.

   Die Grenze liegt bei 56 rem, weil dort eine Zelle rund hundertsechzig Punkt bekommt –
   genug für „1. SC Limbach-Oberfrohna 1“ in zwei Zeilen. Umbrechen darf der Name; gekürzt
   wird er nicht, ein halber Vereinsname benennt niemanden. */
@media (min-width: 56rem) {
  /* Breiter als seine fünf Spalten wird das Band nicht mehr begrenzt – hier ist die
     Breite der Seite genau das, was die Zellen brauchen. */
  .eventband { max-width: none; gap: .3rem; }
  .band-row { grid-template-columns: 2.6rem repeat(5, minmax(0, 1fr)); gap: .3rem; }
  .band-week {
    flex-direction: column; align-items: stretch; justify-content: flex-start;
    gap: .15rem; min-height: 3.2rem; padding: .25rem;
  }
  /* Drei Spalten: Wochentag, Tageszahl, was ansteht. Die ersten beiden so knapp wie
     möglich – jeder Punkt, den sie bekommen, fehlt dem Vereinsnamen daneben. */
  .band-day {
    flex: 0 0 auto;
    display: grid; grid-template-columns: 1.05rem auto minmax(0, 1fr);
    align-items: start; gap: 0 .3rem;
    padding: .05rem .1rem; border-radius: var(--radius-sm);
  }
  .band-wd {
    display: block; padding-top: .2rem;
    font-size: .64rem; font-weight: 600; letter-spacing: .02em;
    color: var(--text-muted);
  }
  .band-info { display: block; padding-top: .1rem; }
}

/* Ein Eintrag: wer spielt, und gegen wen. Der eigene Name fehlt mit Absicht – er stünde
   in jeder Zeile gleich da und unterschiede nichts.

   Die Farbe hängt am Eintrag und nicht am Tag. Am 20.03. spielt die Zweite zu Hause und
   die Erste auswärts; wäre der ganze Tag grün, behauptete er das Heimspiel für beide. */
.band-entry { display: block; color: var(--text); }
.band-entry[data-home] { color: var(--event-home); }
.band-entry + .band-entry { margin-top: .25rem; }
.band-team {
  display: block;
  font-size: .62rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  opacity: .75;
}
/* Gefüllt heißt „bei uns“, ein Ring heißt auswärts – dieselbe Unterscheidung wie bei der
   Tageszahl, nur klein genug, um vor einem Wort zu stehen. */
.band-team::before {
  content: ""; display: inline-block;
  width: .4rem; height: .4rem; margin-right: .3rem;
  border: 1px solid currentColor; border-radius: 50%;
}
.band-entry[data-home] .band-team::before { background: currentColor; }
.band-opp {
  display: block; font-size: .74rem; font-weight: 600; line-height: 1.25;
  overflow-wrap: anywhere;
}

/* Dieselbe Sprache wie in der Liste: Grün heißt „bei uns“, ungefärbt heißt auswärts.
   Wo an einem Tag beides stattfindet, ist die Zahl halb und halb – am 20.03. spielt
   die Zweite zu Hause und die Erste auswärts, und beides ist wahr. */
.band-day.is-heim .band-num, .band-key.is-heim::before {
  background: color-mix(in srgb, var(--event-home) 16%, var(--bg));
  border-color: color-mix(in srgb, var(--event-home) 40%, var(--bg));
  color: var(--event-home);
}
.band-day.is-auswaerts .band-num, .band-key.is-auswaerts::before {
  background: var(--bg); border-color: var(--border);
}
.band-day.is-beides .band-num, .band-key.is-beides::before {
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--event-home) 16%, var(--bg)) 50%, var(--bg) 50%);
  border-color: color-mix(in srgb, var(--event-home) 40%, var(--bg));
}
.band-day.is-today .band-num {
  background: var(--mark); border-color: var(--mark); color: var(--mark-text);
}
/* Der freie Tag steht blass da: Er gehört zum Kalender, aber nicht zur Auskunft. */
.band-day.is-frei .band-num { font-weight: 400; color: var(--text-muted); opacity: .5; }

.band-day:hover .band-num { border-color: var(--accent); color: var(--accent); }
.band-day:hover .band-opp { text-decoration: underline; }
/* Der aufgeklappte Tag bleibt erkennbar, auch wenn die Karte weiter unten steht. */
.band-day[aria-expanded="true"] .band-num {
  border-color: var(--mark); box-shadow: 0 0 0 2px color-mix(in srgb, var(--mark) 30%, transparent);
}

/* Das Band nimmt beim Filtern nichts weg, es dämpft – warum, steht in event-band.php.
   Den Regler --band-fremd setzt das erzeugte CSS der Liste, je Auswahl eine Regel.
   Unter dem Zeiger und im Tastaturfokus gilt er nicht: Wer einen gedämpften Tag
   ansteuert, will ihn lesen können, und ein Fokusring an etwas kaum Sichtbarem wäre
   für die Tastaturbedienung nur ein Rätsel.

   Der Eintrag setzt den Regler zuerst auf 0 zurück: Sonst erbte er ihn vom gedämpften
   Tag darüber und dämpfte ein zweites Mal – 0,28 mal 0,28 ist nicht zurückhaltend,
   sondern weg. Gedämpft wird ein Eintrag nur durch die eigene Regel, die das erzeugte
   CSS innerhalb eines passenden Tages setzt. */
.band-day, .band-entry { opacity: calc(1 - var(--band-fremd, 0) * .72); }
.band-entry { --band-fremd: 0; }
.band-day:hover, .band-day:focus-visible { opacity: 1; }

/* Die Karte eines Tages. Sie steht unter der Monatszeile, in der ihr Tag liegt, und
   zeigt dieselben Zeilen wie die Liste – mit Uhrzeit, Liga und Ort, also genau dem,
   was im Feld keinen Platz hat. Geöffnet ist immer höchstens eine.

   Sie ist keine Blase über dem Band, sondern eine Zeile darin: Eine Blase deckte das
   Raster zu, in dem man gerade liest, und auf dem Telefon deckte sie es ganz zu. */
.band-card { position: relative; margin: .1rem 0 .4rem; }
.band-card[hidden] { display: none; }
.band-card .event-list {
  border-color: color-mix(in srgb, var(--mark) 40%, var(--border));
  background: var(--bg);
}
/* Platz für das Kreuz, damit es keine Zeile überdeckt. */
.band-card .event-item { padding-right: 2.8rem; }
.band-close {
  position: absolute; top: .5rem; right: .5rem;
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.7rem; height: 1.7rem;
  background: var(--bg-soft);
  border: 1px solid var(--border); border-radius: 50%;
  color: var(--text-muted); text-decoration: none;
  font-size: 1.05rem; line-height: 1;
}
.band-close:hover { color: var(--accent); border-color: var(--accent); }

/* Die Zahlen erklären sich selbst, Füllung und Umriss nicht – anders als in der Liste,
   wo „Heimspiel“ als Wort dasteht. Deshalb hier eine Legende und dort keine. */
.band-legend {
  display: flex; flex-wrap: wrap; gap: .2rem .9rem;
  margin: .5rem 0 0; font-size: .8rem; color: var(--text-muted);
}
.band-key { display: inline-flex; align-items: center; gap: .35rem; }
.band-key::before {
  content: ""; width: .85rem; height: .85rem; flex: none;
  border: 1px solid var(--border); border-radius: 4px;
}
/* Der Hinweis steht am anderen Ende derselben Zeile – er gehört zum Band, aber nicht zur
   Legende. Wird die Zeile zu eng, rutscht er von allein darunter. */
.band-tip { margin-left: auto; }

/* Der Umschalter zeigt eines von beidem; im Quelltext steht immer beides. Das kostet
   nichts und hält den Ausdruck bei der Liste, egal was am Bildschirm gerade zu sehen ist.
   Deshalb steht das hier ausdrücklich unter @media screen – die einzige Stelle der Datei,
   die das nötig hat. */
@media screen {
  .events[data-view="liste"] .eventband { display: none; }
  .events[data-view="saison"] .event-group { display: none; }
  /* Der Satz „Keine Termine …“ gehört zur Liste. Im Band bleibt bei jeder Auswahl etwas
     stehen, nur blasser, und ein Hinweis auf Leere wäre schlicht falsch. Das !important
     ist die Ausnahme, die die Regel bestätigt: Die Regel, die ihn einschaltet, wird je
     Auswahl erzeugt und wiegt mit ihrem :has() schwerer als alles, was hier stehen kann. */
  .events[data-view="saison"] .event-empty { display: none !important; }
}

/* Erscheint nur, wenn eine Auswahl keine Termine übrig lässt – dann steht dort
   ein Satz statt einer leeren Fläche. Das erzeugte CSS der Terminliste schaltet ihn ein. */
.event-empty { display: none; color: var(--text-muted); }

/* Der Abstand zwischen zwei Monaten hängt am Monat selbst, nicht an seinem Vorgänger.
   Sonst hinterließe ein leer gefilterter Monat eine Lücke, wo nichts mehr steht. */
.event-group { margin-bottom: 2.4rem; }

.event-month {
  margin: 0 0 .6rem;
  font-size: .78rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-muted);
}

.event-list {
  list-style: none; margin: 0; padding: 0;
  background: var(--bg-soft);
  border: 1px solid var(--border); border-radius: var(--radius);
  overflow: hidden;
}
.event-item {
  display: grid; grid-template-columns: 3.4rem 1fr;
  gap: 0 .9rem; margin: 0; padding: .7rem .9rem;
  border-bottom: 1px solid var(--border);
}

.event-date {
  align-self: start; text-align: center;
  padding: .3rem .2rem; border-radius: var(--radius-sm);
  background: var(--bg); border: 1px solid var(--border);
  color: var(--text-muted); line-height: 1.2;
}
/* Grün heißt: bei uns im Vereinsheim. Alles andere bleibt ungefärbt – so hebt sich
   der eine Fall ab, statt dass zwei Farben gegeneinander stehen und beide erklärt
   werden müssten. Was gespielt wird, steht ohnehin daneben. */
.event-item.is-home .event-date {
  background: color-mix(in srgb, var(--event-home) 14%, var(--bg));
  border-color: color-mix(in srgb, var(--event-home) 35%, var(--bg));
  color: var(--event-home);
}
/* „Heute“ ist ein Ort in der Zeit, keine Wertung und kein Link – dieselbe neutrale
   Markierung wie die aktuelle Seite in der Navigation. Sie sticht durch Schwärze
   hervor und nimmt dem Grün daneben nichts weg. */
.event-item.is-today .event-date {
  background: var(--mark); border-color: var(--mark); color: var(--mark-text);
}

.event-day {
  display: block; font-size: 1.15rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.event-weekday {
  display: block; font-size: .68rem;
  letter-spacing: .04em; text-transform: uppercase;
}
/* „bis 11.“ steht dort, wo sonst das Wochentagskürzel steht – als Satz gelesen,
   nicht als Kürzel, deshalb ohne Versalien. */
.event-weekday-span { text-transform: none; letter-spacing: 0; }
.event-time {
  display: block; margin-top: .2rem; padding-top: .2rem;
  border-top: 1px solid currentColor;
  font-size: .7rem; font-weight: 600; font-variant-numeric: tabular-nums;
  opacity: .85;
}

.event-body { align-self: center; min-width: 0; }
.event-title { display: block; font-weight: 600; overflow-wrap: anywhere; }
/* „nur für Mitglieder“. Eine eigene Zeile und kein Anhängsel am Titel: Der Titel darf
   umbrechen, und der Hinweis soll nicht in seinem letzten Wort hängen bleiben. Gedeckt
   gezeichnet, weil er eine Eigenschaft nennt und keine Warnung ist. */
.event-intern {
  display: inline-block; margin-top: .15rem; padding: 0 .45rem;
  border: 1px solid var(--border); border-radius: 999px;
  font-size: .78rem; color: var(--text-muted);
}
.event-meta { display: block; font-size: .88rem; color: var(--text-muted); }
/* Der Trennpunkt steht im Stylesheet, nicht im Markup: im Ausdruck fällt „Heimspiel“
   weg, und ein Punkt im Quelltext bliebe dann führungslos am Zeilenanfang stehen.
   Das Leerzeichen dahinter muss ein geschütztes sein (\00a0): Ein gewöhnliches würde
   von der Hex-Maskierung davor aufgefressen – in CSS beendet ein Leerzeichen die
   Escape-Sequenz, statt selbst zu zählen. Geschützt ist es ohnehin richtig, sonst
   könnte der Punkt am Zeilenende allein zurückbleiben. */
.event-side + .event-info::before { content: "\00b7\00a0"; }

/* Der Beschreibungstext eines Vereinstermins. Nur Vereinstermine haben einen – die
   Liga- und Verbandstermine bleiben einzeilig, und wo nichts aufzuklappen ist, steht
   auch kein Aufklapper. Deshalb sitzt die Marke im Markup und nicht in jeder Zeile. */
.event-note { margin-top: .2rem; font-size: .88rem; }
.event-note > summary {
  display: inline-flex; align-items: center; gap: .25rem;
  cursor: pointer; color: var(--link); font-weight: 500;
}
.event-note > summary::marker, .event-note > summary::-webkit-details-marker { content: none; display: none; }
/* Das Dreieck wird selbst gezeichnet, damit es in beide Themen passt und sich dreht.
   Der Pfeil zeigt nach rechts und beim Aufklappen nach unten – dieselbe Geste wie
   überall sonst auf der Seite. */
.event-note > summary::before {
  content: ""; width: .42rem; height: .42rem;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(-45deg); transition: transform .15s ease;
}
.event-note[open] > summary::before { transform: rotate(45deg); }
.event-note-text { margin-top: .3rem; color: var(--text-muted); }
.event-note-text > :first-child { margin-top: 0; }
.event-note-text > :last-child { margin-bottom: 0; }

/* In der Tageskarte ist der Text schon die Antwort auf einen Klick. Ein zweiter Klick
   für zwei Sätze wäre einer zu viel, also steht er dort offen und ohne Aufklapper. */
.band-card .event-note > summary { display: none; }
.band-card .event-note-text { margin-top: .2rem; }

/* Kurzfassungen, die es nur auf Papier gibt: Wochentag und Datum als Zeile statt als
   Kalenderblatt, H oder A statt des ganzen Wortes, die Liga als Kürzel und der Ort für
   sich. Dazu der Kopf und der Fuß des Blattes. Siehe den Druckblock am Ende. */
.event-pwd, .event-pdate, .event-pside, .event-pliga, .event-port,
.printhead, .printfoot { display: none; }

/* Eine Paarung ist kein Titel, sondern zwei Namen. Sie steht deshalb normal, und nur
   der eigene Verein ist hervorgehoben – damit beim Überfliegen sofort dasteht, auf
   welcher Seite wir spielen, ohne dass ein Wort dafür nötig wäre. */
.event-pairing { font-weight: 400; }
.event-pairing .is-us { font-weight: 700; }
.event-vs { color: var(--text-muted); }

/* „Heimspiel“ hebt sich vom Rest der Zeile ab, „Auswärtsspiel“ nicht: das eine ist
   die Ausnahme, das andere der Normalfall. */
.event-side { font-weight: 600; }
.event-item.is-home .event-side { color: var(--event-home); }

/* Der Spielplan der Mannschaftsseite benutzt dieselben Zeilen wie der Kalender. Neu ist
   nur die dritte Spalte für das Ergebnis; bei einer noch nicht gespielten Partie fehlt
   sie und die Zeile bleibt zweispaltig. */
.fixtures .event-item { grid-template-columns: 3.4rem 1fr auto; }
.fixtures .event-group { margin-bottom: 1.4rem; }

.event-result {
  align-self: center;
  font-size: 1.05rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* Nur das Ergebnis ist eingefärbt, nicht die ganze Zeile – eine Liste aus roten und
   grünen Balken liest sich wie eine Warnung statt wie ein Spielplan. */
.event-item.is-sieg .event-result { color: var(--green); }
.event-item.is-niederlage .event-result { color: var(--red); }
.event-item.is-remis .event-result { color: var(--text-muted); }

/* Flache Schaltfläche für alles, was neben der Liste steht: „Weitere Spiele anzeigen“,
   „Kalender abonnieren“, „Liste drucken“. Sie sieht aus wie die Filterknöpfe des
   Kalenders, weil sie zur selben Sorte gehört – Nebenaktionen zur Liste, nicht der
   Hauptweg der Seite. */
.pill {
  display: inline-block; cursor: pointer;
  padding: .4rem .9rem;
  background: var(--bg-soft);
  border: 1px solid var(--border); border-radius: 999px;
  font: inherit; font-size: .92rem; font-weight: 600; color: var(--accent);
}
.pill:hover { background: var(--bg); }
/* Das Dreieck sagt, in welche Richtung es geht – ohne es stünde beim aufgeklappten
   Kasten weiter „Weitere Spiele anzeigen“. */
details > summary.pill::after { content: " \25be"; }
details[open] > summary.pill::after { content: " \25b4"; }

/* Dieselbe Schaltfläche, nur rund und wortlos. Quadratisch statt oval, weil ein Symbol
   in einer Kapsel schwimmt; 2,4 rem ist dasselbe Maß wie beim Theme-Umschalter oben im
   Kopf – und über der von Apple wie Google genannten Untergrenze für Tippziele. */
.pill-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.8rem; height: 2.8rem; padding: 0;
}
.icon {
  width: 1.25rem; height: 1.25rem;
  fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
/* Beim wortlosen Aufklapper wäre das Dreieck ein zweites Symbol neben dem ersten. Dass
   er offen ist, sagt stattdessen die Füllung – dieselbe Sprache wie „hier bist du“ bei
   Filter und Ansicht. */
details > summary.pill-icon::after { content: none; }
details[open] > summary.pill-icon {
  background: var(--mark); color: var(--mark-text); border-color: var(--mark);
}

/* Der Haken liegt im selben Knopf wie der Teilen-Graph und tauscht ihn für anderthalb
   Sekunden aus. Ein Text daneben müsste Platz bekommen, den es nicht gibt, und würde
   die Leiste beim Drücken verrücken – genau der Fehler, der beim alten Abo-Knopf schon
   einmal gemessen wurde. */
.share-button .icon + .icon,
.share-button.is-done .icon:first-child { display: none; }
.share-button.is-done .icon + .icon { display: block; }
.share-button.is-done { color: var(--green); border-color: var(--green); }

/* Der Aufklapper trennt die nächsten Partien vom Rest der Saison. */
.fixtures summary { margin-bottom: 1.4rem; }

/* Der Abo-Kasten hängt unter der Werkzeugleiste statt unter seinem Knopf. Der Knopf
   wandert je nach Fensterbreite in die zweite Zeile; der Kasten bliebe dann irgendwo in
   der Mitte hängen oder liefe rechts über den Rand. An der Leiste festgemacht, steht er
   immer innerhalb der Seitenbreite.

   Aufgeklappt liegt er über der Liste, statt sie nach unten zu schieben. Was man beim
   Abonnieren liest, ist ein Absatz lang; die Seite darunter dafür umzubauen, hieße den
   Blick vom Band wegzureißen, das man gerade angesehen hat. */
.subscribe-body {
  position: absolute; z-index: 5;
  left: 0; right: 0; top: calc(100% + .4rem);
  padding: 1rem 1.1rem;
  background: var(--bg-soft);
  border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow);
}
@media (min-width: 58rem) {
  /* Auf dem Schreibtisch so breit, wie ein Absatz gelesen werden will – und am rechten
     Rand, wo auch der Knopf steht, der ihn geöffnet hat. */
  .subscribe-body { left: auto; width: 34rem; }
}
.subscribe-body p { margin: 0 0 .8rem; }
.subscribe-body p:last-child { margin-bottom: 0; }
/* Angeboten wird nur die Auswahl, die gerade gilt; welche das ist, schaltet das erzeugte
   CSS der Terminliste ein. Ohne diese Regel stünden alle untereinander. */
.abo { display: none; }
.subscribe-pick { font-weight: 600; }
/* Die Adresse ist zum Abschreiben da und darf dafür umbrechen, wo es nötig ist. */
.subscribe-body code { overflow-wrap: anywhere; }

/* Ohne JavaScript lässt sich kein Druckdialog öffnen und nichts in die Zwischenablage
   schreiben – dann blieben hier Schaltflächen stehen, die nichts tun. Gedruckt werden
   kann die Seite trotzdem, und die Adresse steht ohnehin in der Adresszeile. */
.print-button, .share-button { display: none; }
.js .print-button, .js .share-button { display: inline-flex; }

/* ---------- Formulare ---------- */

.form { max-width: 34rem; }
/* Der Editor braucht die volle Breite, sonst bricht der Text anders um als auf der Seite. */
.form-wide { max-width: none; }
.field { margin-bottom: 1.1rem; }
.field label { display: block; margin-bottom: .3rem; font-weight: 600; }
.field input, .field textarea, .bild-alt input {
  width: 100%; padding: .65rem .8rem;
  font: inherit; color: var(--text);
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.field input:focus, .field textarea:focus, .bild-alt input:focus { border-color: var(--accent); }
.field textarea { min-height: 9rem; resize: vertical; }
/* Das Sternchen am Pflichtfeld ist ein Hinweis, keine Handlung und keine Warnung –
   es soll auffindbar sein, wenn man es sucht, und sonst still bleiben. */
.req { color: var(--text-muted); }

/* Eine Beitragsstufe. Der Rahmen hält die drei Angaben zusammen, die zusammengehören –
   ohne ihn wären fünf Eingabefelder hintereinander nicht mehr auseinanderzuhalten. */
.stufe {
  margin: 0 0 1.4rem; padding: 1rem 1.1rem .3rem;
  border: 1px solid var(--border); border-radius: var(--radius);
}
.stufe legend { padding: 0 .4rem; font-weight: 600; color: var(--text-muted); }

/* Honeypot: für Menschen unsichtbar, für Bots ein verlockendes Feld. */
.hp {
  position: absolute !important; left: -9999px !important;
  width: 1px; height: 1px; overflow: hidden;
}

/* `color` gehört hierher und nicht zum Erben: Die Meldung bringt ihren eigenen Grund mit,
   also muss sie auch die eigene Schrift bestimmen. Stünde sie in einer eingefärbten Fläche,
   erbte sie sonst deren Textfarbe. Das war bis 2026-09-13 kein gedachter Fall: In der damaligen
   Bearbeitungsleiste (Grund `--mark`, Schrift `--mark-text`) ergab das gemessene 1,12:1 im
   dunklen und 1,20:1 im hellen Modus, verlangt sind 4,5:1. Die Leiste gibt es nicht mehr,
   die Regel bleibt. */
.notice {
  padding: .9rem 1.1rem; border-radius: var(--radius); margin-bottom: 1.4rem;
  background: var(--bg-soft); color: var(--text); border: 1px solid var(--border);
}
.notice p { margin: 0; }
/* Gelungen ist grün, misslungen rot – Rückmeldung, nicht Akzent. Der Grünton kommt
   aus der Variablen, damit die Meldung im dunklen Modus mitzieht. */
.notice-ok  { background: color-mix(in srgb, var(--green) 12%, var(--bg)); border-color: var(--green); }
.notice-err { background: color-mix(in srgb, var(--red) 12%, var(--bg)); border-color: var(--red); }

/* Die Regeln für die Verwaltungsleiste standen hier bis 2026-09-13. Der Streifen über dem
   Kopf ist ersatzlos gestrichen; was darin stand, liegt im Benutzermenü (weiter oben beim
   Menü hinter dem ☰) und die Meldung am Kopf von <main>. */

/* ---------- Block an Ort und Stelle bearbeiten ---------- */

/* Der Block steht im Fluss der Seite, sieht aber sichtbar anders aus als sie: gestrichelt
   und leicht abgesetzt. So bleibt erkennbar, was hier Inhalt ist und was Werkzeug. */
.block-edit {
  margin: 0; padding: .9rem 1rem 1rem;
  border: 1px dashed var(--mark); border-radius: var(--radius);
  background: var(--bg-soft);
}
.block-edit-kopf {
  display: flex; flex-wrap: wrap; align-items: center; gap: .2rem .5rem;
  margin: 0 0 .6rem; font-size: .9rem; color: var(--text-muted);
}
.block-edit-fuss { display: flex; flex-wrap: wrap; gap: .5rem; margin: .8rem 0 0; }
/* Inline sind die Blöcke kurz; die 14rem der Maske ließen jeden Absatz im Leeren stehen. */
.block-edit .editor { min-height: 6rem; }

/* Der Name sieht aus wie die Überschrift, die er ersetzt, und wird erst beim Anfassen zum
   Feld. Ein Eingabekasten in voller Breite über jedem Block wäre der auffälligste Teil der
   Seite – und er ist der Teil, den man am seltensten anrührt. */
.block-edit-name { flex: 1 1 12rem; }
.block-edit-name input {
  width: 100%; padding: .2rem .35rem; margin: -.2rem -.35rem;
  border: 1px solid transparent; border-radius: 4px;
  background: none; color: inherit; font: inherit; font-weight: 700;
}
.block-edit-name input:hover { border-color: var(--border); }
.block-edit-name input:focus { border-color: var(--mark); background: var(--bg); }

/* ---------- Die Blockfolge einer Seite ---------- */

/* Jede Handlung an einem Block ist ein POST mit Weiterleitung; der Server schickt einen Anker
   mit, damit der Block danach wieder im Bild ist. Ein Anker setzt sein Ziel aber an den oberen
   Fensterrand – und dort sitzt `.site-kopf` mit `position: fixed`, also läge der Block
   darunter.

   Deshalb die Kopfhöhe plus eine halbe Zeile Luft, und nicht eine feste Zahl: Wer die
   Schrift vergrößert, vergrößert damit auch --kopf-h, und der Abstand wächst mit.

   Das ist der Weg ohne JavaScript. Mit JavaScript übernimmt assets/js/block-pos.js und hält
   den Block genau dort, wo er vorher stand, statt ihn nach oben zu holen. */
.block-slot,
.block-region { scroll-margin-top: calc(var(--kopf-h) + .7rem); }

.block-slot { margin: 0 0 var(--raum-l); }

/* Ein Bauteil lässt sich nicht bearbeiten, nur verschieben. Der Rahmen sagt das: dieselbe
   gestrichelte Linie wie beim Textblock, aber in Grau und ohne den abgesetzten Untergrund,
   der zum Hineinschreiben einlädt. */
.block-bauteil {
  padding: .9rem 1rem; border: 1px dashed var(--border); border-radius: var(--radius);
}
.block-bauteil > :first-child { margin-top: 0; }
.block-bauteil > :last-child { margin-bottom: 0; }

.block-werkzeug {
  display: flex; flex-wrap: wrap; align-items: center; gap: .3rem;
  margin: 0 0 .4rem; font-size: .9rem; color: var(--text-muted);
}
.block-werkzeug-name { font-weight: 700; }
/* Die Knöpfe stehen rechts und bleiben als Gruppe zusammen. Steht links kein Name, schiebt
   sie der Rand dorthin; ist er so lang, dass die Zeile nicht reicht, wandern sie geschlossen
   in die nächste, statt sich am Umbruch auseinanderreißen zu lassen. */
.block-werkzeug-knoepfe {
  display: flex; flex-wrap: wrap; align-items: center; gap: inherit; margin-left: auto;
}
.block-werkzeug button { padding: .2rem .55rem; line-height: 1.2; }
.block-werkzeug button[disabled] { opacity: .4; cursor: default; }
.block-auge { display: inline-flex; align-items: center; justify-content: center; }
/* Genau eine Zeile hoch, damit das Auge auf derselben Linie sitzt wie die Pfeile daneben. */
.block-auge svg { display: block; height: 1.2em; width: auto; }

/* Die Zielgruppe trägt ein Wort statt eines Sinnbilds und ist damit der breiteste Knopf der
   Leiste. Kleiner gesetzt hält sie sich im Regelfall „alle“ zurück; weicht sie davon ab, ist
   das eine Auskunft über die Seite und darf auffallen. */
.block-zielgruppe { font-size: .8em; }
.block-zielgruppe.ist-eng { border-color: var(--accent); color: var(--accent); font-weight: 700; }

/* Vorbereiten am Schreibtisch, scharf schalten unterwegs – das Auge ist der Knopf, der auf
   dem Telefon getroffen werden muss. Gut 24 px trifft der Daumen nicht zuverlässig, und ein
   Fehlgriff landet auf dem × daneben. Wo mit dem Finger gezeigt wird, werden daraus die
   üblichen 44 px; am Zeiger bleibt die Leiste schmal. */
@media (pointer: coarse) {
  .block-werkzeug button { min-width: 2.75rem; min-height: 2.75rem; }
  .block-werkzeug { gap: .5rem; }
}
/* Ein verborgener Block bleibt in voller Größe stehen – man soll ihn lesen und weiter daran
   arbeiten können. Blass genug, um ihn auf einen Blick von den sichtbaren zu unterscheiden,
   und wieder klar, sobald man ihn anfasst.

   Die Blässe liegt auf dem Inhalt und nicht auf dem Schlitz: opacity auf dem Elternteil
   ließe sich am Kind nicht zurücknehmen, und die Werkzeugleiste mit dem Auge, das den Block
   zurückholt, soll lesbar bleiben. */
.ist-verborgen .block-edit,
.ist-verborgen .block-bild,
.ist-verborgen .block-bauteil { opacity: .5; }
.ist-verborgen:hover .block-edit,
.ist-verborgen:hover .block-bild,
.ist-verborgen:hover .block-bauteil,
.ist-verborgen:focus-within .block-edit,
.ist-verborgen:focus-within .block-bild,
.ist-verborgen:focus-within .block-bauteil { opacity: 1; }

/* Der Bild-Block im Bearbeitungsmodus.

   Kein Rahmen um das Bild herum – anders als beim Bauteil, wo die gestrichelte Linie sagt
   „hieran ist nichts zu machen“. Hier ist etwas zu machen, und das Bild soll dabei genau so
   aussehen wie später auf der Seite; ein Kasten darum verfälschte die einzige Frage, die im
   Bearbeitungsmodus zählt: Passt es an diese Stelle? */
.block-bild > figure { margin-top: 0; }
.block-bild-alt { margin: .4rem 0 0; }
.block-bild-alt q { font-style: italic; }
.block-bild-alt a { margin-left: .4rem; }

/* Ein Block ohne Bild ist der einzige leere Platz, den es hier gibt – und er steht nur im
   Bearbeitungsmodus. Die gestrichelte Linie zeigt, wie viel Seite er einnehmen wird. */
.block-bild-leer {
  margin: 0; padding: 1.4rem 1rem; text-align: center;
  border: 1px dashed var(--border); border-radius: var(--radius);
}

/* Zugeklappt: eine Zeile. Zwei Dutzend Vorschaubilder unter jedem Bild-Block hießen, dass
   von der Seite nichts mehr zu sehen ist – und zu beurteilen ist hier die Seite. */
.block-bild-wahl { margin-top: .6rem; font-size: .9rem; }
.block-bild-wahl > summary {
  cursor: pointer; padding: .2rem 0; color: var(--text-muted); font-weight: 600;
}
.block-bild-wahl > summary:hover { color: var(--accent); }
.block-bild-wahl > form {
  margin-top: .6rem; padding: .9rem 1rem;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-soft);
}
.block-bild-liste {
  list-style: none; padding: 0; margin: 0 0 .9rem;
  display: grid; gap: .6rem;
  grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
}
/* Der Radioknopf selbst ist weg, nicht bloß versteckt: Getippt wird auf die Kachel, und die
   ist ein <label>. `position: absolute` statt `display: none`, damit die Tastatur ihn noch
   erreicht und Vorlesegeräte die Auswahl weiterhin als Auswahl ansagen. */
.block-bild-liste input {
  position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;
}
.block-bild-liste label {
  display: block; padding: .4rem; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg);
}
.block-bild-liste label:hover { border-color: var(--accent); }
/* Das gewählte Bild trägt denselben Ring wie ein Feld im Fokus – zwei Pixel, damit die
   Markierung auch dann zu sehen ist, wenn die Kachel am Rand des Rasters sitzt. */
.block-bild-liste input:checked + label {
  border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent);
}
.block-bild-liste input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 2px; }
.block-bild-liste img {
  display: block; width: 100%; height: 5rem; object-fit: cover; border-radius: var(--radius);
}
.block-bild-liste span {
  display: block; margin-top: .3rem; font-size: .78rem; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Zugeklappt ein schmaler Strich mit einem Pluszeichen: Zwischen acht Blöcken liegen neun
   Einfügestellen, und neun Kästen wären mehr Werkzeug als Seite. Sichtbar wird die Stelle
   beim Darüberfahren und sobald sie offen ist. */
.block-add {
  margin: 0 0 1.8rem; border-top: 1px dashed var(--border);
  font-size: .88rem; color: var(--text-muted);
}
.block-add summary {
  display: flex; align-items: center; gap: .4rem;
  padding: .2rem 0; cursor: pointer; list-style: none; opacity: .45;
}
.block-add summary::-webkit-details-marker { display: none; }
.block-add:hover summary,
.block-add[open] summary,
.block-add summary:focus-visible { opacity: 1; }
.block-add-zeichen {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.4rem; height: 1.4rem; margin-top: -1rem;
  border: 1px solid var(--border); border-radius: 50%;
  background: var(--bg); line-height: 1;
}
.block-add[open] .block-add-zeichen { border-color: var(--mark); color: var(--mark); }
.block-add-gruppe {
  display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin: .5rem 0;
}
.block-add-titel { min-width: 7rem; }
.block-add-gruppe button { padding: .25rem .7rem; font-size: .88rem; }

/* ---------- Mitgliederbereich ---------- */

/* Die `.member-bar` stand hier bis 2026-09-13: ein Kasten am Kopf von /intern/ mit
   „Angemeldet als …“ und einem Abmeldeknopf. Beides steht jetzt im Benutzermenü hinter dem
   Personen-Symbol und damit auf jeder Seite. Zweimal dasselbe auf einer Seite ist einmal zu
   viel, und der Kasten schob die Überschrift um 1,8 rem nach unten. Siehe user-menu.php. */

.button-quiet {
  padding: .35rem .85rem;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  font: inherit; font-size: .92rem; font-weight: 600; cursor: pointer;
}
.button-quiet:hover { border-color: var(--accent); color: var(--accent); }

.doc-list { list-style: none; padding: 0; margin: 0 0 1.8rem; }
.doc-list li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .8rem;
  padding: .7rem 0; margin: 0; border-bottom: 1px solid var(--border);
}
.doc-list li:last-child { border-bottom: 0; }
.doc-list a { font-weight: 600; }
/* Die Angaben brechen auf schmalen Displays in eine eigene Zeile um. */
.doc-meta { flex: 1 1 100%; font-size: .88rem; color: var(--text-muted); }
.doc-list form { margin: 0; }
/* Vergangenes wird nicht versteckt – man braucht es, um das nächste Jahr abzuschreiben –,
   aber es soll den Blick nicht von dem abziehen, was noch kommt. */
.doc-list li.ist-vorbei { opacity: .6; }
/* Eine fehlende Pflichtangabe ist kein Randvermerk: Der Balken macht die Zeile in der
   Liste auffindbar, ohne sie in eine zweite Meldung zu verwandeln. */
.doc-list li.ist-unvollstaendig {
  padding-left: .8rem;
  border-left: 3px solid var(--red);
}
.doc-list li.ist-unvollstaendig .doc-meta { color: var(--red); }

@media (min-width: 46em) {
  .doc-meta { flex: 1 1 auto; }
}

/* Der Aufklapper über dem Upload-Formular. Er sieht nicht aus wie die Schaltfläche, die er
   verbirgt: Aufklappen ist keine Handlung an den Dokumenten, sondern das Aufschlagen einer
   Werkbank. Deshalb die leise Fassung – dasselbe Dreieck wie beim Terminvermerk, nur etwas
   kräftiger gesetzt, weil hier ein Formular dahinter steht und nicht zwei Sätze. */
.doc-upload > summary {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .5rem .7rem; margin-left: -.7rem;
  border-radius: var(--radius-sm);
  cursor: pointer; color: var(--text-muted); font-weight: 600;
}
.doc-upload > summary::marker, .doc-upload > summary::-webkit-details-marker { content: none; display: none; }
.doc-upload > summary::before {
  content: ""; width: .42rem; height: .42rem;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(-45deg); transition: transform .15s ease;
}
.doc-upload[open] > summary::before { transform: rotate(45deg); }
.doc-upload > summary:hover { color: var(--accent); background: var(--bg-soft); }
/* Aufgeklappt ist es die Überschrift des Formulars und darf nicht mehr zurückgenommen sein
   als das, was es überschreibt. */
.doc-upload[open] > summary { color: var(--text); }
.doc-upload[open] { margin-top: 1rem; }
.doc-upload .notice { margin-top: .8rem; }

.hint { margin: .4rem 0 0; font-size: .88rem; color: var(--text-muted); }

/* Der Übersetzungshinweis ist ein .hint wie die anderen, steht aber oft direkt unter einem –
   der Erklärung der Marken. Zwei gleich blasse Zeilen übereinander liest niemand als zwei
   Dinge. Der Strich links trennt sie, ohne den Hinweis zur Warnung aufzublasen: Es ist eine
   Frage an den Verfasser, kein Fehler. */
.block-trans { border-left: 2px solid var(--mark); padding-left: .6rem; }

/* Ein Hinweis, der eine Folge hat – etwa die persönliche Abo-Adresse, die man nicht
   weitergeben soll. Dieselbe Größe wie die anderen Hinweise, aber in der Textfarbe und
   mit Strich: Blass hätte ihn genau der überlesen, den er angeht. Kein Rot, denn es ist
   nichts schiefgegangen; gewarnt wird vor etwas, das man selbst noch tun könnte. */
.hint-warnung {
  border-left: 2px solid var(--mark); padding-left: .6rem; color: var(--text);
}

/* ---------- Verwaltung ---------- */

.admin-nav { margin-bottom: 1.6rem; }
.admin-nav ul { list-style: none; display: flex; flex-wrap: wrap; gap: .4rem; padding: 0; margin: 0; }
.admin-nav a {
  display: inline-block; padding: .4rem .9rem;
  border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--text-muted); text-decoration: none; font-weight: 600; font-size: .95rem;
}
.admin-nav a:hover { border-color: var(--accent); color: var(--accent); }
.admin-nav a[aria-current="page"] { background: var(--mark); color: var(--mark-text); border-color: var(--mark); }

.tag {
  display: inline-block; padding: 0 .45rem; margin-left: .2rem;
  border: 1px solid var(--border); border-radius: 999px;
  font-size: .8rem; color: var(--text-muted);
}

.editor-bar { display: flex; flex-wrap: wrap; gap: .3rem; margin-bottom: .6rem; }
.editor-bar button { min-width: 2.6rem; }

.editor {
  padding: 1rem 1.1rem; min-height: 14rem;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg);
}
.editor > :first-child { margin-top: 0; }
.editor > :last-child { margin-bottom: 0; }
/* Im Editor bekommen leere Spalten einen Rahmen, sonst wären sie nicht anklickbar. */
.editor .col { outline: 1px dashed var(--border); outline-offset: .3rem; min-height: 2.5rem; }
.editor img { border-radius: var(--radius); }

/* Bildauswahl im Editor */
.bild-auswahl {
  margin-top: .8rem; padding: .9rem 1rem;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-soft);
}
.bild-auswahl[hidden] { display: none; }
.bild-auswahl ul {
  list-style: none; padding: 0; margin: .6rem 0;
  display: grid; gap: .6rem;
  grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
}
.bild-wahl {
  display: block; width: 100%; padding: .4rem; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg); color: var(--text); font: inherit; text-align: left;
}
.bild-wahl:hover, .bild-wahl:focus-visible { border-color: var(--accent); }
.bild-wahl img { display: block; width: 100%; height: 5rem; object-fit: cover; border-radius: var(--radius); }
.bild-wahl span {
  display: block; margin-top: .3rem; font-size: .78rem; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Bildverwaltung */
.bild-liste { list-style: none; padding: 0; display: grid; gap: 1rem; }
.bild-liste li {
  display: grid; gap: 1rem; align-items: start;
  padding: 1rem; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-soft);
}
.bild-liste > li > img {
  width: 100%; max-width: 12rem; height: auto; border-radius: var(--radius);
}
.bild-liste code { font-size: .82rem; word-break: break-all; }
.bild-liste .hint { margin-bottom: .5rem; }
.bild-alt { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .5rem; }
.bild-alt input { flex: 1 1 12rem; }
@media (min-width: 46em) {
  .bild-liste li { grid-template-columns: 12rem 1fr; }
}

/* Ankündigungen in der Verwaltung. „aktiv“ sagt, welche gerade läuft – ein Zustand,
   kein Erfolg; deshalb die neutrale Markierung und nicht das Grün von .tag-ok. */
.tag-aktiv { border-color: var(--mark); color: var(--mark); }
.tag-abgelaufen { opacity: .75; }

/* „Übersetzung veraltet“ – wie .tag-aktiv ein Zustand und kein Fehler, deshalb dieselbe
   Markierungsfarbe und nicht das Rot von .tag-fehler. Beide stehen nie nebeneinander. */
.tag-alt { border-color: var(--mark); color: var(--mark); }

/* „nur für Mitglieder“ in der Übersicht der Vereinstermine. Als einzige Marke gefüllt:
   Sie steht neben Marken, die nur etwas ergänzen, und beantwortet als einzige die Frage,
   wer den Termin überhaupt sieht. Wer hier nichts sieht, hält den Termin für öffentlich –
   und das soll dann auch stimmen. */
.tag-intern {
  border-color: var(--mark); background: var(--mark); color: var(--mark-text);
}

/* Liga-Import in der Verwaltung */
.admin-liga tbody th { font-weight: 600; }
/* Nur die Anmerkung bricht um. Der Rest bleibt einzeilig, damit die Tabelle auf dem
   Handy waagerecht scrollt, statt die Spalten zu Wortsäulen zusammenzudrücken. */
.admin-liga td:last-child {
  white-space: normal; min-width: 16rem; color: var(--text-muted);
}
.admin-liga .num { text-align: right; font-variant-numeric: tabular-nums; }
.tag-ok { border-color: var(--green); color: var(--green); }
.tag-fehler { border-color: var(--red); color: var(--red); }
.tag-offen, .tag-nie { opacity: .75; }

.announce-bilder { list-style: none; padding: 0; display: grid; gap: 1rem; }
.announce-bild {
  display: grid; gap: 1rem; align-items: start;
  padding: 1rem; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg-soft);
}
.announce-bild-vorschau img {
  width: 100%; max-width: 12rem; height: auto; border-radius: var(--radius);
}
.announce-bild .field { margin-bottom: .7rem; }
.field select {
  width: 100%; padding: .65rem .8rem;
  font: inherit; color: var(--text);
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius);
}
.field-schmal input, .field-schmal select { max-width: 14rem; }

/* Ein Ankreuzfeld steht neben seiner Beschriftung, nicht darunter, und die Beschriftung
   bleibt anklickbar – deshalb liegt das Feld im Label statt daneben. */
.field label.check {
  display: flex; align-items: center; gap: .55rem;
  font-weight: 400; cursor: pointer;
}
.field label.check input { width: auto; flex: none; accent-color: var(--accent); }

@media (min-width: 46em) {
  .announce-bild { grid-template-columns: 12rem 1fr; }
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* ---------- Fuß ---------- */

.site-footer {
  background: var(--bg-soft);
  border-top: 1px solid var(--border);
  padding: var(--raum-xl) 0;
  font-size: .95rem;
}
/* Drei gleichrangige Spalten: Adresse, Anfahrt, Links.

   Bis 2026-09-14 stand hier ein `auto-fit` mit gleich breiten Dritteln. Das ist an der neuen
   Anfahrt-Spalte gescheitert, und zwar messbar: Neben der 135 Punkt breiten Chipsäule blieben
   in einem 311er Drittel nur 164 für die Karte, und in die passt die Namensnennung nicht –
   sie braucht 159 Punkt, frei sind im Bild 143. Also bekommt die mittlere Spalte jetzt so viel
   Breite, wie ihr Inhalt braucht (`auto`), und die beiden Textspalten teilen sich den Rest.
   Adresse und Links stehen damit nicht mehr exakt gleich breit wie die Karte, aber gleich
   breit wie einander – und das ist die Symmetrie, die man im Fuß sieht.

   Drei Spalten erst ab 66em statt ab rund 700 Punkt: Dazwischen wären die Spalten zwar drei,
   aber jede zu schmal für Chips *und* Karte nebeneinander. Zwei Spalten mit Luft sind besser
   als drei, in denen die Karte zum Briefmarkenformat schrumpft. */
.footer-inner { display: grid; gap: var(--raum-l); }
@media (min-width: 46em) {
  .footer-inner { grid-template-columns: 1fr 1fr; }
}
@media (min-width: 66em) {
  .footer-inner { grid-template-columns: 1fr auto 1fr; }
}
.site-footer h2 { font-size: 1rem; margin: 0 0 .6rem; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); }
.site-footer p { margin: 0; }
.site-footer a { color: var(--text); }
.site-footer a:hover { color: var(--accent); }

/* Die Anfahrt-Spalte. Die Karte ist hier Wegweiser, nicht Schaustück, und ohne den Schatten,
   den Bilder im Inhalt tragen – auf der ohnehin abgesetzten Fußfläche schwebte sie sonst
   doppelt.

   Seit 2026-09-14 liegen Chips und Karte nebeneinander statt untereinander. Vorher stapelte
   die Spalte Überschrift, Karte und Chipzeile und maß damit 204 Punkt Inhalt, während Adresse
   105 und Links 91 brauchten – ein Raster streckt alle drei auf die höchste, also bestimmte
   die Karte allein die Fußhöhe. Nebeneinander bestimmt sie gar nichts mehr.

   Die Chipspalte gibt die Höhe vor, die Karte füllt sie. Das ist die Umkehrung von vorher
   und der ganze Trick: Die Höhe hängt jetzt an zwei Textzeilen und nicht mehr am Seiten-
   verhältnis eines Bildes. `auto` für die erste Spalte heißt „so breit wie der längere Chip“
   (gemessen 135 Punkt für OpenStreetMap), der Rest gehört der Karte.

   Die Karte überspannt beide Zeilen und beginnt damit nicht unter der Überschrift, sondern auf
   deren Höhe (Verein, 2026-09-14). Sonst blieben ihr nur die 4,6rem der Chipsäule, und von
   einem Stadtplan war ein zu flacher Streifen übrig – man sah die Straßen, aber nicht mehr,
   wie sie zueinander liegen. Die Fußhöhe kostet das nichts: Die Spalte war mit Überschrift
   plus Chips ohnehin schon so hoch, die Karte füllt jetzt nur den Platz, der schon da war. */
.footer-karte {
  min-width: 0;
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  column-gap: .8rem; align-items: stretch;
}
.footer-karte > h2      { grid-column: 1; grid-row: 1; }
.footer-karte > .map-links { grid-column: 1; grid-row: 2; }
.footer-karte > .map-figur { grid-column: 2; grid-row: 1 / span 2; }
/* Untereinander statt nebeneinander: Nebeneinander bräuchten die beiden Chips 256 Punkt und
   ließen der Karte in einer 311er Spalte nichts übrig. Übereinander bilden sie eine schmale,
   zwei Zeilen hohe Säule – genau das Maß, das die Karte daneben bekommen soll. */
.map-links {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: .4rem; margin: 0;
}
/* Das Bild schwebt: Der Kartenrahmen selbst ist inhaltsleer, das Blatt liegt absolut darin.
   Sonst brächte das Bild sein Seitenverhältnis in die Rechnung ein – 232 Punkt breit sind 129
   hoch, und die Anfahrt wäre wieder die höchste Spalte im Fuß, diesmal knapp statt deutlich.
   Ein fester Deckel in rem wäre nur bei einer Breite richtig und bei allen anderen falsch;
   inhaltsleer ist er bei jeder richtig, weil die Höhe dann aus Überschrift plus Chipsäule
   nebenan kommt und aus sonst nichts. */
/* `width` und nicht `max-width`: Die mittlere Fußspalte ist `auto`, sie richtet sich also nach
   dem, was ihr Inhalt zu brauchen angibt. Eine Höchstbreite gibt nichts an – das Bild meldete
   damit 128 Punkt und bekam auch nur die. Eine Wunschbreite meldet 232, und `max-width: 100%`
   erlaubt trotzdem, in zwei schmalen Spalten nachzugeben. */
.map-figur {
  position: relative; margin: 0;
  width: 14.5rem; max-width: 100%;
  min-height: 0;
}
.map-link { position: absolute; inset: 0; display: block; }
.map-link picture { display: block; height: 100%; }
/* `cover` beschneidet das Blatt oben und unten, statt es zu stauchen. Das ist unbedenklich,
   weil das Bild um das Vereinsheim herum erzeugt wurde: Was wegfällt, fällt symmetrisch weg,
   und der Punkt, auf den es ankommt, bleibt in der Mitte. */
.map-link img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  border-radius: var(--radius); box-shadow: none;
}

/* Die Namensnennung ist Lizenzpflicht (ODbL), aber keine Nachricht an den Leser. Sie sitzt
   deshalb in der Ecke des Bildes, wie auf jeder anderen Karte auch, statt als dritte Zeile
   darunter – das war die Zeile, die den Hinweis vom Bild getrennt und die Spalte verlängert
   hat. Feste Farben statt der Design-Variablen: Das Kartenbild ist in beiden Designs dasselbe
   helle Blatt, also muss die Schrift darauf in beiden dieselbe sein. */
.map-figur figcaption {
  position: absolute; right: .25rem; bottom: .25rem;
  padding: .05rem .4rem; border-radius: var(--radius-sm);
  background: rgb(255 255 255 / .84); color: #2c3a33;
  font-size: .7rem; line-height: 1.6;
}
.site-footer .map-figur figcaption a { color: inherit; }
.site-footer .map-figur figcaption a:hover { color: var(--green); }

/* Chips statt Textzeilen: Als bloße Zeilen lasen sich die beiden Routen wie eine Bildunter-
   schrift, nicht wie etwas zum Anklicken. Die Pillenform ist dieselbe wie bei den Filter-
   knöpfen des Kalenders. Blau, weil Blau im Haus „anklickbar“ heißt – der Fuß färbt seine
   Links sonst textfarben, und das ist hier zu leise. */
.site-footer .map-chip {
  display: inline-flex; align-items: center; gap: .1rem;
  padding: .25rem .7rem; border-radius: 999px;
  border: 1px solid var(--border); background: var(--bg);
  color: var(--accent); font-size: .85rem; font-weight: 600;
  text-decoration: none; white-space: nowrap;
}
.site-footer .map-chip:hover { border-color: var(--accent); background: var(--bg-soft); }
.map-chip .ext { font-size: .75em; vertical-align: super; }

/* ---------- Einstellungen im Fuß ---------- */

/* Design und Sprache, seit 2026-09-14 hier statt im Kopf (Verein, „E1“). Eine eigene Zeile
   *unter* dem Spaltenraster und keine vierte Spalte darin: Die drei Spalten sind Auskünfte
   über den Verein, das hier ist eine über die Anzeige. Und ein Raster streckt alle Spalten
   auf die höchste – zwei Knöpfe bekämen dieselbe Höhe wie die Anfahrtsskizze daneben.

   Die Trennlinie ist dieselbe wie die Oberkante des Fußes: ein Absatz, kein Bruch. Leise
   gesetzt, weil das hier die Sorte Sache ist, die man einmal im Jahr braucht. */
.footer-einstellungen {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: .4rem 1.4rem;
  /* Über der Linie eine Stufe mehr als darunter: Der Abstand nach oben trennt zwei Zeilen,
     der nach unten hält nur die Linie vom Text fern. */
  margin-top: var(--raum-l); padding-top: var(--raum-m);
  border-top: 1px solid var(--border);
  font-size: .88rem;
}

/* Der Design-Schalter zeigt, wohin er führt, nicht wo man ist: Im hellen Design steht
   „Dunkles Design“ darauf. Ein Schalter, der den Zustand anzeigt, in dem man ohnehin
   sichtbar steckt, sagt nichts – einer, der das Angebot nennt, sagt alles.

   Symbol *und* Wort. Das Symbol allein war oben in der Leiste genau das Problem: ☀ und ☾
   sehen aus wie zwei Zustände, aber man sieht ihnen nicht an, ob sie den jetzigen oder den
   nächsten meinen. Das Wort klärt das, das Symbol findet man schneller wieder. */
.fuss-schalter {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .3rem .8rem .3rem .6rem;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--bg); color: var(--text);
  font: inherit; font-size: .88rem; font-weight: 600;
  cursor: pointer;
}
.fuss-schalter:hover { border-color: var(--accent); color: var(--accent); }
/* Sichtbar ist immer das Wort für das *andere* Design – siehe oben. */
.theme-wort-hell { display: none; }
[data-theme="dark"] .theme-wort-hell { display: inline; }
[data-theme="dark"] .theme-wort-dunkel { display: none; }

/* Die Sprachen ausgeschrieben und nebeneinander, mit der aktuellen markiert. Oben in der
   Leiste stand nur „EN“ und schaltete sofort um; das war ein Knopf, der nicht sagte, wo
   man gerade ist. Unten ist der Platz für beides da. */
.fuss-sprachen { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; }
.fuss-sprachen-titel { color: var(--text-muted); }
.site-footer .fuss-sprachen a {
  padding: .2rem .55rem; border-radius: var(--radius-sm);
  text-decoration: none; font-weight: 600;
}
.site-footer .fuss-sprachen a:hover { color: var(--accent); background: var(--bg-soft); }
/* Die Sprache, in der man liest, ist keine Handlung – dieselbe neutrale Markierung wie bei
   der Seite, auf der man steht. */
.site-footer .fuss-sprachen a[aria-current] { color: var(--mark-text); background: var(--mark); }

/* ---------- Druck ---------- */

/* Der Ausdruck ist für den Aushang am Brett im Vereinsheim gedacht. Auf Papier wird
   nicht gescrollt und nicht geklickt: Was zählt, ist, wie viel auf ein Blatt geht und
   wie schnell man eine Zeile findet. Deshalb wird aus den Kalenderblättern eine
   Tabelle – je Termin genau eine Zeile, Spalten, die über alle Zeilen fluchten:

     Wochentag · Datum · Uhrzeit · H/A · Begegnung · Liga oder Ort · Ergebnis

   Eine eigene Druckseite braucht es dafür nicht. Das Markup bleibt dasselbe; nur
   lösen sich Kalenderblatt und Angabenblock mit display:contents auf, damit ihre Teile
   selbst zu Spalten der Zeile werden. Drei Kurzfassungen stehen schon im Quelltext und
   sind am Bildschirm ausgeblendet: .event-pwd, .event-pdate, .event-pside.

   Welche Auswahl gedruckt wird, entscheidet die Filterleiste: Was am Bildschirm steht,
   steht auch auf dem Blatt. Ohne Drucker prüfen lässt sich das, indem man die Regeln in
   ein <style> kopiert und document.body.style.width = '190mm' setzt – das ist die
   bedruckbare Breite bei diesen Rändern, und nur so fallen Umbrüche auf. */
@media print {
  @page { margin: 12mm 10mm; }

  /* Die Grundgröße des Blattes steht am <html>, nicht am <body>: Seitentitel und
     Monatsüberschrift sind in rem gesetzt, und nur so hängt das ganze Blatt an einem
     einzigen Regler. An dem dreht die Terminliste je Auswahl – eine gefilterte Auswahl
     hat Platz für größere Schrift, siehe event_druckmass(). Die 10 pt hier gelten, wo
     nichts gerechnet wird, etwa auf der Mannschaftsseite.

     Enger gesetzt als am Bildschirm. Dort ist Luft zwischen den Zeilen eine Wohltat, weil
     man scrollt und lange liest; auf einem Blatt am Brett sucht man eine einzelne Zeile. */
  html { font-size: 10pt; }
  body { background: #fff; color: #000; font-size: 1rem; line-height: 1.35; }

  /* Alles, was zum Bedienen da ist, hat auf Papier nichts verloren. */
  .site-kopf, .kopf-platz, .site-footer, .skip, .hero,
  .event-bar, .event-empty,
  /* Das Saisonband ist eine Ansicht für den Bildschirm: Es beantwortet „wann und wie
     dicht“, und wer den Zettel ans Brett hängt, will wissen, gegen wen. Auf Papier ist
     die Liste die Auskunft – unabhängig davon, was am Bildschirm gerade sichtbar ist. */
  .eventband { display: none !important; }

  main { padding: 0; }
  /* width, nicht max-width: Am Bildschirm steht dort min(100% - 2.5rem, …), und die
     2,5 rem Luft an den Rändern blieben sonst auch auf Papier stehen – zusätzlich zu den
     Seitenrändern der @page. Das kostete zweieinhalb Geviert Zeilenbreite, genug, um eine
     Paarung umbrechen zu lassen. */
  .wrap { width: auto; max-width: none; padding: 0; }

  /* „Liste drucken“ heißt: die Liste. Der einleitende Satz erklärt die Seite jemandem,
     der sie im Browser aufschlägt, und die weiterführenden Hinweise darunter sind
     Verweise zum Anklicken – beides ist auf Papier nur Platz. Auch die Überschrift der
     Seite fällt weg: „Wettkampf-Kalender“ sagt einem Blatt voller Spieltermine nichts,
     was es nicht selbst zeigt. An ihrer Stelle steht der Kopf unten. Ohne Termine gibt es
     keine Liste und nichts auszublenden, deshalb hängt die Regel an ihrem Vorhandensein. */
  main:has(.eventblock) > *:not(.eventblock) { display: none; }

  /* Auf Papier wird nichts angeklickt; die Unterstreichung bliebe eine leere Geste.
     Die Quellenangabe nennt die Portale weiterhin beim Namen. */
  a { color: #000; text-decoration: none; }
  .hint { color: #333; font-size: .85em; }

  /* Das Blatt hat drei Teile, und man soll sie auseinanderhalten können, ohne zu lesen:
     Kopf, Liste, Fuß. Beide Striche trennen kräftiger als alles innerhalb der Liste, und
     vor allem steht Luft davor und dahinter – eine Zeile Abstand, wo zwischen zwei
     Terminen ein Achtel steht. Dichte hat die Liste nötig, die Ränder nicht.

     Im Kopf steht, was für alle Termine darunter gleich gilt: Mannschaft und Saison, in
     der Zeile darunter die Liga. Was sich unterscheidet, steht in den Zeilen – was nicht,
     steht hier und nur hier. Klein darunter Verein, Adresse und die Erklärung der beiden
     Buchstaben.

     Welcher Kopf erscheint, entscheidet die Filterleiste: Jede Auswahl bringt ihren
     eigenen mit, sichtbar gemacht vom erzeugten CSS in event-list.php. Sichtbar ist
     immer genau einer, deshalb bleibt es hier bei display: none. */
  .printhead {
    grid-template-columns: 1fr auto; gap: .15em .8em; align-items: baseline;
    margin: 0 0 2.1rem; padding-bottom: .7rem;
    border-bottom: 1.5pt solid #000;
  }
  .printhead-titel  { grid-area: 1 / 1; font-size: 1.5rem; font-weight: 700; line-height: 1.2; }
  .printhead-saison { grid-area: 1 / 2; text-align: right; font-weight: 700; }
  .printhead-liga   { grid-area: 2 / 1; }
  /* Keine feste Zeile, sondern die volle Breite: Wo die Liga fehlt – weil die Auswahl
     mehrere enthält und sie deshalb in den Zeilen steht –, rückt die Herkunftszeile eine
     Zeile hoch, statt eine leere stehen zu lassen. Über beide Spalten muss sie trotzdem
     gehen, sonst setzte die Grid-Automatik sie neben die Liga statt darunter. */
  .printhead-fuss   { grid-column: 1 / -1; margin-top: .6em; font-size: .85em; color: #333; }

  .eventfoot {
    margin-top: 2.1rem; padding-top: .7rem;
    border-top: 1.5pt solid #000;
  }
  .eventfoot .hint { margin: 0; }
  .printfoot { display: block; margin: .4em 0 0; font-size: .85em; color: #333; }

  /* Bei dreißig Zeilen am Stück muss der Monatswechsel ins Auge springen – deshalb ein
     durchgezogener Strich, kräftiger als am Bildschirm, wo schon der Kasten die Monate
     voneinander abhebt. */
  .event-group { margin-bottom: 0; }
  .event-month {
    margin: 1.1em 0 .3em; padding-bottom: .1em;
    color: #000; border-bottom: .75pt solid #000;
    break-after: avoid;
  }
  .event-group:first-of-type .event-month { margin-top: 0; }

  /* Am Bildschirm stehen nur die nächsten beiden Partien offen, der Rest klappt auf – man
     liest dort das Nächste, nicht die Saison. Auf Papier gibt es kein Aufklappen: Ein
     Aushang, auf dem zwei von zwanzig Spielen stehen, ist kein Spielplan. Also fällt der
     Knopf weg und alles steht da. Browser ohne ::details-content drucken wie bisher nur
     das Offene – schlechter, aber nicht falsch. */
  .fixtures details::details-content { content-visibility: visible; }
  .fixtures details > summary { display: none; }

  /* Flächen kosten Toner und drucken je nach Einstellung ohnehin nicht mit. Die Zeile
     sagt in Worten, was das grüne Kalenderblatt am Bildschirm andeutet – auf Papier
     bleibt die Aussage also vollständig, auch ohne Farbe. */
  .event-list {
    background: none; border: 0; border-radius: 0; overflow: visible;
  }
  .event-item {
    break-inside: avoid;
    /* Feste Spalten statt auto: Jede Zeile ist ein eigenes Raster, und nur gleiche
       Breiten lassen die Spalten über alle Zeilen hinweg fluchten.

       Die Maße sind knapp gehalten, weil alles, was hier links steht, dem Titel rechts
       fehlt – und der Titel ist es, der umbricht. Gemessen braucht der längste Wochentag
       1,67 Geviert („Sun“), die Uhrzeit 2,5, das H oder A 0,72. Die Datumsspalte fällt
       aus der Reihe: Sie ist nicht für „10.–11.04.“ bemessen, sondern für die seltene
       Spanne über einen Monatswechsel, „30.04.–02.05.“ – die Spalte bricht nicht um,
       sie liefe in die Uhrzeit hinein.

       Die Spalte für H und A steht als Stellschraube da: Wo eine Auswahl nur Heimspiele
       enthält, fällt sie weg, und das erzeugte CSS in event-list.php dreht sie auf null. */
    --spalte-seite: .9em;
    grid-template-columns: 1.8em 6.1em 2.7em var(--spalte-seite) 1fr auto auto;
    gap: 0 .4em; align-items: baseline;
    padding: .12em .1em;
    border-top: 0; border-bottom: .5pt solid #bbb;
  }
  /* Im Spielplan steht rechts das Ergebnis – aber nur bei gespielten Partien. Bliebe die
     Spalte „auto“, wäre sie in den kommenden Zeilen null breit, und alles links davon
     rutschte nach rechts: zwei Listen in einer. Eine feste Breite hält die Spalte offen,
     auch wenn noch nichts drinsteht. Im Kalender gibt es keine Ergebnisse, dort bliebe
     sie leer, und leerer Platz auf Papier ist nur Platz. */
  .fixtures .event-item { grid-template-columns: 1.8em 6.1em 2.7em var(--spalte-seite) 1fr auto 3.2em; }

  /* Kalenderblatt und Angabenblock lösen sich auf; ihre Teile werden selbst zu Spalten. */
  .event-date, .event-body,
  .event-item.is-home .event-date,
  .event-item.is-today .event-date { display: contents; }

  /* Tageszahl und Wochentagskürzel des Kalenderblatts weichen den Kurzfassungen: Die
     Tageszahl allein reicht nicht, wenn man quer über die Monatsüberschriften hinweg
     liest, und „heute“ wäre auf einem Zettel am Brett schon morgen falsch. */
  .event-day, .event-weekday, .event-side { display: none; }

  /* Der Beschreibungstext steht nicht auf dem Aushang. Der Ausdruck ist eine Terminliste,
     deren Höhe event_papiermass() auf genau ein Blatt rechnet; ein Text je Termin spränge
     diese Rechnung – und ein zugeklappter Aufklapper auf Papier wäre schlicht sinnlos. */
  .event-note { display: none; }

  /* „Heimspiel“ fällt weg, der Trennpunkt davor muss mit. Der Selektor wiederholt den
     vom Bildschirm Zeichen für Zeichen: Ein kürzerer verlöre hier, weil der Nachbargriff
     schwerer wiegt, und der Punkt stünde führungslos vor der Liga. */
  .event-side + .event-info::before { content: none; }

  /* Die ausgeschriebene Angabe weicht ihren Einzelteilen: die Liga als Kürzel, der Ort für
     sich. Getrennt müssen sie sein, weil das erzeugte CSS die Liga wegnehmen können muss,
     wo sie schon im Kopf steht. Stehen beide da, trennt sie derselbe Punkt wie sonst. */
  .event-info { display: none; }
  .event-pliga, .event-port { display: inline; }
  .event-pliga + .event-port::before { content: "\00b7\00a0"; }

  .event-pwd, .event-pdate, .event-pside, .event-time {
    display: block; margin: 0; padding: 0; border: 0;
    background: none; color: #000;
    font-size: inherit; font-weight: 400;
    letter-spacing: 0; text-transform: none;
    white-space: nowrap; opacity: 1;
  }
  .event-pwd    { grid-area: 1 / 1; }
  .event-pdate  { grid-area: 1 / 2; font-variant-numeric: tabular-nums; }
  .event-time   { grid-area: 1 / 3; font-variant-numeric: tabular-nums; }
  .event-pside  { grid-area: 1 / 4; font-weight: 700; }
  .event-title  { grid-area: 1 / 5; font-weight: 400; line-height: 1.25; }
  .event-meta   { grid-area: 1 / 6; text-align: right; font-size: .88em; color: #333; }
  .event-result { grid-area: 1 / 7; text-align: right; font-size: inherit; color: #000; }

  /* Der heutige Termin bleibt erkennbar, aber ohne Fläche – Fettung genügt. */
  .event-item.is-today .event-pwd,
  .event-item.is-today .event-pdate { font-weight: 700; }

  .event-pairing .is-us { font-weight: 700; }
  .event-vs { color: #000; }
}
