/* ================================================================
   [kru:]nest — Turniermodul Design-Grundlage
   ----------------------------------------------------------------
   Step 5 (§12 Punkt 5): Design-Tokens, Grundraster,
   die EINE Match-Komponente in drei Größen.

   Verbindliche Vorlage: turniermodul-prototyp.html.
   - :root-Tokens ÜBERNOMMEN (chromatische Werte unverändert).
   - Schriftfamilie: vom Body geerbt (Plus Jakarta Sans / Sora)
     — keine eigene einbinden (§8.7).
   - Alle Klassen sind scoped auf .t-mod, damit sie die globale
     [kru:]nest-Oberfläche (main.css) nicht berühren.

   Was das hier NICHT macht: keine Screens, keine Endpoints, keine
   Logik. Nur Bausteine, aus denen Screen A (Turnierliste) und
   die späteren Screens zusammengesetzt werden.

   Klassen-Konvention (alle mit `t-`-Präfix, innerhalb .t-mod):
     Layout:
       .t-app, .t-mod, .t-mod-header, .t-mod-body
     Grid:
       .t-shell                     drei Spalten (212px · 1fr · 300px)
     Navigation:
       .t-mod-nav, .t-mod-aside
     Karten / Inhalte:
       .t-card, .t-card-head, .t-card-body
     Buttons / Badges:
       .t-btn, .t-btn--primary, .t-btn--ghost, .t-btn--sm, .t-btn--onDark
       .t-badge, .t-badge--phase
     Match-Komponente (3 Größen):
       .t-match                     Standard-Größe (Spielplan, Gruppen)
       .t-match.t-match--compact    Kompakt (Kontextspalte, Liste)
       .t-match.t-match--large      Groß (Detail-Panel, Beamer)
     Standardisierte Animation/Skelett:
       .t-dot, .t-section-label
   ================================================================ */

/* ----------------------------------------------------------------
   SCOPED DESIGN-TOKENS
   Übernommen aus turniermodul-prototyp.html :root (unverändert).
   ---------------------------------------------------------------- */
.t-mod {
  /* Palette und Schrift kommen aus tokens.css — die Datei wird in
     index.html VOR dieser geladen. Hier stehen nur noch Layout-Vertrag
     und Komponenten. Wer eine Farbe sucht, sucht dort. */

  /* ---- Container-Vertrag (unverändert, nicht anfassen) ---- */
  container-type: inline-size;
  width: 100%;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* ----------------------------------------------------------------
   NACHTMODUS
   ----------------------------------------------------------------
   Selektor an [kru:]nest angepasst: dort steht [data-theme='dark']
   (siehe main.css Zeile 22), kein body.dark oder theme-dark.

   WICHTIG: Die Anzeigetafel dreht sich um. Auf hellem Grund ist sie
   dunkel, auf dunklem Grund hell. So bleibt sie in beiden Modi das
   auffälligste Element, statt mit dem Hintergrund zu verschmelzen.
   ---------------------------------------------------------------- */
/* Die Dunkel-Palette steht in tokens.css, zusammen mit der hellen.
   Wer sie hier sucht: backend/public/style/tokens.css. */

/* ----------------------------------------------------------------
   UNIVERSELLE RESETS (nur innerhalb .t-mod)
   ---------------------------------------------------------------- */
.t-mod *,
.t-mod *::before,
.t-mod *::after {
  box-sizing: border-box;
}
/* ----------------------------------------------------------------
   DAS BLATT — die einzige erhobene Ebene
   ----------------------------------------------------------------
   Artefakt „Turniermodul ohne Kaestchen", dritte Fassung. Bis hierher
   war das Modul durchsichtig: der Kopf hatte einen eigenen weissen
   Grund, die Seitenleiste auch, und dazwischen sah man den Seitengrund
   durch. Drei Flaechen fuer einen Bildschirm.

   Jetzt gibt es genau eine: das Blatt (--panel) auf dem Grund
   (--ground). Der Sprung #F1F0EB auf #D7D6D0 zeichnet die Kante schon —
   1,276 : 1, gemessen genauso viel wie vorher Weiss gegen Hellgrau.
   Darauf liegt genau eine weitere Flaeche, die Spielkarte.

   WARUM :not(.t-dialog-host)
   Die drei Dialoge tragen `t-mod t-dialog-host`, weil sie an
   document.body haengen und die Tokens brauchen (siehe Block DIALOG-HOST
   am Dateiende). Sie sind aber der VORHANG, nicht das Blatt — ein
   --panel darauf haette den halbdurchsichtigen Ueberzug zugestrichen.
   `:not()` zaehlt sein Argument mit, die Regel steht damit bei (0,0,2,0)
   und gewinnt unabhaengig von der Quell-Reihenfolge gegen
   `.t-list-host { background: transparent }` weiter unten. Das ist
   Absicht: die Turnierliste ist nach Abschnitt 02 der Vorlage ebenfalls
   ein Blatt, auf dem Bloecke liegen.
   ---------------------------------------------------------------- */
.t-mod:not(.t-dialog-host) {
  background: var(--panel);
}
/* Am Handy ist das Blatt das GERAET aus Abschnitt 01 — weit gerundet
   (--r-dev, 30px) und mit demselben flachen Hebewert. Auf Desktop wird
   daraus die Schale aus Abschnitt 09 mit der kleineren Flaechen-Ecke;
   diese Regel steht weiter oben bei .t-detail-host.
   Kein Rahmen in beiden Faellen: der Sprung vom Grund auf das Blatt
   zeichnet die Kante schon. */
.t-detail-host > .t-mod,
.t-list-host {
  border-radius: var(--r-dev);
  box-shadow: var(--lift);
}
.t-mod {
  margin: 0;
  padding: 0;
  min-width: 0;
  /* Markenuebernahme: das Modul erbt die Schrift NICHT mehr vom Body.
     Der Kommentar am Dateikopf sagt noch "Schriftfamilie vom Body geerbt
     — keine eigene einbinden (Paragraf 8.7)"; diese Regel ist mit dem
     Marken-Artefakt aufgehoben. Der Charakter des Entwurfs haengt an den
     vier Familien, ohne sie bleibt es die alte App in neuen Farben.
     Geladen werden sie in index.html, ein Request fuer alle vier. */
  font-family: var(--f-body);
  color: var(--ink);
}
/* Ziffern gehoeren ins Mono: Staende, Zeiten, Tabellenwerte. Das ist im
   Artefakt kein Detail, sondern die halbe Handschrift — Zahlen fluchten
   untereinander, und die Tabelle wirkt wie eine Anzeigetafel statt wie
   ein Formular. */
.t-mod :is(.t-standings-table, .t-thirds-table) :is(td, th),
.t-mod
  :is(
    .t-match-score,
    .t-score,
    .t-match-time,
    .t-standings-num,
    .t-thirds-num,
    .t-standings-rank,
    .t-thirds-rank
  ) {
  font-family: var(--f-mono);
  font-variant-numeric: tabular-nums;
}
/* Titel und Teamnamen tragen den Display-Schnitt.
   Die Zellen-Regel darueber ist (0,0,3,0) — sie faerbt ALLE Zellen der
   beiden Tabellen ins Mono, auch die Namensspalte. Damit der Name den
   Display-Schnitt behaelt, muss diese Regel mindestens gleichziehen;
   `.t-mod .t-standings-table .t-standings-team` ist (0,0,3,0) und steht
   spaeter, gewinnt also ueber die Quell-Reihenfolge.
   Das ist genau die Falle, die in dieser Datei schon fuenfmal zugeschlagen
   hat: eine allgemeine Regel, die eine speziellere ueberholt, weil die
   Zaehlung der Klassen zufaellig auf ihrer Seite steht. */
.t-mod :is(.t-title, .t-match-team, .t-team-name),
.t-mod .t-standings-table .t-standings-team,
.t-mod .t-standings-table .t-standings-group-title,
.t-mod .t-thirds-table .t-thirds-team,
.t-mod .t-thirds-card .t-thirds-title {
  font-family: var(--f-disp);
  letter-spacing: -0.01em;
}
/* Der Gruppentitel steht ausserhalb der Tabelle, braucht also keine
   Gegenwehr — hier nur die Groesse aus dem Entwurf. */
.t-mod .t-standings-group-title {
  font-family: var(--f-disp);
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.015em;
  text-transform: none;
  color: var(--ink);
}

/* ----------------------------------------------------------------
   TASTATUR-FOKUS — Basisregel fuer das ganze Modul
   ----------------------------------------------------------------
   Stand vor dem 2026-08-25: mehrere Komponenten setzten `outline: none`,
   ohne einen sichtbaren Ersatz zu liefern (Team-Umbenennen-Feld,
   Einstellungen-Kopfzeile, .t-input). Wer mit der Tastatur navigierte,
   sah nicht, wo der Fokus stand — Bedienung praktisch blind.

   Diese Regel steht ABSICHTLICH ganz oben, direkt hinter den Resets:
   `:focus-visible` ist eine Pseudoklasse, `.t-mod :focus-visible` hat
   damit dieselbe Spezifitaet (0,0,2,0) wie ein spaeteres
   `.t-komponente:focus`. Bei Gleichstand entscheidet die Quell-
   Reihenfolge — eine Komponente kann den Ring hier unten also bewusst
   umdefinieren (z.B. `outline-offset: -1px`, wenn er sonst aus der
   Karte laeuft). Was sie NICHT mehr darf: ihn ersatzlos abschalten.

   Farbe: var(--accent). Nachgerechnet gegen WCAG 1.4.11 (>= 3:1 fuer
   Nicht-Text):
     hell   #8B6B4A auf #FFFFFF 4.88:1 · auf --surface-alt 4.34:1
     dunkel #B8916A auf #23201B 5.64:1 · auf --surface-alt 5.23:1
   Traegt also in beiden Modi.

   `:focus-visible` statt `:focus`: Maus-/Touch-Klicks bekommen weiter
   keinen Ring, nur Tastatur- und Screenreader-Navigation.
   ---------------------------------------------------------------- */
.t-mod :focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ----------------------------------------------------------------
   WIZARD-HOST-MODUS
   Wenn der Wizard offen ist, übernimmt er den vollen Inhaltsbereich
   der App (#content neben #sidebar). Dazu bekommt #grid per JS die
   Klasse t-wizard-host, die das Foto-Grid (`.grid`) aufhebt:
   - display:block statt display:grid → keine 260px-Spalten mehr
   - min-width:0 + width:100% → keine schmale Mittelspalte
   - padding/margin 0 → Wizard füllt #content vollständig
   ---------------------------------------------------------------- */
.t-wizard-host {
  display: block !important;
  width: 100%;
  min-width: 0;
  margin: 0;
  padding: 0;
}
/* Volle Höhe + eigener Scroller NUR auf Desktop (≥768 px).
   Auf Mobile (<768 px) wächst .t-mod mit seinem Inhalt mit und die
   Seite selbst (#content) scrollt — verhindert die zwei verschachtel-
   ten Scrollbalken und die fehlerhafte Tabellen-Breitenrechnung
   innerhalb eines scrollenden Vorfahren. P5-Reparatur 2026-08-25. */
@media (min-width: 768px) {
  .t-wizard-host > .t-mod {
    height: calc(100vh - 60px); /* Platz für App-Header */
    overflow: hidden;
  }
  /* ---- Und ein Kind, das scrollt (2026-08-29) -------------------
     Die Klammer darueber deckelt die Hoehe und schneidet ab, ohne dass
     irgendjemand darunter scrollt — gemessen mit Playwright: bei
     Schritt 2 mit 18 Teams braucht der Wizard 1529 px, verfuegbar sind
     840 px bei 900 px Fensterhoehe; der Weiter-Knopf lag bei y=1545.
     Kein Nachfolger hatte `overflow-y: auto` (Messung: leere Liste),
     und `#content` bekam keine Bildlaufleiste, weil `.t-mod` seine
     Hoehe ja deckelt. Der Knopf war damit auf JEDEM Desktop-Fenster
     unerreichbar, auch auf 1440x1080 — nicht erst ab einer bestimmten
     Hoehe, wie der Backlog-Punkt vermutete. Unter 768 px gab es das
     nie, dort greift die Klammer nicht.

     Gescrollt wird der Body, nicht das Modul: die Fortschrittsanzeige
     ist die Kopfzeile des Wizards und soll stehen bleiben, waehrend
     das Formular laeuft. `.t-wizard-body` ist dafuer bereits gebaut
     (`flex: 1; min-height: 0`) — ihm fehlte nur die Zeile, die aus
     einem Flex-Kind einen Scroller macht. Deshalb hier eine Zeile
     statt eines Umbaus. */
  .t-wizard-host > .t-mod > .t-wizard-body {
    overflow-y: auto;
  }
}

/* Detail-View-Host: dasselbe Muster wie t-wizard-host — wenn ein
   Turnier-Detail offen ist, bricht das Foto-Grid aus main.css
   (`.tournaments-grid { grid-template-columns: repeat(auto-fill, ...) }`)
   die `.t-mod` in eine ~280px-Spalte. Diese Klasse hebt das auf,
   damit `.t-shell` seine volle Breite bekommt und die drei Spalten
   greifen. Wird beim Öffnen gesetzt, beim „Zurück zur Liste"
   über `switchToTournamentInstances()` → `loadTournamentInstances()`
   wieder entfernt (das setzt grid.className = 'grid tournaments-grid'). */
.t-detail-host {
  display: block !important;
  width: 100%;
  min-width: 0;
  margin: 0;
  padding: 0;
}
/* Volle Höhe + eigener Scroller NUR auf Desktop (≥768 px).
   Auf Mobile (<768 px) wächst .t-mod mit seinem Inhalt mit und die
   Seite selbst (#content) scrollt — verhindert die zwei verschachtel-
   ten Scrollbalken und die fehlerhafte Tabellen-Breitenrechnung
   innerhalb eines scrollenden Vorfahren. P5-Reparatur 2026-08-25. */
@media (min-width: 768px) {
  .t-detail-host > .t-mod {
    height: calc(100vh - 60px);
    overflow: hidden;
    /* Ab hier ist das Blatt als Blatt zu sehen: gerundete Ecke und EIN
       sehr weit gestreuter, sehr flacher Schatten (84px Streuung bei 7%
       Deckkraft). Kein Rahmen — der Tonsprung zum Grund zeichnet die
       Kante bereits. Am Handy ist das Blatt randlos, dort waere eine
       Ecke nur eine abgeschnittene Zeile. */
    border-radius: var(--r-plane);
    box-shadow: var(--lift);
    /* Ein Blatt hat eine Breite. Ohne Kappung lief die Schale auf 1920px
       ueber die volle Restbreite, und der Grund war nur noch ein
       Randstreifen — genau der Stapel-Eindruck, den die zweite Fassung
       abgeschafft hat. 1240px ist der Wert, mit dem die Vorlage selbst
       gesetzt ist (`.pg { max-width: 1240px }`). */
    max-width: 1240px;
    margin: 0 auto;
  }
}
/* Und eine Ansicht hat eine LESEBREITE. Auf einem 1240er Blatt bleiben
   nach der Seitenleiste rund 1030px fuer den Inhalt; eine Listenzeile
   ist dann 990px breit und ihr Haken sitzt einen halben Bildschirm vom
   Text entfernt. Im Screenshot bei 1920px war zwischen „GEFAHRENZONE"
   und seinem Dreieck ein Meter Nichts.
   Die Vorlage loest dasselbe Problem in Abschnitt 09 mit SPALTEN
   (`repeat(auto-fit, minmax(300px, 1fr))`); die Ansichten dieses Moduls
   sind einspaltig, also loest es hier eine Kappung. 880px ist die
   Obergrenze, bei der die neunspaltige Gruppentabelle noch atmet. */
@container (min-width: 1000px) {
  .t-view.is-active {
    max-width: 880px;
  }
  /* Der Spielplan geht noch eine Stufe enger. Eine Spielkarte ist zwei
     Namen und zwei Ziffern; auf 860px stehen zwischen „Kellerkinder" und
     der „1" acht Zentimeter Nichts. Das ist derselbe Befund wie A2.2 am
     25.08. („Wrapper max 560px verhindert 800px-Leere auf Desktop") —
     damals lag die Kappung auf .t-match-rows und wurde mit der Zeitachse
     wieder entfernt. Sie gehoert an die LISTE, nicht an die Zeilen: sonst
     waeren Teams und Ziffern schmal und das Fussband darunter breit. */
  #t-schedule-list {
    max-width: 620px;
  }
}

/* ----------------------------------------------------------------
   LISTEN-HOST (A4, 2026-08-25)
   ----------------------------------------------------------------
   Die Turnierliste rendert in #grid, also AUSSERHALB der Detail-View.
   .t-list-card & Geschwister sind aber auf die Tokens von .t-mod
   angewiesen (--ink, --line, --s4, --r-card …) — die stehen nicht in
   main.css. Ohne .t-mod fallen sie auf Initialwerte zurück: kein
   Padding, kein Radius, currentcolor als Rahmen.

   Deshalb bekommt die Listen-Schale zusätzlich die Klasse t-mod (eine
   Wahrheit für die Tokens, keine Kopie) und hier den Gegen-Reset für
   die Layout-Eigenschaften, die .t-mod als Modul-Container mitbringt.
   Dasselbe Muster nutzen seit A5 (2026-08-25) die drei Dialoge, die
   an document.body hängen — .t-dialog-host, ganz am Dateiende. Bis
   dahin hat diese Zeile eine Klasse behauptet, die es noch gar nicht
   gab; jetzt stimmt sie.
   ---------------------------------------------------------------- */
.t-list-host {
  container-type: normal;
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  width: 100%;
  max-width: 1800px;
  margin: 0;
  padding: 0;
  background: transparent;
}
.t-mod table {
  border-collapse: collapse;
  width: 100%;
}
/* ----------------------------------------------------------------
   KASKADENKOLLISION Nr. 6 in dieser Datei (aufgeloest 2026-08-25)
   ----------------------------------------------------------------
   Hier stand EINE Regel:
       .t-mod button { font-family: inherit; font-size: inherit;
                       cursor: pointer; border: none; background: none;
                       color: inherit; }
   Spezifitaet (0,1,1) — eine Klasse plus ein Typ. Jede .t-btn-Regel ist
   (0,1,0). Damit hat dieser Reset im ganzen Modul JEDEN Knopf plattgemacht,
   auch `--primary` und `--danger`: keine Fuellung, kein Rand, geerbte
   Textfarbe, geerbte Schriftgroesse (16px statt 14px). Im Browser
   nachgemessen (Edge, .t-btn--primary):
       background rgba(0,0,0,0)  color rgb(26,20,16)  border 0px none
       font-size 16px
   Der A5-Dialog holte die Optik deshalb unter `.t-dialog-host .t-btn`
   mit hoeherer Gewichtung zurueck — diese Doppelung ist mit dieser
   Aenderung entfallen.

   DIE AUFLOESUNG: der Reset zerfaellt in zwei Teile.
   - Was UNSCHAEDLICH ist, weil keine Komponente es anders will
     (font-family, cursor), bleibt auf `.t-mod button`.
   - Was GESTALTUNG PLAETTET (border, background, color, font-size),
     bekommt `:where(:not([class*="t-btn"]))`. `:where()` traegt selbst
     keine Spezifitaet; der Selektor bleibt (0,1,1) — aber er MATCHT die
     Komponentenknoepfe nicht mehr. Das ist der Punkt: nicht die
     Gewichtung senken, sondern den Geltungsbereich schneiden.
     Attribut-Substring statt `.t-btn`, damit auch ein alleinstehendes
     `.t-btn--xs` (tournament.js:1000) erfasst ist.

   MUSTER, das diese Datei jetzt sechsmal zeigt: der Selektor einer Regel
   sagt nicht, wogegen sie gewinnt. Wer eine Basis-/Reset-Regel schreibt,
   die Komponenten nicht schlagen soll, muss ihren GELTUNGSBEREICH
   schneiden (`:where(:not(...))`) — eine "allgemeinere" Regel ist nicht
   automatisch die schwaechere.

   NACHTRAG 2026-08-26 — Kollision Nr. 7, dieselbe Regel, neues Opfer.
   Die Ausnahme oben nennt nur `t-btn`. Der Kopf-Aktionsknopf heisst aber
   `.t-mod-action` und traegt kein `t-btn` im Klassennamen — der Reset
   MATCHT ihn also, und bei (0,1,1) gegen (0,1,0) gewinnt er. Folge, im
   Browser gemessen (Edge, Pruefstand, .t-mod-action):
       color rgb(11,11,12)   statt  #BF4413 (--flare)
   Die eine orange Aktion, die die Vorlage auf JEDEM Screen zeigt, war
   also tintenschwarz — und weil `font-size: inherit` genauso gewinnt,
   auch 15px statt 12.5px. Sichtbar war das nur als "sieht irgendwie
   nicht aus wie im Artefakt"; per Quelltextlesen war es unsichtbar,
   weil die Regel, die verliert, korrekt dasteht.

   Die Ausnahmeliste ist deshalb ab jetzt eine LISTE, keine Einzelregel:
   wer eine Komponente mit eigener Optik in dieses Modul stellt, traegt
   sie hier ein. Das ist die Stelle, an der der Geltungsbereich
   geschnitten wird — es gibt nur diese eine.
   ---------------------------------------------------------------- */
.t-mod button {
  cursor: pointer;
}
/* `font-family: inherit` steht MIT im geschnittenen Block, nicht darueber.
   Es stand vorher aussen und war damit (0,1,1) gegen (0,1,0) — der Grund,
   warum der Kopf-Aktionsknopf trotz `font-family: var(--f-cond)` in
   IBM Plex Sans erschien. Wer hier eine Komponente austraegt, gibt ihr
   im Gegenzug eine eigene Schriftzeile; sonst faellt sie auf die
   Browser-Voreinstellung zurueck statt auf die Modulschrift. */
/* NACHTRAG 2026-08-26, dritte Fassung — drittes Opfer derselben Regel:
   `.bracket-tab`. Die Runden-Pillen setzen Archivo Narrow in 12,5px
   Versalien; der Reset hier gewann mit (0,1,1) und lieferte 15px in der
   geerbten Schrift. Im Pruefstand bei 390px passten drei Runden dadurch
   nicht nebeneinander und „FINALE" war abgeschnitten — es sah nach einem
   Layoutfehler aus und war ein Schriftgroessen-Fehler.
   Dass diese Liste jetzt zum dritten Mal waechst, ist das eigentliche
   Signal: JEDER Knopf mit eigener Optik gehoert hier eingetragen, und
   zwar BEVOR man seine Regel schreibt. */
.t-mod button:where(:not([class*='t-btn'], .t-mod-action, .t-mod-tab, .t-chip, .bracket-tab)) {
  font-family: inherit;
  font-size: inherit;
  border: none;
  background: none;
  color: inherit;
}
.t-mod input {
  font-family: inherit;
}

/* ----------------------------------------------------------------
   APP-GITTER (Modul-Container in der bestehenden App-Seitenleiste)
   Layout: Seitenleiste (bleibt in main.css) | Modul-Arbeitsbereich
   ---------------------------------------------------------------- */
.t-mod-header {
  /* Markenuebernahme Etappe 2 (2026-08-26): der Kopf traegt jetzt drei
     Dinge in fester Rangfolge — Kicker (wo bin ich), Display-Titel (was
     sehe ich), eine Aktion rechts. Vorher standen Logo, Titel, Untertitel,
     Badges und beliebig viele Knoepfe in EINER Flex-Reihe; auf 390px
     brach das in drei Zeilen um und der Titel war so gross wie alles
     andere. Jetzt: der Titel ist das Groesste auf dem Screen, daran
     erkennt man die Ansicht im Vorbeigehen.

     Dritte Fassung: KEINE Trennlinie und KEIN eigener Grund mehr.
     Der Kopf sitzt auf demselben Papierton wie der Inhalt; getrennt wird
     durch den Schriftsprung — 27px Archivo 800 gegen 12,5px Archivo
     Narrow, das sind 14,5px Groessenunterschied und zwei Schnitte. Das
     traegt zuverlaessiger als eine Kontur in --line, und der Kopf hoert
     auf, ein Kasten ueber dem Blatt zu sein. */
  display: block;
  /* Seitlich dieselben 20px wie in jeder Ansicht — sonst faengt der
     Titel an einer anderen Stelle an als die Tabelle darunter. */
  padding: var(--s2) var(--s5) var(--s4);
}
/* Der Innenteil traegt das Raster, nicht der <header> selbst: das Logo
   ist optional, und die Klasse dafuer sitzt am Markup, das der Renderer
   liefert (renderModulKopf). Ohne Logo bleibt es ein schlichter Block. */
.t-mod-header-inner {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
/* MIT Logo wird daraus ein zweispaltiges Raster: das Bild steht neben
   BEIDEN Zeilen, nicht ueber oder unter einer. Dadurch liest der Kopf
   als ein Gegenstand statt als drei gestapelte Teile. */
.t-mod-header-inner--logo {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    'logo kicker'
    'logo titel';
  column-gap: 13px;
  align-items: center;
}
.t-mod-header-inner--logo > .t-mod-logo {
  grid-area: logo;
}
.t-mod-header-inner--logo > .t-mod-kicker {
  grid-area: kicker;
  align-self: end;
}
.t-mod-header-inner--logo > .t-mod-titlerow {
  grid-area: titel;
  align-self: start;
  margin-top: 3px;
}
/* Der Radius ist RELATIV angegeben, damit er bei 26px im Desktop-Kopf
   genauso sitzt wie bei 54px auf dem Druckbogen — ein fester Pixelwert
   waere in der kleinen Groesse fast ein Kreis.
   Der Haarlinien-Ring ist eine der wenigen bewussten Konturen dieser
   Fassung: es ist das einzige Bild auf dem Schirm, das wir nicht selbst
   zeichnen. Ein sehr helles Logo haette auf dem Blatt sonst keine Kante,
   und wir koennen dem Veranstalter nicht vorschreiben, was er hochlaedt. */
.t-mod-logo {
  width: 44px;
  height: 44px;
  border-radius: 24%;
  object-fit: cover;
  background: var(--sunk);
  border: 1px solid var(--line-soft);
  flex: none;
  display: block;
}
/* Kicker: Turniername und Datum, Mono und gesperrt. Er wiederholt NIE den
   Titel darunter — wer beides gleich benennt, verschenkt eine Zeile. */
.t-mod-kicker {
  display: flex;
  align-items: baseline;
  gap: var(--s2);
  min-width: 0;
  font-family: var(--f-mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  /* Die Kappung sitzt am TEXT, nicht hier — siehe .t-mod-kicker-text.
     Ein erster Anlauf hat sie an diesen Behaelter gehaengt und nichts
     bewirkt: das `white-space: nowrap` stand am span darin, und der
     gewinnt gegen jede Regel am Elternteil. Gemessen kam der Kasten mit
     265px und der Text mit 325px an — abgeschnitten, ohne Auslassungs-
     punkte, weil die die Regel am span mitbringt. Dieselbe Sache an zwei
     Stellen, wie schon achtmal in dieser Datei. */
  overflow: hidden;
}
/* Titelzeile: Display-Titel links, genau EINE Aktion rechts.
   align-items: baseline, damit die Grundlinien fluchten — center laesst
   die kleine Aktion neben dem grossen Titel schweben. */
.t-mod-titlerow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
}

/* ----------------------------------------------------------------
   GRUNDRASTER (3 Spalten, NIE in einem Scrollcontainer auf Desktop)
   Default = Mobile (1 Spalte + Tab-Leiste). Schwellen via
   Container Queries auf .t-mod weiter unten (§8.1).
   ---------------------------------------------------------------- */
.t-shell {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  flex: 1;
  min-height: 0;
  /* KASKADENKOLLISION Nr. 10, gemessen 2026-08-26.
     main.css fuehrt dieselbe Klasse mit
       padding: clamp(8px, 1.5vw, 24px); gap: clamp(12px, 2vw, 24px)
     — gleiche Spezifitaet (0,0,1,0), aber frueher geladen, und dieser
     Block hier setzte beide Eigenschaften nie. Ergebnis: das Blatt hatte
     innen einen unsichtbaren 8px-Rand, den kein grep in tournament.css
     findet. Gemessen lag .t-view damit bei 22px statt bei 14px, und
     alles, was buendig an der Blattkante sitzen soll — Rangband,
     Zustandsstreifen, Gefahrenstreifen — sass 8px daneben.
     Beide Werte gehoeren auf Null: die Vorlage trennt Seitenleiste und
     Inhalt mit einer Haarlinie, die sich zwei Spalten teilen. Ein
     Zwischenraum wuerde sie in der Luft haengen lassen. */
  padding: 0;
  gap: 0;
}

/* Modul-Navigation (linke Spalte).
   Dritte Fassung, Abschnitt 09: die Leiste ist kein Panel mehr. Vorher
   hatte die Schale einen Rahmen UND die Leiste darin einen eigenen Grund
   — zwei Flaechen fuer eine Spalte. Jetzt trennt eine senkrechte
   Haarlinie, dieselbe wie zwischen zwei Tabellenzeilen, nur um 90 Grad
   gedreht. Auf dem Blatt trennt die WEICHE Linie. */
.t-mod-nav {
  border-right: 1px solid var(--line-soft);
  padding: var(--s4) var(--s3);
  background: transparent;
  overflow: auto;
}
/* Voller Radius statt 10px-Rechteck: derselbe Koerper wie die aktive
   Filterpille am Handy — ein Muster, zwei Bildschirmgroessen. */
.t-mod-nav button {
  display: flex;
  align-items: center;
  gap: var(--s3);
  width: 100%;
  padding: 9px var(--s3);
  border-radius: var(--r-pill);
  color: var(--ink-soft);
  text-align: left;
  margin-bottom: 2px;
  white-space: nowrap;
}
.t-mod-nav button:hover {
  background: var(--surface-alt);
}
.t-mod-nav button.is-active {
  background: var(--accent-soft);
  color: var(--accent-soft-ink);
  font-weight: 600;
}
.t-mod-nav .count {
  margin-left: auto;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.t-mod-nav-sep {
  height: 1px;
  background: var(--line);
  margin: var(--s3) var(--s2);
}
/* Der Ausgang aus dem Turnier, 2026-08-26. Er steht ueber den Ansichten,
   weil er keine ist — deshalb eine Haarlinie darunter statt zwischen den
   Ansichten. Leiser als die Ansichts-Eintraege (kleiner, gedaempft): er
   ist der seltene Klick, nicht der haeufige. Die weiche Linie ist
   dieselbe wie am rechten Nav-Rand — ein Trennmuster, nicht zwei.

   Die Selektoren tragen `.t-mod-nav button` mit: die Basisregel oben ist
   (0,1,1), eine blosse `.t-mod-nav-back`-Klasse (0,1,0) verliert gegen sie
   und border-radius/color kaemen nie an. Vierte Kollision dieser Art in
   dieser Datei — deshalb hier ausgeschrieben statt kurz. */
.t-mod-nav button.t-mod-nav-back {
  font-size: 13px;
  color: var(--ink-soft);
  padding-bottom: var(--s3);
  margin-bottom: var(--s3);
  border-bottom: 1px solid var(--line-soft);
  border-radius: 0;
}
.t-mod-nav button.t-mod-nav-back:hover {
  background: transparent;
  color: var(--ink);
}
/* „Neues Turnier" am Fuss der Spalte, 2026-08-26. Spiegelbild zum Ausgang
   oben: Linie DARUEBER statt darunter, weil es wieder keine Ansicht ist.
   Gleiche Zurueckhaltung, aber in Akzentfarbe — es ist die einzige
   erzeugende Handlung im Modul, und der Auto-Sprung macht diese Spalte
   fuer den Organisator zum Regelfall statt zur Ausnahme.
   Wieder mit `.t-mod-nav button` davor: die Basisregel ist (0,1,1). */
.t-mod-nav button.t-mod-nav-neu {
  font-size: 13px;
  color: var(--accent);
  font-weight: 600;
  padding-top: var(--s3);
  margin-top: var(--s3);
  border-top: 1px solid var(--line-soft);
  border-radius: 0;
}
.t-mod-nav button.t-mod-nav-neu:hover {
  background: transparent;
  text-decoration: underline;
}

/* ----------------------------------------------------------------
   MOBILE TAB-LEISTE — verbindlich (§13 Bug aus altem Stand)
   Die Leiste selbst hat eine FESTE Höhe (height, nicht min-height),
   damit sie nicht mitwächst, wenn ein Tab einen längeren Namen hat.
   Pro Tab: flex:0 0 auto, gleiche feste Höhe, line-height:1,
   white-space:nowrap. Damit ist die Höhe tab-unabhängig konstant.

   Verwendung in Screen A (Beispiel):
     <div class="t-mod-tabs" role="tablist">
       <button class="t-mod-tab is-active">Übersicht</button>
       <button class="t-mod-tab">Spielplan</button>
       <button class="t-mod-tab">Gruppen</button>
       <button class="t-mod-tab">Baum</button>
     </div>
   ---------------------------------------------------------------- */
.t-mod-tabs {
  /* SICHTBAR nur < 768px. Auf Desktop versteckt (siehe Media Query). */
  display: none;
  position: relative; /* Bug 15 Politur (2026-08-18): Bezugspunkt
                               für den rechten Scroll-Fade-Hinweis. */
  height: 44px; /* FIXE Höhe, kein min-height */
  align-items: center;
  flex-wrap: nowrap;
  gap: var(
    --s1
  ); /* Bug 15: 4px Mindestabstand zwischen Tabs,
                               damit "Spielplan" und "Übersicht" nicht
                               zusammenkleben. */
  padding: 0 var(--s3); /* Bug 15: linker Einzug, damit der erste
                               Tab nicht am Viewport-Rand klebt. */
  overflow-x: auto;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  scrollbar-width: none;
}
.t-mod-tabs::-webkit-scrollbar {
  display: none;
}
/* Bug 15 Politur (2026-08-18, User-Punkt 1): Auf Mobile hat die Tab-
   Leiste 8 Einträge, die bei 390px Breite nicht alle passen — sie
   scrollt horizontal, aber der User sieht das nicht (Scrollbar ist
   versteckt, rechts abgeschnittene Tabs wirken wie verlorene
   Inhalte). Ein weicher Fade von transparent zu var(--surface) am
   rechten Rand zeigt: „Da geht's weiter, scroll nach links". 32px
   Breite reicht für einen sanften Übergang ohne den ersten Tab
   dahinter zu verstecken.
   P3 (2026-08-24): Auf Mobile wird .t-mod-tabs jetzt als Bottom-Bar
   gefixed (siehe unten). Der Fade-Hinweis wird in dem Container-Query
   per display:none ausgeblendet, weil wir 4 fixe Items haben statt
   horizontalem Scroll. */
.t-mod-tabs::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 32px;
  background: linear-gradient(to right, transparent, var(--surface));
  pointer-events: none;
  border-bottom: 1px solid var(--line); /* Border-Bottom mitziehen,
                                            damit der Fade-Block den
                                            unteren Strich nicht
                                            unterbricht. */
}

/* .t-mod-tab-Definitionen sind jetzt im P3-Block weiter unten
   (flex-direction: column für Bottom-Bar-Buttons). Die frühere
   horizontale Variante (flex: 0 0 auto, height 44 px, border-bottom)
   ist obsolet. */

/* P3 (2026-08-24): Mobile Bottom-Navigation.
   Auf ≤600 px Modulbreite wird .t-mod-tabs fixed am unteren Bildrand
   positioniert (war vorher horizontal scrollbar oben). User-Klärung:
   auf Mobile KEINE Top-Tabs mehr — die Leiste sitzt entweder unten
   (≤600 px) oder sie ist ausgeblendet (>600 px, Sidebar übernimmt).
   Das verhindert doppelte Navigation.

   Breakpoint 600 px statt 767 px: bei 768 px reicht der Platz für die
   Sidebar, bei 600 px nicht mehr. Konsistent mit dem Bottom-Bar-Konzept. */
@container (max-width: 600px) {
  .t-mod-tabs {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* EINZIGE Wahrheit fuer die Hoehe der Bodenleiste. Hoehe und
       safe-area gehoeren zusammen: 56px Inhalt (= .t-mod-tab height)
       PLUS der Home-Indicator, den padding-bottom danach wieder frei
       raeumt. Wer hier etwas aendert, aendert es NUR hier — ein zweiter
       height-Wert in einem spaeteren Block (z.B. dem 767er, der bei
       <= 600px mitgreift) gewinnt ueber die Quell-Reihenfolge und
       zerlegt die Leiste. Genau das war der Bug vom 2026-08-25;
       Herleitung im Kommentar im 767er-Block weiter unten. */
    height: calc(56px + env(safe-area-inset-bottom, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: var(--surface);
    border-top: 1px solid var(--line);
    border-bottom: none;
    overflow-x: hidden;
    z-index: 50;
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.06);
  }
  /* Fade-Hinweis am rechten Rand ist auf der Bottom-Bar sinnlos —
     wir haben 4 fixe Items, kein Scroll. */
  .t-mod-tabs::after {
    display: none;
  }

  /* Sidebar aus, Bottom-Bar an — keine zwei parallelen Leisten. */
  .t-mod-nav {
    display: none;
  }
}

/* Hauptbereich: Padding-Bottom für die fixierte Bottom-Bar.
   WICHTIG: Diese Regel MUSS nach dem @media-Block weiter unten kommen,
   weil .t-mod-main dort als Shorthand `padding: var(--s4) var(--s3)`
   gesetzt wird — bei gleicher Spezifität gewinnt Source-Order, und
   ohne diese Position würde der mobile Shorthand das padding-bottom
   wieder auf var(--s4) zurücksetzen und die letzte Zeile unter der
   Bar verschwinden. (Bug-Fix 2026-08-24 nach Browser-Screenshot.) */
@container (max-width: 600px) {
  .t-mod-main {
    padding-bottom: calc(56px + env(safe-area-inset-bottom, 0px) + var(--s4));
  }
}

/* Bottom-Sheet für „Mehr" (P3, 2026-08-24).
   Slide-up von unten, halbtransparenter Overlay, safe-area-inset-
   bottom für iOS-Notch-Geräte. */
.t-mod-more-sheet {
  display: none;
}
.t-mod-more-sheet[data-open='true'] {
  display: block;
}

.t-mod-more-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 998;
  border: none;
  cursor: pointer;
  padding: 0;
  margin: 0;
}
.t-mod-more-panel {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 999;
  background: var(--surface);
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  padding: 16px 0 calc(16px + env(safe-area-inset-bottom, 0px));
  box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.12);
  animation: t-sheet-slide-up 220ms ease-out;
  max-height: 80vh;
  overflow-y: auto;
}
@keyframes t-sheet-slide-up {
  from {
    transform: translateY(100%);
  }
  to {
    transform: translateY(0);
  }
}
.t-mod-more-close {
  position: absolute;
  top: 8px;
  right: 12px;
  background: transparent;
  border: none;
  font-size: 18px;
  color: var(--ink-soft);
  cursor: pointer;
  padding: 4px 8px;
  line-height: 1;
}
.t-mod-more-close:hover {
  color: var(--ink);
}
.t-mod-more-title {
  margin: 0 24px 12px 16px;
  padding-right: 32px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.t-mod-more-list {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: 0;
}
.t-mod-more-list button {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 14px 20px;
  background: transparent;
  border: none;
  font-size: 15px;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  font-family: inherit;
}
.t-mod-more-list button:hover {
  background: var(--surface-alt, rgba(0, 0, 0, 0.04));
}
.t-mod-more-list button.is-active {
  color: var(--accent);
  font-weight: 600;
}
.t-mod-more-list svg {
  flex: none;
  width: 22px;
  height: 22px;
  color: var(--ink-soft);
}
.t-mod-more-list button.is-active svg {
  color: var(--accent);
}

/* P3: Bottom-Bar-Buttons zeigen Icon + Label vertikal.
   Layout kompakt, damit 4 Items auf 360 px passen. */
.t-mod-tab {
  flex: 1 1 0; /* gleichmäßig verteilt, schrumpfbar */
  min-width: 0;
  height: 56px; /* FIXE Höhe der Bottom-Bar */
  padding: 0 var(--s1);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  border: none;
  background: transparent;
  /* Werte aus dem Artefakt, Abschnitt 01 (.bnav a): Archivo Narrow,
     halbfett, 10.5px, gesperrt, Versalien. `font-family: inherit` stand
     hier vorher und war wirkungslos, solange der .t-mod-button-Reset
     dieselbe Eigenschaft mit hoeherer Gewichtung setzte — jetzt ist die
     Komponente ausgetragen und traegt ihre Schrift selbst. */
  color: var(--muted);
  font-family: var(--f-cond);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  cursor: pointer;
}
.t-mod-tab:hover {
  color: var(--ink);
}
/* Der aktive Reiter traegt Farbe, keinen Balken. Die Vorlage kennt in der
   Bodenleiste keinen Strich — das ist die einzige Stelle im Modul, an der
   Orange nicht den Weg zum Titel meint, sondern den Ort. Ein zusaetzlicher
   Balken machte daraus eine zweite Aussage auf demselben Element. */
.t-mod-tab.is-active {
  color: var(--flare);
}
.t-mod-tab svg {
  flex: none;
  width: 22px;
  height: 22px;
}
.t-mod-tab > span {
  font-size: 11px;
  line-height: 1.1;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* ----------------------------------------------------------------
   NAVIGATIONSLUECKE 601-767px (geschlossen 2026-08-25)
   ----------------------------------------------------------------
   .t-mod-nav war ab Containerbreite <=767px ausgeblendet, die
   Bodenleiste erscheint aber erst ab <=600px. Dazwischen hatte das
   Modul WEDER Seitenleiste NOCH Leiste — gar keine Navigation.

   Im Browser nachgemessen (Edge, .t-mod als Container in #content):
       viewport 1500 -> container 1208  Nav sichtbar
       viewport 1059 -> container  767  Nav WEG, keine Leiste   <- Beginn
       viewport  900 -> container  872  Nav sichtbar (die App-Seitenleiste
                                        klappt bei <=900px weg, das Modul
                                        wird dadurch schlagartig breiter)
       viewport  795 -> container  767  Nav WEG, keine Leiste
       viewport  628 -> container  600  Bodenleiste erscheint
   Das sind ZWEI getrennte Fensterbaender (901-1059 und 629-900), zusammen
   326px Fensterbreite ohne jede Navigation — nicht 167px, wie die reine
   Rechnung am Container nahelegt. Die App-Seitenleiste verbiegt die
   Abbildung viewport->container; lesbar ist die Luecke nur am Container.

   ZWEI NAHELIEGENDE FIXES WURDEN GEBAUT UND VERWORFEN:

   (1) Bodenleiste bis 767px hochziehen. Sie ist `position: fixed;
       left: 0; right: 0` und spannt damit ueber das GANZE FENSTER, nicht
       nur ueber das Modul. Im oberen Band (viewport 901-1059) ist die
       App-Seitenleiste sichtbar — die mobile Leiste laege dann quer
       darueber. Im Prototyp-Screenshot bestaetigt.

   (2) Modul-Seitenleiste bis 601px herunterziehen (Schwelle 768 -> 601).
       Kostet 160px Nav + Rasterluft und wurde AM BILD widerlegt: die
       Gruppentabelle steht im Desktop-Satz mit 9 Spalten, solange der
       Container > 600px ist (die Compact-Umschaltung in
       spielplan-helpers.js haengt an genau derselben 600er-Grenze).
       Gemessene Kopfzeilen-Truncation (scrollWidth > clientWidth):
           ohne Nav  container 767 -> main 743  abgeschnitten: keine
           ohne Nav  container 612 -> main 593  abgeschnitten: Becher
           MIT Nav   container 767 -> main 554  abgeschnitten: Becher
           MIT Nav   container 612 -> main 420  abgeschnitten: Pl., Sp.,
                                                S, U, N, Becher, Diff, Pkt.
       Unterhalb von ~580px Hauptbereich zerfaellt die Tabelle. Mit einer
       160px-Nav ist das ab Container 767 abwaerts unvermeidlich.

   GEWAEHLT: In diesem Band bekommt .t-mod-tabs wieder ihre URSPRUENGLICHE
   Form — die horizontale Leiste OBEN, im Fluss des Moduls. Die kostet
   keine Breite (kein 160px-Abzug), ist kein fensterweit fixiertes
   Element (kein Uebergriff auf die App-Seitenleiste) und liegt im
   Container, folgt also der Modulbreite statt der Fensterbreite.
   Die Basisregel von .t-mod-tabs weiter oben traegt diese Form bereits
   vollstaendig (position: relative, height: 44px, overflow-x: auto,
   border-bottom, Fade-::after) — ihr fehlte nur das display.

   ZUR HOEHEN-WAHRHEIT DER BODENLEISTE (siehe Warnung im 767er-Block
   weiter unten): Dieser Block ist ein BEREICH, min-width 601 UND
   max-width 767. Er kann bei <=600px gar nicht greifen. Die Geometrie
   der Bodenleiste bleibt damit unberuehrt und hat weiterhin genau eine
   Wahrheit — den Block @container (max-width: 600px) weiter oben.
   ---------------------------------------------------------------- */
@container (min-width: 601px) and (max-width: 767px) {
  .t-mod-tabs {
    display: flex;
  }
  /* .t-mod-tab ist seit P3 fuer die Bodenleiste gebaut (Spalte, 56px,
     Icon ueber Label). Hier zurueck auf die Zeilenform, die in die
     44px-Leiste passt. Hoehe bewusst NICHT gesetzt — die kommt aus
     .t-mod-tabs; hier wird nur der Button an die Leiste angepasst. */
  .t-mod-tab {
    flex: 0 0 auto;
    flex-direction: row;
    gap: var(--s2);
    padding: 0 var(--s3);
    align-self: stretch;
    font-size: 14px;
    border-top: none;
    border-bottom: 2px solid transparent;
  }
  .t-mod-tab.is-active {
    border-top-color: transparent;
    border-bottom-color: var(--accent);
  }
  .t-mod-tab svg {
    width: 18px;
    height: 18px;
  }
  .t-mod-tab > span {
    font-size: 14px;
  }
}

/* Hauptbereich (mittlere Spalte) */
/* ----------------------------------------------------------------
   DAS SEITLICHE POLSTER GEHOERT DER ANSICHT, NICHT DEM CONTAINER
   ----------------------------------------------------------------
   Artefakt Abschnitt 04: „Die Tabelle verliert ihre Huelle und gewinnt
   Breite: das Rangband sitzt jetzt buendig an der Blattkante." Und
   Abschnitt 09: „Randlos wird aus dem Fleck eine Bahn."

   Solange .t-mod-main seitlich polstert, kann NICHTS buendig an der
   Blattkante sitzen — auch nicht mit `left: 0`, denn diese Null bezieht
   sich auf den Inhaltsbereich, nicht auf das Blatt. Die Vorlage kennt
   deshalb kein Polster am Bildschirm: dort bringt JEDES Bauteil seine
   20px selbst mit (.hd, .segs, .tblk .th, die Tabellenzellen, .rw,
   .glbl, .tfoot), und wer buendig sein will, laesst sie weg.

   Umgesetzt mit einer Ebene weniger Aufwand und derselben Wirkung: das
   Polster wandert von .t-mod-main auf .t-view. Bauteile, die buendig
   sein muessen, brechen mit demselben Token wieder aus — ein Wert, zwei
   Vorzeichen, keine Drift.
   ---------------------------------------------------------------- */
.t-mod-main {
  padding: var(--s6) 0;
  overflow: auto;
  /* Falls Karten in sehr engen Modul-Breiten (≤320px) doch mal
     breiter werden als die Spalte: Clip statt Scroll — die Seite
     darf sich NIE seitlich schieben lassen (§13 Punkt 1). */
  overflow-x: clip;
  min-width: 0;
}
.t-view {
  display: none;
}
.t-view.is-active {
  display: block;
  padding: 0 var(--s5);
}
/* Die zwei Ausbrecher. Beide sind in der Vorlage buendig an der
   Blattkante gezeichnet:
     die TABELLEN, weil Rangband und Toenung ueber die volle Breite
       laufen sollen — ihre Zellen holen die 20px selbst zurueck
       (siehe Block „Tabelle ohne Huelle");
     die SPIELPLAN-LISTE, weil die Karten dort mit 8px Rand stehen und
       die Zeitmarke genau ueber deren Kanten laeuft. Mit dem Polster
       der Ansicht saessen sie bei 28px und die Achse waere eingerueckt.
   `margin-inline` mit demselben Token, aus dem das Polster kommt: eine
   Wahrheit, zwei Vorzeichen.

   KEIN Kind-Kombinator: die Gruppentabellen haengen unter einem
   Montagepunkt (`[data-tab-body="gruppen-mount"]`), sind also Enkel der
   Ansicht. Mit `>` traf die Regel nie — dieselbe Falle wie beim
   Listen-Host, wo `#content:has(> .t-list-host)` zwei Ebenen daneben
   griff. Gemessen im Pruefstand: das Rangband sass danach nicht buendig,
   sondern 8px WEITER innen als vorher. */
.t-view.is-active #t-schedule-list,
.t-view.is-active .t-standings-karte,
.t-view.is-active .t-thirds-card {
  margin-inline: calc(-1 * var(--s5));
}
.t-view-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-bottom: var(--s4);
  flex-wrap: wrap;
}
.t-view-title {
  font-size: 20px;
  font-weight: 600;
}
.t-view-head .spacer {
  margin-left: auto;
}

/* Kontextspalte (rechte Spalte) */
.t-mod-aside {
  border-left: 1px solid var(--line);
  background: var(--surface);
  padding: var(--s4);
  overflow: auto;
}
.t-aside-block {
  margin-bottom: var(--s6);
}

/* ----------------------------------------------------------------
   RESPONSIVE BREAKPOINTS (Spec §8.1) — als CONTAINER QUERIES auf
   .t-mod, damit das Modul auf seine EIGENE Breite reagiert, nicht
   auf die Fensterbreite. Das ist wichtig, weil [kru:]nest eine eigene
   Seitenleiste hat (236px Desktop, ausgeblendet ≤900px).

   Schwellen (Modul-eigene Breite):
   - ≥1500px   : 3 Spalten (Nav + Main + Aside)
   - 768–1499 : 2 Spalten (Nav + Main), Aside HIDDEN — der Aside-Inhalt
                 wandert in die Übersicht des Main-Views (§8.1).
   - <768px    : 1 Spalte, alles gestapelt, mobile Tab-Leiste
   ---------------------------------------------------------------- */
@container (min-width: 768px) {
  /* Tablet (768–1499): Nav schmaler (160px), damit 2 Karten pro Zeile
     passen. Spec §8.1 nennt 212px nur für Desktop (≥1500). */
  .t-shell {
    grid-template-columns: 160px minmax(0, 1fr);
  }
  /* Spec §8.1: "Kontextspalte verschwindet unter 1500px, Inhalt wandert
     in die Übersicht." Aside bleibt im DOM, wird aber nicht gerendert —
     Screen-A/B fügen die Inhalte dem Übersicht-Tab im Main bei. */
  .t-mod-aside {
    display: none;
  }
}
@container (min-width: 1500px) {
  .t-shell {
    grid-template-columns: 212px minmax(0, 1fr) 300px;
  }
  .t-mod-aside {
    display: block;
  }
}
@container (max-width: 767px) {
  .t-mod-nav {
    border-right: none;
    border-bottom: 1px solid var(--line);
  }
  /* Bug 15 (2026-08-18, User-Punkt 1): Nav bleibt hidden, weil die
     horizontale .t-mod-tabs-Leiste die Navigation übernimmt. Hier
     defensiv doppelt setzen (oben ist .t-mod-nav auch display:none
     im globalen Block). */
  .t-mod-nav {
    display: none;
  }
  /* Bug 15 (2026-08-18, User-Punkt 3): Header fressst ein Drittel des
     Bildschirms, wenn Logo + Titel + Badge + Buttons alle in einer
     flex-Reihe liegen. Auf Mobile zwei sichtbare Zeilen:
       Zeile 1: Logo · Titel · Sub · Badge (alles in einer Reihe,
               Logo schrumpft nicht, Titel-Text wächst)
       Zeile 2: Header-Aktionen (volle Breite, gleichmäßig verteilt)
     flex-basis: 100% auf .t-mod-header-actions zwingt die Aktions-
     Reihe in eine eigene Zeile, auch wenn in Zeile 1 noch Platz wäre. */
  .t-mod-header {
    padding: var(--s3) var(--s4);
    gap: var(--s3);
  }
  .t-mod-header > .t-logo {
    flex: 0 0 auto;
  }
  /* Bug 15 (2026-08-18, User-Punkt 3): Auf Mobile fasst der Renderer
     Titel + Badges in einem .t-mod-header-info-Container zusammen.
     Hier: Titel-Text links wächst, Badges rechts. Bei schmalem
     Viewport fallen die Badges unter den Titel-Text, weil der
     Container flex-wrap: wrap hat. */
  .t-mod-header-info {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s2);
    flex: 1;
    min-width: 0;
  }
  .t-mod-header-info .t-mod-header-text {
    flex: 1 1 auto;
    min-width: 0;
  }
  .t-mod-header-info .t-mod-header-text .t-title {
    font-size: 17px;
  }
  .t-mod-header-info .t-mod-header-badges {
    display: flex;
    gap: var(--s2);
    flex-wrap: wrap;
  }
  .t-mod-header-info .t-mod-header-badges .t-badge {
    flex: 0 0 auto;
  }
  /* Bug 15 (2026-08-18, User-Punkt 4): Kontextspalte entfällt auf
     dem Handy — die Infos wandern in die Übersicht (Etappe B.2).
     Hier defensiv display:none, falls die CSS-Spezifität von oben
     (min-width:768px) durch andere Regeln überschrieben wird. */
  .t-mod-aside {
    display: none;
  }
  /* P5-Reparatur 2026-08-25: Verschachtelte Scrollcontainer auflösen.
     Auf Mobile wächst .t-mod mit seinem Inhalt mit; #content ist der
     einzige Scroller. overflow: auto auf .t-mod-main / .t-mod-aside
     würde sonst innerhalb des scrollenden Vorfahren eine falsche
     Client-Width-Berechnung für table-layout: fixed auslösen
     (Preview-Messung 2026-08-25: Tabelle 555 px in Karte 587 px).
     Desktop-Verhalten (overflow: auto für evtl. lange Sidebars) bleibt
     unverändert. */
  .t-mod-main {
    overflow: visible;
    overflow-x: clip; /* §13 P1: Seite darf NIE seitlich scrollen */
  }
  .t-mod-aside {
    overflow: visible;
  }
  /* Hauptbereich etwas knapper padden — auf 390px ist jeder Pixel
     sichtbar. SEITLICH ist er seit der dritten Fassung ohnehin bei 0:
     das Polster traegt .t-view, damit die Tabellen daraus ausbrechen
     koennen. Hier bleibt nur die geringere Hoehe. */
  .t-mod-main {
    padding: var(--s4) 0;
  }

  /* ----------------------------------------------------------------
     HIER STAND EINE HOEHEN-KOLLISION (aufgeloest 2026-08-25)
     ----------------------------------------------------------------
     Entfernt wurden:
         .t-mod-tabs { height/min-height/max-height: 48px }
         .t-mod-tab  { height: 48px; font-size: 14px }

     Beides waren Reste der alten HORIZONTALEN Top-Tab-Leiste, die der
     Kommentar bei .t-mod-tab oben schon als obsolet bezeichnet, aber
     nie geloescht hat. Seit P3 (2026-08-24) ist .t-mod-tabs auf Mobile
     eine FIXIERTE BODENLEISTE, definiert im Block
     @container (max-width: 600px) weiter oben:
         height: calc(56px + env(safe-area-inset-bottom, 0px));
         padding-bottom: env(safe-area-inset-bottom, 0px);

     Warum das ein Fehler war — und nicht nur Ballast:
     600px-Block und 767px-Block greifen bei <= 600px BEIDE. Container-
     Queries erhoehen die Spezifitaet nicht, beide Selektoren sind
     (0,0,1,0) — also gewinnt die Quell-Reihenfolge, und der 767er steht
     spaeter. Ergebnis: height 48px statt calc(56px + safe-area), dazu
     max-height 48px als harte Klammer. Das padding-bottom aus dem
     600er-Block blieb aber aktiv, und mit box-sizing: border-box
     (Reset oben) frisst es die Hoehe von innen auf:
         iPhone mit 34px Home-Indicator -> 48 - 34 = 14px Inhaltshoehe
         fuer ein 22px-Icon plus 11px-Label.

     DIE FALLE FUER DEN NAECHSTEN (dritter Fall dieser Art in dieser Datei;
     die beiden frueheren: das padding-Shorthand von .t-mod-main, siehe
     Kommentar vor dem 600er-Block oben, und der P7-Fix 3655565 am
     #content-padding in main.css):
       1. Der Breakpoint einer Regel sagt NICHT, in welchem Bereich sie
          allein gilt. 767 und 600 ueberlappen; wer bei <= 600 etwas
          festlegen will, muss pruefen, ob der 767er-Block dieselbe
          Eigenschaft spaeter nochmal setzt.
       2. Die Bodenleisten-Geometrie hat GENAU EINE Wahrheit: den Block
          @container (max-width: 600px) oben. Wer die Hoehe der Leiste
          oder der Tabs aendern will, aendert sie DORT — nicht hier, und
          nicht per zusaetzlicher Regel weiter unten.
       3. Hoehe und safe-area gehoeren zusammen: height ohne
          env(safe-area-inset-bottom) im calc() ergibt auf Geraeten mit
          Home-Indicator eine zu flache Leiste, sobald padding-bottom
          gesetzt ist.
     ---------------------------------------------------------------- */
}

/* ----------------------------------------------------------------
   TITEL / HEADER-ELEMENTE
   ---------------------------------------------------------------- */
.t-logo {
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: var(--ink);
  color: var(--paper);
  display: grid;
  place-items: center;
  font-weight: 700;
  flex: none;
}
/* Der Display-Schnitt traegt die Ansicht. 27px ist bewusst gross fuer
   390px — der Titel darf die Kopfzeile dominieren, weil alles andere
   dort klein und in Mono steht. */
.t-title {
  font-family: var(--f-disp);
  font-size: 27px;
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--ink);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Die eine Aktion. Orange, Versalien, Condensed — sie liest sich als
   Beschriftung, nicht als Knopf, und braucht deshalb keinen Rahmen.
   Mehr als eine gibt es nicht; alles Weitere liegt im Mehr-Blatt. */
.t-mod-action {
  font-family: var(--f-cond);
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--flare);
  background: none;
  border: 0;
  padding: 4px 2px;
  cursor: pointer;
  white-space: nowrap;
  flex: none;
}
.t-mod-action:hover {
  color: var(--ink);
}
/* .t-sub bleibt fuer Stellen, die noch einen Untertitel fuehren.
   Im neuen Kopf uebernimmt der Kicker diese Rolle. */
.t-sub {
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--muted);
}

