/* Pitwall, gemeinsames Aussehen. Dunkel, weil die Live-Ansicht auf einem
   Zweitmonitor neben dem laufenden Spiel steht. */

:root {
  --bg: #0d1014;
  --panel: #171c22;
  --panel-2: #1f262e;
  --line: #2c353f;
  --fg: #e8edf2;
  --dim: #8b97a4;
  --accent: #ffb400;
  --good: #46d17a;
  --bad: #ff5f56;
  --info: #4aa8ff;

  /* Abstandsskala (Aufgabe #828). Fuenf Stufen, aus einer Inventur der bis
     dahin frei gewachsenen Werte ueber Live, Cockpit, Vergleich, Karte und
     Einstellungen abgeleitet: je Stufe der Wert, der die Summe aller
     Verschiebungen zu den echten Altwerten minimiert, nicht eine erfundene
     Rastergroesse. Deshalb 0.4rem und nicht die noch haeufigere 0.5rem als
     Stufe 2, sie liegt bezogen auf die ganze Nachbarschaft (0.3 bis 0.5rem)
     naeher am Schwerpunkt. Wo ein Abstand mit einer JS-Berechnung verzahnt
     ist (Sticky-Block-Geometrie der Timing-Liste, Pin-Skalierung der Karte)
     oder Platz fuer ein Element reserviert statt zwei Bereiche zu trennen,
     bleibt der Altwert stehen. */
  --space-1: 0.15rem;
  --space-2: 0.4rem;
  --space-3: 0.7rem;
  --space-4: 1rem;
  --space-5: 1.2rem;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: "SF Pro Display", "Helvetica Neue", Arial, sans-serif;
  font-variant-numeric: tabular-nums;
}

a { color: var(--info); }

/* Die Kopfnavigation bleibt beim Scrollen stehen.
 *
 * **Warum.** Sie ist der Weg zwischen den Ansichten, und die Seiten sind lang:
 * die Rundenliste hat hundert Zeilen, das Dashboard so viele Kacheln, wie man
 * eingeschaltet hat. Wer von unten in eine andere Ansicht will, sollte nicht
 * erst zurückscrollen müssen.
 *
 * **Der Hintergrund ist undurchsichtig** (`--panel` ist eine volle Farbe, kein
 * Farbauftrag mit Alpha). Ohne das schiene der durchlaufende Inhalt durch die
 * Zeile, und eine halbtransparente Navigation über einem Diagramm ist weder
 * lesbar noch das Diagramm.
 *
 * **Die Ebene liegt über allem** (50 gegen die 40 der fest stehenden
 * Wiedergabeleiste und des Zoombands, siehe analyse.css). Sie überschneiden
 * sich zwar nicht, die eine klebt oben und die anderen stehen unten; aber ein
 * Kopf, der unter irgendetwas verschwinden kann, ist kein Kopf.
 *
 * **Die eigene Höhe steht als `--kopf-hoehe` am Wurzelelement**, gesetzt von
 * `buildHeader` in common.js. Alles, was sonst noch am oberen Rand klebt, die
 * Seitenspalte der Analyse und die Auswahlleiste der Zeiten, hält dort an und
 * nicht bei null. Eine feste Zahl im Stylesheet wäre schon beim nächsten
 * Schriftgrad falsch, und dann klebten zwei Dinge übereinander.
 */
header.top {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: var(--space-5);
  padding: var(--space-3) var(--space-5);
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}

header.top .brand {
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}

header.top nav { display: flex; gap: var(--space-4); }

header.top nav a {
  color: var(--dim);
  text-decoration: none;
  padding: var(--space-1) var(--space-2);
  border-radius: 4px;
}

header.top nav a.active, header.top nav a:hover { color: var(--fg); background: var(--panel-2); }

/* Dropdown-Gruppen der Kopfnavigation (#906): Auswertung, Vorbereitung,
   Mehr. Der Gruppenknopf sieht wie ein gewoehnlicher Reiter aus (gleiche
   Farben, gleicher Hover), ist aber ein <button>, damit Tastatur und
   Screenreader ihn als Klappschalter erkennen (aria-haspopup/aria-expanded,
   siehe navGruppe in common.js). Die Liste liegt absolut unter dem Knopf und
   ist Teil derselben Ebene wie der Kopf, braucht also kein eigenes
   z-index. */
header.top nav .nav-gruppe { position: relative; }

header.top nav .nav-gruppe-knopf {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--dim);
  background: none;
  border: none;
  font: inherit;
  padding: var(--space-1) var(--space-2);
  border-radius: 4px;
  cursor: pointer;
}

header.top nav .nav-gruppe-knopf.active,
header.top nav .nav-gruppe-knopf:hover,
header.top nav .nav-gruppe.offen .nav-gruppe-knopf { color: var(--fg); background: var(--panel-2); }

header.top nav .nav-caret { font-size: 0.7em; }

header.top nav .nav-flyout {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: var(--space-1);
  flex-direction: column;
  min-width: 100%;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: var(--space-1);
}

header.top nav .nav-gruppe.offen .nav-flyout { display: flex; }

header.top nav .nav-flyout a {
  color: var(--dim);
  text-decoration: none;
  padding: var(--space-1) var(--space-2);
  border-radius: 4px;
  white-space: nowrap;
}

header.top nav .nav-flyout a.active, header.top nav .nav-flyout a:hover { color: var(--fg); background: var(--panel); }

header.top .spacer { flex: 1; }
header.top .status { color: var(--dim); font-size: 0.85rem; }
header.top .status b { color: var(--good); }
header.top .status b.off { color: var(--bad); }

/* Anruf-Widget der Live-Ansicht (#908), direkt hinter der Navigation
   eingehaengt, siehe voiceruf.js. Bewusst schmal: ein Statustext plus ein
   bis zwei Icon-Knoepfe, kein eigener Kasten. */
.voiceruf { display: flex; align-items: center; gap: var(--space-2); }
.voiceruf .rufstatus { color: var(--dim); font-size: 0.85rem; white-space: nowrap; }
.voiceruf .rufaktiv { color: var(--good); }
/* Ein eingehender Anruf pulsiert ruhig, damit er im Augenwinkel auf dem
   Zweitmonitor auffaellt, ohne ein eigenes Fenster oder einen Ton zu
   brauchen. */
.voiceruf .rufblinkt { color: var(--accent); animation: rufblinken 1.2s ease-in-out infinite; }
@keyframes rufblinken { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }

/* Der Kopf auf Handybreite (#132).
 *
 * `vermesseKopf` in common.js rechnet seit jeher damit, dass die Navigation
 * auf einem schmalen Fenster umbricht und der Kopf zweizeilig wird. Erlaubt
 * war es nur nie: beide Flex-Behaelter standen ohne `wrap`, und acht Reiter
 * plus Anmeldezustand schoben die Seite auf 1072 px auf. Die Folge war ein
 * waagrechter Scrollbalken auf **jeder** Seite, und der Inhalt begann links
 * ausserhalb des Sichtbaren.
 *
 * Es steht hier und nicht bei einer einzelnen Seite, weil der Kopf allen
 * gehoert.
 *
 * **Die Grenze lag zu tief.** Gemessen braucht der Kopf in einer Reihe
 * 1141 px: acht Reiter, der Quellenzustand, der Anmeldename und der
 * Abmeldeknopf. Bei 800 px Umbruchgrenze rollte damit jede Seite zwischen
 * 801 und 1140 px waagrecht, und genau dort liegt das iPad in beiden
 * Lagen (820 hochkant, 1024 quer). Die Grenze steht deshalb auf 1200 px,
 * knapp oberhalb des gemessenen Bedarfs. Am Schreibtisch aendert sich
 * nichts: was in eine Reihe passt, bricht auch mit `wrap` nicht um. */
@media (max-width: 1200px) {
  header.top { flex-wrap: wrap; gap: var(--space-2) var(--space-4); padding: var(--space-2) var(--space-3); }
  header.top nav { flex-wrap: wrap; gap: var(--space-1) var(--space-2); }
  header.top .spacer { flex: 1 0 100%; height: 0; }
}

main { padding: var(--space-5); }

.mono { font-variant-numeric: tabular-nums; }
.dim { color: var(--dim); }
.good { color: var(--good); }
.bad { color: var(--bad); }
/* Zwischen gut und schlecht: der Vorgang ist durchgelaufen, aber nicht
   vollstaendig. Etwa ein Setup, das aus der Bibliothek verschwunden ist,
   dessen Datei auf dem ausgeschalteten PC aber noch liegt (#67). */
.warn { color: var(--accent); }

button, select, input {
  background: var(--panel-2);
  color: var(--fg);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: var(--space-2) var(--space-3);
  font: inherit;
}

button { cursor: pointer; }
button:hover { border-color: var(--accent); }
button.primary { background: var(--accent); color: #111; border-color: var(--accent); font-weight: 600; }

/* --- Streckenkarte ------------------------------------------------------ */

.mapwrap {
  background: var(--panel);
  border: 4px solid var(--line);
  border-radius: 10px;
  margin-bottom: var(--space-4);
  overflow: hidden;
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}

/* Flaggenlage als Rahmenfarbe der ganzen Kachel (#415, Entscheid Max,
 * 10.08.2026), an die Stelle der frueheren `.flaggenzeile` getreten (siehe
 * zeigeSessionleiste in fahrerkachel.js fuer die Zustandslogik). Vier feste
 * Breite statt der neutralen 1px, sonst waere der Wechsel von grau auf eine
 * Farbe kaum zu sehen, und ein staendig springender Rand (1px -> 4px bei
 * jedem Zustandswechsel) waere ein Layout-Zucken. Deshalb ist die Breite
 * immer 4px, nur die Farbe wechselt.
 *
 * Reihenfolge/Bedeutung siehe fahrerkachel.js: rot vor gelb vor weiss
 * (vor dem Start) vor kariert (Zielflagge/Rennende) vor gruen (frei,
 * laeuft). Ohne Session bleibt es beim neutralen var(--line), siehe dort. */
.mapwrap.flagge-weiss { border-color: var(--fg); }
.mapwrap.flagge-gruen { border-color: var(--good); }
.mapwrap.flagge-gelb { border-color: #ffd23f; }
.mapwrap.flagge-rot { border-color: #ff4d5e; }
/* Kariert ist keine Farbe, sondern ein Muster, deshalb ein eigener Weg statt
 * border-color: zwei Hintergrundebenen, die eine (padding-box) fuellt die
 * Kachel wie gewohnt mit --panel, die andere (border-box) zeichnet ein
 * Schachbrett nur im Rahmenbereich, derselbe Kniff wie das Karo-Symbol bei
 * .mapbar .mapuhr .karo weiter unten, nur als Rahmen statt als Icon. */
.mapwrap.flagge-kariert {
  border-color: transparent;
  background-image:
    linear-gradient(var(--panel), var(--panel)),
    repeating-conic-gradient(#101720 0% 25%, #fff 0% 50%);
  /* 8px statt der 16px des kleinen Karo-Symbols anderswo: der Rahmen ist nur
     4px breit, bei 16px passt kaum ein voller Wechsel hinein und es wirkt
     wie eine gestrichelte Linie statt wie ein Karomuster. Bei 8px stehen
     erkennbar zwei Karos nebeneinander in der Breite. */
  background-size: auto, 8px 8px;
  background-origin: padding-box, border-box;
  background-clip: padding-box, border-box;
}

/* Die blaue Flagge gilt einem Fahrer, nicht der Strecke (#415): sie ersetzt
 * die Rahmenfarbe oben deshalb nicht, sondern kommt als zweiter, innerer
 * Ring dazu. So bleiben Streckenzustand und Fahrerzustand gleichzeitig
 * sichtbar, etwa gelb (Strecke) mit blauem Innenring (der betrachtete
 * Fahrer wird ueberrundet) zugleich. `inset` haelt den Ring innerhalb des
 * schon vorhandenen Rahmens, `overflow: hidden` oben schneidet ihn nicht an,
 * ein inset-Schatten liegt immer innerhalb der Randbox. */
.mapwrap.blau {
  box-shadow: inset 0 0 0 4px var(--info);
}

/* Die Zeichenflaeche und ihre Bedienung. Der Kasten ist der Bezugspunkt fuer
   die Knopfgruppe in der Ecke und sonst nichts: er darf weder Rand noch
   Fuellung haben, sonst waeren die Knoepfe nicht mehr an der Kartenecke. */
.mapwrap .kartenfeld { position: relative; }

.mapwrap canvas {
  display: block;
  width: 100%;
  height: 52vh;
  min-height: 260px;
  touch-action: none;    /* sonst scrollt die Seite beim Ziehen mit */
  user-select: none;     /* und sonst markiert das Ziehen den Seitentext */
}

/* Die Bedienung der Karte, oben rechts in der Kartenecke (#156).
 *
 * **Warum sie hier liegt und nicht mehr in der Leiste.** Der Titelbereich
 * traegt seit #156 vier Angaben (Strecke, Sessiontyp, Uhr, schnellste Runde)
 * und hat fuer Knoepfe keinen Platz mehr. Auf der Karte selbst ist ohnehin
 * der Ort, an dem man sie sucht.
 *
 * **Die Gruppe nimmt keine Zeiger an.** Das ist die entscheidende Zeile:
 * Zoomen, Ziehen und Doppelklick laufen ueber das Canvas darunter, und ein
 * unsichtbares Rechteck darueber wuerde sie in dieser Ecke verschlucken.
 * Nur die Knoepfe selbst greifen wieder zu.
 *
 * **Sie muss auf jedem Untergrund lesbar sein.** Unter ihr liegt mal leere
 * Flaeche, mal die helle Fahrbahn, mal ein Fahrerschild. Die Knoepfe tragen
 * deshalb eine deckende Flaeche und einen weichen Schatten, statt sich auf
 * den Hintergrund der Seite zu verlassen.
 *
 * Angelegt als Gruppe, seit #152 mit drei Knoepfen belegt (einpassen,
 * Sektorfarben, kippen). Weitere reihen sich ohne Umbau ein. */
.mapwrap .kartenknoepfe {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  display: flex;
  gap: var(--space-2);
  pointer-events: none;
}
.mapwrap .kartenknoepfe > * { pointer-events: auto; }
.mapwrap .kartenknoepfe button {
  background: rgba(12, 17, 23, 0.88);
  border-color: rgba(255, 255, 255, 0.22);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
}
.mapwrap .kartenknoepfe button:hover { border-color: var(--accent); }

/* Spiegelbildliche Gruppe oben links (#820, Entscheid Max 20.08.2026),
 * bisher trug nur die rechte Ecke Knoepfe. Es aendert sich nur die Seite,
 * Hoehe, Erscheinungsbild und Pointer-Events bleiben dieselben wie oben. */
.mapwrap .kartenknoepfe.links {
  left: 0.5rem;
  right: auto;
}
/* Ein vom Browser abgeschlagenes Pop-up (#157), umgezogen aus dem
 * frueheren Rennleitungs-Kasten (#820): der Hinweis dazu ist weg, sobald
 * man ihn wegklickt, diese Markierung bleibt und traegt den Grund im
 * Tooltip. */
.mapwrap .kartenknoepfe button.ikon.geblockt {
  border-color: var(--bad);
  color: var(--bad);
}

/* Regler der Kippung (#163), senkrecht unter dem Kippknopf statt in der
 * Reihe daneben, siehe der Kommentar in index.html. Bleibt unsichtbar, bis
 * die Karte tatsaechlich gekippt ist: ohne Kippung ergibt er keinen Sinn und
 * wuerde die Ecke nur zustellen. Der Gitterschalter stand hier bis #405
 * ebenfalls drin, steht seither als eigener, dauerhaft sichtbarer Knopf in
 * der Leiste (siehe index.html), weil das Gitter jetzt auch flach wirkt. */
.mapwrap .kartenknoepfe .kippgruppe {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
}
.mapwrap .kartenknoepfe .kippgruppe .kippregler,
.mapwrap .kartenknoepfe .kippgruppe #drehmodustoggle {
  display: none;
}
.mapwrap .kartenknoepfe .kippgruppe.an .kippregler { display: block; }
/* Umschalter Folgemodus/freie Rotation (#406), erscheint wie der Regler erst
   mit der Kippung: ohne sie hat eine Kameradrehung nichts, worauf sie sich
   bezieht. */
.mapwrap .kartenknoepfe .kippgruppe.an #drehmodustoggle { display: inline-flex; }

/* Der Regler selbst: senkrecht, damit er sich unter dem quadratischen
 * Kippknopf einreiht statt eine eigene Zeile ueber die volle Kartenbreite
 * zu brauchen. `-webkit-appearance` bedient Chrome und Safari, `writing-mode`
 * dieselbe Ausrichtung in Firefox. */
.mapwrap .kartenknoepfe .kippregler {
  width: 1.3rem;
  height: 4.5rem;
  padding: var(--space-2) 0;
  writing-mode: vertical-lr;
  direction: rtl;
  -webkit-appearance: slider-vertical;
  accent-color: var(--accent);
  background: rgba(12, 17, 23, 0.88);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 6px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
  cursor: pointer;
}

.mapbar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--line);
}

.mapbar .spacer { flex: 1; }
/* Der Streckentitel traegt den Herkunftsvermerk der Kontur und wird
   dadurch lang. Er darf die Werkzeugleiste nicht in die Hoehe treiben. */
.mapbar .maptitle {
  font-weight: 600;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 6rem;
  flex: 0 1 auto;
}
/* Die Sessionzeit in der Kartenleiste.
   Genau so gross wie der Streckentitel und mittig dazwischen: es sind zwei
   gleichwertige Angaben, «wo sind wir» und «wie lange noch». Ziffern in
   Tabellenbreite, damit die Zahl beim Herunterzaehlen nicht wackelt, das
   sieht man aus zwei Metern sofort. Ist nichts zu sagen (Session ohne
   Zeitlimit), steht dort nichts. */
.mapbar .mapuhr {
  font-weight: 600;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  flex: 0 0 auto;
}
.mapbar .mapuhr:empty { display: none; }
.mapbar .mapuhr.bad { color: var(--bad); }
/* Zustand statt Zahl: «Letzte Runde», «Rennen beendet». Dieselbe Groesse,
   damit die Leiste beim Umschalten nicht springt. */
.mapbar .mapuhr .lage { display: inline-flex; align-items: center; gap: var(--space-2); }
.mapbar .mapuhr .lage.letzte { color: #ffd97a; }

/* Die drei Vorstart-/Renn-Uhren teilen sich seit #403 (Entscheid Max,
   10.08.2026) die mapuhr-Stelle: Einstellfenster, Bis Sessionstart, Renn-Zeit,
   nacheinander statt gleichzeitig, siehe zeigeSessionleiste in
   fahrerkachel.js. `.lage.vorstart` traegt Punkt, Beschriftung und Zahl fuer
   die beiden Vorstart-Stufen, uebernommen aus der frueheren eigenen Zeile
   `.vorstartzeile` (bis #403). Akzentfarbe fuer Stufe 1 (Einstellfenster),
   gedimmt fuer Stufe 2 (`.session`, Bis Sessionstart): dieselbe Abstufung wie
   vorher zwischen Handlungsaufruf und zweitrangiger Zusatzinfo. */
.mapbar .mapuhr .lage.vorstart { color: var(--accent); gap: var(--space-2); }
.mapbar .mapuhr .lage.vorstart.session { color: var(--dim); }
.mapbar .mapuhr .lage.vorstart b {
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.72rem;
  font-weight: 700;
}
.mapbar .mapuhr .lage.vorstart .punkt {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: currentColor;
  flex: 0 0 auto;
}
.mapbar .mapuhr .lage.vorstart .mono {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}
.mapbar .mapuhr .lage.vorstart .dim { color: var(--dim); font-weight: 400; }
/* Abgeleitete Restzeit (Annahme bzw. #309-Ableitung): kursiv statt fett,
   damit sie sich von einem gemessenen Countdown unterscheidet. */
.mapbar .mapuhr .lage.vorstart .mono.abgeleitet { font-weight: 600; font-style: italic; }
.mapbar .mapuhr .lage.vorstart.session .mono.abgeleitet { color: var(--dim); }

/* Der Sessiontyp im Titelbereich (#156).
   Er traegt als Einziger einen Rahmen: beim Blick auf den Zweitmonitor muss
   ohne Suchen klar sein, ob gerade Training, Qualifying oder Rennen laeuft.
   Kleiner als Titel und Uhr, denn er wechselt nicht und muss nicht gelesen,
   nur erkannt werden. */
.mapbar .mapsession {
  flex: 0 0 auto;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--accent);
  border-radius: 999px;
  white-space: nowrap;
}
.mapbar .mapsession:empty { display: none; }
.mapbar .mapsession .typ {
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--accent);
}

