/* Gestaltung der Infoseite. Eine Datei, keine Abhaengigkeiten, keine fremden
   Server, keine geladenen Schriften — die Seite hat genau eine Aufgabe: die
   Abo-Adresse weiterzugeben.

   Die Vereinsfarben sind Schwarz und Weiss; die Vereinsseite benennt sie
   selbst als --club-primary-500: #000000 und --club-secondary-500: #FFFFFF.
   Mehr wird hier nicht verwendet. Hervorhebungen entstehen deshalb ueber
   Flaeche, Kontrast und Schriftgewicht statt ueber eine dritte Farbe: das
   naechste Spiel ist ein schwarzer Block, Heimspiele tragen eine schwarze
   Kante, der aktive Filter ist schwarz gefuellt. Das rote #990428 der
   Vereinsseite ist die Signalfarbe ihres Redaktionssystems, keine
   Vereinsfarbe — es kommt hier bewusst nicht vor. */

:root {
  --tinte: #000000;
  --tinte-2: #3a3a3a;   /* Fliesstext zweiter Ordnung */
  --leise: #666666;     /* Beiwerk; auf Weiss noch gut lesbar */
  --grund: #f4f4f5;
  --flaeche: #ffffff;
  --linie: #e8e8ea;
  --linie-2: #cfcfd2;
  --auf-dunkel: #ffffff;
  --auf-dunkel-leise: #a6a6a6;

  /* ── Kanten statt Rundungen ──────────────────────────────────────────────
     Grosse Flaechen laufen scharf aus, kleine Bedienelemente behalten eine
     knappe Rundung. Das ist der Unterschied zwischen „Kachel" und „Knopf":
     Wo man tippt, darf es weich sein; was nur traegt, bleibt gerade.
     Durchgehend rund wirkte gefaellig, aber beliebig — gerade Kanten geben
     der Seite eine Haltung, und zu Schwarz-Weiss passt Strenge. */
  --rund: 0px;          /* Karten, Tafeln, der Spielplan */
  --rund-klein: 3px;    /* Knoepfe, Plaketten, Eingabefelder */
  --rund-pille: 999px;  /* die Wettbewerbs-Filter — bewusst der Gegenpol */

  /* Aufklapp-Zeiger. FESTE Groesse, nicht als Anteil der Schrift: Sonst
     schrumpfen sie mit jedem kleiner gesetzten Text mit, und genau so waren
     sie an manchen Stellen auf acht Pixel geschrumpft. */
  --pfeil: 1rem;

  /* ── Schwarz mit Tiefe ───────────────────────────────────────────────────
     Eine grosse Flaeche in reinem Schwarz wirkt flach und schwer. Ein sehr
     flacher Verlauf gibt ihr Richtung, ohne dass man ihn als Farbe bemerkt —
     man sieht kein Grau, nur dass die Flaeche „steht". Diagonal von oben
     links, weil Licht in der Wahrnehmung von dort kommt. */
  --tinte-flaeche:
    radial-gradient(115% 85% at 6% -12%, #43434b 0%, rgba(67, 67, 75, 0) 58%),
    linear-gradient(158deg, #26262c 0%, #0c0c0f 48%, #000000 100%);
  --tinte-band:
    linear-gradient(180deg, #2c2c33 0%, #111114 52%, #000000 100%);
  /* Die helle Innenkante ist der eigentliche Kniff: Sie laesst eine schwarze
     Flaeche wie Material aussehen statt wie ein Loch in der Seite. Ein
     Millimeter Licht an der Oberkante, mehr ist es nicht. */
  --kante-licht: inset 0 1px 0 rgba(255, 255, 255, .13);
  /* Linien AUF der Signalflaeche — drehen sich mit ihr. */
  --linie-auf-signal: rgba(255, 255, 255, .26);

  /* ── Milchglas ─────────────────────────────────────────────────────────
     Die Karten sind nicht mehr deckend, sondern lassen den Lichtgrund
     durchscheinen und verwischen ihn. Das ist der Grund, warum der Grund
     ueberhaupt ein Verlauf sein muss: Auf einem Vollton waere Milchglas
     nicht von einer Flaeche zu unterscheiden.
     Nicht zu durchsichtig — unter etwa 70 % wird Text darauf unruhig,
     weil er sich mit dem bewegt, was dahinter durchscheint. */
  --glas: rgba(255, 255, 255, .74);
  --glas-rand: rgba(255, 255, 255, .7);
  --glas-blur: saturate(150%) blur(22px);
  --korn: .045;

  --grundlicht:
    radial-gradient(100% 62% at 50% -14%, #ffffff 0%, rgba(255, 255, 255, 0) 62%),
    radial-gradient(78% 55% at 108% 104%, #dcdce4 0%, rgba(220, 220, 228, 0) 58%),
    radial-gradient(64% 48% at -12% 92%, #e1e1e9 0%, rgba(225, 225, 233, 0) 60%),
    linear-gradient(180deg, #fafafb 0%, #e9e9ee 100%);

  /* Schatten statt Rahmen: Tiefe traegt die Trennung, nicht eine Linie. */
  --schatten-karte: 0 1px 2px rgba(0, 0, 0, .05), 0 10px 30px -14px rgba(0, 0, 0, .18);
  --schatten-hoch: 0 2px 6px rgba(0, 0, 0, .08), 0 26px 56px -20px rgba(0, 0, 0, .34);

  color-scheme: light;
}

/* ══ Nachtseite ════════════════════════════════════════════════════════════
   Umgeschaltet werden ausschliesslich die Groessen oben — keine einzige
   Regel weiter unten kennt die Farbwelt. Das ist der ganze Grund, warum sie
   Namen tragen statt Werte.

   DIE ENTSCHEIDUNG, AN DER ALLES HAENGT: Was macht der Held-Block?
   Im Hellen ist er die schwarze Flaeche — er hebt hervor, weil er der
   dunkelste Fleck der Seite ist. Im Dunkeln kann er das nicht bleiben:
   Schwarz auf Schwarz hebt nichts hervor. Er kehrt sich deshalb um und wird
   zur HELLSTEN Flaeche. Nicht reinweiss — ein grosser weisser Block blendet
   nachts —, sondern ein heller Grauverlauf mit dunklem Text.

   Damit dreht sich `--tinte-flaeche` mit, und mit ihr alles, was sie
   benutzt: der aktive Filter, der aufgeklappte Knopf, die Baender. Das ist
   gewollt — es ist ueberall dieselbe Aussage „das hier ist gemeint".
   `--auf-dunkel` heisst dann sinngemaess „Text auf der Signalflaeche" und
   ist nachts dunkel. Der Name bleibt, weil ihn hundert Stellen benutzen.

   Zwei Wege hinein: die Systemeinstellung, und der Umschalter im Fuss. Der
   Umschalter gewinnt in beide Richtungen — wer nachts ausdruecklich hell
   will, bekommt hell. */

:root[data-farbe="dunkel"] {
  --tinte: #f2f2f5;          /* Fliesstext — nicht reinweiss, das flimmert */
  --tinte-2: #c9c9d0;
  --leise: #8e8e99;
  --grund: #0d0d10;
  --flaeche: #17171c;
  --linie: #2a2a32;
  --linie-2: #3d3d47;
  --auf-dunkel: #0a0a0c;     /* Text auf der hellen Signalflaeche */
  --auf-dunkel-leise: #4a4a54;

  --tinte-flaeche:
    radial-gradient(105% 80% at 4% -14%, #ffffff 0%, rgba(255, 255, 255, 0) 62%),
    linear-gradient(158deg, #f4f4f6 0%, #e4e4ea 55%, #d6d6de 100%);
  --tinte-band:
    linear-gradient(180deg, #e6e6ea 0%, #cfcfd7 55%, #bcbcc6 100%);
  /* Nachts sitzt die Lichtkante nicht oben, sondern die Schattenkante:
     Auf einer hellen Flaeche im Dunkeln wirkt eine dunkle Oberkante wie
     Tiefe, eine helle wie ein Fehler. */
  --kante-licht: inset 0 1px 0 rgba(0, 0, 0, .12);
  --linie-auf-signal: rgba(0, 0, 0, .22);

  /* Schatten tragen im Dunkeln kaum — Schwarz auf Schwarz sieht man nicht.
     Die Trennung uebernimmt deshalb der Glasrand, der Schatten bleibt nur
     als leichte Verankerung nach unten. */
  --schatten-karte: 0 1px 2px rgba(0, 0, 0, .5), 0 12px 34px -16px rgba(0, 0, 0, .8);
  --schatten-hoch: 0 2px 8px rgba(0, 0, 0, .55), 0 30px 60px -22px rgba(0, 0, 0, .9);

  --glas: rgba(40, 40, 50, .62);
  --grundlicht:
    radial-gradient(125% 75% at 50% -20%, #34343f 0%, rgba(52, 52, 63, 0) 66%),
    radial-gradient(85% 58% at 112% 106%, #22222e 0%, rgba(34, 34, 46, 0) 60%),
    linear-gradient(180deg, #14141b 0%, #060608 100%);
  --glas-rand: rgba(255, 255, 255, .14);
  --korn: .07;

  color-scheme: dark;
}

/* Der Systemeinstellung folgen — ausser der Umschalter sagt ausdruecklich
   etwas anderes. */
@media (prefers-color-scheme: dark) {
  :root:not([data-farbe="hell"]) {
    --tinte: #f2f2f5;
    --tinte-2: #c9c9d0;
    --leise: #8e8e99;
    --grund: #0d0d10;
    --flaeche: #17171c;
    --linie: #2a2a32;
    --linie-2: #3d3d47;
    --auf-dunkel: #0a0a0c;
    --auf-dunkel-leise: #4a4a54;
    --tinte-flaeche:
      radial-gradient(105% 80% at 4% -14%, #ffffff 0%, rgba(255, 255, 255, 0) 62%),
      linear-gradient(158deg, #f4f4f6 0%, #e4e4ea 55%, #d6d6de 100%);
    --tinte-band:
      linear-gradient(180deg, #e6e6ea 0%, #cfcfd7 55%, #bcbcc6 100%);
    --kante-licht: inset 0 1px 0 rgba(0, 0, 0, .12);
    --linie-auf-signal: rgba(0, 0, 0, .22);
    --schatten-karte: 0 1px 2px rgba(0, 0, 0, .5), 0 12px 34px -16px rgba(0, 0, 0, .8);
    --schatten-hoch: 0 2px 8px rgba(0, 0, 0, .55), 0 30px 60px -22px rgba(0, 0, 0, .9);
    --glas: rgba(40, 40, 50, .62);
    --grundlicht:
      radial-gradient(125% 75% at 50% -20%, #34343f 0%, rgba(52, 52, 63, 0) 66%),
      radial-gradient(85% 58% at 112% 106%, #22222e 0%, rgba(34, 34, 46, 0) 60%),
      linear-gradient(180deg, #14141b 0%, #060608 100%);
    --glas-rand: rgba(255, 255, 255, .14);
    --korn: .07;
    color-scheme: dark;
  }
}

* { box-sizing: border-box; }

/* „hidden" muss staerker sein als jedes display aus diesem Stil.
   Der Browser setzt fuer [hidden] zwar `display: none`, aber jede eigene
   Regel mit `display: flex` oder `inline-block` schlaegt das — und dann steht
   ein Element dauerhaft da, das eigentlich verborgen sein sollte. Genau so
   blieb der Probelauf-Hinweis im Bild stehen, obwohl kein Probelauf lief. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--grund);
  color: var(--tinte);
  font: 1.0625rem/1.6 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ── Der Grund ───────────────────────────────────────────────────────────────
   Ein einheitliches Grau ist der Grund, warum eine Seite „nach Dokument"
   aussieht. Hier steht stattdessen Licht: hell in der Mitte oben, wo der
   Blick zuerst hinfaellt, zu den Ecken hin abfallend. Man nimmt es nicht als
   Verlauf wahr — man nimmt wahr, dass die weissen Karten darauf schweben.

   Warum `fixed` statt eines Verlaufs auf dem Body: Ein Verlauf auf dem Body
   waere so hoch wie die Seite, also beim Spielplan ueber mehrere Bildschirme
   gezogen und damit unsichtbar. Fest im Bildschirm bleibt er wirksam.
   Als Element statt `background-attachment: fixed`, weil iOS Safari das
   stillschweigend ignoriert. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  background: var(--grundlicht);
}

/* Feines Korn darueber. Der Grund dafuer ist nicht Nostalgie, sondern
   Bandenbildung: Ein weicher Verlauf ueber einen ganzen Bildschirm zerfaellt
   auf 8-Bit-Anzeigen in sichtbare Streifen. Rauschen bricht die Stufen auf.
   Selbst erzeugt (SVG-Turbulenz, entsaettigt), kein fremdes Bild — bei fuenf
   Prozent Deckkraft sieht man es nicht, man vermisst es nur ohne. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: var(--korn, .045);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='k'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23k)'/%3E%3C/svg%3E");
}

/* Beim Drucken faellt beides weg — Verlauf und Korn kosten nur Toner. */
@media print { body::before, body::after { display: none; } }

.huelle {
  width: min(100% - 2.5rem, 44rem);
  margin-inline: auto;
  padding-block: clamp(2rem, 6vw, 3.5rem) 3rem;
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .karte, #spielplan, .abo {
    background: var(--flaeche);
    border-color: var(--linie);
  }
}

/* ── Kopf ────────────────────────────────────────────────────────────────── */

.kopf { margin-bottom: 1.25rem; }

.augenbraue {
  margin: 0 0 .5rem;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .18em;
  font-weight: 600;
  color: var(--leise);
}

h1 {
  margin: 0;
  font-size: clamp(2.25rem, 8vw, 3.5rem);
  line-height: 1.02;
  letter-spacing: -.035em;
  font-weight: 800;
}

h2 { margin: 0 0 .75rem; font-size: 1.1rem; font-weight: 650; letter-spacing: -.01em; }

/* ── Naechstes Spiel ──────────────────────────────────────────────────────
   Der schwarze Block ist die einzige grosse Flaeche der Seite. Er traegt die
   Vereinsfarbe und beantwortet die haeufigste Frage sofort. */

.held {
  position: relative;
  overflow: hidden;
  /* Verlauf statt Vollton: Die Flaeche bekommt Richtung, ohne dass man den
     Verlauf als solchen bemerkt. */
  background: var(--tinte-flaeche);
  color: var(--auf-dunkel);
  border-radius: var(--rund);
  padding: clamp(1.75rem, 5vw, 2.5rem);
  margin-bottom: 1rem;
  box-shadow: var(--kante-licht), var(--schatten-hoch);
}

/* Ein feiner Lichtsaum an der Oberkante — das, was eine dunkle Flaeche von
   einem schwarzen Loch unterscheidet. Er liegt UEBER dem Inhalt, faengt aber
   keine Beruehrung ab. */
.held::after {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  background: linear-gradient(90deg,
    transparent, rgba(255, 255, 255, .22) 22%, rgba(255, 255, 255, .28) 50%,
    rgba(255, 255, 255, .22) 78%, transparent);
  pointer-events: none;
}

/* ── Jubel ─────────────────────────────────────────────────────────────────
   Ein kleines Maennchen oben rechts, solange Bocholt vorn liegt. Es huepft
   ruhig auf der Stelle und wippt dabei mit den Armen — genug, um den Blick zu
   holen, wenig genug, um beim Lesen nicht zu stoeren. */

.jubel {
  position: absolute;
  top: clamp(.9rem, 3vw, 1.4rem);
  right: clamp(.9rem, 3vw, 1.4rem);
  /* Ueber dem Schimmer, wie der uebrige Inhalt auch. */
  z-index: 2;
  width: clamp(2.2rem, 8vw, 3rem);
  pointer-events: none;
  animation: jubel-huepfen 1.1s ease-in-out infinite;
}

.jubel__bild { display: block; width: 100%; height: auto; }

/* Die Arme wippen leicht gegen den Sprung — das wirkt lebendiger als eine
   starre Figur, die nur auf und ab faehrt. */
.jubel__arme {
  transform-origin: 22px 21px;
  animation: jubel-arme 1.1s ease-in-out infinite;
}

@keyframes jubel-huepfen {
  0%, 100% { transform: translateY(0) rotate(-3deg); }
  50%      { transform: translateY(-6px) rotate(3deg); }
}

@keyframes jubel-arme {
  0%, 100% { transform: rotate(-5deg); }
  50%      { transform: rotate(5deg); }
}

/* Die Zeile ueber der Paarung: „Naechstes Spiel" und daneben die Plakette
   mit der Frist. Der Abstand kommt aus dem `gap` — ohne diese Regeln kleben
   beide Texte aneinander („SpielendeEndstand"). */
.held__marke {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .6rem;
  margin: 0 0 clamp(1rem, 4vw, 1.5rem);
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .18em;
  font-weight: 600;
  color: var(--auf-dunkel-leise);
}

/* Die Plakette selbst: Umriss statt Farbe — in Schwarz-Weiss muss ein Rahmen
   die Arbeit tun, die sonst eine Signalfarbe uebernimmt. */
.held__frist {
  border: 1px solid var(--linie-auf-signal);
  border-radius: 999px;
  padding: .2rem .6rem;
  letter-spacing: .1em;
  color: var(--auf-dunkel);
}

.held__frist:empty { display: none; }

/* Damit die Figur der Zeile nicht ins Gehege kommt, ruecken Text und
   Plakette nach links. */
.held--jubel .held__marke { padding-right: clamp(3rem, 11vw, 4rem); }

/* Waehrend gespielt wird raeumt die Kachel auf.
   Datum, Uhrzeit, Stadion und Wetter beantworten die Frage „wann und wo?" —
   die stellt niemand mehr, waehrend die Mannschaft auf dem Platz steht. Was
   bleibt, ist der Stand; was dazukommt, ist der Weg zu den anderen Partien. */
.held--live .held__wann,
.held--live .held__ort,
.held--live .held__wetter { display: none; }


/* Zwei Wege aus der Kachel heraus, nebeneinander: zu den anderen Partien und
   in die Tabelle. Bei sehr schmalen Schirmen untereinander — zwei Knoepfe auf



/* Aufstellungen — zugeklappt, weil zweimal elf Namen viel Text sind. */
.aufstellung {
  margin-top: 1rem;
  border-top: 1px solid var(--linie-auf-signal);
  padding-top: .8rem;
  color: var(--auf-dunkel);
}

.aufstellung__kopf {
  font-size: .8125rem;
  font-weight: 700;
  cursor: pointer;
  list-style: none;
}

.aufstellung__kopf::-webkit-details-marker { display: none; }

.aufstellung__kopf::after {
  content: '';
  display: inline-block;
  width: .4rem;
  height: .4rem;
  margin-left: .45rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-.1em, -.1em);
  transition: transform .18s ease;
}

.aufstellung[open] .aufstellung__kopf::after {
  transform: rotate(-135deg) translate(-.05em, -.05em);
}

.aufstellung__inhalt {
  display: grid;
  gap: .9rem;
  margin-top: .7rem;
}

.aufstellung__verein {
  margin: 0 0 .25rem;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}

.aufstellung__reihe {
  margin: 0 0 .2rem;
  font-size: .8125rem;
  line-height: 1.45;
  color: var(--auf-dunkel-leise);
}

/* Das Wort davor ist Beschriftung, nicht Inhalt. */
.aufstellung__wort {
  display: inline-block;
  min-width: 3.4rem;
  font-size: .6875rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  opacity: .75;
}

   320 px nebeneinander ergaeben zwei unlesbare Stummel. */
.held__weiter-reihe {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .5rem;
}

@media (max-width: 26rem) {
  .held__weiter-reihe { grid-template-columns: 1fr; }
}

.held__weiter {
  /* Titel oben, Zusatz darunter — nebeneinander bricht beides in der halben
     Breite um, und aus zwei Knoepfen werden vier Zeilen Stolpertext. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .15rem;
  width: 100%;
  padding: .7rem .9rem;
  border: 1px solid var(--linie-auf-signal);
  border-radius: var(--rund-klein);
  background: none;
  color: var(--auf-dunkel);
  font: inherit;
  font-weight: 700;
  text-align: left;
  cursor: pointer;
}

/* Der Pfeil zeigt, dass es weitergeht — gezeichnet, nicht gesetzt: ein
   Unicode-Pfeil wird auf iOS zum bunten Emoji. */
.held__weiter__text::after {
  content: '';
  display: inline-block;
  width: .42rem;
  height: .42rem;
  margin-left: .45rem;
  border-top: 2px solid currentColor;
  border-right: 2px solid currentColor;
  transform: rotate(45deg) translate(-.05em, .05em);
}

.held__weiter__zahl {
  font-weight: 400;
  font-size: .8125rem;
  color: var(--auf-dunkel-leise);
  white-space: nowrap;
}

.held__weiter:hover { border-color: var(--auf-dunkel); }


/* Ohne Bewegung bleibt das Maennchen einfach stehen. Die Aussage steckt in
   der Figur selbst, nicht im Huepfen — anders als beim Konfetti geht dabei
   also nichts verloren. */
@media (prefers-reduced-motion: reduce) {
  .jubel, .jubel__arme { animation: none; }
}

.held__paarung { margin-bottom: clamp(1rem, 4vw, 1.5rem); }

/* Die Paarung wird zur Zeile: Verein links, Tore rechts. */
/* Verein links, Tore rechts — und die Typografie gleich mit. Beides stand
   frueher an zwei weit auseinanderliegenden Stellen; genau so gehen beim
   Umbauen Regeln verloren. */
.held__verein {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin: 0;
  font-size: clamp(1.6rem, 6.5vw, 2.5rem);
  line-height: 1.12;
  letter-spacing: -.03em;
  font-weight: 500;
  color: var(--auf-dunkel-leise);
}

.held__tore {
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  color: var(--auf-dunkel);
}

.held__tore:empty { display: none; }

/* ── Laufendes Spiel ───────────────────────────────────────────────────────
   Der Punkt vor „live" schlaegt ruhig weiter — das ist die einzige Bewegung,
   die im Betrieb dauerhaft laeuft, und sie hat einen Zweck: sie sagt, dass
   die Zahlen daneben gerade wirklich aktuell sind. */

@keyframes puls {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .35; transform: scale(.75); }
}

/* Kraeftiger als ein blosses Aufhellen des Rahmens: die Plakette hebt sich
   beim Einatmen sichtbar von der schwarzen Flaeche ab — Rahmen, Schrift und
   ein Hauch Fuellung ziehen mit, dazu eine leichte Vergroesserung. Der
   Rhythmus bleibt ruhig; staerker heisst deutlicher, nicht hektischer. */
@keyframes vorfreude {
  0%, 100% {
    border-color: var(--linie-auf-signal);
    background: transparent;
    color: var(--auf-dunkel-leise);
    transform: scale(1);
  }
  50% {
    border-color: var(--auf-dunkel);
    background: rgba(255, 255, 255, .14);
    color: var(--auf-dunkel);
    transform: scale(1.06);
  }
}

.held--live .held__frist {
  border-color: var(--auf-dunkel);
  background: var(--auf-dunkel);
  color: var(--tinte);
}

.held--live .held__frist::before {
  content: "";
  display: inline-block;
  width: .45rem;
  height: .45rem;
  margin-right: .4rem;
  border-radius: 50%;
  background: var(--tinte);
  animation: puls 1.6s ease-in-out infinite;
}

/* Kurz vor dem Anpfiff atmet der Rahmen der Frist leise mit. */
.held__frist { animation: vorfreude 2.6s ease-in-out infinite; }
.held--live .held__frist { animation: none; }

/* Nach dem Abpfiff: der Endstand steht ruhig da. Kein Atmen — es gibt nichts
   mehr zu erwarten —, aber eine deutliche Plakette, damit klar ist, dass die
   Zahlen daneben das Ergebnis sind und keine Vorschau. */
.held--beendet .held__frist {
  animation: none;
  border-color: var(--auf-dunkel-leise);
  color: var(--auf-dunkel);
}

/* Wer Bewegung abgestellt hat, bekommt keine — der Live-Punkt bleibt dann
   einfach sichtbar stehen, die Aussage geht nicht verloren. */
@media (prefers-reduced-motion: reduce) {
  .held,
  .held__frist,
  .held--live .held__frist::before { animation: none; }
}

/* Der eigene Verein hebt sich ab — ohne Farbe, nur ueber Gewicht und Helligkeit. */
.held__verein--eigen { color: var(--auf-dunkel); font-weight: 800; }

.held__wann {
  margin: 0 0 .3rem;
  font-size: clamp(1rem, 3.5vw, 1.15rem);
  font-weight: 600;
}

.held__wo {
  margin: 0;
  font-size: .875rem;
  color: var(--auf-dunkel-leise);
}

/* ── Abonnieren ────────────────────────────────────────────────────────────
   Ein Fingertipp statt Adresse kopieren: die Knoepfe uebergeben die Adresse
   direkt an das Kalenderprogramm. Das Kopierfeld bleibt als Rueckfallebene. */

.abo {
  background: var(--glas);
  -webkit-backdrop-filter: var(--glas-blur);
  backdrop-filter: var(--glas-blur);
  border: 1px solid var(--glas-rand);
  border-radius: var(--rund);
  padding: clamp(.9rem, 3vw, 1.15rem);
  margin-bottom: 1rem;
}

/* Der Kopf ist zugleich der Umschalter: die ganze Zeile ist antippbar, nicht
   nur ein kleiner Winkel am Rand. */
.abo__kopf {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.abo__kopftext { display: block; }

.abo__winkel {
  flex: 0 0 auto;
  font-size: var(--pfeil);
  color: var(--leise);
  transition: transform .2s;
}

.abo__kopf[aria-expanded="true"] .abo__winkel { transform: rotate(180deg); }

/* Zugeklappt braucht der Kopf keinen Abstand nach unten. */
.abo__kopf[aria-expanded="true"] { margin-bottom: .7rem; }

.abo__titel { display: block; font-size: .95rem; font-weight: 650; letter-spacing: -.01em; }
.abo__zeile { display: block; margin: .1rem 0 0; color: var(--leise); font-size: .78rem; }

@media (prefers-reduced-motion: reduce) {
  .abo__winkel { transition: none; }
}

.abo-knoepfe {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-bottom: .6rem;
}

/* Schmale Grundbreite, damit die beiden Knoepfe auch auf einem Handy
   nebeneinander stehen statt untereinander — das spart eine ganze Zeile. */
.abo-knopf {
  flex: 1 1 7.5rem;
  /* Nicht unter die eigene Beschriftung schrumpfen: wird es zu eng, sollen
     die Knoepfe lieber untereinander rutschen als der Text ueberlaufen. */
  min-width: fit-content;
  /* Auf breiten Schirmen nicht ueber die halbe Karte wachsen — sonst wirken
     zwei randbreite Balken wuchtiger als noetig. */
  max-width: 15rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.6rem;
  padding: 0 .6rem;
  border: 1.5px solid var(--tinte);
  border-radius: var(--rund-klein);
  background: var(--tinte);
  color: var(--auf-dunkel);
  font-weight: 650;
  font-size: .8125rem;
  white-space: nowrap;
  text-decoration: none;
  text-align: center;
  transition: background .15s, color .15s;
}

.abo-knopf--zweit { background: var(--flaeche); color: var(--tinte); }
.abo-knopf:hover { background: var(--tinte-2); border-color: var(--tinte-2); color: var(--auf-dunkel); }

/* Adresse: muss auf dem Handy umbrechen duerfen, sonst schiebt sie die
   ganze Seite in die Breite. */
/* Die Adresse in der Anleitung: ein Knopf, der wie ein Feld aussieht. Ein
   Antippen kopiert — auf dem Handy ist Markieren von Hand eine Fummelei.
   Darunter Luft, sonst klebt er am ersten Aufklapp-Abschnitt. */
.adresse {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .3rem .75rem;
  width: 100%;
  background: var(--grund);
  border: 1px solid transparent;
  border-radius: var(--rund-klein);
  padding: .55rem .7rem;
  margin-bottom: 1.5rem;
  text-align: left;
}

.adresse--knopf { cursor: pointer; transition: border-color .15s, background .15s; }
.adresse--knopf:hover { border-color: var(--linie-2); }

.adresse--kopiert { border-color: var(--tinte); background: var(--flaeche); }

.adresse__hinweis {
  flex: 0 0 auto;
  font-size: .7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--leise);
  white-space: nowrap;
}

.adresse--kopiert .adresse__hinweis { color: var(--tinte); }

.adresse code {
  flex: 1 1 auto;
  min-width: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .8rem;
  color: var(--tinte-2);
  overflow-wrap: anywhere;
}

button {
  flex: 0 0 auto;
  min-height: 1.95rem;
  padding: 0 .7rem;
  border: 1px solid var(--linie-2);
  border-radius: .45rem;
  background: var(--flaeche);
  color: var(--tinte);
  font: inherit;
  font-weight: 600;
  font-size: .8125rem;
  cursor: pointer;
}

button:hover { border-color: var(--tinte); }

.hinweis { margin: .6rem 0 0; font-size: .8125rem; font-weight: 600; }

/* Kopieren und Anleitung teilen sich eine Zeile — beides sind Nebenwege. */
.abo__fussnote {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .45rem;
  margin: .6rem 0 0;
  font-size: .78rem;
  color: var(--linie-2);
}

/* Ein Knopf, der wie ein Verweis aussieht: er tut etwas, verdient aber kein
   Gewicht — beide Nebenwege sollen gleich leise auftreten. */
.text-knopf {
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--leise);
  font: inherit;
  text-decoration: underline;
  text-underline-offset: .2em;
  cursor: pointer;
}

.text-knopf:hover { color: var(--tinte); border: 0; }
.abo__fussnote a { color: var(--leise); text-underline-offset: .2em; }
.abo__fussnote a:hover { color: var(--tinte); }

/* ── Karten (Anleitung, Erlaeuterung) ────────────────────────────────────── */

.karte {
  /* Milchglas statt Vollton: Die Karte laesst den Lichtgrund durchscheinen
     und verwischt ihn. Deshalb muss der Grund ein Verlauf sein — auf einer
     einfarbigen Flaeche waere Milchglas von einer Flaeche nicht zu
     unterscheiden, und der ganze Aufwand waere umsonst.
     Der feine Rand ist hier kein Zierrat, sondern die Kante des Glases: Ohne
     ihn franst die Karte aus, weil ihr Inneres dem Grund zu aehnlich ist. */
  background: var(--glas);
  -webkit-backdrop-filter: var(--glas-blur);
  backdrop-filter: var(--glas-blur);
  border: 1px solid var(--glas-rand);
  border-radius: var(--rund);
  padding: clamp(1.4rem, 4vw, 1.9rem);
  margin-bottom: 1rem;
  box-shadow: var(--schatten-karte);
}

.karte p { margin: 0 0 .75rem; }
.karte p:last-child { margin-bottom: 0; }

.klein { font-size: .875rem; color: var(--leise); }

details { border-top: 1px solid var(--linie); padding: .9rem 0; }
details:first-of-type { border-top: 0; padding-top: 0; }
details:last-of-type { padding-bottom: 0; }
summary { cursor: pointer; font-weight: 600; }
summary::marker { color: var(--leise); }
details ol { margin: .75rem 0 0; padding-left: 1.25rem; color: var(--tinte-2); }
details li { margin-bottom: .4rem; }
details a { color: var(--tinte); text-underline-offset: .2em; }

/* Beschriftung links, Wert rechts — als feine Zeilenfolge mit Haarlinien
   dazwischen. (Diese Regeln waren beim Umbau des Tabellenteils versehentlich
   mitgeloescht worden; ohne sie standen Beschriftung und Wert untereinander.) */
.stand {
  margin: 0;
  display: grid;
  gap: 1px;
  background: var(--linie);
  border: 1px solid var(--linie);
  border-radius: .625rem;
  overflow: hidden;
}

.stand > div {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1rem;
  justify-content: space-between;
  background: var(--flaeche);
  padding: .6rem .9rem;
}

.stand dt { color: var(--leise); font-size: .875rem; }
.stand dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; }

.fuss { margin-top: 2.5rem; color: var(--leise); font-size: .8125rem; }
/* Links erben die Textfarbe, statt im Browser-Blau zu stehen.
   ES GAB DIESE REGEL NICHT — nur eine für den Fuß. Solange alle Links dort
   standen, fiel das nicht auf; der erste Link außerhalb war sofort blau und
   riss ein Loch in eine Seite aus zwei Farben. Erkennbar bleiben sie durch
   die Unterstreichung, nicht durch die Farbe. */
a { color: inherit; }

.fuss a { color: var(--leise); }

:focus-visible { outline: 2px solid var(--tinte); outline-offset: 3px; border-radius: .35rem; }

/* ── Spielplan ─────────────────────────────────────────────────────────────
   Als ruhige Liste mit feinen Trennlinien statt als Kaestchenreihe: bei gut
   drei Dutzend Spielen liest sich das schneller. Reihenfolge wie auf der
   Vereinsseite — Wann | Wettbewerb | Paarung | Heim/Auswaerts. Auf breiten
   Schirmen als Zeile, auf dem Handy gestapelt. */

/* Nach der Liste ein deutlicher Absatz, bevor die Anleitung beginnt — derselbe
   Abstand wie ueber der Ueberschrift des Spielplans. Ohne ihn stiess die
   naechste Kachel unmittelbar an die letzte Spielzeile. */
.spielplan-bereich { margin-bottom: 2.5rem; }

.bereich-kopf {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  margin: 2.5rem 0 .9rem;
}

.bereich-kopf h2 { margin: 0; font-size: 1.25rem; font-weight: 700; letter-spacing: -.02em; }

.filter { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: 1rem; }

.filter__knopf {
  min-height: 2.25rem;
  padding: 0 .9rem;
  border: 1px solid var(--linie-2);
  border-radius: 999px;
  background: var(--flaeche);
  color: var(--tinte-2);
  font: inherit;
  font-size: .8125rem;
  font-weight: 600;
  cursor: pointer;
}

.filter__knopf:hover { border-color: var(--tinte); color: var(--tinte); }

.filter__knopf.ist-aktiv {
  background: var(--tinte-flaeche);
  border-color: var(--tinte);
  color: var(--auf-dunkel);
  box-shadow: var(--kante-licht);
}

.ladehinweis { color: var(--leise); padding: 1.5rem 0; }

#spielplan {
  background: var(--glas);
  -webkit-backdrop-filter: var(--glas-blur);
  backdrop-filter: var(--glas-blur);
  border: 1px solid var(--glas-rand);
  border-radius: var(--rund);
  /* `clip` statt `hidden`: Beides beschneidet die Ecken, aber `hidden` macht
     das Element laut Spezifikation zu einem Scroll-Container — und dann
     bezöge sich `view()` weiter unten auf ihn statt auf die Seite und stünde
     still. */
  overflow: clip;
  box-shadow: var(--schatten-karte);
}

#spielplan.ladehinweis { background: none; border: 0; }

.monat {
  margin: 0;
  padding: .7rem clamp(1rem, 4vw, 1.5rem);
  background: var(--grund);
  border-block: 1px solid var(--linie);
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .16em;
  color: var(--leise);
}

.monat:first-child { border-top: 0; }

.spiel {
  display: grid;
  /* Die dritte Spalte FEST, nicht „auto": „HEIM" ist rund halb so breit wie
     „AUSWÄRTS", und damit waere die mittlere Spalte in jeder Zeile anders
     breit — die Ergebnisse staenden dann trotz Zentrierung schief. */
  grid-template-columns: 10rem 1fr 6rem;
  grid-template-areas:
    "wann wettbewerb art"
    "wann paarung    parken";
  align-items: center;
  gap: .2rem 1.25rem;
  padding: .9rem clamp(1rem, 4vw, 1.5rem);
  border-top: 1px solid var(--linie);
  /* Platz fuer die Heimspiel-Kante, damit die Zeilen buendig bleiben. */
  border-left: 3px solid transparent;
}

.monat + .spiel { border-top: 0; }

/* Heimspiele bekommen eine schwarze Kante — auf einen Blick erkennbar,
   ganz ohne zusaetzliche Farbe. */
.spiel--heim { border-left-color: var(--tinte); }

/* Noch nicht terminierte Spiele bekommen bewusst KEINE Flaechenmarkierung,
   obwohl das Skript .spiel--offen weiterhin vergibt: derzeit sind 22 von 34
   Spielen noch ohne Anstosszeit — eine Markierung wuerde die Mehrheit
   hervorheben statt die Ausnahme. Der Stern in der Zeitspalte genuegt. */

/* Gespielte Begegnungen treten zurueck: grauer Grund und gedaempfte Schrift.
   Das ERGEBNIS bleibt bewusst kraeftig — bei einem gespielten Spiel ist es die
   eigentliche Information, es waere verkehrt, ausgerechnet das auszugrauen.
   Auch die Kante der Heimspiele wird zurueckgenommen, sonst zoege der
   schwarze Balken den Blick weiter auf die Vergangenheit. */
.spiel--vorbei { background: var(--grund); color: var(--leise); }
.spiel--vorbei .spiel__datum { font-weight: 500; }
.spiel--vorbei .verein,
.spiel--vorbei .verein--eigen { color: var(--leise); }
.spiel--vorbei .verein--eigen { font-weight: 700; }
.spiel--vorbei.spiel--heim { border-left-color: var(--linie-2); }

/* Die Nahtstelle zwischen gespielt und kommend. Seit der Spielplan in zwei
   Blöcke geteilt ist, wird sie nicht mehr erzeugt — die Blocküberschriften
   leisten dasselbe deutlicher. Die Regel bleibt stehen, weil `.grenze` auch
   sonst als Abschnittsband taugt und nichts kostet, solange niemand sie
   vergibt. */
.grenze {
  margin: 0;
  padding: .55rem clamp(1rem, 4vw, 1.5rem);
  background: var(--tinte-band);
  color: var(--auf-dunkel);
  font-size: .68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .16em;
  box-shadow: var(--kante-licht);
}

.grenze + .spiel { border-top: 0; }

.spiel__wann { grid-area: wann; display: flex; flex-direction: column; }
.spiel__tag { font-size: .75rem; color: var(--leise); }
.spiel__datum { font-weight: 600; font-variant-numeric: tabular-nums; }
.spiel__zeit { font-size: .8125rem; color: var(--leise); font-variant-numeric: tabular-nums; }
/* Noch keine Anstosszeit: als schwarze Plakette, damit es beim Ueberfliegen
   sofort auffaellt. Sie steht an derselben Stelle wie sonst die Uhrzeit und
   kann nicht mit der Ergebnis-Plakette kollidieren — ein Spiel hat entweder
   ein Ergebnis oder noch keine Zeit, nie beides. */
.spiel__zeit--offen {
  align-self: flex-start;
  background: var(--tinte);
  color: var(--auf-dunkel);
  border-radius: .3rem;
  padding: .1rem .35rem;
  font-size: .68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
  white-space: nowrap;
}

/* Bei gespielten Terminen bleibt die Plakette zurueckgenommen — dort ist sie
   nur noch Randnotiz. */
.spiel--vorbei .spiel__zeit--offen { background: var(--leise); }

.spiel__wettbewerb {
  grid-area: wettbewerb;
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--leise);
}
.spiel__spieltag::before { content: "·"; margin-right: .4rem; }

/* Die Paarung.
   Auf breiten Schirmen eine Zeile: Heimverein rechtsbuendig, Ergebnis in der
   Mitte, Gastverein linksbuendig — dadurch liegen die Ergebnisse aller Zeilen
   auf derselben Achse. Auf dem Handy wird daraus ein Zweizeiler (siehe unten),
   weil dort fuer drei Spalten schlicht die Breite fehlt. */
.spiel__paarung {
  grid-area: paarung;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(2.9rem, auto) minmax(0, 1fr);
  grid-template-areas: "heim stand gast";
  align-items: baseline;
  gap: .45rem;
}

.paarung__heim { grid-area: heim; text-align: right; }
.paarung__gast { grid-area: gast; text-align: left; }
.paarung__stand { grid-area: stand; justify-self: center; white-space: nowrap; }

.verein { font-weight: 500; }
.verein--eigen { font-weight: 750; }

/* Der Punkt zwischen den Toren steht auch dann da, wenn noch nichts gespielt
   wurde — dann eben allein und leise. */
.paarung__punkt { color: var(--linie-2); margin-inline: .1rem; }

.paarung__tor { font-variant-numeric: tabular-nums; }

/* Mit Ergebnis wird der Stand zur schwarzen Plakette. */
.paarung__stand--ergebnis {
  background: var(--tinte);
  color: var(--auf-dunkel);
  border-radius: .3rem;
  padding: .05rem .4rem;
  font-size: .9375rem;
  font-weight: 700;
}

.paarung__stand--ergebnis .paarung__punkt { color: inherit; }

.spiel--vorbei .paarung__stand--ergebnis { background: var(--tinte); }

/* Seit „Parken" darunter sitzt, gehört die Ortsangabe in die obere Zeile —
   vorher stand sie über beide zentriert. */
.spiel__art { align-self: center; }

.spiel__art {
  grid-area: art;
  text-align: right;
  font-size: .6875rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  font-weight: 600;
  color: var(--leise);
}

@media (max-width: 40rem) {
  .spiel {
    grid-template-columns: 1fr auto;
    grid-template-areas:
      "wann       art"
      "wettbewerb wettbewerb"
      "paarung    paarung"
      "parken     parken";
    gap: .3rem .75rem;
  }
  .spiel__wann { flex-direction: row; flex-wrap: wrap; gap: .45rem; align-items: baseline; }

  /* Auf dem Handy stehen die Vereine UNTEREINANDER und ihre Tore rechts —
     genau wie im schwarzen Block oben. Damit liegen die Zahlen ueber alle
     Spiele hinweg auf einer Achse, voellig unabhaengig von der Namenslaenge,
     und kein Name muss umbrechen. Fuer drei Spalten nebeneinander blieben je
     Verein keine hundert Bildpunkte; „Rot-Weiss Oberhausen" braucht mehr als
     das Anderthalbfache davon.

     Der Trick ist `display: contents`: die drei Teile des Standes werden zu
     eigenen Rasterfeldern, sodass sich Tor und Verein paaren lassen. */
  .spiel__paarung {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "heim torheim"
      "gast torgast";
    gap: .15rem .75rem;
  }

  .paarung__heim, .paarung__gast { text-align: left; }

  .paarung__stand { display: contents; }
  .paarung__punkt { display: none; }

  /* Jedes Tor bekommt hier seine eigene schwarze Plakette. Ohne sie erben die
     Zahlen bei gespielten Partien das Grau der ausgegrauten Zeile und sind
     kaum noch zu lesen — ausgerechnet die Zahl, auf die es ankommt. Die feste
     Mindestbreite haelt einstellige und zweistellige Staende auf einer Achse. */
  .paarung__tor {
    justify-self: end;
    min-width: 1.7rem;
    padding: .05rem .35rem;
    border-radius: .3rem;
    background: var(--tinte);
    color: var(--auf-dunkel);
    text-align: center;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
  }

  /* Ohne Ergebnis kein Kaestchen — sonst staenden zwei leere Flecken da. */
  .paarung__tor:empty {
    background: none;
    padding: 0;
    min-width: 0;
  }

  .paarung__tor:first-of-type { grid-area: torheim; }
  .paarung__tor:last-of-type { grid-area: torgast; }
}

/* ── Tabelle ───────────────────────────────────────────────────────────────
   Aufgeklappt als Liste unter den Knoepfen; auf schmalen Schirmen fallen
   Siege, Unentschieden, Niederlagen und Tore weg — uebrig bleibt, wonach man
   auf dem Handy sieht: Platz, Verein, Spiele, Differenz, Punkte. Waagerecht
   schieben muessen soll man nichts. */

.tafel-knoepfe {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  margin-bottom: 1rem;
}

/* „Vergangene Saisons" ist der laengste der drei Beschriftungen. Auf einem
   Handy nebeneinander gequetscht braeche sie um — deshalb bekommt sie eine
   eigene Zeile, sobald es eng wird. */
.tafel-knopf--breit { flex: 1 1 12rem; }

.tafel-knopf {
  /* Gleiche Breite unabhaengig vom Text: beide bekommen dieselbe Grundgroesse
     und denselben Anteil am uebrigen Platz. */
  flex: 1 1 0;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .1rem;
  min-height: 3.2rem;
  padding: .4rem .6rem;
  /* Feiner Rand statt kraeftiger Kante: Drei Knoepfe nebeneinander mit je
     1,5px Rahmen ergaben ein Gitter. Die Trennung leistet jetzt der
     Schatten. */
  border: 1px solid var(--linie-2);
  border-radius: var(--rund-klein);
  background: var(--flaeche);
  color: var(--tinte);
  box-shadow: var(--schatten-karte);
  font: inherit;
  cursor: pointer;
  transition: background .15s, color .15s;
}

/* Zweizeilig statt einzeilig: nebeneinander muesste die Schrift auf einem
   schmalen Geraet so klein werden, dass sie niemand mehr liest — untereinander
   bestimmt nur noch die laengste EINZELNE Angabe die Breite, und die passt
   ueberall. Zusaetzlich waechst die Schrift mit der Fensterbreite mit, bleibt
   dabei aber zwischen den beiden Grenzen. */
.tafel-knopf__text {
  /* Ohne min-width: 0 wächst ein Flex-Kind über seinen Platz hinaus, statt
     seinen Inhalt zu kürzen — die häufigste Ursache für überlaufende Knöpfe. */
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .1rem;
  min-width: 0;
}

.tafel-knopf__haupt {
  font-size: clamp(.8rem, 2.6vw, .9375rem);
  font-weight: 700;
  line-height: 1.15;
  /* Kein Umbruch: „Liga-Zahlen" brach in zwei Zeilen und riss den Knopf
     hoeher als seine beiden Nachbarn — drei Knoepfe nebeneinander, einer
     davon groesser, sieht nach Versehen aus. Lieber am Bindestrich kuerzen
     als die Reihe verziehen. */
  white-space: nowrap;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tafel-knopf__zusatz {
  font-size: clamp(.65rem, 2.1vw, .75rem);
  font-weight: 600;
  line-height: 1.15;
  opacity: .75;
  white-space: nowrap;
  /* Ein zu langer Zusatz lief aus dem Knopf heraus und quer über den
     Nachbarn. `nowrap` allein genügt dafür nicht: Ohne Begrenzung und
     Überlaufregel wächst die Zeile einfach weiter. Beides gehört zusammen —
     zusammen mit `min-width: 0` am Elternteil. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Ohne Zahlen keine zweite Zeile — der Knopf soll dann nicht hoeher werden. */
.tafel-knopf__zusatz:empty { display: none; }

.tafel-knopf[aria-expanded="true"] .tafel-knopf__zusatz { opacity: .85; }

.tafel-knopf__winkel {
  flex: 0 0 auto;
  margin-left: .35rem;
  font-size: .8rem;
  line-height: 1;
  transition: transform .2s;
}

.tafel-knopf:hover { background: var(--grund); }

.tafel-knopf[aria-expanded="true"] {
  background: var(--tinte-flaeche);
  border-color: var(--tinte);
  color: var(--auf-dunkel);
  box-shadow: var(--kante-licht), var(--schatten-hoch);
}

.tafel-knopf[aria-expanded="true"] .tafel-knopf__winkel { transform: rotate(180deg); }

@media (prefers-reduced-motion: reduce) {
  .tafel-knopf, .tafel-knopf__winkel { transition: none; }
}

.tabelle-tafel { padding: 0; overflow: hidden; margin-bottom: 1rem; }

/* Inhalt einer aufgeklappten Tafel, die keine Tabelle ist. */
.tafel-inhalt { padding: clamp(1rem, 4vw, 1.4rem); }

.tafel-titel {
  margin: 0 0 .8rem;
  font-size: .95rem;
  font-weight: 650;
  letter-spacing: -.01em;
}

.tafel-inhalt .klein { margin: .8rem 0 0; }

.tabelle {
  width: 100%;
  border-collapse: collapse;
  font-size: .875rem;
  font-variant-numeric: tabular-nums;
}

.tabelle th, .tabelle td {
  padding: .5rem .35rem;
  text-align: left;
  border-top: 1px solid var(--linie);
}

.tabelle thead th {
  border-top: 0;
  padding-block: .6rem;
  font-size: .68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--leise);
  background: var(--grund);
}

.tabelle__platz { width: 2.4rem; padding-left: clamp(1rem, 4vw, 1.5rem) !important; color: var(--leise); font-weight: 600; }
/* Veraenderung zum letzten Spieltag.
   Diese beiden Farben sind die EINZIGE Ausnahme von Schwarz-Weiss auf dieser
   Seite — und sie sind keine Vereins-, sondern Signalfarben: sie sagen „auf"
   und „ab", nicht „Bocholt". Getragen wird die Aussage ohnehin von Pfeil und
   Zahl; die Farbe verstaerkt nur. Beide Toene sind auf Weiss ausreichend
   kontrastreich, auch auf der grau hinterlegten Bocholt-Zeile (5,5:1 und 5,8:1). */
.tabelle__pfeil {
  width: 2.6rem;
  padding-left: 0 !important;
  padding-right: .2rem !important;
  text-align: right;
  font-size: .75rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.tabelle__pfeil--hoch { color: #177032; }
.tabelle__pfeil--runter { color: #b42318; }
.tabelle__pfeil--gleich { color: var(--linie-2); }

/* Nur fuer Vorlesesoftware — die Spalte hat sonst keine Ueberschrift. */
.nur-vorlesen {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.tabelle__verein { font-weight: 500; }
.tabelle__zahl { text-align: right; }
.tabelle__punkte { font-weight: 700; padding-right: clamp(1rem, 4vw, 1.5rem) !important; }

/* Das Faehnchen „live" in der Tabelle.

   WAEHREND GESPIELT WIRD bekommt jede Punktezelle zwei feste Spalten: die Zahl
   und den Platz fuer das Faehnchen. Der Platz steht in ALLEN Zeilen, auch in
   denen ohne Faehnchen — sonst ruecken ein- und zweistellige Punktzahlen
   unterschiedlich weit ein, und dann steht kein Faehnchen mehr unter dem
   anderen. Genau das war der Mangel an der ersten Fassung.

   NUR DANN: An einem gewoehnlichen Tag kostete die Spalte Breite, die die
   Tabelle auf dem Telefon nicht hat. */
.tabelle--live .tabelle__punkte {
  display: grid;
  grid-template-columns: 1fr 2.2rem;
  align-items: center;
  gap: .3rem;
}

.tabelle__punktzahl {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* BEWUSST OHNE PULSIEREN: Auf dieser Seite haengt jede Bewegung an der
   Scrollposition, nicht an der Uhr. Neun blinkende Faehnchen in einer Tabelle
   waeren Flimmern statt Hinweis. */
.tabelle__live {
  justify-self: start;
  padding: .1rem .3rem;
  border-radius: var(--rund-klein);
  background: var(--tinte-flaeche);
  color: var(--auf-dunkel);
  font-size: .5625rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  line-height: 1.3;
}

/* Die eigene Mannschaft — wie bei den Heimspielen ueber eine schwarze Kante,
   nicht ueber eine Farbe. */
.tabelle__eigen { background: var(--grund); }
.tabelle__eigen td, .tabelle__eigen th { font-weight: 700; color: var(--tinte); }
.tabelle__eigen .tabelle__platz { box-shadow: inset 3px 0 0 var(--tinte); }

.tabelle-tafel .klein { padding: .7rem clamp(1rem, 4vw, 1.5rem); margin: 0; border-top: 1px solid var(--linie); }

@media (max-width: 34rem) {
  .tabelle__schmal { display: none; }
  .tabelle { font-size: .8125rem; }
}

/* ── Bilanz ────────────────────────────────────────────────────────────────
   Die Formkurve als Reihe von Zeichen: Sieg schwarz gefuellt, Unentschieden
   grau, Niederlage nur als Umriss. In Schwarz-Weiss muss die Fuellung die
   Arbeit tun, die sonst Gruen und Rot uebernehmen. */

.bilanz__form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .3rem;
  margin: 0 0 1rem;
}

.form-zeichen {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: .4rem;
  border: 1.5px solid var(--tinte);
  font-size: .8rem;
  font-weight: 700;
  cursor: help;
}

.form-zeichen--s { background: var(--tinte); color: var(--auf-dunkel); }
.form-zeichen--u { background: var(--linie-2); border-color: var(--linie-2); color: var(--tinte); }
.form-zeichen--n { background: var(--flaeche); color: var(--tinte); }

.form-beschriftung {
  margin-left: .4rem;
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--leise);
}

.tafel-inhalt .stand { margin-bottom: 0; }

/* ── Tabellenverlauf ───────────────────────────────────────────────────────
   Eine einzige Reihe von Werten, eine einzige Farbe — hier braucht es weder
   Legende noch Achsenbeschriftung. Die beiden Hilfslinien markieren Platz 1
   und den letzten Platz; alles Weitere waere Beiwerk. */

.verlauf { padding: 1rem clamp(1rem, 4vw, 1.5rem) .5rem; }

.verlauf__bild { display: block; width: 100%; height: auto; max-height: 5rem; }

.verlauf__hilfslinie { stroke: var(--linie); stroke-width: 1; }

.verlauf__linie {
  fill: none;
  stroke: var(--tinte);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.verlauf__punkt { fill: var(--tinte); }

/* Der aktuelle Stand hebt sich ab — mit einem Ring in der Flaechenfarbe,
   damit er auf der Linie nicht verschwimmt. */
.verlauf__punkt--jetzt { stroke: var(--flaeche); stroke-width: 2; }

.verlauf .klein { margin: .4rem 0 0; }

/* Ort und Wetter im Kopf — nachgeordnete Angaben, deshalb kleiner und
   zurueckgenommen. Das Wetter erscheint erst wenige Tage vor dem Spiel. */
.held__ort {
  margin: .5rem 0 0;
  font-size: .8125rem;
  color: var(--auf-dunkel-leise);
}

.held__wetter {
  display: inline-block;
  margin: .7rem 0 0;
  padding: .3rem .65rem;
  border: 1px solid var(--linie-auf-signal);
  border-radius: var(--rund-klein);
  font-size: .75rem;
  font-weight: 600;
  color: var(--auf-dunkel);
}

#wetter-quelle { margin-top: .6rem; }

/* ── Nachladen durch Herunterziehen ────────────────────────────────────────
   Nur in der App-Ansicht in Betrieb (siehe spielplan.js). Die Leiste haengt
   ueber dem oberen Rand und wird beim Ziehen hereingeschoben. */

.ziehen {
  position: fixed;
  top: -3rem;
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  height: 3rem;
  background: var(--grund);
  color: var(--leise);
  font-size: .75rem;
  font-weight: 600;
  opacity: 0;
  pointer-events: none;
}

.ziehen__pfeil { display: inline-block; font-size: .9rem; transition: transform .2s; }

@keyframes dreht { to { transform: rotate(360deg); } }
.ziehen__pfeil--dreht { animation: dreht .9s linear infinite; }

@media (prefers-reduced-motion: reduce) {
  .ziehen__pfeil, .ziehen__pfeil--dreht { transition: none; animation: none; }
}

/* ── Hinweis auf einen Probelauf ───────────────────────────────────────────
   Nur sichtbar, solange der Dienst im Probemodus laeuft. Bewusst nicht zu
   uebersehen: die Seite ist oeffentlich, und ein vorgefuehrter Spielstand darf
   nicht fuer einen echten gehalten werden.

   Zwei Hinweise, weil einer nicht reicht:

   Der Hinweis steht gross und im Lesefluss, direkt ueber dem naechsten Spiel.
   Ein zusaetzliches Band am oberen Rand gab es anfangs auch — es lag in der
   App-Ansicht hinter der Statusleiste und war ueberfluessig, sobald der grosse
   Hinweis auffaellig genug war.

   Geblinkt wird durch harten Wechsel zwischen Schwarz und Weiss. Farbe braucht
   es dafuer nicht; ein Umschlag zieht den Blick staerker als jedes Rot. */

.probelauf-gross {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: .2rem .7rem;
  margin: 0 0 1rem;
  padding: 1rem;
  border-radius: var(--rund);
  text-align: center;
  animation: probelauf-blinken 1.4s steps(1, end) infinite;
}

.probelauf-gross__marke {
  font-size: 1.35rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .16em;
}

.probelauf-gross__text { font-size: .9rem; font-weight: 600; }

/* Der harte Wechsel zwischen Schwarz und Weiss — kein Ueberblenden, sondern
   ein Umschlag. Das zieht den Blick staerker als jede Farbe. */
@keyframes probelauf-blinken {
  0%, 49%   { background: var(--tinte); color: var(--auf-dunkel); }
  50%, 100% { background: var(--auf-dunkel); color: var(--tinte); box-shadow: inset 0 0 0 2px var(--tinte); }
}

/* Ein Rahmen um die ganze Seite, damit der Zustand auch mitten im Spielplan
   erkennbar bleibt. */
.ist-probelauf { box-shadow: inset 0 0 0 3px var(--tinte); }

/* Ohne Bewegung wird nicht geblinkt — der Hinweis bleibt dann dauerhaft
   schwarz stehen. Die Aussage steckt im Text, nicht im Blinken. */
@media (prefers-reduced-motion: reduce) {
  .probelauf-gross {
    animation: none;
    background: var(--tinte);
    color: var(--auf-dunkel);
  }
}

/* ── Auswertungsseite ──────────────────────────────────────────────────────
   Nicht oeffentlich, aber im selben Gewand wie der Rest — es gibt keinen
   Grund, hier ein zweites Erscheinungsbild zu pflegen. */

.feld { display: block; margin-bottom: .8rem; }

.feld__name {
  display: block;
  margin-bottom: .3rem;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--tinte-2);
}

.feld input {
  width: 100%;
  min-height: 2.8rem;
  padding: 0 .8rem;
  border: 1.5px solid var(--linie-2);
  border-radius: var(--rund-klein);
  background: var(--flaeche);
  color: var(--tinte);
  font: inherit;
}

.feld input:focus { border-color: var(--tinte); outline: none; }

.tafel-knopf--voll { width: 100%; flex: none; max-width: none; margin-top: .4rem; }

/* Saeulen in den beiden Diagrammen. Eine Reihe, eine Farbe — mehr braucht es
   nicht, wenn nur eine Groesse dargestellt wird. */
.saeule { fill: var(--tinte); }
.saeule:hover { fill: var(--tinte-2); }

.achse { fill: var(--leise); font-size: 9px; font-family: inherit; }

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

.verteilung__titel {
  margin: 0 0 .5rem;
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--leise);
}

/* Ein Balken je Zeile: Name, Spur, Anteil. Die Laenge traegt die Aussage,
   die Zahl daneben macht sie genau. */
.balken {
  display: grid;
  grid-template-columns: minmax(0, 7rem) 1fr 2.5rem;
  align-items: center;
  gap: .5rem;
  margin-bottom: .35rem;
  font-size: .8125rem;
}

.balken__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.balken__spur {
  height: .55rem;
  border-radius: 999px;
  background: var(--grund);
  overflow: hidden;
}

.balken__fuellung { display: block; height: 100%; background: var(--tinte); }

.balken__wert {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--tinte-2);
}

/* Zweiter Ton fuer gestapelte Saeulen — hell fuer die neuen Geraete, dunkel
   fuer die wiederkehrenden. Zwei Toene einer Farbe, kein zweiter Farbton. */
.saeule--hell { fill: var(--leise); }

/* Wochentag mal Stunde. Eine Kachel je Feld, die Schwaerze ist die Groesse.
   Als Tabelle gebaut, weil es eine ist — Zeilen sind Tage, Spalten Stunden. */
.raster {
  width: 100%;
  border-collapse: separate;
  border-spacing: 1px;
  table-layout: fixed;
}

.raster th {
  padding: 0;
  font-size: .625rem;
  font-weight: 600;
  color: var(--leise);
  text-align: center;
}

.raster tr > th:first-child { width: 1.6rem; text-align: right; padding-right: .3rem; }

.raster td {
  height: .95rem;
  border-radius: 2px;
  background: var(--grund);
}


/* ── Archiv: vergangene Spielzeiten ────────────────────────────────────────
   Knapper als der Spielplan. Wer hier nachsieht, sucht ein Ergebnis, keine
   Anstosszeit — Datum, Paarung, Endstand genuegen. */
.archiv__huelle { overflow-x: auto; }

.archiv {
  width: 100%;
  border-collapse: collapse;
  font-size: .875rem;
}

.archiv td {
  padding: .4rem .3rem;
  border-bottom: 1px solid var(--linie);
  vertical-align: middle;
}

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

.archiv__wann {
  white-space: nowrap;
  color: var(--leise);
  font-variant-numeric: tabular-nums;
  width: 3.6rem;
}

.archiv__paarung { line-height: 1.3; }
.archiv__verein--eigen { font-weight: 700; }
.archiv__strich { color: var(--leise); margin: 0 .3rem; }

.archiv__ergebnis { text-align: right; white-space: nowrap; width: 3.4rem; }

/* Dieselbe schwarze Plakette wie im Spielplan — ein Ergebnis ohne Feld
   darunter liest sich auf einem Handy nicht. */
.archiv__tore {
  display: inline-block;
  min-width: 2.6rem;
  padding: .15rem .35rem;
  border-radius: var(--rund-klein);
  background: var(--tinte);
  color: var(--flaeche);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

/* Unentschieden und Niederlage bleiben schwarz-weiss; der Sieg bekommt das
   volle Schwarz, der Rest tritt zurueck. Keine Farben — das ist der Verein. */
.archiv__tore--u { background: var(--tinte-2); }
.archiv__tore--n { background: transparent; color: var(--tinte-2); border: 1px solid var(--linie); }

.archiv__zahlen { margin-bottom: 1rem; }

/* ── Der zurückgelegte Spielplan (nur unter /admin) ────────────────────────
   Eine Karte, die sich absetzt: hier wartet etwas auf eine Entscheidung. */
.karte--warnung {
  border: 2px solid var(--tinte);
  background: var(--flaeche);
}

.karte--warnung h2 { margin-top: 0; }

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

.vorschlag__liste {
  margin: 0;
  padding-left: 1.1rem;
  max-height: 14rem;
  overflow-y: auto;
  font-size: .8125rem;
  line-height: 1.5;
}

.vorschlag__liste li { margin-bottom: .15rem; }

/* ── Vorschau auf den nächsten Spieltag ────────────────────────────────────
   Drei Zeilen, eine je Ausgang. Der Sieg steht oben und trägt das volle
   Schwarz — schwarz-weiß bleibt schwarz-weiß, die Rangfolge macht die
   Sättigung, nicht die Farbe. */
.vorschau {
  margin: 0 0 1.2rem;
  padding: .9rem 1rem;
  border: 1px solid var(--linie);
  border-radius: var(--rund-klein);
  background: var(--grund);
}

.vorschau__titel {
  margin: 0 0 .6rem;
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--leise);
}

.vorschau__fall {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: baseline;
  gap: .5rem;
  padding: .35rem 0;
  border-bottom: 1px solid var(--linie);
  font-size: .875rem;
}

.vorschau__fall:last-child { border-bottom: none; }

.vorschau__was { font-weight: 600; }
.vorschau__fall--n .vorschau__was { color: var(--leise); font-weight: 500; }

.vorschau__punkte {
  color: var(--leise);
  font-variant-numeric: tabular-nums;
  font-size: .8125rem;
}

.vorschau__platz {
  min-width: 6.2rem;
  text-align: right;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.vorschau__fall--n .vorschau__platz { font-weight: 600; color: var(--tinte-2); }

@media (max-width: 26rem) {
  .vorschau__fall { grid-template-columns: 1fr auto; }
  .vorschau__punkte { display: none; }
  .vorschau__platz { min-width: 0; }
}

/* Wird das Guthaben knapp, soll der Hinweis auffallen — ohne Farbe, die es
   in diesem Projekt nicht gibt: fetter Text und eine Kante. */
.ist-knapp {
  font-weight: 600;
  color: var(--tinte);
  border-left: 3px solid var(--tinte);
  padding-left: .6rem;
}

/* ── Live-Tabelle ──────────────────────────────────────────────────────────
   Nur sichtbar, solange Partien laufen. Kein Rot, kein Grün: der Punkt
   pulsiert, und das genügt als Signal. */
.tabelle-live {
  margin: 0 0 .8rem;
  padding: .55rem .8rem;
  border-radius: var(--rund-klein);
  background: var(--tinte-band);
  color: var(--auf-dunkel);
  font-size: .8125rem;
  font-weight: 600;
}

.tabelle-live.ist-live::before {
  content: "";
  display: inline-block;
  width: .5rem;
  height: .5rem;
  margin-right: .5rem;
  border-radius: 50%;
  background: var(--auf-dunkel);
  animation: pulsieren 1.6s ease-in-out infinite;
}

@keyframes pulsieren {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .35; transform: scale(.7); }
}

/* Der Zwischenstand neben dem Vereinsnamen. */
.tabelle__imspiel {
  display: block;
  font-size: .6875rem;
  color: var(--leise);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tabelle__imspiel.ist-live { color: var(--tinte); font-weight: 600; }

/* Die Zeile einer Mannschaft, die gerade spielt. */
.tabelle__laeuft { background: var(--grund); }

@media (prefers-reduced-motion: reduce) {
  .tabelle-live.ist-live::before { animation: none; }
}

/* ── Kennzeichnung des Build-Systems ───────────────────────────────────────
   Ein Rahmen, der nichts kostet.

   Auffällig genug, um es nie zu übersehen — und so gebaut, dass sich die
   Ansicht gegenüber dem Live-System um KEINEN Pixel unterscheidet: kein
   Balken, der eine Zeile Höhe frisst, kein Streifen, der Breite nimmt.
   `position:fixed` mit `pointer-events:none` liegt über allem, belegt aber
   keinen Platz und fängt keinen Klick ab. Build und Live lassen sich
   übereinanderlegen und decken sich; nur der Rand ist rot.

   Rahmen und Fähnchen tragen dasselbe Rot: Sie gehören zusammen und sind
   eine Kennzeichnung, nicht zwei.

   Die Farben brechen bewusst mit dem Schwarz-Weiß dieser Seite. Das hier ist
   keine Gestaltung, sondern eine Warnung — und sie erscheint nie live.
   Beides steckt in stil.css, aber ohne `build.js` wird nichts davon erzeugt. */
#systemRahmen {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  border: 4px solid #c2110c;
  /* Der Schein nach innen setzt den Rahmen vom Inhalt ab, ohne eine zweite
     Linie zu ziehen — auf hellem wie auf dunklem Grund. */
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .25);
}

#systemFahne {
  position: fixed;
  top: 50%;
  right: 0;
  z-index: 9999;
  transform: translateY(-50%);
  pointer-events: auto;
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  writing-mode: vertical-rl;
  text-orientation: mixed;
  background: #c2110c;
  color: #fff;
  font: 700 10px/1 inherit;
  letter-spacing: .16em;
  text-transform: uppercase;
  padding: 12px 4px 12px 3px;
  border-radius: 3px 0 0 3px;
  box-shadow: -1px 0 6px rgba(0, 0, 0, .28);
  transition: box-shadow .15s ease;
}

#systemFahne[data-seite="links"] {
  border-radius: 0 3px 3px 0;
  box-shadow: 1px 0 6px rgba(0, 0, 0, .28);
}

#systemFahne:active { cursor: grabbing; box-shadow: 0 0 0 3px rgba(194, 17, 12, .3); }

/* Auf dem Papier hat beides nichts zu suchen. */
@media print { #systemRahmen, #systemFahne { display: none !important; } }

/* Ein Spiel, das die Vereinsseite nicht führt und das aus dem Ligaspielplan
   ergänzt wurde. Kein Ausrufezeichen — nur eine gestrichelte Kante, die man
   sieht, wenn man darauf achtet. Der Termin selbst ist so gültig wie jeder
   andere; er steht bloß woanders. */
.spiel--ergaenzt { border-left: 2px dashed var(--linie-2); padding-left: .5rem; }

/* ── Zahlen zur Liga ───────────────────────────────────────────────────────
   Mehrere kleine Tabellen und Balken in einer Tafel. Sie teilen sich das
   Aussehen mit dem Archiv (.archiv) — es ist dieselbe Art von Liste, und
   zwei Gestaltungen für dieselbe Sache wären eine zu viel. */
.liga-huelle { overflow-x: auto; margin-bottom: 1.2rem; }

.liga-zwei {
  display: grid;
  gap: 1.2rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.liga__zahl, .liga__punkte {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.liga__punkte { font-weight: 700; }
.liga__verlauf { text-align: right; white-space: nowrap; }

/* ── Die Formtabelle auf sehr schmalen Geraeten ─────────────────────────────
   Sie ist die einzige der drei Ligatabellen, die ueberlaeuft: Heim und
   Auswaerts haben fuenf Spalten, sie hat sechs — die letzte ist der Verlauf,
   und der WAECHST. Bis zum fuenften Spieltag stehen dort drei Zeichen, danach
   dauerhaft fuenf; gemessen auf 375 Pixeln (iPhone 13 mini) sind das 31 bzw.
   57 Pixel zu viel.

   Ab 390 Pixeln (iPhone 12/13/14 und alles darueber) passt sie, deshalb
   greift hier nichts — eine Tabelle, die auf dem breiteren Geraet Spalten
   verliert, waere schlechter als eine, die dort passt.

   Was weicht, ist das TORVERHAELTNIS. Die Formtabelle beantwortet „wer laeuft
   gerade" — dafuer tragen Platz, Verein, Punkte und der Verlauf die Aussage.
   Die Tore der letzten fuenf Spiele sind Beiwerk, und sie sind die breiteste
   der schmalen Spalten. Die Spielzahl bleibt: Am Saisonanfang stehen dort
   weniger als fuenf, und dann erklaert sie, warum jemand wenig Punkte hat.

   Die Zeichen ruecken zusaetzlich zusammen. Sie sind nicht anzutippen — der
   Verlauf ist zum Ansehen da —, also kostet das keine Bedienbarkeit. */
@media (max-width: 23.75rem) {
  #liga-form .liga__tore { display: none; }

  #liga-form .form-zeichen {
    width: 1.35rem;
    height: 1.35rem;
    font-size: .72rem;
    border-radius: .32rem;
  }

  #liga-form td { padding-left: .2rem; padding-right: .2rem; }
}

/* Die eigene Mannschaft im Balkendiagramm des Restprogramms. */
.balken--eigen .balken__name { font-weight: 700; }
.balken--eigen .balken__fuellung { background: var(--tinte); }
.balken:not(.balken--eigen) .balken__fuellung { background: var(--linie-2); }

/* ── Direkter Vergleich ────────────────────────────────────────────────────
   Eine Zeile je Duell: wann, wer gegen wen, wie es ausging. */
.vergleich__duelle { margin: .8rem 0; }

.vergleich__zeile {
  display: grid;
  grid-template-columns: 4.5rem 1fr auto;
  gap: .5rem;
  align-items: baseline;
  padding: .3rem 0;
  border-bottom: 1px solid var(--linie);
  font-size: .8125rem;
}

.vergleich__zeile:last-child { border-bottom: none; }
.vergleich__wann { color: var(--leise); font-variant-numeric: tabular-nums; }
.vergleich__paarung { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.vergleich__stand {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  padding: .1rem .4rem;
  border-radius: var(--rund-klein);
}

/* Sieg schwarz, Unentschieden grau, Niederlage nur umrandet — dieselbe
   Abstufung wie im Archiv, ohne Farbe. */
.vergleich__stand--s { background: var(--tinte); color: var(--flaeche); }
.vergleich__stand--u { background: var(--tinte-2); color: var(--flaeche); }
.vergleich__stand--n { border: 1px solid var(--linie-2); color: var(--tinte-2); }

/* ── Abschnitte im Reiter „Zahlen zur Liga" ────────────────────────────────
   Fünf eigenständige Auswertungen in einer Tafel. Ohne Trennung lesen sie
   sich als eine lange Wurst — beim Scrollen weiß man dann nicht mehr, welche
   Zahl zu welcher Frage gehört.

   Die Trennung macht eine Linie ÜBER dem Abschnitt, nicht unter ihm: So
   gehört sie sichtbar zum folgenden Block, und der letzte Abschnitt endet
   ohne überflüssigen Strich. Der erste bekommt keine — davor steht nichts. */
/* Die Abstände von früher, als die Blöcke offen untereinander standen. Als
   Ausklappliste ergaben sie riesige Lücken zwischen den Zeilen — die Trennung
   leistet jetzt die Haarlinie am Kopf, nicht der Zwischenraum. */
.liga-block + .liga-block { margin-top: 0; padding-top: 0; border-top: 0; }

.liga-block__titel {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: -.01em;
}

/* Die Frage unter der Überschrift sagt in fünf Wörtern, wozu die Zahlen gut
   sind. Wer sie liest, muss die Tabelle darunter nicht erst entschlüsseln. */
.liga-block__frage {
  margin: 0 0 .6rem;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--leise);
}

.liga-block .klein { margin: 0 0 .8rem; }
.liga-block .stand { margin-bottom: .8rem; }

/* Auf schmalen Geräten darf die Trennung enger sein — dort ist Platz das
   knappere Gut als Übersicht. */
@media (max-width: 30rem) {
  .liga-block + .liga-block { margin-top: 1.4rem; padding-top: 1.1rem; }
}

/* ── Vorschau: Kurzfassung und Aufklapper ──────────────────────────────────
   Der Erklärtext stand voll ausgeschrieben unter den drei Zeilen und nahm auf
   dem Handy den halben Bildschirm. Weglassen ging nicht — er sagt, worauf die
   Zahlen beruhen, und das gehört dazu. Also: eine Zeile sichtbar, der Rest
   zum Aufklappen. */
.vorschau__kurz {
  margin: .6rem 0 0;
  font-size: .75rem;
  color: var(--leise);
  font-variant-numeric: tabular-nums;
}

/* Der allgemeine details-Stil setzt eine Linie und viel Polsterung — das
   ist für die Anleitung richtig, hier aber genau das Gegenteil des Zwecks. */
.vorschau__mehr {
  margin-top: .3rem;
  border-top: 0;
  padding: 0;
}

.vorschau__mehr > summary {
  cursor: pointer;
  font-size: .75rem;
  font-weight: 600;
  color: var(--tinte-2);
  /* Der Dreieckszeiger des Browsers sitzt je nach Gerät anders; ein eigener
     ist an dieser Stelle verlässlicher. */
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: .3rem;
}

.vorschau__mehr > summary::-webkit-details-marker { display: none; }

.vorschau__mehr > summary::before {
  content: "▸";
  font-size: var(--pfeil);
  transition: transform .15s ease;
}

.vorschau__mehr[open] > summary::before { transform: rotate(90deg); }
.vorschau__mehr > summary:hover { color: var(--tinte); }
.vorschau__mehr .klein { margin: .4rem 0 0; }

@media (prefers-reduced-motion: reduce) {
  .vorschau__mehr > summary::before { transition: none; }
}

/* ── Die Rechnung hinter der Vorschau ──────────────────────────────────────
   Aufgeklappt steht hier, wie die Zahlen zustande kommen — und vor allem die
   Verteilung: dass „Platz 4–7" eben nicht heißt, jeder dieser Plätze sei
   gleich oft dabei. Das ist die Zahl, die den Unterschied macht. */
.vorschau__methode { padding-top: .2rem; }

.vorschau__vert { margin: .8rem 0; }

.vorschau__vert-titel {
  margin: 0 0 .35rem;
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--leise);
}

/* Etwas schmaler als die Balken im Ligareiter — hier stehen mehrere Gruppen
   untereinander, und die Zeilen sollen als Gruppe lesbar bleiben. */
.vorschau__vert .balken {
  grid-template-columns: 4.6rem 1fr 2.6rem;
  margin-bottom: .25rem;
  font-size: .75rem;
}

.vorschau__vert .balken__spur { height: .45rem; }

/* ── Einladung auf den Homebildschirm ──────────────────────────────────────
   Ein Band am unteren Rand, kein Kasten mitten im Bild: Es soll ansprechen,
   ohne den Spielplan zu verdecken — und es fährt sanft ein statt aufzuploppen.

   Unten und nicht oben, weil dort auf dem Handy der Daumen liegt und weil
   oben schon der schwarze Block mit dem nächsten Spiel steht. */
.einladung {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  padding: .9rem 1rem calc(.9rem + env(safe-area-inset-bottom, 0px));
  background: var(--flaeche);
  border-top: 2px solid var(--tinte);
  box-shadow: 0 -8px 24px rgba(0, 0, 0, .12);
  transform: translateY(110%);
  transition: transform .32s cubic-bezier(.22, .8, .28, 1);
}

.einladung.ist-da { transform: translateY(0); }

.einladung__inhalt {
  display: flex;
  align-items: flex-start;
  gap: .7rem;
  max-width: 34rem;
  margin: 0 auto;
}

.einladung__symbol { border-radius: var(--rund-klein); flex: 0 0 auto; }
.einladung__text { flex: 1 1 auto; min-width: 0; }

.einladung__titel { display: block; font-size: .95rem; line-height: 1.25; }

.einladung__zeile {
  display: block;
  margin-top: .15rem;
  font-size: .8125rem;
  color: var(--tinte-2);
  line-height: 1.35;
}

/* Das Kreuz zum Wegtippen: groß genug für einen Daumen (44 px sind die
   Untergrenze, unter der auf einem Touchgerät nichts mehr sicher trifft). */
.einladung__schliessen {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  margin: -.6rem -.6rem 0 0;
  border: 0;
  background: none;
  color: var(--leise);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.einladung__schliessen:hover { color: var(--tinte); }

.einladung__knoepfe {
  max-width: 34rem;
  margin: .7rem auto 0;
  display: flex;
}

.einladung__anleitung {
  max-width: 34rem;
  margin: .8rem auto 0;
  padding-left: 1.3rem;
  font-size: .8125rem;
  color: var(--tinte-2);
  line-height: 1.5;
}

.einladung__anleitung li { margin-bottom: .35rem; }
.einladung__zeichen { font-size: 1.05em; }

/* Damit das Band nichts verdeckt, endet die Seite etwas höher. */
body:has(.einladung:not([hidden])) .fuss { padding-bottom: 9rem; }

@media (prefers-reduced-motion: reduce) {
  .einladung { transition: none; }
}

/* Kurze Rückmeldung, wenn ein Gerät als eigenes markiert wurde. Verschwindet
   nach ein paar Sekunden von selbst — es ist eine Bestätigung, kein Zustand. */
.eigen-hinweis {
  position: fixed;
  top: calc(.6rem + env(safe-area-inset-top, 0px));
  left: 50%;
  transform: translateX(-50%);
  z-index: 80;
  max-width: min(30rem, calc(100vw - 2rem));
  padding: .6rem .9rem;
  border-radius: var(--rund-klein);
  background: var(--tinte-flaeche);
  color: var(--auf-dunkel);
  font-size: .8125rem;
  font-weight: 600;
  line-height: 1.35;
  box-shadow: var(--schatten-hoch);
}


/* Der Fußtext ist zugleich der versteckte Weg, ein Gerät als eigenes zu
   markieren (fünfmal tippen). Er soll deshalb nicht wie ein Knopf aussehen,
   aber auf einem Handy zuverlässig zu treffen sein — und beim Mehrfachtippen
   darf nichts blau markiert werden. */
.fuss p {
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* ── „Weitere Spiele anzeigen" ─────────────────────────────────────────────
   Ein Knopf über und unter dem Ausschnitt. Er sagt in Klammern, wie viele
   noch kommen — beim endlosen Nachladen beim Scrollen weiß man das nie, und
   genau deshalb ist es hier ein Knopf und kein Scroll-Ereignis. */
/* Der Knopf war ein gestrichelter Rahmen über die volle Breite — er sah aus
   wie ein Platzhalter und ging zwischen den Zeilen unter. Jetzt eine dunkle
   Fläche, mittig, nur so breit wie ihr Text.

   NICHT über die volle Breite: Ein Knopf, der so breit ist wie der Inhalt,
   liest sich als Abschluss des Blocks. Einer, der in der Mitte steht, liest
   sich als Angebot — und darum geht es hier. */
.mehr-knopf {
  display: block;
  margin: .9rem auto;
  padding: .6rem 1.4rem;
  border: 0;
  border-radius: var(--rund-pille);
  background: var(--tinte-flaeche);
  color: var(--auf-dunkel);
  box-shadow: var(--kante-licht), var(--schatten-karte);
  font: inherit;
  font-size: .8125rem;
  font-weight: 600;
  cursor: pointer;
  transition: transform .15s ease, box-shadow .15s ease;
}
.mehr-knopf:hover { box-shadow: var(--kante-licht), var(--schatten-hoch); }
.mehr-knopf:focus-visible { outline: 2px solid var(--tinte); outline-offset: 2px; }
.mehr-knopf:active { transform: translateY(1px); }

@media (prefers-reduced-motion: reduce) { .mehr-knopf { transition: none; } }

/* Das Kleingedruckte zum Abo — drei Absätze, die ausgeschrieben den halben
   Bildschirm füllten. Wichtig sind sie trotzdem: An der Warnung vor dem
   einmaligen Import scheitern die meisten Abos. Deshalb aufklappbar statt
   weggelassen. Dieselbe schlanke Form wie bei der Vorschau. */
.abo__mehr {
  margin-top: .9rem;
  border-top: 0;
  padding: 0;
}

.abo__mehr > summary {
  cursor: pointer;
  font-size: .8125rem;
  font-weight: 600;
  color: var(--tinte-2);
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: .3rem;
}

.abo__mehr > summary::-webkit-details-marker { display: none; }

.abo__mehr > summary::before {
  content: "▸";
  font-size: var(--pfeil);
  transition: transform .15s ease;
}

.abo__mehr[open] > summary::before { transform: rotate(90deg); }
.abo__mehr > summary:hover { color: var(--tinte); }
.abo__mehr .klein { margin: .6rem 0 0; }

/* Der Zeitstempel des letzten Abgleichs: gehört dazu, aber ganz leise. */
.abo__stand { margin-top: .9rem; color: var(--leise); }

@media (prefers-reduced-motion: reduce) {
  .abo__mehr > summary::before { transition: none; }
}

/* ── Blocküberschriften im Spielplan ───────────────────────────────────────
   „Kommende Spiele" und „Vergangene Spiele". Sie tragen die Umkehrung der
   Reihenfolge: Ohne sie stünde eine Liste da, die scheinbar wahllos springt.

   Sie sitzen INNERHALB der Spielplan-Karte und brauchen deshalb dieselbe
   Polsterung wie alles darin — mit bloßem Außenabstand klebten sie am Rand,
   während die Zeilen darunter eingerückt waren.

   Drei Ebenen, klar unterschieden: Blocktitel (schwarz, groß) über
   Monatstrenner (grau, gesperrt) über Spielzeile. */
/* Als schwarzes Band, nicht als bloße Überschrift: Auf weißem Grund gingen
   die beiden Zeilen zwischen den Monatsbändern unter, obwohl sie die
   wichtigere Gliederung tragen. Jetzt sind die drei Ebenen auf einen Blick
   auseinanderzuhalten — schwarzes Band, graues Band, weiße Zeile. */
.block-titel {
  margin: 0;
  padding: .8rem clamp(1rem, 4vw, 1.5rem);
  background: var(--tinte-band);
  color: var(--auf-dunkel);
  font-size: .82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
  border-top: 0;
  box-shadow: var(--kante-licht);
}

.block-titel + * { border-top: 0; }

/* Zwischen den Blöcken bleibt etwas Luft — zwei schwarze Bänder direkt
   übereinander (bei leerem Block) sähen aus wie ein Fehler. */
.block-titel:not(:first-child) { margin-top: .35rem; }

/* Die vergangenen Spiele werden NICHT zusätzlich eingefärbt: `.spiel--vorbei`
   tut das bereits, und ein zweiter Grauton auf demselben Block machte die
   Monatstrenner darin unsichtbar. Die kräftige Kante über der Überschrift
   trägt die Trennung allein. */

/* Der Weg zurück. Zurückhaltender als „mehr anzeigen": Aufklappen ist das,
   was man will, Zuklappen nur das, was man danach manchmal braucht. */
/* Der Rückweg ist leiser als das Angebot: keine Fläche, nur ein Rand. Wer
   zuklappt, sucht den Knopf gezielt — er muss nicht auffallen. */
.mehr-knopf--zu {
  background: none;
  border: 1px solid var(--linie-2);
  box-shadow: none;
  color: var(--leise);
  font-weight: 500;
}
.mehr-knopf--zu:hover { color: var(--tinte-2); border-color: var(--tinte-2); box-shadow: none; }


/* ── Feinschliff der Modernisierung ────────────────────────────────────────
   Die Änderungen oben tragen das Meiste. Was hier folgt, sind die Kleinig-
   keiten, an denen man den Unterschied zwischen „aufgeräumt" und „gestaltet"
   festmacht. */

/* Überschriften enger gesetzt. Bei großen Graden wirkt der normale Abstand
   zwischen den Buchstaben zu locker — je größer die Schrift, desto enger
   darf sie stehen. */
h1 { letter-spacing: -.03em; }
h2 { letter-spacing: -.02em; }

/* Die schwarzen Plaketten (Ergebnisse, Spielstände) bekommen denselben
   Verlauf wie die großen Flächen — sonst stünde ein flaches Schwarz neben
   einem tiefen, und genau das fällt auf. */
.paarung__tor,
.archiv__tore,
.vergleich__stand--s,
.held__tore { background-image: var(--tinte-band); }

/* Übergänge: kurz genug, um nicht zu bremsen, lang genug, um wahrgenommen zu
   werden. Ohne sie wirkt jede Zustandsänderung wie ein Schnitt. */
.tafel-knopf,
.filter__knopf,
.mehr-knopf,
.abo-knopf { transition: background-color .18s ease, border-color .18s ease,
             color .18s ease, box-shadow .18s ease; }

/* Die Zeilen im Spielplan heben sich beim Überfahren leicht ab — auf dem
   Rechner eine Rückmeldung, auf dem Handy unsichtbar und damit kostenlos. */
@media (hover: hover) {
  .spiel:hover { background: var(--grund); }
  .spiel--vorbei:hover { background: var(--linie); }
}

/* Der Fuß bekommt Luft und eine feine Trennung statt einer Linie quer über
   die ganze Breite. */
.fuss { border-top: 1px solid var(--linie); padding-top: 1.5rem; margin-top: 2rem; }

@media (prefers-reduced-motion: reduce) {
  .tafel-knopf, .filter__knopf, .mehr-knopf, .abo-knopf { transition: none; }
}


/* ── Die Reiterleiste ────────────────────────────────────────────────────────
   Fest am unteren Rand, weil der Daumen dort ist. Das ist der ganze
   Unterschied zwischen einer Website-Navigation (oben) und einer
   App-Navigation (unten): Ein Telefon hält man in einer Hand.

   Aus Milchglas, damit der Inhalt darunter durchscheint statt abgeschnitten
   zu wirken — man sieht, dass die Seite weitergeht. */
.reiter {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  display: flex;
  background: var(--glas);
  -webkit-backdrop-filter: saturate(150%) blur(24px);
  backdrop-filter: saturate(150%) blur(24px);
  border-top: 1px solid var(--linie);
  /* Ohne den Zuschlag läge die Leiste auf iPhones unter dem Streifen zum
     Wischen — der unterste Reiter wäre schlicht nicht zu treffen. */
  padding-bottom: env(safe-area-inset-bottom, 0);
}

.reiter__knopf {
  flex: 1 1 0;
  min-width: 0;
  appearance: none;
  border: 0;
  background: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .15rem;
  padding: .55rem .2rem .5rem;
  font: inherit;
  color: var(--leise);
  cursor: pointer;
  /* Die Fläche zum Tippen misst mit der Polsterung rund 56 px — unter 44 px
     trifft man auf einem Telefon nicht mehr zuverlässig. */
  min-height: 3.5rem;
  transition: color .15s ease;
}

/* Die Reiter tragen jetzt gezeichnete Symbole statt Schriftzeichen. Feste
   Maße statt Schriftgröße: Ein SVG richtet sich nicht nach der Zeilenhöhe,
   und Schriftzeichen sahen je nach Gerät verschieden groß aus. */
.reiter__zeichen {
  width: 1.35rem;
  height: 1.35rem;
  display: block;
}

.reiter__wort {
  font-size: .66rem;
  font-weight: 600;
  letter-spacing: .04em;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Der gewählte Reiter trägt Farbe UND einen Strich. Farbe allein genügt
   nicht: Auf einer schwarz-weißen Seite ist „dunkler" kein verlässliches
   Signal, und wer Farben schlecht unterscheidet, sieht gar keins. */
.reiter__knopf[aria-current="page"] {
  color: var(--tinte);
  position: relative;
}

.reiter__knopf[aria-current="page"]::before {
  content: "";
  position: absolute;
  top: 0;
  left: 22%;
  right: 22%;
  height: 2px;
  background: var(--tinte);
}

.reiter__knopf:focus-visible { outline: 2px solid var(--tinte); outline-offset: -3px; }

/* Die Seite braucht unten Platz, sonst verdeckt die Leiste die letzte Zeile. */
body { padding-bottom: 4.5rem; }

/* Ohne backdrop-filter würde der Inhalt durch die halbdurchsichtige Leiste
   hindurchscheinen — unlesbar. Dort volldeckend. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .reiter { background: var(--flaeche); }
}

@media (prefers-reduced-motion: reduce) {
  .reiter__knopf { transition: none; }
}

/* Die alten Knöpfe mitten auf der Seite haben ausgedient — die Leiste tut
   dasselbe, nur erreichbar. */
.tafel-knoepfe { display: none; }

/* Die Ansichtenlogik benutzt bewusst eine eigene Klasse statt `hidden`:
   `hidden` steht an manchen Elementen schon aus inhaltlichen Gründen, und
   beides zu vermischen blendete Leeres ein. */
.ansicht-aus { display: none !important; }

/* ── Zeilenumbruch ───────────────────────────────────────────────────────────
   `balance` verteilt die Zeilen einer Überschrift gleichmäßig, statt die
   letzte mit einem einzelnen Wort stehen zu lassen — „… die letzten 8" /
   „Duelle" war genau dieser Fall. `pretty` tut dasselbe zurückhaltender für
   Fließtext.

   Beides sind reine Verbesserungen: Wer sie nicht kennt, bricht wie bisher.
   Die Zahlen hält deshalb zusätzlich ein geschütztes Leerzeichen zusammen
   (`bindeZahlen` im Skript) — das wirkt überall. */
h1, h2, h3, .tafel-titel, .stand__wert, .vergleich__titel {
  text-wrap: balance;
}

p, dd, .klein, .held__ort {
  text-wrap: pretty;
}

/* ── Bewegung im Grund ───────────────────────────────────────────────────────
   Der Grund lag bisher völlig still: `position: fixed`, ein Lichtfeld, das
   sich beim Scrollen um kein Pixel rührt. Genau das lässt eine lange Seite
   tot wirken — man scrollt an einer Wand vorbei.

   Jetzt wandert das Licht mit. Die Bewegung ist an die Scrollposition
   gekoppelt, nicht an die Uhr: Sie folgt dem Finger, statt von selbst zu
   laufen. Das ist der Unterschied zwischen „lebendig" und „zappelig" —
   die Seite bewegt sich nur, wenn der Benutzer sie bewegt.

   `animation-timeline: scroll()` braucht KEIN JavaScript und läuft im
   Compositor, also nicht im Hauptthread. Ein Scroll-Listener, der bei jedem
   Pixel eine Eigenschaft setzt, wäre der klassische Weg — und der klassische
   Grund, warum Seiten beim Scrollen haken.

   Wer es nicht kann (ältere Browser), bekommt den ruhenden Grund von vorher.
   Nichts fehlt, es bewegt sich nur nichts — genau richtig für eine
   Verbesserung, die niemand vermisst, wenn sie ausbleibt. */

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: scroll()) {
    body::before {
      /* Etwas größer als der Bildschirm, sonst liefe beim Verschieben eine
         Kante ins Bild. */
      animation: grundWandert linear both;
      animation-timeline: scroll(root block);
      will-change: transform;
    }

    /* Sehr wenig Weg: 5 % Versatz über die ganze Seitenhöhe. Man bemerkt es
       nicht als Bewegung, sondern als Tiefe — der Grund liegt hinter dem
       Inhalt und geht deshalb langsamer mit. Mehr wirkt sofort billig. */
    @keyframes grundWandert {
      from { transform: scale(1.35) translate3d(-6%, 9%, 0) rotate(-4deg); }
      to   { transform: scale(1.35) translate3d(6%, -9%, 0) rotate(4deg); }
    }

    /* Das Korn wandert gegenläufig und langsamer. Dadurch verschiebt es sich
       gegen den Verlauf, statt mit ihm zu kleben — die Fläche bekommt eine
       Textur, die sich beim Scrollen minimal verändert. */
    body::after {
      animation: kornWandert linear both;
      animation-timeline: scroll(root block);
    }

    @keyframes kornWandert {
      from { transform: scale(1.1) translate3d(0, -4%, 0); }
      to   { transform: scale(1.1) translate3d(0, 4%, 0); }
    }
  }
}


/* ── Die Zeilen kommen herein ────────────────────────────────────────────────
   Beim Scrollen hebt sich jede Spielzeile beim Eintreten kurz an und wird
   voll deckend. Das ist der zweite Teil der Belebung: Der Grund bewegt sich,
   und der Inhalt kommt ihm entgegen.

   DIE WICHTIGE VORSICHTSMASSNAHME steht in `from`: Die Zeile beginnt bei
   35 % Deckkraft, nicht bei null. Griffe die Zeitachse aus irgendeinem Grund
   nicht — falscher Scroll-Container, künftige Änderung am Aufbau —, bliebe
   das Element im Anfangszustand hängen. Bei null wäre der halbe Spielplan
   unsichtbar, und niemand käme darauf, dass eine Animation schuld ist.
   Bei 35 % ist es blass. Blass ist ein Schönheitsfehler, unsichtbar ist
   Datenverlust. */

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .spiel {
      animation: zeileHerein linear both;
      animation-timeline: view();
      /* Nur beim Eintritt von unten, und schnell abgeschlossen: Wer scrollt,
         soll lesen können, nicht auf das Ende einer Animation warten. */
      animation-range: entry 0% entry 42%;
    }

    @keyframes zeileHerein {
      from { opacity: .35; transform: translate3d(0, 26px, 0) scale(.97); }
      to   { opacity: 1;   transform: none; }
    }
  }
}


/* ── Der wandernde Lichtschein ───────────────────────────────────────────────
   Die Scroll-Bewegung allein war nicht zu bemerken — sie setzt voraus, dass
   jemand scrollt, und auf einer kurzen Ansicht scrollt niemand. Deshalb eine
   zweite Ebene, die von selbst läuft: ein großer, sehr weicher Lichtfleck,
   der in gut einer halben Minute einmal quer wandert.

   Langsam genug, dass man ihm nicht zusieht — schnell genug, dass die Fläche
   nicht steht. Wer die Seite eine Weile offen hat, sieht sie sich verändern,
   ohne sagen zu können, woran es liegt. Genau das ist die Absicht.

   Ein eigenes Element, kein Pseudoelement von `body`: Dessen `::before` hängt
   an der Scrollposition, `::after` trägt das Korn. Zwei Animationen auf
   demselben `transform` schließen einander aus. */
.stimmung {
  position: fixed;
  /* Über den Rand hinaus, damit beim Wandern keine Kante ins Bild läuft. */
  inset: -25%;
  z-index: -2;
  pointer-events: none;
  background: radial-gradient(46% 40% at 50% 50%,
    var(--schein) 0%, transparent 76%);
}

/* Auf hellem Grund ein SCHATTEN, nachts ein Schein: Der wandernde Fleck
   muss sich von seiner Umgebung absetzen können, und nach oben ist bei
   Weiß Schluss. Ein weißer Schein auf fast weißem Grund bewegt nichts —
   das war der Grund, warum von der ersten Fassung nichts zu sehen war. */
:root { --schein: rgba(28, 28, 45, .068); }
:root[data-farbe="dunkel"] { --schein: rgba(150, 156, 200, .153); }
@media (prefers-color-scheme: dark) {
  :root:not([data-farbe="hell"]) { --schein: rgba(150, 156, 200, .153); }
}

@media (prefers-reduced-motion: no-preference) {
  .stimmung { animation: scheinWandert 22s ease-in-out infinite alternate; }

  @keyframes scheinWandert {
    from { transform: translate3d(-22%, -16%, 0) scale(1); }
    to   { transform: translate3d(22%, 18%, 0) scale(1.4); }
  }
}

/* ── Der Schimmer über dem nächsten Spiel ────────────────────────────────────
   Ein schmaler heller Streifen zieht in großen Abständen einmal quer über den
   Held-Block. Auf einer großen einfarbigen Fläche ist das der Effekt, der am
   deutlichsten wirkt — und der einzige Ort, an dem er hier hingehört: Der
   Block ist die eine Stelle, auf die der Blick ohnehin fallen soll.

   Die lange Pause im Zeitplan (55 % des Zyklus steht er still) ist wichtig.
   Ein durchlaufender Schimmer wirkt nach zwei Runden wie ein Ladebalken. */
.held::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  /* Flache Flanken: Der Verlauf braucht 28 Prozentpunkte, um von nichts auf
     voll zu kommen, statt 12. Eine kurze Strecke ergibt eine sichtbare
     Kante — und genau die fiel als „hart" auf, nicht die Helligkeit allein. */
  background: linear-gradient(102deg,
    transparent 22%, var(--schimmer) 50%, transparent 78%);
}

:root { --schimmer: rgba(255, 255, 255, .17); }
:root[data-farbe="dunkel"] { --schimmer: rgba(0, 0, 0, .065); }
@media (prefers-color-scheme: dark) {
  :root:not([data-farbe="hell"]) { --schimmer: rgba(0, 0, 0, .065); }
}

/* Der Inhalt muss über dem Schimmer liegen, sonst zieht er über die Schrift.

   DAS `:not(.jubel)` IST KEIN SCHNOERKEL. Ohne die Ausnahme traf `position:
   relative` auch das jubelnde Männchen — hoehere Spezifitaet als `.jubel` und
   weiter unten, also gewinnt es. Das Männchen fiel damit aus seiner Ecke oben
   rechts in den Textfluss und landete unten links, ohne dass an ihm selbst
   etwas geaendert worden waere. Wer hier weitere absolut gesetzte Kinder
   einhaengt, muss sie ebenso ausnehmen. */
.held > *:not(.jubel) { position: relative; z-index: 2; }

@media (prefers-reduced-motion: no-preference) {
  .held::before { animation: schimmerZieht 20s linear infinite; }

  @keyframes schimmerZieht {
    0%, 30% { transform: translate3d(-115%, 0, 0); }
    100%    { transform: translate3d(115%, 0, 0); }
  }
}


/* Derselbe Schimmer über den Bändern. Der Held allein wäre ein Einzelstück;
   so zieht sich die Bewegung durch die Seite. Versetzt, damit nicht alles
   im Gleichschritt blinkt — das sähe nach Defekt aus, nicht nach Leben. */
.block-titel { position: relative; overflow: clip; }

.block-titel::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(102deg,
    transparent 22%, var(--schimmer) 50%, transparent 78%);
}

@media (prefers-reduced-motion: no-preference) {
  .block-titel::after { animation: schimmerZieht 20s linear infinite; }
  .block-titel:nth-of-type(2)::after { animation-delay: 7s; }
}


/* ── Der Farbknopf ───────────────────────────────────────────────────────────
   Oben rechts, auf einer Linie mit der Augenbraue — dort, wo bei fast jeder
   App die Einstellungen sitzen, und weit genug vom Titel, um ihn nicht zu
   stören.

   EIN Knopf, der durchschaltet, statt drei nebeneinander: Die Einstellung
   wird einmal getroffen, nicht ständig, und ein Symbol braucht keine
   Beschriftung, solange es den geltenden Zustand zeigt. */
.kopf__zeile {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

/* Die Augenbraue trug den Abstand nach unten. Jetzt sitzt sie in einer Zeile
   mit dem Knopf, und der Abstand gehört an die Zeile. */
.kopf__zeile .augenbraue { margin-bottom: 0; }
.kopf__zeile { margin-bottom: .5rem; }

.farbknopf {
  flex: 0 0 auto;
  appearance: none;
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  /* Deutlich größer als das Symbol: 40 px ist die untere Grenze dessen, was
     sich auf einem Telefon zuverlässig treffen lässt. */
  padding: 0;
  border: 1px solid var(--linie);
  border-radius: var(--rund-pille);
  background: var(--glas);
  -webkit-backdrop-filter: var(--glas-blur);
  backdrop-filter: var(--glas-blur);
  color: var(--leise);
  cursor: pointer;
  /* Ein Stück nach oben, damit er mit der Oberkante der Augenbraue fluchtet
     statt mit ihrer Schriftlinie. */
  margin-top: -.45rem;
  transition: color .18s ease, border-color .18s ease;
}

.farbknopf:hover { color: var(--tinte); border-color: var(--linie-2); }
.farbknopf:focus-visible { outline: 2px solid var(--tinte); outline-offset: 2px; }

.farbknopf__bild {
  /* Alle drei liegen übereinander im selben Rasterfeld — so springt der Knopf
     beim Umschalten nicht, und es muss nichts nachgeladen werden. */
  grid-area: 1 / 1;
  width: 1.15rem;
  height: 1.15rem;
  display: none;
}

.farbknopf__bild.ist-da { display: block; }

/* Bis das Skript läuft, zeigt der Knopf das Symbol für „System" — sonst
   stünde er einen Augenblick leer da. */
.farbknopf__bild[data-fuer="system"] { display: block; }
.farbknopf__bild.ist-da ~ .farbknopf__bild[data-fuer="system"]:not(.ist-da),
.farbknopf:has(.ist-da) .farbknopf__bild[data-fuer="system"]:not(.ist-da) { display: none; }

@media (prefers-reduced-motion: reduce) {
  .farbknopf { transition: none; }
}

/* ── Benachrichtigungen ──────────────────────────────────────────────────────
   Bedient wird ausschließlich über die Glocke oben rechts. Diese Zeile ist
   nur noch Auskunft und erscheint nur, wenn es etwas zu sagen gibt — wenn
   der Browser blockt oder auf iOS im Browser, wo es Push nicht gibt. */

/* Welche Nachrichten dieses Gerät bekommen will.
   Erscheint mit der Glocke, verschwindet mit ihr. */
.push-wahl {
  margin-top: 1rem;
  padding-top: .9rem;
  border-top: 1px solid var(--linie);
}

/* Die Kopfzeile ist der Schalter — dasselbe Muster wie bei den Liga-Bloecken
   und der Aufstellung. Der eigene Pfeil ersetzt das Dreieck des Browsers, das
   auf jedem System anders aussieht. */
.push-wahl__titel {
  margin: 0;
  font-size: .8125rem;
  font-weight: 700;
  cursor: pointer;
  list-style: none;
}

.push-wahl__titel::-webkit-details-marker { display: none; }

.push-wahl__titel::after {
  content: '';
  display: inline-block;
  width: .4rem;
  height: .4rem;
  margin-left: .45rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-.1em, -.1em);
  transition: transform .18s ease;
}

.push-wahl[open] .push-wahl__titel::after {
  transform: rotate(-135deg) translate(-.05em, -.05em);
}

.push-wahl[open] .push-wahl__felder { margin-top: .7rem; }

.push-wahl__felder {
  display: grid;
  gap: .5rem;
}

/* Die ganze Zeile ist die Schaltfläche, nicht nur das Kästchen — auf dem
   Telefon trifft niemand ein 13-Pixel-Quadrat zuverlässig. */
.push-wahl__feld {
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  min-height: 2.2rem;
  cursor: pointer;
}

.push-wahl__feld input {
  flex: 0 0 auto;
  width: 1.1rem;
  height: 1.1rem;
  margin-top: .1rem;
  accent-color: var(--tinte);
}

.push-wahl__text {
  display: flex;
  flex-direction: column;
  gap: .1rem;
  /* Der Text nimmt, was uebrig ist, und darf schrumpfen — `min-width: 0`
     erlaubt das Umbrechen erst. Ohne die Angabe wuerde er die Mengenspalte
     aus der Zeile draengen. */
  flex: 1 1 auto;
  min-width: 0;
  font-size: .8125rem;
  line-height: 1.35;
}

.push-wahl__klein {
  color: var(--leise);
  font-size: .75rem;
}

/* Wie viel klingelt das? — rechts am Rand, weil die Angaben nur dann
   untereinander fluchten, und nur fluchtend lassen sie sich vergleichen.
   `margin-left: auto` schiebt sie dorthin, ohne dass der Text daneben eine
   feste Breite braucht.

   `white-space: nowrap` ist Pflicht: „ca. 50 je Spieltag" ueber zwei Zeilen
   umgebrochen sieht aus wie zwei Angaben.

   Zahlen mit fester Zifferbreite (`tabular-nums`), damit die 4 nicht schmaler
   steht als die 50 — sonst wackelt die Spalte. */
.push-wahl__menge {
  flex: 0 0 auto;
  margin-left: auto;
  padding-left: .6rem;
  /* Auf Hoehe der TITELZEILE, nicht mittig zur ganzen Zeile: Bricht die
     Erklaerung darunter um, ruckte eine mittig gesetzte Angabe nach unten
     weg und die Spalte verlaufe treppenfoermig. So steht sie immer neben
     dem fetten Wort, auf das sie sich bezieht. */
  align-self: flex-start;
  margin-top: .18rem;
  color: var(--leise);
  font-size: .7rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-align: right;
}

/* Auf schmalen Geraeten hat die Angabe rechts keinen Platz mehr neben einem
   umgebrochenen Erklaertext. Dann lieber gar nicht anzeigen als die Zeile
   zerreissen — die Erklaerung ist das Wichtigere von beiden. */
@media (max-width: 22rem) {
  .push-wahl__menge { display: none; }
}


/* Die Unterauswahl zum ganzen Spieltag.
   Eingerückt und leiser als die Haken darüber — sie gehört zu einem von
   ihnen und ist keine gleichrangige Entscheidung. */
.push-wahl__umfang {
  display: grid;
  gap: .35rem;
  margin: -.15rem 0 .2rem 1.7rem;
  padding-left: .7rem;
  border-left: 2px solid var(--linie);
}

.push-wahl__feld--klein { min-height: 1.9rem; }

.push-wahl__feld--klein .push-wahl__text { font-size: .78rem; }
.push-wahl__feld--klein .push-wahl__menge { margin-top: .1rem; }
.push-wahl__feld--klein input { width: 1rem; height: 1rem; }

.push-wahl__hinweis {
  margin: .7rem 0 0;
  font-size: .75rem;
  color: var(--leise);
  min-height: 1em;
}

.push-hinweis {
  margin: 1rem 0 0;
  padding-top: .9rem;
  border-top: 1px solid var(--linie);
  font-size: .8rem;
  line-height: 1.45;
  color: var(--leise);
}

/* Das Glockensymbol in der Einladung sitzt im selben Feld wie sonst das
   App-Symbol — deshalb dieselbe Größe, damit die Zeile nicht springt. */
.einladung__symbol--zeichen {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  color: var(--auf-dunkel);
}

/* Die Datenschutzseite ist reiner Text — schmaler gesetzt als der Spielplan,
   weil lange Zeilen sich schlecht lesen. */
.huelle--text { width: min(100% - 2.5rem, 38rem); padding-bottom: 4rem; }
.huelle--text h2 { font-size: 1.15rem; margin: 2rem 0 .5rem; }
.huelle--text p { margin: 0 0 .9rem; }
.vorspann { font-size: 1.05rem; color: var(--tinte-2); }
.zurueck { margin-top: 2.5rem; padding-top: 1.25rem; border-top: 1px solid var(--linie); }

/* Die beiden Knöpfe oben rechts als Paar. */
.kopf__werkzeuge { display: flex; gap: .5rem; flex: 0 0 auto; }

/* Die Glocke trägt dieselbe Form wie der Farbknopf — dessen Regeln gelten
   über die gemeinsame Klasse. Hier nur, was sie unterscheidet. */
.glockenknopf__bild { grid-area: 1 / 1; width: 1.2rem; height: 1.2rem; display: none; }
.glockenknopf__bild.ist-da { display: block; }

/* Eingeschaltet ist der auffälligere Zustand — er soll auf einen Blick von
   „aus" zu unterscheiden sein, auch ohne den Strich zu bemerken. */
.glockenknopf[aria-pressed="true"] { color: var(--tinte); border-color: var(--linie-2); }

/* ── Nachrichten ─────────────────────────────────────────────────────────────
   Das Symbol trägt dieselbe Form wie Glocke und Farbknopf. Der Punkt an der
   Ecke erscheint, solange etwas dabei ist, das dieses Gerät nicht gesehen hat. */
.briefknopf { position: relative; }
.briefknopf__bild { width: 1.2rem; height: 1.2rem; }

.briefknopf__punkt {
  position: absolute;
  top: .28rem;
  right: .28rem;
  width: .5rem;
  height: .5rem;
  border-radius: 50%;
  background: var(--tinte);
  /* Ein Ring in der Flächenfarbe setzt den Punkt vom Symbol darunter ab —
     ohne ihn klebt er an der Kante des Briefs. */
  box-shadow: 0 0 0 2px var(--flaeche);
}

/* Das native <dialog> bringt Fokusführung, Escape und das Abdunkeln des
   Hintergrunds mit. Selbst gebaut wäre davon jedes Stück eine eigene
   Fehlerquelle — besonders die Fokusfalle, an der fast jede Eigenbau-Lösung
   scheitert. */
.nachrichten {
  width: min(100% - 2rem, 32rem);
  max-height: min(80vh, 40rem);
  padding: 0;
  border: 1px solid var(--glas-rand);
  border-radius: var(--rund);
  background: var(--flaeche);
  color: var(--tinte);
  box-shadow: var(--schatten-hoch);
  overflow: hidden;
}

.nachrichten::backdrop { background: rgba(0, 0, 0, .45); backdrop-filter: blur(2px); }

/* Der einmalige Hinweis auf neue Wahlmoeglichkeiten.
   Derselbe Rahmen wie der Nachrichten-Dialog, nur mit Innenabstand — hier
   steht Fliesstext, keine Liste. */
.neuheit {
  width: min(100% - 2rem, 26rem);
  padding: 1.4rem;
  border: 1px solid var(--glas-rand);
  border-radius: var(--rund);
  background: var(--flaeche);
  color: var(--tinte);
  box-shadow: var(--schatten-hoch);
}

.neuheit::backdrop { background: rgba(0, 0, 0, .45); backdrop-filter: blur(2px); }
.neuheit h2 { margin: 0 0 .6rem; font-size: 1.15rem; }
.neuheit p { margin: 0 0 .8rem; font-size: .875rem; line-height: 1.5; }

/* Nebeneinander, solange es passt — auf 320 px untereinander, sonst waeren
   es zwei unlesbare Stummel. */
.neuheit__knoepfe {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  margin-top: 1rem;
}

.neuheit__knoepfe .knopf { flex: 1 1 9rem; }

.knopf {
  padding: .6rem 1rem;
  border: 1px solid var(--tinte);
  border-radius: var(--rund-klein, .6rem);
  background: var(--tinte-flaeche);
  color: var(--auf-dunkel);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.knopf--leise {
  background: transparent;
  color: var(--tinte);
  border-color: var(--linie);
}

.nachrichten__kopf {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem clamp(1rem, 4vw, 1.4rem);
  border-bottom: 1px solid var(--linie);
  /* Bleibt beim Scrollen stehen — sonst scrollt der Schließen-Knopf weg. */
  position: sticky;
  top: 0;
  background: var(--flaeche);
}

.nachrichten__kopf h2 { margin: 0; font-size: 1.1rem; }

.nachrichten__zu {
  appearance: none;
  border: 0;
  background: none;
  font: inherit;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--leise);
  cursor: pointer;
  /* Groß genug zum Treffen — ein × allein ist ein Ziel von 12 Pixeln. */
  min-width: 2.75rem;
  min-height: 2.75rem;
}

.nachrichten__zu:hover { color: var(--tinte); }

#nachrichten-liste { overflow-y: auto; max-height: calc(min(80vh, 40rem) - 4rem); }

.nachricht {
  padding: .9rem clamp(1rem, 4vw, 1.4rem);
  border-bottom: 1px solid var(--linie);
}

.nachricht:last-child { border-bottom: 0; }

/* Was seit dem letzten Ansehen dazugekommen ist. */
.nachricht--neu { border-left: 3px solid var(--tinte); }

/* Die Nachricht, deren Benachrichtigung angetippt wurde.

   Wer eine Meldung öffnet, will DIESE lesen — an einem Spieltag stehen im
   Briefkasten zwanzig Einträge. Ohne Hinweis, welcher gemeint war, ist das
   Antippen eine halbe Sache.

   Die Kante links, weil sie den Blick führt, ohne die Zeile zu verschieben:
   Ein Rahmen ringsum verschöbe den Text um seine Breite, und beim Aufblenden
   ruckelte die ganze Liste. Der Grund dazu ist bewusst schwach — die
   Hervorhebung soll finden helfen, nicht schreien.

   `--tinte-flaeche` statt einer festen Farbe: Sie dreht sich mit der
   Farbwelt, wie überall auf dieser Seite. */
.nachricht--angetippt {
  border-left: 3px solid var(--tinte-flaeche);
  background: color-mix(in srgb, var(--tinte-flaeche) 7%, transparent);
}

/* Ein kurzes Aufblenden beim Öffnen, damit das Auge sie findet — einmal, dann
   Ruhe. Wer Bewegung abbestellt hat, bekommt nur die ruhende Fläche. */
@media (prefers-reduced-motion: no-preference) {
  .nachricht--angetippt { animation: nachricht-gefunden 1.6s ease-out 1; }
}

@keyframes nachricht-gefunden {
  0%   { background: color-mix(in srgb, var(--tinte-flaeche) 22%, transparent); }
  100% { background: color-mix(in srgb, var(--tinte-flaeche) 7%, transparent); }
}

.nachricht__titel { margin: 0 0 .2rem; font-weight: 700; font-size: .95rem; }
.nachricht__text { margin: 0 0 .35rem; font-size: .9rem; line-height: 1.5; }
.nachricht__wann { margin: 0; font-size: .72rem; color: var(--leise); }

/* Der Verlauf im Adminbereich — zugeklappt.
   An einem Spieltag kommen sieben Meldungen zusammen, über eine Saison
   Hunderte. Ausgeklappt wäre das eine Bildschirmlänge je Nachricht.

   NUR HIER, nicht im öffentlichen Nachrichten-Dialog: Dort sind es wenige
   Nachrichten, die man lesen soll — nicht durchsuchen. */
#push-verlauf .nachricht {
  padding: 0;
  border-bottom: 1px solid var(--linie);
}

#push-verlauf .nachricht__titel {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .8rem;
  margin: 0;
  padding: .55rem clamp(1rem, 4vw, 1.4rem);
  cursor: pointer;
  list-style: none;
}

#push-verlauf .nachricht__titel::-webkit-details-marker { display: none; }

/* Der Titel darf umbrechen, der Zeitpunkt nie — sonst rutscht das Datum in
   die nächste Zeile und die Liste wird unruhig. */
#push-verlauf .nachricht__wort { font-weight: 700; font-size: .9rem; }

#push-verlauf .nachricht__wann {
  flex: 0 0 auto;
  font-size: .72rem;
  color: var(--leise);
  white-space: nowrap;
}

#push-verlauf .nachricht__wann::after {
  content: '';
  display: inline-block;
  width: .35rem;
  height: .35rem;
  margin-left: .5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-.1em, -.1em);
  transition: transform .18s ease;
}

#push-verlauf .nachricht[open] .nachricht__wann::after {
  transform: rotate(-135deg) translate(-.05em, -.05em);
}

#push-verlauf .nachricht__text {
  margin: 0;
  padding: 0 clamp(1rem, 4vw, 1.4rem) .3rem;
  font-size: .875rem;
  line-height: 1.5;
}

#push-verlauf .nachricht__zahlen {
  margin: 0;
  padding: 0 clamp(1rem, 4vw, 1.4rem) .7rem;
  font-size: .72rem;
  color: var(--leise);
}

/* „Weitere anzeigen" unter dem Verlauf.
   Zurückhaltend, nicht wie ein Hauptknopf: Er erweitert eine Liste, er löst
   nichts aus. Über die volle Breite, damit er als Abschluss der Liste zu
   lesen ist und nicht als Teil des letzten Eintrags. */
.verlauf-mehr {
  display: block;
  width: 100%;
  padding: .7rem 1rem;
  border: 0;
  border-top: 1px solid var(--linie);
  background: none;
  color: var(--tinte-2);
  font: inherit;
  font-size: .8125rem;
  cursor: pointer;
}

.verlauf-mehr:hover {
  color: var(--tinte);
  background: var(--linie);
}



#nachrichten-leer { padding: 1.4rem; margin: 0; }

/* ── Kein Zoomen ─────────────────────────────────────────────────────────────
   Eine Zwei-Finger-Geste beim Scrollen zoomt, und danach steht die Seite
   schief — in der App besonders ärgerlich, weil dort keine Adressleiste zum
   Zurücksetzen da ist.

   `touch-action: pan-x pan-y` erlaubt Scrollen in beide Richtungen und
   verbietet genau das Zoomen — anders als `none`, das auch das Scrollen
   abwürgt.

   AUSDRÜCKLICH AUCH IM BROWSER, auf Ansage. Das ist ein Eingriff in die
   Bedienbarkeit: Wer schlecht sieht, zoomt sonst. Was den Verlust mildert,
   ist die Schrift selbst — die Seite setzt KEINE festen Pixelgrößen für
   Fließtext, sondern rechnet in `rem`. Damit folgt sie der Schriftgröße, die
   im Browser oder im System eingestellt ist, und wer größer lesen will,
   stellt es dort ein. Wer hier je feste Pixelgrößen einführt, nimmt diesen
   Ausweg mit weg. */
html, body { touch-action: pan-x pan-y; }

/* ── Aufklapp-Zeiger ─────────────────────────────────────────────────────────
   Alle Zeiger sind auf `--pfeil` vereinheitlicht. Was hier dazukommt, ist die
   TREFFERFLÄCHE: Ein Zeiger von 16 Pixeln ist gut zu sehen, aber immer noch
   schlecht zu treffen. Bei `<summary>` ist die Fläche standardmäßig genau so
   hoch wie der Text — bei kleiner Schrift also rund 14 Pixel. */
summary {
  min-height: 2.5rem;
  display: flex;
  align-items: center;
}

/* Der Zeiger, den der Browser selbst setzt (Anleitung), war der letzte, den
   `--pfeil` nicht erreichte. `::marker` nimmt nur wenige Eigenschaften an —
   `font-size` gehört dazu. */
summary::marker { font-size: var(--pfeil); }

/* Die beiden eigenen Zeiger sollen nicht springen, wenn sich die Zeile
   dreht — feste Breite hält den Text an seinem Platz. */
.vorschau__mehr > summary::before,
.abo__mehr > summary::before {
  width: var(--pfeil);
  text-align: center;
  flex: 0 0 auto;
}

/* Der Winkel im Abo-Kopf: Die Fläche zum Tippen ist der ganze Kopf, aber der
   Zeiger selbst saß gedrängt am Rand. */
.abo__winkel { padding: .25rem; margin: -.25rem; }

/* Der laufende Stand im Fuß — leiser als alles andere dort. Er ist eine
   Auskunft für den Betreiber, kein Teil dessen, wofür Besucher kommen. */
.stand {
  /* Ganz unten und mittig: Der Stand ist eine Auskunft für den Betreiber,
     keine Aussage über den Verein. Als zentrierte Fußzeile liest er sich als
     das, was er ist; linksbündig zwischen den übrigen Absätzen sah er aus wie
     ein vergessener Satz. */
  margin: 1.75rem 0 0;
  padding-top: 1rem;
  border-top: 1px solid var(--linie);
  text-align: center;
  font-size: .72rem;
  color: var(--leise);
  font-variant-numeric: tabular-nums;
  opacity: .8;
}

/* Jede Angabe eine Zeile. Aneinandergehängt brach der Text dort um, wo
   gerade der Platz endete — mitten in „Übertrag steht aus". */
.stand__zeile { display: block; }

/* ── Die Liga-Bereiche ───────────────────────────────────────────────────────
   Fünf eigenständige Auswertungen, jede zum Aufklappen. Untereinander
   gestellt ergaben sie eine Seite, die man nur noch scrollt.

   In der Kopfzeile steht die Kernaussage, nicht nur der Titel — ein
   Verzeichnis aus fünf Überschriften wäre übersichtlich und nutzlos zugleich. */
.liga-block { border-top: 1px solid var(--linie); }
.liga-block:first-of-type { border-top: 0; }

.liga-block__kopf {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .75rem;
  padding: .55rem 0;
  min-height: 2.75rem;
  cursor: pointer;
  list-style: none;
}

.liga-block__kopf::-webkit-details-marker { display: none; }

/* Der Zeiger vorn, damit die Zeile als aufklappbar zu erkennen ist — die
   Kernaussage rechts würde sonst wie eine bloße Angabe aussehen. */
.liga-block__kopf::before {
  content: "▸";
  font-size: var(--pfeil);
  color: var(--leise);
  flex: 0 0 auto;
  width: var(--pfeil);
  text-align: center;
  transition: transform .15s ease;
}

.liga-block[open] > .liga-block__kopf::before { transform: rotate(90deg); }

.liga-block__titel {
  flex: 1 1 auto;
  font-weight: 700;
  font-size: .95rem;
  margin: 0;
}

.liga-block__kurz {
  flex: 0 1 auto;
  min-width: 0;
  font-size: .78rem;
  color: var(--leise);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.liga-block__kurz:empty { display: none; }

.liga-block__inhalt { padding-bottom: 1.1rem; }

/* ── Die Spieltage der Liga ────────────────────────────────────────────────
   Neun Partien je Spieltag, dazu die Datumszeilen. Gleiches Raster wie beim
   direkten Vergleich — es ist dieselbe Art Zeile, und zwei verschiedene
   Raster nebeneinander in einer Tafel sehen aus wie ein Versehen. */

.spieltage__steuer {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: .5rem;
  margin-bottom: .7rem;
}

/* 2,75rem sind die 44px, unter denen Finger danebengreifen. Ein Pfeil, den
   man dreimal antippen muss, ist schlimmer als keiner. */
.spieltage__pfeil {
  min-width: 2.75rem;
  min-height: 2.75rem;
  display: grid;
  place-items: center;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--tinte);
  background: none;
  border: 1px solid var(--linie-2);
  border-radius: var(--rund-klein);
  cursor: pointer;
}

.spieltage__pfeil:hover:not(:disabled) { background: var(--linie); }

/* Am ersten und letzten Spieltag ist Schluss. Ausgegraut statt versteckt:
   Ein Pfeil, der verschwindet, laesst die Leiste springen. */
.spieltage__pfeil:disabled { opacity: .3; cursor: default; }

.spieltage__marke {
  text-align: center;
  font-weight: 700;
  font-size: .95rem;
}

.spieltage__liste {
  list-style: none;
  margin: 0;
  padding: 0;
}

.spieltag__tag {
  margin-top: .8rem;
  padding-bottom: .15rem;
  font-size: .6875rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--leise);
}

.spieltag__tag:first-child { margin-top: 0; }

.spieltag__partie {
  display: grid;
  grid-template-columns: 3rem 1fr auto;
  gap: .5rem;
  align-items: baseline;
  padding: .32rem 0;
  border-bottom: 1px solid var(--linie);
  font-size: .8125rem;
}

.spieltag__partie:last-child { border-bottom: none; }

.spieltag__zeit {
  color: var(--leise);
  font-variant-numeric: tabular-nums;
}

.spieltag__paarung {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Das eigene Spiel faellt auf, ohne dass eine Farbe dazukommt — auf einer
   Seite ueber den 1. FC Bocholt sucht man zuerst diese Zeile. */
.spieltag__verein--eigen { font-weight: 700; }

.spieltag__stand {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  white-space: nowrap;
}

/* Laufende Partien tragen die Signalflaeche — dieselbe Aussage wie ueberall
   sonst auf der Seite: „das hier ist gemeint". */
.spieltag__stand--live {
  padding: .1rem .4rem;
  border-radius: var(--rund-klein);
  background: var(--tinte-flaeche);
  color: var(--auf-dunkel);
}

.spieltag__minute {
  margin-left: .35rem;
  font-weight: 400;
  font-size: .6875rem;
  opacity: .85;
}

/* Der Spieltag mit laufenden Partien hebt sich schon in der Liste ab. */
.spieltag__partie--live { background: var(--linie); }


@media (prefers-reduced-motion: reduce) {
  .liga-block__kopf::before { transition: none; }
}

/* ── Umkreis-Knöpfe ──────────────────────────────────────────────────────────
   „Parken" und „Essen" in der Zeile eines Auswärtsspiels. Zurückhaltend: Sie
   sind ein Angebot, keine Hauptsache — die Zeile handelt vom Spiel, nicht von
   der Anreise. */
.umkreis {
  grid-area: parken;
  justify-self: end;
  align-self: center;
  display: flex;
  gap: .4rem;
  flex-wrap: wrap;
}

.umkreis__knopf {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  padding: .3rem .6rem;
  border: 1px solid var(--linie-2);
  border-radius: var(--rund-pille);
  font-size: .72rem;
  font-weight: 600;
  color: var(--tinte-2);
  text-decoration: none;
  white-space: nowrap;
  /* Die Trefferfläche ist größer als der sichtbare Rand — die Knöpfe dürfen
     klein bleiben, ohne dass man danebentippt. */
  min-height: 1.9rem;
}

/* Die Symbole sind gezeichnet, nicht aus der Schrift geholt: Das
   naheliegende 🅿️ oder 🍴 führt die Systemschrift nur als Emoji — bunt auf
   Apple-Geräten, sonst ein leeres Kästchen. */
.umkreis__bild {
  width: 1rem;
  height: 1rem;
  flex: 0 0 auto;
}

.umkreis__knopf:hover { border-color: var(--tinte); color: var(--tinte); }
.umkreis__knopf:hover::before { background: var(--tinte); }
.umkreis__knopf:focus-visible { outline: 2px solid var(--tinte); outline-offset: 2px; }

/* Auf schmalen Schirmen stehen die Knöpfe in einer eigenen Zeile — rechts
   neben der Paarung ist kein Platz für zwei. */
@media (max-width: 40rem) {
  .umkreis { justify-self: start; margin-top: .35rem; }
}

/* Vergangene Spiele brauchen weder Parkplatz noch Restaurant. */
.spiel--vorbei .umkreis { display: none; }

/* Der Hinweis auf die Partner des Vereins. Etwas kräftiger als der übrige
   Fuß — es ist eine Aussage, keine Fußnote —, aber ohne Fläche und Rahmen:
   Er soll würdigen, nicht werben. */
/* ── Die Partner des Vereins ─────────────────────────────────────────────────
   Ein eigener Abschnitt unter der letzten Kachel, kein Absatz im Fuß. Dort
   stand er zwischen Quellenangaben und Lizenzhinweisen und las sich wie eine
   Fußnote — dabei ist er eine Aussage.

   Deshalb bekommt er Fläche wie eine Kachel, aber ohne Rand und Schatten: Er
   soll dazugehören, nicht mit dem Spielplan um Aufmerksamkeit ringen. */
/* ── Die Protokolle im Adminbereich ──────────────────────────────────────────
   Zwei Ausklappbereiche. Zugeklappt, weil man sie selten braucht — und wenn,
   dann gezielt. Die Zahl in der Kopfzeile sagt, ob sich das Öffnen lohnt. */
.protokoll { border-top: 1px solid var(--linie); }

.protokoll__kopf {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .75rem;
  padding: .55rem 0;
  min-height: 2.75rem;
  cursor: pointer;
  list-style: none;
}

.protokoll__kopf::-webkit-details-marker { display: none; }

.protokoll__kopf::before {
  content: "▸";
  font-size: var(--pfeil);
  color: var(--leise);
  flex: 0 0 auto;
  width: var(--pfeil);
  text-align: center;
  transition: transform .15s ease;
}

.protokoll[open] > .protokoll__kopf::before { transform: rotate(90deg); }

.protokoll__titel { flex: 1 1 auto; font-weight: 700; font-size: .95rem; }
.protokoll__kurz { flex: 0 0 auto; font-size: .78rem; color: var(--leise); }

/* Der Inhalt scrollt für sich. Vierhundert Einträge würden die Seite sonst
   auf mehrere Meter strecken, und der Weg zurück nach oben wäre weit. */
.protokoll__inhalt {
  max-height: 22rem;
  overflow-y: auto;
  padding-bottom: .9rem;
  font-variant-numeric: tabular-nums;
}

/* Ein Datumsbalken je Tag statt an jeder Zeile — fünfzig Zeilen mit
   demselben Datum sind fünfzig Wiederholungen. */
.protokoll__tag {
  position: sticky;
  top: 0;
  margin: .6rem 0 .25rem;
  padding: .25rem 0;
  background: var(--flaeche);
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--leise);
}

.protokoll__zeile {
  display: grid;
  grid-template-columns: 3rem 9rem 1fr;
  gap: .5rem;
  padding: .3rem 0;
  font-size: .8rem;
  line-height: 1.4;
  border-top: 1px solid var(--linie);
}

.protokoll__uhr { color: var(--leise); }
.protokoll__was { font-weight: 600; }
.protokoll__text { color: var(--tinte-2); overflow-wrap: anywhere; }

/* Auf schmalen Schirmen untereinander — drei Spalten blieben sonst je
   dreißig Pixel breit. */
@media (max-width: 34rem) {
  .protokoll__zeile { grid-template-columns: 3rem 1fr; }
  .protokoll__text { grid-column: 2; }
}

@media (prefers-reduced-motion: reduce) {
  .protokoll__kopf::before { transition: none; }
}

/* ── Das Zeitreise-Band ──────────────────────────────────────────────────────
   Die Seite ist öffentlich. Rechnet sie mit einem anderen Datum, muss das
   unübersehbar sein — dieselbe Überlegung wie beim Probelauf, der seine
   erfundenen Spielstände ausweist. */
.zeitreise {
  position: sticky;
  top: 0;
  z-index: 50;
  margin: 0;
  padding: .6rem clamp(1rem, 4vw, 1.5rem);
  background: var(--tinte-flaeche);
  color: var(--auf-dunkel);
  box-shadow: var(--kante-licht);
  font-size: .8rem;
  font-weight: 600;
  line-height: 1.4;
  text-align: center;
}


/* Die Zustellzahl in der Kopfzeile einer verschickten Nachricht.
   Rechts neben dem Titel, vor dem Zeitpunkt — „ist sie angekommen?" ist die
   erste Frage, und sie soll bei allen Eintraegen auf einmal zu sehen sein,
   ohne jeden einzeln aufzuklappen.

   Feste Zifferbreite, damit die Spalte nicht wackelt; nowrap, weil „8/8 ·
   3× geöffnet" umgebrochen wie zwei Angaben aussaehe. */
/* KEIN `margin-left: auto` — die Kopfzeile steht auf `space-between`, und
   drei Kinder verteilen sich dort von selbst: Titel links, Zahlen in der
   Mitte, Zeitpunkt rechts. Ein zusaetzliches `auto` schoebe die Zahlen an
   den Zeitpunkt heran. */
.nachricht__kurz {
  flex: 0 0 auto;
  color: var(--leise);
  font-size: .72rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Auf sehr schmalen Geraeten hat sie neben Titel und Zeit keinen Platz —
   dann lieber weglassen als die Zeile zerreissen. Im Aufgeklappten steht
   sie ohnehin ausgeschrieben. */
@media (max-width: 24rem) {
  .nachricht__kurz { display: none; }
}


/* ── Hinweis auf eine neue Fassung ──────────────────────────────────────────

   Ein Band über der Reiterleiste, kein Dialog: Ein modales Fenster verlangt
   eine Antwort, bevor es weitergeht — hier gibt es aber nichts zu
   entscheiden. Wer weiterlesen will, liest weiter.

   Über der Leiste, nicht darunter: Die Reiter sind das, womit der Daumen
   arbeitet; ein Band davor wäre ein Hindernis. `env(safe-area-inset-bottom)`
   liegt in der Höhe der Leiste bereits drin — deshalb hier nur ihr Abstand. */
.neue-fassung {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(4.5rem + env(safe-area-inset-bottom, 0px));
  z-index: 60;
  display: flex;
  align-items: center;
  gap: .6rem;
  max-width: min(30rem, calc(100vw - 2rem));
  padding: .6rem .7rem .6rem 1rem;
  border-radius: 999px;
  background: var(--tinte-flaeche);
  color: var(--auf-dunkel);
  box-shadow: 0 .5rem 1.5rem rgba(0, 0, 0, .25);
}

.neue-fassung__text { margin: 0; font-size: .85rem; line-height: 1.3; }

.neue-fassung__knopf {
  flex: 0 0 auto;
  padding: .35rem .8rem;
  border: 1px solid var(--linie-auf-signal);
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: .8rem;
  font-weight: 700;
  cursor: pointer;
}

.neue-fassung__zu {
  flex: 0 0 auto;
  width: 1.9rem;
  height: 1.9rem;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  opacity: .7;
}

.neue-fassung__zu:hover { opacity: 1; }

/* Auf schmalen Geräten untereinander — drei Dinge nebeneinander auf 320 px
   ergäben drei unlesbare Stummel. */
@media (max-width: 24rem) {
  .neue-fassung { flex-wrap: wrap; border-radius: 1rem; }
  .neue-fassung__text { flex: 1 1 100%; }
}

/* Beim Drucken hat ein Hinweis auf eine neue Fassung nichts verloren. */
@media print { .neue-fassung { display: none; } }