/* ----------------------------------------------------------------
   BADGES
   ---------------------------------------------------------------- */
.t-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  background: var(--surface-alt);
  color: var(--ink-soft);
  white-space: nowrap;
}
.t-badge--phase {
  background: var(--accent-soft);
  color: var(--accent-soft-ink);
}

/* ----------------------------------------------------------------
   BUTTONS — eine Größe + drei Modifiers
   ---------------------------------------------------------------- */
/* Dritte Fassung, Abschnitt 07: der Knopf ist eine gefuellte PILLE ohne
   Kontur. Vorher: weisse Flaeche mit 1px-Rahmen in --line auf weissem
   Blatt — eine Kontur, die nur behauptet, dass man hier druecken darf.
   Jetzt sagt es die Fuellung. --sunk ist die eingesenkte Flaeche, die
   auch Schrittwaehler, Eingabefelder und Herkunftspillen tragen; in
   beiden Themen liegt sie UNTER dem Blatt, nicht darueber.

   Der Rahmen bleibt als `1px solid transparent` stehen, obwohl er nichts
   mehr zeichnet: .t-btn--ghost setzt seit jeher nur `border-color` und
   verliesse sich sonst auf eine Breite, die es nicht mehr gaebe — die
   Knopfreihe wuerde beim Ueberfahren um 2px springen.

   Schrift: Archivo Narrow, Versalien, gesperrt. Dieselbe Sprache wie
   die Segmentpillen und die Kopf-Aktion — im ganzen Modul gibt es nur
   noch diese eine Beschriftungsform fuer Handlungen. */
.t-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  padding: 9px 16px;
  border-radius: var(--r-pill);
  font-family: var(--f-cond);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  border: 1px solid transparent;
  background: var(--sunk);
  color: var(--ink);
  white-space: nowrap;
}
/* KASKADENKOLLISION Nr. 5 (aufgeloest 2026-08-25).
   `.t-btn:hover` ist (0,2,0) — eine Klasse PLUS eine Pseudoklasse — und
   schlaegt damit jede Varianten-Regel `.t-btn--primary` / `--danger`
   (0,1,0). Die Varianten-Hover-Regeln setzten nur `filter`, kein
   `background`. Folge: sobald der Zeiger einen gefuellten Knopf
   beruehrte, fiel seine Fuellung auf --surface-alt zurueck und der
   weisse Text stand auf hellem Grund — "Endgueltig loeschen" war beim
   Ueberfahren ein leeres Kaestchen mit duennem rotem Rand, gemessen
   1.13:1. Der gefaehrlichste Knopf der App verlor seine Beschriftung
   genau in dem Moment, in dem man ihn anfasst.
   REGEL DARAUS: wer eine Variante ueber eine Fuellung definiert, muss
   ihren :hover die Fuellung ERNEUT setzen — `filter` allein ist kein
   Ersatz, weil es die Herkunft des `background` nicht aendert.
   Der Fix steht bewusst bei den Varianten und nicht als
   `.t-btn:not(...)` an der Basis: die Basis-Hover soll fuer den
   Standardknopf weiter gelten. */
.t-btn:hover {
  background: var(--surface-alt);
}
/* KASKADENKOLLISION Nr. 9, gemessen 2026-08-26 im Browser.
   Hier stand `color: var(--accent-ink)`. Dieser Token ist in tokens.css
   NUR in den beiden Dunkel-Bloecken definiert — im Hellmodus gibt es ihn
   nicht. Eine undefinierte Custom Property loescht die GANZE Deklaration
   still: der Knopf fiel auf `color: var(--ink)` aus der Basisregel
   zurueck und trug damit tintenschwarze Schrift auf Orange.
   Gemessen: rgb(11,11,12) auf rgb(191,68,19) = 3,73 : 1, durchgefallen.
   Sichtbar war es nicht — schwarz auf Orange sieht nicht kaputt aus,
   es sieht nur schlecht aus, und im Nachtmodus stimmte es sogar.
   --on-flare aus dem Artefakt ist in ALLEN DREI Traeger-Bloecken
   definiert (hell #FFFFFF = 5,19 : 1, dunkel #160A04) und ist genau
   dafuer gemacht: die Schrift AUF der Signalfarbe. */
.t-btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-flare);
}
.t-btn--primary:hover {
  background: var(--accent); /* Nr. 5: Fuellung erneut setzen */
  border-color: var(--accent);
  filter: brightness(1.06);
}
.t-btn--sm {
  padding: 6px 12px;
  font-size: 12px;
}
.t-btn--ghost {
  border-color: transparent;
  background: transparent;
  color: var(--ink-soft);
}
/* Der Ghost-Knopf behaelt bewusst `border-width: 1px` bei
   `border-color: transparent` — sonst springt das Layout, sobald er
   neben einem berandeten Knopf steht oder selbst einen Rand bekommt. */
.t-btn--ghost:hover {
  background: var(--surface-alt);
  color: var(--ink);
}
.t-btn--onDark {
  background: rgba(255, 255, 255, 0.12);
  border-color: transparent;
  color: #fff;
}
.t-btn--onDark:hover {
  background: rgba(255, 255, 255, 0.2);
}

/* Größerer Modifier für Hero-CTAs — P5-Re-Fix-3 (2026-08-25):
   „K.-o.-Phase starten" sollte visuell wie ein Hauptbutton wirken,
   nicht wie eine schmale Tab-Aktion. Padding + Schrift + Icon-Größe
   hoch, damit der Block Aufmerksamkeit zieht. */
.t-btn--lg {
  padding: 12px 22px;
  font-size: 15px;
  font-weight: 600;
  border-radius: calc(var(--r-btn) + 2px);
  gap: var(--s3);
}
.t-btn--lg .t-btn-icon {
  width: 18px;
  height: 18px;
  stroke-width: 2.25;
  flex-shrink: 0;
}

/* ----------------------------------------------------------------
   KARTEN
   ---------------------------------------------------------------- */
.t-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
}
.t-card-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line);
}
.t-card-title {
  font-weight: 600;
  font-size: 15px;
}
.t-card-body {
  padding: var(--s4);
}

/* P5-Re-Fix-3 (2026-08-25): Hero-CTA-Block „K.-o.-Phase starten".
   Eigene Card unten unter den Gruppentabellen — visuell vom Rest
   abgesetzt, damit klar ist: das hier ist der nächste Schritt.
   Akzent-Border links + weicher Akzent-Hintergrund hebt den Block
   aus der Standard-Standings-Optik heraus. */
/* Der Farbverlauf ist mit der dritten Fassung entfallen: er war der
   einzige im ganzen Modul, und ein Verlauf ist eine Flaeche, die sich
   nicht entscheidet. Streifen und Optik stehen jetzt im Block
   „Tabelle ohne Huelle" weiter unten, wo auch die uebrigen Huellen
   abgeraeumt werden — an EINER Stelle, damit die Ausnahme dort steht,
   wo die Regel steht. */
.t-fill-ko-card {
  margin-top: var(--s4);
}
.t-fill-ko-card-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s3);
  padding: var(--s4) var(--s4);
}
.t-fill-ko-card-body > p {
  margin: 0;
}
.t-fill-ko-note {
  font-size: 13px;
  color: var(--ink-soft);
  max-width: 56ch;
}
.t-fill-ko-btn {
  box-shadow:
    0 1px 0 color-mix(in srgb, var(--accent) 60%, transparent),
    0 2px 6px color-mix(in srgb, var(--accent) 18%, transparent);
}
.t-fill-ko-btn:hover {
  box-shadow:
    0 1px 0 color-mix(in srgb, var(--accent) 70%, transparent),
    0 4px 10px color-mix(in srgb, var(--accent) 22%, transparent);
  transform: translateY(-1px);
}
.t-fill-ko-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  transform: none;
}

/* ----------------------------------------------------------------
   TOOLBAR + FILTER-CHIPS
   Filter-Reihenfolge im Spielplan (Spec §13.6):
   Alle · Nur offene · Beendet · Gruppenphase · K.O. · Spieltage · Gruppen
   ---------------------------------------------------------------- */
.t-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  padding: var(--s3) var(--s4);
  background: var(--surface-alt);
  border-bottom: 1px solid var(--line);
}
.t-chip {
  /* Ausgetragen aus dem .t-mod-button-Reset (Kollision Nr. 7) — deshalb
     traegt der Chip seine Schrift ab hier selbst. Werte aus dem Artefakt,
     Abschnitt 03: Archivo Narrow, halbfett, gesperrt, Versalien. */
  font-family: var(--f-cond);
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  cursor: pointer;
  padding: 7px 14px;
  border-radius: var(--r-pill);
  /* Dritte Fassung: „kein Behaelter, inaktiv ist reiner Text, aktiv eine
     tintengefuellte Pille." Die Segmentleiste war eine umrandete
     Vertiefung mit einer erhobenen Pille darin — drei Ebenen fuer vier
     Woerter. Der Rand bleibt als durchsichtige Kante stehen, damit der
     aktive Chip nicht um 2px waechst. */
  border: 1px solid transparent;
  background: none;
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.3;
  transition:
    background 0.12s,
    color 0.12s,
    border-color 0.12s;
}
.t-chip:hover {
  color: var(--ink);
}
/* Der aktive Chip ist SCHWARZ, nicht orange (Vorlage, Abschnitt 03).
   Vorher stand hier var(--accent). Orange ist im ganzen Modul dem Weg
   zum Titel vorbehalten — ein Filterzustand ist keiner. Wo Orange
   beides bedeutet, bedeutet es nichts mehr. */
.t-chip.is-active {
  background: var(--ink);
  color: var(--panel);
  border-color: var(--ink);
}
.t-chip.is-active .count {
  color: var(--panel);
  opacity: 0.72;
}
.t-chip .count {
  margin-left: 6px;
  font-size: 12px;
  opacity: 0.75;
  font-variant-numeric: tabular-nums;
}
.t-chip.is-active .count {
  opacity: 0.9;
}

/* ----------------------------------------------------------------
   FILTER-DROPDOWN (Etappe A2.6, 2026-08-20)
   ----------------------------------------------------------------
   Ein Filter-Trigger-Button + Dropdown. Aktive Auswahl wird als
   zusätzlicher Chip NEBEN dem Trigger angezeigt, damit der User
   auf einen Blick sieht, was gefiltert ist. Klick auf Trigger
   toggelt das Dropdown (durch main.js wireFilterDropdown).
*/
.t-filter-wrap {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  position: relative;
}
.t-filter-trigger {
  font: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink-soft);
  font-size: 13px;
  line-height: 1.3;
  transition:
    background 0.12s,
    color 0.12s,
    border-color 0.12s;
}
.t-filter-trigger:hover {
  background: var(--paper);
}
.t-filter-trigger[aria-expanded='true'] {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}
.t-filter-trigger-caret {
  font-size: 11px;
  transition: transform 0.15s;
}
.t-filter-trigger[aria-expanded='true'] .t-filter-trigger-caret {
  transform: rotate(180deg);
}
.t-filter-dropdown {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 50;
  min-width: 200px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r2, 8px);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
  padding: 4px;
}
.t-filter-dropdown[hidden] {
  display: none;
}
.t-filter-dropdown-inner {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.t-filter-item {
  font: inherit;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--ink-soft);
  font-size: 13px;
  text-align: left;
}
.t-filter-item:hover {
  background: var(--paper);
}
.t-filter-item.is-active {
  background: var(--accent);
  color: #fff;
}
.t-filter-item .count {
  font-size: 12px;
  opacity: 0.75;
  font-variant-numeric: tabular-nums;
}

/* ----------------------------------------------------------------
   HINWEIS-TEXT (Platzhalter, Ablauf, leere Filter-Treffer)
   ---------------------------------------------------------------- */
.t-hint {
  color: var(--ink-soft);
  font-size: 13px;
  font-style: italic;
  margin: 0;
}
.t-hint strong {
  color: var(--ink);
  font-style: normal;
  font-weight: 600;
}

/* P1 (2026-08-24): Kompakter Hinweis für Beste-Dritte-Tabelle bei
   mixedGroupSizes. Vorher war der Hint 3 Zeilen kursiv mit Spec-Verweis,
   jetzt EINE Zeile + Info-Icon als Pseudo-Element. */
.t-hint--compact {
  color: var(--ink-soft);
  font-size: 12px;
  font-style: normal;
  margin: 0 0 10px 0;
  display: flex;
  align-items: center;
  gap: 6px;
}
.t-hint--compact::before {
  content: '';
  width: 14px;
  height: 14px;
  flex: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238a7e74' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cline x1='12' y1='16' x2='12' y2='12'/%3E%3Cline x1='12' y1='8' x2='12.01' y2='8'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
}

/* ----------------------------------------------------------------
   ASIDE-LISTE (Plattenbelegung u. ä. Kompaktlisten in der Kontextspalte)
   ---------------------------------------------------------------- */
.t-aside-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  font-size: 13px;
}
.t-aside-list li {
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--line);
}
.t-aside-list li:last-child {
  border-bottom: none;
}
.t-aside-list strong {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* Die Mono-Beschriftung der Vorlage (`.glbl`): 10,5px, 0,17em gesperrt,
   in --muted, 22px Luft darueber und 8px darunter. Diese Asymmetrie ist
   der Punkt — oben viel, unten wenig bindet die Ueberschrift an das,
   was unter ihr steht. Sie beschriftet und trennt in einem Zug, und
   genau deshalb steht zwischen zwei Tabellen keine zweite Kontur mehr.
   Vorher fehlte der obere Abstand ganz: „Beste Dritte · 2 Plaetze frei"
   klebte im Screenshot bei 1920px an der Fusszeile der Gruppe darueber. */
.t-section-label {
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
  margin: 22px 0 8px;
}

/* ----------------------------------------------------------------
   GRUPPENTABELLE — A3 (Redesign Teil 1, umgesetzt 2026-08-25)
   ----------------------------------------------------------------
   Drei Aenderungen gegenueber Etappe B.3:

   1. AUSRICHTUNG. Vorher war alles linksbuendig — Zahlen standen links
      unter einer linksbuendigen Ueberschrift. Das sieht ausgerichtet aus,
      ist es aber nicht, sobald die Zahlen unterschiedlich lang sind.
      Jetzt: Text links, Zahlen rechts, Ueberschrift wie Wert.
   2. SYMBOLE. Vorher markierten Stern, Platznummer und Haken dasselbe.
      Jetzt genau ZWEI Signale: hinterlegte Flaeche (Status) und
      Trennlinie (Grenze). Der Haken ist mit A3 entfallen — die Regel
      dafuer stand in main.css und ist dort geloescht.
   3. ZUSTAENDE STATT ZAEHLUNG. `is-first`/`is-second` kodierten die
      Annahme "immer genau zwei steigen auf". Jetzt `is-qualified`,
      `is-cutoff`, `is-pending`, abgeleitet aus `qualifyPerGroup`
      (siehe renderStandingsGroups).

   ARBEITSTEILUNG MIT main.css — bitte vor dem naechsten Eingriff lesen:
   diese Komponente ist in BEIDEN Stylesheets definiert. main.css haelt
   die Layout-Maschinerie (table-layout, Colgroup-Hooks, die
   Mobile-Regeln unter `.t-mod .t-standings-table`), hier steht die
   Modul-Palette. tournament.css laedt SPAETER und gewinnt bei gleicher
   Spezifitaet — die Mobile-Regeln in main.css tragen deshalb bewusst
   ein `.t-mod ` davor. Wer hier eine Eigenschaft ergaenzt, die main.css
   bedingt setzt, macht sie dort still wirkungslos. */
.t-standings-group-title {
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-soft);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin: 0 0 var(--s3);
}
.t-standings-table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  table-layout: fixed;
}
.t-standings-table th,
.t-standings-table td {
  padding: 8px 6px;
  border-bottom: 1px solid var(--line);
}
.t-standings-table th {
  font-size: 10px;
  font-weight: 600;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding-bottom: 6px;
}