/* Die schnellste Runde des Feldes, am rechten Rand des Titelbereichs (#156).
   Die Zeit steht in Accentfarbe und Tabellenziffern, der Name daneben
   zurueckhaltend: gesucht wird die Zahl, der Name beantwortet nur die
   Anschlussfrage. Der Name darf kuerzen, die Zahl nie, deshalb sitzt die
   Beschneidung am Namen und nicht am ganzen Feld. */
.mapbar .mapbest {
  flex: 0 1 auto;
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
  font-size: 0.85rem;
}
.mapbar .mapbest:empty { display: none; }
.mapbar .mapbest .mono {
  color: var(--accent);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.mapbar .mapbest .wer {
  color: var(--dim);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Die Kartenleiste auf schmalem Schirm (#140, #156).
 *
 * Sie stand ursprünglich in einer Reihe ohne Umbruch: Streckentitel,
 * Sessionzeit, drei Knoepfe und die Ausrichtungswahl. Gemessen ragte die
 * Wahl bei 390 px 124 px ueber den Rand, und «Karte aus» lag unsichtbar
 * ausserhalb.
 *
 * Die Bedienung ist mit #156 ganz aus der Leiste heraus in die Kartenecke
 * gewandert, dafuer stehen jetzt vier Angaben darin. Der Umbruch bleibt
 * deshalb noetig, nur anders geschnitten: Titel und Sessiontyp in einer
 * Zeile, Uhr und schnellste Runde in der zweiten. Die beiden Fuellstuecke
 * fallen weg, in zwei Zeilen haetten sie nur Loecher gerissen. */
@media (max-width: 1100px) {
  .mapbar { flex-wrap: wrap; row-gap: var(--space-2); }
  .mapbar .spacer { display: none; }
  /* Keiner der vier bekommt eine feste Zeile zugewiesen. Die Leiste bricht
     erst um, wenn es wirklich eng wird, und zwar in dieser Reihenfolge: bei
     rund 600 px passt noch alles nebeneinander, darunter faellt zuerst die
     schnellste Runde in eine zweite Zeile, dann die Uhr. Feste Zeilen haetten
     bei 600 px drei fast leere Reihen ergeben und den Kartenausschnitt um
     rund 50 px gekuerzt.
     Nachgeben darf nur, was sich kuerzen laesst: der Streckentitel und der
     Fahrername in der Bestzeit. Sessiontyp und Uhr sind kurz und ganz oder
     gar nicht lesbar. */
  .mapbar .maptitle { flex: 0 1 auto; min-width: 4rem; }
  .mapbar .mapbest { flex: 1 1 9rem; }
}

/* Knoepfe ohne Beschriftung. Das Sinnbild traegt die Bedeutung, der Wortlaut
   steht im `title` und erscheint beim Darauffahren. Quadratisch, damit die
   Leiste bei mehreren Sinnbildern ruhig bleibt. */
/* Ikonknoepfe. Ohne `.mapbar` davor, weil die Analyseseite dieselbe
   Bedienung an ihrer kleinen Karte braucht und dafuer dasselbe Sinnbild
   benutzt: eine zweite Fassung waere nur eine zweite Wahrheit. */
button.ikon {
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
button.ikon svg {
  width: 1rem;
  height: 1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Eingeschalteter Umschalter. Ohne Beschriftung braucht es dafuer ein
   sichtbares Merkmal, sonst waere der Zustand nur im Tooltip zu erfahren. */
button.ikon.an {
  border-color: var(--accent);
  color: var(--accent);
}
/* Eingeschaltet, faerbt aber gerade nichts ein.
   Eine graue Fahrbahn ist mehrdeutig: Rundenbeginn, Fahrer nicht im Feld
   oder tatsaechlich nichts zu zeigen. Der Punkt sagt "an, aber ohne Wirkung",
   den Grund nennt der Tooltip. Ohne ihn sah jeder dieser Faelle wie ein
   Defekt aus, und ein echter Defekt fiel deshalb nicht auf. */
button.ikon.an.unklar { position: relative; }
button.ikon.an.unklar::after {
  content: '';
  position: absolute;
  right: 0.18rem;
  top: 0.18rem;
  width: 0.32rem;
  height: 0.32rem;
  border-radius: 50%;
  background: #e8c33a;
}

/* Streckeninformationen der laufenden Session, unter der Werkzeugleiste.
   Sessiontyp, Restzeit und schnellste Runde standen bis #156 ebenfalls hier
   und stehen jetzt oben im Titelbereich. Uebrig bleibt, was man einmal
   nachschaut: Wetter, Temperaturen, Bedingung, Sim-Uhrzeit, Sessionlaenge. */
/* Die Felder verteilen sich ueber die ganze Breite, statt links zu kleben.
   Dafuer ein Raster mit gleich breiten Spalten und nicht `space-between`:
   Bricht die Leiste um, stuende in der letzten Zeile sonst ein einzelnes
   Feld ueber die volle Breite gezogen, oder zwei Felder mit einem Loch
   dazwischen. Im Raster behalten die Felder ihre Spalten und stehen auch
   in einer halb gefuellten Zeile untereinander (#162).
   Die Spaltenbreite selbst ist die Aufteilung, die mit dem Platz mitgeht
   (`1fr` je Spalte, `auto-fit` fuer die Spaltenzahl). Fest war bisher nur
   der Abstand zwischen den Spalten (#402, Ruecklmeldung Max: "sieht immer
   so ein bisschen rueppelig aus"): ein Chip stand linksbuendig in seiner
   Spalte (`justify-items: start`), liess dahinter je nach eigener Breite
   unterschiedlich viel Leerraum vor dem naechsten festen Gap. `center`
   nutzt stattdessen die ganze mitgehende Spalte als Abstand, das Gap
   bleibt nur noch ein kleiner Mindestwert zwischen den Spaltenraendern. */
.sessionleiste {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr));
  justify-items: center;
  text-align: center;
  column-gap: var(--space-3);
  row-gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--line);
  background: var(--panel-2);
  font-size: 0.85rem;
}
/* Ein langes Feld darf umbrechen, aber nie die Leiste breiter machen: sonst
   kommt der waagrechte Rollbalken zurueck, den die Live-Ansicht nicht hat. */
.sessionleiste > * { min-width: 0; max-width: 100%; }
.sessionleiste:empty { display: none; }
/* Wert oben und etwas groesser, Bezeichnung darunter (#402, Wunsch Max):
   braucht horizontal weniger Platz als die vorige Zeile "Bezeichnung Wert"
   nebeneinander, wird dafuer bewusst hoeher. Markup bleibt <b>Bezeichnung</b>
   gefolgt vom Wert-Span (siehe feld() in fahrerkachel.js), die Reihenfolge
   dreht sich rein visuell ueber Flex-Spalte plus `order`. */
.sessionleiste .sfeld {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.sessionleiste .sfeld > span {
  order: -1;
  font-size: 1rem;
}
.sessionleiste .sfeld b {
  order: 1;
  color: var(--dim);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.72rem;
  margin-top: var(--space-1);
}
/* Die Regeln fuer Sessionkopf, Restzeitchip und Sessiontyp-Chip sind mit
   #156 entfallen; die drei Angaben stehen im Titelbereich, siehe dort. */

/* Die eigene Flaggenzeile (#219) ist mit #415 entfallen (Entscheid Max,
   10.08.2026): die Flaggenlage faerbt seither den aeusseren Rahmen der
   ganzen Kachel, siehe .mapwrap weiter oben und zeigeSessionleiste in
   fahrerkachel.js. */

/* Warnband im Cockpit (#160, A6), nur auf fahrer.html: Qualifying, Training
   und die uebrigen Nicht-Renn-Sessiontypen drehen die Bedeutung von "vor
   mir"/"hinter mir" und dem Delta um (Zeitrueckstand statt Streckenabstand,
   siehe fahrer.js zeigeQualiband). Zuoberst in der Kartenspalte, noch vor dem
   Titelbereich: das betrifft sowohl das Fuenferfenster links als auch die
   Timing-Liste rechts, darum steht es in der Mitte, wo beide Spalten es im
   Blick haben, statt einem der beiden zugeschlagen zu sein. `:empty` blendet
   sie aus (siehe zeigeQualiband in fahrer.js), ein eigener, ruhigerer Ton
   statt der Akzentfarbe: ein
   Hinweis auf eine andere Bedeutung ist kein Countdown und keine Warnung wie
   die Flaggenzeile, sondern erklaerender Text. */
.qualiband {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--dim);
  font-size: 0.85rem;
}
.qualiband:empty { display: none; }
.qualiband b {
  color: var(--fg);
  font-weight: 700;
}

/* --- Live-Ansicht, bewusst gross --------------------------------------- */

/* Die Live-Ansicht fuellt genau einen Bildschirm. Gescrollt wird nur die
   Timing-Liste, nicht die Seite: auf einem Zweitmonitor neben dem laufenden
   Spiel soll nichts wegrutschen, was man im Vorbeischauen braucht. */
body.liveseite {
  height: 100vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
main.livemain {
  flex: 1;
  min-height: 0;         /* sonst waechst der Inhalt ueber den Bildschirm */
  padding: var(--space-3) var(--space-4);
}

/* Drei Spalten: angeheftete Fahrer, Karte, Timing-Liste.
   Die Karte ist die Mitte und bekommt den meisten Platz. Die Liste steht
   rechts ueber die volle Hoehe, damit dort deutlich mehr Fahrer sichtbar
   sind als in einer Zeile unter der Karte. */
.live {
  display: grid;
  /* 430px statt 400px Listenminimum: die Fahrernamen waren Max auch nach
     der schmaleren Aktuell-Spalte noch zu knapp (Abnahme 24.08.2026); die
     30px fliessen komplett in die 1fr-Namensspalte der Fahrerzeile. */
  grid-template-columns: minmax(0, 2.5fr) minmax(430px, 1.15fr);
  gap: var(--space-4);
  height: 100%;
  min-height: 0;
  align-items: stretch;
}
/* Rasterfelder duerfen schmaler werden als ihr Inhalt.
 *
 * Ohne das gilt fuer jedes Feld `min-width: auto`, und die Spur waechst auf
 * die kleinstmoegliche Breite ihres Inhalts. Am Handy war das die
 * Timing-Zeile mit ihren sieben Spalten: sie mass 403 px, die Spur wurde
 * 405 px breit, und die ganze Seite rollte waagrecht, obwohl `.live` selbst
 * brav 358 px hatte. Die Zeile selbst wird weiter unten umgebaut, aber die
 * Schranke gehoert hierher: sonst reisst der naechste zu breite Inhalt
 * dieselbe Wunde wieder auf. */
.live > * { min-width: 0; }
/* Sobald ein Fahrer angeheftet ist, kommt links eine schmale Spalte dazu.
   Die angehefteten Kacheln stehen darin untereinander, nicht links und
   rechts der Karte: so bleibt die Karte in einem Stueck und die Liste
   behaelt ihre Breite. */
.live.mit-pins {
  grid-template-columns: minmax(265px, 0.85fr) minmax(0, 2.2fr) minmax(430px, 1.05fr);
}
/* Die Spalten fuer die weggeklappte Karte sind mit #156 entfallen: «Karte
   aus» gibt es nicht mehr, die Karte ist der Grund fuer diese Ansicht. */

.pinspalte {
  display: none;
  flex-direction: column;
  gap: var(--space-3);
  min-height: 0;
  overflow-y: auto;
  /* Wird von fahrer.js (Cockpit) je nach Zahl der grossen Kacheln gesetzt,
     damit mehrere nebeneinander noch in die Spalte passen. Auf der
     Live-Seite bleibt es beim Grundwert 1 (#894): dort scrollt die Spalte
     stattdessen. */
  --pinskala: 1;
}
.live.mit-pins .pinspalte { display: flex; }
.pinspalte .pinslot { display: none; }
/* Flex-Spalte statt Block: das Cockpit haengt der Kachel noch die
   Ausfallzeile und die Handmarke an (siehe die A3-Regeln weiter unten), und
   die brauchen einen definierten Abstand zur Kachel darueber. Bis #289 stand
   hier ausserdem die Pfeilleiste zum Umsortieren als eigenes Element neben
   der Kachel, seither sitzt sie in der Kachel selbst (".kopfsinnbilder
   .pinordnung", siehe buildPinOrdnung in live.js), darum keine Zeile mehr
   noetig. */
.pinspalte .pinslot.belegt { display: flex; flex-direction: column; align-items: stretch; gap: var(--space-2); }

/* --- Cockpit-Ansicht (#279): Fensterbreite waehlbar (drei oder fuenf) -----
 *
 * Statisches Element am Kopf der Spalte, siehe Kommentar in fahrer.html.
 * Bleibt stehen, wenn fahrer.js die Kacheln darunter aufraeumt (raeumeFensterDom
 * in wechsleBreite entfernt gezielt nur ".pinslot"/".duellfeld"). */
.fensterwahl {
  display: flex;
  gap: var(--space-2);
  margin: 0 var(--space-1);
}
.fensterwahl button {
  flex: 1;
  padding: var(--space-2) 0;
  font-size: 0.85rem;
  font-weight: 600;
}
/* Aktive Breite: derselbe Akzent wie bei den Ikonknoepfen (button.ikon.an),
   hier ohne die Ikon-Masse, weil der Knopf eine Ziffer statt eines Sinnbilds
   traegt. */
.fensterwahl button.an {
  border-color: var(--accent);
  color: var(--accent);
}

/* --- Cockpit-Ansicht (#160, A3): Ausfallzeile, Handmarke, Handauswahl -----
 *
 * Das Cockpit haengt der Kachel bis zu zwei Textzeilen UNTER an: die
 * Ausfallzeile (Fahrzeug wird seit N s nicht mehr gemeldet) und die
 * Handmarke (von Hand gewaehlt, aendern). ".pinslot.belegt" ist seit #289
 * ohnehin eine Spalte (die Umsortier-Pfeile sitzen seither in der Kachel
 * selbst statt daneben, siehe die Regel weiter oben), diese Seite braucht
 * dafuer keine eigene Regel mehr. */
.fahrerseite .pinslot > .ausfallzeile,
.fahrerseite .pinslot > .handmarke { margin: var(--space-2) var(--space-1) 0; font-size: 0.85rem; }
.fahrerseite .pinslot > .handmarke a { margin-left: var(--space-2); }

/* --- Cockpit-Ansicht (#160, A4): das Fuenferfenster ------------------------
 *
 * Bis zu fuenf Kacheln stehen jetzt in der Spalte statt einer. Ohne
 * Kennzeichnung waeren sie ununterscheidbar; ein Akzentrand markiert, welche
 * der fuenf meine eigene ist, genau das ist der Zweck dieser Seite.
 * (Die Abstufung gross/klein selbst kam mit A5, siehe weiter unten: nur die
 * grosse Kachel ist noch `.card`, die kleine ist `.fahrerzeile.kleinkachel`,
 * der Akzentrand gilt darum ausdruecklich nur `.card`, meine eigene Kachel
 * ist ja immer gross.) */
.fahrerseite .pinslot.mein-fahrzeug > .card {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

/* Der Hinweis ohne Kachel (kein Alias, nicht im Feld): eigener Innenbehaelter
   mit derselben Polsterung wie die Kachel selbst (siehe .pinslot .card
   weiter unten), damit der Text nicht am Rand der Spalte klebt. */
.fahrerseite .pinslot .hinweisinhalt {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-3); width: 100%;
}
/* Die Auswahlliste im Hinweis: ein Knopf je gemeldetem Fahrer im Feld. */
.pinspalte .handliste { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.pinspalte .handliste button { padding: var(--space-2) var(--space-3); font-size: 0.85rem; }

/* --- Cockpit-Ansicht (#160, A5): die kleine Kachel -------------------------
 *
 * Fuer die zwei entfernten Nachbarn wird dieselbe Zeile gebaut wie rechts in
 * der Timing-Liste (buildRow/updateRow mit ohneBalken=true, siehe fahrer.js):
 * Rang, Zustandspunkt, Name, Zeiten, Delta bleiben dadurch automatisch
 * identisch zur Liste, keine dritte Darstellungsform noetig. `.ohnebalken`
 * gibt es schon (der geklebte Feldblock rechts nutzt dieselbe Klasse fuer
 * denselben Zweck, siehe oben) und entfernt den Mini-Sektor-Balken ganz aus
 * dem Raster.
 *
 * Die Spalte links ist aber schmaler als die Timing-Liste rechts
 * (`minmax(265px, 0.85fr)` gegen `minmax(400px, 1.05fr)` in `.live.mit-pins`
 * oben), und die normale Zeile ist auf die breite Form ausgelegt (sieben
 * Spalten nebeneinander, siehe `.fahrerzeile`): ihre schmale, zweizeilige
 * Form greift erst per @media unter 1100px FENSTERbreite, nicht Spaltenbreite
 * (siehe die Begruendung dort). Ohne eigene Regel wuerde die Zeile hier also
 * auf jedem normal breiten Bildschirm seitlich ueberstehen. `.kleinkachel`
 * uebernimmt darum die schmale Form fest, dazu etwas groessere Schrift als in
 * der Liste: die Spezifikation will sie "etwas groesser gesetzt", kleiner als
 * eine grosse Kachel, aber immer noch aus zwei Metern lesbar. Beide Regeln
 * stehen mit vier Klassenselektoren und schlagen damit `.fahrerzeile` bzw.
 * `.fahrerzeile.ohnebalken` allein durch Spezifitaet, keine Regelreihenfolge
 * noetig. */
.fahrerseite .pinslot .fahrerzeile.kleinkachel {
  cursor: default;
  grid-template-columns: 2.4rem 13px minmax(0, 1fr) auto 5.6rem;
  grid-template-areas:
    "rang dot name name current"
    "rang dot last best delta";
  row-gap: var(--space-1);
  padding: var(--space-3) var(--space-3);
  font-size: 1rem;
}
.fahrerseite .pinslot .fahrerzeile.kleinkachel > [data-f="last"] { text-align: left; }
.fahrerseite .pinslot .fahrerzeile.kleinkachel > [data-f="last"],
.fahrerseite .pinslot .fahrerzeile.kleinkachel > [data-f="best"] { font-size: 0.92rem; }
/* Dieselbe Beschriftung wie in der schmalen Form der Liste, aus demselben
   Grund: ohne sie stehen zwei Zeiten und ein Abstand in der zweiten Zeile
   nebeneinander, und welche welche ist, waere nicht mehr zu erkennen. */
.fahrerseite .pinslot .fahrerzeile.kleinkachel > [data-f="last"]::before { content: 'letzte '; }
.fahrerseite .pinslot .fahrerzeile.kleinkachel > [data-f="best"]::before { content: 'beste '; }
.fahrerseite .pinslot .fahrerzeile.kleinkachel > [data-f="last"]::before,
.fahrerseite .pinslot .fahrerzeile.kleinkachel > [data-f="best"]::before {
  color: var(--dim);
  font-size: 0.62rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* --- Das Duellfeld zwischen zwei Kacheln --------------------------------
 *
 * Es sitzt buchstaeblich zwischen den beiden Fahrern, um die es geht, und
 * nicht in einer eigenen Ecke: der Abstand ist eine Aussage ueber genau
 * dieses Paar, und wer ihn sucht, schaut zwischen die beiden Namen.
 *
 * Bewusst dunkler und schmaler als eine Kachel. Es ist ein Bindeglied, kein
 * dritter Fahrer, und es darf die beiden Kacheln nicht ueberstrahlen. */
.pinspalte .duellfeld { display: none; }
.pinspalte .duellfeld.belegt {
  display: block;
  margin: -0.3rem 0;
  padding: var(--space-2) var(--space-3) var(--space-3);
  border-radius: 8px;
  background: #131a22;
  border: 1px solid #263140;
  text-align: center;
}
/* Abstand und Trend seit #401 auf einer Zeile: der Trend braucht keine
   eigene Erklaerung mehr ("holt auf"/"verliert"), Vorzeichen und Farbe
   reichen, siehe zeigeDuelle in live.js/fahrer.js. */
.duellfeld .duellzeile {
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: var(--space-2);
}
.duellfeld .duellzahl {
  font-variant-numeric: tabular-nums;
  font-size: calc(2.05rem * var(--pinskala));
  line-height: 1.1;
  font-weight: 600;
}
.duellfeld .duellzahl.dim { color: var(--dim); font-weight: 400; }
.duellfeld .duelltrend {
  font-size: calc(1.05rem * var(--pinskala));
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.duellfeld .duelltrend.dim { color: var(--dim); font-weight: 400; }
/* Die Rundenbilanz unter dem Trend: dieselbe Aussage über eine ganze Runde
   statt über drei Sekunden. Kleiner gesetzt als der Trend, weil sie nicht
   die Zahl ist, auf die man springt, sondern die, die man prüft. */
.duellfeld .duellbilanz {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0 var(--space-3);
  margin-top: var(--space-1);
  font-size: calc(0.82rem * var(--pinskala));
  font-variant-numeric: tabular-nums;
  color: var(--dim);
}
.duellfeld .duellbilanz .bpost { display: inline-flex; gap: var(--space-2); align-items: baseline; }
.duellfeld .duellbilanz .bpost i { font-style: normal; }
/* Seit #408 auf Groesse der Live-Veraenderung (.duelltrend) gehoben: die
   Zahlen von "letzte Runde" und "laufend" waren kleiner als der Trend
   darueber, obwohl sie dieselbe Gewichtung verdienen. Text und Position der
   Beschriftung bleiben unangetastet, nur die Zahl selbst waechst. */
.duellfeld .duellbilanz .bpost b {
  font-weight: 600;
  color: var(--fg);
  font-size: calc(1.05rem * var(--pinskala));
}
.duellfeld .duellbilanz .bpost b.good { color: var(--good); }
.duellfeld .duellbilanz .bpost b.bad { color: var(--bad); }
.duellfeld .duellbilanz .bpost b.gleich { color: var(--dim); }
/* Ausgegrauter Strich statt Erklaertext, wenn noch keine Bilanz vorliegt
   (Nachtrag zu #408): "letzte Runde"/"laufend" stehen dann trotzdem da,
   nur der Wert ist ein Platzhalter, siehe zeigeBilanz in live.js/fahrer.js.
   Dasselbe Prinzip wie .vwert.dim im Rundenvergleich darunter. */
.duellfeld .duellbilanz .bpost b.dim { color: var(--dim); font-weight: 400; }
/* Seit #401 ohne eigene Trennlinie: die Bilanz darueber sagt schon genug,
   ein Strich dazwischen war reine Dekoration ohne eigene Aussage.
   Der Abstand oberhalb ist seit #408 groesser als vorher (0.3rem): die
   Rundendifferenz ist in dieselbe Zeile wie die Sektoren gerutscht und hat
   dabei eine ganze Zeile Platz freigegeben, ein Teil davon kommt hier als
   Luft zurueck, statt dass der Block enger wirkt als vorher. */
.duellfeld .duellrunden {
  margin-top: var(--space-3);
  font-size: calc(0.9rem * var(--pinskala));
  font-variant-numeric: tabular-nums;
  color: var(--dim);
}
/* Zwei Zeilen je Feld (seit #401): oben Label und Rundenmarke nebeneinander
   (z.B. "S1 R3"), darunter allein die Zeit. Seit #408 steht die
   Rundendifferenz (frueher eigene Zeile "Runde R9" ueber den Sektoren, das
   Wort "Runde" ist dabei entfallen, siehe zeigeRundenvergleichBasis in
   fahrerkachel.js) als erstes, viertes Feld links neben S1/S2/S3 in
   derselben Reihe: "so dass alles noch etwas mehr zusammenruckt" (Wunsch
   Max). Alle vier Felder teilen sich deshalb dieselbe Groesse. */
.duellfeld .duellrunden .vwert {
  display: block;
  font-size: calc(1.15rem * var(--pinskala));
  margin-top: var(--space-1);
}
.duellfeld .duellrunden .vsektoren {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-1) var(--space-2);
}
.duellfeld .duellrunden .vsektoren > div { text-align: center; }
.duellfeld .duellrunden .vlabel { font-size: 0.78rem; }
/* Welche Rundennummer hinter Gesamtzeit oder Sektor steckt (#287): Runde und
   Sektoren koennen aus verschiedenen Runden stammen, siehe
   aktualisierePaarvergleich in fahrerkachel.js. Bewusst unauffaellig, das
   ist eine Nebeninfo, keine eigene Kennzahl. Steht seit #401 inline neben
   dem Label statt in eigener Zeile. */
.duellfeld .duellrunden .paarrunde {
  font-style: normal;
  font-size: 0.62rem;
  color: var(--dim);
  margin-left: var(--space-1);
}
/* Ungueltige Runde hinter einem Sektorwert (#875): der Wert selbst stimmt
   und bleibt lesbar, nur die Marke wird rot und traegt ein Sternchen. Ein
   gefuelltes Abzeichen wie .invalid-flag haette in dem vierspaltigen Gitter
   keinen Platz und wuerde die Zahl daneben erschlagen. */
.duellfeld .duellrunden .paarrunde.ungueltig { color: var(--bad); }

/* Die anklickbaren Spaltenkoepfe: Abstandsspalte, Klassenkopf (#822) und
   Dichtekopf (#824). Sie sahen bis #871 aus wie die uebrigen Koepfe, nur
   unterstrichelt, und teilten sich gestapelt die letzte Gitterzelle des
   Kopfs: ein eigenes achtes Gitterfeld haette den Kopf und die Fahrerzeilen
   darunter, die dieselben grid-template-columns teilen, auseinanderlaufen
   lassen, und nebeneinander brauchte jeder Knopf ohnehin mehr Breite als die
   damaligen 4rem der Abstandsspalte hergaben. Seit #871 standen sie darum
   nebeneinander in einer eigenen Zeile ueber dem Spaltenkopf.
   Seit #916 sind sie wieder Teil der letzten Gitterzelle: die Spalte
   "Letzte" faellt im Cockpit weg und die Namen in der Live-Timing-Liste sind
   auf den Initialbuchstaben gekuerzt, die Zelle ist dadurch breit genug
   geworden (siehe grid-template-columns bei .fahrerzeile weiter unten). Sie
   ist jetzt ein Kind von .fahrerkopf statt eine eigene Zeile davor, darum
   erben Farbe, Schriftgroesse und Grossschreibung wieder von dort, wie vor
   #871. */
.spaltenkopfe {
  display: flex;
  flex-wrap: wrap;   /* Sicherheitsnetz: stehen alle drei Knoepfe zugleich auf
                        ihrer langen Beschriftung, bricht die Zelle lieber in
                        eine zweite Zeile um, als Text abzuschneiden oder die
                        Spalte zu sprengen (#916, vor allem im Cockpit knapp). */
  /* flex-start statt flex-end (#934): die Zelle ist mit 7.5rem breit genug
     fuer die laengste Kombination ("Alle Leader Kompakt") bemessen, bei den
     kuerzeren Beschriftungen blieb mit flex-end unbenutzter Raum zwischen der
     Spalte "Beste" und den Knoepfen, die dadurch gegenueber der Tabelle
     abgesetzt wirkten. Links buendig ruecken sie direkt an "Beste" heran. */
  justify-content: flex-start;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}
.spaltenkopfe .spaltenkopf {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}
.spaltenkopfe .spaltenkopf:hover { color: var(--fg); }

/* Gruppenkopf der klassengetrennten Fahrerliste (#822): eine schlichte
   Beschriftungszeile zwischen den Teillisten, kein Fahrer, nicht anklickbar. */
.klassenkopfzeile {
  padding: var(--space-1) var(--space-2) var(--space-1);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--dim);
}
.klassenkopfzeile:not(:first-child) {
  margin-top: var(--space-1);
  border-top: 1px solid var(--line);
  padding-top: var(--space-2);
}

/* Klassen-Etikett an einer Fahrerzeile (#822): nur auf Fahrern ausserhalb
   der Standardklasse, siehe updateRow in fahrerkachel.js. Klein und dezent,
   die Zeile soll dadurch nicht schwerer wirken als sie ist. */
.klassen-etikett {
  margin-left: var(--space-2);
  padding: 0 var(--space-1);
  font-size: 0.6rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  color: var(--dim);
  vertical-align: middle;
}

/* Flagge der Nationalitaet vor dem Namen (#9IT). Sie steht als Emoji im
   Text, braucht also keine Farbe und keine Bilddatei; nur der Abstand nach
   rechts und ein fester Platz, damit die Namen daneben untereinander
   buendig bleiben. Faellt die Flagge auf ein Textkuerzel zurueck (Nordirland),
   bleibt sie in derselben Spalte, nur kleiner gesetzt. */
.nationsflagge {
  flex: 0 0 auto;
  margin-right: var(--space-1);
  font-size: 0.85em;
  line-height: 1;
}

/* --- Der Rundenauszug in der Fahrerkachel ------------------------------- */
.card .auszug { margin-top: var(--space-3); border-top: 1px solid #263140; }
.card .auszugkopf {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) 0 var(--space-1);
  background: none;
  border: 0;
  color: var(--dim);
  font: inherit;
  font-size: calc(0.85rem * var(--pinskala));
  cursor: pointer;
  text-align: left;
}
.card .auszugkopf:hover { color: var(--fg); }
.card .auszugkopf .kappe { transition: transform 0.12s; display: inline-block; }
.card .auszugkopf.auf .kappe { transform: rotate(180deg); }
.card .auszugwahl { display: flex; gap: var(--space-1); margin: var(--space-1) 0 var(--space-2); }
.card .auszugwahl .stufe {
  flex: 1;
  padding: var(--space-1) 0;
  background: #182029;
  border: 1px solid #2c3644;
  border-radius: 5px;
  color: var(--dim);
  font: inherit;
  font-size: calc(0.8rem * var(--pinskala));
  cursor: pointer;
}
.card .auszugwahl .stufe.an {
  background: #22303f;
  border-color: var(--accent);
  color: var(--fg);
}
/* Rundennummer, Zeit, drei Sektoren, Marken. Feste Spalten, damit die
   Zahlen untereinander stehen und sich vergleichen lassen. */
.card .auszugliste .azeile {
  display: grid;
  grid-template-columns: 1.6rem 1fr 1fr 1fr 1fr auto;
  gap: var(--space-2);
  align-items: baseline;
  font-variant-numeric: tabular-nums;
  font-size: calc(0.85rem * var(--pinskala));
  padding: var(--space-1) 0;
}
.card .auszugliste .azeile .anr { color: var(--dim); text-align: right; }
.card .auszugliste .azeile .asek { color: var(--dim); }
/* Persoenliche Bestzeit gruen, Session-/Feldbestzeit violett: dieselbe
   Konvention wie bei den Mini-Sektoren (server/minisectors.js, #280),
   violett gehoert nur der einen echten Bestzeit im ganzen Feld gleichzeitig
   (Entscheid Max, 09.08.2026, #287). Vorher war hier jede persoenliche
   Bestzeit violett. */
.card .auszugliste .azeile .azeit.beste { color: var(--good); font-weight: 600; }
.card .auszugliste .azeile .azeit.feldbest { color: var(--violett, #c792ea); font-weight: 600; }
/* Ungueltige Runde: die Zeit selbst wird rot statt eines Zusatztexts (#287,
   Entscheid Max), damit die Spalten dahinter nicht mehr verrutschen (siehe
   die Begruendung in fahrerkachel.js/zeichneAuszug). Bewusst ein anderer
   Rotton als var(--bad) und kursiv statt fett: var(--bad) steht im
   Duellfeld daneben fuer "verloren" (Differenzwerte, dort fett in <b>), das
   waere sonst dieselbe Farbe fuer zwei verschiedene Aussagen im selben
   Bildausschnitt. #ff9aa8 ist dieselbe blasse Ungueltig-Farbe, die die
   Marke .marke.ungueltig hier schon benutzt (siehe unten). */
.card .auszugliste .azeile .azeit.ungueltig-zeit { color: #ff9aa8; font-style: italic; font-weight: 400; }
.card .auszugliste .azeile.ungueltig { opacity: 0.6; }
.card .auszugliste .marke {
  font-style: normal;
  font-size: 0.68rem;
  padding: 0 var(--space-1);
  border-radius: 3px;
  background: #2c3644;
  color: var(--dim);
}
.card .auszugliste .marke.ungueltig { background: #4a2530; color: #ff9aa8; }

/* Die angehefteten Kacheln stehen in einer schmalen Spalte untereinander.
   Etwas kleiner als eine freistehende Kachel, aber immer noch aus zwei
   Metern lesbar. Mit jeder weiteren Kachel ruecken sie ueber --pinskala
   zusammen, statt der Karte daneben Breite wegzunehmen. */
.pinslot .card {
  cursor: pointer;
  border-color: var(--accent);
  padding: calc(0.9rem * var(--pinskala)) calc(1.1rem * var(--pinskala));
  min-width: 0;
}

/* --- Pfeile zum Umsortieren angehefteter Kacheln (#158, seit #289 in der
   Kachel) -------------------------------------------------------------
 *
 * Bis #289 (09.08.2026) sassen sie bewusst nicht in der Kopfzeile der
 * Kachel, sondern als eigenes Element daneben im ".pinslot": dort draengten
 * sich schon Farbpunkt, Kuerzel-Badge, Name sowie die Sinnbilder fuer Folgen
 * und Vordermann, lange Namen wurden dort schon ohne diese Pfeile
 * abgeschnitten (#222). Der Vordermann-Knopf ist mit #281 entfallen, seither
 * ist in der Zeile wieder Platz, und Max wollte die Pfeile dort, wo die
 * uebrigen Kachel-Sinnbilder stehen: links vom Fadenkreuz-Knopf. Sie
 * stecken jetzt in ".kopfsinnbilder" neben ".folgeknopf", siehe buildCard
 * in fahrerkachel.js und buildPinOrdnung in live.js. Die Positionierung
 * dieser Zeile selbst steht deshalb bei ".card .kopfsinnbilder" weiter
 * unten, nicht mehr hier.
 *
 * Nur ab zwei angehefteten Fahrern ueberhaupt da (Klasse "mit-ordnung" am
 * Slot, siehe zeigePinOrdnung in live.js), bei einem einzigen waeren beide
 * Pfeile wirkungslos. Wo sie steht, ist jeder Pfeil immer sichtbar; am
 * oberen bzw. unteren Ende der Liste wird das jeweilige Sinnbild nur
 * ausgegraut (".aus"), nicht entfernt, sonst spraenge der andere Pfeil beim
 * Verschwinden des ersten.
 *
 * Bewusst weiter nicht an --pinskala gekoppelt: die Pfeile sollen in der
 * knappsten Dichtestufe genauso gut zu treffen sein wie in der vollen,
 * genau wie der Fadenkreuz-Knopf daneben. */
.card .kopfsinnbilder .pinordnung { display: none; }
.pinslot.mit-ordnung .card .kopfsinnbilder .pinordnung {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.card .kopfsinnbilder .pinordnung button.ikon { width: 1.6rem; height: 1.6rem; }
.card .kopfsinnbilder .pinordnung button.ikon svg { width: 0.85rem; height: 0.85rem; }
.card .kopfsinnbilder .pinordnung button.ikon.aus {
  opacity: 0.35;
  cursor: default;
}
.card .kopfsinnbilder .pinordnung button.ikon.aus:hover { border-color: var(--line); }
/* Wieviel Rand der Name dafuer reserviert, steht bei ".card .kopfsinnbilder"
   weiter unten, zusammen mit dem Fadenkreuz-Knopf. */
.pinslot .card .name { font-size: calc(1.25rem * var(--pinskala)); }
.pinslot .card .big { font-size: calc(2.5rem * var(--pinskala)); }
.pinslot .card .delta { font-size: calc(2.1rem * var(--pinskala)); }
.pinslot .card .field .value { font-size: calc(1.2rem * var(--pinskala)); }
.pinslot .card .sectors div { font-size: calc(1.05rem * var(--pinskala)); }
/* Die drei Felder (Letzte, Beste, Tempo) stehen dreispaltig nebeneinander
   (Entscheid Max, #413). Moeglich wird das erst durch die gekuerzten Titel
   in fahrerkachel.js: "Letzte Runde" und "Session-Bestzeit" waren als
   Beschriftung zu breit fuer drei Spalten in den rund 265 px der Kachel. */
.pinslot .card .row {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--space-2) var(--space-4);
  align-items: end;
}
/* Die senkrechten Abstaende gehoeren mit an --pinskala.
 *
 * Sie standen bisher fest, und genau daran scheiterte das Kleinerwerden:
 * eine Kachel besteht der Hoehe nach ueberwiegend aus Abstaenden zwischen
 * ihren Bloecken. Von 461 px einer Kachel bei sechs Pins waren rund 130 px
 * blosser Rand, den keine Schriftgroesse je angefasst hat. */
.pinslot .card .sub { margin-bottom: calc(0.8rem * var(--pinskala)); }
.pinslot .card .row { margin-top: calc(0.9rem * var(--pinskala)); }
.pinslot .card .sectors { margin-top: calc(0.7rem * var(--pinskala)); }
.pinslot .card .track-bar { margin-top: calc(0.9rem * var(--pinskala)); }
.pinslot .card .auszug { margin-top: calc(0.7rem * var(--pinskala)); }
.pinslot .card .strategie { margin-top: calc(0.8rem * var(--pinskala)); }

/* --- Dichtestufen der Pin-Spalte ----------------------------------------
 *
 * `--pinskala` allein genuegt nicht. Sie verkleinert die Schrift, nicht aber
 * die Zahl der Zeilen, und die macht die Hoehe. Eine Kachel blieb deshalb
 * auch bei sechs Pins ueber 400 px hoch: sechs davon ergaben ein Rollband
 * von drei Metern, von dem eineinhalb Kacheln zu sehen waren. Angeheftet
 * waren sechs Fahrer, anschauen liessen sich zwei. Genau das war an der
 * Anheftfunktion kaputt (#136).
 *
 * Deshalb faellt jetzt gestaffelt weg, was in der Kachel am ehesten
 * entbehrlich ist. **Welche Stufe gilt, entscheidet die Messung und nicht
 * die Zahl der Pins**: fahrer.js (Cockpit) nimmt die hoechste Stufe, die noch
 * ganz in die Spalte passt (siehe passeDichteAn). Auf einem hohen
 * Zweitmonitor bleiben damit auch bei vier Kacheln alle Felder stehen, auf
 * einem flachen Fenster wird schon bei zweien gekuerzt. Die Kachelbreite
 * bleibt in jedem Fall dieselbe, die Karte daneben gibt nichts ab.
 *
 * Auf der Live-Seite gibt es das seit #894 nicht mehr: dort sollen moeglichst
 * viele Daten sichtbar bleiben, die Spalte scrollt stattdessen bei
 * Platzmangel.
 *
 * Die Reihenfolge des Wegfallens folgt dem, was anderswo ohnehin steht: das
 * Automodell steht in der Timing-Liste, die drei Sektorkaesten sagen
 * dasselbe wie der Balken darunter. Zuletzt bleibt, was es nur hier gibt:
 * Name, laufende Runde, Delta, letzte Runde und der Sektorbalken. Das
 * Duellfeld dazwischen wird nie gekuerzt, es ist der Grund, zwei Fahrer
 * nebeneinander anzuheften.
 *
 * **Session-Bestzeit und Tempo fallen bei keiner Stufe mehr weg (Entscheid
 * Max, 10.08.2026, #397).** Sie standen frueher hinter demselben Argument
 * wie das Automodell, sie stehen auch in der Timing-Liste, doch genau das
 * verlangte einen Blick auf eine andere Stelle des Bildschirms waehrend man
 * die Kachel schon offen hat. Auf einem flachen Fenster verschwanden sie
 * schon ab drei angehefteten Fahrern, das war keine bewusste Entscheidung
 * mehr, sondern reine Platznot. Beide Felder bleiben jetzt unabhaengig von
 * der Fahrerzahl sichtbar, das `.row`-Feld selbst wird nicht mehr
 * dichtegesteuert versteckt.
 *
 * **Der Ausklapper bleibt immer erreichbar (#222).** Der Schadensblock stand
 * bis hierher nicht auf dieser Liste und fiel bei «knapp» trotzdem ganz weg,
 * Knopf und Inhalt zusammen (eine eigene Regel weiter unten hat ihn extra
 * dazugenommen). Das traf ausgerechnet die einzige Kachel, in der der
 * Schaden je etwas zeigt, das eigene Fahrzeug: fremde Fahrzeuge haben ohne
 * Collector gar keine Schadensdaten und bauen den Block gar nicht erst ein
 * (siehe zeigeSchaden in live.js). Anders als die Sektorkaesten steht der
 * Schaden pro Zone nirgendwo sonst, die Rennleitung meldet ihn nur
 * pauschal. Deshalb bleibt der Schadensblock jetzt in jeder Stufe stehen.
 * Zugeklappt (die Grundstellung, siehe `schadenAuf` in live.js) ist er kaum
 * mehr als eine Kopfzeile; ausgeklappt darf er die Kachel wachsen lassen wie
 * bei ein oder zwei Pins auch, dann rollt die Spalte, statt den Ausklapper
 * wirkungslos zu machen. Ein Knopf, der sich klicken laesst, ohne dass sich
 * etwas zeigt, waere kein kompakteres Ausklappen, sondern derselbe Fehler in
 * kleiner. */
.pinspalte[data-dichte='mittel'] .card .sub,
.pinspalte[data-dichte='knapp'] .card .sub,
.pinspalte[data-dichte='mittel'] .card .sectors,
.pinspalte[data-dichte='knapp'] .card .sectors { display: none; }
/* Die drei Felder der Zeile (Letzte, Beste, Tempo) fallen bei keiner Stufe
   mehr weg, siehe grosser Kommentar oben, und sie brechen ab #414 auch bei
   keiner Stufe mehr auf eine Spalte um. Bis dahin stand hier eine Regel, die
   die Zeile bei «knapp» auf eine Spalte umschaltete, und «knapp» haengt an
   der gemessenen Kachelhoehe, die schon ab drei angehefteten Fahrern kippen
   kann (siehe passeDichteAn in fahrer.js). Damit brach die Zeile
   nicht nach Bildschirmbreite um, sondern nach Fahrerzahl, und genau das
   wollte Max nicht: die drei Felder sollen immer nebeneinander stehen, ausser
   auf wirklich schmalem Bildschirm. Dieser Fall ist jetzt allein Sache des
   Medienquery fuer Telefonbreite weiter unten (`@media (max-width: 819px)`),
   der nichts von Fahrerzahl oder Dichtestufe weiss. */
/* Nur die Beschriftung «Aktuelle Runde N» faellt weg, ausdruecklich als
   direktes Kind: `.label` heissen auch die Beschriftungen in den Feldern,
   und ein «1:29.774» ohne das Woertchen «letzte Runde» darueber ist eine
   Zahl ohne Aussage. */
.pinspalte[data-dichte='knapp'] .card > .label,
.pinspalte[data-dichte='knapp'] .card .strategie { display: none; }
/* Der Schaden (Knopf wie ausgeklappter Koerper) faellt bei keiner Stufe mehr
   weg, siehe Begruendung oben. */

/* Die Karte fuellt ihre Spalte, statt eine feste Hoehe zu haben. */
.live .mapwrap {
  margin-bottom: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
/* Die Kette muss durchgehend sein: der Kasten um das Canvas ist seit #156
   dazwischen und wuerde die Dehnung sonst abfangen. */
.live .mapwrap .kartenfeld { flex: 1; min-height: 0; display: flex; }
.live .mapwrap canvas { flex: 1; height: auto; min-height: 0; }

/* Timing-Liste rechts, eigener Scrollbereich. */
.listenspalte {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.listenspalte .fahrerliste {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* Explizit, nicht nur die Vorgabe (#934): ohne eigenen Wert setzt der
     Browser overflow-x automatisch auch auf "auto", sobald overflow-y einen
     Wert ungleich "visible" hat (CSS-Spezifikation der Achsen-Kopplung). Bei
     zu breiter Zeile erschien dadurch ein waagrechter Rollbalken, statt dass
     die Liste, wie beabsichtigt, nur senkrecht scrollt. Die Zeile selbst ist
     mit #934 wieder schmal genug, dies bleibt als Sicherung stehen. */
  overflow-x: hidden;
  padding-right: 2px;   /* Platz fuer den Rollbalken, sonst wackelt die Breite */
}

/* --- Schmaler Schirm: zwei Stufen, nicht drei (#140) ---------------------
 *
 * **820 px ist die Grenze.** Darueber gilt das Breitlayout, darunter der
 * Einspalter. Die Zahl ist die Breite eines iPads hochkant, und das iPad
 * ist bewusst ein schmaler Schreibtisch und kein grosses Telefon: es
 * bekommt kein eigenes drittes Layout, sondern dieselben Spalten wie der
 * Monitor, nur enger geschnitten.
 *
 * Zwischen 820 und 1100 px reichen die Mindestbreiten des Schreibtischs
 * nicht (265 px Kacheln plus 400 px Liste plus Karte sind mehr, als ein
 * iPad hat). Die Spalten bekommen dort deshalb kleinere Untergrenzen. Die
 * Timing-Zeile passt sich mit an, sie wird ab 1100 px zweizeilig, siehe
 * weiter unten. */

/* Stufe 1: enger Schreibtisch, iPad. Spalten bleiben, sie ruecken zusammen. */
@media (min-width: 820px) and (max-width: 1100px) {
  .live { grid-template-columns: minmax(0, 1.6fr) minmax(330px, 1fr); }
  .live.mit-pins {
    grid-template-columns: minmax(190px, 0.8fr) minmax(0, 1.55fr) minmax(330px, 1fr);
  }
  main.livemain { padding: var(--space-3) var(--space-3); }
  .live { gap: var(--space-3); }

  /* Das Innere der Fahrerkacheln bei 190 px Spaltenbreite.
   *
   * Die Kachel ist am Schreibtisch fuer rund 265 px gebaut und teilt ihre
   * Bloecke in zwei bis vier Spalten. Auf 190 px hiess das gemessen: die
   * vier Spritfelder liefen zu «TANK VERBRAUCHREICHTSTINT» ineinander, die
   * «PERSOENLICHE BESTZEIT» war rechts abgeschnitten, und vom dritten
   * Sektorkasten war die Haelfte weg.
   *
   * Aus vier Spalten werden zwei, aus drei wird eine. Untereinander wird
   * die Kachel hoeher, aber die Kachelspalte rollt ohnehin, und eine hohe
   * lesbare Kachel ist einer flachen unlesbaren vorzuziehen. Die drei
   * Sektorkaesten bleiben nebeneinander, sie tragen nur «S1» und eine
   * Zahl und passen zu dritt auch hier. */
  .pinslot .card .strategie { grid-template-columns: repeat(2, 1fr); }
  .pinslot .card .sectors { gap: var(--space-2); }
  .pinslot .card .sectors div {
    padding-left: var(--space-1);
    padding-right: var(--space-1);
    /* Ein Kasten hat hier rund 52 px. «S3 28.667» braucht in voller Groesse
       mehr, und die letzte Ziffer verschwand am Rand. */
    font-size: calc(0.9rem * var(--pinskala));
  }
}

/* Stufe 2: Telefon. Eine Spalte, und die Seite darf wieder rollen.
 *
 * **Die Reihenfolge steht hier und nicht im HTML.** Im Quelltext kommen
 * erst die angehefteten Kacheln, dann die Karte, dann die Liste, weil das
 * am Schreibtisch die Reihenfolge von links nach rechts ist. Untereinander
 * gestapelt ergaebe das eine Seite, die mit bis zu sechs Fahrerkacheln
 * beginnt und die Karte weit unten versteckt. Zuoberst gehoert die Karte:
 * sie ist das eine Bild, an dem sich in zwei Sekunden ablesen laesst, wo
 * alle stehen. Darunter die Timing-Liste, ganz zuletzt die Kacheln.
 *
 * Der Ticker der Rennleitung folgt dabei von selbst: live.js haengt ihn bei
 * angehefteten Fahrern unter die Kacheln, und die stehen jetzt unten. */
@media (max-width: 819px) {
  body.liveseite { height: auto; overflow: auto; }
  .live, .live.mit-pins {
    grid-template-columns: minmax(0, 1fr);
    height: auto;
  }
  .live .mapwrap { order: 1; }
  .live .listenspalte { order: 2; }
  .live .pinspalte { order: 3; }
  /* Die Karte bekommt eine feste Hoehe statt der Restspalte. `45vh` allein
     reicht nicht: quer gehalten sind das keine 180 px und die Kontur wird
     zur Linie. Die Untergrenze faengt das ab. */
  .live .mapwrap .kartenfeld { flex: none; }
  .live .mapwrap canvas { flex: none; height: max(45vh, 240px); }
  /* Gescrollt wird jetzt die Seite. Rollten Liste und Kachelspalte
     zusaetzlich in sich, gaebe es zwei Rollbereiche ineinander, und der
     Finger traefe je nach Stelle den falschen. */
  .listenspalte .fahrerliste { overflow-y: visible; }
  .pinspalte { overflow-y: visible; }
  main.livemain { padding: var(--space-3) var(--space-3); }
  .live { gap: var(--space-4); }
  /* Die drei Felder der Zeile (Letzte, Beste, Tempo) brechen ab #414 nur noch
     nach Fensterbreite auf eine Spalte um, nicht mehr nach Fahrerzahl oder
     Dichtestufe (siehe der entfernte Kommentar weiter oben). Diese Regel hier
     ist die fuer echtes Telefon, unter 820 px. Bei 820 bis 1100 px (iPad,
     enger Schreibtisch) gilt dieselbe Umschaltung schon laenger, aus #140,
     unveraendert und ebenfalls rein breitenbasiert: siehe `.pinslot .card
     .row` im Medienquery weiter oben. */
  .pinslot .card .row { grid-template-columns: 1fr; }
}

/* Ueber der Timing-Liste stand bis #147 eine Kopfzeile mit zwei
   Erklaertexten und dem Schalter fuer den Vordermann. Der Schalter sitzt
   jetzt als Sinnbild in den Fahrerkacheln, die Texte sind entfallen, und
   damit auch die Regel dazu. Der Spaltenkopf darunter (.fahrerkopf) ist
   davon nicht betroffen. */

/* Kompakte Fahrerliste. Bei vierzig Fahrzeugen auf einem oeffentlichen
   Server ist ein Raster aus grossen Kacheln nicht mehr lesbar. */
.fahrerliste { display: flex; flex-direction: column; gap: 2px; }

/* Die Timing-Liste zeigt je Fahrer Position, Name, laufende Runde, letzte
   Runde, schnellste Runde der Session und den Abstand. Letzte und beste
   Runde stehen im schmalen Format (1:48.2), sonst passen sechs Spalten
   nicht in die rechte Spalte; die volle Genauigkeit steht im Tooltip. */
.fahrerzeile {
  display: grid;
  /* Die letzte Spalte trug seit #916 8.5rem statt 4rem, damit die drei
     Umschalt-Buttons im Kopf hineinpassen (siehe .spaltenkopfe oben). Mit
     #934 auf 7.5rem verkleinert: seit die Buttons dort linksbuendig statt
     rechtsbuendig stehen, braucht die Zelle nicht mehr die volle Breite, und
     der Platz fehlte, seit Letzte/Beste breiter wurden (naechster Absatz) -
     bei aktiver Ergebnisspalte (nach Rennende) ragte die Zeile dadurch aus
     der Liste, siehe Fehlerbericht Max 24.08.2026. 7.5rem reicht der
     laengsten Beschriftungskombination «Alle Leader Kompakt» (gemessen
     ca. 108px) weiterhin mit Luft, ohne Zeilenumbruch. In den Zeilen
     darunter bleibt die Spalte die Delta-Spalte; text-align:right liess den
     Wert bis 24.08.2026 am rechten Rand dieser breiten Zelle kleben, mit
     grossem Leerraum zur Spalte «Beste» (Befund Max nach dem Rennen). In der
     Abnahme am 24.08.2026 wurden angeklebt-links, 1.2rem Luft und
     rechtsbuendig alle live durchprobiert; geblieben ist linksbuendig mit
     1.2rem Luft, der Endentscheid von Max nach dem Blick auf rechtsbuendig
     («klebt am Rand, mach es wie links»). Die Namensspalte hat zusaetzlich
     ueber das angehobene Listenminimum mehr Platz bekommen (430px statt
     400px, siehe .live). */
  /* Letzte und Beste sind 3.6rem statt 3.2rem breit (Wunsch Max, 24.08.2026):
     die Werte stehen rechtsbuendig, die breitere Spalte wird zu sichtbarer
     Luft zwischen den Zeiten. Der Platz kommt aus der Namensspalte, die erst
     am 400px-Anschlag der Listenspalte eine Spur frueher abschneidet.
     Aktuell ist seit dem Zehntel-Format (fmtLapKurz statt voller Zeit)
     4.4rem statt 5.2rem breit; der frei werdende Platz fliesst ueber die
     1fr-Namensspalte den Fahrernamen zu, die Max zu schmal waren
     (Abnahme 24.08.2026). */
  grid-template-columns: 2rem 12px minmax(3rem, 1fr) 4.4rem 3.6rem 3.6rem 7.5rem;
  grid-template-areas:
    "rang dot name current last best delta"
    "bar  bar bar  bar     bar  bar  bar";
  align-items: center;
  /* 0.35rem und nicht 0.45rem zwischen den Spalten.
   *
   * Die sechs Abstaende zwischen sieben Spalten waren zusammen 43 px. Dazu
   * die 294 px der festen Spalten und die 48 px Untergrenze der Namensspalte:
   * die Spalten brauchten zusammen 385 px. Die Listenspalte ist aber nur
   * 400 px schmal (`minmax(400px, ...)` in `.live`), und davon gehen 2 px
   * Rollbalken, 2 px Rahmen und 16 px Polster ab, es bleiben 380 px. Die
   * Spalten ragten also knapp 5 px darueber hinaus; einen Rollbalken gab das
   * nie, weil das rechte Polster der Zeile den Ueberhang gerade noch aufnahm,
   * aber gepasst hat es nie: gemessen blieb rechts statt 8 px nur 4 px
   * Polster stehen. Mit 0.35rem bleiben knapp 5 px Luft, und die Namensspalte
   * bekommt sogar 53 statt 48 px. */
  gap: 0.2rem 0.35rem;
  padding: 0.3rem 0.5rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 6px;
  cursor: pointer;
  font-size: 0.92rem;
  min-width: 0;
}
/* Der Spaltenkopf teilt das Raster der Zeilen, damit die Beschriftungen
   ueber ihren Spalten stehen. Er scrollt nicht mit, er steht ueber dem
   Rollbereich. */
.fahrerkopf {
  display: grid;
  grid-template-columns: 2rem 12px minmax(3rem, 1fr) 4.4rem 3.6rem 3.6rem 7.5rem;
  gap: 0.35rem;   /* derselbe Abstand wie in den Zeilen, sonst wandern die
                     Beschriftungen von ihren Spalten weg */
  padding: 0 0.5rem 0.3rem;
  margin-right: 2px;   /* dieselbe Rollbalkenbreite wie die Liste darunter */
  color: var(--dim);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
/* Rechtsbuendig ab der Spalte "Aktuell", also ab dem fuenften Kind: seit
   #884 steht die (meist ausgeblendete) Ergebnisspalte als zweites Kind im
   Text, und `nth-child` zaehlt auch ausgeblendete Kinder mit. Die Zahl gilt
   damit in beiden Zustaenden, mit und ohne Ergebnisspalte. */
.fahrerkopf > span:nth-child(n + 5) { text-align: right; }

.fahrerzeile > .rang { grid-area: rang; }
.fahrerzeile > .dot { grid-area: dot; }
.fahrerzeile > .fname { grid-area: name; min-width: 0; }
.fahrerzeile > [data-f="current"] { grid-area: current; }
.fahrerzeile > [data-f="last"] { grid-area: last; }
.fahrerzeile > [data-f="best"] { grid-area: best; }
.fahrerzeile > .delta { grid-area: delta; }
.fahrerzeile > .track-bar { grid-area: bar; }
/* Das Kuerzel bleibt ausgeblendet: der Farbtupfer verbindet die Zeile
   ohnehin mit der Karte, und der volle Name steht daneben. */
.fahrerzeile > .kuerzel { display: none; }
/* Die beiden schmalen Zeitspalten etwas kleiner, damit die Namensspalte
   nicht auf ihr Minimum gedrueckt wird. */
.fahrerzeile > [data-f="last"], .fahrerzeile > [data-f="best"] { font-size: 0.82rem; }
.fahrerzeile:hover { border-color: var(--dim); }

/* --- Ergebnisspalte nach dem Rennende (#884) ------------------------------
 *
 * Seit #876 steht die Liste nach der Zielflagge in der Reihenfolge, in der
 * die Ziellinie ueberquert wurde. Was ACC danach noch an Zeitstrafen
 * verrechnet, verschiebt diese Reihenfolge nicht mehr; genau dafuer steht die
 * schmale Spalte neben Pos (Entscheid Max, 21.08.2026).
 *
 * **Geschaltet wird am <body>** (zeichneFahrerliste in fahrerkachel.js), weil
 * Spaltenkopf und Fahrerzeilen zwei getrennte Elemente mit demselben Raster
 * sind und nur gemeinsam umschalten duerfen. Ausserhalb des Rennendes greift
 * hier gar keine Rasterregel: die Liste steht dann Zeichen fuer Zeichen wie
 * bisher, ohne leere Spalte und ohne leeren Spaltenabstand (#872).
 *
 * **Die Zelle ist ausgeblendet und nicht nur leer.** Damit taucht sie in den
 * Kacheln, die dieselbe Zeile bauen (kleine Cockpit-Kachel im .pinslot), gar
 * nicht erst im Raster auf und stoert deren eigene Spaltenaufteilung nicht.
 * Im Spaltenkopf gilt dasselbe: ausgeblendet zaehlt sie das Gitter nicht mit
 * und verteilt die uebrigen Koepfe wieder auf die bisherigen sieben Spalten.
 * Deshalb steht sie dort an ihrer Rasterstelle im Text und nicht per
 * grid-column: das Gitter platziert ein Kind mit fester Spalte in der
 * Reihenfolge des Textes, ein nachgestelltes Kind laege damit in Zeile 2. */
.fahrerzeile > .erg, .fahrerkopf > .ergkopf { display: none; }
body.ergebnisspalte .fahrerliste > .fahrerzeile > .erg {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  grid-area: erg;
  line-height: 1.15;
}
.fahrerzeile > .erg > .ergwert { color: var(--dim); font-size: 0.82rem; }
/* Aufgerueckt gruen, zurueckgefallen rot, sonst gedimmt wie jede
   Nebenangabe: dieselbe Sprache wie die uebrigen Abzeichen der Zeile. */
.fahrerzeile > .erg > .ergwert.auf { color: var(--good); }
.fahrerzeile > .erg > .ergwert.ab { color: var(--bad); }
/* Gewonnen/verloren seit dem Start (#917), zweite, kleinere Zeile derselben
   Zelle: eigene Frage, eigene Zahl, siehe gewinnverlustFeld. Kleiner als
   ergwert gesetzt, damit auf den ersten Blick klar ist, welche der beiden
   Zahlen ACCs amtliche Wertung ist (oben) und welche den Rennverlauf seit
   dem Start zeigt (unten). */
.fahrerzeile > .erg > .startwert { color: var(--dim); font-size: 0.68rem; }
.fahrerzeile > .erg > .startwert.auf { color: var(--good); }
.fahrerzeile > .erg > .startwert.ab { color: var(--bad); }
body.ergebnisspalte .fahrerkopf > .ergkopf { display: block; text-align: right; }

/* Der Platz fuer die achte Spalte kommt aus der Namensspalte, der einzigen
   dehnbaren: ihr Mindestmass sinkt dafuer von 3rem auf 2rem. Erst am
   Anschlag der Listenbreite (400 px, siehe .live) wird der Name dadurch eine
   Spur frueher abgeschnitten, ueberall sonst kostet es nichts. */
body.ergebnisspalte .fahrerkopf,
body.ergebnisspalte .fahrerliste > .fahrerzeile {
  grid-template-columns: 2rem 2.2rem 12px minmax(2rem, 1fr) 4.4rem 3.6rem 3.6rem 7.5rem;
}
body.ergebnisspalte .fahrerliste > .fahrerzeile {
  grid-template-areas:
    "rang erg dot name current last best delta"
    "bar  bar bar bar  bar     bar  bar  bar";
}

/* --- Cockpit ohne die Spalte "Letzte" (#916) ------------------------------
 *
 * Im Cockpit zeigt die Timing-Liste rechts nur noch die aktuelle und die
 * beste Runde, die zuletzt gefahrene Zeit steht schon in der Fahrerkachel
 * daneben. Die Zelle selbst baut buildRow/updateRow weiterhin fuer beide
 * Seiten (geteilte Funktion, siehe fahrerkachel.js), hier wird sie nur
 * ausgeblendet und die freiwerdende Spalte aus dem Raster genommen.
 *
 * Betroffen ist nur die echte Timing-Liste (.fahrerliste, id="cars"), nicht
 * die kleine Kachel im Pinslot (.pinslot .fahrerzeile.kleinkachel): die
 * zeigt "Letzte" weiterhin, sie ist eine andere Liste mit eigener Regel
 * weiter oben.
 *
 * Die letzte Spalte ist hier nur 6rem breit, nicht 8.5rem wie auf der
 * Live-Seite: das Cockpit zeigt immer die Pinspalte mit den Nachbarn, seine
 * Listenspalte steht darum praktisch staendig auf ihrem Mindestmass von
 * 400px (gemessen, #916), deutlich schmaler als auf der Live-Seite. Die
 * Namen bleiben hier ausserdem voll ausgeschrieben (kein initialenName,
 * siehe fahrer.js), 6rem reicht fuer die ueblichen zwei kurzen
 * Beschriftungen plus eine lange, bei allen drei Knoepfen gleichzeitig auf
 * der langen Beschriftung faengt .spaltenkopfe den Rest per Zeilenumbruch
 * auf (flex-wrap, siehe oben). */
body.fahrerseite .fahrerliste > .fahrerzeile > [data-f="last"] { display: none; }
body.fahrerseite .fahrerkopf {
  grid-template-columns: 2rem 12px minmax(3rem, 1fr) 4.4rem 3.6rem 6rem;
}
body.fahrerseite .fahrerliste > .fahrerzeile {
  grid-template-columns: 2rem 12px minmax(3rem, 1fr) 4.4rem 3.6rem 6rem;
  grid-template-areas:
    "rang dot name current best delta"
    "bar  bar bar  bar     bar  bar";
}
body.fahrerseite.ergebnisspalte .fahrerkopf,
body.fahrerseite.ergebnisspalte .fahrerliste > .fahrerzeile {
  grid-template-columns: 2rem 2.2rem 12px minmax(2rem, 1fr) 4.4rem 3.6rem 6rem;
}
body.fahrerseite.ergebnisspalte .fahrerliste > .fahrerzeile {
  grid-template-areas:
    "rang erg dot name current best delta"
    "bar  bar bar bar  bar     bar  bar";
}

/* --- Die Timing-Zeile auf schmalem Schirm (#140) -------------------------
 *
 * Sieben Spalten nebeneinander brauchen gemessen 403 px. Auf einem Telefon
 * mit 390 px hiess das: der Name schrumpfte auf «Tobi...» und die
 * Abstandsspalte lag halb ausserhalb. Weglassen kam nicht in Frage, in
 * einem Rennen ist jede der drei Zeiten und der Abstand eine Angabe, auf
 * die man schaut.
 *
 * Also zwei Zeilen statt einer. Oben steht, was man im Vorbeischauen
 * sucht: Platz, Name und die laufende Runde. Darunter die beiden
 * Nachschlagezeiten und der Abstand. Der Name bekommt dadurch die ganze
 * Breite und steht endlich ganz da.
 *
 * Die Regel gilt ab 1100 px und nicht erst ab 820: im engen Breitlayout
 * ist die Listenspalte 275 px schmal, dort passen sieben Spalten genauso
 * wenig. Gemessen braucht die neue Form 247 px. */
@media (max-width: 1100px) {
  .fahrerzeile {
    grid-template-columns: 2.2rem 12px minmax(0, 1fr) auto 5.4rem;
    grid-template-areas:
      "rang dot name name current"
      "rang dot last best delta"
      "bar  bar bar  bar  bar";
    row-gap: var(--space-1);
    padding: var(--space-2) var(--space-2);
  }
  /* Der Platz steht neben beiden Textzeilen, nicht neben der oberen. */
  .fahrerzeile > .rang, .fahrerzeile > .dot { align-self: center; }
  /* Die letzte Runde beginnt am linken Rand ihrer Zeile, sonst klebte sie
     rechts an der Bestzeit und die Zeile haette ein Loch in der Mitte. */
  .fahrerzeile > [data-f="last"] { text-align: left; }
  /* Beide Nachschlagezeiten eine Spur groesser als am Schreibtisch: dort
     stehen sie in einer engen Kolonne, hier haben sie eine eigene Zeile,
     und 0.82rem liest sich am Telefon nicht. */
  .fahrerzeile > [data-f="last"], .fahrerzeile > [data-f="best"] { font-size: 0.9rem; }
  /* Jede der beiden traegt ihre Beschriftung selbst.
     Sie stand bisher im Spaltenkopf, und der faellt gleich unten weg. Ohne
     sie stuenden in der zweiten Zeile drei Zahlen der Form «1:30.4 1:30.4
     +0.712» nebeneinander, und welche davon die letzte und welche die beste
     Runde ist, waere schlicht nicht mehr zu erkennen. */
  .fahrerzeile > [data-f="last"]::before { content: 'letzte '; }
  .fahrerzeile > [data-f="best"]::before { content: 'beste '; }
  .fahrerzeile > [data-f="last"]::before,
  .fahrerzeile > [data-f="best"]::before {
    color: var(--dim);
    font-size: 0.62rem;
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: 0.04em;
  }

  /* Der Spaltenkopf kann der zweizeiligen Form nicht folgen: sechs
     Beschriftungen ueber zwei Zeilen sind mehr Streifen als Auskunft. Die
     Beschriftungen fallen hier ganz weg (alle Spans verschwinden), nur noch
     als schmaler Abstand vor der Liste. Die Knopfreihe .spaltenkopfe ist
     seit #916 ein Kind von .fahrerkopf (siehe app.css weiter oben) und kein
     span, die Regel darunter trifft sie darum nicht: sie bleibt als
     einziges sichtbares Kind im jetzt flexen Spaltenkopf stehen, weiterhin
     rechtsbuendig. */
  .fahrerkopf {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    padding-bottom: var(--space-1);
  }
  .fahrerkopf > span { display: none; }

  /* Ergebnisspalte (#884): in der zweizeiligen Form steht sie unter der
     Pos-Spalte statt daneben. Eine achte Spalte waere hier nicht
     unterzubringen, und die Rang-Zelle hat die zweite Zeile ohnehin nur
     mitbelegt, weil sie beide ueberspannte. Der Spaltenkopf ist hier ein
     Flexbehaelter ohne Beschriftungen, das gilt fuer "Erg." genauso. */
  body.ergebnisspalte .fahrerliste > .fahrerzeile {
    grid-template-columns: 2.2rem 12px minmax(0, 1fr) auto 5.4rem;
    grid-template-areas:
      "rang dot name name current"
      "erg  dot last best delta"
      "bar  bar bar  bar  bar";
  }
  body.ergebnisspalte .fahrerkopf > .ergkopf { display: none; }

  /* Cockpit ohne "Letzte" (#916), auch in dieser zweizeiligen Form: "Beste"
     uebernimmt die Flaeche, die sich "Letzte" und "Beste" sonst teilen,
     dieselben fuenf Spalten wie oben bleiben unveraendert. Das Ausblenden
     von [data-f="last"] gilt bereits unabhaengig von der Fensterbreite,
     siehe app.css weiter oben. */
  body.fahrerseite .fahrerliste > .fahrerzeile {
    grid-template-areas:
      "rang dot name name current"
      "rang dot best best delta"
      "bar  bar bar  bar  bar";
  }
  body.fahrerseite.ergebnisspalte .fahrerliste > .fahrerzeile {
    grid-template-areas:
      "rang dot name name current"
      "erg  dot best best delta"
      "bar  bar bar  bar  bar";
  }
}
.fahrerzeile.stale { opacity: 0.4; }

/* Jede Zeile ist ein eigener Stapelbereich.
 *
 * Ohne das stiegen einzelne Teile aus den normal mitlaufenden Zeilen ueber
 * den geklebten Feldblock und schienen mitten durch ihn hindurch. Der
 * Positionsmarker im Mini-Sektor-Balken traegt `z-index: 3`, der
 * Sektortrenner `z-index: 2`, und die `.track-bar` darum herum ist zwar
 * `position: relative`, aber mit `z-index: auto` eben **kein** eigener
 * Stapelbereich. Damit galten diese Werte im selben Bereich wie der
 * `z-index` der geklebten Zeile und schlugen ihn.
 *
 * `isolation: isolate` sperrt sie in ihre Zeile ein, ohne selbst einen
 * z-index zu vergeben. Damit entscheidet nur noch die Ebene der Zeilen
 * untereinander, was oben liegt, und das ist genau die Frage, um die es
 * geht. Bewusst hier auf allen Zeilen und nicht nur auf der `.track-bar`:
 * so kann kein spaeter hinzukommendes Element in einer Zeile denselben
 * Fehler noch einmal ausloesen. */
.fahrerzeile { isolation: isolate; }
.fahrerzeile.angeheftet { border-color: var(--accent); background: var(--panel-2); }

/* Der Feldblock um den ersten angehefteten Fahrer bleibt im Bild.
 *
 * Bewusst `sticky` und nicht `fixed`: die Zeilen bleiben Teil des Flusses und
 * scrollen ganz normal mit, sie halten erst am Rand, wenn sie sonst
 * hinausliefen. Der undurchsichtige Hintergrund ist noetig, damit die darunter
 * durchlaufenden Zeilen nicht durchscheinen.
 *
 * Oben und unten zugleich geht in CSS nur mit beiden Angaben; der Browser
 * haelt die Zeile dann an dem Rand, an dem sie sonst verschwinden wuerde.
 * Die konkreten Werte setzt live.js je Zeile, weil sie von den Hoehen der
 * uebrigen Blockzeilen abhaengen, siehe `vermesseBlock`. */
.listenspalte .fahrerliste .fahrerzeile.blockzeile {
  position: sticky;
  top: 0;
  bottom: 0;
  /* Deutlich ueber allem, was in einer gewoehnlichen Zeile vorkommt. Die
     Zeilen sind zwar seit `isolation: isolate` gegeneinander abgeschottet,
     aber ein reichlicher Abstand kostet nichts und macht die Absicht
     lesbar: der geklebte Block liegt oben, immer. */
  z-index: 10;
  background: var(--panel);
}

/* Der geklebte Zustand ist deutlich abgesetzt.
 *
 * Solange der Block an seinem echten Platz steht, sieht er aus wie der Rest
 * der Liste; die Klasse `klebt` kommt erst dazu, wenn er wirklich am Rand
 * haelt. Dann soll auf einen Blick klar sein, dass hier nicht die Liste
 * weiterlaeuft, sondern ein festgehaltener Ausschnitt steht: eigener
 * Hintergrund, durchgehender Akzentstreifen am linken Rand, und oben und
 * unten eine Akzentlinie als Abgrenzung.
 *
 * Alle drei Mittel sind bewusst hoehenneutral. Der Streifen ist ein
 * `inset`-Schatten und kein Rahmen, und die Abgrenzung faerbt den ohnehin
 * vorhandenen Rahmen ein, statt einen zweiten hinzuzufuegen. Jede
 * Hoehenaenderung ginge in die Klebeerkennung ein, siehe live.js. */
.listenspalte .fahrerliste .fahrerzeile.blockzeile.klebt {
  background: #1a2027;
  border-color: var(--dim);
  box-shadow: inset 2px 0 0 var(--accent);
}
.listenspalte .fahrerliste .fahrerzeile.blockzeile.klebt.blockoben {
  border-top-color: var(--accent);
}
.listenspalte .fahrerliste .fahrerzeile.blockzeile.klebt.blockunten {
  border-bottom-color: var(--accent);
}

/* Der geklebte Block ist eine Flaeche, nicht fuenf hinterlegte Zeilen.
 *
 * Jede Zeile fuer sich hatte zwar ihren undurchsichtigen Hintergrund, aber
 * zwischen zwei Zeilen liegen die 2 px Abstand der Liste (`gap`, in live.js
 * als `BLOCK_LUECKE` dieselbe Zahl), und die abgerundeten Ecken lassen an
 * jeder Naht zusaetzlich vier Zwickel frei. Genau dort lief die Liste
 * sichtbar durch den Block hindurch.
 *
 * Zwei hoehenneutrale Mittel, und hoehenneutral ist hier Bedingung: jede
 * Aenderung an der Zeilenhoehe ginge in die Klebeerkennung ein, siehe den
 * Kopfkommentar in live.js.
 *
 * 1. Die Fuge fuellt ein Schlagschatten, der die Zeile um genau die
 *    Luecke nach unten verlaengert. Er gehoert zur geklebten Zeile und liegt
 *    damit auf derselben Ebene wie sie, also ueber der durchlaufenden Liste.
 *    Die letzte Zeile des Blocks bekommt ihn nicht, sonst legte sich der
 *    Streifen unter der Abgrenzung auf die erste freie Zeile.
 * 2. Nach innen sind die Ecken eckig. Nur die Aussenkanten des Blocks
 *    bleiben rund, dadurch liest er sich als ein Stueck. */
.listenspalte .fahrerliste .fahrerzeile.blockzeile.klebt:not(.blockunten) {
  box-shadow: inset 2px 0 0 var(--accent), 0 2px 0 #1a2027;
}
.listenspalte .fahrerliste .fahrerzeile.blockzeile.klebt { border-radius: 0; }
.listenspalte .fahrerliste .fahrerzeile.blockzeile.klebt.blockoben {
  border-radius: 6px 6px 0 0;
}
.listenspalte .fahrerliste .fahrerzeile.blockzeile.klebt.blockunten {
  border-radius: 0 0 6px 6px;
}
/* Ein Block aus einer einzigen Zeile ist oben und unten zugleich. */
.listenspalte .fahrerliste .fahrerzeile.blockzeile.klebt.blockoben.blockunten {
  border-radius: 6px;
}
/* Der angeheftete Fahrer behaelt seinen abgesetzten Hintergrund und seinen
   Akzentrahmen, damit er im Block sofort als der gesuchte zu erkennen ist. */
.listenspalte .fahrerliste .fahrerzeile.blockzeile.angeheftet {
  background: var(--panel-2);
  border-color: var(--accent);
}

/* Die vier Nachbarn im geklebten Block ohne Mini-Sektor-Balken. Die zweite
   Rasterzeile faellt dabei ganz weg, sonst bliebe ihre Luecke als leerer
   Streifen stehen. */
.fahrerzeile.ohnebalken {
  grid-template-areas: "rang dot name current last best delta";
}
/* Dieselbe Zeile mit Ergebnisspalte (#884). Sie muss hier eigens stehen: die
   Regel darueber beschreibt sieben Felder, mit der achten Spalte haengt der
   Browser eine leere neunte hinten an und die Zeile laeuft aus der Liste
   heraus, genau wie im Kommentar unten beschrieben. */
body.ergebnisspalte .fahrerliste > .fahrerzeile.ohnebalken {
  grid-template-areas: "rang erg dot name current last best delta";
}
/* Cockpit ohne "Letzte" (#916): dieselbe Lücke wie oben, auch fuer Zeilen
   ohne Mini-Sektor-Balken. Das trifft im Cockpit die ganze Timing-Liste,
   nicht nur die geklebten Nachbarn der Live-Seite: dichteKompaktCockpit
   steht standardmaessig auf "Kompakt" und reicht als ohneBalken an jede
   Zeile durch (siehe fahrer.js). Fuenf statt vier Klassenselektoren, damit
   diese Regel die gleich starke body.ergebnisspalte-Fassung oben bei
   gesetzter Ergebnisspalte trotzdem schlaegt. */
body.fahrerseite .fahrerliste > .fahrerzeile.ohnebalken {
  grid-template-areas: "rang dot name current best delta";
}
body.fahrerseite.ergebnisspalte .fahrerliste > .fahrerzeile.ohnebalken {
  grid-template-areas: "rang erg dot name current best delta";
}
.fahrerzeile.ohnebalken > .track-bar { display: none; }

/* Ohne Balken heisst nicht «wieder einzeilig».
 *
 * **Das war die Ursache des waagrechten Rollbalkens.** Die Regel darueber
 * beschreibt sieben Felder in einer Reihe, und das ist die Aufteilung des
 * Schreibtischs. Unter 1100 px hat die Zeile aber nur fuenf Spalten, siehe
 * die zweizeilige Form weiter oben. Sieben Felder auf fuenf Spalten legt der
 * Browser nicht etwa um, er haengt zwei weitere Spalten hinten an. Die Zeile
 * selbst bleibt so breit wie die Liste, ihre Spalten laufen aber rechts aus
 * ihr heraus. Gemessen auf einem iPad hochkant (834 px): die Liste ist
 * 330 px breit, der Inhalt der geklebten Zeilen reichte bis 342 px, und
 * genau diese 12 px waren der waagrechte Rollbalken. Er sass an der Liste
 * und nicht an der Seite, weil `overflow-y: auto` auf `.fahrerliste` die
 * waagrechte Achse stillschweigend mit auf `auto` stellt.
 *
 * Genau darum trat es nur mit angehefteten Fahrern auf, denn `ohnebalken`
 * bekommen nur die Nachbarn im geklebten Block, und den gibt es nur mit Pin.
 *
 * Hier bleibt die Zeile also zweizeilig, es faellt nur die dritte
 * Rasterzeile mit dem Balken weg. Die Regel steht **nach** der obigen und
 * nicht im Medienblock weiter oben: beide sind gleich stark, und dann
 * entscheidet die Reihenfolge. */
@media (max-width: 1100px) {
  .fahrerzeile.ohnebalken {
    grid-template-areas:
      "rang dot name name current"
      "rang dot last best delta";
  }
  /* Mit Ergebnisspalte (#884) steht sie auch hier in der zweiten Zeile
     unter der Pos-Spalte, siehe die zweizeilige Form weiter oben. */
  body.ergebnisspalte .fahrerliste > .fahrerzeile.ohnebalken {
    grid-template-areas:
      "rang dot name name current"
      "erg  dot last best delta";
  }
  /* Cockpit ohne "Letzte" (#916), auch hier: "Beste" belegt die Flaeche
     beider Zellen, wie bei der balkigen Form weiter oben. */
  body.fahrerseite .fahrerliste > .fahrerzeile.ohnebalken {
    grid-template-areas:
      "rang dot name name current"
      "rang dot best best delta";
  }
  body.fahrerseite.ergebnisspalte .fahrerliste > .fahrerzeile.ohnebalken {
    grid-template-areas:
      "rang dot name name current"
      "erg  dot best best delta";
  }
}

.fahrerzeile .rang { color: var(--dim); font-variant-numeric: tabular-nums; }
.fahrerzeile i.dot { width: 12px; height: 12px; border-radius: 50%; }
.fahrerzeile .kuerzel {
  letter-spacing: 0.06em;
  color: var(--dim);
  background: var(--panel-2);
  border-radius: 4px;
  padding: var(--space-1) var(--space-2);
  font-size: 0.8rem;
  text-align: center;
}
.fahrerzeile .fname { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fahrerzeile .zeit { font-variant-numeric: tabular-nums; font-size: 1rem; text-align: right; }
.fahrerzeile .zeit.bad { color: var(--bad); }

/* Die schnellste Runde des Feldes in der Rennfarben-Konvention: violett,
   derselbe Ton wie ein Bestwert im Mini-Sektor-Balken. Sie steht nur beim
   Fahrer, der sie haelt. */
.fahrerzeile .zeit.feldbest { color: #b06cff; font-weight: 700; }

/* Persoenliche Bestzeit verbessert: kurz gruen aufblinken. Danach faellt die
   Zelle in ihre normale Farbe zurueck, die Auszeichnung soll nicht bleiben. */
.fahrerzeile .zeit.verbessert { animation: bestblink 1.6s ease-out 1; }
@keyframes bestblink {
  0% { color: var(--good); background: rgba(70, 209, 122, 0.28); }
  70% { color: var(--good); background: rgba(70, 209, 122, 0.10); }
  100% { background: transparent; }
}
.fahrerzeile .delta { font-size: 1rem; font-weight: 600; text-align: left; padding-left: 1.2rem; }
.fahrerzeile .track-bar.schmal { margin-top: 0; height: 7px; }


.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: var(--space-4) var(--space-5);
}

.card.stale { opacity: 0.45; }

.card .name {
  font-size: 1.7rem;
  font-weight: 700;
  line-height: 1.1;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
/* Der Namenstext selbst (#934): ohne min-width:0 haelt ein Flex-Kind seine
   Inhaltsbreite und bricht lieber um, als zu schrumpfen, ein langer Name
   lief darum in der Kachel zweizeilig statt einzeilig zu bleiben (Befund
   Max, 24.08.2026). Dieselbe Loesung wie .fahrerzeile .fname: schrumpfen
   und mit Ellipse abschneiden statt umzubrechen. Die Kopfsinnbilder rechts
   oben (.kopfsinnbilder) bleiben unangetastet, siehe padding-right oben. */
.card .name .fname {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Die Sinnbilder sitzen in der oberen rechten Ecke der Kachel, ueber dem
   Namen, in der Zeile ".kopfsinnbilder" (siehe buildCard in
   fahrerkachel.js). Absolut, damit sie die Zeilenhoehe des Namens nicht
   antasten: die Kacheln schrumpfen bei sechs Pins ueber --pinskala, und ein
   Knopf im Textfluss haette dort jedes Mal den Umbruch verschoben.

   Der Name laesst rechts genau so viel Platz frei, wie die Zeile braucht.
   Sonst laeuft ein langer Fahrername darunter, und in der 190 px schmalen
   Spalte unter 1100 px waere das sichtbar geworden. Normalerweise steht dort
   nur das Fadenkreuz (1.5rem, 0.5rem Rand), darum der schmale Grundwert.
   (Bis #281 stand hier zusaetzlich ein zweiter Knopf fuer den Vordermann;
   der Platz dafuer ist mit ihm entfallen, siehe fahrerkachel.js.)

   Bei angehefteten Kacheln mit mindestens zwei Pins kommen seit #289
   (09.08.2026) links vom Fadenkreuz die beiden Umsortier-Pfeile dazu
   (".pinordnung", siehe weiter oben und buildPinOrdnung in live.js) und der
   Name braucht entsprechend mehr Rand. Zuvor sassen die Pfeile als eigenes
   Element neben der Kachel; das war noetig, solange die Kopfzeile noch den
   inzwischen entfallenen Vordermann-Knopf trug (#281), seither ist dort
   wieder Platz. */
.card { position: relative; }
.card .name { padding-right: 2.5rem; }
.pinslot.mit-ordnung .card .name { padding-right: 6.1rem; }
.card .kopfsinnbilder {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  z-index: 2;
}
.card button.ikon.folgeknopf {
  width: 1.5rem;
  height: 1.5rem;
}
.card button.ikon.folgeknopf svg { width: 0.9rem; height: 0.9rem; }
/* Eingeschaltet: derselbe Akzent wie bei den uebrigen Umschaltern, dazu ein
   leichter Schein, damit es auch aus zwei Metern auffaellt. */
.card button.ikon.folgeknopf.an {
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 0 8px rgba(255, 180, 0, 0.45);
}

.card .name i.dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  flex: 0 0 auto;
  /* Bis #818 hier eine per Inline-Style gesetzte Fahrerfarbe, seit dem
     Feedback-Call Max/Alino (20.08.2026) einheitlich weiss. */
  background: #ffffff;
}

.card .name .kuerzel {
  font-size: 1rem;
  letter-spacing: 0.08em;
  color: var(--dim);
  background: var(--panel-2);
  border-radius: 4px;
  padding: var(--space-1) var(--space-2);
}

/* Die Position vor dem Punkt (#898), im Stil des Kuerzels daneben. Ohne Rang
   bleibt sie per [hidden] weg statt als leerer Kasten dazustehen. Podiumfarbe
   nach der Zielflagge wie .fahrerzeile .rang.podium weiter unten, dieselben
   Farbwerte. */
.card .name .rang {
  font-size: 1rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--dim);
  background: var(--panel-2);
  border-radius: 4px;
  padding: var(--space-1) var(--space-2);
}
.card .name .rang.podium { color: #101720; }
.card .name .rang.podium.p1 { background: linear-gradient(180deg, #ffe082, #d4a017); }
.card .name .rang.podium.p2 { background: linear-gradient(180deg, #e6e9ee, #a8b0ba); }
.card .name .rang.podium.p3 { background: linear-gradient(180deg, #e0a878, #a9713c); }

/* Fahrzeug (und Aufenthaltsort) plus persoenliche Bestzeit, getrennt mit │.
 *
 * Zwei Kinder statt einer Textzeile: der Fahrzeugname kann lang werden
 * («McLaren 720S GT3 Evo», teils mit Aufenthaltsort dahinter) und soll die
 * Zeile trotzdem nie umbrechen, denn die Bestzeit gehoert bewusst hierher
 * und nicht auf die Namenszeile (dort sprengen lange Fahrernamen wie «Mr.
 * Montgomery 55» die Breite). Also wird das Fahrzeug bei Bedarf mit Ellipse
 * abgeschnitten (`min-width: 0` auf beiden Ebenen ist dafuer Pflicht,
 * flex-Kinder sind sonst nie schmaler als ihr Inhalt) und die Bestzeit
 * bleibt als `flex: 0 0 auto` immer vollstaendig sichtbar am Ende der
 * Zeile. */
.card .sub {
  color: var(--dim);
  font-size: 0.9rem;
  margin-bottom: var(--space-3);
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
}
.card .sub .fahrzeug {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.card .sub .sub-pb {
  flex: 0 0 auto;
  white-space: nowrap;
  color: var(--fg);
  font-weight: 600;
}
.card .sub .sub-pb .pbtrenner {
  color: var(--dim);
  font-weight: 400;
  margin-right: var(--space-2);
}

.big {
  font-size: 4.2rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
}

.delta { font-size: 3rem; font-weight: 700; line-height: 1; }

/* Laufende Runde: die grosse Zeit, daneben klein die Rundenzahl und das
   Delta zur Bestzeit. Beides sind Beiwerte zu dieser Zeit und stehen
   deshalb daneben statt in eigenen Feldern. */
.laufend { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.laufend .big { min-width: 0; }
.laufend .beiwerk {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  line-height: 1.15;
}
.laufend .rundenzahl {
  color: var(--dim);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  white-space: nowrap;
}
/* Das Delta steht an der auffaelligen Stelle neben der Rundenzeit: beim
   Fahren ist es die Zahl, auf die man schaut. Die Rundenzahl ist dafuer in
   die Beschriftung gewandert ("Aktuelle Runde 12"). */
.delta.gross { font-size: 2rem; font-weight: 700; margin-left: auto; }
.card .label b { color: var(--fg); font-weight: 700; margin-left: var(--space-1); }

/* Gas und Bremse live, als zwei Saeulen. Gibt es nur fuer Fahrer mit
   Collector, sonst ist der ganze Block ausgeblendet. */
/* Gas und Bremse als liegende Balken, einer je Zeile. Waagrecht liest sich
   der Ausschlag im Vorbeischauen besser als zwei schmale Saeulen, und die
   Kachel ist ohnehin breiter als hoch. */
.pedale { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-3); }
/* `display: flex` schlaegt das HTML-Attribut `hidden`, deshalb hier
   ausdruecklich: ohne Collector ist der ganze Block weg, nicht leer. */
.pedale[hidden] { display: none; }
.pedale .pedal { display: flex; align-items: center; gap: var(--space-2); }
.pedale .pedal .label {
  color: var(--dim);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  /* Feste Breite, sonst wandern die Balken je nach Wortlaenge */
  flex: 0 0 3.4rem;
}
.pedale .saeule {
  display: block;
  flex: 1 1 auto;
  height: 12px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 4px;
  position: relative;
  overflow: hidden;
}
.pedale .saeule u {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0;
  transition: width 80ms linear;
}
.pedale .gas .saeule u { background: var(--good); }
.pedale .bremse .saeule u { background: var(--bad); }
.pedale .wert {
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  /* Ebenfalls fest, damit der Balken nicht bei jeder Zahl zuckt */
  flex: 0 0 2.2rem;
  text-align: right;
}

.row { display: flex; gap: var(--space-5); flex-wrap: wrap; align-items: flex-end; }
.field .label { color: var(--dim); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.08em; }
.field .value { font-size: 1.6rem; font-weight: 600; }

/* Das Ungueltig-Abzeichen der Letzte-Runde (#872): es haengte inline hinter
   dem Zeittext und schob die enge dreispaltige Zeile Letzte/Beste/Tempo
   (rund 190 px auf dem iPad, #416) bei jedem Erscheinen zur Seite oder liess
   sie umbrechen. Jetzt liegt es absolut positioniert auf Hoehe der
   Beschriftung "Letzte", dafuer braucht .feld-last ein eigenes
   position:relative. Absolut positionierte Elemente beanspruchen keinen
   Platz im Fluss, die Kachel hat also mit und ohne Abzeichen exakt dieselbe
   Hoehe und Breite. */
.field.feld-last { position: relative; }
.field.feld-last .invalid-flag {
  position: absolute;
  top: 0;
  right: 0;
  font-size: 0.6rem;
  padding: 1px var(--space-1);
  white-space: nowrap;
}

/* Drei gleich breite Sektorfelder, immer sichtbar. Gleich breit, damit die
   Kachel nicht zappelt, sobald aus einem Platzhalter eine Zeit wird. */
.sectors { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-3); margin-top: var(--space-3); }
.sectors div {
  background: var(--panel-2);
  border-radius: 5px;
  padding: var(--space-2) var(--space-3);
  font-size: 1.1rem;
}
/* Noch keine Zeit: der Platzhalter steht gedaempft da, damit er nicht wie
   eine gefahrene Zeit aussieht. Das Feld bleibt trotzdem stehen. */
.sectors div.leer { color: var(--dim); }

/* Fortschrittsbalken der laufenden Runde, unterteilt in Mini-Sektoren.
   Die Farben folgen dem Rennstandard, siehe server/minisectors.js. */
.track-bar {
  margin-top: var(--space-4);
  height: 14px;
  background: var(--panel-2);
  border-radius: 4px;
  position: relative;
  overflow: hidden;
}

/* Die Farbe steht in einer eigenen Variablen und nicht unmittelbar im
   `background`. Der gerade befahrene Abschnitt braucht dieselbe Farbe als
   Verlauf statt als Flaeche, siehe `.laufend` weiter unten, und mit der
   Variablen gibt es sie nur an einer Stelle. */
.track-bar u.mini {
  position: absolute;
  top: 0;
  bottom: 0;
  border-right: 1px solid rgba(13, 16, 20, 0.75);
  background: var(--minifarbe, transparent);
  transition: background-color 120ms linear;
}
.track-bar u.mini.offen   { --minifarbe: transparent; }
.track-bar u.mini.grau    { --minifarbe: #4a545f; }
.track-bar u.mini.gelb    { --minifarbe: #e8c33a; }
.track-bar u.mini.gruen   { --minifarbe: var(--good); }
.track-bar u.mini.violett { --minifarbe: #b06cff; }
/* Ungueltige laufende Runde. Bewusst ein sattes Rot, deutlich weiter weg
   vom Gelb ("langsamer") als ein blosser Orangeton es waere. */
.track-bar u.mini.rot     { --minifarbe: #e03a2f; }

/* Der Abschnitt, in dem das Fahrzeug gerade steckt.
 *
 * Er wartet nicht mehr darauf, verlassen zu werden, sondern waechst mit der
 * Fahrposition. `--fuellung` kommt von live.js aus dem Fortschritt, den der
 * Server meldet, die Farbe aus dem vorlaeufigen Vergleich mit der eigenen
 * Bestrunde. Ein harter Farbwechsel statt eines weichen Uebergangs: der
 * Verlauf soll die Fuellkante zeigen, nicht sie verwischen.
 *
 * Kein `transition` hier. Der Wert aendert sich zehnmal je Sekunde, ein
 * Uebergang wuerde ihn nur hinterherhinken lassen. */
.track-bar u.mini.laufend {
  background: linear-gradient(to right,
    var(--minifarbe) var(--fuellung, 0%),
    transparent var(--fuellung, 0%));
  transition: none;
}

/* Die Gelbphase, sektorgenau: ein Band ueber genau dem Sektor, fuer den
   ACC Gelb meldet. Es liegt ueber den Abschnittsfarben und deckt sie
   bewusst nicht zu, es faerbt sie ein. Der Streifenschnitt ist derselbe wie
   auf einer echten Flaggenstation und nicht mit einer Bestzeit zu
   verwechseln. Unsichtbar, solange nichts gemeldet ist, siehe
   setzeSektorFlaggen in live.js. */
.track-bar b.sektorgelb {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  background: repeating-linear-gradient(
    45deg,
    rgba(255, 210, 63, 0.55) 0 5px,
    rgba(255, 210, 63, 0.18) 5px 10px);
  transition: opacity 0.15s linear;
}
.track-bar b.sektorgelb.an { opacity: 1; }

/* Die drei echten ACC-Sektoren deutlich staerker abgetrennt */
.track-bar b.sektortrenner {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 3px;
  margin-left: -1px;
  background: var(--fg);
  z-index: 2;
}

.track-bar i.marker {
  position: absolute;
  top: -2px;
  bottom: -2px;
  width: 4px;
  border-radius: 2px;
  background: var(--accent);
  box-shadow: 0 0 6px rgba(255, 180, 0, 0.9);
  z-index: 3;
}

.invalid-flag {
  display: inline-block;
  background: var(--bad);
  color: #111;
  font-weight: 700;
  font-size: 0.75rem;
  padding: var(--space-1) var(--space-2);
  border-radius: 3px;
  vertical-align: middle;
}

/* --- Analyse ------------------------------------------------------------ */

/* Die Filterzeile traegt Auswahlfelder, Pillen und einen Knopf. Ein
   gemeinsamer Abstand fuer alle, statt drei verschiedener je nach Element:
   die Zeile war vorher optisch schief, obwohl nichts daran falsch war.
   `.spacer` schiebt die Statuszeilen ans andere Ende. */
.filters {
  display: flex;
  gap: var(--space-3) var(--space-3);
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: 0;
}
.filters .spacer { flex: 1 1 auto; }
.filters label:not(.pille) { display: inline-flex; align-items: center; gap: var(--space-2); }
/* Nebensaechliches kleiner: Umfang der Datenbank, Zustand der Verbindung. */
.klein { font-size: 0.82rem; }

/* Die Auswahlpille: ein Wort, das an oder aus ist.
   Angewählt heisst gelb, und zwar Rand und Schrift zugleich; nur eine
   Randfarbe ist auf einem dunklen Untergrund im Vorbeischauen keine Aussage.
   Sie steht hier und nicht mehr in analyse.css, weil sie an drei Stellen
   gebraucht wird: Dashboard-Wahl, Analyse-Filter und seit #139 die
   Fahrerauswahl im Pre-Race-Baukasten. Dessen Seite lud analyse.css nicht,
   und genau daran scheiterte dort die sichtbare Auswahl. */
.dash-pille {
  font-size: 0.75rem;
  padding: var(--space-1) var(--space-2);
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--dim);
  cursor: pointer;
}
.dash-pille:hover { border-color: var(--accent); }
.dash-pille.an {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}
/* Gewählt, aber gerade nicht im Feld: die Wahl bleibt gelb, der gestrichelte
   Rand sagt, dass sie sich heute nicht auflösen lässt. */
.dash-pille.an.fehlt { border-style: dashed; opacity: 0.75; }

/* Durchgehend Tabellenziffern: nur so stehen die Zeiten aller Zeilen
   untereinander in derselben Breite und lassen sich von oben nach unten
   vergleichen, statt von Zeile zu Zeile zu zappeln. */
table.laps {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
  font-variant-numeric: tabular-nums;
}
table.laps th, table.laps td {
  padding: var(--space-2) var(--space-3);
  text-align: left;
  /* Die Trennlinie je Zeile ist bewusst schwächer als die Linien der
     Oberfläche sonst. Bei zweihundert Runden untereinander wird aus einem
     kräftigen Strich je Zeile ein Gitter, das lauter ist als die Zahlen. */
  border-bottom: 1px solid color-mix(in srgb, var(--line) 50%, transparent);
  white-space: nowrap;
}
table.laps th {
  color: var(--dim);
  font-weight: 600;
  text-transform: uppercase;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--line);
}
table.laps tr:hover td { background: var(--panel); }
/* Eine Rundenzeile ist ein Schalter und darf sich beim Darauffahren auch so
   anfuehlen. Der Strich links ist derselbe, den eine gewaehlte Zeile in
   ihrer Rundenfarbe traegt: er kuendigt an, was ein Klick tut. */
table.laps tr.lap:hover td { background: var(--panel-2); }
table.laps tr.lap:hover td:first-child { box-shadow: inset 3px 0 0 var(--dim); }
table.laps tr.lap.gewaehlt:hover td:first-child { box-shadow: none; }
/* Zahlen rechtsbündig, Kopf wie Wert. */
table.laps th.num, table.laps td.num { text-align: right; }
/* Die Rundenzeit steht kräftiger da als die Sektoren daneben. */
table.laps td.zeit { color: var(--fg); font-weight: 600; }
table.laps tr.invalid td { color: var(--dim); }
/* Ungültige Runde: die Zeit selbst rot und kursiv statt gedämpft (#300).
 * Bis dahin stand hier dieselbe Dämpfung wie in den übrigen Zellen der
 * Zeile; die Gültigkeitsspalte, die die Ungültigkeit bis dahin trug, ist
 * mit #300 entfallen, die Farbe der Zeit übernimmt das jetzt allein,
 * dieselbe Konvention wie im Rundenauszug der Timing-Liste (#287, siehe
 * .card .auszugliste .azeile .azeit.ungueltig-zeit weiter unten).
 *
 * Ein eigener Rotton statt var(--bad) und kursiv statt fett: var(--bad)
 * steht in den Analyse-Tabs (z.B. die Deltaspalte im Sektorenvergleich)
 * für "langsamer, verloren" - direkt neben denselben Rundenzeiten in
 * derselben Werkzeugfamilie. Dieselbe Farbe für zwei verschiedene Aussagen
 * wäre auch hier verwechselbar, aus genau dem Grund, aus dem #287 dort
 * schon einen eigenen Ton gewählt hat. */
table.laps tr.invalid td.zeit { color: #ff9aa8; font-style: italic; font-weight: 400; }

/* --- Setup-Bibliothek --------------------------------------------------
 *
 * Beide Listen der Setup-Seite koennen lang werden: auf einem gewachsenen
 * PC liegen ein paar hundert Dateien. Sie bekommen deshalb einen eigenen
 * Rollbereich statt die Seite auf mehrere Bildschirme zu strecken, und der
 * Tabellenkopf bleibt dabei stehen, sonst weiss man in Zeile 180 nicht
 * mehr, welche Spalte was ist. */
.setupliste {
  max-height: 26rem;
  overflow: auto;
  /* Der Rahmen macht sichtbar, dass hier gerollt wird und nicht die Seite. */
  border: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  border-radius: 8px;
}

/* Die Liste reicht bis an den unteren Bildschirmrand (#76).
 *
 * **Warum nicht einfach eine groessere Zahl statt der 26rem.** Jede feste
 * Hoehe ist auf genau einem Bildschirm richtig: auf dem Laptop zu hoch, auf
 * dem Monitor daneben verschenkt sie den halben Platz. Die Seite ist deshalb
 * eine Spalte ueber die Fensterhoehe (`100dvh`, nicht `100vh`, sonst liegt
 * auf dem Handy das untere Ende unter der Adressleiste), und die Liste ist
 * das einzige Glied darin, das waechst. Kopftext, Filterzeile und die
 * Collector-Zeile darunter behalten ihre Hoehe, alles Uebrige gehoert der
 * Liste.
 *
 * `min-height: 0` an Main und Panel ist Pflicht: ein Flex-Element schrumpft
 * sonst nicht unter seinen Inhalt, die Tabelle drueckte die Seite auf und
 * gerollt wuerde am Ende doch wieder das Fenster. */
body.setupseite {
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
body.setupseite > header.top { flex: 0 0 auto; }
body.setupseite > main,
body.setupseite > main > .panel {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
body.setupseite .setupliste {
  flex: 1 1 auto;
  max-height: none;
  min-height: 8rem;
}

/* Am Handy zurueck zur normalen Seite.
 *
 * Auf einem schmalen Schirm brauchen der Erklaertext und die umgebrochene
 * Filterzeile fast die ganze Fensterhoehe. Bliebe die Seite dort eine
 * feste Spalte, bekaeme die Liste ein paar Zeilen und der Text liesse sich
 * nicht mehr wegrollen, weil das Fenster selbst nicht rollt. Also rollt
 * hier wieder die Seite, und die Liste laeuft in voller Laenge durch. */
@media (max-width: 720px) {
  body.setupseite {
    height: auto;
    overflow: visible;
    display: block;
  }
  body.setupseite > main,
  body.setupseite > main > .panel { display: block; }
  body.setupseite .setupliste { max-height: 70dvh; }
}
.setupliste:has(> p) { border: none; }
table.setups thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--panel);
}
/* Der Name traegt die Zeile, der Rest ist Beiwerk. Ohne diese Breite teilen
   sich sechs Spalten den Platz gleichmaessig, und ausgerechnet der Name
   bekommt am wenigsten. */
table.setups td:first-child { white-space: normal; min-width: 12rem; }
/* Die Knopfspalte: Knopf, Knopf, Meldung. Die Meldung darf umbrechen, die
   Knoepfe nicht, und sie soll die Spalte nicht bei jedem Vorgang aufblaehen. */
table.setups td.tat {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  white-space: normal;
}
table.setups td.tat button { padding: var(--space-1) var(--space-3); font-size: 0.82rem; }
table.setups td.tat > span { flex: 1 1 8rem; min-width: 0; }

/* --- Die Aktionsspalte als Sinnbilder (#103) -----------------------------
 *
 * Die Knoepfe tragen `ikon` und holen sich Form und Strichstaerke von dort
 * (siehe `button.ikon` weiter oben). Hier steht nur, was die Tabelle daran
 * anders braucht: die Zeilenhoehe soll nicht wachsen, also etwas kleiner als
 * an der Karte, und die Regel eine Zeile darueber setzt sonst wieder ein
 * waagrechtes Polster, das aus dem Quadrat ein Rechteck machen wuerde. */
table.setups td.tat button.ikon {
  padding: 0;
  width: 1.75rem;
  height: 1.75rem;
  flex: 0 0 auto;
}
table.setups td.tat button.ikon svg { width: 0.95rem; height: 0.95rem; }
/* Loeschen ist die einzige Aktion ohne Rueckweg. Ohne Beschriftung braucht sie
   deshalb ein zweites Merkmal ausser dem Papierkorb, sonst sieht sie aus wie
   Duplizieren daneben. Nur die Farbe, kein gefuellter roter Kasten: die Zeile
   soll warnen, nicht schreien. */
table.setups td.tat button.ikon.gefahr { color: var(--bad); }
table.setups td.tat button.ikon.gefahr:hover {
  border-color: var(--bad);
  background: color-mix(in srgb, var(--bad) 16%, var(--panel-2));
}
/* Am schmalen Schirm werden die Knoepfe groesser, nicht kleiner. Ein Ziel von
   1.75rem trifft man mit der Maus, mit dem Finger nicht: unter 2.75rem faengt
   das Danebentippen an, und vier Ziele nebeneinander verzeihen das nicht. */
@media (max-width: 720px) {
  /* Die Breite muss dazugesagt werden. Die Zelle ist ein Flexkasten in einer
     Tabelle, die am Handy waagrecht rollt: ohne Untergrenze schrumpft sie auf
     das, was die Tabelle uebrig laesst, und die vier Ziele brechen auf drei
     Reihen um. Das hat die Zeile am Pruefstand auf 219 px aufgeblaeht. 15rem
     fasst vier Ziele (4 x 2.75rem), ihre drei Abstaende und das Zellpolster
     in einer Reihe; bei 13rem rutschte der Papierkorb noch in die zweite. */
  table.setups td.tat { gap: var(--space-2); min-width: 15rem; }
  table.setups td.tat button.ikon { width: 2.75rem; height: 2.75rem; }
  table.setups td.tat button.ikon svg { width: 1.2rem; height: 1.2rem; }
}

/* Die Statusspalte (#72) traegt zwei Auskuenfte untereinander: ob die Datei
   mit dem Serverstand synchron ist (#75) und ob das Setup am verwalteten
   Platz bereitliegt (#69). Untereinander und nicht nebeneinander, damit die
   Spalte eine feste Breite behaelt, auch wenn eine der beiden Zeilen fehlt,
   weil der PC nicht geantwortet hat. */
table.setups td.setupstatus { white-space: normal; line-height: 1.35; }

/* Die sortierbaren Spaltentitel (#78) brauchen hier keine eigenen Regeln:
   die Setup-Tabelle traegt `laps setups`, und `table.laps th.sortierbar`
   weiter unten (Bestzeiten) bringt Zeiger, Farbe und Pfeil schon mit. Zwei
   Fassungen desselben Aussehens waeren nur eine Gelegenheit, sie
   auseinanderlaufen zu lassen. */

/* Das aktive Setup (#79).
 *
 * Zwei Zeichen fuer denselben Zustand, und beide sind noetig: die Marke sagt
 * beim Lesen der Zeile, was Sache ist, der Streifen links findet die Zeile in
 * einer langen Liste, ohne dass man jede Statusspalte absucht. Der Hintergrund
 * bleibt dezent: die Zeile soll auffindbar sein, nicht schreien. */
table.setups tr.aktivzeile td { background: color-mix(in srgb, var(--good) 12%, transparent); }
table.setups tr.aktivzeile td:first-child { box-shadow: inset 3px 0 0 var(--good); }
.aktivmarke {
  display: inline-block;
  padding: var(--space-1) var(--space-2);
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--good);
  border: 1px solid color-mix(in srgb, var(--good) 55%, transparent);
  background: color-mix(in srgb, var(--good) 14%, transparent);
}

/* --- Gruppierte Rundenliste ------------------------------------------- */

/* Ein Spaltenraster für die ganze Tabelle, nicht je Gruppe eines.
 *
 * Die Rundenzeilen entstehen erst beim Aufklappen einer Fahrergruppe. Bei
 * automatischer Spaltenberechnung verschob jede geöffnete Gruppe damit die
 * Spalten der **gesamten** Tabelle, auch die der Gruppen darüber, die man
 * gar nicht angefasst hatte. Die Breiten stehen deshalb fest (colgroup in
 * renderLaps), und der Browser hält sich daran.
 *
 * Feste Breiten heissen zwingend abgeschnittener Text: ein zu langer
 * Fahrzeugname darf seine Spalte nicht aufdrücken, er endet mit drei
 * Punkten. Die Breiten sind so gewählt, dass das im Alltag nicht vorkommt;
 * eine wandernde Spalte wäre der schlechtere Handel. */
/* Die Summe der festen Spalten plus ein brauchbarer Rest für den Namen.
 *
 * Ohne diese Untergrenze schrumpft in einem schmalen Fenster ausgerechnet die
 * eine Spalte ohne feste Breite auf null, und der Fahrername steht als
 * «Max Gi...» da. Der Name ist die Beschriftung der Zeile; von allem in
 * dieser Tabelle ist er das Letzte, was gekürzt werden darf.
 *
 * Unterhalb dieser Breite schiebt der Bereich deshalb waagrecht. Eine
 * Bildlaufleiste ist sichtbar und erklärt sich, ein gekürzter Name nicht.
 * Pitwall steht ohnehin auf einem Zweitmonitor; ab etwa 1500 Pixeln
 * Fensterbreite kommt sie gar nicht erst vor. */
#laps { overflow-x: auto; }
/* Fusszeile unter der Sessionliste: Nachladen-Knopf oder Hinweis, dass alles
   geladen ist, siehe #277. */
.weitere-laden { margin-top: var(--space-3); }
.weitere-laden.dim { font-size: 0.82rem; }
/* Breite fest statt Mindestbreite (#300): vorher hatte die Namensspalte
 * keine eigene Breite und sog sich mit "Rest" jeden Platz an, der auf einem
 * breiten Schirm über die anderen, alle fest gesetzten Spalten hinausging.
 * Ein Fahrername oder ein Streckenname braucht das nicht. Jetzt haben alle
 * Spalten aus SPALTEN_BREITEN (analyse.js) eine feste Breite, und die Summe
 * ergab die 84rem, auf die die Tabelle bis #307 selbst fest stand: auf
 * breiten Schirmen blieb sie so breit, egal wie viel Platz das Panel daneben
 * noch hätte.
 *
 * Seit #307 (Wunsch Max) soll die Tabelle stattdessen die volle verfügbare
 * Breite ausnutzen. `width: 84rem` wird darum zu `width: 100%`, und
 * SPALTEN_BREITEN steht seither in Prozent statt in rem, in genau denselben
 * Verhältnissen wie zuvor: alle Spalten wachsen dadurch gleichmässig mit,
 * die Fahrerspalte bleibt anteilig so schmal wie seit #300. `min-width: 84rem`
 * bewahrt das Verhalten auf schmalen Schirmen unverändert: darunter greift
 * weiterhin `#laps { overflow-x: auto }`, statt Spalten bis zur Unlesbarkeit
 * zu stauchen. */
table.laps.gruppiert { table-layout: fixed; width: 100%; min-width: 84rem; }
table.laps.gruppiert td, table.laps.gruppiert th {
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Die Sessionliste trägt vierzehn Spalten. Mit dem Aussenabstand der übrigen
   Tabellen wäre sie deutlich breiter als der Bildschirm; enger gesetzt passt
   sie und wirkt zugleich ruhiger. */
table.laps.gruppiert td, table.laps.gruppiert th { padding-left: var(--space-2); padding-right: var(--space-2); }

/* --- Die Gruppenköpfe im selben Spaltenraster -------------------------- *
 *
 * Session- und Fahrerkopf waren früher je eine einzige Zelle über die ganze
 * Breite mit einem Flexkasten darin. Ihre Angaben rutschten damit je nach
 * Textlänge an eine andere Stelle, und keine zwei Sessionköpfe standen
 * untereinander. Jetzt sind es echte Zellen des einen Rasters, siehe
 * SPALTEN_BREITEN in analyse.js. Sie werden deshalb genau wie Rundenzellen
 * behandelt: eine Zeile, gekürzt statt umgebrochen. Ein Umbruch würde die
 * Zeilenhöhe verändern und damit die ganze Liste unruhig machen. */
table.laps tr.gruppe td {
  border-bottom: none;
  white-space: nowrap;
  vertical-align: middle;
}
table.laps tr.gruppe:hover td { background: var(--panel-2); }
table.laps tr.gruppe span { margin-left: 0; font-weight: 400; }

/* Beide Gruppenebenen sind zuklappbar und starten zugeklappt. */
table.laps tr.gruppe { cursor: pointer; user-select: none; }
/* Die Kappe hat eine eigene, schmale Spalte ganz links. Sie ist damit die
   einzige Spalte, in der die Gruppenebene sichtbar wird, und der Grund,
   warum die Rundenzeilen eingerückt wirken, ohne dass eine Angabe
   verschoben wäre. */
table.laps tr.gruppe td.kappenzelle { padding-left: 0; padding-right: 0; text-align: center; }
/* Streckenzustand, seit #300 allein in der Spalte: die Single-/Multiplayer-
 * Kennung, die sich das bis dahin mit ihr teilte, ist entfallen. */
table.laps tr.gruppe td.umstaende { display: table-cell; }
table.laps tr.gruppe .kappe {
  color: var(--dim);
  font-size: 0.8rem;
  display: inline-block;
  width: 0.8rem;
}
table.laps tr.gruppe.offen .kappe { color: var(--fg); }

/* Bestzeit im Gruppenkopf: auch zugeklappt sichtbar und direkt anwählbar,
   damit man für den häufigsten Vergleich keine Gruppe aufmachen muss. Sie
   steht in derselben Spalte wie die Wahlmarke der Rundenzeilen, also in der
   Spalte, die überall sagt, was im Vergleich liegt. */
table.laps tr.gruppe td.bestzelle { overflow: visible; }
button.bestzeit {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  max-width: 100%;
  padding: var(--space-1) var(--space-2);
  font-size: 0.85rem;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--panel-2);
  color: var(--fg);
  cursor: pointer;
}
button.bestzeit:hover { border-color: var(--accent); }
/* Dieselbe Rennfarben-Konvention wie in den Zeilen darunter: violett die
   schnellste Runde der Session, gruen die schnellste eines Fahrers.
   Eingefaerbt wird nur die Zahl, die Auswahlmarkierung des Knopfes (Rahmen
   und Balken links) bleibt davon unberuehrt und dominant. */
/* Dieselbe Violett-Konvention wie #287 (var(--violett, #c792ea) statt eines
 * eigenen Tons wie hier vorher, #300). */
button.bestzeit.best-session .mono { color: var(--violett, #c792ea); font-weight: 700; }
button.bestzeit.best-fahrer .mono { color: var(--good); font-weight: 700; }
button.bestzeit.ohnekurve { cursor: help; color: var(--dim); }
button.bestzeit.gewaehlt {
  border-color: var(--wahl);
  box-shadow: inset 3px 0 0 var(--wahl);
}
button.bestzeit .wahlmarke {
  margin-left: 0;
  color: var(--wahl);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 700;
}
button.bestzeit .wahlmarke:empty { display: none; }

/* Die Session ist die obere Ebene und bekommt die kräftigere Abgrenzung:
   eine Linie darüber, Luft davor und den Streckennamen in der Akzentfarbe.
   Alles Weitere daneben ist Beiwerk und bleibt gedämpft. */
table.laps tr.gruppe.session td {
  border-top: 1px solid var(--line);
  padding-top: var(--space-5);
  padding-bottom: var(--space-2);
  font-size: 0.95rem;
}
table.laps tr.gruppe.session b { color: var(--accent); font-size: 1rem; }
table.laps tr.gruppe.session .typ {
  background: var(--panel-2);
  border-radius: 4px;
  padding: var(--space-1) var(--space-2);
  font-size: 0.76rem;
  color: var(--dim);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
/* Sessiontypen farblich unterscheiden (#300, Entscheid Max): Qualifying,
 * Race und Hotstint bekommen je eine eigene Farbe, alles andere (Practice,
 * Superpole, Hotlap, HotlapSuperpole, Replay) bleibt beim gedämpften
 * Grundton oben, der reicht für "keiner der drei wichtigen".
 *
 * Rot, Weiß, Grün und Violett sind für die Zeitfarben aus #287 reserviert
 * (Gültigkeit und Bestzeiten, direkt in dieser Tabelle: tr.invalid td.zeit,
 * tr.lap.sessionbest/.fahrerbest td.zeit weiter unten) - dieselben Farben
 * hier zu verwenden wäre in derselben Tabelle doppeldeutig, genau die
 * Schwierigkeit, vor der dieser Punkt im Auftrag ausdrücklich warnt. Frei
 * bleiben aus der bestehenden Palette nur Amber und Blau; für die dritte,
 * Hotstint, kommt ein Türkis dazu, im selben gedämpften, mittelhellen Ton
 * wie die übrigen Akzentfarben der App, aber neu (--teal ist dieselbe Art
 * Variable-mit-Fallback wie --violett in #287, nirgends in :root
 * deklariert). */
table.laps tr.gruppe.session .typ.st-qualifying { color: var(--info); }
table.laps tr.gruppe.session .typ.st-race { color: var(--accent); }
table.laps tr.gruppe.session .typ.st-hotstint { color: var(--teal, #2dd4bf); }

/* Untergruppe: der Fahrer, mit seinem In-Game-Namen. Eine Stufe leiser als
   die Session.
 *
 * Eingerückt wird ausdrücklich nur die Kappe und nicht die ganze Zeile. Eine
 * Einrückung über alle Zellen würde jede Angabe des Fahrerkopfs um denselben
 * Betrag aus ihrer Spalte schieben, und damit wäre genau das kaputt, was
 * dieses Raster herstellt. Die Hierarchie trägt die Kappe, die Schriftgrösse
 * und die Linie über der Session. */
table.laps tr.gruppe.fahrer td {
  padding-top: var(--space-3);
  padding-bottom: var(--space-2);
  font-size: 0.88rem;
}
table.laps tr.gruppe.fahrer td.kappenzelle { padding-left: var(--space-2); }
table.laps tr.gruppe.fahrer b { font-size: 0.92rem; }
table.laps tr.gruppe.fahrer .konto {
  color: var(--good);
  font-size: 0.76rem;
  border: 1px solid color-mix(in srgb, var(--good) 45%, transparent);
  border-radius: 999px;
  padding: var(--space-1) var(--space-2);
}

/* Berührungszähler hinter dem Fahrernamen. Bewusst klein und ohne Fläche:
   er soll auffindbar sein, wenn man ihn sucht, und nicht mit der Bestzeit um
   Aufmerksamkeit ringen. Er erscheint nur bei mehr als null, siehe
   unfallMarke() in analyse.js. */
table.laps tr.gruppe.fahrer .unfaelle {
  margin-left: var(--space-2);
  font-size: 0.72rem;
  color: var(--accent);
  white-space: nowrap;
  cursor: help;
}
table.laps tr.gruppe.fahrer .unfaelle .zahl {
  margin-left: var(--space-1);
  font-variant-numeric: tabular-nums;
}

/* Die ganze Zeile ist der Schalter für den Vergleich */
table.laps tr.lap { cursor: pointer; }
/* Rundenzeilen stehen unter ihrem Fahrer, also auch optisch eingerückt. In
   der gruppierten Liste besorgt das die leere Kappenspalte ganz links; nur
   die Bestzeitentabelle daneben braucht die Einrückung noch als Abstand. */
table.laps.rekorde tr.lap td:first-child { padding-left: var(--space-5); }
table.laps tr.lap td.marke {
  font-size: 0.75rem;
  color: var(--dim);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
}

/* Runde ohne Positionsspur: gedämpft, aber klar gekennzeichnet statt
   stumm unklickbar. Ein Klick darauf erklärt sich, siehe .lap-hinweis. */
table.laps tr.lap.ohnekurve { cursor: help; }
table.laps tr.lap.ohnekurve td { color: var(--dim); }

/* Abzeichen, eine Bauart für alle.
 *
 * Vorher hatte jedes seine eigene Grösse, Farbe und Füllung. In einer Liste
 * mit zweihundert Runden standen dadurch in jeder Zeile zwei kräftig
 * gefüllte Marken, und die Tabelle bestand mehr aus Abzeichen als aus
 * Zeiten. Grundform ist deshalb der ruhige Umriss; Farbe bekommt nur, was
 * eine Ausnahme meldet, also die ungültige Runde. Damit findet das Auge
 * genau das, was es finden soll. */
.badge {
  display: inline-block;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--dim);
  border-radius: 999px;
  padding: var(--space-1) var(--space-2);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: none;
  white-space: nowrap;
  font-weight: 600;
}
.badge + .badge { margin-left: var(--space-2); }

/* Ungültige Runden sind ausdrücklich vergleichbar, sie müssen aber überall
   als solche erkennbar bleiben, auch in der Legende des Vergleichsdiagramms. */
.badge.ungueltig {
  color: var(--bad);
  border-color: color-mix(in srgb, var(--bad) 45%, transparent);
  background: color-mix(in srgb, var(--bad) 10%, transparent);
}
#legend .badge.ungueltig, #compare-info .badge.ungueltig { margin-left: var(--space-2); }

/* Runde mit Fahrzeugkanälen aus dem Collector. Ein Zusatz, keine Warnung:
   nur der Rand nimmt einen Hauch der Akzentfarbe auf, gefüllt wird nichts. */
.badge.telemetrie {
  color: color-mix(in srgb, var(--accent) 35%, var(--dim));
  border-color: color-mix(in srgb, var(--accent) 25%, var(--line));
}

/* Ersatzkennung für den Server (#302): ein knapper Code neben dem
   Streckennamen im Sessionkopf, statt einer eigenen Spalte (#300 hat die
   Tabelle bewusst schmaler gemacht). Bewusst gedämpft wie telemetrie: es ist
   eine Näherung, kein Servername, siehe server/serverkennung.js. */
.badge.server {
  margin-left: var(--space-2);
  letter-spacing: 0.08em;
  color: color-mix(in srgb, var(--info) 45%, var(--dim));
  border-color: color-mix(in srgb, var(--info) 30%, var(--line));
}

/* Von Hand gesetzter Servername (#308): ein echter Name statt eines
 * Vier-Zeichen-Codes, deshalb ohne dessen weite Laufweite und in normaler
 * Textfarbe statt gedaempft. Er ist die sicherere Auskunft als die
 * automatische Ersatzkennung daneben, siehe SPALTEN_BREITEN-Kommentar in
 * analyse.js zur Reihenfolge der beiden. */
.badge.server.manual {
  letter-spacing: 0.01em;
  color: var(--fg);
  border-color: color-mix(in srgb, var(--info) 45%, var(--line));
  background: color-mix(in srgb, var(--info) 10%, transparent);
}

/* Setup-Herkunft (#302). "pitwall" (kein Widerspruch zum zuletzt aktivierten
   Setup) bleibt so gedämpft wie telemetrie; "spiel" (TC/TC-Cut/ABS weichen
   ab) bekommt dieselbe Warnfarbe wie eine ungültige Runde, aus demselben
   Grund: eine Erwartung stimmt hier nicht. */
.badge.setup.pitwall {
  color: color-mix(in srgb, var(--good) 40%, var(--dim));
  border-color: color-mix(in srgb, var(--good) 25%, var(--line));
}
.badge.setup.spiel {
  color: var(--bad);
  border-color: color-mix(in srgb, var(--bad) 45%, transparent);
  background: color-mix(in srgb, var(--bad) 10%, transparent);
}

/* Bremspunktvergleich: früher gebremst ist nicht automatisch besser, deshalb
   keine Wertung in Gut und Schlecht, nur zwei unterscheidbare Richtungen. */
td.frueher { color: #4aa8ff; }
td.spaeter { color: #ffb400; }

/* Kurzes Aufblitzen der angeklickten Zeile, damit der Hinweis oben
   erkennbar zu dieser Zeile gehört */
@keyframes lapblitz {
  from { background: rgba(74, 168, 255, 0.35); }
  to { background: transparent; }
}
table.laps tr.lap.blitz td { animation: lapblitz 1.1s ease-out; }

.lap-hinweis {
  display: none;
  border-left: 3px solid var(--info);
  background: var(--panel-2);
  border-radius: 0 6px 6px 0;
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
  font-size: 0.9rem;
  line-height: 1.45;
}
.lap-hinweis.sichtbar { display: block; }

/* Angewählte Runde in der Farbe ihrer Vergleichskurve. Der Balken links
   trägt die Erkennung, die Fläche darf deshalb zurückhaltend bleiben:
   eine kräftig eingefärbte Zeile überstrahlt die Zahlen, die man in ihr
   lesen will. */
table.laps tr.lap.gewaehlt td {
  background: color-mix(in srgb, var(--wahl) 13%, transparent);
}
table.laps tr.lap.gewaehlt:hover td {
  background: color-mix(in srgb, var(--wahl) 20%, transparent);
}
table.laps tr.lap.gewaehlt td:first-child {
  box-shadow: inset 4px 0 0 0 var(--wahl);
}
table.laps tr.lap.gewaehlt td.marke { color: var(--wahl); font-weight: 700; }

/* --- Bestzeiten: eine Tabelle je Strecke, Spalten sortierbar ------------ */

table.laps.rekorde tr.lap { cursor: pointer; }
table.laps.rekorde tr.lap.ohnekurve { cursor: help; }
/* Die erste Spalte traegt im Kopf den Streckennamen. Er ist die Ueberschrift
   der Tabelle und darf sich vom Rest abheben. */
table.laps.rekorde thead th:first-child { color: var(--fg); font-size: 0.8rem; }

table.laps th.sortierbar {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
table.laps th.sortierbar:hover { color: var(--fg); }
/* Die aktive Sortierspalte traegt Farbe und Pfeil. Beides zusammen, nicht
   nur der Pfeil: bei zehn Spalten sucht man einen einzelnen Pfeil zu lange. */
table.laps th.sortierbar.sortiert { color: var(--accent); }
table.laps th.sortierbar .pfeil { margin-left: var(--space-1); font-size: 0.7em; }

/* Bestzeiten in der Rundenliste, in der Rennfarben-Konvention.
 *
 * Violett ist die schnellste Runde der ganzen Session, gruen die schnellste
 * des jeweiligen Fahrers. Faellt beides zusammen, gewinnt violett, das
 * entscheidet analyse.js beim Setzen der Klasse.
 *
 * Eingefaerbt wird nur die Zahl und nicht die Zeile. Die Flaeche gehoert der
 * Vergleichsauswahl, die muss dominant bleiben und von dieser Auszeichnung
 * unterscheidbar. Derselbe Ton wie im Live-Timing, damit violett ueberall
 * dasselbe heisst - seit #300 auch derselbe Farbwert wie in #287
 * (var(--violett, #c792ea) statt eines eigenen Tons). */
table.laps tr.lap.sessionbest td.zeit { color: var(--violett, #c792ea); font-weight: 700; }
table.laps tr.lap.fahrerbest td.zeit { color: var(--good); font-weight: 700; }
/* Bis #300 gab es hier noch tr.invalid.sessionbest/.fahrerbest, weil
 * schnellste() in analyse.js notfalls auch eine ungueltige Runde als
 * Bestzeit lieferte. Seit Max' Entscheid "eine Bestzeit ist immer eine
 * gueltige Runde" liefert schnellste() nie mehr beides zugleich, die beiden
 * Regeln koennen nicht mehr zusammentreffen, die Zusatzregeln sind
 * ersatzlos entfallen statt als toter Code stehen zu bleiben. */

/* Ein Abstand fuer alle Flaechen, nicht drei verschiedene.
 *
 * Die senkrechten Abstaende waren gewachsen: Panels mit 1.2rem darunter,
 * Ueberschriften mit 0.8rem, einzelne Bloecke mit eigenen Randangaben im
 * style-Attribut. Zusammen ergab das eine Seite, die an jeder Stelle anders
 * atmet. Jetzt gilt ueberall dasselbe Mass, und Ausnahmen stehen mit
 * Begruendung daneben. */
.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-4);
}
.panel:last-child { margin-bottom: 0; }
.panel h2 {
  margin: 0 0 var(--space-3);
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--dim);
}

.chart { width: 100%; }

.aliasrow { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-bottom: var(--space-2); }
.aliasrow b { min-width: 5rem; }

.panel h3 {
  margin: var(--space-5) 0 var(--space-2);
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--dim);
}
.panel h3:first-of-type { margin-top: var(--space-2); }

/* Zugeklappter Bereich, etwa die Liste der Fahrer ohne Konto */
details.aufklapper { margin: var(--space-2) 0 var(--space-1); }
details.aufklapper > summary {
  cursor: pointer;
  user-select: none;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--dim);
  padding: var(--space-2) 0;
}
details.aufklapper > summary:hover { color: var(--fg); }
details.aufklapper > summary .dim { text-transform: none; letter-spacing: 0; }
details.aufklapper[open] > summary { color: var(--fg); margin-bottom: var(--space-2); }

/* Fahrer ohne Konto, je Zeile ein Zuordnungsfeld. Das ersetzt das frühere
   Auswahlfeld in jeder einzelnen Rundenzeile. */
.offenrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--line);
}
.offenrow:last-child { border-bottom: none; }
.offenrow b { min-width: 14rem; }
.offenrow .dim { min-width: 6rem; font-size: 0.85rem; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: var(--space-1) var(--space-2) var(--space-1) var(--space-3);
  font-size: 0.85rem;
}
.chip button {
  border: none;
  background: none;
  color: var(--dim);
  padding: 0 var(--space-1);
  font-size: 1rem;
  line-height: 1;
}
.chip button:hover { color: var(--bad); }

/* Eine Session je Zeile, mit ihrem Umfang und dem Loeschknopf ganz rechts.
   Der Knopf steht bewusst am Zeilenende und nicht neben dem Streckennamen:
   so liegt er nirgends dort, wo man beim Lesen ohnehin hinklickt. */
.sessionrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.9rem;
}
.sessionrow:last-child { border-bottom: none; }
.sessionrow b { min-width: 12rem; }
.sessionrow .spacer { flex: 1; }
.sessionrow .zahl { font-variant-numeric: tabular-nums; }
.sessionrow .typ {
  background: var(--panel-2);
  border-radius: 4px;
  padding: var(--space-1) var(--space-2);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--dim);
}
.sessionrow .laeuft {
  color: var(--good);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
button.gefaehrlich { color: var(--dim); }
button.gefaehrlich:hover { border-color: var(--bad); color: var(--bad); }

.legend { display: flex; gap: var(--space-4); flex-wrap: wrap; margin-bottom: var(--space-3); font-size: 0.9rem; }
.legend span i { display: inline-block; width: 12px; height: 12px; border-radius: 2px; margin-right: var(--space-2); vertical-align: -1px; }

/* --- Anmeldung ---------------------------------------------------------- */

.login-wrap { max-width: 340px; margin: 12vh auto; }
.login-wrap .card { padding: var(--space-5); }
.login-wrap label { display: block; margin: var(--space-3) 0 var(--space-2); color: var(--dim); font-size: 0.85rem; }
.login-wrap input, .login-wrap select { width: 100%; }
.login-wrap button { width: 100%; margin-top: var(--space-5); }
.error { color: var(--bad); margin-top: var(--space-3); min-height: 1.2em; }

/* uPlot an das dunkle Aussehen anpassen */
.u-legend { color: var(--fg); font-size: 0.85rem; }
.u-title { color: var(--fg); }

/* --- Kanalwahl und Wiedergabe in der Analyse --------------------------- */

.kanalwahl {
  display: flex;
  align-items: center;
  /* Dichter als vorher: die Umschalter sind jetzt Pillen und tragen ihren
     Rand selbst, ein Zentimeter Luft dazwischen liess sie auseinanderfallen. */
  gap: var(--space-2);
  flex-wrap: wrap;
  margin: var(--space-1) 0 var(--space-3);
  font-size: 0.9rem;
}

/* Runde/Rennen-Wahl fuer den Tankinhalt (#226): eine Unterwahl direkt hinter
   der Sprit-Pille, kein eigener Block. Etwas dichter und kleiner als die
   Zusatzkanaele selbst, damit sichtbar bleibt, dass sie zu "Sprit" gehoert
   und keine eigenstaendige Zuschaltung ist. */
.tankmodus { display: inline-flex; gap: var(--space-2); margin-left: -0.15rem; }
.tankmodus .pille { padding: var(--space-1) var(--space-3); font-size: 0.78rem; }

/* Wiedergabe: Bedienleiste, Streckenkarte, Zahlenstand */
.playback { margin-bottom: var(--space-4); }
.pb-leiste {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
}
.pb-leiste .spacer { flex: 1; }
.pb-leiste button { padding: var(--space-1) var(--space-3); }
.pb-leiste button.pb-tempo.gewaehlt { border-color: var(--accent); color: var(--accent); font-weight: 700; }
.pb-uhr { font-size: 1.3rem; font-weight: 700; }

#pb-karte {
  display: block;
  width: 100%;
  height: 240px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
}

.pb-stand {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  margin-top: var(--space-2);
  font-size: 0.88rem;
}
.pb-stand .pb-zeile i {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  margin-right: var(--space-2);
}

/* Zeitcursor über den Diagrammen. Ein eigenes Element statt eines Strichs
   im Bild: so laesst er sich sechzigmal je Sekunde verschieben, ohne dass
   vier Diagramme neu gezeichnet werden muessen. */
.zeitcursor {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  margin-left: -1px;
  background: var(--accent);
  box-shadow: 0 0 6px rgba(255, 180, 0, 0.8);
  pointer-events: none;
  z-index: 5;
}
.u-over { cursor: crosshair; }

/* Streckenbedingung im Gruppenkopf der Analyse */
table.laps tr.gruppe .bedingung {
  margin-left: var(--space-4);
  color: var(--info);
  border: 1px solid color-mix(in srgb, var(--info) 45%, transparent);
  border-radius: 4px;
  padding: 0 var(--space-2);
  font-size: 0.8rem;
}

/* Reifentabelle: die vier Räder einer Runde bleiben als Block erkennbar */
table.laps.reifen td { padding-top: var(--space-1); padding-bottom: var(--space-1); }
table.laps.reifen td[rowspan] { vertical-align: top; font-weight: 600; padding-left: var(--space-3); }

/* Eingestellte Fahrhilfen in der Legende des Vergleichs */
.legend .hilfen {
  margin-left: var(--space-2);
  color: var(--dim);
  font-size: 0.78rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0 var(--space-2);
}

/* Zielflagge vor dem Fahrernamen: die Session ist für diesen Fahrer vorbei.
   Kein Emoji, sondern ein gezeichnetes Karomuster. Ein Emoji brächte die
   Farben seiner Schriftart mit, und auf einer dunklen Oberfläche neben
   lauter Grautönen ist das jedes Mal ein Fremdkörper. */
.fahrerzeile .zielflagge[hidden] { display: none; }

/* Der Punkt vor dem Namen sagt den Zustand, nicht den Fahrer. Grau ist der
   Normalfall und tritt bewusst zurueck: in einem Feld von dreissig Wagen
   sind neunundzwanzig unauffaellig, und wenn alle bunt sind, faellt der
   eine, auf den es ankommt, nicht mehr auf. Dieselben Farben wie auf der
   Karte, siehe ZUSTAND_FARBE in map.js. */
.fahrerzeile i.dot.z-normal { background: #9aa7b4; }
.fahrerzeile i.dot.z-ueberrundet { background: #4aa8ff; }
.fahrerzeile i.dot.z-gelb { background: #ffd23f; }
.fahrerzeile i.dot.z-rot { background: #ff4d5e; }
/* Der Unfallmelder ist nicht die Gelbphase, sondern ihr mutmasslicher
   Grund. Deshalb dieselbe Farbe, aber als Ring statt als Fleck: gemessene
   Flagge und abgeleitete Vermutung sollen sich unterscheiden lassen. */
.fahrerzeile i.dot.z-unfall {
  background: transparent;
  box-shadow: inset 0 0 0 3px #ffd23f;
}

/* Die Flagge sitzt an der Stelle des Farbtupfers und ist deshalb eckig
   statt rund. Sie darf gerne gross wirken: am Rennende ist «ist der schon
   durch?» die haeufigste Frage, und das alte Fleckchen von zehn mal acht
   Pixeln war auf einem Zweitmonitor in zwei Metern nicht zu sehen. */
.fahrerzeile .dot.zielflagge {
  border-radius: 2px;
  outline: 1px solid var(--line);
  background-color: #101720;
  background-image:
    linear-gradient(45deg, var(--fg) 25%, transparent 25%, transparent 75%, var(--fg) 75%),
    linear-gradient(45deg, var(--fg) 25%, transparent 25%, transparent 75%, var(--fg) 75%);
  background-size: 6px 6px;
  background-position: 0 0, 3px 3px;
}

/* Die Flaggenlage stand hier als Chip in der Sessionleiste, dann (#219) als
   eigene Zeile. Mit #415 faerbt sie stattdessen den Kachelrahmen (`.mapwrap`
   weiter oben). */

/* --- Sessionende ---------------------------------------------------------
 *
 * Der kleine Chip in der Sessionleiste ist mit #156 entfallen, es gab ihn
 * nur zusaetzlich zur grossen Uhr im Titelbereich. Uebrig bleibt sein
 * Zielflaggen-Karo, das die Uhr weiterhin traegt. */
.mapbar .mapuhr .karo {
  display: inline-block;
  width: 14px;
  height: 11px;
  border-radius: 2px;
  background-color: #101720;
  background-image:
    linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%),
    linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%);
  background-size: 6px 6px;
  background-position: 0 0, 3px 3px;
}

/* --- Podium --------------------------------------------------------------
 *
 * Nur die Positionszahl, nicht die ganze Zeile. Gold, Silber und Bronze auf
 * drei vollen Zeilen machten aus einer Ergebnisliste eine Urkunde, und
 * ausgerechnet die drei wichtigsten Zeilen waeren dann die am schlechtesten
 * lesbaren. */
.fahrerzeile .rang.podium {
  border-radius: 4px;
  padding: 0 var(--space-2);
  font-weight: 700;
  color: #101720;
}
.fahrerzeile .rang.podium.p1 { background: linear-gradient(180deg, #ffe082, #d4a017); }
.fahrerzeile .rang.podium.p2 { background: linear-gradient(180deg, #e6e9ee, #a8b0ba); }
.fahrerzeile .rang.podium.p3 { background: linear-gradient(180deg, #e0a878, #a9713c); }


/* --- Filter als Umschaltpillen ------------------------------------------
 *
 * Vorher standen dort blanke Systemkontrollkästchen. Sie sind auf einer
 * dunklen Oberfläche hellgrau und quadratisch, also genau das, was sonst
 * nirgends auf dieser Seite vorkommt, und ihr Zustand war aus zwei Metern
 * nicht zu erkennen.
 *
 * Eine Pille zeigt ihren Zustand mit der ganzen Fläche statt mit einem
 * Häkchen von zwölf Pixeln. Das Kästchen bleibt im Dokument und bleibt
 * bedienbar: es trägt weiterhin die ID, an der das Skript hängt, und die
 * Tastaturbedienung geht unverändert. */
.pille {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel-2);
  color: var(--dim);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  font-size: 0.88rem;
}
.pille:hover { border-color: var(--accent); color: var(--fg); }
/* Das Kästchen selbst verschwindet aus dem Bild, nicht aus dem Dokument:
   ausgeblendet mit display:none wäre es auch für die Tastatur weg. */
.pille input { position: absolute; opacity: 0; width: 0; height: 0; }
.pille:has(input:checked) {
  background: color-mix(in srgb, var(--accent) 18%, var(--panel-2));
  border-color: var(--accent);
  color: var(--fg);
  font-weight: 600;
}
/* Sichtbarer Fokus, sonst wäre die Tastaturbedienung blind. */
.pille:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --- Bestätigungsdialog vor dem Aktivieren eines Setups (#292) -----------
 *
 * Ein Overlay über der ganzen Seite, egal welche gerade offen ist: der
 * Dialog kann von der Setup-Liste kommen oder von einem Wegpunkt der
 * Pre-Race-Prozedur, die auf jeder Seite mitläuft (siehe zeigeSetupBestaetigung
 * in common.js). */
.sb-overlay {
  position: fixed;
  inset: 0;
  background: rgba(8, 10, 13, 0.72);
  display: flex;
  align-items: center;
  justify-content: center;
  /* Bewusst ueber der Kopfnavigation (z-index 50): ein Bestaetigungsdialog
     soll die ganze Seite blockieren, Navigation eingeschlossen, siehe
     tools/selftest.js, Abschnitt "Geklebte Kopfnavigation". */
  z-index: 100;
  padding: var(--space-4);
}
.sb-karte {
  width: 100%;
  max-width: 22rem;
  margin: 0;
}
.sb-karte h2 { margin: 0 0 var(--space-2); font-size: 1rem; }
.sb-druck {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2) var(--space-4);
  margin: var(--space-3) 0;
}
.sb-zeile {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-1) 0;
}
.sb-zeile:not(.sb-druck .sb-zeile) {
  border-top: 1px solid var(--line);
  margin-top: var(--space-2);
  padding-top: var(--space-3);
}
.sb-knoepfe {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

/* --- Einheitliche Tooltips (#827) ------------------------------------------
 *
 * Ersetzt den browsereigenen `title`-Tooltip durch einen eigenen, dunklen,
 * mehrzeiligen Kasten, siehe die Mechanik in common.js. Bewusst ueber dem
 * Bestaetigungsdialog (z-index 100): eine Erklaerung darin (z.B. die
 * Restzeit-Zeilen) muss lesbar bleiben, auch waehrend der Dialog offen ist. */
.pw-tooltip {
  position: fixed;
  z-index: 200;
  max-width: 22rem;
  padding: var(--space-2) var(--space-3);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--fg);
  font-size: 0.82rem;
  line-height: 1.4;
  white-space: pre-line;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.55);
  pointer-events: none;
}