/* Ausrichtung: Text links, Zahlen rechts — Ueberschrift wie Wert.
   Die Rang-Spalte steht links. Der Doppelselektor ist Absicht und kein
   Versehen: main.css fuehrt `.t-standings-table .t-standings-rank`
   (0,0,2,0) mit `text-align: right` und schlaegt damit ein einfaches
   `.t-standings-rank` (0,0,1,0) — trotz frueherer Ladereihenfolge. Bei
   GLEICHER Spezifitaet gewinnt die spaeter geladene Datei, also diese.
   Das ist die Kollisionsklasse, die in diesem Modul schon achtmal
   zugeschlagen hat; hier wird sie ausdruecklich mitgezaehlt. */
.t-standings-table th.is-rank,
.t-standings-table th.is-team,
.t-standings-table .t-standings-rank,
.t-standings-table .t-standings-team {
  text-align: left;
}
.t-standings-table th.is-num,
.t-standings-num {
  text-align: right;
}

.t-standings-rank {
  color: var(--ink-soft);
  font-size: 12px;
}
/* Die Differenz traegt KEINE Ampelfarbe mehr (main.css setzte #2e7d32
   und #c62828, zwei hartcodierte Werte ausserhalb der Palette).
   Grund ist die Bedeutungsregel des Artefakts: Gruen heisst in diesem
   Modul „qualifiziert", Karmin heisst „Gefahr". Ein gruenes „+6" sagt
   also etwas, was es nicht meint — und es stand direkt neben einer
   gruenen Qualifikationszeile, die dasselbe Gruen fuehrt.
   Die Vorlage setzt die ganze Tabelle in --ink; die Zahl selbst hat ein
   Vorzeichen, das reicht. */
.t-standings-table .t-standings-num.is-positive,
.t-standings-table .t-standings-num.is-negative,
.t-thirds-table .t-thirds-num.is-positive,
.t-thirds-table .t-thirds-num.is-negative {
  color: var(--ink-2);
}
.t-standings-team {
  font-weight: 500;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.t-standings-num.is-points {
  font-weight: 700;
  color: var(--ink);
}

/* ----------------------------------------------------------------
   QUALIFIKATION — Markenuebernahme (2026-08-26)
   ----------------------------------------------------------------
   Der Rang traegt die Farbe, und zwar als BAND LINKS, nicht als
   Symbol in der Zelle und nicht als Flaeche allein.

   Zwei Zustaende:
     is-lead       Platz 1 — Orange. Die einzige Stelle in der Tabelle,
                   an der der Akzent auftaucht: hier faengt der Weg zum
                   Titel an.
     is-qualified  Platz 2..N — Gruen. Steigt auf, fuehrt aber nicht.

   WARUM DAS BAND UND NICHT NUR DIE FLAECHE:
   Am 25.08. hatte ich die beiden Zustaende ueber getoente Zeilen
   getrennt und gemessen, dass Gruen gegen Bernstein nur 1.02:1
   unterscheidbar ist — bei Rot-Gruen-Schwaeche also gar nicht. Damals
   trug eine Trennlinie das farbunabhaengige Signal. Der Marken-Entwurf
   loest es besser: das Band ist eine FORM am Zeilenanfang. "Band da"
   gegen "Band nicht da" liest jeder, unabhaengig von der Farbe, und die
   Grenze der Aufstiegsplaetze ist dort, wo die Baender aufhoeren. Die
   Trennlinie ist damit ueberfluessig geworden und entfallen — nicht
   weil sie stoerte, sondern weil ihre Aufgabe jetzt jemand anders macht.

   Die Flaechen bleiben als leiser Zweitton (soft-Varianten der Tokens,
   im Dunkelmodus rgba-Schleier). Sie tragen nichts allein, sie
   verstaerken nur das Band.
   ---------------------------------------------------------------- */
.t-standings-row.is-lead td {
  background: var(--flare-soft);
}
.t-standings-row.is-qualified td {
  background: var(--qual-soft);
}

/* Das Band. Sitzt auf der Rangzelle, weil die immer die erste ist —
   ein eigenes Element dafuer waere eine Spalte, die nur Farbe traegt. */
.t-standings-row.is-lead .t-standings-rank,
.t-standings-row.is-qualified .t-standings-rank {
  position: relative;
  font-weight: 600;
}
/* Dritte Fassung: 3px statt 4px und mit angeschnittener Aussenkante.
   Das Band sitzt jetzt BUENDIG an der Blattkante, weil die Tabelle ihre
   Huelle verloren hat (Block „Tabelle ohne Huelle" weiter unten) — in
   der Karte hoerte es 14px vor dem Rand auf und wirkte wie ein Fleck. */
.t-standings-row.is-lead .t-standings-rank::before,
.t-standings-row.is-qualified .t-standings-rank::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  border-radius: 0 3px 3px 0;
}
.t-standings-row.is-lead .t-standings-rank::before {
  background: var(--flare);
}
.t-standings-row.is-qualified .t-standings-rank::before {
  background: var(--qual);
}
/* ABWEICHUNG VOM ARTEFAKT, nachgerechnet.
   Die Vorlage setzt die Rangziffer in der getoenten Zeile auf die
   Signalfarbe (`tbody tr.lead td.pl { color: var(--flare) }`). Gemessen
   im Browser steht --flare #BF4413 auf --flare-soft #F2E1D8 bei
   4,08 : 1 und faellt damit durch — 12px, halbfett, also gilt 4,5.
   Die Paarung ist an KEINEM Toenungswert zu retten: eine hellere
   Toenung waere gegen den Papierton nicht mehr zu sehen (gerechnet
   4,22 : 1 selbst beim helleren #F7E5DC der zweiten Fassung), eine
   dunklere macht es schlimmer. Orange auf Orange geht nicht.

   Also traegt die Ziffer die Tinte. Das kostet nichts, weil der Zustand
   dreifach steht: 3-px-Band an der Blattkante, getoente Zeile, Position.
   Die Vorlage sagt das selbst — „Das Band links bleibt das
   farbunabhaengige Signal", und eine Ziffer, die man nicht lesen kann,
   ist kein Signal, sondern Dekoration. Die gruene Paarung besteht mit
   4,86 : 1; sie zieht trotzdem mit, damit die beiden Ranghinweise nicht
   verschieden gebaut sind. */
.t-standings-row.is-lead .t-standings-rank,
.t-standings-row.is-qualified .t-standings-rank {
  color: var(--ink);
}

/* Fusszeile der Gruppenkarte: die Regel steht unter der Tabelle, auf die
   sie sich bezieht — nicht in einem Hilfetext, den niemand oeffnet. */
/* Dritte Fassung: ohne Fuellung, mit Regel. „Plaetze 1–2 ziehen direkt
   ein" sass in einem grau gefuellten Band — jetzt eine Haarlinie
   darueber, sonst nichts. Der orange Strich davor ist derselbe wie das
   Rangband: Legende und Sache tragen dieselbe Form. */
.t-standings-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: 11px var(--s5) 2px;
  border-top: 1px solid var(--line-soft);
  background: none;
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}
.t-standings-foot > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Aus dem Quadrat wird ein Strich in der Form des Rangbandes: 10x3px mit
   voller Rundung. Ein Quadrat war die einzige Stelle im Modul, an der
   die Legende anders aussah als das, was sie erklaert. */
.t-standings-foot .t-foot-mark {
  display: inline-block;
  width: 10px;
  height: 3px;
  border-radius: var(--r-pill);
  background: var(--flare);
  margin-right: 8px;
  vertical-align: 3px;
}

/* ----------------------------------------------------------------
   SCREEN A — TURNIERLISTE (§13.2)
   ----------------------------------------------------------------
   Card-Grid: repeat(auto-fill, minmax(260px, 1fr))
   Spec sagt 340px, aber bei 390px Viewport (Real-Width Modul
   nach Padding ~286px) würde 340px zwangsweise horizontalen
   Scroll erzeugen. 260px ist der Kompromiss: bei 390px → 1 Spalte
   (passt), bei 820px → 2 Spalten (passt), bei 1920px → 3–4 Spalten
   (kein Rand >5 %).
   Pro Karte: Logo + Name + Datum + Status-Badge + Kurzinfo + Progress.
   ---------------------------------------------------------------- */
.t-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--s4);
}

/* Listen-Karte — kompakter als .t-card (für Wizard, Tabellen).
   Hat einen Hover-Afford, ist aber selbst noch KEIN klickbarer Link
   (Detail-View kommt in der nächsten Iteration). */
/* Artefakt Abschnitt 02: „Aus drei Turnierkarten werden drei Bloecke auf
   einem Blatt. Was jedes Turnier zusammenhaelt, ist der 20-px-
   Innenabstand — was sie trennt, eine Haarlinie."
   Der Rahmen bleibt als durchsichtige Kante stehen, weil der laufende
   Block ihn als Zustandsstreifen einfaerbt (unten) und die Zeile sonst
   beim Wechsel um 2px springt. */
.t-list-card {
  position: relative;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 17px var(--s5) 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  transition: background 0.15s ease;
}
.t-list-card + .t-list-card {
  border-top: 1px solid var(--line-soft);
}
.t-list-card:hover {
  background: var(--raised);
}
/* Der Streifen sagt „laeuft gerade". Vorher unterschied nur die Pille
   oben rechts das laufende Turnier von den anderen; jetzt ist es beim
   Ueberfliegen der Liste die erste Information, noch vor dem Namen —
   und es ist Farbe UND Ort, also auch ohne Farbsehen lesbar. */
.t-list-card:has(> .t-list-card-row > .t-list-card-status--running)::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--live);
}
.t-list-card-row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
}

/* Der Textblock zwischen Logo und Status-Badge. Nimmt den freien Platz
   und stapelt Name + Datum/Ort. min-width:0 ist Pflicht, sonst kann der
   Name nicht kürzen und schiebt das Badge aus der Karte. */
.t-list-card-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Die ganze Karte ist die Aktion — kein "Öffnen"-Knopf mehr
   (Redesign-Plan §3.3). role=button + tabindex kommen aus dem Renderer. */
.t-list-card[role='button'] {
  cursor: pointer;
}
.t-list-card[role='button']:hover {
  background: var(--raised);
}
/* Der Ring liegt INNEN: der Block reicht bis an die Blattkante, ein
   aeusserer Ring waere dort halb abgeschnitten. */
.t-list-card[role='button']:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* Logo — nur, wenn es eins GIBT. Der Ersatzkasten mit dem
   Anfangsbuchstaben ist mit der dritten Fassung entfallen (siehe
   renderTournamentListCard). Groesse und Radius wie im Modulkopf,
   damit dasselbe Bild an beiden Orten gleich aussieht — relativer
   Radius, Haarlinien-Ring, vertiefte Flaeche darunter. */
.t-list-card-logo {
  width: 44px;
  height: 44px;
  border-radius: 24%;
  background: var(--sunk);
  border: 1px solid var(--line-soft);
  flex: none;
  overflow: hidden;
}
.t-list-card-logo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Der Turniername ist das Größte auf der Karte (Redesign-Plan §3.3).
   Kein flex:1 mehr — die Breitenverteilung macht jetzt .t-list-card-main,
   der Name ist nur noch dessen erste Zeile. */
.t-list-card-name {
  font-family: var(--f-disp);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--ink);
  margin: 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Datum — klein, gedämpft, unter dem Namen */
.t-list-card-date {
  font-family: var(--f-mono);
  font-size: 11.5px;
  color: var(--muted);
}

/* Status-Badge — vier deutsche Zustände (§13.2).
   Hintergrund + Text-Farbe aus dem Token-System. */
/* Die Pille traegt die Schrift der Vorlage: Archivo Narrow, Versalien,
   gesperrt — dieselbe Sprache wie Segmente, Knoepfe und Kopf-Aktion. */
.t-list-card-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 11px;
  border-radius: var(--r-pill);
  font-family: var(--f-cond);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  white-space: nowrap;
  background: var(--sunk);
  color: var(--muted);
}
.t-list-card-status::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: currentColor;
  opacity: 0.85;
}
/* Vier hartcodierte Hex-Werte standen hier — #EEEAE3, #DCE7F0, #2F5F86,
   #DDE8DD. Sie lagen ausserhalb der Palette und blieben im Nachtmodus
   hell: drei helle Pillen auf schwarzem Grund.
   Und „laeuft" trug ORANGE. Orange ist im Modul der Weg zum Titel; was
   gerade laeuft, ist Teal. Wo eine Farbe zwei Dinge bedeutet, bedeutet
   sie keines mehr. */
/* --ink-2 statt --muted, aus demselben Grund wie beim Schrittwaehler:
   auf --sunk traegt --muted nur 4,38 : 1, und die Pille ist 11px. */
.t-list-card-status--draft {
  background: var(--sunk);
  color: var(--ink-2);
}
.t-list-card-status--ready {
  background: var(--sunk);
  color: var(--ink-2);
}
.t-list-card-status--running {
  background: var(--live-soft);
  color: var(--live);
}
.t-list-card-status--finished {
  background: var(--qual-soft);
  color: var(--qual);
}

/* Kurzinfo — "16 Teams · 4 Gruppen · 12 von 24 Spielen gespielt" */
.t-list-card-info {
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

/* Fortschrittsbalken */
.t-list-card-progress {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}
.t-list-card-progress-bar {
  position: relative;
  height: 5px;
  background: var(--sunk);
  border-radius: 999px;
  overflow: hidden;
}
.t-list-card-progress-fill {
  position: absolute;
  inset: 0 auto 0 0;
  background: var(--accent);
  border-radius: 999px;
  transition: width 0.2s ease;
}
.t-list-card-progress-label {
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* Leerer Zustand — Admin vs. Member (§13.2). */
.t-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--s8) var(--s4);
  text-align: center;
  color: var(--ink-soft);
  min-height: 240px;
  gap: var(--s2);
}
/* .t-empty-state-icon wurde entfernt: das Icon wirkte wie ein Button,
   war aber keiner. Bis der Wizard steht, reicht reiner Text. */
.t-empty-state-text {
  font-size: 15px;
  color: var(--ink);
  max-width: 360px;
}
.t-empty-state-hint {
  font-size: 13px;
  color: var(--ink-soft);
  margin-top: var(--s2);
}

/* Leerzustand INNERHALB einer gefuellten Ansicht (2026-08-29).
   User zum Spielplan: „'keine spiele in dieser auswahl' sieht nicht
   schoen aus. lieber eine schoenere schrift und nicht so linksbuendig."

   Vorher war das ein `<p class="t-hint">`: kursiver Fliesstext,
   linksbuendig an der Blattkante, in derselben Machart wie eine
   Fussnote unter einer Tabelle. Es las sich wie ein vergessener
   Kommentar, nicht wie eine Ansicht.

   Es wird KEIN zweites Muster erfunden: `.t-empty-state` gibt es
   bereits (Turnierliste ohne Turnier). Dieser Modifier passt es nur an
   den Ort an — die 240px Mindesthoehe sind fuer eine leere SEITE
   gedacht; mitten in einer Ansicht mit Kopfzeile und Filterleiste
   daruber reisst das ein Loch. Und die Ueberschrift bekommt den
   Display-Schnitt, den im Modul alles traegt, was eine Aussage macht
   (Gruppentitel, Teamnamen) — genau die „schoenere Schrift".

   Der Text bleibt an der Blattkante zentriert, nicht am Textblock:
   `.t-empty-state` ist Flex mit `align-items: center`, das Polster
   kommt von `padding`. Deshalb steht hier keine eigene Einrueckung. */
.t-empty-state--filter {
  min-height: 0;
  padding: var(--s6) var(--s5);
  gap: var(--s1);
}
.t-empty-state--filter .t-empty-state-text {
  margin: 0;
  font-family: var(--f-disp);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.018em;
  color: var(--ink);
}
.t-empty-state--filter .t-empty-state-hint {
  margin: 0;
  max-width: 34ch;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}

/* Kontextmenü-Icon (⋯) — nur sichtbar wenn als Admin gerendert */
.t-list-card-menu-btn {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--ink-soft);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  flex: none;
}
.t-list-card-menu-btn:hover {
  border-color: var(--line);
  color: var(--ink);
  background: var(--surface-alt);
}

/* Kontextmenü der Listenkarte (A4, 2026-08-25).
   Löschen liegt hier statt als Mülleimer direkt auf der Karte — sonst
   trifft man beim Öffnen leicht daneben. Der Wrap ist der Positions-
   bezug; die Karte selbst hat kein overflow:hidden, das Menü darf also
   über den Kartenrand hinausragen. */
.t-list-card-menu-wrap {
  position: relative;
  flex: none;
}
.t-list-card-menu {
  position: absolute;
  top: calc(100% + var(--s1));
  right: 0;
  z-index: 20;
  min-width: 160px;
  padding: var(--s1);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-btn);
  box-shadow: 0 8px 24px rgba(31, 27, 22, 0.16);
  display: flex;
  flex-direction: column;
}
.t-list-card-menu[hidden] {
  display: none;
}
.t-list-card-menu-item {
  display: flex;
  align-items: center;
  gap: var(--s2);
  width: 100%;
  padding: 8px var(--s3);
  border-radius: var(--r-btn);
  font-size: 14px;
  color: var(--ink);
  text-align: left;
  white-space: nowrap;
}
.t-list-card-menu-item:hover {
  background: var(--surface-alt);
}
.t-list-card-menu-item.danger {
  color: var(--danger);
}
.t-list-card-menu-item.danger:hover {
  background: var(--surface-alt);
}

/* View-Header mit Titel + Sub + Aktion (rechts) */
.t-view-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-bottom: var(--s4);
  flex-wrap: wrap;
}
.t-view-title {
  font-size: 20px;
  font-weight: 600;
}
.t-view-sub {
  font-size: 13px;
  color: var(--ink-soft);
}
.t-view-head .spacer {
  margin-left: auto;
}

/* ----------------------------------------------------------------
   MATCH-KOMPONENTE — drei Größen, identische Klasse
   ----------------------------------------------------------------
   Verwendung (vertikal-stacked, Anzeigetafel-Layout):
     <div class="t-match">                              ← Standard
       <div class="t-match-bar"></div>
       <div class="t-match-meta">…</div>
       <div class="t-match-rows">                       ← Wrapper: max 560px,
         <div class="t-match-team">${homeName}</div>       damit Name + Score
         <div class="t-match-score" data-area="home-score">${homeScore}</div>  zusammenrücken
         <div class="t-match-team right">${awayName}</div>  (User-Forderung:
         <div class="t-match-score" data-area="away-score">${awayScore}</div>  Desktop-Leere weg).
       </div>
       <div class="t-match-action">…</div>
     </div>

   Modifier-Klassen:
     .t-match--compact    Kontextspalte (kein Meta, kein Action)
     .t-match--large      Detail-Panel + Beamer (mehr Atem)
     .t-match--bracket    Turnierbaum (eigene Grid-Geometrie, siehe unten)

   Layout: 4 Spalten × 3 Zeilen — bar (links) | meta (oben) |
   rows (Team-Score-Paare, max 560px) | action (rechts, optional).
   ---------------------------------------------------------------- */
/* ----------------------------------------------------------------
   MATCH-KARTE — Markenuebernahme Etappe 3 (2026-08-26)
   ----------------------------------------------------------------
   Die Karte ist umgebaut, das MARKUP aber unveraendert. Der Renderer
   liefert weiter bar / meta / rows / action in dieser Reihenfolge; die
   neue Anordnung entsteht allein ueber Grid-Bereiche. Das war Absicht:
   ein Umbau, der ohne Renderer-Aenderung auskommt, kann auch nicht
   versehentlich ein Datenfeld verlieren.

   Vorher: schmaler Farbbalken links, Zeit/Platte in einer 120px-Spalte
   daneben, Teams rechts. Die Karte las sich von links nach rechts wie
   eine Tabellenzeile.

   Jetzt (Entwurf): Team ueber Team, Ergebnis rechts, darunter durch eine
   Linie abgesetzt die Meta-Zeile in Mono. Man liest zuerst, WER spielt —
   das ist am Spieltisch die Frage, nicht wann.

   Der Balken ist nicht mehr die Statusanzeige (dafuer traegt die Karte
   selbst einen Rand), sondern nur noch bei laufenden Spielen sichtbar.
   Siehe .t-match-bar weiter unten. */
/* ----------------------------------------------------------------
   DIE HARTE FASSUNG — Abnahme Jonas, 2026-08-26
   ----------------------------------------------------------------
   „nimm bei den spielen im spielplan fassung 1 (hart) aber genauso wie
   sie dort eben angezeigt wird."

   Das Artefakt stellt in Abschnitt 03 b dieselbe Paarung dreimal
   nebeneinander — hart, aufgeloest, weich — und setzt im Modul selbst
   die WEICHE ein. Fuer die Spielkarten im Spielplan gilt ausdruecklich
   die HARTE. Sie ist die einzige Stelle im Modul, an der ein Kaestchen
   bleibt; alles andere (Tabellen, Listen, Fussbaender) ist aufgeloest.

   Gemessen an der Vorlage (getComputedStyle auf `.mhard`), nicht
   geschaetzt — das sind die Zahlen, die dort stehen:
     Fuellung   --raised   (#FAF9F6 hell, #202024 dunkel)
     Rahmen     1px solid --line
     Ecke       12px  — die dritte erklaerte Ausnahme neben Logo (24 %)
                       und Druckbogen (3 px). Kein --r-plane: die harte
                       Fassung ist an ihrer kleineren Ecke erkennbar.
     Schatten   keiner
     Rand       0 8px, 14px zwischen zwei Karten
   ---------------------------------------------------------------- */
.t-match {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    'rows'
    'meta';
  gap: 0;
  padding: 0;
  margin: 0 var(--s2) 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--raised);
  box-shadow: none;
  overflow: hidden;
}
.t-match:last-child {
  margin-bottom: 0;
}

/* Der Balken traegt keinen Status mehr — das tut jetzt der Rand der
   Karte. Er bleibt im Markup (der Renderer liefert ihn) und wird
   ausgeblendet, statt ihn aus dem Renderer zu entfernen: eine tote
   Regel ist billiger zurueckzunehmen als ein geloeschtes Element. */
.t-match-bar {
  display: none;
}
/* ---- Zustaende der Karte — Markenuebernahme Etappe 3 ----
   Der Zustand sitzt jetzt am Rand der Karte und an der Ziffer, nicht
   mehr an einem Balken links. Drei Faelle, drei Mittel:

     laeuft    Teal-Rand + Teal-Meta-Zeile. Das eine Spiel, das JETZT
               passiert, soll sich vom Stapel abheben, ohne Orange zu
               benutzen — Orange ist der Weg zum Titel, nicht "gerade
               aktiv".
     beendet   ruhig. Der Sieger traegt Orange in der Ziffer, der
               Verlierer verblasst. Kein Haken, kein Rand.
     offen     nichts. Eine offene Partie ist der Normalfall und
               braucht keine Auszeichnung. */
.t-match--done .t-match-team {
  color: var(--muted);
}
.t-match--done .t-match-team.is-winner {
  color: var(--ink);
}
/* Die Sieger-Ziffer ist das einzige Orange auf der Karte. Sie steht in
   derselben Zeile wie der Siegername — deshalb ueber den Geschwister-
   Selektor, nicht ueber eine eigene Klasse im Renderer. */
.t-match--done .t-match-team.is-winner + .t-match-score {
  color: var(--flare);
  font-weight: 600;
}
/* Laufend, harte Fassung: Farbrahmen PLUS Schattenring PLUS eingefaerbtes
   Fussband. Die weiche Fassung ersetzt das durch einen 3-px-Streifen —
   hier gilt aber die harte, und dort sind es diese drei Mittel.
   Der Ring misst 3 px (Vorlage: `box-shadow: 0 0 0 3px var(--live-soft)`),
   nicht 1 px wie bisher. */
.t-match--live {
  border-color: var(--live);
  box-shadow: 0 0 0 3px var(--live-soft);
}
.t-match--live .t-match-meta {
  background: var(--live-soft);
  border-top-color: var(--live);
  color: var(--live);
}

/* Das FUSSBAND der harten Fassung. Es sitzt buendig an der unteren
   Kante (die Karte hat overflow:hidden, der Radius schneidet es an),
   traegt die eingesenkte Flaeche und ist durch eine harte Linie vom
   Spielfeld darueber getrennt.
   Vorlage `.mhard .cap`: padding 8px 12px, background --sunk,
   border-top 1px solid --line. Links der Ort, rechts der Zustand bzw.
   die Aktion — beides Mono 10,5px, gesperrt, gedaempft. */
.t-match-meta {
  grid-area: meta;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  min-width: 0;
  padding: 8px var(--s3);
  border-top: 1px solid var(--line);
  background: var(--sunk);
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.t-match-meta > * {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.t-match-meta-line {
  /* Keine Ellipse, kein nowrap — die Spalte ist breit genug. */
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
}
/* Der Ort: Gruppe und Platte, bei einem laufenden Spiel „Laeuft" und
   die Platte. Die Uhrzeit steht nicht mehr hier — sie steht einmal je
   Block in der Zeitmarke. */
.t-match-meta-ort {
  display: inline-flex;
  align-items: center;
}
/* Der Punkt vor „Laeuft" nimmt die Farbe des Bandes an, statt eine
   eigene mitzubringen: im Fussband eines laufenden Spiels ist ohnehin
   alles Teal, und ein zweites Teal daneben waere eine Behauptung. */
.t-dot-live {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  margin-right: 6px;
  flex: none;
}

/* Team+Score-Wrapper (Redesign A2.1) — hält die beiden Zeilen zusammen
   und begrenzt die Breite auf 560 px, damit der Team-Name und sein
   Score-Feld auf Desktop nah beieinander stehen. Bei sehr breiten
   Viewports (1920 px) bleibt die Action rechts daneben sichtbar, ohne
   dass 800 px Leere zwischen Name und Zahl entstehen. */
.t-match-rows {
  grid-area: rows;
  max-width: none;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    'home  home-s'
    'away  away-s';
  /* align-items: stretch (nicht center). Die Trennlinie ist eine
     Eigenschaft der ZEILE, nicht der Zelle: nur wenn beide Zellen die
     volle Zeilenhoehe fuellen, liegt ihre Rahmen-Oberkante auf derselben
     Hoehe. Mit `center` hing die flachere Zelle in der Mitte und ihr
     Rahmen sass entsprechend tiefer — gemessen 10px im offenen Zustand
     (375px) und 0.5px im eingetragenen. Die Zellen zentrieren ihren
     Inhalt selbst (beide sind Flex-Reihen mit align-items: center), der
     Text steht also weiterhin mittig. */
  align-items: stretch;
  /* column-gap: 0 mit Absicht. Die Trennlinie zwischen den Teams laeuft
     ueber beide Spalten; eine Luecke dazwischen wuerde sie zerschneiden,
     und die Karte saehe aus wie zwei getrennte Tabellen nebeneinander.
     Der Abstand kommt stattdessen aus dem Zell-Padding. */
  column-gap: 0;
  row-gap: 0;
}
/* Trennlinie ZWISCHEN den beiden Teams, nicht um sie herum. Sie sagt
   "zwei Parteien", waehrend ein Rahmen nur "ein Kasten" sagt.

   DIE DRITTE ZEILE IST DER EIGENTLICHE FIX (2026-08-28). Die Linie
   besteht aus zwei Stuecken: dem Rahmen der Team-Zelle (linke Spalte)
   und dem der Score-Zelle (rechte Spalte). Das rechte Stueck fiel im
   OFFENEN Zustand ersatzlos aus, weil `.t-match-score.empty` weiter
   unten `border: 0` setzt — beide Selektoren wiegen (0,2,0), und der
   spaetere gewinnt. Sichtbar war das als eine Linie, die ohne Ergebnis
   bei 89% der Kachelbreite endet und mit Ergebnis bis an den Rand
   laeuft (gemessen im Screenshot des Betreibers: rechtes Stueck ohne
   Ergebnis gar nicht vorhanden, mit Ergebnis vorhanden).
   `.t-match-rows > .t-match-score[data-area='away-score']` wiegt
   (0,3,0) und steht damit ueber jeder Zustands-Regel der Komponente.
   Die `:nth-child`-Zeilen bleiben als strukturelle Grundlage stehen —
   sie tragen die Team-Zelle und wirken auch dort, wo ein Renderer
   (noch) kein `data-area` liefert. */
.t-match-rows > :nth-child(3),
.t-match-rows > :nth-child(4),
.t-match-rows > .t-match-score[data-area='away-score'] {
  border-top: 1px solid var(--line-soft);
}
/* Vorlage `.mhard .tmr`: 9px 12px. Die Zelle traegt das Polster, nicht
   die Karte — sonst laege die Trennlinie zwischen den Teams nicht auf
   voller Breite. */
.t-match-rows > * {
  padding: 9px 0;
}
.t-match-rows > .t-match-team {
  padding-left: var(--s3);
  padding-right: var(--s3);
}
/* Die Score-Zelle traegt DASSELBE senkrechte Polster wie die Team-Zelle
   und fuellt ihre Spalte ganz aus (justify-self: stretch). Beides ist
   Bedingung dafuer, dass die Trennlinie in jedem Zustand gleich hoch
   liegt UND gleich lang ist. Vorher stand hier nur `padding-right`, das
   senkrechte Polster kam aus `.t-match-rows > *` — und wurde im offenen
   Zustand von `.t-match-score.empty { padding: 0 }` (gleiche Spezifitaet,
   spaetere Zeile) wieder abgeraeumt. Die Zelle war dadurch flacher und
   12px schmaler als im eingetragenen Zustand: die rechte Haelfte der
   Linie rutschte nach unten und wurde kuerzer.
   Das Polster steht hier als EIN Kurzschreiber, damit keine spaetere
   Regel derselben Spezifitaet nur die Haelfte davon trifft. */
.t-match-rows > .t-match-score {
  padding: 9px var(--s3) 9px 0;
  justify-self: stretch;
  align-self: stretch;
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.t-match-rows .t-match-team {
  grid-area: home;
}
.t-match-rows .t-match-team.right {
  grid-area: away;
}
.t-match-rows .t-match-score[data-area='home-score'] {
  grid-area: home-s;
}
.t-match-rows .t-match-score[data-area='away-score'] {
  grid-area: away-s;
}

/* Teams — vertikal gestapelt, jede Zeile bekommt ihren Score rechts. */
.t-match-team {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-width: 0;
  font-family: var(--f-disp);
  font-weight: 600;
  font-size: 16.5px;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.t-match-team .name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.t-match-team.right {
  justify-content: flex-start; /* Override Default — Name linksbündig wie Heim */
  text-align: left;
}
.t-match-team .name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
/* Sieger: fetter, nicht farbig. Die Farbe traegt die Ziffer daneben —
   Name UND Zahl in Orange waeren zwei Signale fuer dieselbe Aussage. */
.t-match-team.is-winner .name {
  font-weight: 700;
  color: var(--ink);
}

/* Farb-Punkt (8px) vor dem Heim-Namen bzw. nach dem Gast-Namen.
   Teams ohne gesetzte Farbe bekommen einen dezenten grauen Punkt. */
.t-dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  flex: none;
  display: inline-block;
  border: 1px solid rgba(0, 0, 0, 0.08);
}

/* ANZEIGETAFEL — Score-Felder (Redesign A2.1).
   Dunkles Board, helle Ziffern, scharfe 4px-Ecken, FESTE HÖHE 32 px
   (sonst springen gefüllte und leere Felder). padding:0 + grid+
   place-items:center zentriert die Ziffer in beiden Zuständen
   identisch. Das ist die Signatur des Moduls: ein "echtes"
   Anzeigetafel-Gefühl statt Plastik-Pille.
   Zwei Felder pro Karte — eines pro Team — statt "3 : 2" als
   kombinierter Text. */
/* Die Anzeigetafel faellt weg. Im alten Entwurf war das Ergebnis ein
   dunkles Feld mit heller Ziffer — die Signatur des Moduls. Das
   Marken-Artefakt setzt stattdessen auf die Mono-Ziffer selbst: der
   SIEGER traegt Orange, der Verlierer bleibt grau. Damit markiert die
   Farbe den Weg zum Titel statt eine Flaeche zu fuellen, und die Karte
   bleibt ruhig genug, um fuenf Stueck untereinander zu ertragen. */
.t-match-score {
  font-family: var(--f-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  font-size: 17px;
  line-height: 1;
  color: var(--ink-2);
  background: none;
  border: 0;
  border-radius: 0;
  height: auto;
  min-width: 2.2em;
  text-align: right;
  justify-self: end;
}
/* Offen: Gedankenstrich, keine Null. 0:0 waere ein Ergebnis, das
   niemand eingetragen hat. */
/* Offen: nur ein gedaempfter Gedankenstrich. Der gestrichelte Kasten,
   der hier stand, gehoerte zur Anzeigetafel — er zeichnete den Umriss
   des dunklen Score-Feldes nach, das es nicht mehr gibt. Ohne Feld ist
   der Umriss ein Kaestchen um einen Bindestrich. */
/* Kein `padding: 0` und kein `border: 0` mehr. Beides war Gegenwehr
   gegen die alte Anzeigetafel bzw. gegen `.t-match-score` aus main.css —
   und beides traf im Spielplan die Zeile statt das Feld: `padding: 0`
   nahm der Zelle ihr Polster (die rechte Haelfte der Trennlinie rutschte
   nach unten), `border: 0` nahm ihr den Rahmen (die rechte Haelfte fiel
   ganz aus). Der offene Zustand unterscheidet sich vom eingetragenen nur
   noch in Farbe und Strichstaerke der Ziffer — genau das, was er
   aussagen soll. Das Polster steht jetzt in
   `.t-match-rows > .t-match-score`, die Linie in der Regel weiter oben. */
.t-match-score.empty {
  color: var(--muted);
  background: transparent;
  font-weight: 500;
}

/* Action-Spalte — Admin-Button ODER Member-Text. NIE leer: der Member
   sieht entweder die Bezeichnung oder „Beendet", der Admin sieht den
   Button (oder „Erneut" bei schon eingetragenem Ergebnis). Spannt
   über alle 3 Zeilen rechts, sitzt dank .t-match-rows direkt neben
   dem Score-Bereich — wirkt nicht mehr "am Bildschirmrand verloren". */
.t-match-action {
  grid-area: act;
  align-self: center;
  text-align: right;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--s2);
}
.t-match-action-text {
  font-size: 12px;
  color: var(--ink-soft);
  text-align: right;
}

/* Kompakte Variante — Kontextspalte / Listen.
   Kein Meta, kein Action. Zwei Zeilen (Home/Away), Score rechts.
   Atemräume und Schrift verkleinert; Wrapper ohne max-width. */
.t-match.t-match--compact {
  grid-template-columns: 3px minmax(0, 1fr);
  grid-template-rows: auto auto;
  grid-template-areas:
    'bar rows'
    'bar rows';
  padding: var(--s2) var(--s3);
  gap: 4px var(--s2);
  font-size: 13px;
}
.t-match.t-match--compact .t-match-rows {
  max-width: none;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto;
  grid-template-areas:
    'home  home-s'
    'away  away-s';
  column-gap: var(--s2);
}
.t-match.t-match--compact .t-match-team {
  font-size: 13px;
}
.t-match.t-match--compact .t-match-score {
  font-size: 13px;
  height: 24px;
  min-width: 32px;
}

/* Große Variante — Detail-Panel + Beamer, eigene Karte pro Match.
   Gleiche Geometrie wie Standard, nur mehr Atem. */
.t-match.t-match--large {
  grid-template-columns: 4px 144px minmax(0, 560px) auto;
  padding: var(--s4) var(--s6);
  border: 1px solid var(--line);
  border-bottom: 1px solid var(--line); /* Override "last-child"-Reset */
  border-radius: var(--r-card);
  margin-bottom: var(--s3);
  gap: 6px var(--s4);
}
.t-match.t-match--large:last-child {
  margin-bottom: 0;
}
.t-match.t-match--large .t-match-meta-ort {
  font-size: 12px;
}
.t-match.t-match--large .t-match-team {
  font-size: 17px;
}
.t-match.t-match--large .t-match-score {
  font-size: 22px;
  height: 40px;
  min-width: 60px;
}

/* Auf <768px Modul-Breite: Action wandert in die oberste Zeile,
   Team+Score-Bereich darunter in voller Breite (max-width weg). */
@container (max-width: 767px) {
  /* ----------------------------------------------------------------
     HIER STAND DIE FUENFTE KASKADENKOLLISION (aufgeloest 2026-08-26)
     ----------------------------------------------------------------
     Entfernt wurde eine komplette Neudefinition des Karten-Grids:
         .t-match { grid-template-areas: "bar meta act" / "bar rows rows" }
         .t-match-rows { ... }
         .t-match-score { height: 30px; min-width: 42px }

     Sie war fuer die ALTE Karte gebaut (Balken links, Meta oben rechts)
     und hat die neue Anordnung aus der Basisregel bei jeder Modulbreite
     unter 768px still ueberschrieben — also im Hauptfall. Der Kopf der
     Karte sass dadurch oben statt unten, und die Teamnamen liefen in die
     Meta-Zeile.

     Wie die vier Faelle davor gefunden: nicht im Code gelesen, sondern
     gemessen. `getComputedStyle(karte).gridTemplateAreas` lieferte
     "bar meta act", waehrend in der Basisregel "rows" / "meta" / "act"
     stand — die Differenz zwischen Quelltext und Berechnung ist der
     einzige verlaessliche Hinweis auf eine Kollision.

     Die neue Karte braucht KEINE breitenabhaengige Sonderbehandlung:
     sie ist ein senkrechter Stapel und funktioniert von 320px bis
     Desktop mit derselben Regel. Wer hier wieder ein Grid definiert,
     sollte einen Grund nennen koennen, der bei 1920px nicht auch gilt.
     ---------------------------------------------------------------- */
  .t-match-score {
    font-size: 16px;
  }
}

/* ----------------------------------------------------------------
   DOT (klein, 8×8) — wiederverwendbares Indikator-Element.
   Basis-Definition steht oben bei .t-match. Hier nur die Status-Modifier.
   ---------------------------------------------------------------- */
.t-dot.t-dot--open {
  background: var(--open);
}
.t-dot.t-dot--qualified {
  background: var(--qualified);
}
.t-dot.t-dot--out {
  background: var(--out);
}

/* =================================================================
   TURNERBAUM — Etappe B.4 (Bracket-Tab)
   ----------------------------------------------------------------
   Vollbreite KO-Phase als links-nach-rechts-Baum. Runden = Spalten
   (früheste links, Finale rechts). Verbindungslinien zwischen den
   Spielen sind Pflicht-Bestandteil (Spec §8.2): „Wer kommt gegen
   wen?" muss auf einen Blick erkennbar sein.

   Layout-Trick (User-Hinweis): Spalten 2+ bekommen größeren gap +
   top-offset, sodass die Karte auf halber Höhe zwischen den beiden
   Quell-Spalten-Karten sitzt. Konvergenz ergibt sich geometrisch.
   Für 8er-Baum:
     Spalte 1 (QF, 4): gap 16px, kein Versatz
     Spalte 2 (SF, 2): gap 48px, top-offset 32px
     Spalte 3 (F,  1): gap 112px, top-offset 96px
   Für 16er-Baum (4 Spalten) interpoliert die Formel analog.
   ================================================================= */
/* === TURNIERBAUM — Etappe B.4 (Architektur-Reset Bug-16-Nachschlag) ===
   User-Korrektur 2026-08-19: "Vier normale Spalten nebeneinander, jede
   mit ihren Karten. Fertig. Kein grid-row span, kein margin-top, keine
   Konvergenz-Rechnung. Das ist übersichtlicher als ein Baum mit falsch
   platzierten Karten."
   Konsequenzen:
   - Kein margin-top auf Cards.
   - Keine Verbindungslinien (::before/::after weg).
   - 3RD-Match liegt in der Final-Spalte unter der F-Card.
   - Card hat min-width:0 damit sie auf Mobile bis zur vollen Spaltenbreite
     schrumpfen kann, ohne dass der Score abgeschnitten wird. */
.bracket-wrap {
  display: flex;
  flex-direction: row;
  gap: var(--s6);
  align-items: flex-start;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: var(--s2);
  /* Scroll-Snap: Desktop proximity (locker), Mobile mandatory. */
  scroll-snap-type: x proximity;
  scroll-padding-left: var(--s3);
}
/* Echte Spalten-Container. Cards stapeln sich via Flex-Column-Gap.
   Keine Konvergenz — "schlichtes Übereinander" laut User-Direktive.
   Desktop-Breite: 220-260px (Original vor Bug-16-Nachschlag-Versuchen).
   User-Korrektur 2026-08-19: "und desktop kann es genauso bleiben wies war" */
.bracket-col {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  flex: 0 0 auto;
  min-width: 220px;
  max-width: 260px;
  scroll-snap-align: start;
}
.bracket-col-label {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 600;
  margin-bottom: var(--s1);
}

/* 3RD-Subblock (Bug-16-Nachschlag 2026-08-19): liegt INNERHALB der
   Final-Spalte, unter der F-Card, mit eigenem kleinem Label.
   border-top dashed grenzt ihn visuell von der F-Region ab. */
.bracket-3rd {
  margin-top: var(--s5);
  padding-top: var(--s4);
  border-top: 1px dashed var(--line);
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}
.bracket-3rd-label {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 600;
}

/* Bracket-Knoten — VERTIKAL: Team + Score, Team + Score, Meta darunter.
   User-Korrektur 2026-08-19: "ich will es wie davor, also untereinander
   die namen und ergebnisse. so wie es es davor war". Score rechts
   vom Team (typische Turnierbaum-Anordnung). Card-Höhe 90px auf
   Desktop, schmaler auf Mobile.
   Layout via data-area (Bug-16 Fix 2026-08-19): nth-of-type ist BRÜCHIG
   hier, weil ALLE Kinder divs sind. data-area matched semantisch.
   Grid-Template: bar (3px) | home (flex) | home-score (min 36px) —
   die Score-Spalte hat eine FESTE Mindestbreite, damit der Score
   IMMER sichtbar ist (User-Korrektur 2026-08-19: "man sieht das
   ergebnis nicht, siehe screenshot"). */
.t-match--bracket {
  display: grid;
  grid-template-columns: 3px minmax(0, 1fr) minmax(36px, auto);
  grid-template-rows: auto auto auto;
  grid-template-areas:
    'bar home  home-score'
    'bar away  away-score'
    'bar meta  meta';
  /* Dritte Fassung: dieselbe Fuellung und dieselbe 12px-Ecke wie die
     Spielkarte im Spielplan — ein Spiel sieht in jeder Ansicht gleich
     aus. Links KEIN Polster: der Zustandsstreifen ist die erste
     Rasterspalte und muss buendig an der Kartenkante sitzen, sonst
     schwebt er im Nichts. Der Abstand zum Namen kommt aus dem
     Spalten-Gap. */
  gap: 4px var(--s3);
  padding: 10px 12px;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: 12px;
  min-height: 90px;
  align-content: center;
  min-width: 0;
  overflow: hidden;
  /* Bezugspunkt fuer den Zustandsstreifen: der muss ueber die volle
     Kartenhoehe laufen und an der Kante sitzen. Als Rasterzelle blieb er
     im Polster stecken und lief oben und unten 10px vor der Kante aus —
     im Screenshot sah er aus wie ein abgerutschtes Lesezeichen. */
  position: relative;
}
/* „K.-o.-Partien tragen den orangen Kantenstreifen, weil sie buchstaeblich
   der Weg zum Titel sind. In der Gruppenphase bleibt der Streifen weg —
   sonst waere er Dekoration statt Bedeutung." Vorher war er in --line,
   also unsichtbar und ohne Aussage. */
.t-match--bracket [data-area='bar'] {
  /* `display: block` ist Pflicht und keine Redundanz: `.t-match-bar` ist
     seit Etappe 3 global auf `display: none` gesetzt (der Spielplan
     traegt seinen Zustand am Kartenrand, nicht an einem Balken). Ohne
     diese Zeile blieb der Streifen unsichtbar, und die Regel darunter
     sah trotzdem korrekt aus. */
  display: block;
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--flare);
  border-radius: 0 3px 3px 0;
}
.t-match--bracket.t-match--placeholder [data-area='bar'] {
  background: var(--line);
}
.t-match--bracket [data-area='home'] {
  grid-area: home;
}
.t-match--bracket [data-area='away'] {
  grid-area: away;
}
.t-match--bracket [data-area='home-score'] {
  grid-area: home-score;
}
.t-match--bracket [data-area='away-score'] {
  grid-area: away-score;
}
/* Aus dem zentrierten Band mit Trennlinie wird eine Bildunterschrift —
   dieselbe wie unter den Spielkarten im Spielplan: Mono, 10,5px,
   gesperrt, gedaempft, linksbuendig. „Zurueck kommt die Flaeche, nicht
   die Moeblierung darin." */
.t-match--bracket [data-area='meta'] {
  grid-area: meta;
  width: 100%;
  font-family: var(--f-mono);
  font-size: 10.5px;
  color: var(--muted);
  margin-top: 5px;
  text-align: left;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.t-match--bracket [data-area='home'],
.t-match--bracket [data-area='away'] {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-width: 0;
  font-size: 13px;
}
.t-match--bracket [data-area='home'] .name,
.t-match--bracket [data-area='away'] .name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Baum-Karten sprechen dieselbe Sprache wie die Spielplan-Karten:
   Mono-Ziffer, kein dunkles Feld. Bis zur Markenuebernahme stand hier
   die Anzeigetafel (--board als Fuellung, --board-ink als Ziffer) — sie
   war die Signatur des alten Entwurfs. Wer sie hier stehen laesst,
   bekommt zwei verschiedene Ergebnis-Darstellungen in derselben App,
   je nachdem ob man auf Spielplan oder Baum schaut. */
.t-match--bracket [data-area='home-score'],
.t-match--bracket [data-area='away-score'] {
  font-family: var(--f-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  text-align: right;
  min-width: 2.2em;
  padding: 0;
  font-size: 15px;
  background: none;
  color: var(--ink-2);
  border: 0;
  border-radius: 0;
  line-height: 1.2;
  align-self: center;
}
.t-match--bracket [data-area='home-score'].empty,
.t-match--bracket [data-area='away-score'].empty {
  color: var(--muted);
  font-weight: 500;
  background: transparent;
  border: 0;
}
/* Der Sieger traegt Orange — wie im Spielplan, ueber den
   Geschwister-Selektor statt einer eigenen Klasse im Renderer. */
.t-match--bracket.t-match--done .t-match-team.is-winner + [data-area$='-score'] {
  color: var(--flare);
}
.t-match--bracket.t-match--placeholder .name {
  font-style: italic;
  /* War var(--muted, #8A8077): --muted ist der GLOBALE Token aus main.css
     (hell #8a7e74 = 3.95:1 auf Weiss, 3.51:1 auf --surface-alt — beides
     unter 4.5). Das Modul hat mit --ink-soft einen eigenen, jetzt
     konformen Token fuer Nebentext; hier greift die Bracket-Karte an der
     Modul-Grenze vorbei in die App-Palette. Auf den Modul-Token gezogen. */
  color: var(--ink-soft);
}
.t-match--bracket .t-dot--placeholder {
  background: transparent;
  border-style: dashed;
}
/* Sieger-Highlighting (Bug-16-Nachschlag-2, bleibt aktiv): bei beendeten
   Matches bekommt der Gewinner fett+schwarz, der Verlierer grau.
   Card selbst hat schon t-match--done (Border-Treatment). */
.t-match--bracket .t-match-team.is-winner .name {
  color: var(--ink);
  font-weight: 700;
}
.t-match--bracket.t-match--done [data-area='home']:not(.is-winner) .name,
.t-match--bracket.t-match--done [data-area='away']:not(.is-winner) .name {
  /* siehe .t-match--bracket.t-match--placeholder .name — Modul-Token
     statt globalem --muted. */
  color: var(--ink-soft);
  font-weight: 400;
}
.t-match--bracket.t-match--done .t-dot:not(.t-dot--placeholder) {
  opacity: 0.5;
}
.t-match--bracket.t-match--done [data-area='home'].is-winner .t-dot,
.t-match--bracket.t-match--done [data-area='away'].is-winner .t-dot {
  opacity: 1;
}

/* Runden-Tab-Leiste (oberhalb des Brackets) — Mobile-First, Desktop
   versteckt via display: none. */
.bracket-tabs {
  display: none;
  gap: var(--s2);
  padding: 0 var(--s3) var(--s3);
  overflow-x: auto;
  scrollbar-width: none;
}
.bracket-tabs::-webkit-scrollbar {
  display: none;
}
.bracket-tab {
  flex: 0 0 auto;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  font-size: 13px;
  color: var(--ink-soft);
  cursor: pointer;
  white-space: nowrap;
  font-family: inherit;
}
.bracket-tab.is-active {
  background: var(--accent, #1d3557);
  color: #fff;
  border-color: var(--accent, #1d3557);
  font-weight: 600;
}

/* === Mobile (≤ 767px) — Tab-Bar + Snap zwischen Spalten.
   Pragmatischer Fallback auf @media statt @container: Edge-Headless
   rendert @container unzuverlässig, und unser t-mod ist ohnehin @media-
   gesteuert. WICHTIG (User-Korrektur 2026-08-19): "wenn ich unter vf bin
   will ich nicht noch daneben abgeschnitten das hf sehen" — Spalte
   ist EXAKT so breit wie der sichtbare Wrap-Bereich, scroll-snap-stop:
   always sorgt dafür dass immer nur eine Spalte voll sichtbar ist,
   angeschnittene Nachbarn sind nicht im Viewport.

   Spalte = 100% vom Flex-Parent (.bracket-wrap). NICHT 100cqw und NICHT
   100vw — beide haben sich in der Praxis als zu breit erwiesen:
     - 100vw = 440px (App-Window), aber Wrap nur 372px → 68px overflow
     - 100cqw - s3*2 = 388px (tmod 412 - 24 padding), immer noch 16px
       breiter als Wrap → Score-Track am rechten Rand abgeschnitten
   User-Korrektur 2026-08-19: "man sieht das ergebnis nicht" — der
   Score saß genau in diesem 16px-Overflow-Streifen und wurde vom
   Wrap-Clip weggeschnitten. Mit flex: 0 0 100% (vom Wrap) sitzt die
   Spalte bündig im Wrap, Card-Inhalt inclusive Score ist voll sichtbar.
   Der Wrap selbst ist auf Mobile ohnehin auf die volle Inhaltsbreite
   begrenzt, also muss die Spalte nicht noch zusätzlich schrumpfen. */
@media (max-width: 767px) {
  .bracket-tabs {
    display: flex;
  }
  /* Die Spaltenueberschrift wiederholt hier den aktiven Reiter: auf
     Mobile ist genau eine Spalte sichtbar, und die Pille darueber sagt
     schon „HALBFINALE". Zweimal derselbe Name untereinander war die
     Beschwerde, die den View-Kopf gekostet hat (Beschwerde 6) — dieselbe
     Doppelung, eine Ansicht weiter. Auf Desktop bleibt sie: dort stehen
     alle Runden nebeneinander und es gibt keine Reiter. */
  .bracket-col-label {
    display: none;
  }
  .bracket-wrap {
    scroll-snap-type: x mandatory;
    scroll-padding-left: 0;
  }
  .bracket-col {
    /* Spalte = exakte Wrap-Breite (kein Overflow, kein Score-Clip). */
    flex: 0 0 100%;
    width: 100%;
    max-width: 100%;
    scroll-snap-align: start;
    scroll-snap-stop: always;
  }
  .bracket-3rd {
    max-width: 100%;
  }
}

/* Sehr schmale Geräte (<= 380px): Cards bleiben auf Bildschirm-Breite,
   Font minimal kleiner damit alles in die 3-4cm passt. Galaxy Fold
   Cover, iPhone SE. Beachte: getrennte home-score / away-score (nicht
   kombinierter "score" — der wurde in Bug-16-Nachschlag-3 entfernt). */
@media (max-width: 380px) {
  .t-match--bracket [data-area='home-score'],
  .t-match--bracket [data-area='away-score'] {
    font-size: 13px;
  }
  .t-match--bracket [data-area='home'] .name,
  .t-match--bracket [data-area='away'] .name {
    font-size: 12px;
  }
  .t-match--bracket [data-area='meta'] {
    font-size: 9px;
  }
}

/* ----------------------------------------------------------------
   REDUCED MOTION (§8.7)
   ---------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .t-mod *,
  .t-mod *::before,
  .t-mod *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* =================================================================
   SCREEN B — WIZARD (§13.3)
   ----------------------------------------------------------------
   Die einzige Ansicht im Modul, die absichtlich schmal ist.
   Formular max 900px, zentriert. Rechts ab 1200px Modul-Breite
   eine Live-Vorschau-Karte. Alles über Container Queries auf .t-mod.
   ================================================================= */

.t-wizard {
  background: var(--paper);
}

/* Body: Form links, Vorschau rechts (ab 1200px) */
.t-wizard-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s6);
  padding: var(--s6);
  align-items: start;
  flex: 1;
  min-height: 0;
}

/* Form-Bereich — schmal, zentriert, max 900px */
.t-wizard-form {
  display: flex;
  flex-direction: column;
  gap: var(--s6);
  max-width: 900px;
  width: 100%;
  margin: 0 auto;
  /* In Grid-Items: justify-self:center statt margin:auto, weil
     width:100% keinen free-space für margin-auto lässt. */
  justify-self: center;
}

/* ---------- Fortschrittsanzeige ---------- */
.t-wizard-progress {
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: var(--s4) var(--s6);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}

.t-wizard-progress-title {
  font-size: 17px;
  font-weight: 600;
  margin-right: var(--s4);
}

.t-wizard-progress-list {
  display: flex;
  align-items: center;
  gap: var(--s2);
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.t-wizard-progress-list::-webkit-scrollbar {
  display: none;
}

.t-wizard-progress-step {
  flex: 0 0 auto;
}
.t-wizard-progress-step button {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 6px 10px;
  border-radius: 999px;
  background: transparent;
  color: var(--ink-soft);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
}
.t-wizard-progress-step button:hover:not(:disabled) {
  background: var(--surface-alt);
  color: var(--ink);
}
.t-wizard-progress-step.is-current button {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
  cursor: default;
}
.t-wizard-progress-step.is-done button {
  color: var(--accent);
}
.t-wizard-progress-step:not(.is-reachable) button {
  opacity: 0.5;
  cursor: not-allowed;
}
.t-wizard-progress-dot {
  display: inline-grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  background: var(--surface-alt);
  color: var(--ink-soft);
  font-size: 12px;
  font-weight: 600;
  flex: none;
}
.t-wizard-progress-step.is-current .t-wizard-progress-dot {
  background: var(--accent);
  color: #fff;
}
.t-wizard-progress-step.is-done .t-wizard-progress-dot {
  background: var(--accent);
  color: #fff;
}
.t-wizard-progress-label {
  font-size: 13px;
}

.t-wizard-progress-cancel {
  padding: 6px 12px;
  border-radius: var(--r-btn);
  background: transparent;
  color: var(--ink-soft);
  font-size: 13px;
}
.t-wizard-progress-cancel:hover {
  color: var(--ink);
  background: var(--surface-alt);
}

/* ---------- Step-Bereich ---------- */
.t-wizard-step {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
}
.t-wizard-step-title {
  font-size: 22px;
  font-weight: 600;
  margin: 0;
  letter-spacing: -0.005em;
}
.t-wizard-step-hint {
  margin: 0;
  font-size: 14px;
  color: var(--ink-soft);
  line-height: 1.5;
}

/* ---------- Form-Felder ---------- */
.t-wizard-fields {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
}

.t-wizard-field {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}
.t-wizard-field-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}

.t-wizard-row {
  display: grid;
  gap: var(--s4);
}
.t-wizard-row.two {
  grid-template-columns: 1fr 1fr;
}
.t-wizard-row.three {
  grid-template-columns: 1fr 1fr 1fr;
}

/* Eingabefelder tragen ihre Kontur als INNENSCHATTEN auf vertiefter
   Flaeche — dieselbe Machart wie das Score-Feld im Dialog. Das ist die
   einzige Ausnahme, die die dritte Fassung bei den Konturen macht, und
   sie hat einen Grund: ein Feld muss man treffen, nicht nur finden. */
.t-input {
  width: 100%;
  padding: 12px 14px;
  border-radius: var(--r-plane);
  border: 0;
  box-shadow: inset 0 0 0 1px var(--line);
  background: var(--sunk);
  color: var(--ink);
  font-family: var(--f-mono);
  font-size: 14px;
  line-height: 1.4;
}
.t-input:focus {
  /* Maus/Touch: kein Ring, nur Rahmen + weicher Schein. Der Schein allein
     war nie eine Fokus-Anzeige — --accent-soft (#F0E7DA) auf weissem
     Grund sind 1.1:1. */
  outline: none;
  box-shadow: inset 0 0 0 2px var(--accent);
}
/* Tastatur: sichtbarer Ring. Muss NACH .t-input:focus stehen — gleiche
   Spezifitaet (0,0,2,0), es entscheidet die Quell-Reihenfolge. */
.t-input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
textarea.t-input {
  resize: vertical;
  min-height: 120px;
  font-family: inherit;
}

/* Mode-Karten (Step 3) */
.t-wizard-mode-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--s3);
}
.t-wizard-mode-card {
  display: flex;
  flex-direction: column;
  /* align-items: stretch (default) sorgt dafür, dass label + desc
     die Card-Breite füllen und Text umbricht, statt über die
     Card-Kante hinaus zu laufen. */
  align-items: stretch;
  gap: var(--s2);
  text-align: left;
  padding: var(--s4);
  border-radius: var(--r-card);
  border: 1px solid var(--line);
  background: var(--surface);
  cursor: pointer;
  transition:
    border-color 0.15s ease,
    background 0.15s ease;
  min-width: 0;
}
.t-wizard-mode-card:hover:not(:disabled) {
  border-color: var(--accent-soft);
  background: var(--surface-alt);
}
.t-wizard-mode-card.is-active {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.t-wizard-mode-card.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.t-wizard-mode-card-label {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  display: block;
  width: 100%;
  min-width: 0;
}
.t-wizard-mode-card-desc {
  font-size: 13px;
  color: var(--ink-soft);
  line-height: 1.4;
  display: block;
  width: 100%;
  min-width: 0;
  /* Lange Wörter (z. B. "Single-Elimination") müssen brechen können,
     damit sie nicht über die Card-Kante laufen. */
  overflow-wrap: anywhere;
}

/* Choice-Grid (Sport, kompakter) */
.t-wizard-choice-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--s2);
}
.t-wizard-choice {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: var(--s3);
  border-radius: var(--r-btn);
  border: 1px solid var(--line);
  background: var(--surface);
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease;
  /* Grid-Track darf schrumpfen — Card-Inhalt soll umbrechen,
     nicht die Spalte verbreitern. */
  min-width: 0;
  overflow-wrap: anywhere;
}
.t-wizard-choice:hover {
  border-color: var(--accent-soft);
}
.t-wizard-choice.is-active {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.t-wizard-choice-label {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.t-wizard-choice-sub {
  font-size: 12px;
  color: var(--ink-soft);
}

/* Verteilungsmethode (Radio) */
.t-wizard-method-list {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}
.t-wizard-method-row {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: 14px;
  color: var(--ink);
  cursor: pointer;
}

/* Checkbox-Reihen */
.t-wizard-checkbox-row {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: 14px;
  color: var(--ink);
  cursor: pointer;
}
.t-wizard-checkbox-row input[type='checkbox'] {
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
}

/* Stepper (Aufsteiger, Beste Dritte, Tische) */
.t-wizard-stepper {
  display: flex;
  align-items: center;
  gap: var(--s2);
}
.t-wizard-stepper-num {
  font-size: 22px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  min-width: 32px;
  text-align: center;
  color: var(--ink);
}

/* Gruppen-Steuerung (Step 3) */
.t-wizard-group-control {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
}
.t-wizard-group-num {
  font-size: 22px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  min-width: 32px;
  text-align: center;
  color: var(--ink);
}
.t-wizard-group-dist {
  font-size: 14px;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* Punkteregel */
.t-wizard-points-row {
  display: flex;
  gap: var(--s3);
  flex-wrap: wrap;
}
.t-wizard-points-cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  color: var(--ink-soft);
}

/* Tiebreaker-Liste */
.t-wizard-tie-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  border: 1px solid var(--line);
  border-radius: var(--r-btn);
  overflow: hidden;
  /* Während eines aktiven Drags die gap entfernen, damit der
     Placeholder nahtlos zwischen die echten Rows rutscht. */
  position: relative;
}
.t-wizard-tie-list.is-dragging-active {
  gap: 0;
}
.t-wizard-tie-row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s2) var(--s3);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  /* DnD-Indikator + Touchscreen-tauglich: Browser darf während
     einer Geste nicht eigenmächtig scrollen. */
  touch-action: none;
  user-select: none;
  cursor: grab;
}
.t-wizard-tie-row:active {
  cursor: grabbing;
}
.t-wizard-tie-row:last-child {
  border-bottom: none;
}
.t-wizard-tie-row.is-dragging {
  /* Wird vom JS auf position:absolute gesetzt — hier nur Optik. */
  opacity: 0.55;
  background: var(--surface-alt);
  box-shadow: 0 4px 16px rgba(31, 27, 22, 0.12);
  z-index: 5;
}
.t-wizard-tie-placeholder {
  /* Reserviert den Platz während des Drags, damit das Layout
     nicht springt. Höhe wird per JS gesetzt. */
  list-style: none;
  margin: 0;
  padding: 0;
  border-radius: 6px;
  background: var(--accent-soft);
  border: 2px dashed var(--accent);
}
.t-wizard-tie-num {
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 12px;
  font-weight: 600;
  flex: none;
}
.t-wizard-tie-label {
  flex: 1;
  font-size: 14px;
  color: var(--ink);
}
.t-wizard-tie-actions {
  display: flex;
  gap: 2px;
}

/* Drop-Zone (Logo) */
.t-wizard-drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s1);
  padding: var(--s6) var(--s4);
  border: 2px dashed var(--line);
  border-radius: var(--r-card);
  background: var(--surface);
  text-align: center;
  cursor: pointer;
  transition:
    border-color 0.15s ease,
    background 0.15s ease;
}
.t-wizard-drop:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.t-wizard-drop-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.t-wizard-drop-sub {
  font-size: 13px;
  color: var(--ink-soft);
}
.t-wizard-drop-link {
  color: var(--accent);
  font-weight: 500;
  text-decoration: underline;
}
.t-wizard-drop-meta {
  font-size: 12px;
  color: var(--ink-soft);
}

/* Teams-Input (Step 2) */
.t-wizard-teams-paths {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--s3);
}
@media (max-width: 767px) {
  .t-wizard-teams-paths {
    grid-template-columns: minmax(0, 1fr);
  }
}
.t-wizard-teams-path {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  padding: var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  background: var(--surface);
}
.t-wizard-teams-path-head {
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.t-wizard-teams-path-hint {
  margin: 0;
  font-size: 12px;
  color: var(--ink-soft);
}
.t-wizard-stepper {
  display: flex;
  align-items: center;
  gap: var(--s2);
}
.t-wizard-stepper-input {
  width: 80px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.t-wizard-teams-input {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}
.t-wizard-teams-textarea {
  min-height: 140px;
}
.t-wizard-teams-cta {
  display: flex;
  gap: var(--s2);
  flex-wrap: wrap;
}
.t-wizard-teams-counter {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
.t-wizard-team-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
  /* Verhindert, dass der Browser Scroll-/Zoom-Gesten abfängt,
     bevor unser Pointer-DnD-Handler reagieren kann. */
  touch-action: pan-y;
}
.t-wizard-team-row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s2) var(--s3);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: relative;
  /* Wichtig: Zeilen selbst dürfen NICHT scrollen/zoomen,
     sonst greift der Pointer-Handler nicht. */
  touch-action: none;
}
.t-wizard-team-row:last-child {
  border-bottom: none;
}
.t-wizard-team-row.is-dragging {
  z-index: 10;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.15);
  /* War var(--surface-2, var(--surface)) — --surface-2 existiert nicht, der
     Fallback trug die Regel allein. --surface-alt IST die zweite Flaeche des
     Sets (hell #F5F1E9 / dunkel #2A2620) und hebt die gezogene Zeile sichtbar
     vom weissen Grund ab, wofuer der Fallback --surface nie taugte. */
  background: var(--surface-alt);
  cursor: grabbing;
}
.t-wizard-team-placeholder {
  list-style: none;
  background: rgba(0, 0, 0, 0.04);
  border: 1px dashed var(--line);
  border-radius: var(--r-btn);
}
.t-wizard-team-dot {
  width: 10px;
  height: 10px;
  border-radius: 999px;
  flex: none;
}
.t-wizard-team-name {
  flex: 1;
  font-size: 14px;
  color: var(--ink);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Wenn der Name ein <input> ist, soll er nüchtern wirken. */
.t-wizard-team-name-input {
  flex: 1;
  min-width: 0;
  padding: 4px 8px;
  border: 1px solid transparent;
  border-radius: var(--r-btn);
  background: transparent;
  font-size: 14px;
  color: var(--ink);
  font-family: inherit;
  /* Touch-Gesten nicht abfangen — DnD lebt davon. */
  touch-action: manipulation;
}
.t-wizard-team-name-input:hover {
  border-color: var(--line);
}
.t-wizard-team-name-input:focus {
  /* Der Fallback #5A8A6B war ausserdem ein Gruenton aus einem fremden
     Set — --accent ist im Modul immer definiert, der Fallback ist weg. */
  border-color: var(--accent);
  outline: none;
  background: var(--surface);
}
/* Tastatur: hier stand `outline: none` OHNE Ersatz — ein 1px-Rahmen auf
   einer weissen Zeile ist keine Fokus-Anzeige. Ring nach der :focus-Regel,
   damit die Quell-Reihenfolge ihn gewinnen laesst. */
.t-wizard-team-name-input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.t-wizard-team-actions {
  display: flex;
  gap: 2px;
}

.t-wizard-warn {
  padding: var(--s2) var(--s3);
  border-radius: var(--r-btn);
  background: #fbefe0;
  border: 1px solid #e3c99a;
  color: #6e5118;
  font-size: 13px;
}

.t-wizard-live-info {
  padding: var(--s2) var(--s3);
  border-radius: var(--r-btn);
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 13px;
  font-weight: 500;
  overflow-wrap: anywhere;
}

/* Zusammenfassung (Step 5) */
.t-wizard-summary {
  margin: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  background: var(--surface);
  display: flex;
  flex-direction: column;
}
.t-wizard-summary-row {
  display: grid;
  grid-template-columns: 160px 1fr auto;
  gap: var(--s3);
  align-items: center;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line);
}
.t-wizard-summary-row:last-child {
  border-bottom: none;
}
.t-wizard-summary-row dt {
  font-size: 13px;
  color: var(--ink-soft);
  margin: 0;
}
.t-wizard-summary-row dd {
  font-size: 14px;
  color: var(--ink);
  margin: 0;
  font-weight: 500;
}
.t-wizard-summary-edit {
  font-size: 13px;
  color: var(--accent);
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 6px;
}
.t-wizard-summary-edit:hover {
  background: var(--accent-soft);
}
.t-wizard-summary-cta {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--s2);
}
.t-wizard-summary-cta > .t-btn {
  align-self: flex-end;
}
.t-wizard-gen-error {
  font-size: 13px;
  color: var(--danger, #a33);
  background: rgba(170, 51, 51, 0.06);
  border: 1px solid rgba(170, 51, 51, 0.25);
  border-radius: var(--r2);
  padding: 8px 12px;
  text-align: left;
}

/* ---------- Confirm-Dialog (Spec §13.10) ---------- */
.t-confirm-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(31, 27, 22, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  /* env() direkt statt var(--sat): tournament.css muss auch dort tragen,
     wo die :root-Variablen aus main.css nicht geladen sind. */
  padding: calc(16px + env(safe-area-inset-top, 0px)) calc(16px + env(safe-area-inset-right, 0px))
    calc(16px + env(safe-area-inset-bottom, 0px)) calc(16px + env(safe-area-inset-left, 0px));
}
.t-confirm-dialog {
  background: var(--panel);
  /* Der Fallback `var(--r-card, 12px)` ist am 26.08. entfallen.
     Begruendung von gestern, eingeloest: der Dialog haengt zwar weiter
     per document.body.appendChild() ausserhalb von .t-mod, bekommt aber
     seit der Dialog-Spur die Klassen `t-mod t-dialog-host` mit
     (DIALOG_TOKEN_CLASSES in dialog-host.js). Damit erreichen ihn die
     Modul-Tokens, und der Fallback war genau das, was der Kommentar
     angekuendigt hatte: totes Gewicht, sobald der Host steht. */
  border-radius: var(--r-card);
  box-shadow: 0 24px 64px rgba(31, 27, 22, 0.32);
  padding: 24px;
  max-width: 440px;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}
.t-confirm-title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: var(--ink);
}
.t-confirm-message {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
}
.t-confirm-expected {
  margin: 0;
  font-size: 13px;
  color: var(--ink-soft);
  font-family: ui-monospace, 'SF Mono', Consolas, monospace;
}
/* Dieselbe Machart wie .t-input: vertiefte Flaeche, Ring als
   Innenschatten. `--r2` kam aus main.css und war hier eine Wette darauf,
   dass die App-Skala und die Modul-Skala denselben Wert fuehren.
   `--f-mono` statt einer eigenen Schriftliste: die stand hier als
   vierte Kopie derselben Familie. */
.t-confirm-input {
  width: 100%;
  font-size: 14px;
  font-family: var(--f-mono);
  padding: 12px 14px;
  border: 0;
  box-shadow: inset 0 0 0 1px var(--line);
  border-radius: var(--r-plane);
  background: var(--sunk);
  color: var(--ink);
  box-sizing: border-box;
}
.t-confirm-input:focus {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--accent);
}
.t-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--s2);
}
/* Derselbe Fall wie bei --accent-ink eine Regel weiter oben: --danger-ink
   ist nur im Dunkeln definiert, und ohne ihn trug der gefaehrlichste
   Knopf der App hell schwarze Schrift auf Karmin. --on-danger steht in
   allen drei Bloecken. */
.t-btn--danger {
  background: var(--danger);
  color: var(--on-danger);
  border: 1px solid var(--danger);
}
/* Kaskadenkollision Nr. 5, zweiter Fall: ohne diese Regel schlaegt
   `.t-btn:hover` (0,2,0) die Fuellung von `.t-btn--danger` (0,1,0), und
   der Loeschen-Knopf wird beim Ueberfahren zum leeren Kaestchen
   (gemessen 1.13:1). Fuellung erneut setzen, nicht nur `filter`. */
.t-btn--danger:hover {
  background: var(--danger);
  border-color: var(--danger);
  filter: brightness(1.06);
}
.t-btn--danger:disabled {
  background: var(--surface-alt);
  color: var(--ink-soft);
  border-color: var(--line);
  cursor: not-allowed;
}

/* ---------- Footer ---------- */
.t-wizard-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding-top: var(--s4);
  border-top: 1px solid var(--line);
  flex-wrap: wrap;
}
.t-wizard-next-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--s1);
  margin-left: auto;
}
.t-wizard-next-hint {
  font-size: 12px;
  color: var(--ink-soft);
  text-align: right;
}

/* ---------- Live-Vorschau (rechts) ---------- */
.t-wizard-preview-wrap {
  display: none;
}
.t-wizard-preview {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: var(--s4);
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  position: sticky;
  top: 0;
}
.t-wizard-preview-head {
  padding-bottom: var(--s3);
  border-bottom: 1px solid var(--line);
}
.t-wizard-preview-title {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 600;
  margin-bottom: 2px;
}
.t-wizard-preview-sub {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}
.t-wizard-preview-row {
  display: flex;
  justify-content: space-between;
  gap: var(--s3);
  align-items: baseline;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.t-wizard-preview-label {
  color: var(--ink-soft);
}
.t-wizard-preview-value {
  color: var(--ink);
  font-weight: 500;
  text-align: right;
}
.t-wizard-preview-hint {
  font-size: 12px;
  color: var(--ink-soft);
  background: var(--surface-alt);
  padding: var(--s2) var(--s3);
  border-radius: var(--r-btn);
  line-height: 1.4;
}

/* ---------- Container Queries: Vorschau-Rechts ab 980px ----------
   980px reicht, weil das Wizard-Modul in [kru:]nest neben der
   236px-Sidebar sitzt und effektiv (Fensterbreite - 236) breit ist.
   Bei 1280px Viewport → 1044px Wizard → Preview sichtbar. Wer ein
   kleineres Fenster hat, bekommt die Vorschau unter dem Formular
   (einspaltig). Das ist explizit besser als eine zu breite Form-
   Spalte, in der die Labels verschwinden.
   ---------------------------------------------------------------- */
@container (min-width: 980px) {
  .t-wizard-body {
    grid-template-columns: minmax(0, 1fr) 320px;
    align-items: start;
  }
  .t-wizard-preview-wrap {
    display: block;
  }
  .t-wizard-form {
    margin: 0;
  }
}

/* ---------- Container Queries: Mobile (<768px) ---------- */
@container (max-width: 767px) {
  .t-wizard-progress {
    padding: var(--s3) var(--s4);
    gap: var(--s2);
  }
  .t-wizard-progress-title {
    font-size: 15px;
    margin-right: var(--s2);
  }
  .t-wizard-progress-label {
    display: none;
  }
  .t-wizard-progress-list {
    flex: 1;
    min-width: 0;
  }
  .t-wizard-body {
    padding: var(--s4);
  }
  .t-wizard-step-title {
    font-size: 19px;
  }
  .t-wizard-row.two,
  .t-wizard-row.three {
    grid-template-columns: 1fr;
  }
  .t-wizard-mode-grid {
    grid-template-columns: 1fr;
  }
  .t-wizard-choice-grid {
    grid-template-columns: 1fr;
  }
  .t-wizard-summary-row {
    grid-template-columns: 1fr auto;
    gap: var(--s2);
  }
  .t-wizard-summary-row dt {
    grid-column: 1;
    grid-row: 1;
  }
  .t-wizard-summary-row dd {
    grid-column: 1;
    grid-row: 2;
    font-size: 15px;
  }
  .t-wizard-summary-edit {
    grid-column: 2;
    grid-row: 1 / 3;
    align-self: center;
  }
  .t-wizard-footer {
    flex-direction: column;
    align-items: stretch;
  }
  .t-wizard-next-wrap {
    align-items: stretch;
    margin-left: 0;
  }
  .t-wizard-next-wrap .t-btn {
    width: 100%;
    justify-content: center;
  }
  .t-wizard-next-hint {
    text-align: left;
  }
}

/* ================================================================
   Logo-Feld (Step 1, Spec §3 / §8.4)
   ================================================================ */
.t-wizard-logo-field {
  gap: var(--s2);
}
.t-wizard-logo-status {
  font-size: 13px;
  color: var(--ink-soft);
  min-height: 1.2em;
}
.t-wizard-logo-status.is-pending {
  color: var(--ink-soft);
}
.t-wizard-logo-status.is-ok {
  /* --ok gab es nie; ENTSCHEID: bestehenden Token benutzen statt einen
     zweiten Gruen-Namen einzufuehren. --done/--qualified ist der
     Erfolgs-Gruenton des Sets (hell #4F7A4A, dunkel #6B9A66) und deckt
     "Upload geglueckt" mit ab. (2026-08-25) */
  color: var(--done);
}
.t-wizard-logo-status.is-error {
  color: var(--danger, #c0392b);
}
.t-wizard-logo-preview {
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r-btn);
  background: var(--surface-alt);
}
.t-wizard-logo-img {
  max-width: 128px;
  max-height: 128px;
  width: auto;
  height: auto;
  object-fit: contain;
  /* Stand hier als var(--r-sm, 4px). --r-sm gab es nie -- und es wird
     auch nicht nachgetragen: die Radien-Skala des Moduls ist SEMANTISCH
     (--r-card / --r-btn / --r-board), keine Groessenskala sm/md/lg. Ein
     --r-sm waere ein zweites Namensschema; --r-board (zufaellig auch 4px)
     waere fuer ein Logo-Vorschaubild schlicht gelogen. Bleibt darum ein
     Literal, bis es einen zweiten Nutzer gibt. (2026-08-25) */
  border-radius: 4px;
  background: var(--surface);
}
.t-wizard-logo-picker {
  display: flex;
  align-items: center;
  gap: var(--s3);
}

/* ----------------------------------------------------------------
 * Teams-View (C5) — post-Generate.
 *
 * Layout: 1 Spalte < 720px (Card-Stacking), 2 Spalten (Name +
 * Color) ab 720px. Seed-Badge links, Status-Indikator rechts.
 * ---------------------------------------------------------------- */
.t-teams-view {
  background: var(--surface);
  border: 1px solid var(--line, #e7dfd2);
  border-radius: 12px;
  padding: 16px 20px;
  max-width: 920px;
  margin: 24px auto;
}

.t-teams-head {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
}

.t-teams-title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
}

.t-teams-summary {
  font-size: 0.9rem;
  min-height: 1.5em;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 6px 10px;
  border-radius: 6px;
  background: transparent;
  border: 1px solid transparent;
}
.t-teams-summary[data-state='ok'] {
  background: rgba(44, 110, 63, 0.08);
  border-color: rgba(44, 110, 63, 0.25);
  color: #2c6e3f;
}
.t-teams-summary[data-state='saving'] {
  background: rgba(170, 140, 30, 0.08);
  border-color: rgba(170, 140, 30, 0.25);
  color: #8a6a1a;
}
.t-teams-summary[data-state='err'] {
  background: rgba(170, 51, 51, 0.08);
  border-color: rgba(170, 51, 51, 0.3);
  color: #a33;
}

.t-teams-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  /* Kein Abstand mehr zwischen den Zeilen: seit sie keine Kaesten mehr
     sind, traegt die Haarlinie die Trennung. Beides zusammen haette
     dasselbe zweimal gesagt. */
  gap: 0;
}

.t-teams-row {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr) auto 24px;
  gap: 10px;
  align-items: center;
  padding: 8px 10px;
  border: 1px solid var(--line, #e7dfd2);
  border-radius: 8px;
  background: var(--paper, #faf7f1);
  transition: border-color 0.15s ease;
}
.t-teams-row[data-row-state='saving'] {
  border-color: rgba(170, 140, 30, 0.45);
}
.t-teams-row[data-row-state='ok'] {
  border-color: rgba(44, 110, 63, 0.4);
}
.t-teams-row[data-row-state='err'] {
  border-color: rgba(170, 51, 51, 0.45);
  background: rgba(170, 51, 51, 0.04);
}

.t-teams-seed {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  background: var(--surface);
  border: 1px solid var(--line, #e7dfd2);
  border-radius: 50%;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--ink-soft);
}

.t-teams-name-input {
  width: 100%;
}

.t-teams-color-wrap {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.t-teams-color-input {
  width: 36px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--line, #e7dfd2);
  border-radius: 6px;
  background: var(--surface);
  cursor: pointer;
}

.t-teams-row-status {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  font-size: 1rem;
  font-weight: 700;
}
.t-teams-row-status[data-state='ok'] {
  color: #2c6e3f;
}
.t-teams-row-status[data-state='err'] {
  color: #a33;
}
.t-teams-row-status[data-state='saving'] {
  color: #8a6a1a;
}
.t-teams-row-status[data-state='idle'] {
  color: transparent;
}

/* X-Reset-Button (Farbe zurücksetzen) */
.t-btn--xs {
  padding: 2px 8px;
  font-size: 0.85rem;
  line-height: 1.4;
  border-radius: 999px;
  border: 1px solid var(--line, #e7dfd2);
  background: var(--surface);
  color: var(--ink-soft);
  cursor: pointer;
}
/* Hier stand eine zweite `.t-btn--ghost`-Definition mitsamt eigener
   Hover-Farbe `rgba(0, 0, 0, 0.04)`. Sie stand SPAETER in der Quelle und
   gewann damit bei gleicher Spezifitaet gegen die Definition oben im
   Knopf-Abschnitt — zwei Wahrheiten fuer denselben Knopf, und die
   gewinnende war die themafremde: ein Schwarz-Alpha-Hover ist auf
   dunklem Grund praktisch unsichtbar. Entfernt 2026-08-25; es gilt die
   Definition im Knopf-Abschnitt mit `--surface-alt`. */

/* Mobile: schmaler Bildschirm, weniger Padding. */
@media (max-width: 600px) {
  .t-teams-row {
    grid-template-columns: 28px minmax(0, 1fr) auto;
  }
  .t-teams-row-status {
    display: none;
  }
}

/* ----------------------------------------------------------------
   REGELN-TAB (Spec §8.4 Info-Seite, User-Punkt 5)
   Plain-Text mit Paragraphs. Edit-Mode für Admins mit Textarea.
   ---------------------------------------------------------------- */
.t-rules-paragraphs p {
  margin: 0 0 1em;
  line-height: 1.55;
  color: var(--ink, #1f1b16);
}
.t-rules-paragraphs p:last-child {
  margin-bottom: 0;
}
.t-rules-paragraphs br {
  line-height: 1.55;
}

.t-rules-textarea {
  width: 100%;
  min-height: 240px;
  resize: vertical;
  padding: var(--s3, 12px);
  font-family: inherit;
  font-size: 14px;
  line-height: 1.5;
  border: 1px solid var(--line, #e7dfd2);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink, #1f1b16);
  box-sizing: border-box;
}
.t-rules-textarea:focus {
  outline: 2px solid var(--accent, #b85c2c);
  outline-offset: 1px;
  border-color: var(--accent, #b85c2c);
}
.t-rules-meta {
  display: flex;
  align-items: center;
  gap: var(--s3, 12px);
  margin-top: var(--s3, 12px);
}
.t-rules-meta .spacer {
  margin-left: auto;
}
.t-rules-meta > span {
  font-size: 12px;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
.t-rules-overflow-note {
  font-size: 12px;
  color: var(--ink-soft);
  font-style: italic;
  margin-top: 1em;
}

/* ----------------------------------------------------------------
   TEAMS-TAB (Etappe B.5)
   Liste mit Inline-Rename (Admin) + Pointer-DnD-Reihenfolge
   (Admin + Status === 'draft'). Read-only für Member. Visuell
   angelehnt an Wizard-Step-2 (.t-wizard-team-row), aber ohne
   Action-Buttons (Rename = Klick auf Name, DnD = Handle ziehen).
   ---------------------------------------------------------------- */
.t-teams-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s2, 8px);
}
/* Dritte Fassung, Abschnitt 06: keine Huelle je Team. Zwoelf umrandete
   Kaesten untereinander waren zwoelf Behauptungen „das hier ist ein
   eigener Gegenstand" — bei einer Aufzaehlung stimmt das nicht. Was
   trennt, ist die Haarlinie; was zusammenhaelt, ist der gleiche Abstand.
   Der Rahmen bleibt als transparente Kante stehen, weil die Zustaende
   beim Speichern (`data-row-state`) ihn einfaerben — sonst spraenge die
   Zeile beim Sichern um 2px. */
.t-team-row {
  display: grid;
  grid-template-columns: auto 28px 1fr auto;
  align-items: center;
  gap: var(--s3, 12px);
  padding: 13px 0;
  background: none;
  border: 1px solid transparent;
  border-radius: 0;
  position: relative;
  touch-action: pan-y; /* vertikalen Scroll erlauben, horizontales DnD blockieren */
  user-select: none;
}
/* Rueckzugs-Knopf (2026-09-01): fuenfte Spalte, aber nur wo es ihn gibt.
   `.t-team-row` oben deklariert vier Spalten — ein fuenftes Kind landet
   sonst in einer impliziten Auto-Spalte, deren Breite niemand geprueft
   hat. Die Klasse sitzt an der Liste (`renderTeamsList` setzt sie mit
   `canWithdraw`), damit Listen ohne Knopf keine leere Spalte samt `gap`
   mitschleppen.

   Der Knopf traegt `t-btn t-btn--ghost t-btn--sm` und faellt damit unter
   die Ausnahmeliste des Resets in Zeile 419 (`:not([class*='t-btn'])`) —
   die `.t-btn`-Regeln greifen, ohne dass hier eine Farbe wiederholt wird.
   Genau diese Wiederholung war die frueher belegte Falle. */
.t-teams-list.has-withdraw .t-team-row {
  grid-template-columns: auto 28px 1fr auto auto;
}
/* Unter 430px steht der Knopf UNTER dem Namen, nicht daneben.
   Gemessen, nicht geschaetzt (Pruefstand 2026-09-01): nebeneinander faellt
   die Namensspalte bei 375px von 233px auf 155px. Gekuerzt wurden sehr
   lange Vereinsnamen zwar auch vorher schon — der Kontrolllauf ohne Knopf
   zeigt das —, aber 78px herzugeben, damit ein Knopf danebensteht, ist der
   falsche Tausch: den Namen liest man in jeder Zeile, den Knopf braucht
   man einmal im Leben eines Turniers.
   Der Setzplatz faellt hier ohnehin weg; er steht im Seeding-Block. */
@media (max-width: 430px) {
  .t-teams-list.has-withdraw .t-team-row {
    grid-template-columns: auto 28px 1fr;
    column-gap: var(--s2, 8px);
    row-gap: 6px;
  }
  .t-teams-list.has-withdraw .t-team-seed {
    display: none;
  }
  /* Zweite Reihe, unter dem Namen — nicht unter dem Griff. */
  .t-teams-list.has-withdraw .t-team-withdraw {
    grid-column: 3;
    justify-self: start;
  }
}
.t-team-row + .t-team-row {
  border-top-color: var(--line-soft);
}
.t-team-row.is-dragging {
  z-index: 10;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  cursor: grabbing;
  background: var(--surface);
}
.t-team-row--placeholder {
  border: 1px dashed var(--line, #e5ded5);
  background: transparent;
  visibility: hidden; /* Platzhalter hält die Höhe, ist unsichtbar */
}
.t-team-drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  font-size: 16px;
  line-height: 1;
  color: var(--ink-soft);
  cursor: grab;
  border-radius: 4px;
}
.t-team-drag-handle:hover {
  background: var(--line, #e5ded5);
  color: var(--ink, #1b1b1f);
}
.t-team-drag-handle.is-readonly {
  cursor: default;
  /* Member-View: kein Glyph (Renderer lässt das Zeichen weg), nur Platzhalter */
}
.t-team-marker {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 600;
  background: var(--line, #e5ded5);
  color: var(--ink, #1b1b1f);
  flex-shrink: 0;
}
.t-team-name {
  font-size: 15px;
  font-weight: 500;
  color: var(--ink, #1b1b1f);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.t-teams-list.is-draggable .t-team-name {
  cursor: pointer; /* Editier-Hinweis via Cursor + Edit-Hint-Text */
  border-bottom: 1px dashed transparent;
}
.t-teams-list.is-draggable .t-team-row:hover .t-team-name {
  border-bottom-color: var(--line, #e5ded5);
}
.t-team-name-input {
  font-size: 15px;
  font-weight: 500;
  color: var(--ink, #1b1b1f);
  width: 100%;
  min-width: 0;
  padding: 4px 6px;
  border: 1px solid var(--accent, #1d3557);
  border-radius: 4px;
  background: var(--surface);
  font-family: inherit;
}
/* --bg war der App-Grund (creme) und im Modul gar nicht definiert — der
   Fallback #F7F4EE sprang ein und setzte einen cremefarbenen Fleck auf
   das Blatt. Genau die Sorte Quelle, die kein grep in dieser Datei
   findet, weil der Wert nur als Fallback dasteht. --sunk ist die
   eingesenkte Flaeche, die auch Schrittwaehler und Herkunftspillen
   tragen; die Herkunftspille der Vorlage ist ausserdem Mono. */
.t-team-seed {
  font-family: var(--f-mono);
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  padding: 3px 8px;
  background: var(--sunk);
  border-radius: var(--r-pill);
}
/* Er steht seit dem 26.08. EINMAL ueber der Liste statt in jeder Zeile
   (siehe renderTeamsList). Damit ist er ein Hinweis wie jeder andere und
   braucht keine eigene Sonderbehandlung mehr — nur den Mono-Ton der
   uebrigen Beschriftungen. */
.t-team-edit-hint {
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  font-style: normal;
  margin: 0 0 var(--s2);
}
/* Auch hier stand `var(--bg, #F7F4EE)` — creme, aus einem Fallback, den
   kein grep nach einer Farbe findet. Der Hinweis ist jetzt das, was die
   Vorlage an dieser Stelle zeigt: ein Streifen an der Kante und Text,
   kein getoenter Kasten. */
.t-hint--info {
  position: relative;
  background: none;
  border: 0;
  border-radius: 0;
  padding: var(--s2) 0 var(--s2) var(--s3);
  font-style: normal; /* Override für t-hint italic */
  margin-bottom: var(--s3);
}
.t-hint--info::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  border-radius: var(--r-pill);
  background: var(--flare);
}

/* Mobile (≤ 600px): kompakter Marker, alles andere bleibt.
   Zwei Zeilen von hier sind mit der dritten Fassung entfallen:

   `padding: 6px 10px` — ein Shorthand, das die 20px-Fluchtlinie der
   Zeile wieder eingezogen haette. Die Zeile bringt ihr Polster seit dem
   26.08. selbst mit und bricht dafuer aus dem Polster der Ansicht aus;
   ein Shorthand hier setzt beide Seiten zurueck. Dieselbe Falle wie beim
   #content-Padding im P7-Fix.

   `.t-team-edit-hint { display: none }` — der Hinweis stand damals in
   JEDER Zeile und waere mobil dreimal umgebrochen. Er steht jetzt
   einmal ueber der Liste und passt in eine Zeile. Ausgeblendet hiess
   ausserdem: am Handy erfaehrt niemand, dass man umbenennen kann — und
   das Handy ist der Hauptfall. */
@media (max-width: 600px) {
  .t-team-row {
    grid-template-columns: auto 24px 1fr auto;
    padding-top: 11px;
    padding-bottom: 11px;
  }
  .t-team-name {
    font-size: 14px;
  }
  .t-team-marker {
    width: 24px;
    height: 24px;
    font-size: 12px;
  }
}

/* === Etappe B.7 — Einstellungen-Tab ============================== */
.t-settings-grid {
  display: flex;
  flex-direction: column;
  gap: var(--s5);
}
/* ----------------------------------------------------------------
   EINSTELLUNGEN ALS LISTE — Markenuebernahme Etappe 7
   ----------------------------------------------------------------
   "Listen statt Formular": jede Sektion ist eine Karte, die Kopfzeile
   traegt den Namen in Mono-Versalien, darunter stehen Zeilen mit
   Beschriftung links und Wert rechts.

   Die AUFKLAPPBARKEIT bleibt. Sie stammt aus Etappe B.7 und ist
   zustandsabhaengig vorbelegt (im Entwurf offen, im Lauf zu). Der
   Marken-Entwurf zeigt zwar flache Abschnitte, aber das ist eine
   Zeichnung von vier Sektionen — im echten Turnier sind es sieben, und
   zugeklappte Bereiche sind dort kein Schmuck, sondern der Grund, warum
   der Tab ueberhaupt bedienbar ist. Form uebernommen, Funktion behalten.
   ---------------------------------------------------------------- */
.t-settings-section {
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-s);
  overflow: hidden;
}
.t-settings-section-title {
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
  margin: 0;
}
.t-settings-section-body {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s3) var(--s4) var(--s4);
}

/* ----------------------------------------------------------------
   LISTENZEILE — Beschriftung links, Wert rechts
   ----------------------------------------------------------------
   Die Grundform der Einstellungen. Der Wert steht in Mono, weil er
   meistens eine Zahl, eine Zeit oder ein Name ist und rechtsbuendig
   fluchten soll. Die Erklaerung steht KLEIN UNTER der Beschriftung,
   nicht daneben: auf 390px ist rechts kein Platz fuer zwei Spalten
   Text, und ein Hilfetext, der den Wert verdraengt, ist keiner.
   ---------------------------------------------------------------- */
.t-list-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: 12px var(--s4);
  min-width: 0;
}
.t-list-row + .t-list-row {
  border-top: 1px solid var(--line-soft);
}
.t-list-label {
  min-width: 0;
  font-weight: 500;
  color: var(--ink);
}
.t-list-label small {
  display: block;
  font-weight: 400;
  font-size: 12.5px;
  color: var(--muted);
}
.t-list-value {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font-family: var(--f-mono);
  font-size: 13px;
  color: var(--ink-2);
}
.t-list-chevron {
  color: var(--muted);
}

/* Statuskarte am Kopf des Tabs. Das Band links ist Teal, weil sie sagt
   "laeuft gerade" — dieselbe Bedeutung wie am Rand der laufenden
   Match-Karte, also dieselbe Farbe. */
.t-status-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 4px solid var(--live);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-s);
  padding: var(--s3) var(--s4);
}
.t-status-card-head {
  display: inline-flex;
  align-items: center;
  font-family: var(--f-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--live);
}
.t-status-card-nums {
  display: flex;
  gap: var(--s6);
  margin-top: var(--s2);
}
.t-status-card-nums b {
  display: block;
  font-family: var(--f-disp);
  font-weight: 700;
  font-size: 26px;
  line-height: 1;
  color: var(--ink);
}
.t-status-card-nums span {
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.t-settings-actions {
  display: flex;
  gap: var(--s2);
  flex-wrap: wrap;
}
.t-settings-lock-hint {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
  background: var(--warn-soft);
  border-left: 3px solid var(--warn);
  border-radius: var(--r-card);
  font-size: 12px;
}

/* === Gefahrenzone ================================================ */
.t-danger-zone {
  border: 2px solid var(--danger, #b3261e);
  border-radius: var(--r-card);
  padding: var(--s4);
  background: rgba(179, 38, 30, 0.04);
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}
.t-danger-zone-title {
  color: var(--danger, #b3261e);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin: 0;
}
.t-danger-zone-body {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}
.t-danger-zone-actions {
  display: flex;
  gap: var(--s2);
  flex-wrap: wrap;
}

/* === Etappe B.8 — Klappbar (Einstellungen-Tab) ==================== */
.t-settings-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  width: 100%;
  padding: var(--s3) var(--s4);
  background: var(--raised);
  border: 0;
  border-bottom: 1px solid var(--line);
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.t-settings-section-header:hover {
  background: var(--surface-alt);
}
/* Vorher teilte sich die Kopfzeile eine Regel mit :hover und setzte
   `outline: none`. Der Hintergrundwechsel #FFFFFF -> #F5F1E9 ist als
   Fokus-Anzeige 1.13:1, also praktisch nichts — und ein reiner
   Farbwechsel ohne Formaenderung faellt bei Farbsehschwaeche ganz weg.
   Jetzt: Hintergrund bleibt als Hover-Feedback, der Fokus bekommt den
   Ring aus der Basisregel oben. `outline-offset: -2px`, weil die
   Kopfzeile die volle Sektionsbreite hat und ein aeusserer Ring sonst
   an den Nachbarn stoesst. */
.t-settings-section-header:focus-visible {
  background: var(--surface-alt);
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
.t-settings-section-header .t-settings-section-title {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 600;
  margin: 0;
}
.t-settings-section-toggle {
  display: inline-block;
  font-size: 14px;
  color: var(--ink-soft);
  transition: transform 0.15s ease;
}
.t-settings-section[data-collapsed='true'] .t-settings-section-body {
  display: none;
}
.t-settings-section[data-collapsed='true'] .t-settings-section-toggle {
  transform: rotate(-90deg);
}
.t-settings-section:first-child .t-settings-section-header {
  border-top: 1px solid var(--line, #e5e2da);
}

/* === Etappe B.8 — Banner (Revert-Hinweis) ============================ */
.t-banner {
  display: flex;
  align-items: flex-start;
  gap: var(--s2, 8px);
  padding: var(--s3, 12px);
  border-radius: var(--r-card, 8px);
  font-size: 13px;
  line-height: 1.45;
  margin: 0;
}
.t-banner--warning {
  background: var(--warn-soft);
  border-left: 3px solid var(--warn);
  color: var(--ink, #1a1a17);
}

/* === Etappe B.8 — Shift + Reschedule Forms =========================== */
.t-shift-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2, 8px);
  padding: var(--s3, 12px);
  background: var(--surface-alt, #fafaf7);
  border-radius: var(--r-card, 8px);
}
.t-shift-form-label {
  display: inline-flex;
  align-items: center;
  gap: var(--s2, 8px);
  flex: 1 1 200px;
}
.t-shift-minutes {
  width: 90px;
}
.t-reschedule-form {
  display: flex;
  flex-direction: column;
  gap: var(--s3, 12px);
  padding: var(--s3, 12px);
  background: var(--surface-alt, #fafaf7);
  border-radius: var(--r-card, 8px);
}
.t-reschedule-row {
  display: flex;
  align-items: center;
  gap: var(--s2, 8px);
}

/* === Etappe B.8 — Error-Hint (für try/catch-Fallback in loadEinstellungenTab) */
.t-hint--error {
  border-left: 3px solid var(--danger, #b3261e);
  background: rgba(179, 38, 30, 0.06);
  color: var(--danger, #b3261e);
  padding: var(--s3, 12px);
  border-radius: var(--r-card, 8px);
}

/* === Etappe B.8 — DnD-Hover + Touch-Picker-Modal ===================== */
.t-group-team-card {
  cursor: grab;
}
.t-group-team-card:hover {
  box-shadow: 0 0 0 2px var(--accent, #1d6fdb);
}
.t-pick-team-modal {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: calc(16px + env(safe-area-inset-top, 0px)) calc(16px + env(safe-area-inset-right, 0px))
    calc(16px + env(safe-area-inset-bottom, 0px)) calc(16px + env(safe-area-inset-left, 0px));
}
.t-pick-team-modal-card {
  background: var(--surface);
  border-radius: var(--r-card, 8px);
  padding: var(--s4, 16px);
  width: min(420px, 92vw);
  display: flex;
  flex-direction: column;
  gap: var(--s3, 12px);
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.18);
  /* Lange Teamlisten: Karte deckelt sich am Innenraum des Backdrops und
     scrollt selbst, statt unter Statusleiste/Home-Indikator zu laufen. */
  max-height: 100%;
  overflow-y: auto;
}
.t-pick-team-modal-card h3 {
  margin: 0;
  font-size: 14px;
}
.t-pick-team-radios {
  display: flex;
  flex-direction: column;
  gap: var(--s2, 8px);
}
.t-pick-team-actions {
  display: flex;
  gap: var(--s2, 8px);
  justify-content: flex-end;
}

/* === Groups-Board (DnD) ========================================== */
.t-groups-board {
  display: grid;
  grid-template-columns: repeat(var(--group-count, 3), minmax(160px, 1fr));
  gap: var(--s3);
}
.t-groups-column {
  background: var(--surface-alt, #fafaf7);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: var(--s3);
  min-height: 200px;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}
.t-groups-column-header {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 600;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.t-group-count {
  background: var(--accent-soft, #e8edf3);
  color: var(--ink);
  border-radius: 12px;
  padding: 2px 8px;
  font-size: 11px;
  font-weight: 700;
}
.t-groups-column-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  min-height: 60px;
}
.t-group-team-card {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  /* Etappe B.8.1: Paar-Klick-Tausch statt DnD. Cursor ist pointer,
     nicht mehr grab. */
  cursor: pointer;
  font-size: 13px;
  user-select: none;
  touch-action: manipulation;
}
.t-group-team-card.is-selected {
  /* War var(--accent-tint, var(--surface-2)) — BEIDE undefiniert, damit war
     die ganze Deklaration ungueltig und die Karte durchsichtig statt getoent.
     --accent-soft ist genau dieser getoente Akzent-Grund und existiert in
     hell UND dunkel. Kein neuer Token. */
  background: var(--accent-soft);
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent);
}
.t-group-team-card.is-dragging {
  position: fixed;
  z-index: 100;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
  cursor: grabbing;
  pointer-events: none;
  width: var(--card-width, 180px);
}
.t-group-team-card.is-placeholder {
  visibility: hidden;
  border: 1px dashed var(--line);
  background: transparent;
  cursor: default;
}
.t-group-team-card-dot {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  flex-shrink: 0;
}
.t-group-team-card-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* === Paar-Klick-Tausch-Bar (Etappe B.8.1) ========================= */
.t-swap-bar {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
  margin-top: var(--s3);
  /* siehe .t-group-team-card.is-selected — war doppelt undefiniert. */
  background: var(--accent-soft);
  border: 1px solid var(--accent);
  border-radius: var(--r-card);
}
.t-swap-bar[hidden] {
  display: none;
}
.t-swap-bar-label {
  flex: 1;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}

/* === Spielplan-Edit-Mode ======================================== */
.t-match-edit-time,
.t-match-edit-field {
  font-family: inherit;
  font-size: 13px;
  padding: 4px 6px;
  border: 1px solid var(--accent);
  border-radius: 4px;
  background: var(--surface);
  color: var(--ink);
  width: auto;
}
.t-match-edit-field {
  width: 56px;
  text-align: center;
}
.t-match-edit-time:disabled,
.t-match-edit-field:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.t-schedule-edit-bar {
  display: flex;
  gap: var(--s3);
  align-items: center;
  padding: var(--s3);
  background: var(--accent-soft, #e8edf3);
  border-radius: var(--r-card);
  margin-bottom: var(--s4);
  flex-wrap: wrap;
}
.t-schedule-edit-config {
  display: flex;
  align-items: center;
  gap: var(--s3);
  font-size: 13px;
}
.t-schedule-edit-config input {
  width: 64px;
  padding: 4px 6px;
  border: 1px solid var(--accent);
  border-radius: 4px;
  font-family: inherit;
  text-align: center;
}
.t-schedule-edit-actions {
  display: flex;
  gap: var(--s2);
}

/* === Spielfelder-Editor ========================================= */
.t-fields-editor {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}
.t-fields-editor-row label {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: 13px;
}
.t-fields-count {
  width: 64px;
  padding: 4px 6px;
  border: 1px solid var(--line);
  border-radius: 4px;
  font-family: inherit;
  text-align: center;
}
.t-fields-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--s2);
}
.t-field-row {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
}
.t-field-order {
  font-size: 11px;
  color: var(--ink-soft);
  font-weight: 600;
  min-width: 24px;
}
.t-field-name {
  flex: 1;
  font-family: inherit;
  font-size: 13px;
  padding: 4px 6px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--surface);
  color: var(--ink);
  min-width: 0;
}
.t-field-name:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.t-fields-editor-actions {
  display: flex;
  gap: var(--s2);
  flex-wrap: wrap;
}
.t-fields-locked-hint {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
  background: var(--warn-soft);
  border-left: 3px solid var(--warn);
  border-radius: var(--r-card);
  font-size: 12px;
}

/* === Etappe B.7 — Mobile ========================================= */
@media (max-width: 600px) {
  .t-groups-board {
    grid-template-columns: 1fr;
  }
  .t-schedule-edit-bar {
    flex-direction: column;
    align-items: stretch;
  }
  .t-schedule-edit-config {
    flex-wrap: wrap;
  }
  .t-fields-list {
    grid-template-columns: 1fr;
  }
  .t-settings-actions {
    flex-direction: column;
    align-items: stretch;
  }
  .t-danger-zone-actions {
    flex-direction: column;
    align-items: stretch;
  }
}

/* ================================================================
   DIALOG-HOST + ERGEBNIS-DIALOG (A5, 2026-08-25)
   ================================================================
   Drei Dialoge dieser App hängen an `document.body`, nicht im Modul:

     #result-entry-modal   Ergebnis eintragen      (main.js)
     .t-confirm-backdrop   Löschen bestätigen      (tournament.js)
     .t-pick-team-modal    Team verschieben        (main.js)

   Alle Tokens des Moduls stehen auf `.t-mod` (Zeile 42), nicht auf
   `:root`. Ausserhalb dieses Teilbaums erben sie nicht — und ein
   `var(--line)` ohne Wert macht nicht "irgendwas Graues", sondern die
   ganze Deklaration ungültig. Genau daher kamen die gemeldeten
   Ausfälle: eckige Ecken, fehlende Rahmen, gap:0, Hover-Aussetzer.

   Die Lösung ist dieselbe wie bei der Turnierliste (`.t-list-host`,
   ab Zeile 205): das Wurzelelement bekommt `t-mod` zusätzlich — EINE
   Wahrheit für die Tokens, keine Kopie — und hier den Gegen-Reset für
   die Layout-Eigenschaften, die `.t-mod` als Modul-Container mitbringt.

   Was genau zurückgenommen wird und warum:
     container-type  .t-mod ist ein Container (`inline-size`, Zeile 89).
                     Bliebe das stehen, würden die Container-Queries des
                     Moduls (`@container (min-width: …)`) gegen die
                     Dialogbreite messen statt gegen den Arbeitsbereich
                     — die Bodenleiste und die Tab-Umschaltung hängen
                     daran.
     flex-direction  .t-mod ist `flex-direction: column` (Zeile 92). Der
                     Backdrop zentriert aber über align-items und
                     justify-content, und in einer Spalte tauschen die
                     beiden ihre Achsen: `align-items: flex-end` schöbe
                     den Dialog nach RECHTS statt nach unten.
     width           .t-mod ist `width: 100%` (Zeile 90); der Backdrop
                     ist `position: fixed; inset: 0` und rechnet seine
                     Breite selbst aus.
     padding         `.t-mod { padding: 0 }` (Zeile 134) schlägt im
                     Kaskaden-Rang `.dlg-bg { padding: 20px }` aus
                     main.css — ohne Rückgabe klebte der Dialog am
                     Bildschirmrand.
     height/overflow greifen heute nur über `.t-wizard-host > .t-mod`
                     bzw. `.t-detail-host > .t-mod` (Zeilen 158/186),
                     also nicht am body. Sie stehen hier trotzdem: der
                     Tag, an dem ein Dialog in einen dieser Hosts
                     gehängt wird, wäre sonst ein sehr stiller Fehler.
   ---------------------------------------------------------------- */
.t-dialog-host {
  container-type: normal;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  width: auto;
  height: auto;
  overflow: visible;
  padding: var(--s4);
  padding-top: calc(var(--s4) + env(safe-area-inset-top, 0px));
  padding-bottom: calc(var(--s4) + env(safe-area-inset-bottom, 0px));
}

/* Hier stand ein kompletter Nachbau der Knopf-Optik unter
   `.t-dialog-host .t-btn…` (0,2,0 statt 0,1,0), der den flachmachenden
   Reset `.t-mod button` nur IM DIALOG ueberstimmte. Sein eigener
   Kommentar nannte den sauberen Schnitt bereits beim Namen: ein
   `:where()` um `button`. Der ist jetzt gemacht (siehe Kaskadenkollision
   Nr. 6 im Reset-Abschnitt), die Doppelung damit erledigt und entfernt.

   Der Nachbau war ausserdem SELBST die Quelle des schlimmsten Falls:
   seine Zeile `.t-dialog-host .t-btn:hover { background: var(--surface-alt) }`
   ist (0,3,0) und schlug `.t-dialog-host .t-btn--danger` (0,2,0), waehrend
   die zugehoerige Hover-Regel nur `filter` setzte — "Endgueltig loeschen"
   war beim Ueberfahren ein leeres Kaestchen (Weiss auf #F5F1E9, 1.13:1).
   Zwei Ebenen desselben Fehlers uebereinander; beide sind mit dem
   Schnitt oben und den Varianten-Hover-Regeln im Knopf-Abschnitt weg.
   Die Dialoge tragen `t-mod t-dialog-host` (dialog-host.js:49) und
   erben die Knopf-Optik jetzt aus derselben einen Quelle wie das Modul. */

/* `--r3` gibt es im Modul nicht (es kennt --r-card/--r-btn/--r-board),
   deshalb hatte die Lösch-Bestätigung eckige Ecken. Bis der Token an
   seiner Definitionsstelle nachgezogen ist, bekommt der Dialog hier
   den Modul-Radius — nicht als zweite Wahrheit, sondern als die
   richtige: --r-card ist der Radius jeder Karte im Modul. */
.t-dialog-host .t-confirm-dialog {
  border-radius: var(--r-card);
}

/* ----------------------------------------------------------------
   DIALOG-KARTE
   ---------------------------------------------------------------- */
/* Dritte Fassung, Abschnitt 07: „Beide sind jetzt eine Flaeche statt
   drei." Der Dialog ist das Blatt, das ueber allem liegt — Papierton,
   Flaechen-Ecke, der hoehere der beiden Hebewerte. Kopf und Fuss
   verlieren dabei ihre Trennlinien; den Kopf traegt derselbe
   Schriftsprung wie im Modul. */
.t-dialog {
  background: var(--panel);
  border-radius: var(--r-plane);
  width: 100%;
  max-width: 420px;
  box-shadow: var(--lift-hi);
  display: flex;
  flex-direction: column;
  /* 90vh liess auf 844px-Displays nur ~42px Luft — weniger als die Dynamic
     Island hoch ist. Zweite Zeile ueberschreibt, wo dvh verstanden wird. */
  max-height: 90vh;
  max-height: calc(
    100dvh - 32px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)
  );
}

.t-dialog-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s5) var(--s5) 2px;
}
/* Dialog-Kopf — Markenuebernahme Etappe 8.
   Dieselbe Rangfolge wie die Kopfzeile des Moduls: kleiner Kicker,
   grosser Display-Titel. Der Kicker ist hier die Subline (welches
   Spiel, wann, welche Platte) — sie stand vorher UNTER dem Titel und
   las sich wie eine Fussnote. Ueber dem Titel beantwortet sie die
   Frage "worueber reden wir", bevor der Titel sagt "was tun wir". */
.t-dialog-title {
  margin: 0;
  font-family: var(--f-disp);
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.018em;
  color: var(--ink);
}
/* Zwei Klassen, damit die Regel gegen `.t-mod button` (Zeile 217)
   ankommt — sonst fiele die Farbe auf `inherit` zurück. */
.t-dialog .t-dialog-close {
  width: 32px;
  height: 32px;
  border-radius: var(--r-btn);
  color: var(--ink-soft);
  background: transparent;
  border: none;
  display: grid;
  place-items: center;
  font-size: 16px;
  flex: none;
}
.t-dialog .t-dialog-close:hover {
  background: var(--surface-alt);
  color: var(--ink);
}

.t-dialog-body {
  padding: var(--s4) var(--s5) 6px;
  overflow-y: auto;
  flex: 1;
}

/* „Vorher: grau gefuellte Leiste mit Trennlinie, darin zwei umrandete
   Knoepfe — drei Ebenen fuer zwei Entscheidungen. Jetzt liegen die
   Knoepfe direkt auf dem Dialog."
   Beide gleich breit, wie `.btn { flex: 1 }` in der Vorlage: mit dem
   Daumen trifft man eine halbe Zeile besser als ein Wort. Die orange
   Fuellung ist die Hierarchie, nicht die Groesse. */
.t-dialog-foot {
  display: flex;
  gap: 9px;
  padding: var(--s3) var(--s5) var(--s5);
}
.t-dialog-foot > .t-btn {
  flex: 1;
}

/* Die graue Zeile mit Runde, Zeit und Platte — das Spiel steht damit
   genau einmal im Dialog, nicht zusätzlich in einer Vorschau-Box. */
.t-dialog-subline {
  order: -1;
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0;
}
.t-dialog-subline:empty {
  display: none;
}

/* Auswahlfeld, wenn kein Spiel vorgegeben ist */
.t-field {
  margin-bottom: var(--s4);
}
.t-field-label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-soft);
  margin-bottom: var(--s2);
}
.t-field-select {
  width: 100%;
  padding: 10px var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r-btn);
  background: var(--surface);
  color: var(--ink);
  font-size: 14px;
}

/* ---- Die Eingabezeilen ----
   Teamname links, Eingabefeld rechts — in einer Zeile, damit klar ist,
   welche Zahl zu wem gehört. Die Felder greifen die Anzeigetafel-Optik
   auf: dunkel im Ruhezustand wäre zu viel, aber Radius und Ziffern-
   behandlung sind dieselben.

   Die Felder starten LEER, nicht mit 0 (siehe main.js): eine
   vorbelegte Null muss man erst wegtippen, und ein versehentlich
   gespeichertes 0:0 kostet mehr Zeit als es spart. */
.t-score-entry {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}
.t-score-entry-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 84px;
  gap: var(--s3);
  align-items: center;
}
.t-score-entry-team {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-width: 0;
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
}
.t-score-entry-team .name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Die Score-Felder: Mono, gross, mittig. Sie sind das einzige, was in
   diesem Dialog getippt wird, und sollen mit dem Daumen ohne Hinsehen
   treffbar sein — deshalb 48px hoch und nicht kleiner.

   LEER, NICHT NULL: die Felder starten ohne Wert (der Renderer setzt
   keinen). Eine vorbelegte 0 waere ein Ergebnis, das niemand
   eingetragen hat — und beim Speichern eines halb ausgefuellten
   Dialogs stuende sie als echtes 0:3 in der Tabelle. Der Platzhalter
   zeigt denselben Gedankenstrich wie die Karte. */
/* Das Eingabefeld ist die eine Stelle, die ihre Kontur behaelt — man
   muss es TREFFEN, nicht nur finden. Sie liegt aber als Innenschatten
   statt als Rahmen, und das Feld sitzt auf --sunk: vertiefte Flaeche
   plus Ring ist die einzige zweite Ebene, die der Entwurf noch kennt. */
.t-score-entry-input {
  width: 100%;
  height: 48px;
  border: 0;
  box-shadow: inset 0 0 0 1px var(--line);
  border-radius: var(--r-plane);
  background: var(--sunk);
  color: var(--ink);
  font-family: var(--f-mono);
  font-size: 22px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: center;
  padding: 0;
}
.t-score-entry-input:focus {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--accent);
}
/* Die Pfeilchen der Zahlenfelder stören hier nur */
.t-score-entry-input::-webkit-outer-spin-button,
.t-score-entry-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.t-score-entry-input {
  -moz-appearance: textfield;
  appearance: textfield;
}

/* ---- Auf dem Handy fährt der Dialog von unten ein ----
   Nur der Ergebnis-Dialog: er ist der einzige, den man während eines
   Turniers dutzendfach mit dem Daumen bedient. Die Lösch-Bestätigung
   und der Team-Picker bleiben mittig — eine Bestätigung, die von unten
   hereinwischt, lädt zum Wegtippen ein. Deshalb der Modifier
   `--sheet` statt einer Regel auf `.t-dialog-host`. */
@media (max-width: 599px) {
  .t-dialog-host--sheet {
    align-items: flex-end;
    padding: 0;
  }
  .t-dialog {
    max-width: none;
    border-radius: var(--r-card) var(--r-card) 0 0;
    padding-bottom: env(safe-area-inset-bottom);
    animation: t-dialog-up 0.2s ease;
  }
  .t-score-entry-row {
    grid-template-columns: minmax(0, 1fr) 96px;
  }
  .t-score-entry-input {
    height: 56px;
    font-size: 26px;
  }
}
@keyframes t-dialog-up {
  from {
    transform: translateY(24px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  .t-dialog {
    animation: none;
  }
}

/* ================================================================
   DRUCKLAYOUT (@media print) — 2026-08-25
   ================================================================
   Es gab keins. `grep -c '@media print'` lieferte in main.css UND
   tournament.css 0, der "Drucken"-Knopf (main.js:2915, Kontextmenue
   :2922, Handler :3083) rief blank `window.print()` — also der
   Bildschirm auf Papier, mit Seitenleiste, Bodenleiste, Filtern und
   Knoepfen. Ein Knopf ohne Funktion ist laut Hausregel kein Knopf.

   WAS GEDRUCKT WIRD: der fachliche Kern — Spielplan, Gruppentabellen,
   Turnierbaum. Alles Bedienbare faellt weg; auf Papier kann man nichts
   klicken, und jeder Knopf kostet eine Zeile, die eine Paarung braucht.

   DREI ENTSCHEIDE, die man dem Ergebnis nicht ansieht:

   1. ALLE DREI ANSICHTEN AUF EINMAL. Auf dem Bildschirm zeigt das Modul
      genau einen Tab (`.t-view.is-active`). Wer druckt, will aber das
      TURNIER, nicht den Tab, in dem er gerade steht. Der Druck loest
      `.is-active` deshalb auf und zeigt Spielplan, Gruppen und Baum
      nacheinander — jeweils auf einer eigenen Seite.
      HAKEN: Gruppen und Baum haengen an Lazy-Mounts
      (`[data-tab-body="gruppen-mount"]`), die erst beim ersten
      Tab-Klick gefuellt werden. Wer direkt aus dem Spielplan druckt,
      haette sonst zwei leere Ueberschriften auf zwei leeren Seiten.
      Deshalb blendet die `:has()`-Regel weiter unten eine Ansicht aus,
      deren Mount leer ist. Faellt `:has()` aus (aeltere Engine), wird
      NUR diese eine Regel verworfen — der Rest des Drucklayouts steht.

   2. FARBE IST AUF PAPIER KEIN TRAEGER. Statusstreifen (offen /
      laeuft / beendet) unterscheiden sich am Bildschirm nur durch die
      Farbe des 3px-Balkens; im Graudruck sind alle drei dasselbe Grau.
      Statt `print-color-adjust: exact` zu erzwingen (das haengt am
      Treiber und schluckt Toner) bekommt jeder Zustand ein Merkmal,
      das ohne Farbe traegt: der Balken wird zu einem Rand mit eigener
      STRICHART, und `.t-match-action-text` bleibt stehen — dort steht
      der Zustand bereits als WORT ("Beendet"). Nur der Knopf darin
      faellt weg, nicht der Text.
      Dieselbe Logik bei den Badges: statt Fuellung ein Rand.

   3. DIE ANZEIGETAFEL WIRD UMGEDREHT. `.t-match-score` ist am
      Bildschirm ein dunkler Block mit heller Ziffer (--board /
      --board-ink). Auf Papier waere das ein schwarzer Kasten pro
      Ergebnis. Im Druck also weiss mit Rand und schwarzer Ziffer.

   Kein `@page`-Kopf mit Turniername und Datum: der Modulkopf traegt
   Namen und Phase und bleibt als erste Zeile stehen. Ein DATUM steht
   im Detail-Kopf gar nicht im DOM (main.js baut dort Modus und
   Team-Zahl) — das nachzuruesten ist eine Aenderung an main.js und
   gehoert nicht in eine CSS-Runde.
   ---------------------------------------------------------------- */
@media print {
  @page {
    size: A4 portrait;
    margin: 14mm;
  }

  /* ---- ERST DIE TOKENS, DANN ALLES ANDERE ------------------------
     Im Dunkelmodus druckte diese Seite fast leer: --ink ist dort
     #F2EDE4, also hellbeige — auf weissem Papier praktisch unsichtbar.
     Im ersten Druck-Screenshot mit gesetztem [data-theme=dark] waren
     Ueberschriften, Teamnamen und Tabellenwerte nur noch zu erahnen.
     Das ist kein Randfall: das Theme haengt am Geraet des Druckenden,
     nicht am Dokument.
     Papier hat genau EIN Thema. Der Druck setzt deshalb den kompletten
     Token-Satz des Moduls auf Schwarz-auf-Weiss zurueck — an EINER
     Stelle, statt an jeder Regel eine Druckfarbe nachzuziehen.
     Der Selektor traegt [data-theme=dark] mit, weil der Dunkel-Block
     oben (0,2,0) sonst gewinnt.

     WARUM HIER !important STEHT (2026-08-29, nachgemessen).
     Der Satz darueber stimmte, als er geschrieben wurde, und war
     danach still falsch. Der Dunkel-Block heisst inzwischen
     (tokens.css:347):

       [data-theme='dark'] :is(.t-mod, .t-brand,
                               #content:has(> .t-detail-host),
                               #content:has(.t-list-host))

     `:is()` uebernimmt die Spezifitaet seines STAERKSTEN Arms, und
     einer davon traegt eine ID. Der Selektor steht damit bei (1,2,0),
     die Zeile hier bei (0,2,0) — der ganze Druck-Reset war im
     Nachtmodus wirkungslos. Gemessen am Geraet unter `media: print`
     mit gesetztem data-theme=dark: `--ink` kam als #f1f0ed heraus,
     also der Dunkelwert, und die Aufstiegsbaender druckten in
     #f0794d. Genau der Fehler, den der Absatz oben als behoben
     beschreibt, nur an einem Selektor, der seither umgebaut wurde.

     Ein Spezifitaets-Wettrennen waere die falsche Antwort: die naechste
     Erweiterung jener :is()-Liste gewinnt es wieder, und zwar
     unbemerkt, weil ein zu schwacher Reset nichts meldet. `!important`
     schlaegt jede nicht-wichtige Deklaration unabhaengig von der
     Spezifitaet — und die Aussage dahinter ist keine Notloesung,
     sondern die Hausregel dieses Blocks: Papier hat genau EIN Thema,
     und im Druck gibt es nichts, was einen Druckwert ueberstimmen
     duerfte. Der Detektor dagegen steht in druck-tokens.test.js. */
  .t-mod,
  [data-theme='dark'] .t-mod {
    --paper: #ffffff !important;
    --surface: #ffffff !important;
    --surface-alt: #ffffff !important;
    --ink: #000000 !important;
    --ink-soft: #3a3a3a !important;
    --line: #999999 !important;
    --accent: #000000 !important;
    --accent-soft: #ffffff !important;
    --accent-soft-ink: #000000 !important;
    --open: #000000 !important;
    --qualified: #000000 !important;
    --done: #000000 !important;
    --out: #555555 !important;
    --live: #000000 !important;
    --danger: #000000 !important;
    --warn: #000000 !important;
    --warn-soft: #ffffff !important;

    /* NACHTRAG 2026-08-29 — der Satz oben ist der von Etappe 8 und hat
       die Markenuebernahme nicht mitbekommen. Gemessen: vierzehn heute
       benutzte Farbtoken standen nicht darin, darunter --muted, --ink-2,
       --panel, --sunk, --raised und --ground. Im Nachtmodus sind das
       saemtliche Grau- und Flaechenwerte — also genau der Fehler, den der
       Kommentar oben als behoben beschreibt, nur an anderen Namen. Der
       Detektor dagegen steht in druck-tokens.test.js.

       Vier Werte sind hier ausserdem entfallen (--board, --board-ink,
       --board-dim, --danger-ink): die Anzeigetafel ist am 26.08. aus dem
       Modul geflogen, ihre Druckfarbe war seither eine Karteileiche.

       Die Flaechen werden ALLE weiss, nicht abgestuft grau: eine
       Tonleiter aus sechs Grauwerten, die am Bildschirm Tiefe erzeugt,
       wird auf Papier zu sechs kaum unterscheidbaren Schleiern — sie
       kosten Toner und tragen nichts. Auf Papier tragen Kanten. */
    --ground: #ffffff !important;
    --panel: #ffffff !important;
    --raised: #ffffff !important;
    --sunk: #ffffff !important;
    --line-soft: #bbbbbb !important;
    --ink-2: #222222 !important;
    --muted: #3a3a3a !important;
    /* Aufstieg und Titel: Farbe traegt auf Papier nicht (Entscheid 2
       weiter unten). Die getoente Zeile wird weiss, das 3-px-Band an der
       Blattkante bleibt als Signal — aber in Tinte statt in Orange bzw.
       Gruen, sonst waere es im Graudruck derselbe Ton wie die Zeile. */
    --flare: #000000 !important;
    --flare-soft: #ffffff !important;
    --qual: #000000 !important;
    --qual-soft: #ffffff !important;
    --live-soft: #ffffff !important;
    --on-flare: #ffffff !important;
    --on-danger: #ffffff !important;
  }

  /* ---- Modul-Rahmen: alle Scroll- und Hoehenklammern loesen -------
     .t-mod ist am Bildschirm ein Container mit fester Hoehe und
     overflow:hidden; im Druck wuerde damit alles nach der ersten
     Seite verschwinden. */
  .t-mod {
    display: block;
    height: auto;
    min-height: 0;
    max-height: none;
    overflow: visible;
    background: #fff;
  }
  .t-shell {
    display: block;
  }
  .t-mod-main,
  .t-mod-aside {
    display: block;
    padding: 0;
    overflow: visible;
    height: auto;
    max-height: none;
  }

  /* ---- Was auf Papier nichts verloren hat ------------------------
     Navigation (Seitenleiste, Bodenleiste, Kontextmenue, Bottom-Sheet),
     alles Bedienbare (Knoepfe, Filter, Editoren) und die Kontextspalte.
     Die Bodenleiste ist position:fixed — ohne diese Zeile klebte sie
     auf JEDER Seite. */
  .t-mod-nav,
  .t-mod-tabs,
  .t-mod-more-sheet,
  .t-mod-more-overlay,
  .t-mod-more-panel,
  .t-mod-aside,
  .t-dialog-host,
  .t-toolbar,
  .t-filter-wrap,
  .t-filter-dropdown,
  .t-btn,
  .t-view-head .spacer,
  .t-match-action .t-btn,
  .t-schedule-edit-bar,
  .t-schedule-edit-actions,
  .t-fill-ko-card,
  .bracket-tabs,
  .t-swap-bar,
  .t-hint {
    display: none !important;
  }

  /* ---- Die drei fachlichen Ansichten, jede auf eigener Seite ----- */
  .t-view {
    display: none !important;
  }
  .t-view[data-view='spielplan'],
  .t-view[data-view='gruppen'],
  .t-view[data-view='baum'] {
    display: block !important;
    break-before: page;
  }
  /* Die erste gedruckte Ansicht faengt keinen Seitenumbruch an. */
  .t-view[data-view='spielplan'] {
    break-before: auto;
  }

  /* Leere Lazy-Mounts nicht als leere Seite drucken (Entscheid 1). */
  .t-view:has(> [data-tab-body]:empty) {
    display: none !important;
  }

  /* ---- Kopf ------------------------------------------------------ */
  .t-mod-header {
    border-bottom: 2px solid #000;
    padding: 0 0 6mm;
    margin-bottom: 6mm;
    background: none;
    break-after: avoid;
  }
  .t-view-head {
    break-after: avoid;
    border-bottom: 1px solid #999;
    padding-bottom: 2mm;
  }

  /* ---- Nichts zerreissen ----------------------------------------- */
  .t-card,
  .t-group-card,
  .t-match,
  .bracket-col,
  .t-standings-table tr,
  .t-thirds-table tr {
    break-inside: avoid;
  }
  .t-card {
    border: 1px solid #999;
    box-shadow: none;
    background: none;
    margin-bottom: 4mm;
  }

  /* ---- Turnierbaum: alle Runden, kein horizontaler Scroll -------- */
  .bracket-wrap {
    display: flex;
    flex-wrap: wrap;
    overflow: visible;
    gap: 8mm;
  }
  .bracket-col {
    min-width: 55mm;
    max-width: 70mm;
  }

  /* ---- Farbe ersetzen (Entscheid 2) ------------------------------
     Strichart statt Farbe: gepunktet = offen, doppelt = laeuft,
     durchgezogen = beendet. */
  .t-match-bar {
    background: none !important;
    border-left: 3px dotted #000;
  }
  .t-match--live .t-match-bar {
    border-left-style: double;
    border-left-width: 4px;
  }
  .t-match--done .t-match-bar {
    border-left-style: solid;
  }
  .t-match--live .t-match-meta-ort::after {
    content: ' · laeuft';
    font-weight: 700;
  }
  .t-match--done .t-match-team {
    color: #000;
  }
  .t-match {
    background: none;
    border-bottom: 1px solid #ccc;
  }
  .t-match-action-text {
    display: inline;
    font-size: 10pt;
  }

  .t-badge {
    background: none !important;
    color: #000 !important;
    border: 1px solid #000;
  }
  .t-dot {
    border: 1px solid #000;
    background: none !important;
  }

  /* ---- Anzeigetafel umdrehen (Entscheid 3) ----------------------- */
  .t-match-score {
    background: none !important;
    color: #000 !important;
    border: 1px solid #000;
  }
  /* (2026-08-26) Der gestrichelte Umriss ist mit der Anzeigetafel
     entfallen; auf Papier blieb sonst ein leeres Kaestchen stehen. */
  .t-match-score.empty {
    border: 0;
  }

  /* ---- Tabellen lesbar auf Papier -------------------------------- */
  .t-standings-table,
  .t-thirds-table {
    font-size: 10pt;
  }
  .t-standings-table th,
  .t-thirds-table th {
    border-bottom: 1px solid #000;
  }
  /* Der Zeilenhintergrund steht auf der ZELLE, nicht auf der Zeile —
     wer nur `tr` neutralisiert, bekommt auf Papier trotzdem ein graues
     Band (im ersten Druck-Screenshot genau so passiert). Deshalb `td`.
     Klassennamen ZWEIMAL nachgezogen: A3 machte aus is-first/is-second
     die Namen is-qualified/is-pending/is-cutoff (2026-08-25), die
     Markenuebernahme einen Tag spaeter daraus is-lead/is-qualified
     (spielplan-helpers.js:884). Diese Regel blieb bei der zweiten
     Umbenennung stehen und entfaerbte seither zwei Klassen, die der
     Renderer nicht mehr ausgibt — der orange Fuehrungsstreifen von
     is-lead ging ungebremst aufs Papier. Sechste Kollision derselben
     Art in diesem Modul; der Detektor dagegen ist druck-tokens.test.js,
     der die Klassen des Renderers gegen diesen Block haelt.

     Der Zeilenhintergrund steht auf der ZELLE, nicht auf der Zeile —
     wer nur `tr` neutralisiert, bekommt auf Papier trotzdem ein graues
     Band. Deshalb `td`.

     Das 3-px-Band auf der Rangzelle wird NICHT abgeraeumt: es ist das
     Signal, das ohne Farbe traegt ("Band da" gegen "Band nicht da"),
     und es steht ueber --flare/--qual schon auf Schwarz. */
  .t-standings-row.is-lead,
  .t-standings-row.is-qualified,
  .t-standings-row.is-lead td,
  .t-standings-row.is-qualified td,
  .t-thirds-row.is-qualified,
  .t-thirds-row.is-qualified td {
    background: none !important;
  }

  /* Das Logo-Ersatzquadrat ist am Bildschirm eine dunkle Flaeche
     (background: var(--ink)) — auf Papier ein schwarzer Klotz in der
     Kopfzeile jedes Ausdrucks. Als Umriss statt als Flaeche. Ein
     echtes <img class="t-logo"> bleibt unberuehrt. */
  .t-logo--placeholder {
    background: none !important;
    border: 1px solid #000;
    color: #000 !important;
  }
  .t-thirds-row.is-out {
    opacity: 1;
    font-style: italic;
  }

  /* ---- Fokusringe und Animationen gehoeren nicht auf Papier ------ */
  .t-mod :focus-visible {
    outline: none;
  }
  .t-mod * {
    animation: none !important;
    transition: none !important;
  }
}

/* ----------------------------------------------------------------
   SEGMENT-LEISTE — Markenuebernahme Etappe 2
   ----------------------------------------------------------------
   Loest die Chip-Reihen ab, wo genau EINE Wahl gilt (Gruppe A/B/C,
   Runden/Ganzer Baum, Spielmodus). Optisch eingesenkt, die aktive
   Flaeche liegt als Panel darauf — das sagt "eine von diesen", waehrend
   eine Chip-Reihe "beliebig viele" nahelegt.

   Nicht zu verwechseln mit .t-filter-chip weiter unten: die Chips sind
   Filter mit Anzahl, die Segmente sind ein Umschalter. Zwei Bedeutungen,
   zwei Formen — deshalb bewusst nicht dieselbe Komponente.
   ---------------------------------------------------------------- */
.t-seg {
  display: flex;
  gap: 4px;
  padding: 4px;
  background: var(--sunk);
  border: 1px solid var(--line);
  border-radius: var(--r-btn);
}
.t-seg > button {
  flex: 1;
  min-width: 0;
  padding: 8px 4px;
  border: 0;
  border-radius: 7px;
  background: none;
  font-family: var(--f-cond);
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.t-seg > button:hover {
  color: var(--ink);
}
.t-seg > button.is-active {
  background: var(--panel);
  color: var(--ink);
  box-shadow: var(--shadow-s);
}

/* ----------------------------------------------------------------
   FILTER-CHIPS mit Anzahl — Markenuebernahme Etappe 2
   ----------------------------------------------------------------
   Jeder Chip traegt seine Zahl. Wer "Offen 7" sieht, weiss ohne einen
   Klick, wie weit der Tag ist — das war der Grund, die Zahl ueberhaupt
   mitzurendern statt sie im Filter zu verstecken.
   ---------------------------------------------------------------- */
.t-filter-row {
  display: flex;
  gap: var(--s2);
  overflow-x: auto;
  scrollbar-width: none;
  padding-bottom: 2px;
}
.t-filter-row::-webkit-scrollbar {
  display: none;
}
.t-filter-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel);
  font-family: var(--f-cond);
  font-weight: 600;
  font-size: 12.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-2);
  cursor: pointer;
}
.t-filter-chip .t-filter-n {
  font-family: var(--f-mono);
  font-size: 11.5px;
  color: var(--muted);
}
/* Aktiv ist TINTE, nicht Orange. Orange bleibt dem Weg zum Titel
   vorbehalten — ein aktiver Filter ist ein Ort, kein Titelweg. */
.t-filter-chip.is-active {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--panel);
}
.t-filter-chip.is-active .t-filter-n {
  color: var(--panel);
  opacity: 0.72;
}

/* Der Kicker darf UMBRECHEN — bis zu zwei Zeilen, dann kuerzt er.
   Vorher stand hier `white-space: nowrap`, und der laengste Kicker des
   Moduls („Nur du als Organisator siehst diesen Bereich") wurde zu
   „…siehst diesen B…". Ein abgeschnittener Satz ist keine Information,
   sondern ein Raetsel.
   Die Vorlage laesst ihn an derselben Stelle in zwei Zeilen laufen; das
   Logo steht ohnehin neben BEIDEN Kopfzeilen und traegt den Umbruch mit,
   ohne dass der Kopf auseinanderfaellt. Zwei Zeilen sind die Grenze:
   danach waere der Titel darunter nicht mehr das Groesste auf dem
   Schirm, und daran erkennt man die Ansicht im Vorbeigehen. */
.t-mod-kicker-text {
  min-width: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

/* ----------------------------------------------------------------
   FARBPUNKTE VOR DEN TEAMNAMEN — Markenuebernahme Etappe 3
   ----------------------------------------------------------------
   Der Entwurf zeigt in der Match-Karte KEINEN Punkt. Der Grund ist
   nicht Geschmack: die Teamfarbe wiederholt nur, was der Name schon
   sagt, und auf 390px kostet sie den Platz, den lange Namen brauchen.
   In der Gruppentabelle bleibt der Punkt, dort ordnet er Zeilen einer
   Gruppe zu — dieselbe Form, zwei verschiedene Aufgaben.

   Ausgeblendet statt aus dem Renderer entfernt: das Element traegt die
   Teamfarbe als Inline-Style und wird von applyPropagatedMatches
   angefasst. Eine tote CSS-Regel ist billiger zurueckzunehmen als ein
   geloeschtes Element mit Logik daran. */
.t-match .t-match-team > .t-dot {
  display: none;
}

/* Aktionszeile der Karte: eigene Zeile unter der Meta, rechtsbuendig.
   Sie traegt entweder den Admin-Knopf ("Ergebnis erfassen") oder den
   Mitglieder-Text ("Beendet"). Vorher lag sie als vierte Grid-Spalte
   neben den Teams und lief bei langen Namen aus der Karte. */
/* Die Aktion ist die rechte Haelfte des Fussbands, keine eigene Zeile
   mehr. Kein Grid-Bereich, keine Trennlinie, kein eigenes Polster — das
   bringt das Band mit. */
.t-match-action {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--s2);
  min-width: 0;
  flex: none;
}
.t-match-action:empty {
  display: none;
}
/* Der Mitglieder-Text ist Zustand, kein Knopf — also dieselbe Sprache
   wie die Meta-Zeile darueber. */
.t-match-action > span,
.t-match-action > .t-match-status {
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ----------------------------------------------------------------
   ZEITACHSE — Markenuebernahme Etappe 5
   ----------------------------------------------------------------
   Uhrzeit links in Mono, eine Haarlinie dazwischen, rechts der Zustand.
   Die Marke gliedert den Spielplan nach Anstosszeit statt nach Gruppe:
   am Spieltisch ist "was kommt als Naechstes" die Frage, nicht "wer
   spielt in Gruppe B".

   Der aktuelle Block traegt Orange — hier ist die einzige Stelle im
   Spielplan, an der der Akzent auftaucht. Alles Vergangene bleibt grau,
   alles spaeter Geplante ebenfalls: nur EIN Block ist "als Naechstes",
   sonst waere es keine Reihenfolge.
   ---------------------------------------------------------------- */
.t-zeitmarke {
  display: flex;
  align-items: center;
  gap: 11px;
  /* Vorlage `.tmk`: 24px Luft darueber, 8px darunter, seitlich auf der
     Kante der Karten (die tragen margin: 0 8px). Die Uhrzeit haengt
     damit 12px weiter links als die Teamnamen — die Zeitmarke steht
     eine Ebene ueber den Karten, also darf sie ausbrechen. */
  margin: var(--s6) 0 var(--s2);
  padding: 0 var(--s2);
  font-family: var(--f-mono);
}
.t-zeitmarke:first-child {
  margin-top: 0;
}
.t-zeitmarke-zeit {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  flex: none;
}
.t-zeitmarke-linie {
  flex: 1;
  height: 1px;
  /* Auf dem Blatt trennt die WEICHE Linie. --line waere hier der Ton,
     den die Karten als Rahmen tragen — zwei gleich harte Kanten
     nebeneinander, und die Achse waere so laut wie die Spiele. */
  background: var(--line-soft);
}
.t-zeitmarke-zustand {
  flex: none;
  display: inline-flex;
  align-items: center;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.t-zeitmarke.is-next .t-zeitmarke-zustand {
  color: var(--flare);
}
.t-dot-flare {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--flare);
  margin-right: 6px;
  flex: none;
}

/* ----------------------------------------------------------------
   DER WEG ZUM TITEL — entfallen, Entscheid Jonas 2026-08-26
   ----------------------------------------------------------------
   Hier standen sieben Regeln fuer eine Miniatur, die es im Modul nicht
   gibt: renderBracket setzt `const wegHtml = ''` und begruendet das mit
   Jonas' Urteil („das sieht nicht schoen aus").

   Das Artefakt zeigt die Strecke in Abschnitt 05 ausdruecklich — der
   Entscheid des Menschen schlaegt die Vorlage, und er steht seit dem
   26.08. im Renderer. Was sie leisten sollte (zeigen, wie weit es noch
   ist), leisten die Runden-Pillen mit Zahlen.

   Die Regeln sind deshalb geloescht statt auskommentiert: totes CSS
   sieht aus wie eine Moeglichkeit, und die naechste Sitzung haette
   `.t-weg` fuer eine benutzte Komponente gehalten.
   ---------------------------------------------------------------- */

/* Runden-Pillen: der Stand steht in der Pille, die aktuelle Runde ist
   TINTE gefuellt. Orange bleibt dem Titelweg vorbehalten — eine Runde
   ist ein Ort, kein Ziel. */
/* Dritte Fassung, Abschnitt 05: „Halbfinale 0/2 war vorher eine eigene
   Komponente mit eigenem Rahmen. Jetzt ist es die Segmentzeile aus
   Abschnitt 01 mit Zaehlwert — eine Form weniger im System."
   Deshalb dieselben Werte wie .t-chip: Archivo Narrow, Versalien,
   gesperrt, kein Behaelter, aktiv tintengefuellt. Vorher stand hier
   Archivo 600 in 14px mit Rahmen — auf 390px passten drei Runden damit
   nicht nebeneinander und die dritte war abgeschnitten. */
.bracket-tabs {
  display: flex;
}
.bracket-tab {
  display: inline-flex;
  align-items: baseline;
  gap: 7px;
  padding: 7px 14px;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  background: none;
  font-family: var(--f-cond);
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.bracket-tab:hover {
  color: var(--ink);
}
.bracket-tab-stand {
  font-family: var(--f-mono);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.bracket-tab.is-active {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--panel);
}
.bracket-tab.is-active .bracket-tab-stand {
  color: var(--panel);
  opacity: 0.68;
}

/* Gefahrenzone: Karmin am Rand und in der Kopfzeile. Sie ist der einzige
   Bereich, der eine eigene Farbe traegt — alles andere in den
   Einstellungen ist neutral, damit dieser eine Block auffaellt, ohne
   dass man ihn suchen muss. */
.t-settings-section.t-danger-zone {
  border-color: var(--danger);
}
.t-settings-section.t-danger-zone .t-settings-section-header {
  background: var(--panel);
  border-bottom-color: var(--danger);
}
.t-settings-section.t-danger-zone .t-settings-section-title {
  color: var(--danger);
}

/* Zuschauer-Link: die Zeile aus der Nachbar-Session trug ein Inline-Flex
   am Eingabefeld, weil tournament.css waehrend ihres Baus umgebaut wurde
   (bewusst kein neues CSS von dort). Die Klasse holt das nach — das
   Inline-Style darf beim naechsten Anfassen des Blocks raus. */
.t-settings-actions > .t-input {
  flex: 1;
  min-width: 0;
}

/* Teamname im Ergebnis-Dialog: Display-Schnitt wie auf der Karte, damit
   man dieselbe Partie wiedererkennt, die man angetippt hat. */
.t-score-entry-team .name {
  font-family: var(--f-disp);
  font-weight: 600;
  font-size: 16px;
  letter-spacing: -0.01em;
  color: var(--ink);
}
/* Der Farbpunkt bleibt HIER, anders als auf der Karte: im Dialog steht
   nur eine Partie, der Punkt kostet keinen Platz und hilft beim
   Zuordnen, welches Feld zu welchem Team gehoert. */

/* ---- Bestaetigungs-Dialog ---- */
.t-confirm-title {
  margin: 0;
  font-family: var(--f-disp);
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.018em;
  color: var(--ink);
}
.t-confirm-message {
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--ink-2);
}
/* Die Zahl im Satz ist der Punkt: "10 erfasste Ergebnisse gehen
   verloren" bremst, "Bist du sicher?" nicht. Deshalb traegt sie die
   Tinte und das Gewicht, waehrend der Rest des Satzes zurueckhaengt. */
.t-confirm-message strong {
  color: var(--ink);
  font-weight: 600;
}
.t-confirm-expected,
.t-confirm-input {
  font-family: var(--f-mono);
}
.t-confirm-expected {
  font-size: 12.5px;
  color: var(--muted);
}
.t-confirm-input {
  border-radius: var(--r-btn);
  background: var(--raised);
  font-size: 14px;
}

/* Der Platzhalter zeigt denselben Gedankenstrich wie die Karte — leer
   heisst "noch kein Ergebnis", und das soll im Dialog genauso aussehen
   wie in der Liste. Ein leeres Feld sagt es auch, aber undeutlicher. */
.t-score-entry-input::placeholder {
  color: var(--muted);
  opacity: 1;
  font-weight: 500;
}

/* Der Dialog-Kopf traegt jetzt zwei Zeilen links und den Schliessen-
   Knopf rechts. Vorher lag die Subline im Body und las sich wie eine
   Fussnote unter dem Titel; oben beantwortet sie "worueber reden wir",
   bevor der Titel sagt "was tun wir" — dieselbe Rangfolge wie die
   Kopfzeile des Moduls. `order: -1` an der Subline ist damit
   gegenstandslos, sie steht ohnehin zuerst im Markup. */
.t-dialog-head-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.t-dialog-head-text .t-dialog-subline {
  order: 0;
}

/* ================================================================
   DRUCKBOEGEN — Markenuebernahme Etappe 9
   ================================================================
   Zwei Zustaende, eine Formatierung: am Bildschirm liegen die Boegen
   als Papier auf dem Grund (Vorschau im Drucken-Tab), im Druck sind
   sie das einzige, was uebrig bleibt.

   Bewusst SCHWARZ AUF WEISS, unabhaengig vom Thema. Ein Bogen ist
   Papier; im Nachtmodus einen dunklen Bogen zu zeigen und dann hell zu
   drucken waere eine Vorschau, die luegt. Deshalb stehen hier feste
   Farben und keine Tokens — die einzige Stelle im Modul, an der das
   richtig ist.
   ================================================================ */
.t-druck {
  display: flex;
  flex-direction: column;
  gap: var(--s5);
}
/* ---- Die Vorschau am Handy (2026-08-29) ------------------------
   Gemessen bei 375 px: der Bogen war 307 px breit, sein Inhalt
   brauchte 563 px — und `.t-mod-main` traegt `overflow-x: clip`.
   Der Bogen wurde also ABGESCHNITTEN, nicht scrollbar: die rechten
   Tabellenspalten (Ergebnis, Punkte) waren am Telefon schlicht nicht
   erreichbar, und niemand konnte pruefen, was der Drucker ausgibt.

   Bewusst SCROLLEN statt umbrechen. Ein Bogen ist ein A4-Blatt; wer
   ihn am Handy schmal umbrechen laesst, sieht eine Anordnung, die
   der Drucker nie ausgibt — die Vorschau wuerde luegen, und genau
   dafuer ist sie da ("Was man sieht, kommt aus dem Drucker",
   spielplan-helpers.js:1486). 560 px ist die gemessene Mindestbreite
   des Bogeninhalts, nicht geschaetzt.

   Der Scroller sitzt auf `.t-druck` und nicht auf `.t-bogen`: die
   Blattkante soll mitwandern, sonst scrollt Papier in einem Rahmen,
   der stehen bleibt. Im Druck ist beides gegenstandslos — dort setzt
   der Druckblock `.t-druck` ohnehin auf `display: block`.

   `screen and` ist nicht Zierrat: ohne das Wort greift die Regel auch
   im Druck. Gemessen mit emulateMedia bei 375 px Viewport stand der
   Bogen dann mit min-width 560 px in einem 335 px breiten Rahmen mit
   overflow-x — auf Papier gibt es kein Scrollen, das haette rechts
   abgeschnitten gedruckt. Der Scroll-Rahmen ist eine Bedienhilfe des
   Bildschirms und hat im Druck nichts zu suchen. */
@media screen and (max-width: 660px) {
  .t-druck {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    /* Der Schatten des Blattes braucht Luft, sonst schneidet ihn der
       Scroll-Rahmen an der Oberkante ab. */
    padding: 4px 0;
  }
  .t-bogen {
    min-width: 560px;
    /* Das seitliche Polster faellt kleiner aus: 24 px auf einem
       Blatt, das ohnehin gescrollt wird, sind 48 px Scrollweg ohne
       Inhalt. */
    padding: 20px 16px;
  }
}
.t-bogen {
  background: #fff;
  color: #111;
  width: 100%;
  max-width: 760px;
  /* MINDESTHOEHE statt aspect-ratio, und overflow sichtbar.
     Mit `aspect-ratio` und `overflow: hidden` haette ein Bogen die Form
     eines Blattes — und ein Turnier mit 30 Partien haette die Haelfte
     davon still abgeschnitten. Auf Papier soll Inhalt UMBRECHEN, nicht
     verschwinden; am Bildschirm soll die Vorschau zeigen, was wirklich
     gedruckt wird. 760 x 1.414 = 1075px ist die A4-Proportion als
     Untergrenze, damit ein kurzer Bogen trotzdem wie ein Blatt aussieht. */
  min-height: 1075px;
  margin: 0 auto;
  padding: 30px 32px;
  border-radius: 2px;
  box-shadow:
    0 2px 4px rgba(11, 11, 12, 0.1),
    0 20px 48px rgba(11, 11, 12, 0.16);
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow: visible;
  font-family: var(--f-body);
}
.t-bogen--quer {
  min-height: 707px;
  max-width: 1000px;
}
.t-bogen-kopf {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 14px;
  border-bottom: 2px solid #111;
  /* Doppellinie unter dem Kopf — die klassische Aushang-Signatur:
     2px Balken plus 1px Haarlinie 3px darunter (Spread -3 laesst vom
     4px-Schatten genau 1px stehen). Kein zweites Element noetig. */
  box-shadow: 0 4px 0 -3px #111;
  padding-bottom: 9px;
}
.t-bogen-kopf-links {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  min-width: 0;
}
/* Der Textblock zwischen Logo und Metaspalte (2026-08-29).
   `min-width: 0` steht oben am Elternteil, aber die Kette bricht eine
   Ebene tiefer: das <div> um Marke und Titel ist selbst ein Flex-Kind
   mit der Vorgabe `min-width: auto` und schrumpft deshalb nicht unter
   seine laengste Zeile. Gemessen bei 375 px: der Titel endete bei
   x=281, die Metaspalte begann bei x=240 — 41 px Text auf Text.
   Ein Elternteil mit min-width: 0 hilft nichts, wenn das Kind es nicht
   auch hat. */
.t-bogen-kopf-links > div {
  min-width: 0;
}
/* Der Turniername bricht lieber um, als in die Metaspalte zu laufen.
   Kein `ellipsis`: auf einem Bogen ist der Name die Ueberschrift, und
   eine abgeschnittene Ueberschrift auf Papier ist schlimmer als eine
   zweizeilige. */
.t-bogen-titel {
  overflow-wrap: anywhere;
}
/* Die Metaspalte gibt nicht nach: Datum, Ort und Spielzahl sind kurz
   und stehen rechtsbuendig als Block. Ohne `flex-shrink: 0` gibt sie
   als erste nach und ihre Zeilen brechen an falschen Stellen. */
.t-bogen-meta {
  flex: 0 0 auto;
}
/* 54px = 19 mm auf A4. Der Ring in hellem Grau bleibt: ein sehr helles
   Logo haette auf weissem Papier sonst ueberhaupt keine Kante.
   10px Luft zur 2px starken schwarzen Kopflinie, damit ein dunkles Logo
   nicht mit ihr zu einem Klotz verschmilzt — das passiert genau bei den
   Vereinswappen, die ohnehin dunkel sind.
   GRAUSTUFEN ist Absicht und kein Fehler: der Bogen zeigt das Logo so,
   wie es aus einem Schwarz-Weiss-Drucker kommt. Wer sein Wappen in Gruen
   und Dunkelblau kennt, haelt zwei fast gleiche Grautoene im Ausdruck
   sonst fuer einen Fehler des Programms. Die Vorschau ist ehrlich. */
.t-bogen-logo {
  width: 54px;
  height: 54px;
  border-radius: 24%;
  object-fit: cover;
  border: 1px solid #ccc;
  flex: 0 0 auto;
  margin-bottom: 2px;
  filter: grayscale(1);
}
.t-bogen-marke {
  font-family: var(--f-mono);
  font-size: 8.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  /* Flare als einziger Farbtupfer des Blattes. Er TRAEGT nichts (auf
     einem S/W-Laser wird er Mittelgrau, das ist einkalkuliert) — er
     unterschreibt nur. Information bleibt Schwarz auf Weiss. */
  color: #bf4413;
}
.t-bogen-titel {
  margin: 2px 0 0;
  font-family: var(--f-disp);
  font-weight: 800;
  font-size: 25px;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: #111;
}
.t-bogen-meta {
  text-align: right;
  font-family: var(--f-mono);
  font-size: 8.5px;
  line-height: 1.5;
  color: #555;
}
.t-bogen-koerper {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.t-bogen-raster {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 16px;
  align-content: start;
}
.t-bogen-baum {
  justify-content: center;
}
.t-bogen-baum svg {
  width: 100%;
  height: auto;
}
.t-bogen-abschnitt {
  font-family: var(--f-mono);
  font-size: 8.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #555;
  border-bottom: 1px solid #ddd;
  padding-bottom: 3px;
  margin-top: 2px;
}
table.t-bogen-tab {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--f-mono);
  font-size: 10px;
  font-variant-numeric: tabular-nums;
}
table.t-bogen-tab th {
  font-size: 8px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #555;
  font-weight: 500;
  padding: 4px 4px;
}
table.t-bogen-tab td {
  padding: 5px 4px;
  border-top: 1px solid #e4e4e4;
}
table.t-bogen-tab .l {
  text-align: left;
}
table.t-bogen-tab .r {
  text-align: right;
}
table.t-bogen-tab .nm {
  font-family: var(--f-disp);
  font-size: 11px;
  font-weight: 600;
}
/* Rang als GRAUWERT plus Legende, nicht als Farbe: ein
   Schwarz-Weiss-Drucker macht Orange und Gruen zum selben Grau. */
table.t-bogen-tab tr.lead td {
  background: #ececea;
}
table.t-bogen-tab tr.qual td {
  background: #f6f6f5;
}
table.t-bogen-tab td.pl {
  color: #111;
}
table.t-bogen-tab tr.lead td.pl,
table.t-bogen-tab tr.qual td.pl {
  font-weight: 700;
}
/* Das Eintragefeld: der Grund, warum ein Bogen kein Bildschirmfoto ist. */
table.t-bogen-tab td.offen {
  color: #999;
  letter-spacing: 0.06em;
}
/* Schreiblinie fuer unbesetzte Teams: der Bogen wird VOR dem Turnier
   gedruckt und mit dem Kuli gefuellt. Die Linie traegt die Handschrift,
   der Platzhaltertext darunter sagt klein, WER auf die Linie gehoert
   ("Sieger HF1") — Formular-Konvention, damit die Handschrift den
   Hinweis nicht ueberschreibt. */
.t-bogen-schreib {
  display: inline-flex;
  flex-direction: column;
  vertical-align: bottom;
}
.t-bogen-schreib .lin {
  min-width: 110px;
  height: 14px;
  border-bottom: 1px solid #111;
}
.t-bogen-schreib .ph {
  font-family: var(--f-mono);
  font-size: 6.5px;
  font-style: normal;
  font-weight: 400;
  letter-spacing: 0.05em;
  color: #999;
  padding-top: 1px;
}
.t-bogen-legende {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  font-family: var(--f-mono);
  font-size: 8px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #666;
}
.t-bogen-legende b {
  display: inline-block;
  width: 8px;
  height: 8px;
  border: 1px solid #111;
  margin-right: 5px;
  vertical-align: -1px;
}
.t-bogen-legende b.voll {
  background: #111;
}
.t-bogen-fuss {
  margin-top: auto;
  border-top: 1px solid #bbb;
  padding-top: 6px;
  display: flex;
  justify-content: space-between;
  gap: 10px;
  font-family: var(--f-mono);
  font-size: 8px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #666;
}
/* SVG-Klassen des Baum-Bogens */
.t-bogen-baum .rl {
  font-family: var(--f-mono);
  font-size: 9px;
  letter-spacing: 1.6px;
  fill: #666;
}
.t-bogen-baum .tn {
  font-family: var(--f-disp);
  font-size: 11px;
  font-weight: 600;
  fill: #111;
}
.t-bogen-baum .tp {
  font-family: var(--f-disp);
  font-size: 11px;
  font-weight: 500;
  fill: #999;
}
.t-bogen-baum .sc {
  font-family: var(--f-mono);
  font-size: 10px;
  font-weight: 600;
  fill: #111;
}
.t-bogen-baum .bx {
  fill: none;
  stroke: #111;
  stroke-width: 1;
}
.t-bogen-baum .bxo {
  fill: none;
  stroke: #bbb;
  stroke-width: 1;
  stroke-dasharray: 4 3;
}
.t-bogen-baum .cn {
  fill: none;
  stroke: #aaa;
  stroke-width: 1;
}
.t-bogen-baum .flare {
  stroke: #bf4413;
  stroke-width: 1.4;
}
.t-bogen-baum .flare-t {
  fill: #bf4413;
}
/* Schreiblinie im Baum (Team- und Score-Eintrag) + kleiner
   Platzhalterhinweis darunter — Gegenstueck zu .t-bogen-schreib. */
.t-bogen-baum .wl {
  stroke: #111;
  stroke-width: 0.8;
}
.t-bogen-baum .ph {
  font-family: var(--f-mono);
  font-size: 6.5px;
  letter-spacing: 0.04em;
  fill: #999;
}

/* ---- Was beim Drucken passiert ----
   Die Boegen sind das EINZIGE, was aufs Papier kommt: die App wird
   ausgeblendet, nicht graustufig mitgedruckt. Das ist der Unterschied
   zum bisherigen Drucklayout, das den Bildschirm entfaerbt hat —
   achtzehn Karten fuellten so drei Seiten, wo eine Tabelle eine
   braucht.

   Dass die Boegen wirklich VOR dem Bildschirm-Drucklayout gewinnen,
   regelt der WER-GEWINNT-Block am Ende dieses @media-print-Blocks —
   die Rangfolge steht dort EINMAL, mit Spezifitaets-Rechnung und
   Rueckfall-Begruendung. Am 30.08. haben zwei Sessions dieselben
   Regeln unabhaengig gebaut (Merge 5b28884); der zweite Satz ist
   wieder raus, bevor er zur naechsten Doppelstruktur wird.

   Jeder Bogen beginnt eine neue Seite. Innerhalb eines Bogens duerfen
   Tabellenzeilen und Gruppenbloecke nicht auseinandergerissen werden;
   eine Gruppentabelle, die ueber den Seitenrand faellt, ist auf Papier
   unbrauchbar. */
@media print {
  /* Der K.-o.-Baum ist das einzige Blatt im Querformat: eine Klammer
     mit 3+ Runden braucht die Breite, nicht die Hoehe. Benannte Seite
     statt globalem size-Wechsel — die anderen Boegen bleiben hoch.
     Engines ohne benannte Seiten (aeltere Safari) drucken den Baum
     hochkant kleiner skaliert: lesbar, nur nicht optimal. */
  @page bogen-quer {
    size: A4 landscape;
    margin: 12mm;
  }
  .t-bogen--quer {
    page: bogen-quer;
  }

  .t-druck {
    display: block;
    gap: 0;
  }
  .t-bogen {
    box-shadow: none;
    border-radius: 0;
    max-width: none;
    min-height: 0;
    width: 100%;
    padding: 0;
    margin: 0;
    break-after: page;
    page-break-after: always;
  }
  .t-bogen:last-child {
    break-after: auto;
    page-break-after: auto;
  }
  .t-bogen-gruppe,
  .t-bogen-tab tr {
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .t-bogen-baum svg {
    max-height: 96vh;
  }

  /* ---- Masse fuers Papier, nicht fuer den Schirm -----------------
     Die px-Werte der Vorschau sind auf 760px Kartenbreite gerechnet;
     auf 182mm Satzspiegel (A4 minus 14mm Raender) kommen sie als
     ~7.5pt an — Aushang-Schrift, die man mit dem Finger verfolgen
     muss. Druck rechnet in pt/mm: Tabellen 9pt, Teamnamen 10pt,
     Titel 20pt, Logo 19mm (die Grenze, unter der ein Buerolaser
     feine Wappenstriche zusammenfallen laesst — siehe druckKopf). */
  .t-bogen-titel {
    font-size: 20pt;
  }
  .t-bogen-logo {
    width: 19mm;
    height: 19mm;
  }
  .t-bogen-kopf {
    padding-bottom: 3mm;
    gap: 6mm;
  }
  .t-bogen-koerper {
    gap: 3.5mm;
  }
  .t-bogen-raster {
    gap: 4mm 8mm;
  }
  .t-bogen-marke,
  .t-bogen-meta,
  .t-bogen-abschnitt {
    font-size: 7.5pt;
  }
  table.t-bogen-tab {
    font-size: 9pt;
  }
  table.t-bogen-tab .nm {
    font-size: 10pt;
  }
  table.t-bogen-tab th {
    font-size: 7pt;
    padding: 1.6mm 1.2mm;
  }
  table.t-bogen-tab td {
    padding: 1.8mm 1.2mm;
  }
  /* Schreiblinien in Papiermass: 5 mm Schreibraum je Linie im
     Spielplan (Kulischrift braucht ~6 mm inkl. Unterlaengenpuffer),
     Hinweistext knapp ueber 5pt — lesbar, aber eindeutig kein Eintrag. */
  .t-bogen-schreib .lin {
    min-width: 34mm;
    height: 5mm;
  }
  .t-bogen-schreib .ph {
    font-size: 5.5pt;
  }
  .t-bogen-legende,
  .t-bogen-fuss {
    font-size: 7pt;
  }
  .t-bogen-fuss {
    padding-top: 2.5mm;
    margin-top: 5mm;
  }
  /* Flaechen und das Logo-Grau sollen wirklich aufs Papier: ohne
     exact rechnet der Treiber die zarten Rangtoene oft zu Weiss. */
  .t-bogen {
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }

  /* ---- WER GEWINNT, WENN BEIDE DRUCKBLOECKE GELTEN ---------------
     (Fehlerbefund 2026-08-29, gemessen mit page.emulateMedia print.)

     Dieser Block hat den aelteren nie abgeloest, sondern stand daneben.
     Der aeltere schaltet `.t-view { display: none !important }` und
     holt spielplan/gruppen/baum wieder herein — der Drucken-Tab ist
     dort NICHT genannt. Die Boegen liegen aber genau darin
     (main.js:3679). Ergebnis am Geraet: `.t-bogen` war unter
     `media: print` NULLMAL sichtbar, gedruckt wurde der entfaerbte
     Bildschirm, und der Modulkopf fiel durch die Regel unten auch noch
     weg. Der Ausdruck hatte also weder Bogen noch Kopf — genau die
     Meldung "vom Handy falsch formatiert".

     Statt einen der beiden Bloecke zu loeschen, bekommen sie eine
     Rangfolge, denn beide Faelle sind echt:
       - Boegen im DOM  -> sie sind der Ausdruck (Entscheid 26.08.).
       - keine Boegen   -> der aeltere Block traegt weiter, sonst
                           druckt der Knopf ploetzlich leeres Papier.
     Der Schalter ist `:has(.t-druck .t-bogen)`. Faellt `:has()` aus,
     verfallen nur diese Regeln und es bleibt beim alten Verhalten —
     nicht bei einem leeren Blatt.

     Spezifitaet nachgerechnet, weil in diesem Repo schon vier bedingte
     Regeln still verloren gingen: der aeltere Block setzt
     `.t-view[data-view='spielplan']` = (0,2,0) mit !important. Hier
     steht `.t-mod:has(.t-druck .t-bogen) .t-view` = (0,3,0), ebenfalls
     !important. Hoehere Spezifitaet gewinnt, und die Datei ist ohnehin
     dieselbe und spaeter. */
  .t-mod:has(.t-druck .t-bogen) .t-view,
  .t-mod:has(.t-druck .t-bogen) .t-view[data-view='spielplan'],
  .t-mod:has(.t-druck .t-bogen) .t-view[data-view='gruppen'],
  .t-mod:has(.t-druck .t-bogen) .t-view[data-view='baum'] {
    display: none !important;
  }
  .t-mod:has(.t-druck .t-bogen) .t-view[data-view='drucken'] {
    display: block !important;
    break-before: auto;
  }
  /* Die Erklaerkarte mit dem Drucken-Knopf ist Bedienung, kein Inhalt.
     Der Knopf faellt schon durch die `.t-btn`-Zeile des aelteren
     Blocks, die beiden Absaetze darueber nicht — sie haetten sonst eine
     halbe Seite vor dem ersten Bogen gefuellt. */
  .t-druck-hinweis {
    display: none !important;
  }

  /* Der Bogen ersetzt die Ansicht: alles andere im Modul bleibt drin.
     NUR wenn es Boegen gibt — jeder Bogen traegt seinen EIGENEN Kopf
     mit Logo und Stand, der Modulkopf waere ein zweiter daruber. Im
     Rueckfall dagegen ist der Modulkopf die einzige Ueberschrift des
     Blattes; ihn unbedingt auszublenden war genau der Fehler, der bis
     zum 30.08. ein Blatt ohne Bogen UND ohne Kopf druckte. */
  .t-mod:has(.t-druck .t-bogen) > .t-mod-header,
  .t-mod > .t-mod-tabs,
  .t-mod .t-mod-nav,
  .t-mod .t-mod-aside,
  .t-mod .t-mod-more-sheet {
    display: none !important;
  }
}

/* ----------------------------------------------------------------
   GRUPPENTABELLEN UNTEREINANDER — Stand 2026-08-26, zweite Runde
   ----------------------------------------------------------------
   Der Segment-Umschalter von heute Vormittag ist wieder raus (Entscheid
   Jonas nach dem Browser-Blick: alle Gruppen untereinander, mit Abstand).
   Was bleibt, ist die Machart der Vorlage je Tabelle — Kopfzeile mit
   Spielstand, Baender links, Fusszeile mit der Aufstiegsregel.

   Der Abstand ist hier kein Zierrat: er uebernimmt die Aufgabe, die
   vorher der Umschalter hatte, naemlich zu zeigen, wo eine Gruppe endet
   und die naechste anfaengt. Deshalb --s5 und nicht --s2.
   ---------------------------------------------------------------- */
.t-standings-karte + .t-standings-karte,
.t-standings-karte + .grp-lbl,
.t-standings-karte + .t-thirds-card {
  margin-top: var(--s5);
}

/* ----------------------------------------------------------------
   TABELLE OHNE HUELLE — Artefakt Abschnitt 04
   ----------------------------------------------------------------
   „Die Tabelle verliert ihre Huelle und gewinnt Breite: das Rangband
   sitzt jetzt buendig an der Blattkante statt 14px eingerueckt in einer
   Karte."

   Das Markup bleibt, wie es ist (`.t-card.t-standings-karte >
   .t-card-body`). Der Kasten wird hier abgeraeumt, nicht dort
   herausoperiert — .t-card ist eine Klasse, die an Dutzenden Stellen im
   Modul haengt, und ein zweites Markup fuer dieselbe Tabelle waere genau
   die zweite Struktur, an der in diesem Repo schon vier Bedienelemente
   gestorben sind.

   Das seitliche Polster wandert von der Huelle an die AEUSSEREN ZELLEN.
   Nur so liegt die linke Kante der ersten Zelle auf der Blattkante — und
   genau dort setzt das Rangband mit `left: 0` an. Haette die Huelle
   weiter gepolstert, saesse das Band wieder im Nichts.
   ---------------------------------------------------------------- */
/* Und zwar fuer JEDE Karte in einer Ansicht, nicht nur fuer die zwei
   Tabellen. „Hier lagen vorher acht umrandete Listen uebereinander" —
   das galt dem Regelwerk und dem Baum genauso. Eine Regel statt acht
   Ausnahmen; wer eine Huelle behalten will, sagt es ausdruecklich
   (siehe .t-fill-ko-card weiter unten). */
.t-mod-main .t-card {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
}
.t-mod-main .t-card > .t-card-body {
  padding-left: 0;
  padding-right: 0;
}
.t-standings-karte > .t-card-body,
.t-thirds-card > .t-card-body {
  padding: 0;
}

/* Die eine Huelle, die bleibt — und zwar in der Sprache dieser Fassung:
   „K.-o.-Phase starten" ist der naechste Schritt und muss sich vom Rest
   abheben. Statt Rahmen plus Farbverlauf traegt der Block jetzt den
   3-px-Streifen, mit dem das Modul ueberall sonst Zustand zeigt. */
.t-mod-main .t-card.t-fill-ko-card {
  position: relative;
  margin-top: var(--s5);
  border: 0;
}
.t-mod-main .t-card.t-fill-ko-card::before {
  content: '';
  position: absolute;
  left: calc(-1 * var(--s5));
  top: 0;
  bottom: 0;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--flare);
}

/* Kopfzeile der Tabelle: 20px seitlich wie die Zellen, oben viel Luft,
   unten wenig — dieselbe Asymmetrie wie bei den Mono-Beschriftungen.
   Sie bindet die Ueberschrift an die Zeilen darunter; genau das leistete
   vorher der Rahmen. */
.t-standings-head {
  padding: var(--s5) var(--s5) 7px;
}
.t-standings-karte .t-standings-group-title,
.t-thirds-card .t-thirds-title {
  font-family: var(--f-disp);
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.018em;
  text-transform: none;
  color: var(--ink);
  margin: 0;
}

/* Aussenspalten tragen das Polster. Die Selektoren sind (0,0,2,0), damit
   sie mit den main.css-Regeln gleichziehen und ueber die spaetere
   Ladereihenfolge gewinnen. */
.t-standings-table th:first-child,
.t-standings-table td:first-child,
.t-thirds-table th:first-child,
.t-thirds-table td:first-child {
  padding-left: var(--s5);
}
.t-standings-table th:last-child,
.t-standings-table td:last-child,
.t-thirds-table th:last-child,
.t-thirds-table td:last-child {
  padding-right: var(--s5);
}

/* Trennung ZWISCHEN Zeilen statt UNTER jeder Zeile: die letzte Zeile
   bekommt sonst einen Strich ins Leere, und ohne Huelle sieht der aus
   wie ein abgeschnittener Rahmen. Auf dem Blatt trennt die weiche Linie.
   Die Kopfzeile behaelt ihre Linie darunter — sie trennt zwei
   verschiedene Dinge, nicht zwei gleiche. */
.t-standings-table td,
.t-thirds-table td {
  border-bottom: 0;
}
.t-standings-table tbody tr + tr td,
.t-thirds-table tbody tr + tr td {
  border-top: 1px solid var(--line-soft);
}
.t-standings-table thead th,
.t-thirds-table thead th {
  border-bottom: 1px solid var(--line-soft);
}

.t-standings-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
}
.t-standings-sub {
  flex: none;
  font-family: var(--f-mono);
  font-size: 11.5px;
  color: var(--muted);
}

/* Streifen in der Fusszeile: ein Segment je Gruppenspiel. */
.t-standings-prog {
  display: flex;
  gap: 3px;
  flex: none;
}
.t-standings-prog > i {
  display: block;
  width: 15px;
  height: 4px;
  border-radius: 2px;
  background: var(--line);
}
.t-standings-prog > i.is-done {
  background: var(--qual);
}

/* ----------------------------------------------------------------
   BESTE DRITTE — angeglichen an die Gruppentabelle (2026-08-29)
   ----------------------------------------------------------------
   User: „Beste dritte tabelle ist zu weit links, soll auf gleicher
   Höhe sein wie Gruppen und die Tabelle soll auch gleich aussehen und
   nicht mit dem Häkchen."

   GEMESSEN, nicht geraten (Prüfstand ?view=gruppen, Edge):
     vorher   „Gruppe A" x = 444 · „Beste Dritte" x = 424   (1280px)
              „Gruppe A" x =  34 · „Beste Dritte" x =  14   ( 375px)
   Die Tabellenzellen fluchteten längst (beide tragen
   `padding-left: var(--s5)` auf der ersten Spalte, Block „Tabelle ohne
   Huelle" weiter oben). Nur die Überschrift hing nackt im
   `.t-card-body` mit Polster 0, während der Gruppentitel in
   `.t-standings-head` sitzt. Der Renderer setzt diesen Kopf jetzt auch
   hier — deshalb steht in diesem Block KEINE eigene Einrückung: der
   Versatz ist am Markup behoben, nicht per Gegen-Polster überklebt.

   ARBEITSTEILUNG MIT main.css — der Grund, warum dieser Block so
   ausführlich ist: die Dritten-Tabelle bekam ihre Palette bis heute
   ausschliesslich aus main.css (Zeilen ~5665–5990), also aus der Zeit
   VOR der Markenübernahme. Sie trug deshalb hartcodierte Werte
   (#2e7d32 als Zeilenton, ' ✓' als Haken) und andere Masse als die
   Gruppentabelle, die hier laengst angeglichen wurde. Jede Regel unten
   zieht genau EINE dieser Abweichungen nach. tournament.css laedt
   spaeter und gewinnt bei gleicher Spezifitaet; wo main.css spezifischer
   ist, steht hier bewusst ein Selektor mehr — nachgezaehlt, nicht
   gehofft.
   ---------------------------------------------------------------- */

/* 1. MASSE. tournament.css hatte Zellpolster und Kopfzeilen-Schnitt nur
      fuer `.t-standings-table` nachgezogen; die Dritten-Tabelle blieb bei
      den main.css-Werten (6px 8px, th 11px). Zwei Tabellen untereinander
      mit verschieden hohen Zeilen sehen aus wie zwei Bauteile. */
.t-thirds-table th,
.t-thirds-table td {
  padding: 8px 6px;
}
.t-thirds-table th {
  font-size: 10px;
  font-weight: 600;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding-bottom: 6px;
}
.t-thirds-table .t-thirds-team {
  font-weight: 500;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.t-thirds-table .t-thirds-num.is-points {
  font-weight: 700;
  color: var(--ink);
}

/* 2. RANGSPALTE LINKS. main.css setzt `.t-thirds-table .t-thirds-rank`
      (0,0,2,0) auf `text-align: right` — fuer die Standings-Tabelle ist
      das hier oben schon mit einem Doppelselektor gekontert, die Dritten
      standen noch aussen vor. Die Rangziffer muss links stehen, sonst
      klebt sie an der Teamspalte und das Band unten sitzt neben einer
      Luecke. */
.t-thirds-table .t-thirds-rank {
  text-align: left;
  color: var(--ink-soft);
  font-size: 12px;
}

/* 3. QUALIFIKATION: BAND STATT HAKEN.
      Der Haken (`content: ' ✓'`, main.css:5941) faellt weg — er war das
      einzige Symbol dieser Art im ganzen Modul, und er sagte dasselbe wie
      das Band, das die Gruppentabellen fuehren. Die AUSSAGE bleibt
      vollstaendig: dieselbe getoente Flaeche (--qual-soft) und dasselbe
      3-px-Band (--qual) an der Blattkante wie bei
      `.t-standings-row.is-qualified`.

      Die Quelle in main.css ist am selben Tag ERSATZLOS entfernt worden
      (dort im Block „Qualifikations-Marker der GRUPPENTABELLE"). Diese
      Regel ist damit keine Ueberstimmung mehr, sondern eine Sperre: sie
      haelt den Haken auch dann draussen, wenn ihn jemand im anderen
      Stylesheet wieder einfuehrt. Sie ist (0,0,4,0) und gewinnt damit
      unabhaengig von der Ladereihenfolge; ein `content: none` auf
      gleicher Stufe haette nur so lange gehalten, wie die Reihenfolge
      der beiden Stylesheets stimmt — und genau daran ist in diesem
      Modul schon achtmal etwas still gestorben. */
.t-thirds-table .t-thirds-row.is-qualified .t-thirds-rank::after {
  content: none;
}
/* main.css:5983 setzt den Ton auf der ZEILE mit einem hartcodierten
   rgba(46,125,50,0.06). Der Ton gehoert auf die ZELLE (sonst deckt der
   Zellhintergrund ihn im Dunkelmodus ab, siehe Druck-Block weiter oben,
   der genau deshalb `td` adressiert) und er gehoert an das Token. */
.t-thirds-row.is-qualified {
  background: none;
}
.t-thirds-row.is-qualified td {
  background: var(--qual-soft);
}
.t-thirds-row.is-qualified .t-thirds-rank {
  position: relative;
  font-weight: 600;
  color: var(--ink);
}
.t-thirds-row.is-qualified .t-thirds-rank::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--qual);
}
/* 4. KEINE AUSGEGRAUTEN ZEILEN. main.css:5986 dimmt `.t-thirds-row.is-out`
      auf `opacity: .55`. In der Gruppentabelle stehen die nicht
      qualifizierten Zeilen normal da — die Unterscheidung traegt das Band,
      nicht der Kontrast. Das Dimmen war ausserdem die einzige Stelle im
      Modul, an der echte Werte schlechter lesbar gemacht werden. */
.t-thirds-row.is-out {
  opacity: 1;
}

/* 5. LEGENDE NEBEN DEM TITEL. Der Strich vor „Top N qualifizieren sich"
      ist derselbe wie in der Fusszeile der Gruppenkarte — nur gruen statt
      orange, weil er hier das gruene Band erklaert. `.t-foot-mark` bekommt
      seine Masse bisher nur unter `.t-standings-foot`; im Kopf gilt sie
      nicht, also hier noch einmal. */
/* Der Kopf traegt hier zwei Angaben statt Titel plus kurzer Zahl. Unter
   rund 370px Modulbreite passt „Top N qualifizieren sich" nicht mehr
   neben „Beste Dritte" — gemessen bei 360px Viewport brach dann der
   TITEL um (40px hoch statt der 20px einer Gruppenkarte), weil
   `.t-standings-sub` `flex: none` traegt und nicht nachgibt.
   Statt den Text zu kuerzen darf die Legende auf eine eigene Zeile
   rutschen: sie bleibt vollstaendig lesbar, und der Titel bleibt
   einzeilig wie in jeder Gruppenkarte daneben. */
.t-thirds-card .t-standings-head {
  flex-wrap: wrap;
}
.t-thirds-card .t-thirds-title {
  white-space: nowrap;
}
.t-standings-sub .t-foot-mark {
  display: inline-block;
  width: 10px;
  height: 3px;
  border-radius: var(--r-pill);
  background: var(--qual);
  margin-right: 7px;
  vertical-align: 3px;
}

/* 6. FUSSZEILE OHNE STREIFEN. Die Gruppenkarte fuellt rechts einen
      Fortschrittsstreifen; die Dritten-Wertung hat nichts Vergleichbares
      zu zeigen, hier steht nur die Wertungsregel links. `white-space:
      nowrap` aus der Basisregel muss weg — der Satz ist laenger als die
      Karte auf dem Handy und wuerde sonst per Ellipse abgeschnitten. */
.t-thirds-foot {
  white-space: normal;
}
.t-thirds-foot > span {
  overflow: visible;
  text-overflow: clip;
}

/* ----------------------------------------------------------------
   STATUSGRUPPEN DER TURNIERLISTE — Nacharbeit 2026-08-26, Beschwerde 5
   ----------------------------------------------------------------
   „unter 'bereit' bei der turnierauswahl ist noch ein kasten im
   hintergrund". Der Kasten stand in main.css und ist dort entfallen;
   hier steht sein Ersatz. Die Regeln muessen in DIESER Datei stehen,
   nicht in main.css: sie benutzen --f-mono und --muted, und die gibt es
   nur unter .t-mod / .t-brand. Die Listenseite traegt beide Klassen
   ueber t-list-host (siehe Block LISTEN-HOST) — genau deshalb, weil
   sie sonst ausserhalb des Token-Bereichs laege.
   ---------------------------------------------------------------- */
.t-list-group {
  display: flex;
  flex-direction: column;
  /* Kein Abstand mehr zwischen den Bloecken: seit sie keine Karten sind,
     trennt die Haarlinie. Der Abstand steckt in der Beschriftung
     darueber — 22px oben, 8px unten, dieselbe Asymmetrie wie ueberall. */
  gap: 0;
  width: 100%;
}
/* Beschriftung statt Ueberschrift: klein, Mono, gesperrt, Versalien —
   dieselbe Machart wie .grp-lbl in der Vorlage. Sie benennt die Gruppe,
   ohne sie einzurahmen. */
.t-list-group-label {
  font-family: var(--f-mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 22px var(--s5) 8px;
}
/* Die Liste steht auf dem Blatt und braucht selbst kein Polster mehr:
   Beschriftung und Bloecke bringen ihre 20px mit, und die Trennlinien
   sollen von Kante zu Kante laufen. Vorher war die Schale eine
   Flex-Spalte mit 16px Abstand zwischen den Gruppen. */
.t-list-host {
  gap: 0;
  padding: 0 0 var(--s5);
  overflow: hidden;
  /* Ein Blatt, kein Wandbehang. Ohne Kappung lief die Liste auf 1920px
     ueber 1800px Breite, und ein Turnierblock hatte 1760px fuer drei
     Woerter. Die Vorlage zeigt sie als Blatt auf einem Grund — dieselbe
     Rolle wie das Modul-Blatt daneben. */
  max-width: 1000px;
  margin: 0 auto;
}
/* Eine Spalte, immer. Die Bloecke trennt eine Haarlinie, und die gibt es
   nur untereinander — in einem mehrspaltigen Raster haette `+` je nach
   Umbruch mal getroffen und mal nicht. */
.t-list-host .tournament-instance-grid {
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
}

/* ----------------------------------------------------------------
   FILTER-REIHE, DIE PASST — vierte Fassung, 2026-08-26
   ----------------------------------------------------------------
   Jonas: "tatsaechlich gefaellt mir die filterfkt. ausm artefakt besser
   als das dropdown. aber dann muss alles ohne zur seite zu scrollen, auf
   eine seite passen."

   Das ist der Punkt, an dem die erste Chip-Fassung gescheitert ist —
   nicht an den Chips. Sie hatte bis zu neun Filter (Phasen plus einen je
   Gruppe) und deshalb overflow-x: auto; die Reihe wurde rechts
   abgeschnitten und halbierte einen Chip.

   Jetzt vier feste Filter, und die Loesung ist keine Scroll-Leiste,
   sondern Verzicht: die Reihe teilt die Breite gleichmaessig auf, es
   gibt kein overflow mehr. Was nicht passt, kann auch nicht wegscrollen.

   Fuenfte Fassung am selben Tag, Jonas: "die einzelnen Kaestchen sind zu
   schmal, es sieht nicht schoen aus."

   Gemessen war das kein Platzmangel, sondern ein nicht abgerufener Platz:
   .t-chips war `flex: 0 1 auto` und legte sich um die vier Woerter — 293px
   breit, waehrend die Leiste 612px hatte. Damit gab es nie einen
   Ueberschuss, den `flex: 1 1 auto` an den Kindern haette verteilen
   koennen; jeder Chip blieb auf Inhaltsbreite mit 6px Luft neben dem Wort.
   Eine Pille mit 6px Polster bei 16px Eckenradius liest sich gequetscht.

   Zwei Zeilen heben das: die Reihe waechst (`flex: 1 1 auto`), die Chips
   teilen zu gleichen Teilen (`flex: 1 1 0`). Gemessen 84px je Chip statt
   66 — rund 14px Polster neben dem Wort. Der Deckel bei 360px ist Absicht:
   ohne ihn stehen die vier Woerter auf 1280px 104px auseinander und lesen
   sich als Navigationsleiste, nicht als Filter (bei 440px gemessen und
   verworfen). Unter 360px Container gilt weiter Wortbreite statt gleicher
   Breite (siehe @container weiter unten); gemessen von 320px bis 1600px
   kuerzt kein Wort und laeuft keine Seite waagerecht ueber.
   ---------------------------------------------------------------- */
.t-chips {
  display: flex;
  gap: 8px;
  min-width: 0;
  flex: 1 1 auto;
  max-width: 360px;
}
.t-chips > .t-chip {
  /* 1 1 0 statt 1 1 auto: alle vier gleich breit, unabhaengig davon, ob
     das Wort ALLE oder FERTIG heisst — vier gleiche Felder lesen sich als
     eine Reihe, vier verschieden breite als vier Dinge. Das Polster hier
     ist nur die Untergrenze fuer schmale Container; die sichtbare Luft
     kommt aus der Aufteilung. */
  flex: 1 1 0;
  min-width: 0;
  padding: 7px 10px;
  display: inline-flex;
  align-items: baseline;
  justify-content: center;
  gap: 6px;
  overflow: hidden;
  white-space: nowrap;
}
/* Die Zahl darf schrumpfen, die Beschriftung nicht: "OFFEN" ohne Zahl
   ist noch ein Filter, eine Zahl ohne Wort ist nichts. */
.t-chips > .t-chip > .count {
  flex: none;
}
/* Der inaktive Chip bleibt reiner Text (Vorlage, Abschnitt 03) — aber
   sein Feld ist jetzt breit genug, dass man es treffen kann, ohne es zu
   sehen. Die Tuchfuehlung beim Ueberfahren zeigt, wie weit es reicht. */
.t-chips > .t-chip:not(.is-active):hover {
  background: var(--sunk);
}

/* ----------------------------------------------------------------
   DER RAHMEN, IN DEM DAS MODUL SITZT — Beschwerde 1
   ----------------------------------------------------------------
   „ich will nicht mehr das cremefarbene im turniermodul haben."

   Die hartnaeckigste Creme-Quelle stand nicht im Modul, sondern
   darunter: #content traegt 30px/28px Polster und zeigt darin den
   App-Grund (main.css: body { background: var(--bg) }, hell #f8f5f1).
   Das Modul lag also in einem cremefarbenen Passepartout — im Browser
   gemessen rgb(248,245,241), per grep in tournament.css unauffindbar,
   weil der Wert dort gar nicht vorkommt.

   tokens.css gibt #content die Marken-Palette, wenn das Modul sein
   direktes Kind ist; hier wird sie angewandt. Beides gehoert zusammen:
   ohne den Token-Traeger waere var(--ground) hier undefiniert, und eine
   undefinierte Custom Property loescht die ganze Deklaration STILL —
   dieselbe Falle wie beim Dialog-Host. Der Fehler saehe dann exakt so
   aus wie gar keine Regel.
   ---------------------------------------------------------------- */
/* Nachtrag 2026-08-26, dritte Fassung: der Listen-Zweig hat nie
   getroffen. Die Liste haengt zwei Ebenen tiefer —
     #content > #grid.tournaments-grid > section.t-list-host
   (main.js:2807) —, `> .t-list-host` verlangt aber ein direktes Kind.
   Ergebnis: die Turnieruebersicht stand weiter im cremefarbenen
   App-Grund, waehrend die Detailansicht laengst auf dem Papierton lag.
   tokens.css hatte dieselbe Lehre schon gezogen und dort das `>`
   gestrichen (Zeile 60); hier war es stehengeblieben — dieselbe Regel
   an zwei Stellen, eine davon repariert. Gemessen im Pruefstand, nicht
   erschlossen: rgb(248,245,241) hinter der Liste.
   Der Detail-Zweig behaelt sein `>` mit Grund: dort IST der Host das
   direkte Kind, und die engere Fassung haelt den Radius klein. */
#content:has(> .t-detail-host),
#content:has(.t-list-host),
#content:has(> .tournament-page-shell.t-mod) {
  background: var(--ground);
}

/* ----------------------------------------------------------------
   KOPF UND KNOEPFE KOMPAKTER — Nacharbeit 2026-08-26, zweite Runde
   ----------------------------------------------------------------
   Jonas: „spielplan kann da kleiner stehen, dieses feld nimmt zu viel
   platz ein" und „ergebnis eintragen ... auch kleiner und nicht so
   platzeinnehmend".

   ABWEICHUNG VON DER VORLAGE, ausdruecklich gewuenscht: das Artefakt
   setzt den Titel auf 27px. Auf einem 390px-Schirm frisst der Kopf damit
   zusammen mit Kicker und Polster rund ein Fuenftel der Hoehe, bevor der
   erste Spielstand zu sehen ist. Auf schmalen Schirmen deshalb 21px und
   engeres Polster. Ab 601px bleibt es bei den Artefakt-Werten — dort ist
   der Platz da, und der grosse Titel ist das, woran man die Ansicht im
   Vorbeigehen erkennt.
   ---------------------------------------------------------------- */
@container (max-width: 600px) {
  .t-mod-header {
    /* Oben mehr als die frueheren 4px: seit das Blatt eine 30px-Ecke hat
       und das Logo 44px misst, sass beides auf der Rundung.
       Seitlich bleibt es bei den 20px der Vorlage — Jonas' Beschwerde
       („nimmt zu viel platz ein") galt der HOEHE des Kopfes, nicht
       seiner Breite, und ein schmaleres Polster als in den Ansichten
       darunter setzt den Titel auf eine eigene Fluchtlinie. */
    padding: var(--s3) var(--s5) var(--s2);
  }
  /* `row-gap`, nicht `gap`: das Shorthand hier stand spaeter in der Datei
     als `.t-mod-header-inner--logo { column-gap: 13px }` und setzte den
     Spaltenabstand bei gleicher Spezifitaet mit auf Null — gemessen
     klebte der Kicker mit 0px am Logo. Gemeint war nur die Zeile. */
  .t-mod-header-inner {
    row-gap: 0;
  }
  .t-title {
    font-size: 21px;
    line-height: 1.15;
  }
  .t-mod-kicker {
    font-size: 10px;
    letter-spacing: 0.14em;
  }
}

/* Der kleine Knopf. Gleiche Optik wie .t-btn, nur weniger Anlauf:
   die Handlung bleibt erkennbar, ohne die halbe Zeile zu belegen. */
.t-btn--klein {
  padding: 6px 12px;
  font-size: 13px;
}

/* Icon-Knopf in der App-Kopfleiste („Aktualisieren"). Quadratisch, das
   Zeichen traegt die Bedeutung — die Beschriftung steht im aria-label
   und im Tooltip, nicht daneben.

   WARUM `button.` DAVOR: main.css:242 setzt
   `button.tournament-header-btn { width: auto }` — Spezifitaet (0,1,1)
   gegen (0,1,0) hier. Die Breite von 38px hat deshalb NIE gegolten; der
   Knopf war gemessen 20x38px, also ein hochkant stehender Kasten mit
   10px-Radius um ein 18px-Zeichen. Genau die Falle, die in diesem Repo
   schon mehrfach zugeschlagen hat: dieselbe Komponente in zwei Dateien.
   Mit `button.` stehen beide auf (0,1,1) und tournament.css gewinnt
   ueber die Quell-Reihenfolge.

   KEINE KAPSEL IN RUHE: kein Rahmen, kein Grund — nur das Zeichen.
   `.btn-ghost` bringt einen 1.5px-Rahmen mit, der hier ausdruecklich
   abgeraeumt wird. Die Flaeche erscheint erst beim Zeigen/Antippen.
   Die Trefferflaeche bleibt trotzdem 44x44 (WCAG 2.5.8 / Apple HIG),
   das Zeichen darin 18px. */
button.tournament-icon-btn {
  width: 44px;
  height: 44px;
  min-width: 44px;
  padding: 0;
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  border: 0;
  background: none;
  box-shadow: none;
  border-radius: 999px;
  color: var(--muted);
  /* `.btn-primary` (main.css) bringt `margin-top: 4px` mit. In einer
     mittig ausgerichteten Flex-Reihe sitzt „Neu" dadurch 2px tiefer als
     ein Nachbar ohne diesen Anlauf — gemessen. Derselbe Wert hier legt
     beide Knoepfe auf dieselbe Mittelachse. */
  margin-top: 4px;
}
/* Dezente Hervorhebung statt Dauer-Kapsel. `--border` traegt in beiden
   Themen (main.css :root und [data-theme='dark']), es braucht also
   keinen eigenen Dunkel-Block. */
button.tournament-icon-btn:hover {
  background: var(--border);
  border-color: transparent;
  color: var(--text2);
}
button.tournament-icon-btn:active {
  background: var(--border2);
}
/* Tastaturbedienung: ohne Rahmen waere der Knopf im Fokus sonst gar
   nicht auffindbar. `:focus-visible`, damit der Ring beim Antippen
   nicht stehen bleibt. */
button.tournament-icon-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  background: var(--border);
  color: var(--text2);
}
.tournament-icon-btn svg {
  width: 18px;
  height: 18px;
  stroke: currentColor;
  fill: none;
}

/* ----------------------------------------------------------------
   META-ZEILE AUF KLEINEN HANDYS — Fund Jonas, iPhone SE, 2026-08-26
   ----------------------------------------------------------------
   „passt platte 1 zB nicht ganz in die zeile und ist dann verschoben
   weil die 1 dann nicht unter platte steht sondern links."

   Zwei Ursachen, zwei Massnahmen:

   1. Der Umbruch MITTEN im Begriff. Behoben im Renderer mit einem
      geschuetzten Leerzeichen („Platte 1"), siehe resolveFieldName.
      Das ist die eigentliche Reparatur — sie wirkt in jeder Breite.

   2. Der Kampf um die Zeile. `.t-match-meta` ist eine Flex-Reihe mit
      space-between: links Ort, rechts Zustand. Unter ~380px bleibt fuer
      beides zusammen zu wenig Platz, und der linke Teil bricht um. Statt
      ihn abzuschneiden (dann fehlt die Platte, und die braucht man am
      Tisch) stapeln wir: Ort oben, Zustand darunter, beide linksbuendig.
      Untereinander ist kein Notbehelf — es ist dieselbe Information in
      der Reihenfolge, in der man sie liest.

   380px ist gemessen, nicht gegriffen: das iPhone SE hat 375px Viewport,
   der Container ist nach Polster etwa 347px breit.

   NACHTRAG 2026-08-26, dritte Fassung — Massnahme 2 ist zurueckgenommen.
   Sie stammt aus der Zeit, als rechts im Band ein WORT stand
   ("Bestaetigt", "20 Min"). Untereinander gestapelt las sich das noch
   als zwei Angaben. Seit die harte Fassung dort die AKTION traegt, steht
   nach dem Umbruch ein einzelner Knopf allein in einer eigenen Zeile —
   im Screenshot bei 390px sah das aus wie ein Fehler, nicht wie ein
   Fussband. Die Vorlage (`.mhard .cap`) kennt genau zwei Haelften auf
   EINER Zeile.
   Statt zu stapeln darf jetzt der linke Teil UMBRECHEN: der Ort behaelt
   beide Angaben, die Aktion bleibt rechts und buendig oben. Massnahme 1
   (das geschuetzte Leerzeichen) traegt weiterhin die eigentliche
   Reparatur — sie verhindert den Umbruch mitten im Begriff.
   ---------------------------------------------------------------- */
@container (max-width: 380px) {
  .t-match-meta {
    align-items: flex-start;
    gap: var(--s2);
  }
  .t-match-meta > * {
    min-width: 0;
  }
}

/* ================================================================
   EINSTELLUNGEN — Listenzeilen statt Formularfeldern
   ----------------------------------------------------------------
   Jonas, 2026-08-26: „die ganzen einstellungen ... das muss hochwertiger,
   besser zu bedienen aussehen ... sieht auch noch aus wie ein relikt aus
   alter zeit." Und: „es soll mobil wunderschön aussehen. alles top
   bedienbar sein für den turnierleiter."

   Vorher lagen hier rohe <input type="number">, drei verschieden breite
   Knoepfe und zwei Hinweiskaesten in EINEM Flex-Container. Jede Zeile
   hatte ihre eigene Hoehe, ihre eigene Kante, ihre eigene Logik.

   Die Vorlage (Abschnitt 06) macht es anders und immer gleich:
     Beschriftung links · Wert rechts in Mono · Erklaerung klein darunter
   Drei Ansichten folgen derselben Regel — Teams, Regelwerk,
   Einstellungen. Genau das ist hier nachgebaut.

   Warum Listenzeilen und keine Formularfelder: ein Turnierleiter bedient
   das mit dem Daumen, im Stehen, an der Platte. Eine Zeile mit grosser
   Trefferflaeche schlaegt ein Eingabefeld, das erst die Tastatur holt.
   Getippt werden kann trotzdem — der Stepper hat ein echtes Feld in der
   Mitte, nur muss man es nicht.
   ================================================================ */

/* Die Gruppenbeschriftung ist mit den einklappbaren Abschnitten
   entfallen — sie ist dort die Kopfzeile (.t-settings-section-title).
   Totes CSS sieht aus wie eine Moeglichkeit. */

/* ---- Die Liste ---- */
.t-lst {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-s);
  overflow: hidden;
}
.t-lst--danger {
  border-color: var(--danger);
}

/* ---- Eine Zeile ---- */
.t-lrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: 13px var(--s4);
  min-height: 52px; /* Daumenflaeche, nicht Zierrat */
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
}
.t-lrow + .t-lrow {
  border-top: 1px solid var(--line-soft);
}
button.t-lrow {
  cursor: pointer;
}
button.t-lrow:hover {
  background: var(--raised);
}
button.t-lrow:disabled {
  cursor: default;
  opacity: 0.45;
}
button.t-lrow:disabled:hover {
  background: none;
}

.t-lrow-lb {
  min-width: 0;
  font-weight: 500;
  color: var(--ink);
}
/* Die Erklaerung steht UNTER der Beschriftung, klein und gedaempft —
   nicht als eigener Hinweiskasten daneben. Ein Kasten pro Zeile war der
   Hauptgrund, warum der alte Block so unruhig wirkte. */
.t-lrow-lb small {
  display: block;
  margin-top: 1px;
  font-size: 12.5px;
  font-weight: 400;
  color: var(--muted);
}
.t-lrow-vl {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-family: var(--f-mono);
  font-size: 13px;
  color: var(--ink-2);
}
.t-chev {
  flex: none;
  color: var(--muted);
  font-size: 17px;
  line-height: 1;
}

/* Handlungszeile: die Beschriftung IST die Handlung, deshalb traegt sie
   die Farbe. Kein zusaetzlicher Knopf in der Zeile — das waere ein
   Bedienelement in einem Bedienelement. */
.t-lrow--action .t-lrow-lb {
  color: var(--flare);
  font-weight: 600;
}
.t-lrow--danger .t-lrow-lb {
  color: var(--danger);
  font-weight: 600;
}
.t-lrow--action:disabled .t-lrow-lb,
.t-lrow--danger:disabled .t-lrow-lb {
  color: var(--muted);
}

/* ---- Stepper: −  Wert  + ---- */
.t-step {
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--sunk);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 3px;
  flex: none;
}
.t-step-btn {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--ink-2);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  font-family: var(--f-body);
}
.t-step-btn:hover {
  background: var(--panel);
  color: var(--ink);
}
.t-step-btn:disabled {
  opacity: 0.35;
  cursor: default;
}
.t-step-btn:disabled:hover {
  background: none;
  color: var(--ink-2);
}
/* Das Feld in der Mitte ist ein echtes Eingabefeld: steppen ist der
   schnelle Weg, tippen der genaue. Es sieht aber nicht wie ein Formular
   aus, sondern wie der Wert, der es ist. */
.t-step-wert {
  width: 68px;
  min-width: 0;
  border: 0;
  background: none;
  text-align: center;
  font-family: var(--f-mono);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  padding: 0;
  -moz-appearance: textfield;
}
.t-step-wert::-webkit-outer-spin-button,
.t-step-wert::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.t-step-wert:focus-visible {
  outline: 2px solid var(--flare);
  outline-offset: 2px;
  border-radius: 4px;
}
/* --ink-2 statt --muted: der Schrittwaehler sitzt auf --sunk, und dort
   traegt --muted gemessen nur 4,38 : 1 (12px, also gilt 4,5). Auf dem
   Blatt waere derselbe Ton mit 5,08 : 1 in Ordnung — die vertiefte
   Flaeche ist der Unterschied. */
.t-step-einheit {
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--ink-2);
  padding-right: 6px;
}

/* Abstand zwischen den Gruppen — dieselbe Rolle wie zwischen den
   Gruppentabellen: sie zeigt, wo ein Thema endet. */
.t-lst + .t-grp-lbl {
  margin-top: var(--s5);
}
.t-settings-grid > .t-hint {
  margin-top: calc(var(--s1) * -1);
}

/* ================================================================
   ABSCHNITT 06 — DIE LISTEN VERLIEREN IHRE HUELLEN
   ----------------------------------------------------------------
   „Hier lagen vorher acht umrandete Listen uebereinander. Es sind
   dieselben Zeilen, nur ohne Huellen: die Mono-Zeile darueber gruppiert,
   die Haarlinie trennt."

   Was BLEIBT, ist das Einklappen. Die Vorlage zeichnet vier flache
   Abschnitte; im echten Turnier sind es neun, und zugeklappte Bereiche
   sind dort kein Schmuck, sondern der Grund, warum der Tab bedienbar
   ist. Die FORM kommt aus der Vorlage, die FUNKTION bleibt.

   Aus der Kopfzeile wird damit genau das, was die Vorlage `.glbl` nennt:
   eine Mono-Beschriftung, 10,5px, 0,17em gesperrt, in --muted, mit viel
   Luft darueber und wenig darunter. Diese Asymmetrie bindet die
   Ueberschrift an die Zeilen unter ihr — genau das leistete vorher der
   Rahmen. Das Dreieck rechts bleibt: es ist das einzige, was eine
   zugeklappte von einer flachen Beschriftung unterscheidet.
   ================================================================ */
.t-settings-section,
.t-lst {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
}
/* Die KOPFZEILE steht bewusst NICHT hier, sondern weiter unten im Block
   „Einstellungen zugeklappt". Dort steht die vierte Regel derselben
   Klasse in dieser Datei, und sie ist die letzte — bei gleicher
   Spezifitaet gewinnt sie. Eine fuenfte hier oben haette gar nichts
   getan: gemessen kam padding-left mit 16px an, obwohl an dieser Stelle
   `padding: 22px 0 8px` stand. Wer diese Klasse anfasst, fasst sie
   DORT an. */
.t-settings-section:first-child .t-settings-section-header {
  border-top: 0;
}
.t-settings-section-header:focus-visible {
  background: none;
  outline-offset: -2px;
}
.t-settings-section-header .t-settings-section-title,
.t-settings-section-title {
  font-family: var(--f-mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--muted);
}
.t-settings-section-toggle {
  color: var(--line);
}
/* Der Koerper braucht kein eigenes Polster mehr: die Zeilen bringen
   ihres mit, und seitlich polstert die Ansicht. */
.t-settings-section-body {
  padding: 0;
  gap: var(--s3);
}
/* Der Abstand zwischen den Abschnitten steckt jetzt IN der Beschriftung
   (22px darueber, 8px darunter) — genau da, wo die Vorlage ihn hat. Ein
   zusaetzlicher Flex-Abstand von 20px kam obendrauf und riss die Liste
   auseinander: zwischen zwei zugeklappten Zeilen standen gemessen 50px
   Leere. Die Asymmetrie ist der Punkt, nicht die Summe. */
.t-settings-grid {
  gap: 0;
}

/* Die Zeile selbst holt sich die volle Blattbreite und gibt das Polster
   innen zurueck — dasselbe Muster wie bei den Tabellen. Damit laufen
   Trennlinie und Trefferflaeche von Kante zu Kante, waehrend der Text
   auf der Fluchtlinie der Ansicht bleibt. Am Spieltisch mit dem Daumen
   ist das der Unterschied zwischen „getroffen" und „danebengetippt". */
.t-lrow,
.t-team-row {
  margin-inline: calc(-1 * var(--s5));
  padding-left: var(--s5);
  padding-right: var(--s5);
  /* Die Breite ist hier der Punkt, nicht Beiwerk. `width: 100%` bezieht
     sich auf den ELTERNBLOCK und weiss nichts vom eigenen Rand: mit
     -20px links UND rechts wanderte die Zeile nur nach links und blieb
     40px zu kurz — gemessen bei 390px von 14 bis 336 statt bis 376. Der
     Wert rechts (Schrittwaehler, Haken) stand damit 40px vor dem
     Blattrand und die Trennlinien hoerten im Nichts auf.
     `width: auto` half auch nicht: .t-lrow ist ein <button> in einem
     Block, und ein Knopf ohne Breitenangabe schrumpft auf seinen Inhalt
     (gemessen 340,266px). Die Zeile muss also ausdruecklich sagen, wie
     breit sie ist: so breit wie der Elternblock PLUS die zwei Rinnen,
     aus denen sie ausbricht. Ein Wert, zwei Vorzeichen — wie ueberall
     sonst in dieser Fassung. */
  width: calc(100% + var(--s5) * 2);
}
/* Die Gefahrenzone: Karminstreifen statt Karminrahmen. Der rote Rahmen
   war der lauteste Kasten im ganzen Entwurf — und stand ganz unten, wo
   ihn niemand aus Versehen trifft. Jetzt tragen die Zeilen selbst den
   Streifen und die karminrote Schrift.
   Der Streifen sitzt am Blattrand, also muss die Zeile aus dem Polster
   der Ansicht ausbrechen und es innen zurueckgeben — dasselbe Muster
   wie bei den Tabellen. */
.t-lst--danger,
.t-settings-section.t-danger-zone {
  /* `border: 0`, nicht `border-color: transparent`: .t-danger-zone setzt
     das Shorthand mit 2px, und eine durchsichtige 2px-Kante ruecken die
     Zeilen trotzdem ein. */
  border: 0;
  background: none;
  padding: 0;
}
.t-settings-section.t-danger-zone .t-settings-section-header {
  background: none;
}
.t-settings-section.t-danger-zone .t-settings-section-title {
  color: var(--danger);
}
.t-lrow--danger {
  position: relative;
}
.t-lrow--danger::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--danger);
}

/* Der Zustandsblock oben: aus der Statuskarte wird ein Streifenblock.
   Sie hatte Rahmen, Schatten UND einen Teal-Balken links — der Balken
   allein sagte schon alles. Er bleibt, der Rest faellt weg. Die drei
   Zahlen sind das Signal, nicht die Umrandung. */
.t-status-card {
  position: relative;
  margin-inline: calc(-1 * var(--s5));
  padding: 15px var(--s5) 17px;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.t-status-card::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--live);
}

/* Der Schrittwaehler ist eine gefuellte Pille ohne Rahmen — die Flaeche
   zeigt, dass man tippen darf, ohne dass ein Rahmen es behauptet. */
.t-step {
  border: 0;
  border-radius: var(--r-pill);
}
.t-step-btn {
  border-radius: var(--r-pill);
}

/* ----------------------------------------------------------------
   FILTER ALS AUSWAHLFELD — dritte Fassung, Entscheid Jonas 2026-08-26
   ----------------------------------------------------------------
   „ich wollte wieder dass ich filtern kann nicht so nebeneinander."
   ABWEICHUNG VON DER VORLAGE, die eine Chip-Reihe zeigt.

   Zwei Dinge sind dabei mitgegangen, die vorher gestoert haben:
   der Behaelter drumherum (die Reihe sass in einem weissen Kasten mit
   harter rechter Kante, die einen Chip halbierte) und die Frage, wo die
   Reihe endet. Ein Feld hat einen Anfang und ein Ende.

   Die Anzahl bleibt sichtbar — hinter jeder Option und rechts am Feld.
   Ohne sie waere der Wechsel ein Verlust gewesen: „Offen 7" sagt ohne
   Tippen, wie weit der Tag ist.
   ---------------------------------------------------------------- */
.t-filter {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
}
.t-filter-feld {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-width: 0;
  padding: 9px var(--s3);
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel);
  cursor: pointer;
}
.t-filter-marke {
  font-family: var(--f-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  flex: none;
}
/* Das Feld selbst ist unsichtbar gemacht, aber es IST ein echtes
   <select>: auf dem Handy oeffnet das die System-Auswahl, die man mit
   dem Daumen bedient und nicht lernen muss. Ein nachgebautes Menue
   muesste Tastatur, Fokus und Schliessen-bei-Klick-daneben nachbauen. */
.t-filter-select {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  background: none;
  padding: 0 var(--s2) 0 0;
  min-width: 0;
  font-family: var(--f-cond);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink);
  cursor: pointer;
}
.t-filter-select:focus-visible {
  outline: 2px solid var(--flare);
  outline-offset: 4px;
  border-radius: 4px;
}
.t-filter-pfeil {
  flex: none;
  color: var(--muted);
  font-size: 12px;
  pointer-events: none;
}
.t-filter-stand {
  flex: none;
  font-family: var(--f-mono);
  font-size: 11.5px;
  color: var(--muted);
}

/* Die Werkzeugleiste traegt keinen eigenen Grund mehr. Sie war ein
   weisser Kasten um einen Filter — ein Behaelter um ein Bedienelement,
   das seine Kante schon selbst hat. */
.t-toolbar {
  background: none;
  border: 0;
  padding: 0;
  box-shadow: none;
}

/* ----------------------------------------------------------------
   ZWEI BRAUNE SCHATTEN, DIE KEIN TOKEN ERREICHT
   ----------------------------------------------------------------
   main.css:226 und main.css:783 setzen
       box-shadow: 0 2px 8px rgba(138, 106, 74, .22)
   also das alte Beige HART, ohne Token. Sie ueberleben jede
   Palettenumstellung — nach dem Wechsel auf Orange blieb um den Knopf
   ein brauner Hof stehen. Genau der Fall, den kein grep in
   tournament.css findet: der Wert steht dort gar nicht.

   Nur im Modulbereich ueberschrieben, nicht in main.css geaendert:
   der Rest der App darf beige bleiben (Entscheid "nur das Modul").
   ---------------------------------------------------------------- */
#content:has(.t-list-host) .btn-primary,
#content:has(.t-list-host) .tournament-new-instance-btn,
#content:has(> .t-detail-host) .btn-primary {
  box-shadow: var(--shadow-s);
}

/* Die Listenkarte bekommt ihre Tiefe zurueck. --shadow-s war definiert
   und ungenutzt; auf cremefarbenem Grund waere er ohnehin unsichtbar
   gewesen, auf #E4E4E2 traegt er. */
.t-list-card {
  box-shadow: var(--shadow-s);
}

/* Der Logo-Platzhalter war ein schwarzer Klotz von 56px — die Regel
   stammt aus der Zeit, als --ink ein weiches Braun war. Mit der neuen
   Palette wurde daraus reines Schwarz, und bei 56px frass er ein
   Fuenftel der Kartenbreite. Er ist ein PLATZHALTER; er soll den Platz
   des Logos markieren, nicht ihn beanspruchen. */
.t-list-card-logo {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: var(--sunk);
  border: 1px solid var(--line);
  color: var(--muted);
  font-size: 15px;
}
/* ----------------------------------------------------------------
   EINSTELLUNGEN EINGEKLAPPT — Entscheid Jonas 2026-08-26
   ----------------------------------------------------------------
   "es soll eingeklappt sein die ganzen einstellungen sodass es
   uebersichtlicher ist."

   Zu heisst nicht versteckt: alle Kopfzeilen stehen da, mit Namen. Der
   Tab ist damit ein Inhaltsverzeichnis statt einer Liste, durch die man
   scrollt.

   HIER STAND ZUERST EIN ZWEITER SATZ KLASSEN (.t-grp, .t-grp-kopf,
   .t-grp-koerper) neben dem vorhandenen .t-settings-section — und
   damit die achte Kollision derselben Art in dieser Datei: zwei Regeln
   fuer dieselbe Sache, von denen eine gewinnt. Sichtbar wurde es
   anders: der Umschalt-Handler kannte die neuen Klassen nicht, und vier
   Abschnitte hatten eine Kopfzeile, die auf Klick nichts tat.

   Jetzt gibt es nur .t-settings-section. Was hier steht, ergaenzt die
   vorhandene Optik um zwei Dinge: eine groessere Trefferflaeche fuer
   den Daumen und den Zustand des Pfeils.
   ---------------------------------------------------------------- */
/* KASKADENKOLLISION Nr. 11, gemessen 2026-08-26. Dies ist die VIERTE
   Regel fuer `.t-settings-section-header` in dieser Datei und die
   letzte — bei gleicher Spezifitaet gewinnt sie. Eine fuenfte, weiter
   oben angelegte Regel mit `padding: 22px 0 8px` hat deshalb nichts
   getan; im Browser kam weiter 16px links an, und die Beschriftungen
   standen 16px weiter innen als die Zeilen darunter.
   Die Werte der dritten Fassung stehen deshalb HIER: die Kopfzeile ist
   die Mono-Beschriftung der Vorlage (`.glbl`), also 22px Luft darueber
   und 8px darunter, seitlich nichts — die Ansicht polstert schon. */
.t-settings-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  width: 100%;
  min-height: 0;
  padding: 22px 0 8px;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
}
.t-settings-section-header:hover {
  background: none;
}
.t-settings-section-header:hover .t-settings-section-title {
  color: var(--ink-2);
}
.t-danger-zone .t-settings-section-title {
  color: var(--danger);
}
.t-settings-section-toggle {
  flex: none;
  color: var(--muted);
  font-size: 13px;
  line-height: 1;
  transition: transform 0.15s ease;
}
/* Der Rumpf sitzt in einer Karte, die schon einen Rand hat — die Liste
   darin braucht keinen zweiten. Sonst waere es wieder ein Kaestchen im
   Kaestchen, genau das, was Jonas beim Spielplan beanstandet hat.
   Die Trennlinie oben ist mit der dritten Fassung entfallen: seit die
   Kopfzeile eine Mono-Beschriftung ist, trennt der Abstand (22 oben, 8
   unten). Eine Linie DAZU haette dasselbe zweimal gesagt — und sie lief
   nur ueber die Listenbreite, nicht ueber das Blatt, sass also weder
   buendig noch mittig. */
.t-settings-section-body > .t-lst {
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.t-settings-section-body > .t-hint {
  padding: var(--s2) 0;
}

/* ----------------------------------------------------------------
   SPIELPLAN-LEISTE — Filter und Aktion in EINER Zeile
   ----------------------------------------------------------------
   Jonas, dritte Runde: „der filter ist erneut ein kaestchen was zu nah
   am spielplan ist, ergebnis eintragen sieht da leider auch etwas fehl
   am platz aus."

   Beides war dasselbe Problem: zwei Zeilen uebereinander, in denen je
   ein einzelnes Element schwebte. Zwei halbe Zeilen sehen aus wie zwei
   Fehler. Jetzt eine Leiste — Filter links, Aktion rechts.

   Und Luft nach unten: der Filter klebte am ersten Spiel. `--s5` ist
   derselbe Abstand, der auch zwei Gruppentabellen trennt; er sagt hier
   dasselbe, naemlich „anderer Gegenstand".
   ---------------------------------------------------------------- */
.t-spielplan-leiste {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
  margin-bottom: var(--s5);
}
/* GEMESSEN, nicht geschaetzt (Edge, 390px): mit dem Knopf in derselben
   Zeile bleiben je Chip 72px — und "Fertig 13" braucht 78px. Alle vier
   Chips kuerzten still, bei 320px sogar mit Auslassungspunkten. Die
   Bedingung "passt auf eine Seite" war damit erfuellt und der Inhalt
   trotzdem unlesbar; das ist kein Erfolg.

   Also bekommt die Chip-Reihe die VOLLE Zeile. Der Knopf rutscht
   darunter und rueckt nach rechts — nah an den Filter, weit weg vom
   ersten Spiel. Er schwebt damit nicht mehr allein ueber einem einzelnen
   Kaestchen, sondern schliesst die Bedienleiste ab.

   Ab 520px Container passen beide nebeneinander; dort ist Platz genug,
   dass die Chips ihre Beschriftung behalten. */
.t-spielplan-leiste {
  flex-wrap: wrap;
  row-gap: var(--s2);
}
.t-spielplan-leiste > .t-toolbar {
  flex: 1 1 100%;
  min-width: 0;
}
.t-spielplan-leiste > .t-btn {
  flex: none;
  margin-left: auto;
}
@container (min-width: 520px) {
  .t-spielplan-leiste > .t-toolbar {
    flex: 1 1 auto;
  }
}

/* Hier stand eine Regel, die den Knopf unter 380px auf VOLLE Breite
   zog. Sie stammt aus dem Layout von vor zehn Minuten, in dem Filter und
   Knopf sich eine Zeile teilten — dort war der volle Knopf richtig.
   Seit die Chips die ganze Zeile bekommen, ist er es nicht mehr: ein
   randvoller oranger Balken ueber dem ersten Spiel ist lauter als das
   Spiel. Gemessen im Pruefstand gesehen, nicht ueberlegt. */

/* Der Spielplan besteht aus Zeitmarken und Spielkarten — mehr nicht.
   Die aeussere Karte ist am 26.08. entfallen (Jonas: „nicht wie bislang
   kaestchen in einem grossen kasten"). Der Abstand zwischen den Karten
   uebernimmt, was vorher der Kasten geleistet hat. */
#t-schedule-list {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}

/* Unter ~360px Container reicht die Breite fuer vier Chips in 12.5px
   nicht mehr — gemessen kuerzten dort alle vier. Kleinere Schrift statt
   Auslassungspunkte: ein Chip, dessen Wort man nicht lesen kann, ist
   kein Filter mehr. 11.5px ist die Untergrenze, bei der Archivo Narrow
   in Versalien noch sauber steht. */
@container (max-width: 360px) {
  .t-chips {
    gap: 4px;
  }
  .t-chips > .t-chip {
    /* Zurueck auf natuerliche Breiten: gleich breite Felder sind Luxus
       und brauchen Luft. Auf 320px Geraetebreite kuerzten mit `1 1 0`
       gemessen OFFEN und FERTIG — ein Chip, dessen Wort man nicht lesen
       kann, ist kein Filter mehr. Die Reihe waechst hier trotzdem und
       verteilt den frueher ungenutzten Platz (auf 375px 57px) als
       Polster, nur eben nach Wortlaenge statt zu gleichen Teilen. */
    flex: 1 1 auto;
    padding: 7px 4px;
    font-size: 11.5px;
    letter-spacing: 0.03em;
  }
  .t-chips > .t-chip > .count {
    font-size: 10.5px;
  }
}

/* ================================================================
   Zuschauer-Link: eigener Name (28.08.2026)
   ----------------------------------------------------------------
   Neuer Block, ans Ende gehaengt und in sich geschlossen. Er aendert
   KEINE bestehende Regel und schreibt in keinen vorhandenen
   Dark-Block: die Farben kommen ausschliesslich aus Tokens, die in
   beiden Themen definiert sind (--ink, --muted, --line, --sunk,
   --danger, --warn, --warn-soft) — damit braucht der Block keine
   zweite Fassung fuer dunkel und kann keine fremde ueberschreiben.

   Der Praefix und das Feld sitzen in EINER Zeile, weil die Adresse
   erst zusammen eine Adresse ist. Auf schmalen Geraeten bricht die
   Zeile um, statt das Eingabefeld auf Daumenbreite zu quetschen.
   ================================================================ */
.t-pslug {
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
}
.t-pslug-titel {
  display: block;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--s2);
}
.t-pslug-zeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s1);
  margin-bottom: var(--s2);
}
/* Der unveraenderliche Teil der Adresse. Monospace, damit er sich vom
   editierbaren Teil abhebt, ohne dass ein Rahmen noetig waere. */
.t-pslug-praefix {
  font-family: var(--f-mono);
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.t-pslug-feld {
  flex: 1 1 12ch;
  min-width: 0;
  font-family: var(--f-mono);
}
.t-pslug-vorschau {
  margin: 0 0 var(--s2);
  font-size: 13px;
  color: var(--muted);
  overflow-wrap: anywhere;
}
.t-pslug-fehler {
  margin: 0 0 var(--s2);
  font-size: 13px;
  font-weight: 600;
  color: var(--danger);
  overflow-wrap: anywhere;
}
/* Die Warnung, dass der alte Link stirbt. Sie steht VOR dem Knopf und
   traegt als einzige Flaeche in diesem Block einen Ton — sie ist die
   Aussage, die man nicht ueberlesen darf. */
.t-pslug-warnung {
  margin-bottom: var(--s3);
  padding: var(--s2) var(--s3);
  border-left: 3px solid var(--warn);
  border-radius: var(--r-btn);
  background: var(--warn-soft);
  color: var(--ink);
  font-style: normal;
  font-size: 13px;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
@media (max-width: 600px) {
  /* Auf 375px bleibt fuer das Feld sonst eine Handvoll Zeichen uebrig.
     Der Praefix bekommt dann die volle Zeile fuer sich. */
  .t-pslug-praefix {
    flex: 1 0 100%;
  }
  .t-pslug-feld {
    flex: 1 1 100%;
  }
}
