/*
 * Design-System von verkehrslage.de
 *
 * Handgeschrieben und ohne Build-Schritt. Der Grund ist eine
 * Abhängigkeitsentscheidung: das Aussehen der Seiten darf nicht davon
 * abhängen, dass jemand vorher npm ausgeführt hat. Die Templates liegen in Go,
 * und ein Utility-Framework würde bei jeder Templateänderung einen
 * CSS-Neubau erfordern – sonst fehlen Klassen stillschweigend.
 *
 * Mobile-first: alle Grundregeln gelten für kleine Bildschirme, Erweiterungen
 * stehen in Media-Queries. Es gibt genau zwei Sprungpunkte, und sie sind an
 * den Inhalt gebunden, nicht an Gerätenamen:
 *
 *   ab 62rem  Inhalt und Kontextspalte liegen nebeneinander (zwei Spalten),
 *             der Schnellzugriff bleibt ein Streifen über beiden.
 *   ab 82rem  der Schnellzugriff wird zur linken Spalte (drei Spalten).
 *
 * Unter 62rem ist alles eine Spalte. Der Schnellzugriff ist dort ein waagrecht
 * schiebbarer Streifen – aber nur zwischen 62rem und dem Ende dieser Datei:
 * Abschnitt 10 bricht ihn für das Telefon um und macht die Meldungsarten zum
 * Menü (D-098). Für den Bereich dazwischen bleibt der Streifen; auf 700 Pixeln
 * Breite ist er keine Zumutung, sondern eine Reihe mit sichtbarem Ende.
 *
 * Abschnitt 10 bricht mit dieser Ordnung: er steht in einer max-width-Abfrage
 * und gilt nur bis 61.98rem. Der Grund ist nicht Bequemlichkeit, sondern eine
 * Zusage – die Telefon-Ansicht wurde umgebaut, die Desktop-Ansicht dabei nicht
 * angetastet (D-097). In einer max-width-Abfrage ist das nachweisbar: keine
 * Basisregel wurde geändert, und jede neue Regel endet an der 62rem-Grenze.
 * Dieselbe Abfrage steht schon bei der Verwaltungsleiste in Abschnitt 8, aus
 * demselben Grund und mit demselben Wert.
 *
 * Aufbau: 1. Tokens  2. Grundlagen  3. Kopf und Fuß  4. Dreispaltiges Gerüst
 *         5. Bausteine  6. Die Bildsprache  7. Karte  8. Betrieb
 *         9. Bewegung und Druck  10. Telefon
 *
 * Das Farbbild: Schiefer (D-096). Grundton Graphit, Handlung Kiefer, dazu
 * die Statusfarben der Meldungsarten und eine vierstufige Reihe für den
 * Lagewert. Alle Werte stehen in den Tokens; kein Farbwert steht weiter unten
 * im Stylesheet, und keiner steht in einer Vorlage.
 */

/* ------------------------------------------------------------ 1. Tokens */

:root {
  /* Farben. Schiefer: Graphit ist die Website, Kiefer die Handlung, Kupfer
     die Ausnahme. Rot, Bernstein, Braun und leuchtendes Blau bleiben den
     Meldungsarten. Die Bedienung darf mit einer Meldung nicht verwechselt
     werden (D-096, docs/design.md).

     Grün heißt nicht frei. Kiefer ist nur Interaktion – Link, aktiver
     Schalter, Lupe – und grauer als die Panne. Die Rolle „ruhig" trägt
     Graphit, die Markenfarbe selbst. */
  --c-bg: #f3f4f2;
  --c-surface: #ffffff;
  --c-surface-2: #eaeeeb;
  --c-surface-3: #dde3df;
  --c-text: #1a1f1c;
  --c-muted: #5c6560;
  --c-border: #d8ddd9;
  --c-border-strong: #b4bcb6;

  --c-primary: #2c3545;
  --c-primary-strong: #1a212c;
  --c-primary-soft: #e8ecf0;
  --c-accent: #1f6b63;
  --c-accent-strong: #15564f;
  --c-accent-soft: #e3f2ef;

  /* Der Fokusring liegt bewusst außerhalb der Marke. Gold sitzt wegen
     outline-offset immer auf der Seitenfläche und nie auf dem Bedienelement.
     Mit einer Meldungsart ist es nicht zu verwechseln – die bernsteinfarbene
     Baustelle ist dunkler und steht nie als Ring um einen Schalter. */
  --c-focus: #d97706;

  /* Statusfarben je Meldungsart. Sie sind die einzige Stelle, an der die
     Zuordnung von Meldungsart zu Farbe festgelegt ist. Die Töne sind über den
     Farbkreis verteilt und nicht nur nach Helligkeit gestuft, damit sie auch
     bei Farbfehlsichtigkeit auseinandergehalten werden können.

     Geisterfahrer ist Wein, nicht mehr Magenta. Die Panne ist See, nicht
     Kiefer. Blitzer bleibt klares Blau, VWD Stahl – beide weg vom Graphit. */
  --c-tailback: #c81e1e;
  --c-roadworks: #c26a12;
  --c-accident: #b01040;
  --c-slippery: #8b7310;
  --c-view: #64748b;
  --c-objects: #8b5e34;
  --c-wrongway: #9d174d;
  --c-closure: #7f1d1d;
  --c-camera: #1864ab;
  --c-cluster: #475569;
  --c-vwd: #3d4f6f;

  /* Pannenfahrzeuge. Seeblau, damit die Handlung (Kiefer) und die Störung
     auseinanderliegen. Dunkel gehalten, damit es nicht als „freie Fahrt"
     gelesen wird. */
  --c-breakdown: #0e7490;

  /* Die vier Stufen des Lagewerts. Eigene Reihe, keine Meldungsfarbe als
     „ruhig". Kühl nach warm: Graphit, Stahl, Kupfer, Karmin. */
  --c-level-good: #2c3545;
  --c-level-normal: #3d5a80;
  --c-level-busy: #c45c26;
  --c-level-bad: #c0272d;

  /* Vorläufiger Ländertacho, solange zwei Vergleichswochen fehlen:
     Anteil am Höchststand, drei Bänder. */
  --c-peak-low: #2f8a4e;
  --c-peak-mid: #c9a227;
  --c-peak-high: #c0272d;

  /* Verläufe. Stein nach Kiefer, auf den wenigen Flächen der Marke:
     Markenzeichen, Kopfband, aktive Schalter. Der Schimmer und die Aurora
     bleiben so schwach, dass Text darauf seinen Kontrast behält. */
  --grad-brand: linear-gradient(118deg, #1a212c 0%, #2c3545 48%, #1f6b63 100%);
  --grad-tint: linear-gradient(150deg, rgb(44 53 69 / 7%) 0%, rgb(31 107 99 / 6%) 55%, rgb(255 255 255 / 0%) 100%);
  --grad-aurora:
    radial-gradient(62rem 24rem at 10% -8rem, rgb(31 107 99 / 10%), transparent 70%),
    radial-gradient(48rem 20rem at 88% -6rem, rgb(196 92 38 / 7%), transparent 70%);

  /* Abstände auf einer Vierer-Stufung. */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 0.75rem;
  --s-4: 1rem;
  --s-5: 1.5rem;
  --s-6: 2rem;
  --s-7: 3rem;

  /* Schriftgrößen wachsen mit dem Viewport, bleiben aber begrenzt. */
  --t-small: 0.8125rem;
  --t-body: 1rem;
  --t-lead: clamp(1.0625rem, 1rem + 0.4vw, 1.1875rem);
  --t-h3: clamp(1.0625rem, 1rem + 0.5vw, 1.25rem);
  --t-h2: clamp(1.1875rem, 1.05rem + 0.9vw, 1.5rem);
  --t-h1: clamp(1.5rem, 1.2rem + 2.2vw, 2.375rem);

  --radius: 0.875rem;
  --radius-sm: 0.5rem;
  --radius-lg: 1.375rem;
  --radius-pill: 999px;

  /* Schatten mit Farbstich statt reinem Schwarz: reines Schwarz wirkt auf
     dem steingrauen Grund schmutzig. Der Stich folgt der Tinte.

     Der Markenschatten liegt unter den Schaltflächen: eine Kiefer-Pille
     wirft kiefernes Licht. Ohne ihn sieht ein farbiger Knopf auf hellem Grund
     ausgeschnitten aus. */
  --shadow-sm: 0 1px 2px rgb(26 31 28 / 6%);
  --shadow: 0 1px 2px rgb(26 31 28 / 5%), 0 6px 18px -6px rgb(26 31 28 / 14%);
  --shadow-lg: 0 2px 4px rgb(26 31 28 / 7%), 0 18px 40px -12px rgb(26 31 28 / 22%);
  --shadow-brand: 0 8px 20px -8px rgb(31 107 99 / 40%);

  /* Breiten. Die Hülle ist so bemessen, dass bei drei Spalten die Mittelspalte
     noch etwa 50rem behält – die Breite, in der Meldungslisten lesbar sind. */
  --w-shell: 92rem;
  --w-prose: 44rem;
  --w-rail: 15rem;
  --w-aside: 20rem;
  --w-admin-nav: 15.25rem;

  /* Höhe der Kopfzeile. Die klebenden Seitenspalten brauchen sie als Abstand
     nach oben; ohne diesen Wert verschwinden ihre Überschriften darunter. */
  --h-header: 3.75rem;

  /* Die Maße der Telefon-Ansicht (Abschnitt 10). Sie stehen hier und nicht
     dort, weil der klebende Schnellzugriff die Kopfhöhe als Abstand braucht
     und die Tab-Leiste ihre eigene Höhe als Polster unter dem Fuß: dieselbe
     Zahl an drei Stellen, und keine davon darf raten.

     Der Kopf ist so hoch wie im Desktop, die Tab-Leiste etwas höher – sie
     trägt Symbol und Wort übereinander. */
  --h-header-phone: 3.75rem;
  --h-tabbar: 3.5rem;

  /* Die kleinste Fläche, die eine Fingerspitze zuverlässig trifft. 44 Pixel
     sind der Wert beider Plattformrichtlinien; ein Mauszeiger braucht ihn
     nicht, deshalb steht er nur in Abschnitt 10. */
  --tap: 2.75rem;

  /* Die Ränder, die das Gerät selbst beansprucht: Notch, abgerundete Ecken,
     Gestenbalken. Ohne viewport-fit=cover im Viewport-Meta sind alle vier
     null, und die Regeln fallen auf ihren Normalfall zurück. */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);

  /* Systemschrift statt einer nachgeladenen Datei. Keine Einbindung von
     Google Fonts: das überträgt bei jedem Seitenaufruf die IP-Adresse des
     Besuchers an einen Dritten. */
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-num: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;
}

/* Dunkelmodus. Von Anfang an vorgesehen, weil die Karte ohnehin zwei
   Kachelvarianten braucht. Die Statusfarben werden nicht nur aufgehellt,
   sondern entsättigt: volle Sättigung flimmert auf dunklem Grund. */
@media (prefers-color-scheme: dark) {
  :root {
    --c-bg: #0e1110;
    --c-surface: #161a19;
    --c-surface-2: #1c2220;
    --c-surface-3: #262d2a;
    --c-text: #e7ebe8;
    --c-muted: #9aa39d;
    --c-border: #2a322f;
    --c-border-strong: #3d4843;

    --c-primary: #8eb8b2;
    --c-primary-strong: #c5ddd9;
    --c-primary-soft: #1a2e2c;
    --c-accent: #e08a58;
    --c-accent-strong: #f0b08a;
    --c-accent-soft: #3a2418;
    --c-focus: #fbbf24;

    --c-tailback: #f87171;
    --c-roadworks: #f0a850;
    --c-accident: #fb7185;
    --c-slippery: #dcbb4d;
    --c-view: #a7b0cd;
    --c-objects: #d0a476;
    --c-wrongway: #e879a9;
    --c-closure: #f87171;
    --c-camera: #6aa8f5;
    --c-cluster: #aab2d0;
    --c-vwd: #9aa4cf;
    --c-breakdown: #5ccfe0;

    --c-level-good: #c5cdd6;
    --c-level-normal: #8eadd4;
    --c-level-busy: #f0a070;
    --c-level-bad: #f87171;

    --c-peak-low: #4ade80;
    --c-peak-mid: #fbbf24;
    --c-peak-high: #f87171;

    /* Der Verlauf wird dunkler, nicht heller: Auf schwarzem Grund ist ein
       Markenzeichen in den hellen Tönen des Tagmodus ein Scheinwerfer. */
    --grad-brand: linear-gradient(118deg, #12171a 0%, #243038 48%, #15564f 100%);
    --grad-tint: linear-gradient(150deg, rgb(142 184 178 / 12%) 0%, rgb(224 138 88 / 8%) 55%, rgb(255 255 255 / 0%) 100%);
    --grad-aurora:
      radial-gradient(62rem 24rem at 10% -8rem, rgb(31 107 99 / 22%), transparent 70%),
      radial-gradient(48rem 20rem at 88% -6rem, rgb(196 92 38 / 16%), transparent 70%);

    --shadow-sm: 0 1px 2px rgb(0 0 0 / 30%);
    --shadow: 0 1px 2px rgb(0 0 0 / 28%), 0 6px 18px -6px rgb(0 0 0 / 45%);
    --shadow-lg: 0 2px 4px rgb(0 0 0 / 30%), 0 18px 40px -12px rgb(0 0 0 / 55%);
    --shadow-brand: 0 8px 22px -10px rgb(31 107 99 / 55%);
  }
}

/* ------------------------------------------------------------ 2. Grundlagen */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--font);
  font-size: var(--t-body);
  line-height: 1.6;
  text-wrap: pretty;
  /* Zwei weiche Farbwolken am oberen Rand, eine kiefer, eine kupfer. Sie
     binden die Kopfzeile an den Seitenkörper und geben der Seite ihre Farbe,
     ohne eine Fläche zu setzen, die den Inhalt verdrängt: Sie enden vor dem
     ersten Absatz, und jede Karte darüber liegt in ihrer eigenen weißen
     Fläche. */
  background-image: var(--grad-aurora);
  background-repeat: no-repeat;
  background-size: 100% 34rem;
}

h1,
h2,
h3 {
  margin: 0 0 var(--s-3);
  line-height: 1.2;
  text-wrap: balance;
  font-weight: 650;
}

h1 {
  font-size: var(--t-h1);
  letter-spacing: -0.02em;
}

h2 {
  font-size: var(--t-h2);
  letter-spacing: -0.01em;
}

h3 {
  font-size: var(--t-h3);
}

p {
  margin: 0 0 var(--s-4);
}

a {
  color: var(--c-primary);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

a:hover {
  color: var(--c-primary-strong);
}

/* Ein sichtbarer Fokusring in einer Kontrastfarbe. Das alte Theme entfernte
   den Fokus per outline: none, was Tastaturbedienung unmöglich machte. */
:focus-visible {
  outline: 3px solid var(--c-focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

code {
  font-family: var(--font-num);
  font-size: 0.9em;
  padding: 0.1em 0.35em;
  border-radius: var(--radius-sm);
  background: var(--c-surface-2);
  overflow-wrap: anywhere;
}

time {
  color: var(--c-muted);
  font-variant-numeric: tabular-nums;
}

ul,
ol {
  margin: 0 0 var(--s-4);
  padding-left: var(--s-5);
}

.skiplink {
  position: absolute;
  left: -9999px;
  z-index: 100;
  padding: var(--s-2) var(--s-4);
  background: var(--c-primary);
  color: #fff;
  border-radius: 0 0 var(--radius) 0;
}

.skiplink:focus {
  left: 0;
  top: 0;
}

/* Text nur für Screenreader. Nicht display:none, weil der Inhalt dann auch
   dort verschwindet: die Beschriftung der Kartenlegende ist für Sehende durch
   die Anordnung erkennbar, für andere nur durch diesen Text. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.hint,
.meta {
  color: var(--c-muted);
  font-size: var(--t-small);
}

.lead {
  font-size: var(--t-lead);
  color: var(--c-muted);
  max-width: var(--w-prose);
}

.button {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-5);
  border-radius: var(--radius-pill);
  background-image: var(--grad-brand);
  color: #fff;
  font-weight: 600;
  text-decoration: none;
  box-shadow: var(--shadow-brand);
  transition: transform 120ms ease, box-shadow 120ms ease, filter 120ms ease;
}

.button:hover {
  color: #fff;
  filter: saturate(1.08) brightness(1.06);
  transform: translateY(-1px);
}

/* ------------------------------------------------------------ 3. Kopf und Fuß */

.site-header {
  position: relative;
  /* Nebelgraphit, nicht das Weiß der Karten. Kiefer sitzt in Wortmarke,
     Navigation und Band – nicht noch einmal als Flächenstich, sonst wird
     der Kopf nachts kupferbraun. */
  background-color: color-mix(in srgb, var(--c-primary-soft) 82%, var(--c-surface));
  background-image: var(--grad-tint);
  border-bottom: 1px solid var(--c-border);
  z-index: 30;
}

/* Die Kopfzeile klebt erst, wenn sie nur eine Zeile hoch ist. Auf dem Telefon
   steht das Suchfeld unter der Marke; klebend nähme das zwei Zeilen Höhe
   dauerhaft vom Bildschirm. */
@media (min-width: 62rem) {
  .site-header {
    position: sticky;
    top: 0;
    background-color: color-mix(in srgb, var(--c-primary-soft) 55%, transparent);
    background-image: var(--grad-tint);
    backdrop-filter: blur(14px) saturate(140%);
  }
}

/* Das Verlaufsband trägt Graphit nach Kiefer. Drei Pixel, volle Deckkraft:
   eine Haarlinie verschluckt den Kieferanteil. */
.site-header::after {
  content: "";
  position: absolute;
  inset: auto 0 -1px;
  height: 3px;
  background-image: var(--grad-brand);
}

/* Marke, Suche und Navigation liegen in benannten Feldern. Damit bestimmt das
   Raster die Anordnung, und die Dokumentreihenfolge kann der Leserichtung auf
   dem großen Bildschirm folgen: Marke, Suche, Navigation. */
.site-header__inner {
  max-width: var(--w-shell);
  margin: 0 auto;
  padding: var(--s-3) var(--s-4);
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "brand nav"
    "search search";
  gap: var(--s-3) var(--s-5);
  align-items: center;
}

.brand {
  grid-area: brand;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-weight: 700;
  font-size: 1.125rem;
  color: var(--c-primary);
  text-decoration: none;
  letter-spacing: -0.02em;
}

.brand:hover {
  color: var(--c-primary-strong);
}

.brand__mark {
  width: 2rem;
  height: 2rem;
  padding: 0.25rem;
  flex: none;
  border-radius: 0.55rem;
  background-image: var(--grad-brand);
  color: #fff;
  box-shadow: var(--shadow-brand);
}

.brand__tld {
  color: var(--c-accent);
  font-weight: 600;
}

.mainnav {
  grid-area: nav;
}

.mainnav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.mainnav a {
  display: block;
  padding: var(--s-1) 0;
  color: var(--c-muted);
  text-decoration: none;
  font-weight: 550;
  font-size: var(--t-small);
  border-bottom: 2px solid transparent;
}

.mainnav a:hover {
  color: var(--c-primary);
}

.mainnav a[aria-current="page"] {
  color: var(--c-accent);
  border-bottom-color: var(--c-accent);
}

/* „Mein Radar" ist der einzige Eintrag, der nicht ins Lagebild führt, sondern
   in den persönlichen Bereich. Er steht deshalb abgesetzt am Ende – die
   Navigation soll die beiden Adressräume zeigen und nicht verwischen. */
.mainnav__radar {
  color: var(--c-accent);
}

.mainnav__radar:hover {
  color: var(--c-accent-strong);
}

@media (min-width: 62rem) {
  .mainnav li:last-child {
    margin-left: auto;
    padding-left: var(--s-4);
    border-left: 1px solid var(--c-border);
  }
}

/* Die Suche im Kopf ist auf jeder Seite erreichbar. Sie ersetzt damit die
   Suchblöcke, die vorher in den Seitenkörper eingestreut waren – und die es
   in mehrfacher Ausführung auf einer Seite gab. */
.site-header__search {
  grid-area: search;
  min-width: 0;
}

.site-header__search .search {
  margin: 0;
  max-width: none;
}

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

@media (min-width: 62rem) {
  .site-header__inner {
    grid-template-columns: auto minmax(12rem, 26rem) 1fr;
    grid-template-areas: "brand search nav";
  }

  .mainnav {
    justify-self: end;
  }

  .mainnav a {
    font-size: var(--t-body);
  }
}

/* Der Fuß trägt oben dasselbe Verlaufsband wie die Kopfzeile unten. Damit ist
   der Seitenkörper zwischen zwei Markenkanten eingespannt, und der Fuß ist
   nicht bloß der Ort, an dem die Seite aufhört. */
.site-footer {
  position: relative;
  border-top: 1px solid var(--c-border);
  background: var(--c-surface);
  margin-top: var(--s-7);
}

.site-footer::before {
  content: "";
  position: absolute;
  inset: -1px 0 auto;
  height: 2px;
  background-image: var(--grad-brand);
  opacity: 0.85;
}

.site-footer__inner {
  max-width: var(--w-shell);
  margin: 0 auto;
  padding: var(--s-6) var(--s-4);
  display: grid;
  gap: var(--s-4);
  color: var(--c-muted);
  font-size: var(--t-small);
}

.site-footer__note {
  max-width: var(--w-prose);
  margin: 0;
}

.site-footer__nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-footer__places {
  display: grid;
  gap: var(--s-2);
}

.site-footer__title {
  margin: var(--s-2) 0 0;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-muted);
}

.site-footer__meta {
  margin: 0;
}

/* Hinweis auf veraltete oder unvollständige Daten. Das Altsystem lieferte in
   diesem Fall stillschweigend eine lückenhafte Seite. */
.notice {
  max-width: var(--w-shell);
  margin: var(--s-4) auto 0;
  padding: var(--s-3) var(--s-4);
  border: 1px solid color-mix(in srgb, var(--c-roadworks) 35%, var(--c-border));
  border-left: 4px solid var(--c-roadworks);
  border-radius: var(--radius);
  background: var(--c-surface);
  color: var(--c-text);
  font-size: var(--t-small);
  box-shadow: var(--shadow-sm);
}

@media (min-width: 62rem) {
  .notice {
    margin-inline: var(--s-5);
    max-width: calc(var(--w-shell) - 2 * var(--s-5));
  }
}

.notice--inline {
  margin: 0 0 var(--s-5);
  max-width: none;
}

/* ------------------------------------------------------- 4. Dreispaltiges Gerüst */

/* Die Seite hat genau einen Scrollbalken, den des Dokuments.
 *
 * Die beiden Seitenspalten klebten zuvor am Kopf und brachten je einen eigenen
 * Scrollbereich mit. Das las sich in der Beschreibung gut – der Schnellzugriff
 * bleibt in Reichweite – und wirkte im Gebrauch unruhig: Drei Flächen, die
 * unterschiedlich schnell laufen, zerlegen die Seite optisch in drei Seiten,
 * und wo der Zeiger gerade steht, entscheidet darüber, was das Mausrad bewegt.
 *
 * Die Spalten sind deshalb gewöhnliche Rasterfelder. Sie laufen mit, enden wo
 * ihr Inhalt endet, und die verbleibende Fläche bleibt leer. Das kostet in der
 * dreispaltigen Ansicht etwas Weißraum unter den kürzeren Spalten und ist der
 * Preis dafür, dass sich die Seite als ein Stück bewegt. */
.layout {
  max-width: var(--w-shell);
  margin: 0 auto;
  padding: var(--s-4) var(--s-4) var(--s-7);
  display: grid;
  gap: var(--s-5);
  align-items: start;
}

.layout__main {
  min-width: 0;
}

.layout__rail,
.layout__aside {
  min-width: 0;
}

@media (min-width: 62rem) {
  .layout {
    padding-inline: var(--s-5);
    grid-template-columns: minmax(0, 1fr) var(--w-aside);
  }

  /* Zwei Spalten: der Schnellzugriff bleibt ein Streifen über beiden, damit
     die Kontextspalte neben dem Inhalt die volle Höhe behält. */
  .layout__rail {
    grid-column: 1 / -1;
  }
}

@media (min-width: 82rem) {
  .layout {
    grid-template-columns: var(--w-rail) minmax(0, 1fr) var(--w-aside);
  }

  .layout__rail {
    grid-column: auto;
  }
}

/* Schnellzugriff. Unter 82rem ein waagrechter Streifen, darüber eine Spalte.
   Unter 62rem gilt keins von beidem: dort bricht Abschnitt 10 die Auswahl um
   und macht aus den Meldungsarten ein Menü. Diese Regeln hier sind damit die
   Fassung für das Fenster zwischen 62rem und 82rem – und die Grundlage, auf der
   beide Seiten aufbauen. */
.rail {
  display: grid;
  gap: var(--s-4);
}

.rail__group {
  min-width: 0;
}

.rail__title {
  margin: 0 0 var(--s-2);
  font-size: var(--t-small);
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-muted);
}

.rail__title:has(+ .rail__scope) {
  margin-bottom: 0.15em;
}

.rail__scope {
  margin: 0 0 var(--s-2);
  font-size: var(--t-small);
  font-weight: 450;
  line-height: 1.35;
  color: var(--c-muted);
}

.rail__list {
  display: flex;
  gap: var(--s-2);
  margin: 0;
  padding: 0 0 var(--s-1);
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  /* Die Kanten sollen sichtbar anreißen, dass es weitergeht. */
  scroll-snap-type: x proximity;
}

.rail__list > li {
  flex: none;
  scroll-snap-align: start;
}

/* Gruppen, die nur als Spalte Sinn haben: als Streifen wären es drei
   Schiebeleisten übereinander. Ihre Ziele stehen auf den Seiten selbst und im
   Fuß, es geht kein Weg verloren. */
.rail__group--wide {
  display: none;
}

@media (min-width: 82rem) {
  .rail__group--wide {
    display: block;
  }

  .rail__title {
    margin-bottom: var(--s-1);
  }

  .rail__scope {
    margin-bottom: var(--s-1);
  }

  .rail__list {
    display: grid;
    gap: var(--s-1);
    overflow: visible;
  }

  .rail__list > li {
    scroll-snap-align: none;
  }

  /* In der Spalte werden aus Pillen Zeilen: eine senkrechte Liste ist auf
     einen Blick zu überfliegen, gestapelte Pillen sind es nicht. */
  .rail__list .chip {
    width: 100%;
    justify-content: flex-start;
    border-color: transparent;
    background: none;
    border-radius: var(--radius-sm);
    /* Enger als eine Pille: die Spalte soll ohne eigenen Rollbalken in ein
       Fenster von 900 Pixeln Höhe passen. Ein Mauszeiger braucht nicht die
       44 Pixel, die eine Fingerspitze braucht. */
    padding: 0.375rem var(--s-2);
    box-shadow: none;
  }

  .rail__list .chip:hover {
    background: var(--c-surface);
    border-color: var(--c-border);
  }

  /* In der Spalte ist der aktive Eintrag eine getönte Zeile in der Farbe
     seiner Meldungsart. Der Verlauf der Pillenform bleibt dem Streifen
     überlassen: neun eingefärbte Zeilen mit weißer Schrift übereinander wären
     eine Farbtafel und keine Liste. */
  .rail__list .chip--active {
    background: color-mix(in srgb, var(--c-type, var(--c-primary)) 12%, var(--c-surface));
    border-color: transparent;
    color: var(--c-type, var(--c-primary-strong));
  }

  .rail__list .chip--active .icon {
    color: var(--c-type, var(--c-primary-strong));
  }

  /* Ohne diese Zeilen gewinnt .chip--active:hover (weiße Schrift) über die
     Spaltenfarbe, während .rail__list .chip:hover den Grund auf die Fläche
     setzt – weiße Schrift auf hellem Grund, die Zeile ist weg. */
  .rail__list .chip--active:hover {
    background: color-mix(in srgb, var(--c-type, var(--c-primary)) 18%, var(--c-surface));
    border-color: transparent;
    color: var(--c-type, var(--c-primary-strong));
  }

  .rail__list .chip--active:hover .icon {
    color: var(--c-type, var(--c-primary-strong));
  }
}

/* Kontextspalte. Sie trägt, was zur Seite gehört, aber nicht ihr Gegenstand
   ist: Staustufen neben der Karte, Nachbarorte, Angaben zur Meldung. */
.aside {
  display: grid;
  gap: var(--s-4);
}

.panel {
  padding: var(--s-4);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}

.panel__title {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin: 0 0 var(--s-3);
  font-size: var(--t-body);
  font-weight: 650;
}

.panel__title::before {
  content: "";
  width: 0.25rem;
  align-self: stretch;
  min-height: 1.1em;
  border-radius: var(--radius-pill);
  background-image: var(--grad-brand);
}

.panel__title:has(+ .panel__scope) {
  margin-bottom: 0.15em;
}

.panel__scope {
  margin: 0 0 var(--s-3);
  font-size: var(--t-small);
  font-weight: 450;
  color: var(--c-muted);
}

.panel > :last-child {
  margin-bottom: 0;
}

/* ------------------------------------------------------------ 5. Bausteine */

/* Die Farbe einer Meldungsart.
 *
 * Sie erscheint an fünf Stellen: als Kante der Meldungskarte, als Balken in
 * der Aufteilung nach Art, als Symbol im Schnellzugriff, als Pille der
 * Kartenlegende und als Linie eines Stauverlaufs.
 *
 * Die Marker der Karte stehen nicht mehr darunter: sie sind Zeichnungen mit
 * eigenen Farben (D-063). Die Pille der Legende trägt beides – die Zeichnung
 * als Bild und die Farbe der Art als Rand und Grund.
 *
 * Die Zuordnung steht deshalb genau einmal hier und wird von dort über
 * --c-type weitergegeben. Vorher stand sie je Darstellung erneut, und jede
 * neue Darstellung war eine weitere Liste von zehn Farbwerten, die man beim
 * Ändern übersehen kann.
 *
 * Die Namen sind die Symbolnamen der Typtabelle in Go (LegendIcon). Damit
 * benutzen Vorlage, Island und Stylesheet denselben Bezeichner, und ein Typ,
 * der dazukommt, braucht hier eine Zeile statt fünf. */
.card--tailback,
.bar--tailback,
.chip--tailback,
.map__legend-item--tailback,
.stretch--tailback { --c-type: var(--c-tailback); }

.card--roadworks,
.bar--roadworks,
.chip--roadworks,
.map__legend-item--roadworks,
.stretch--roadworks { --c-type: var(--c-roadworks); }

.card--accident,
.bar--accident,
.chip--accident,
.map__legend-item--accident,
.stretch--accident { --c-type: var(--c-accident); }

.card--slippery,
.bar--slippery,
.chip--slippery,
.map__legend-item--slippery,
.stretch--slippery { --c-type: var(--c-slippery); }

.card--view,
.bar--view,
.chip--view,
.map__legend-item--view,
.stretch--view { --c-type: var(--c-view); }

.card--objects,
.bar--objects,
.chip--objects,
.map__legend-item--objects,
.stretch--objects { --c-type: var(--c-objects); }

.card--breakdown,
.bar--breakdown,
.chip--breakdown,
.map__legend-item--breakdown,
.stretch--breakdown { --c-type: var(--c-breakdown); }

.card--wrongway,
.bar--wrongway,
.chip--wrongway,
.map__legend-item--wrongway,
.stretch--wrongway { --c-type: var(--c-wrongway); }

.card--closure,
.bar--closure,
.chip--closure,
.map__legend-item--closure,
.stretch--closure { --c-type: var(--c-closure); }

/* Alle drei Blitzerarten teilen eine Farbe: Wer nach einem Blitzer sucht, sucht
   nach einem Blitzer, und die Unterscheidung mobil, teilstationär oder fest
   liegt im Symbol und in der Beschriftung.

   Bei den teilstationären wäre eine eigene Farbe sogar irreführend – sie sind
   die Zwischenstufe zwischen den beiden anderen, und in der Leiste der
   Meldungsarten stehen sie unmittelbar neben den mobilen. Ein dritter Ton dort
   hieße: eine andere Sache. */
.card--mobile,
.card--fixed,
.card--semifixed,
.bar--mobile,
.bar--fixed,
.bar--semifixed,
.chip--mobile,
.chip--fixed,
.chip--semifixed,
.map__legend-item--mobile,
.map__legend-item--fixed,
.map__legend-item--semifixed,
.map__rail-item--fixed { --c-type: var(--c-camera); }

.card--vwd,
.map__rail-item--vwd,
.stretch--vwd { --c-type: var(--c-vwd); }

.map__rail-item--closure { --c-type: var(--c-closure); }

.map__rail-item--traffic { --c-type: var(--c-accent); }

.card--cluster { --c-type: var(--c-cluster); }

/* Symbole kommen aus einem SVG-Sprite und werden über <use> eingesetzt.
   Wichtig: hier darf keine fill-Regel stehen. Ein externes Sprite lässt sich
   vom Dokument nicht durchgestalten, wohl aber über vererbte Eigenschaften –
   die Symbole malen mit currentColor, und nur color wird hier gesetzt. Eine
   fill-Regel würde die Angaben im Sprite überstimmen und Konturzeichnungen
   ausfüllen. */
.icon {
  width: 1.25rem;
  height: 1.25rem;
  flex: none;
  vertical-align: -0.2em;
}

.block {
  margin-top: var(--s-6);
}

.block > h2,
.block > h3 {
  display: flex;
  align-items: center;
  gap: var(--s-2);
}

.block > h2::before {
  content: "";
  width: 0.25rem;
  align-self: stretch;
  min-height: 1.1em;
  border-radius: var(--radius-pill);
  background-image: var(--grad-brand);
}

/* Aufmacher. Der Schimmer und die Kante darüber sind der größte Farbeinsatz
   einer Seite. */
.hero {
  position: relative;
  margin-bottom: var(--s-5);
  padding: var(--s-5);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-lg);
  background: var(--c-surface);
  background-image: var(--grad-tint);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.hero::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 4px;
  background-image: var(--grad-brand);
}

.hero h1 {
  margin-bottom: var(--s-2);
}

.hero > :last-child {
  margin-bottom: 0;
}

/* Die Fehlseite: ein Aufmacher und ein Weg zurück. Suche und Einstiege
   liegen schon im Kopf und im Schnellzugriff. */
.hero--miss {
  padding: var(--s-7) var(--s-6);
}

.miss__actions {
  margin-top: var(--s-5);
}

/* Meldungskarten */

.cards {
  display: grid;
  gap: var(--s-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Die Farbe der Karte kommt aus der Meldungsart: Kante, Überschrift und
   Symbolkachel aus einem Wert. Ohne Art bleibt die neutrale Sammelfarbe. */
.card {
  position: relative;
  padding: var(--s-3) var(--s-4);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-left: 4px solid var(--c-type, var(--c-cluster));
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  transition: box-shadow 120ms ease, transform 120ms ease, border-color 120ms ease;
}

.card:hover {
  box-shadow: var(--shadow);
  transform: translateY(-1px);
  border-color: color-mix(in srgb, var(--c-type, var(--c-primary)) 30%, var(--c-border));
  border-left-color: var(--c-type, var(--c-cluster));
}

.card__title {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin: 0;
  color: var(--c-type, var(--c-text));
  font-weight: 600;
}

/* Das Symbol sitzt in einer eingefärbten Kachel: heller Grund in der Farbe der
   Meldungsart, Zeichnung in der Vollfarbe, ein Haarring in derselben Farbe.
   Sie gibt der Art einen Ankerpunkt, der auch beim Überfliegen einer langen
   Liste greift. Gemischt wird aus currentColor, damit je Art nur ein Farbwert
   gepflegt werden muss. */
.card__title .icon {
  width: 1.75rem;
  height: 1.75rem;
  padding: 0.3125rem;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, currentColor 13%, var(--c-surface));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 22%, transparent);
  color: inherit;
  vertical-align: middle;
}

.card__reason {
  margin: var(--s-2) 0 0;
  color: var(--c-muted);
}

.card__facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-1) var(--s-3);
  margin: var(--s-2) 0 0;
  font-size: var(--t-small);
}

.card__facts:empty {
  display: none;
}

.card__facts dt {
  color: var(--c-muted);
}

.card__facts dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
}

/* Redaktionelle Meldungen */

.messages {
  display: grid;
  gap: var(--s-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.message {
  padding: var(--s-3) var(--s-4);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}

.message:hover {
  border-color: var(--c-border-strong);
}

.message__title {
  margin: 0;
  font-size: var(--t-body);
  font-weight: 600;
  line-height: 1.4;
}

.message__title a {
  text-decoration: none;
}

.message__title a:hover {
  text-decoration: underline;
}

.message__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-1) var(--s-3);
  margin: var(--s-2) 0 0;
  font-size: var(--t-small);
  color: var(--c-muted);
}

.message__text {
  margin: var(--s-2) 0 0;
  line-height: 1.55;
}

/* Die Gültigkeit. Sie ist das, was der Bestand von Blitzer.de nicht hat, und
   unterscheidet den Zwischenfall von heute Abend von der Dauerbaustelle. */
.message__valid {
  margin: var(--s-2) 0 0;
  font-size: var(--t-small);
  color: var(--c-muted);
}

/* Die Kennung einer Meldung. Sie trägt die Gegenfarbe und nicht den Grundton:
   In derselben Zeile stehen Verweise, und ein graphitfarbenes Etikett zwischen
   graphitfarbenen Verweisen sähe aus wie ein Knopf, der nicht reagiert. */
.tag {
  padding: 0.1em 0.6em;
  border-radius: var(--radius-pill);
  background: var(--c-accent-soft);
  color: var(--c-accent-strong);
  font-weight: 600;
}

.tag--fresh {
  background: var(--c-accent-soft);
  color: var(--c-accent-strong);
}

.tag--stale {
  background: var(--c-surface-3);
  color: var(--c-muted);
}

/* Die Schwere einer amtlichen Meldung.

   Der Farbverlauf ist derselbe wie beim Lagewert – kühl nach warm –, damit
   dieselbe Aussage auf einer Seite nicht zweimal verschieden aussieht. Getragen
   wird sie doppelt: als Etikett am Text und als Kante am Eintrag. Die Kante
   allein wäre für Farbenblinde keine Auskunft, das Etikett allein macht eine
   Liste von hundert Meldungen nicht überfliegbar. */
.message--closure,
.message--disruption,
.message--restriction {
  border-left-width: 3px;
}

.message--closure {
  border-left-color: var(--c-level-bad);
}

.message--disruption {
  border-left-color: var(--c-level-busy);
}

.message--restriction {
  border-left-color: var(--c-level-normal);
}

.tag--closure {
  background: color-mix(in oklab, var(--c-level-bad) 14%, transparent);
  color: var(--c-level-bad);
}

.tag--disruption {
  background: color-mix(in oklab, var(--c-level-busy) 16%, transparent);
  color: var(--c-level-busy);
}

.tag--restriction {
  background: color-mix(in oklab, var(--c-level-normal) 14%, transparent);
  color: var(--c-level-normal);
}

.tag--notice {
  background: var(--c-primary-soft);
  color: var(--c-primary-strong);
}

/* Stau-Stufen */

.tier + .tier {
  margin-top: var(--s-4);
}

.tier__title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  margin-bottom: var(--s-2);
  font-size: var(--t-small);
  font-weight: 650;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--c-muted);
}

.tier__count {
  padding: 0 0.5em;
  border-radius: var(--radius-pill);
  background: var(--c-surface-2);
  color: var(--c-text);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-small);
  text-transform: none;
}

/* In der Kontextspalte stehen die Staukarten enger: dort zählt der Überblick,
   nicht die Einzelangabe. */
.aside .cards {
  gap: var(--s-2);
}

/* Die Stauliste einer Stufe: eine Zeile je Stau, Zeitverlust rechtsbündig.
   Karten wären hier eine Bildlaufstrecke. Die untere Stufe endet nach zwanzig
   Zeilen mit der Restzahl. */
.tailbacks {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--t-small);
}

.tailbacks__item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-1) var(--s-2);
  padding: var(--s-2) 0;
  border-top: 1px solid var(--c-border);
}

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

/* Rechts am Rand, auch wenn die Zeile umbricht: „1 Std. 24 Min." passt neben
   einem langen Ortsnamen nicht mehr in die Spalte, und linksbündig unter dem
   Text sähe es wie ein zweiter Satzteil aus. */
.tailbacks__delay {
  margin-inline-start: auto;
  color: var(--c-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.tailbacks__more {
  padding: var(--s-2) 0;
  border-top: 1px solid var(--c-border);
  color: var(--c-muted);
  font-variant-numeric: tabular-nums;
}

.aside .card {
  padding: var(--s-2) var(--s-3);
  font-size: var(--t-small);
}

.aside .card__title .icon {
  width: 1.5rem;
  height: 1.5rem;
  padding: 0.25rem;
}

/* Verweisketten: Meldungsarten und Nachbarorte */

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-pill);
  background: var(--c-surface);
  color: var(--c-text);
  text-decoration: none;
  font-size: var(--t-small);
  font-weight: 550;
  white-space: nowrap;
  box-shadow: var(--shadow-sm);
}

.chip:hover {
  border-color: color-mix(in srgb, var(--c-type, var(--c-primary)) 60%, var(--c-border));
  color: var(--c-type, var(--c-primary));
}

/* Trägt der Schalter eine Meldungsart, zeichnet ihr Symbol in ihrer Farbe.
   Der Schnellzugriff ist damit ohne Beschriftung lesbar, und die neun Arten
   sind dieselben neun Farben wie auf der Karte und in den Meldungslisten.
   Vorher waren alle neun Symbole grau – eine Liste, die man lesen musste. */
.chip .icon {
  width: 1rem;
  height: 1rem;
  color: var(--c-type, var(--c-muted));
}

.chip--active {
  background-image: var(--grad-brand);
  border-color: transparent;
  color: #fff;
}

.chip--active:hover {
  color: #fff;
}

.chip--active .icon,
.chip--active:hover .icon {
  color: #fff;
}

.chips--plain .chip {
  border-radius: var(--radius-sm);
}

/* Blätterung */

.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  margin-top: var(--s-5);
  padding-top: var(--s-4);
  border-top: 1px solid var(--c-border);
}

.pagination__link {
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-pill);
  background: var(--c-surface);
  font-weight: 600;
  font-size: var(--t-small);
  text-decoration: none;
}

a.pagination__link:hover {
  border-color: var(--c-primary);
}

.pagination__link--off {
  color: var(--c-muted);
  opacity: 0.55;
  box-shadow: none;
}

.pagination__page {
  color: var(--c-muted);
  font-size: var(--t-small);
  font-variant-numeric: tabular-nums;
}

/* Brotkrümelpfad */

.breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-2);
  margin: 0 0 var(--s-2);
  padding: 0;
  list-style: none;
  font-size: var(--t-small);
  color: var(--c-muted);
}

.breadcrumb li + li::before {
  content: "›";
  margin-right: var(--s-2);
  color: var(--c-border-strong);
}

/* Leerzustand */

.emptystate {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-3);
  align-items: baseline;
  padding: var(--s-4);
  border: 1px dashed var(--c-border-strong);
  border-radius: var(--radius);
  background: var(--c-surface);
  color: var(--c-muted);
}

.emptystate__time {
  font-size: var(--t-small);
}

/* Suche */

.search {
  max-width: 34rem;
  margin: var(--s-4) 0 0;
}

.search__label {
  display: block;
  margin-bottom: var(--s-1);
  font-size: var(--t-small);
  font-weight: 600;
  color: var(--c-muted);
}

.search__row {
  position: relative;
}

.search__input {
  display: block;
  width: 100%;
  min-width: 0;
  padding: var(--s-3) 2.75rem var(--s-3) var(--s-4);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--radius-pill);
  background: var(--c-surface);
  color: var(--c-text);
  font: inherit;
  /* 16 px verhindert, dass iOS beim Fokussieren hineinzoomt. */
  font-size: max(1rem, var(--t-body));
  box-shadow: var(--shadow-sm);
}

.search__input::placeholder {
  color: var(--c-muted);
}

.search__input::-webkit-search-decoration {
  -webkit-appearance: none;
}

.search__submit {
  position: absolute;
  top: 50%;
  right: 0.2rem;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--c-muted);
  cursor: pointer;
}

.search__submit:hover,
.search__submit:focus-visible {
  color: var(--c-primary);
  background: var(--c-primary-soft);
}

.search__submit .icon {
  width: 1.15rem;
  height: 1.15rem;
  vertical-align: 0;
}

/* Vorschlagsliste der Island. Ohne JavaScript existiert sie nicht, und das
   Formular bleibt trotzdem benutzbar. */
.search__suggestions {
  position: absolute;
  top: calc(100% + var(--s-2));
  left: 0;
  right: 0;
  z-index: 40;
  margin: 0;
  padding: var(--s-1);
  list-style: none;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  max-height: 18rem;
  overflow-y: auto;
}

.search__suggestion {
  display: block;
  width: 100%;
  padding: var(--s-2) var(--s-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.search__suggestion[aria-selected="true"],
.search__suggestion:hover {
  background: var(--c-primary-soft);
  color: var(--c-primary-strong);
}

.search__suggestion small {
  color: var(--c-muted);
  margin-left: var(--s-2);
}

/* Fließtext */

.prose {
  max-width: var(--w-prose);
}

.prose h2 {
  margin-top: var(--s-6);
}

/* Eine Anschrift ist im Impressum eine Anschrift und keine Hervorhebung; die
   Kursivstellung der Browser hilft dort nichts. Der Abstand darunter ist der
   eines Absatzes, den address von sich aus nicht hat – sonst klebte die
   Vertretungsangabe an der Hausnummer. */
.prose address {
  margin: 0 0 var(--s-4);
  font-style: normal;
}

.facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-2) var(--s-4);
  margin: 0;
  padding: 0;
}

.facts dt {
  color: var(--c-muted);
  font-size: var(--t-small);
}

.facts dd {
  margin: 0;
  overflow-wrap: anywhere;
}

/* Kacheln „Neueste Meldungen nach Art" */

.grid {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}

.grid__item {
  padding: var(--s-4);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}

.grid__title {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--t-body);
  margin-bottom: var(--s-2);
}

.grid__title .icon {
  width: 1.5rem;
  height: 1.5rem;
  padding: 0.25rem;
  border-radius: var(--radius-sm);
  background: var(--c-surface-2);
  color: var(--c-muted);
}

.minilist {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--t-small);
}

.minilist li {
  padding: var(--s-2) 0;
  border-top: 1px solid var(--c-border);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-2);
  justify-content: space-between;
}

.minilist li:first-child {
  border-top: 0;
}

.minilist__empty {
  margin: 0;
  color: var(--c-muted);
  font-size: var(--t-small);
}

/* Seitenköpfe. Alle Seitenarten tragen denselben Aufbau – Brotkrümel, Kicker,
   Überschrift, Lead –, deshalb steht die Regel einmal und nicht siebenmal. */

.situation__head,
.directory__head,
.ranking__head,
.knowledge__head,
.article__head,
.message-page__head,
.event__head,
.radar__head {
  margin-bottom: var(--s-5);
}

.situation__head h1 {
  font-size: clamp(1.75rem, 1.35rem + 2.8vw, 2.75rem);
  font-weight: 750;
}

.message-page__head h1 {
  max-width: 28ch;
}

.message-page__text {
  margin: var(--s-3) 0 0;
  max-width: var(--w-prose);
  font-size: var(--t-lead);
  line-height: 1.55;
}

/* Die Angaben der Landesmeldestelle: Kacheln wie die Kennzahlen im Lagebild,
   mit Stahlkante statt Graphit, damit die Seite nicht wie eine Störungskarte
   der Gemeinschaft wirkt. */
.message-facts {
  margin: 0 0 var(--s-5);
  padding: var(--s-4);
  border: 1px solid var(--c-border);
  border-left: 4px solid var(--c-vwd);
  border-radius: var(--radius);
  background: var(--c-surface);
  box-shadow: var(--shadow-sm);
}

.message-facts__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2) var(--s-4);
  margin-bottom: var(--s-4);
}

.message-facts__kicker {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin: 0;
  color: var(--c-vwd);
  font-size: var(--t-small);
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.message-facts__kicker .icon {
  width: 1.25rem;
  height: 1.25rem;
  flex: none;
}

.message-facts__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin: 0;
}

.message-facts__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-3);
  margin: 0;
}

@media (min-width: 36rem) {
  .message-facts__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.message-facts__item {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--radius);
  background: var(--c-surface-2);
}

.message-facts__item--wide {
  grid-column: 1 / -1;
}

.message-facts__item dt {
  color: var(--c-muted);
  font-size: var(--t-small);
}

.message-facts__item dd {
  margin: 0;
  font-weight: 650;
  overflow-wrap: anywhere;
}

.message-facts__item .hint {
  display: block;
  margin-top: 0.15rem;
  font-size: var(--t-small);
  font-weight: 500;
  color: var(--c-muted);
}

/* Die Zeile über der Überschrift sagt, was für eine Seite das ist – „Stadt
   oder Gemeinde", „Bundesland", „Meldungsart". Sie steht in der Markenfarbe,
   weil sie zur Einordnung der Website gehört und nicht zum Ort: Grau gelesen
   wirkte sie wie eine Fußnote zur Überschrift. */
.kicker {
  margin: 0 0 var(--s-1);
  font-size: var(--t-small);
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-primary);
}

/* Brotkrümel. Es gab sie im Altsystem nicht, weil die Adressen flach waren und
   es nichts zu zeigen gab. */

.crumbs {
  margin: 0 0 var(--s-3);
  font-size: var(--t-small);
  color: var(--c-muted);
}

.crumbs ol {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.crumbs li + li::before {
  content: "›";
  margin-right: var(--s-2);
  color: var(--c-border-strong);
}

.crumbs a {
  color: inherit;
}

/* --------------------------------------------------------- Der Kopfstreifen

   Nur auf /stau, fest offen. Tacho, Kennzahlen und Tagesverlauf als
   drei Flächen unter einer Zeile aus Titel und Merken. */

.gauge {
  margin: 0 0 var(--s-5);
  padding: var(--s-5);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-lg);
  background-color: var(--c-surface);
  background-image: var(--grad-brand), var(--grad-tint);
  background-repeat: no-repeat;
  background-size: 100% 4px, auto;
  background-position: top, top;
  box-shadow: var(--shadow);
  overflow: hidden;
}

.gauge__heading {
  margin-bottom: var(--s-5);
}

.gauge__kicker {
  margin: 0 0 var(--s-1);
  color: var(--c-accent);
  font-size: var(--t-small);
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.gauge__title {
  margin: 0;
  font-size: var(--t-h2);
  font-weight: 700;
  color: var(--c-primary);
  letter-spacing: -0.02em;
}

.gauge__note {
  margin: var(--s-2) 0 0;
  max-width: 36rem;
  color: var(--c-muted);
  font-size: var(--t-small);
}

.gauge__body {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}

/* Oben die Mini-Karten, unten der Verlauf über die volle Breite. */
.gauge__minis {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: 1fr;
  align-items: stretch;
}

@media (min-width: 40rem) {
  .gauge__minis {
    grid-template-columns: auto minmax(0, 1fr);
  }
}

.gauge__arc {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  padding: var(--s-5) var(--s-6);
  border-radius: var(--radius);
  background: var(--c-surface-2);
}

.gauge__arc svg {
  display: block;
  width: 11rem;
  height: auto;
}

/* Das Wort zum Wert als getönte Pille unter dem Bogen. Dieselbe Farbe
   wie der Bogen: die Stufe wird in Zahl und Wort gesagt. */
.gauge__tone {
  margin: 0;
  padding: 0.2em 0.8em;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, currentColor 14%, var(--c-surface));
  font-size: var(--t-small);
  font-weight: 650;
}

.gauge__tone--good {
  color: var(--c-level-good);
}
.gauge__tone--normal {
  color: var(--c-level-normal);
}
.gauge__tone--busy {
  color: var(--c-level-busy);
}
.gauge__tone--bad {
  color: var(--c-level-bad);
}
.gauge__tone--peak-low {
  color: var(--c-peak-low);
}
.gauge__tone--peak-mid {
  color: var(--c-peak-mid);
}
.gauge__tone--peak-high {
  color: var(--c-peak-high);
}
.gauge__tone--unknown {
  color: var(--c-muted);
  font-weight: 500;
}

.gauge__facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-2) var(--s-4);
  margin: 0;
  padding: 0;
}

.gauge__facts dt {
  color: var(--c-muted);
  font-size: var(--t-small);
}

.gauge__facts dd {
  margin: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.gauge .gauge__facts {
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: var(--s-3);
}

.gauge__stat {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--radius);
  background: var(--c-surface-2);
}

.gauge .gauge__facts dd {
  font-size: var(--t-lead);
  font-weight: 700;
  letter-spacing: -0.02em;
}

.gauge .gauge__facts .hint {
  display: block;
  margin-top: 0.15rem;
  font-size: var(--t-small);
  font-weight: 500;
  color: var(--c-muted);
}

.gauge__spark {
  min-width: 0;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--radius);
  background: var(--c-surface-2);
}

.gauge__spark .spark {
  width: 100%;
  height: 11.5rem;
}

@media (min-width: 48rem) {
  .gauge__spark .spark {
    height: 13.5rem;
  }
}

.gauge__spark svg {
  display: block;
  width: 100%;
}

.gauge__sparktitle {
  margin: 0 0 var(--s-2);
  font-size: var(--t-small);
  font-weight: 650;
  color: var(--c-muted);
}

.gauge__trend {
  margin: var(--s-2) 0 0;
  font-size: var(--t-small);
  font-weight: 600;
}

.gauge__trend--rising {
  color: var(--c-level-bad);
}
.gauge__trend--falling {
  color: var(--c-level-good);
}
.gauge__trend--flat,
.gauge__trend--unknown {
  color: var(--c-muted);
  font-weight: 500;
}

.save {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  margin: 0;
}

.save__link {
  font-size: var(--t-small);
}

/* Gemerkt heißt gemerkt: die Schaltfläche behält ihre Form und wechselt nur
   Farbe und Beschriftung. Ein Wechsel der Größe verschöbe den Kopfstreifen
   unter dem Zeigefinger. */
.save .button[aria-pressed="true"] {
  border-color: var(--c-primary);
  color: var(--c-primary-strong);
  background: var(--c-primary-soft);
}

.button--ghost {
  background: var(--c-surface);
  background-image: none;
  border: 1px solid var(--c-border-strong);
  color: var(--c-text);
}

.button--ghost:hover {
  border-color: var(--c-primary);
  color: var(--c-primary);
  filter: none;
}

/* ------------------------------------------------------------------ Kacheln

   Bundesländer und Verzeichniseinträge. Eine Kachel ist die kleinste Form, in
   der Name, Anzahl und Zeitverlust nebeneinander noch lesbar sind. */

.tiles {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
}

.tile__link {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas:
    "arc name"
    "arc count"
    "arc delay";
  gap: 0 var(--s-3);
  align-items: center;
  height: 100%;
  padding: var(--s-3);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
  color: inherit;
  text-decoration: none;
  box-shadow: var(--shadow-sm);
  transition: box-shadow 120ms ease, transform 120ms ease, border-color 120ms ease;
}

.tile__link:hover {
  border-color: color-mix(in srgb, var(--c-primary) 55%, var(--c-border));
  box-shadow: var(--shadow);
  transform: translateY(-1px);
}

.tile__arc {
  grid-area: arc;
}

.tile__arc svg {
  display: block;
  width: 3rem;
  height: auto;
}

.tile__name {
  grid-area: name;
  font-weight: 650;
}

.tile__count {
  grid-area: count;
  font-size: var(--t-small);
  color: var(--c-muted);
}

.tile__delay {
  grid-area: delay;
  font-size: var(--t-small);
  font-weight: 600;
  color: var(--c-roadworks);
}

/* ------------------------------------------------------------------- Ebenen

   Der Einstieg zu den dauerhaften Anlagen, direkt unter der Karte. Eine Zeile
   und kein Abschnitt: Die Auskunft ist eine Randbemerkung zur Karte darüber,
   und ein eigener Block mit Überschrift stellte sie neben die Aufteilung nach
   Meldungsart – genau dort, wo sie nicht hingehört. */

.layers {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2) var(--s-3);
  /* Näher an die Karte als an den nächsten Abschnitt: Der Satz gehört zu ihr. */
  margin: calc(-1 * var(--s-3)) 0 var(--s-5);
  font-size: var(--t-small);
}

.layers__text {
  flex: 1 1 14rem;
  color: var(--c-muted);
  text-wrap: pretty;
}

/* ------------------------------------------------------------------- Balken

   Die Aufteilung nach Meldungsart. Der Balken trägt keine eigene Aussage – die
   Zahl daneben tut es –, aber er macht die Verhältnisse in einem Blick
   erfassbar, und darum geht es auf einer Übersichtsseite. */

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

.bar + .bar {
  border-top: 1px solid var(--c-border);
}

.bar__link {
  display: grid;
  grid-template-columns: auto minmax(6rem, 9rem) 1fr auto;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-1);
  color: inherit;
  text-decoration: none;
}

.bar__link:hover {
  background: color-mix(in srgb, var(--c-type, var(--c-primary)) 7%, var(--c-surface));
}

/* Symbol und Balken tragen die Farbe der Meldungsart. Vorher war die Liste
   neunmal grau mit neun violetten Balken: sie zeigte die Verhältnisse und
   verschwieg, wovon. Jetzt ist die Aufteilung nach Art eine Farbtafel, die zu
   Karte, Legende und Meldungsliste passt – dieselben neun Farben, dieselbe
   Reihenfolge. */
.bar__link .icon {
  width: 1.5rem;
  height: 1.5rem;
  padding: 0.25rem;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--c-type, var(--c-primary)) 12%, var(--c-surface));
  color: var(--c-type, var(--c-primary));
}

.bar__label {
  font-size: var(--t-small);
}

.bar__track {
  height: 0.5rem;
  border-radius: var(--radius-pill);
  background: var(--c-surface-3);
  overflow: hidden;
}

.bar__fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background-image: linear-gradient(
    90deg,
    color-mix(in srgb, var(--c-type, var(--c-primary)) 55%, var(--c-surface)),
    var(--c-type, var(--c-primary))
  );
}

.bar__value {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

/* --------------------------------------------------------------- Ranglisten

   In der Kontextspalte als kompakte Liste, auf der eigenen Seite als Tabelle.
   Zwei Darstellungen, weil eine Rangliste mit vier Kennzahlen in einer 18rem
   breiten Spalte nicht lesbar ist. */

.ranks {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: rank;
}

.rank {
  display: grid;
  grid-template-columns: 1.5rem 1fr auto;
  align-items: baseline;
  gap: var(--s-2);
  padding: var(--s-2) 0;
  border-top: 1px solid var(--c-border);
  font-size: var(--t-small);
}

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

.rank__pos {
  color: var(--c-muted);
  font-variant-numeric: tabular-nums;
}

.rank__name {
  overflow-wrap: anywhere;
}

.rank__value {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-small);
}

.table th,
.table td {
  padding: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--c-border);
  text-align: left;
  vertical-align: middle;
}

.table th {
  color: var(--c-muted);
  font-weight: 600;
  white-space: nowrap;
}

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

.table__bar {
  width: 6rem;
  min-width: 4rem;
  height: 0.4rem;
  border-radius: var(--radius-pill);
  background: var(--c-surface-3);
  overflow: hidden;
}

.table__fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background-image: var(--grad-brand);
}

/* ------------------------------------------------------------- Verzeichnis */

.alphabet {
  margin: 0 0 var(--s-4);
}

/* Der Reihenumbruch gehört auf die Liste und nicht auf das nav darüber: Dessen
   einziges Kind ist die Liste, und ein Flex-Behälter mit einem Kind bricht
   nichts um – die Buchstaben standen dadurch untereinander. */
.alphabet__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.alphabet__item {
  display: inline-flex;
  align-items: baseline;
  gap: var(--s-1);
  min-width: 2.5rem;
  justify-content: center;
  padding: var(--s-2);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-sm);
  background: var(--c-surface);
  color: inherit;
  text-decoration: none;
  font-weight: 650;
  text-transform: uppercase;
}

.alphabet__item:hover {
  border-color: var(--c-primary);
  color: var(--c-primary);
}

.alphabet__count {
  font-size: var(--t-small);
  font-weight: 400;
  color: var(--c-muted);
}

/* ------------------------------------------------------------- Vorgänge

   Die Liste gibt es nur, weil die Website ein Gedächtnis hat: „besteht seit
   zwei Stunden" und „wächst" stehen in keiner Quelle, sie entstehen aus der
   Reihe der Abrufe. */

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

.event-row + .event-row {
  border-top: 1px solid var(--c-border);
}

.event-row__link {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-1) var(--s-3);
  padding: var(--s-3) var(--s-1);
  color: inherit;
  text-decoration: none;
}

.event-row__link:hover {
  background: var(--c-surface-2);
}

.event-row__title {
  font-weight: 600;
}

.event-row__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-3);
  font-size: var(--t-small);
  color: var(--c-muted);
}

.event-row__delay {
  font-weight: 600;
  color: var(--c-roadworks);
}

.event__facts {
  margin-bottom: var(--s-4);
}

.event__peak {
  margin: 0 0 var(--s-4);
  font-size: var(--t-small);
  color: var(--c-muted);
}

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
  margin: var(--s-2) 0 0;
  font-size: var(--t-small);
  color: var(--c-muted);
}

.legend__km::before,
.legend__delay::before {
  content: "";
  display: inline-block;
  width: 0.75rem;
  height: 0.75rem;
  margin-right: var(--s-1);
  border-radius: 2px;
  vertical-align: baseline;
}

.legend__km::before {
  background: var(--c-primary);
}

.legend__delay::before {
  background: var(--c-roadworks);
}

/* ---------------------------------------------------------------- Wissen */

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

.articles__item {
  padding: var(--s-4) 0;
  border-top: 1px solid var(--c-border);
}

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

.articles__title {
  margin: 0 0 var(--s-1);
  font-size: var(--t-h3);
}

.articles__summary {
  margin: 0;
  max-width: var(--w-prose);
  color: var(--c-muted);
}

.panel__list {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--t-small);
}

.panel__list li {
  padding: var(--s-2) 0;
  border-top: 1px solid var(--c-border);
}

.panel__list li:first-child {
  border-top: 0;
}

.panel__more {
  margin: var(--s-3) 0 0;
  font-size: var(--t-small);
}

/* Der aufklappbare Rest einer Liste. Er hält die Kontextspalte kurz, ohne die
   Verweise aus dem Dokument zu nehmen: details ist im geschlossenen Zustand
   nur unsichtbar, der Inhalt steht im Quelltext und ist auffindbar. */
/* Der aufklappbare Rest, im Hauptstrang wie in der Spalte: Eine Ortsseite hat
   über hundert amtliche Meldungen, und zwölf davon sind die Auskunft. */
.block__rest {
  margin-top: var(--s-4);
}

.block__rest > summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--c-primary);
}

.block__rest[open] > summary {
  margin-bottom: var(--s-3);
}

.panel__rest {
  margin-top: var(--s-3);
  border-top: 1px solid var(--c-border);
  padding-top: var(--s-3);
}

.panel__rest > summary {
  cursor: pointer;
  font-size: var(--t-small);
  font-weight: 600;
  color: var(--c-primary);
}

.panel__rest[open] > summary {
  margin-bottom: var(--s-3);
}

/* --------------------------------------------------------------- Mein Radar

   Der einzige Teil der Website mit eigenem Zustand im Browser. Die Karten
   sehen aus wie die Kacheln der Länderübersicht, tragen aber mehr: Der Radar
   ist die Seite, die ein Besucher täglich öffnet, und darf deshalb dichter
   sein als eine, die er einmal über eine Suche findet. */

.radar__fallback,
.radar__empty {
  margin: 0 0 var(--s-4);
  padding: var(--s-3) var(--s-4);
  border: 1px dashed var(--c-border-strong);
  border-radius: var(--radius);
  color: var(--c-muted);
  font-size: var(--t-small);
}

.radar__board {
  margin: 0 0 var(--s-5);
}

.radar__types {
  margin: 0 0 var(--s-4);
  padding: 0;
  border: 0;
}

.radar__types legend {
  padding: 0;
  margin-bottom: var(--s-2);
  font-size: var(--t-small);
  font-weight: 650;
  color: var(--c-muted);
}

/* Die Meldungsarten sind Schaltflächen und keine Verweise: sie ändern die
   Ansicht, sie führen nicht woandershin. aria-pressed trägt den Zustand, die
   Färbung folgt ihm. */
.radar__types .chip {
  cursor: pointer;
  font: inherit;
  font-size: var(--t-small);
  font-weight: 550;
}

/* Gewählt heißt: in der Farbe der Meldungsart. Es ist dieselbe Färbung wie bei
   der eingeschalteten Pille der Kartenlegende, und aus demselben Grund – neun
   Schalter in einer Markenfarbe zeigen, dass sie an sind, und verschweigen, wozu
   sie gehören. */
.radar__types .chip[aria-pressed="true"] {
  border-color: color-mix(in srgb, var(--c-type, var(--c-primary)) 45%, var(--c-border));
  background: color-mix(in srgb, var(--c-type, var(--c-primary)) 9%, var(--c-surface));
  color: var(--c-type, var(--c-primary-strong));
  font-weight: 650;
}

.radar__nationwide {
  margin: 0 0 var(--s-4);
  font-size: var(--t-small);
  color: var(--c-muted);
}

.radar__list {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
}

.radar__card {
  position: relative;
  height: 100%;
  padding: var(--s-4);
  border: 1px solid var(--c-border);
  border-left: 4px solid var(--c-primary);
  border-radius: var(--radius);
  background: var(--c-surface);
  box-shadow: var(--shadow-sm);
}

.radar__card:hover {
  border-color: color-mix(in srgb, var(--c-primary) 40%, var(--c-border));
  box-shadow: var(--shadow);
}

.radar__card[data-tone="good"] {
  border-left-color: var(--c-level-good);
}
.radar__card[data-tone="normal"] {
  border-left-color: var(--c-level-normal);
}
.radar__card[data-tone="busy"] {
  border-left-color: var(--c-level-busy);
}
.radar__card[data-tone="bad"] {
  border-left-color: var(--c-level-bad);
}

.radar__cardhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-2);
  margin-bottom: var(--s-2);
}

.radar__cardhead h3 {
  margin: 0;
  min-width: 0;
  font-size: var(--t-body);
}

.radar__card-actions {
  position: relative;
  z-index: 1;
  display: flex;
  flex: none;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-3);
}

.radar__card--edit .radar__card-link::after {
  content: none;
}

.radar__card--edit .radar__cardhead {
  flex-wrap: wrap;
}

.radar__rename {
  position: relative;
  z-index: 1;
  display: flex;
  flex: 1 1 12rem;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
}

.radar__rename--view {
  flex: 1 1 100%;
}

.radar__rename-input {
  flex: 1 1 8rem;
  min-width: 0;
  padding: 0.35rem 0.65rem;
  border: 1px solid var(--c-border-strong);
  border-radius: var(--radius-sm);
  background: var(--c-surface);
  color: var(--c-text);
  font: inherit;
  font-size: max(1rem, var(--t-body));
}

.radar__rename-input:focus {
  outline: 2px solid var(--c-focus);
  outline-offset: 1px;
}

.radar__card-link {
  color: inherit;
  text-decoration: none;
}

.radar__card-link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.radar__card-link:hover {
  color: var(--c-primary-strong);
}

.radar__meta {
  margin: 0 0 var(--s-2);
  color: var(--c-muted);
  font-size: var(--t-small);
}

.radar__kinds {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.radar__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  margin: 0 0 var(--s-4);
}

.radar__view-title {
  margin: 0;
  font-size: var(--t-lead);
  font-weight: 650;
}

.radar__toolbar-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}

.radar__remove {
  position: relative;
  z-index: 1;
  flex: none;
  padding: 0;
  border: 0;
  background: none;
  color: var(--c-muted);
  font: inherit;
  font-size: var(--t-small);
  text-decoration: underline;
  cursor: pointer;
}

.radar__remove:hover {
  color: var(--c-accident);
}

.radar__figures {
  margin: 0 0 var(--s-2);
  font-size: var(--t-small);
}

.radar__pois {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--t-small);
  color: var(--c-muted);
}

.radar__pois li {
  padding: var(--s-1) 0;
  border-top: 1px solid var(--c-border);
}

.radar__poitype {
  font-weight: 600;
  color: var(--c-text);
}

/* Ein Hinweis, der eine Störung meldet und keine Datenlücke: andere Farbe als
   .notice, sonst sähen „Daten sind alt" und „Abruf gescheitert" gleich aus. */
.notice--warn {
  border-color: color-mix(in srgb, var(--c-accident) 35%, var(--c-border));
  border-left-color: var(--c-accident);
}

/* ------------------------------------------------------ 6. Die Bildsprache
 *
 * Lagewert-Bogen, Tagesverlauf, Wochenraster und Streckenstreifen. Die vier
 * Zeichnungen entstehen serverseitig in internal/view/chart.go und tragen dort
 * ausschließlich Klassennamen – kein Farbwert, keine Schriftgröße.
 *
 * Das ist Absicht: Die Elemente sollen dem Farbschema folgen, und ein Bogen
 * mit eingebauten Farben wäre im Dunkelmodus ein weißer Fleck. Es hat aber
 * eine Kehrseite, die hier einmal eingetreten ist: Weil die Gegenstücke im
 * Stylesheet fehlten, wurde nichts gezeichnet. Ein Pfad ohne stroke ist
 * unsichtbar, und so fehlten Bogen, Verlauf, Raster und Streifen auf jeder
 * Seite – ohne Fehlermeldung, ohne Lücke im Aufbau, mit Beschriftung und
 * Zahl an ihrem Platz. Wer hier Klassen umbenennt, benennt sie in zwei
 * Dateien um.
 *
 * In den drei SVG-Zeichnungen stehen die Schriftgrößen in Nutzereinheiten des
 * viewBox und nicht in rem: Sie werden als Ganzes skaliert, und eine rem-Angabe
 * darin würde beim Verkleinern zur Überschrift. Der Streckenstreifen ist
 * deshalb kein SVG – bei ihm skaliert nichts außer den Abständen. */

.arc {
  display: block;
}

/* Die Bahn ist die Fläche, der Wert die Farbe seiner Stufe. Ist der Wert noch
   nicht bekannt, bleibt es bei der Bahn und einem Strich – eine erfundene
   Skala wäre schlechter als keine. */
.arc__track {
  stroke: var(--c-surface-3);
}

/* Die vier Stufen ausgeschrieben, auch die, die dem Standardwert entspricht:
   Die Zuordnung von Stufe zu Farbe soll hier als Tabelle lesbar sein, und ein
   Ton, der seine Farbe nur durch Weglassen bekommt, fehlt beim Lesen. */
.arc__value {
  stroke: var(--c-level-normal);
}

.arc--good .arc__value {
  stroke: var(--c-level-good);
}

.arc--normal .arc__value {
  stroke: var(--c-level-normal);
}

.arc--busy .arc__value {
  stroke: var(--c-level-busy);
}

.arc--bad .arc__value {
  stroke: var(--c-level-bad);
}

.arc--peak-low .arc__value {
  stroke: var(--c-peak-low);
}

.arc--peak-mid .arc__value {
  stroke: var(--c-peak-mid);
}

.arc--peak-high .arc__value {
  stroke: var(--c-peak-high);
}

/* Ohne Lagewert ist die Bahn heller. Sie trägt dann einen Strich statt einer
   Zahl, und eine Bahn in voller Stärke sähe aus wie ein Wert von null – also
   wie „nichts los" statt „noch nicht messbar". */
.arc--unknown .arc__track {
  stroke: color-mix(in srgb, var(--c-surface-3) 60%, var(--c-surface));
}

.arc__number {
  fill: var(--c-text);
  font-family: var(--font);
  font-size: 30px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

.arc__number--empty {
  fill: var(--c-muted);
  font-weight: 500;
}

/* Der Tagesverlauf: eine Linie vom Zeitverlust, von 0 Uhr bis Mitternacht.
   Bekannte Messungen sind verbunden; „jetzt" ist der letzte Punkt. Die Schrift
   skaliert mit der viewBox – deshalb meet. */
.spark {
  display: block;
  width: 100%;
  height: auto;
}

.spark__grid {
  stroke: var(--c-border);
  stroke-width: 1;
}

.spark__ytick,
.spark__xtick,
.spark__daylabel {
  fill: var(--c-muted);
  font-family: var(--font);
  font-size: 9px;
  font-variant-numeric: tabular-nums;
}

.spark__ytick {
  text-anchor: end;
}

.spark__xtick {
  text-anchor: middle;
}

.spark__daylabel {
  font-weight: 650;
  fill: var(--c-text);
}

.spark__line {
  stroke: var(--c-primary);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.spark__now {
  fill: var(--c-accent);
}

/* Das Wochenraster. Die Sättigung eines Feldes kommt aus dem Zeitverlust und
   steht als opacity in der Zeichnung; die Farbe steht hier. Ein Feld ohne
   Aufzeichnung ist nicht „null Minuten", sondern leer, und deshalb grau. */
.week {
  display: block;
  max-width: 100%;
  height: auto;
}

.week__cell {
  fill: var(--c-primary);
}

.week__cell--empty {
  fill: var(--c-surface-3);
  opacity: 1;
}

.week__day,
.week__hour {
  fill: var(--c-muted);
  font-family: var(--font);
  font-size: 9px;
}

/* Der Streckenstreifen.
   Die Bahn ist eine ruhige Fläche, auf der zwei Formen liegen: Balken für
   Meldungen mit Ausdehnung, Punkte für punktuelle. Beide tragen die Farbe ihres
   Zeitverlusts und einen Rand in der Flächenfarbe, damit zwei Meldungen am
   selben Ort als zwei erkennbar bleiben.

   Das Element ist bewusst kein SVG: Nur die Positionen sind relativ, alle Maße
   stehen hier in Bildpunkten. Ein SVG hätte auf einem Telefon auch die Schrift
   und die Punkte mitverkleinert (siehe RoadStrip in chart.go). */
.strip {
  --strip-track: 12px;
  --strip-dot: 10px;
  --strip-row: 13px;

  margin-bottom: var(--s-4);
}

/* Platz über der Bahn, aber nur so viel, wie Reihen belegt sind. */
.strip--rows1 {
  padding-top: var(--strip-row);
}

.strip--rows2 {
  padding-top: calc(2 * var(--strip-row));
}

/* Die Bahn ist breiter als der Bereich der Meldungen: Ihre runden Kappen ragen
   um einen halben Punkt über die erste und die letzte Meldung hinaus, statt von
   ihnen überdeckt zu werden. */
.strip__band {
  padding: 0 calc(var(--strip-dot) / 2);
  background: var(--c-surface-3);
  border: 1px solid var(--c-border);
  border-radius: 999px;
}

.strip__marks {
  position: relative;
  height: var(--strip-track);
}

.strip__event,
.strip__stretch {
  position: absolute;
  border: 1.5px solid var(--c-surface);
  box-sizing: border-box;
}

.strip__event {
  top: calc(50% - var(--strip-dot) / 2 - var(--strip-lift, 0px));
  width: var(--strip-dot);
  height: var(--strip-dot);
  margin-left: calc(var(--strip-dot) / -2);
  border-radius: 50%;
}

.strip__stretch {
  top: calc(-1px - var(--strip-lift, 0px));
  height: calc(var(--strip-track) + 2px);
  min-width: var(--strip-dot);
  transform: translateX(-50%);
  border-radius: 999px;
}

/* Eine gestapelte Meldung steht über ihrer Stelle und ist über einen Halm mit
   ihr verbunden – ohne ihn schwebt sie über der Strecke, und die Zuordnung
   wäre geraten. Der Halm reicht von der Unterkante bis zur Mitte der Bahn,
   und die liegt bei einem Punkt und einem Balken unterschiedlich tief. */
.strip__row1 {
  --strip-lift: var(--strip-row);
}

.strip__row2 {
  --strip-lift: calc(2 * var(--strip-row));
}

.strip__row1::before,
.strip__row2::before {
  content: "";
  position: absolute;
  top: 100%;
  left: calc(50% - 1px);
  width: 2px;
  background: var(--c-border-strong);
}

.strip__event.strip__row1::before,
.strip__event.strip__row2::before {
  height: calc(var(--strip-lift) - var(--strip-dot) / 2);
}

.strip__stretch.strip__row1::before,
.strip__stretch.strip__row2::before {
  height: calc(var(--strip-lift) - var(--strip-track) / 2 - 1px);
}

/* Ohne Zeitangabe bleibt die Meldung neutral. Die Farbskala beginnt erst dort,
   wo eine Messung vorliegt (siehe delayTone in chart.go). */
.strip__event--none,
.strip__stretch--none {
  background: var(--c-cluster);
}

.strip__event--good,
.strip__stretch--good {
  background: var(--c-level-good);
}

.strip__event--normal,
.strip__stretch--normal {
  background: var(--c-level-normal);
}

.strip__event--busy,
.strip__stretch--busy {
  background: var(--c-level-busy);
}

.strip__event--bad,
.strip__stretch--bad {
  background: var(--c-level-bad);
}

/* Die Ortsnamen. Der Teilstrich steht an der genauen Stelle, der Name darunter
   mittig – außer an den Enden, wo er am Rand bleibt. */
.strip__names {
  position: relative;
  height: 24px;
  margin: 0 calc(var(--strip-dot) / 2);
}

.strip__tick {
  position: absolute;
  top: 3px;
  width: 1px;
  height: 5px;
  background: var(--c-border-strong);
}

/* Die Breite ist begrenzt, weil ein Landkreisname länger sein kann als die
   halbe Spalte. Die Schätzung in fitLabels hält die Namen auf einer breiten
   Spalte auseinander; auf einer schmalen bleiben nur die beiden Enden, und die
   dürfen sich auch dort nicht berühren. */
.strip__label {
  position: absolute;
  top: 11px;
  max-width: 46%;
  overflow: hidden;
  transform: translateX(-50%);
  color: var(--c-muted);
  font-size: 11px;
  line-height: 1.1;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.strip__label--first,
.strip__label--last {
  transform: none;
}

/* Auf schmalen Spalten reicht der Platz nur für die Enden der Strecke. Zwei
   lesbare Namen sind mehr wert als sechs, die sich überschreiben – die
   Schätzung in fitLabels rechnet mit einer Spalte von 620 Bildpunkten. */
@media (max-width: 620px) {
  .strip__label,
  .strip__tick {
    display: none;
  }

  .strip__label--first,
  .strip__label--last,
  .strip__tick--first,
  .strip__tick--last {
    display: block;
  }
}

/* Der senkrechte Autobahnverlauf: eine stilisierte Fahrbahn links, die
   Ereignisse rechts in der Reihenfolge der Strecke. */
.course {
  display: flex;
  gap: var(--s-3);
  margin-top: var(--s-2);
}

.course__lane {
  flex: 0 0 18px;
  min-height: 4rem;
  border-radius: 4px;
  background: var(--c-text);
  position: relative;
}

.course__lane::after {
  content: "";
  position: absolute;
  top: 10px;
  bottom: 10px;
  left: 50%;
  width: 2px;
  transform: translateX(-50%);
  background: repeating-linear-gradient(
    to bottom,
    var(--c-surface) 0 7px,
    transparent 7px 14px
  );
}

.course__side {
  flex: 1;
  min-width: 0;
}

.course__end {
  margin: 0;
  color: var(--c-muted);
  font-size: var(--t-small);
}

.course__list {
  margin: var(--s-2) 0;
  padding: 0;
  list-style: none;
}

.course__item {
  padding: var(--s-2) 0;
  border-top: 1px solid var(--c-border);
}

.course__item:first-child {
  border-top: 0;
  padding-top: 0;
}

.course__title {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  margin: 0;
  font-size: var(--t-small);
}

.course__title .icon {
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  margin-top: 0.1em;
}

.course__meta {
  margin: var(--s-1) 0 0 1.75rem;
  color: var(--c-muted);
  font-size: var(--t-small);
}

/* ------------------------------------------------------------ 7. Karte */

.map {
  --map-canvas-h: clamp(19rem, 48vh, 30rem);
  position: relative;
  display: flex;
  flex-direction: column;
  margin: 0 0 var(--s-5);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.map__canvas {
  /* Die Höhe wächst mit dem Viewport, bleibt aber in Grenzen: auf dem Telefon
     soll die Liste unter der Karte noch anreißen. Im dreispaltigen Layout hat
     die Mittelspalte mehr Platz, deshalb darf die Karte dort höher werden. */
  height: var(--map-canvas-h);
  background: var(--c-surface-2);
  z-index: 0;
}

@media (min-width: 62rem) {
  .map {
    --map-canvas-h: clamp(24rem, 56vh, 40rem);
  }
}

.map__fallback {
  margin: 0;
  padding: var(--s-4);
  color: var(--c-muted);
  font-size: var(--t-small);
  text-align: center;
}

/* Sobald die Island übernommen hat, verschwindet der Hinweis. */
.map[data-island-ready] .map__fallback {
  display: none;
}

/* Die Island hängt ihre Bedienung in einen eigenen Knoten unter der Karte.
   display: contents löst diesen Knoten aus dem Layout, damit seine Teile
   entweder über der Karte liegen (Werkzeuge, Statusfeld) oder unter ihr in der
   Reihe stehen (Filterleiste) – ohne dass React die Kartenfläche anfassen
   muss. */
.map__controls {
  display: contents;
}

/* Einstellungsleiste über der Karte, bündig am rechten Rand. Eine Fläche
   statt einzelner Pillen, damit die Karte ruhig bleibt. Zugeklappt nur das
   Symbol; an oder aus steht in Grund und Symbolfarbe. Der Anfasser oben
   schiebt die Namen dazu. */
.map__rail {
  --rail-icon: 1.35rem;
  --rail-pad: 0.55rem;
  --rail-gap: 0.55rem;
  position: absolute;
  top: 0;
  right: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  width: calc(var(--rail-pad) * 2 + var(--rail-icon));
  max-height: 100%;
  overflow: hidden;
  border: 1px solid var(--c-border);
  border-top: 0;
  border-right: 0;
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
  background: color-mix(in srgb, var(--c-surface) 92%, transparent);
  backdrop-filter: blur(8px);
  box-shadow: var(--shadow);
  transition: width 180ms ease;
}

.map__rail--open {
  width: 12.5rem;
}

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

.map__rail-handle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0;
  width: 100%;
  min-height: 2.35rem;
  padding: 0.4rem var(--rail-pad);
  border: 0;
  border-bottom: 1px solid var(--c-border);
  border-radius: 0;
  background: transparent;
  color: var(--c-muted);
  font: inherit;
  font-size: var(--t-small);
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
}

.map__rail--open .map__rail-handle {
  justify-content: flex-start;
  gap: var(--rail-gap);
}

.map__rail-handle .icon {
  flex: none;
  width: var(--rail-icon);
  height: var(--rail-icon);
  color: inherit;
}

.map__rail-handle:hover {
  color: var(--c-text);
  background: color-mix(in srgb, var(--c-surface-2) 70%, transparent);
}

.map__rail-list {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: auto;
  padding: 0.15rem 0;
}

.map__rail-item {
  display: grid;
  grid-template-columns: var(--rail-icon) minmax(0, 1fr);
  column-gap: var(--rail-gap);
  align-items: center;
  width: 100%;
  min-height: 2.25rem;
  padding: 0.4rem var(--rail-pad);
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--c-text);
  font: inherit;
  font-size: var(--t-small);
  font-weight: 600;
  line-height: 1.2;
  text-align: start;
  cursor: pointer;
}

.map__rail-item--action {
  border-top: 1px solid var(--c-border);
}

.map__rail-item--action + .map__rail-item--action {
  border-top: 0;
}

.map__rail-item .icon {
  width: var(--rail-icon);
  height: var(--rail-icon);
  justify-self: center;
  color: var(--c-muted);
}

.map__rail-item:hover {
  background: color-mix(in srgb, var(--c-surface-2) 80%, transparent);
}

.map__rail-item:hover .icon {
  color: var(--c-primary);
}

/* Eingeschaltet: die Zeile trägt die Farbe der Ebene, nicht nur das Symbol.
   Besonders Stahl (VWD) wäre als reine Symbolfarbe vom gedämpften Aus-Zustand
   kaum zu trennen. Ohne eigene Farbe (Verkehrsfluss) bleibt die Handlung.
   Vollbild hat keinen Dauerzustand. */
.map__rail-item--on,
.map__rail-item:has(input:checked) {
  background: color-mix(in srgb, var(--c-type, var(--c-primary)) 34%, var(--c-surface));
  box-shadow: inset 3px 0 0 var(--c-type, var(--c-primary));
  color: var(--c-type, var(--c-primary-strong));
}

.map__rail-item--on:hover,
.map__rail-item:has(input:checked):hover {
  background: color-mix(in srgb, var(--c-type, var(--c-primary)) 44%, var(--c-surface));
}

.map__rail-item--on .icon,
.map__rail-item:has(input:checked) .icon {
  color: var(--c-type, var(--c-primary));
}

.map__rail-item--on:hover .icon,
.map__rail-item:has(input:checked):hover .icon {
  color: var(--c-type, var(--c-primary-strong));
}

.map__rail-name {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  opacity: 0;
  transition: opacity 140ms ease;
}

.map__rail-handle .map__rail-name {
  flex: 0 1 0;
  width: 0;
  min-width: 0;
}

.map__rail--open .map__rail-handle .map__rail-name {
  flex: 1 1 auto;
  width: auto;
}

.map__rail--open .map__rail-name {
  opacity: 1;
}

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

/* Verkehrsfluss: echte Checkbox, sichtbar bleibt nur das Symbol. Die
   Pillen der Meldungsarten verstecken die Checkbox in der Pille selbst;
   hier sagt die Farbe des Symbols an oder aus. */
.map__rail-item input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.map__rail-item:has(input:focus-visible),
.map__rail-handle:focus-visible,
.map__rail-item:focus-visible {
  outline: 3px solid var(--c-focus);
  outline-offset: -3px;
}

/* Hinweis auf Orts- und Autobahnkarten: nur dieser Bestand, alles andere
   steht auf der Startseite. Eine Leiste oben auf der Fläche, bündig an der
   linken Kante und vor der Einstellungsleiste. */
.map__scope {
  position: absolute;
  top: 0;
  left: 0;
  right: 2.5rem;
  z-index: 11;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin: 0;
  padding: 0.55rem 0.85rem 0.55rem 0.95rem;
  background: color-mix(in srgb, var(--c-surface) 94%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--c-border);
  box-shadow: var(--shadow-sm);
  color: var(--c-text);
  font-size: var(--t-small);
  line-height: 1.35;
}

.map__scope::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 3px;
  background: var(--grad-brand);
}

.map__rail--open ~ .map__scope {
  right: 12.6rem;
}

.map__scope .icon {
  width: 1.15rem;
  height: 1.15rem;
  color: var(--c-primary);
}

.map__scope-copy {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.15rem 0.55rem;
  margin: 0;
}

.map__scope-link {
  font-weight: 650;
  color: var(--c-primary);
  text-decoration: none;
  white-space: nowrap;
}

.map__scope-link:hover {
  color: var(--c-primary-strong);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.map:has(.map__scope) .map__status {
  top: 3.35rem;
}

/* Statusfeld über der Karte, links oben: es beschreibt, was gerade zu sehen
   ist, und darf dafür keine Zeile im Layout beanspruchen.

   Links unten stand es einmal – und lag damit auf der Legende: Die Bedienung
   ist im Layout durchsichtig (display: contents), ihre absoluten Teile beziehen
   sich also auf die ganze Kartenkarte, und deren Unterkante ist die Unterkante
   der Legendenzeile, nicht die der Kartenfläche. Oben links gibt es diese
   Verwechslung nicht: dort ist die Oberkante beider dieselbe. */
.map__status {
  position: absolute;
  top: var(--s-2);
  left: var(--s-2);
  right: 3.25rem;
  z-index: 10;
  margin: 0;
  padding: var(--s-1) var(--s-3);
  width: fit-content;
  max-width: calc(100% - 3.25rem);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--c-surface) 92%, transparent);
  backdrop-filter: blur(8px);
  color: var(--c-muted);
  font-size: var(--t-small);
  box-shadow: var(--shadow-sm);
}

.map__status:empty {
  display: none;
}

/* Der Knopf, mit dem ein Ausschnitt ins Radar wandert. Er sitzt auf der
   Kartenfläche, unten links – unabhängig von der Legende darunter. */
.map__radar {
  position: absolute;
  left: var(--s-2);
  top: calc(var(--map-canvas-h) - 3.4rem);
  z-index: 20;
}

.map__radar-add {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.55rem 0.95rem;
  border: 0;
  border-radius: var(--radius-pill);
  background-image: var(--grad-brand);
  color: #fff;
  font: inherit;
  font-size: var(--t-small);
  font-weight: 650;
  letter-spacing: 0.01em;
  text-decoration: none;
  box-shadow: var(--shadow-brand);
  cursor: pointer;
}

.map__radar-add .icon {
  width: 1.15rem;
  height: 1.15rem;
  flex: none;
}

.map__radar-add:hover {
  color: #fff;
  filter: saturate(1.08) brightness(1.06);
}

.map__radar-add--done {
  background-image: none;
  background: color-mix(in srgb, var(--c-surface) 92%, transparent);
  color: var(--c-primary-strong);
  border: 1px solid color-mix(in srgb, var(--c-primary) 35%, var(--c-border));
  box-shadow: var(--shadow-sm);
  backdrop-filter: blur(8px);
}

.map__radar-add--done:hover {
  color: var(--c-primary-strong);
  filter: none;
  border-color: var(--c-primary);
}

.map--full .map__radar {
  top: auto;
  bottom: 6.5rem;
}

/* Berührungsregel vor der Zuwendung: senkrecht scrollt die Seite, damit ein
   Wischen über die Karte nicht in ihr endet. Nach dem Antippen setzt Leaflets
   eigene Regel wieder, und der Finger schiebt die Karte in jede Richtung.

   Die Klassen der Kartenfläche stehen beide im Selektor, weil Leaflets Regel
   `touch-action: none` gleich viele Klassen zählt und später geladen wird – ein
   kürzerer Selektor würde stillschweigend verlieren. */
.map:not(.map--engaged) .map__canvas.leaflet-container {
  touch-action: pan-y;
}

/* Hinweis in der Mitte der Karte, wenn das Rad noch der Seite gehört. Er liegt
   über der Kartenfläche, nimmt aber keine Klicks an: der Klick, auf den er
   hinweist, muss durch ihn hindurch bei der Karte ankommen. */
.map__hint {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 10;
  margin: 0;
  padding: var(--s-2) var(--s-4);
  max-width: calc(100% - 2 * var(--s-5));
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--c-text) 88%, transparent);
  color: var(--c-bg);
  font-size: var(--t-small);
  font-weight: 600;
  text-align: center;
  box-shadow: var(--shadow-lg);
  pointer-events: none;
  animation: fade-in 120ms ease-out;
}

/* Bewegliche Blitzer unter Zoom 11: keine Koordinaten, nur dieser Hinweis.
   Er sitzt am unteren Rand der Kartenfläche, nicht in der Mitte, und fängt
   keine Klicks, damit Zoomen weiter die Karte trifft (D-079). */
.map__zoom-banner {
  position: absolute;
  top: var(--map-canvas-h);
  left: 50%;
  transform: translate(-50%, calc(-100% - var(--s-3)));
  z-index: 10;
  margin: 0;
  padding: var(--s-2) var(--s-4);
  max-width: calc(100% - 2 * var(--s-5));
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--c-text) 88%, transparent);
  color: var(--c-bg);
  font-size: var(--t-small);
  font-weight: 600;
  text-align: center;
  box-shadow: var(--shadow-lg);
  pointer-events: none;
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

/* Filterleiste unter der Karte. Sie gehört in den Fluss: eine Auswahl, die
   den Kartengrund überdeckt, verdeckt genau das, worüber sie entscheidet. */
.map__bar {
  padding: var(--s-3);
  border-top: 1px solid var(--c-border);
  background: var(--c-surface);
}

.map__barhead {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2) var(--s-3);
  margin-bottom: var(--s-2);
}

.map__barlabel {
  font-size: var(--t-small);
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-muted);
  margin-right: auto;
}

.map__link {
  padding: 0;
  border: 0;
  background: none;
  color: var(--c-primary);
  font: inherit;
  font-size: var(--t-small);
  font-weight: 600;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.map__link:hover {
  color: var(--c-primary-strong);
}

/* Zwischen 62rem und 45rem steht die Auswahl in einer Zeile und wird waagrecht
   geschoben; ab 45rem bricht sie um. Unter 62rem hebt Abschnitt 10 das auf: Die
   Legende schaltet die Ebenen, und was man nicht sieht, kann man nicht
   ausschalten – die 200 Pixel, die der Umbruch dort kostet, sind das wert
   (D-098). */
.map__legend {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--s-2);
  margin: 0;
  /* Senkrecht Platz für den Fokusring, waagrecht für die letzte Pille. */
  padding: 3px;
  border: 0;
  /* Ein fieldset schrumpft von sich aus nicht unter seinen Inhalt: ohne diese
     Zeile wird die Leiste 1067 Pixel breit und von der Karte abgeschnitten,
     statt sich schieben zu lassen. */
  min-inline-size: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}

.map__legend::-webkit-scrollbar {
  display: none;
}

@media (min-width: 45rem) {
  .map__legend {
    flex-wrap: wrap;
    overflow-x: visible;
  }
}

/* Echte Checkboxen statt der klickbaren div-Elemente des Altsystems: damit
   funktioniert die Legende auch mit Tastatur und Screenreader. Sichtbar ist
   davon nur die Zeichnung – die Pille selbst zeigt den Zustand. */
.map__legend-item {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-pill);
  background: var(--c-surface);
  font-size: var(--t-small);
  font-weight: 550;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
}

.map__legend-item input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.map__legend-item:has(input:focus-visible) {
  outline: 3px solid var(--c-focus);
  outline-offset: 2px;
}

/* Eine eingeschaltete Pille trägt die Farbe ihrer Meldungsart, nicht die der
   Marke. Neun Schalter in einer Markenfarbe hätten gezeigt, dass sie an sind, und
   verschwiegen, wozu sie gehören.

   Die Farbe bleibt die des eigenen Token-Satzes und nicht die der Zeichnung
   daneben: Sie verbindet die Pille mit der Meldungskarte und dem Balken in der
   Aufteilung, wo dieselbe Art in derselben Farbe steht. Die Zeichnungen kennen
   diese Unterscheidung nicht – bei ihnen sind alle Ereignisarten orange. */
.map__legend-item:has(input:checked) {
  border-color: color-mix(in srgb, var(--c-type, var(--c-primary)) 45%, var(--c-border));
  background: color-mix(in srgb, var(--c-type, var(--c-primary)) 9%, var(--c-surface));
  color: var(--c-type, var(--c-primary-strong));
}

.map__legend-item:not(:has(input:checked)) {
  color: var(--c-muted);
}

/* Die Zeichnung ist die Verbindung zwischen Schalter und Marker: dasselbe Bild
   in der Pille wie auf der Kartenfläche. Vorher stand hier ein Farbpunkt, der
   die Farbe teilte, aber nicht die Form (D-063).

   Ausgeschaltet verliert sie ihre Farbe: Ein Fünftel Sättigung, etwas
   abgedunkelt, nimmt der Pille die Behauptung, ihre Art liege auf der Karte –
   und lässt die Form deutlich genug stehen, dass die Legende weiter erklärt,
   was sie erklären soll. */
.map__pin {
  width: 1.625rem;
  height: 1.625rem;
  flex: none;
  /* Der obere Rand der Zeichnung ist Luft, der untere die Spitze. Ein kleiner
     Ausgleich stellt sie auf die Schriftlinie. */
  margin-block: -2px;
}

.map__legend-item:not(:has(input:checked)) .map__pin {
  filter: saturate(0.2) brightness(0.85) opacity(0.7);
}

/* Hinweis, dass die Auswahl im weiten Ausschnitt noch nicht greift. Er
   beansprucht eine ganze Zeile, damit er nicht zwischen den Schaltern
   verschwindet. */
.map__note {
  margin: var(--s-2) 0 0;
  color: var(--c-muted);
  font-size: var(--t-small);
}

/* Vollbild. Kein Fullscreen-API, sondern eine feste Positionierung: die
   Browser-Vollbildschicht nimmt die Seite aus dem Dokumentfluss, und die
   Bedienleiste der Island hinge dann außerhalb. So bleibt alles zusammen, und
   Escape schließt es genauso. */
.map--full {
  position: fixed;
  inset: 0;
  z-index: 60;
  margin: 0;
  border: 0;
  border-radius: 0;
  padding-bottom: env(safe-area-inset-bottom);
}

.map--full .map__canvas {
  flex: 1 1 auto;
  height: auto;
  min-height: 0;
}

.map--full .map__zoom-banner {
  /* Im Vollbild ist die Kartenhöhe flexibel; die Filterleiste bleibt unten
     im Fluss. Der Hinweis sitzt knapp darüber, nicht in der Bildmitte. */
  top: auto;
  bottom: 6.5rem;
  transform: translate(-50%, 0);
}

.map--full .map__rail {
  top: 0;
  right: 0;
}

/* Solange die Karte den Bildschirm füllt, soll die Seite darunter nicht
   mitscrollen. */
html:has(.map--full) {
  overflow: hidden;
}

/* Kartenmarker einzelner Meldungen: die Pin-Zeichnungen von Blitzer.de, von
   Leaflet als divIcon eingesetzt (D-063). Die Maße stehen zusätzlich als
   Attribute am Bild, weil Leaflet den Ankerpunkt daraus rechnet – hier steht
   nur, was das Bild darüber hinaus braucht.

   Der Schatten kommt aus dem Stylesheet und nicht als zweite Zeichnung unter
   dem Pin, wie in der App: eine Datei weniger je Marker, und die Form folgt
   der Zeichnung statt einer Ellipse darunter. */
.pin {
  display: block;
  filter: drop-shadow(0 2px 3px rgb(26 20 64 / 35%));
}

/* Sammelmarker. Sie sind keine Meldung, sondern eine Anzahl – ein Kreis mit
   dieser Zahl sagt das, eine Zeichnung nicht. */
.marker--cluster {
  display: grid;
  place-items: center;
  width: 2.375rem;
  height: 2.375rem;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 2px 6px rgb(26 20 64 / 40%);
  background: var(--c-cluster);
  color: #fff;
  font-size: var(--t-small);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Bewegliche Blitzer: Zahlen auf dem 4×3-Raster, Blitzerblau statt Grau,
   etwas größer, damit sie neben den übrigen Sammelpunkten als eigene Auskunft
   lesbar bleibt (D-079). */
.marker--camera-cluster {
  width: 2.75rem;
  height: 2.75rem;
  background: var(--c-camera);
}

.popup {
  font-size: var(--t-small);
  min-width: 12rem;
}

.popup__title {
  margin: 0 0 var(--s-1);
  font-weight: 650;
}

.popup__facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0 var(--s-2);
  margin: 0;
  font-size: var(--t-small);
}

.popup__facts dt {
  color: var(--c-muted);
}

.popup__facts dd {
  margin: 0;
}

.popup__link {
  display: inline-block;
  margin-top: var(--s-2);
  font-size: var(--t-small);
  font-weight: 600;
}

/* Hülle des Markers. Leaflet setzt hier sonst leaflet-div-icon mit weißem
   Kasten und Rahmen; die eigene Klasse verhindert das. */
.marker-host {
  background: none;
  border: 0;
}

/* Stauverläufe werden als gestrichelte, laufende Linie dargestellt – das
   visuelle Merkmal des Altsystems, dort über setInterval erzeugt, hier über
   eine CSS-Animation ohne Zeitgeber im JavaScript. */
/* Die Linienfarbe kommt aus derselben Zuordnung wie die Marker. Leaflet setzt
   stroke über ein Attribut, das von dieser Regel überschrieben wird. */
.stretch {
  stroke: var(--c-type, var(--c-cluster));
  stroke-dasharray: 10 8;
  animation: flow 1.2s linear infinite;
}

/* Sperrungen zeichnet Leaflet in drei Lagen (Rand, Bahn, Baken). Die
   laufende Stau-Strichelung gilt für sie nicht – auch falls noch eine
   .stretch--closure-Klasse anliegt. */
.stretch--closure {
  animation: none;
}

/* Zeichen 250: roter Kreis, weißer Balken. Die Maße setzt Leaflet am Host. */
.closure-sign {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: #f92241;
  box-shadow: 0 0 0 1px #111, 0 1px 2px rgb(0 0 0 / 35%);
  position: relative;
}

.closure-sign::after {
  content: "";
  position: absolute;
  left: 18%;
  right: 18%;
  top: 42%;
  height: 16%;
  border-radius: 1px;
  background: #fff;
}

@keyframes flow {
  to {
    stroke-dashoffset: -18;
  }
}

/* Leaflets eigene Bedienelemente an das Design angepasst. Ohne diese Regeln
   sitzen quadratische Kästen mit 1px-Rahmen aus dem Jahr 2011 auf einer
   ansonsten runden Oberfläche. */
.leaflet-container {
  font-family: var(--font);
  background: var(--c-surface-2);
}

/* Die Wärmekarte fängt keine Zeiger: Zoomen und Verschieben gehören der Karte. */
.leaflet-heatmap-layer {
  pointer-events: none;
}

.leaflet-bar,
.leaflet-control-attribution {
  border: 1px solid var(--c-border) !important;
  border-radius: var(--radius-pill) !important;
  box-shadow: var(--shadow) !important;
  overflow: hidden;
}

.leaflet-bar a,
.leaflet-bar a:hover {
  width: 2.25rem;
  height: 2.25rem;
  line-height: 2.25rem;
  background: color-mix(in srgb, var(--c-surface) 92%, transparent);
  color: var(--c-text);
  border-bottom-color: var(--c-border);
  font-size: 1.25rem;
}

.leaflet-bar a:hover {
  color: var(--c-primary);
}

.leaflet-control-attribution {
  padding: 0 var(--s-2);
  background: color-mix(in srgb, var(--c-surface) 88%, transparent) !important;
  color: var(--c-muted);
  font-size: 0.6875rem;
}

.leaflet-control-attribution a {
  color: var(--c-muted);
}

.map-zoom-level {
  font-variant-numeric: tabular-nums;
  color: var(--c-text);
}

.leaflet-popup-content-wrapper {
  border-radius: var(--radius) !important;
  background: var(--c-surface);
  color: var(--c-text);
  box-shadow: var(--shadow-lg);
}

.leaflet-popup-content {
  margin: var(--s-3);
  line-height: 1.5;
}

.leaflet-popup-tip {
  background: var(--c-surface);
}

.leaflet-popup-close-button {
  color: var(--c-muted) !important;
  padding: var(--s-2) var(--s-2) 0 0 !important;
}

/* ------------------------------------------------------------ 8. Betrieb */

/* Der Betriebsbereich hat ein eigenes Gerüst und ein eigenes Farbbild.
   Lagebild-Tokens gelten hier nicht: kein Graphit-Verlauf, keine Kiefer,
   keine Aurora. Grund ist eigenes Schiefer, die Aktion ein Cyan – flache Flächen,
   engerer Radius, neutrale Schatten.

   Unter 62rem stapelt sich das: erst die Navigation als Streifen, dann der
   Inhalt. Die Einträge schieben waagrecht, damit sieben Punkte auf einem
   Telefon erreichbar bleiben, ohne die Seite zu verlängern. */
.admin-body {
  min-height: 100vh;
  --c-bg: #eef0f3;
  --c-surface: #ffffff;
  --c-surface-2: #e6e9ee;
  --c-surface-3: #d6dbe2;
  --c-text: #12171c;
  --c-muted: #5b6570;
  --c-border: #d3d8de;
  --c-border-strong: #b4bcc6;
  --c-primary: #0c6a82;
  --c-primary-strong: #084e60;
  --c-primary-soft: #e4f1f5;
  --c-accent: #0c6a82;
  --c-accent-strong: #084e60;
  --c-accent-soft: #e4f1f5;
  --c-focus: #0e7490;
  --c-action: #0b6474;
  --c-action-hover: #084e5b;
  --c-action-ink: #ffffff;
  --grad-brand: none;
  --grad-tint: none;
  --grad-aurora: none;
  --shadow-sm: 0 1px 2px rgb(18 23 28 / 6%);
  --shadow: 0 1px 2px rgb(18 23 28 / 5%), 0 6px 16px -8px rgb(18 23 28 / 14%);
  --shadow-lg: 0 2px 4px rgb(18 23 28 / 7%), 0 16px 36px -12px rgb(18 23 28 / 20%);
  --shadow-brand: none;
  --radius: 0.5rem;
  --radius-sm: 0.375rem;
  --radius-lg: 0.75rem;
  --radius-pill: 0.5rem;
  background: var(--c-bg);
  background-image: none;
  color: var(--c-text);
}

@media (prefers-color-scheme: dark) {
  .admin-body {
    --c-bg: #0d1014;
    --c-surface: #161a20;
    --c-surface-2: #1e242c;
    --c-surface-3: #272e38;
    --c-text: #e8edf2;
    --c-muted: #93a0ad;
    --c-border: #2c343e;
    --c-border-strong: #3d4754;
    --c-primary: #5ecad6;
    --c-primary-strong: #9adbe4;
    --c-primary-soft: #14343a;
    --c-accent: #5ecad6;
    --c-accent-strong: #9adbe4;
    --c-accent-soft: #14343a;
    --c-focus: #22d3ee;
    --c-action: #0b6474;
    --c-action-hover: #0e7d8f;
    --c-action-ink: #ffffff;
  }
}

.admin-body .button {
  background-image: none;
  background-color: var(--c-action);
  color: var(--c-action-ink);
  border-radius: var(--radius-sm);
  box-shadow: none;
}

.admin-body .button:hover {
  color: var(--c-action-ink);
  background-color: var(--c-action-hover);
  filter: none;
  transform: none;
}

.admin {
  min-height: 100vh;
  display: grid;
  align-items: start;
}

.admin__nav {
  background: var(--c-surface);
  border-bottom: 1px solid var(--c-border);
  padding: var(--s-3);
  display: grid;
  gap: var(--s-3);
}

.admin-brand {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  color: inherit;
  text-decoration: none;
  min-width: 0;
}

.admin-brand:hover {
  color: inherit;
}

.admin-brand__mark {
  width: 1.85rem;
  height: 1.85rem;
  flex: none;
  padding: 0.28rem;
  border-radius: var(--radius-sm);
  background: var(--c-action);
  color: var(--c-action-ink);
}

.admin-brand__text {
  display: grid;
  line-height: 1.2;
  min-width: 0;
}

.admin-brand__kicker {
  font-size: 0.6875rem;
  font-weight: 650;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-primary);
}

.admin-brand__name {
  font-weight: 700;
  letter-spacing: -0.02em;
}

.admin-brand__tld {
  color: var(--c-muted);
  font-weight: 550;
}

.admin-menu {
  display: grid;
  gap: var(--s-5);
  min-width: 0;
  align-content: start;
}

.admin-menu__group {
  display: grid;
  gap: var(--s-4);
  align-content: start;
}

.admin-menu__title {
  display: block;
  margin: 0;
  padding: 0 0.45rem;
  font-size: 0.6875rem;
  font-weight: 650;
  letter-spacing: 0.08em;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--c-muted);
}

.admin-menu__list {
  display: grid;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.admin-menu__list li {
  min-width: 0;
}

.admin-menu__link {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 0.35rem 0.55rem;
  border-radius: var(--radius-sm);
  color: var(--c-text);
  text-decoration: none;
  background: var(--c-surface-2);
  border: 1px solid transparent;
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}

.admin-menu__link:hover {
  color: var(--c-primary-strong);
  background: var(--c-primary-soft);
  border-color: color-mix(in srgb, var(--c-primary) 22%, transparent);
}

.admin-menu__link--active {
  color: var(--c-primary-strong);
  background: var(--c-primary-soft);
  border-color: color-mix(in srgb, var(--c-primary) 28%, transparent);
  box-shadow: var(--shadow-sm);
}

.admin-menu__icon {
  display: grid;
  place-items: center;
  width: 1.35rem;
  height: 1.35rem;
  flex: none;
  border-radius: var(--radius-sm);
  background: var(--c-surface);
  color: var(--c-primary);
}

.admin-menu__icon .icon {
  width: 0.9rem;
  height: 0.9rem;
}

.admin-menu__link--active .admin-menu__icon {
  background: var(--c-action);
  color: var(--c-action-ink);
}

.admin-menu__copy {
  display: grid;
  gap: 0;
  min-width: 0;
}

.admin-menu__label {
  font-weight: 650;
  line-height: 1.15;
}

.admin-menu__hint {
  display: none;
  font-size: 0.6875rem;
  color: var(--c-muted);
  line-height: 1.2;
}

@media (max-width: 61.98rem) {
  .admin-menu {
    display: flex;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: var(--s-2);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
    padding-bottom: var(--s-1);
  }

  .admin-menu__group {
    display: contents;
  }

  .admin-menu__title {
    display: none;
  }

  .admin-menu__list {
    display: flex;
    gap: var(--s-1);
  }

  .admin-menu__list li {
    flex: none;
  }
}

.admin__stage {
  min-width: 0;
  padding: var(--s-5) var(--s-4) var(--s-7);
}

.admin-top {
  margin-bottom: var(--s-5);
}

.admin-top .lead {
  margin-bottom: 0;
}

.admin-empty {
  display: grid;
  justify-items: start;
  gap: var(--s-3);
  padding: var(--s-6);
  border-radius: var(--radius-lg);
  background: var(--c-surface);
  border: 1px dashed var(--c-border-strong);
  box-shadow: var(--shadow-sm);
  max-width: 40rem;
}

.admin-empty h2 {
  margin: 0;
}

.admin-empty p {
  margin: 0;
  color: var(--c-muted);
  max-width: 32rem;
}

.admin-empty__mark {
  display: flex;
  gap: 0.45rem;
  margin-bottom: var(--s-1);
}

.admin-empty__dot {
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: var(--c-action);
  opacity: 0.45;
}

.admin-empty__dot:nth-child(2) {
  opacity: 0.7;
  transform: translateY(-0.15rem);
}

.admin-empty__dot:nth-child(3) {
  opacity: 1;
}

.admin-panel {
  display: grid;
  gap: var(--s-5);
  max-width: 72rem;
}

.admin-panel__head h2 {
  margin: 0 0 var(--s-2);
}

.admin-panel__head p {
  margin: 0;
  color: var(--c-muted);
  max-width: 46rem;
}

.admin-flash,
.admin-note,
.admin-error,
.admin-path {
  margin: 0;
}

.admin-flash {
  padding: var(--s-3) var(--s-4);
  border-radius: var(--radius);
  background: var(--c-primary-soft);
  border: 1px solid color-mix(in srgb, var(--c-primary) 28%, transparent);
}

.admin-note,
.admin-path {
  color: var(--c-muted);
  font-size: var(--t-small);
}

.admin-error {
  color: #8b1e1e;
}

@keyframes admin-warn-blink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0.12; }
}

.admin-anomaly {
  display: flex;
  align-items: flex-start;
  gap: 0.65rem;
  margin: 0.85rem 0 0;
  padding: 0.7rem 0.85rem;
  border-radius: var(--radius);
  background: #8b1e1e;
  color: #fff8f8;
  font-weight: 650;
  line-height: 1.35;
}

.admin-anomaly__icon,
.admin-import__warn {
  flex: none;
  width: 1.2rem;
  height: 1.2rem;
  animation: admin-warn-blink 0.9s step-end infinite;
}

.admin-menu__link--alert,
.admin-menu__link--alert:hover,
.admin-menu__link--alert.admin-menu__link--active {
  color: #fff8f8;
  background: #8b1e1e;
  border-color: #6d1515;
}

.admin-menu__link--alert .admin-menu__hint {
  color: #ffd4d4;
}

.admin-menu__link--alert .admin-menu__icon {
  background: #6d1515;
  color: #ffe08a;
}

.admin-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
}

button.button {
  border: 0;
  cursor: pointer;
  font: inherit;
}

button.button:disabled {
  opacity: 0.55;
  cursor: wait;
  transform: none;
  filter: none;
}

.admin-stats {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: var(--s-3);
  margin: 0;
}

.admin-stats--five {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}

@media (max-width: 61.99rem) {
  .admin-stats--five {
    grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  }
}

.admin-stats > div {
  padding: var(--s-4) var(--s-3);
  border-radius: var(--radius);
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  box-shadow: var(--shadow-sm);
}

.admin-stats dt {
  font-size: 0.75rem;
  color: var(--c-muted);
}

.admin-stats dd {
  margin: 0.2rem 0 0;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

.admin-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  overflow: hidden;
}

.admin-table caption {
  text-align: left;
  font-weight: 650;
  padding-bottom: var(--s-2);
}

.admin-table th,
.admin-table td {
  text-align: left;
  padding: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--c-border);
  font-variant-numeric: tabular-nums;
}

.admin-table th {
  font-size: 0.75rem;
  color: var(--c-muted);
  font-weight: 650;
}

.admin-table tr:last-child td {
  border-bottom: 0;
}

.admin-table__hot td {
  background: color-mix(in srgb, var(--c-danger, #b42318) 8%, transparent);
}

.admin-table__local {
  color: var(--c-muted);
  font-size: var(--t-small);
}

.admin-table__ref {
  display: inline-block;
  max-width: 28rem;
  overflow-wrap: anywhere;
  font-size: var(--t-small);
}

.admin-table__ref--own {
  color: var(--c-muted);
  font-weight: 400;
}

.admin-table__ip {
  color: inherit;
  text-decoration: none;
}

.admin-table__ip:hover {
  color: var(--c-accent);
  text-decoration: underline;
}

.admin-panel--stats {
  max-width: 52rem;
}

.stat-card {
  display: grid;
  gap: var(--s-4);
  padding: var(--s-5);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.stat-card--hour {
  gap: var(--s-3);
}

.stat-card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-4);
}

.stat-card__head h2 {
  margin: 0 0 0.2rem;
  font-size: 1.05rem;
}

.stat-card__head p {
  margin: 0;
  color: var(--c-muted);
  font-size: var(--t-small);
}

.stat-card__sum {
  margin: 0;
  text-align: right;
  color: var(--c-muted);
  font-size: var(--t-small);
}

.stat-card__sum span {
  display: block;
  color: var(--c-text);
  font-size: 1.55rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.stat-card__plot {
  margin: 0 -0.25rem;
}

.stat-card__foot {
  margin: 0;
  color: var(--c-muted);
  font-size: var(--t-small);
  font-variant-numeric: tabular-nums;
}

.stat-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  padding: 0.2rem;
  background: var(--c-surface-2);
  border-radius: var(--radius-pill);
}

.stat-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
}

.stat-pills--services {
  width: fit-content;
}

.stat-pills__item {
  display: inline-flex;
  align-items: center;
  padding: 0.35rem 0.75rem;
  border-radius: calc(var(--radius-pill) - 0.1rem);
  color: var(--c-muted);
  font-size: 0.8125rem;
  font-weight: 650;
  text-decoration: none;
}

.stat-pills__item:hover {
  color: var(--c-text);
  background: color-mix(in srgb, var(--c-surface) 70%, transparent);
}

.stat-pills__item--on,
.stat-pills__item--on:hover {
  color: var(--c-text);
  background: var(--c-surface);
  box-shadow: var(--shadow-sm);
}

.stat-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.stat-legend__item {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  font-size: 0.875rem;
}

.stat-legend__swatch {
  width: 0.65rem;
  height: 0.65rem;
  border-radius: 999px;
  background: var(--c-muted);
}

.stat-legend__name {
  color: var(--c-muted);
}

.stat-legend__num {
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}

.stat-legend__item--verkehrslage .stat-legend__swatch {
  background: #0c6a82;
}

.stat-legend__item--map-atudo .stat-legend__swatch {
  background: #c45c26;
}

.stat-legend__item--maplibre .stat-legend__swatch {
  background: #5a4fcf;
}

.stat-chart {
  display: block;
  width: 100%;
  height: auto;
}

.stat-chart__grid {
  stroke: var(--c-border);
  stroke-width: 1;
}

.stat-chart__ytick {
  fill: var(--c-muted);
  font-size: 10px;
  text-anchor: end;
}

.stat-chart__xtick {
  fill: var(--c-muted);
  font-size: 10px;
  text-anchor: middle;
}

.stat-chart__bar--verkehrslage {
  fill: #0c6a82;
}

.stat-chart__bar--map-atudo {
  fill: #c45c26;
}

.stat-chart__bar--maplibre {
  fill: #5a4fcf;
}

.stat-chart__zero {
  fill: var(--c-surface-3);
}

@media (prefers-color-scheme: dark) {
  .stat-legend__item--verkehrslage .stat-legend__swatch,
  .stat-chart__bar--verkehrslage {
    background: #5ecad6;
    fill: #5ecad6;
  }

  .stat-legend__item--map-atudo .stat-legend__swatch,
  .stat-chart__bar--map-atudo {
    background: #f0a36a;
    fill: #f0a36a;
  }

  .stat-legend__item--maplibre .stat-legend__swatch,
  .stat-chart__bar--maplibre {
    background: #9b93f0;
    fill: #9b93f0;
  }
}

.admin-table--schema td:first-child code {
  font-size: 0.8125rem;
  word-break: break-all;
}

.admin-table--schema td:nth-child(2) {
  white-space: nowrap;
  color: var(--c-muted);
}

.admin-table--schema td:last-child {
  color: var(--c-muted);
}

.admin-services {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14.5rem, 1fr));
  gap: var(--s-3);
}

.admin-dash {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr));
  gap: var(--s-3);
}

.admin-dash__item {
  display: grid;
  gap: var(--s-2);
  align-content: start;
}

.admin-dash__card {
  display: grid;
  gap: 0.3rem;
  padding: var(--s-3);
  border-radius: var(--radius);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  text-decoration: none;
  color: inherit;
}

.admin-dash__card:hover {
  border-color: color-mix(in srgb, var(--c-primary) 40%, var(--c-border));
}

.admin-dash__name {
  font-weight: 650;
}

.admin-dash__path {
  font-size: var(--t-small);
  color: var(--c-muted);
  overflow-wrap: anywhere;
}

.admin-dash__state {
  margin-top: 0.2rem;
  font-size: var(--t-small);
  font-weight: 650;
}

.admin-dash__item--off {
  filter: grayscale(1);
  opacity: 0.55;
}

.admin-dash__item--off .admin-dash__card,
.admin-dash__item--off .admin-dash__card:hover {
  background: color-mix(in srgb, var(--c-muted) 14%, var(--c-surface));
  border-color: var(--c-border);
  color: var(--c-muted);
}

.admin-dash__item--off .admin-dash__path,
.admin-dash__item--off .admin-dash__state,
.admin-dash__item--off .admin-dash__num,
.admin-dash__item--off .admin-dash__unit {
  color: var(--c-muted);
}

.admin-dash__hits {
  margin: 0;
  padding: 0 var(--s-1);
}

.admin-dash__num {
  display: block;
  font-size: 1.45rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.admin-dash__unit {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.75rem;
  color: var(--c-muted);
}

.admin-service {
  display: grid;
  gap: var(--s-2);
  align-content: start;
  min-width: 0;
  padding: var(--s-3);
  border-radius: var(--radius);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
}

.admin-service:hover {
  border-color: color-mix(in srgb, var(--c-primary) 40%, var(--c-border));
}

.admin-service--off {
  filter: grayscale(1);
  opacity: 0.7;
  border-color: var(--c-border);
}

.admin-service__link {
  display: grid;
  gap: 0.2rem;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}

.admin-service__link:hover .admin-service__name {
  text-decoration: underline;
}

.admin-service__name {
  font-weight: 650;
}

.admin-service__path {
  font-size: var(--t-small);
  color: var(--c-muted);
  overflow-wrap: anywhere;
}

.admin-service__lead {
  font-size: var(--t-small);
  color: var(--c-muted);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.admin-service__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  margin-top: 0.15rem;
}

.admin-service__meta {
  margin: 0;
  font-size: var(--t-small);
  color: var(--c-muted);
}

.admin-service__form .button {
  padding: 0.35rem 0.7rem;
  font-size: var(--t-small);
}

.admin-tokens {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s-4);
}

.admin-panel--import {
  max-width: 80rem;
}

.admin-import {
  display: grid;
  gap: var(--s-5);
}

.admin-import__nav {
  display: grid;
  align-content: start;
  gap: 0.15rem;
  margin: 0;
  padding: 0.25rem;
  border-radius: var(--radius);
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
}

.admin-import__family {
  display: grid;
  gap: 0.1rem;
  margin-top: 0.2rem;
  padding-top: 0.2rem;
  border-top: 1px solid var(--c-border);
}

.admin-import__family-label {
  margin: 0;
  padding: 0.2rem 0.65rem 0.05rem;
  font-size: 0.6875rem;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-muted);
}

.admin-import__link {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.2rem 0.75rem;
  align-items: baseline;
  padding: 0.45rem 0.65rem;
  border-radius: var(--radius-sm);
  color: var(--c-muted);
  text-decoration: none;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.25;
}

.admin-import__link--child {
  padding-left: 1.15rem;
}

.admin-import__link:hover {
  color: var(--c-primary-strong);
  background: var(--c-primary-soft);
}

.admin-import__link--active {
  color: var(--c-primary-strong);
  background: var(--c-surface);
  box-shadow: var(--shadow-sm);
}

.admin-import__name {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  overflow-wrap: anywhere;
}

.admin-import__meta {
  font-size: 0.75rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--c-muted);
}

.admin-import__link--active .admin-import__meta {
  color: var(--c-primary);
}

.admin-import__link--alert,
.admin-import__link--alert:hover,
.admin-import__link--alert.admin-import__link--active {
  color: #fff8f8;
  background: #8b1e1e;
}

.admin-import__link--alert .admin-import__meta,
.admin-import__link--alert.admin-import__link--active .admin-import__meta {
  color: #ffe08a;
}

.admin-import__link--alert .admin-import__warn {
  color: #ffe08a;
}

.admin-faults {
  display: grid;
  gap: var(--s-5);
}

.admin-faults__group {
  display: grid;
  gap: var(--s-4);
}

.admin-faults__kind {
  margin: 0;
}

.admin-row--family td {
  padding-top: 0.7rem;
  padding-bottom: 0.35rem;
  font-size: 0.75rem;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-muted);
  background: var(--c-surface-2);
}

.admin-row--child td:first-child {
  padding-left: 1.5rem;
}

.admin-row--alert td {
  background: #8b1e1e;
  color: #fff8f8;
}

.admin-row--alert a {
  color: #fff8f8;
}

.admin-row--alert .admin-import__warn {
  vertical-align: -0.15em;
  margin-right: 0.35rem;
  color: #ffe08a;
}

.admin-source:has(.admin-anomaly) {
  border-color: #8b1e1e;
  box-shadow: 0 0 0 1px #8b1e1e;
}

@media (prefers-reduced-motion: reduce) {
  .admin-anomaly__icon,
  .admin-import__warn {
    animation: none;
  }
}

.admin-import__stage {
  min-width: 0;
}

@media (min-width: 48rem) {
  .admin-import {
    grid-template-columns: 13.5rem minmax(0, 1fr);
    align-items: start;
  }

  .admin-import__nav {
    position: sticky;
    top: var(--s-4);
  }
}

.admin-source {
  display: grid;
  gap: var(--s-4);
  padding: var(--s-4);
  border-radius: var(--radius);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
}

.admin-source h3 {
  margin: 0 0 0.35rem;
  font-size: 0.95rem;
}

.admin-source p {
  margin: 0;
  color: var(--c-muted);
  max-width: 42rem;
}

.admin-source__related {
  margin-top: 0.55rem;
}

.admin-source__related a {
  font-weight: 650;
}

.admin-source .admin-anomaly {
  color: #fff8f8;
}

.admin-source .admin-error {
  color: #8b1e1e;
}

.admin-source__meta {
  display: grid;
  grid-template-columns: 7.5rem minmax(0, 1fr);
  gap: 0.35rem 0.75rem;
  align-items: baseline;
  margin-top: 0.55rem;
  color: inherit;
}

.admin-source__meta span {
  color: var(--c-muted);
  font-size: var(--t-small);
}

.admin-source__meta code {
  overflow-wrap: anywhere;
  font-size: var(--t-small);
}

.admin-panel--apis {
  max-width: 80rem;
}

.admin-api {
  display: grid;
  gap: var(--s-3);
  padding: var(--s-4);
  border-radius: var(--radius);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
}

.admin-api h2 {
  margin: 0 0 0.35rem;
  font-size: 0.95rem;
}

.admin-api .admin-panel__head {
  margin: 0;
}

.admin-api__url {
  display: inline-block;
  max-width: 36rem;
  overflow-wrap: anywhere;
  font-size: var(--t-small);
}

.admin-api__note {
  margin: 0.35rem 0 0;
  font-weight: 400;
  color: var(--c-muted);
  max-width: 36rem;
}

.admin-source__form {
  display: grid;
  gap: var(--s-3);
}

.admin-source__form label {
  display: grid;
  gap: 0.35rem;
}

.admin-source__label {
  font-size: var(--t-small);
  color: var(--c-muted);
}

.admin-source__form select {
  width: min(100%, 18rem);
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-bg);
  color: inherit;
  font: inherit;
}

.admin-source__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}

.admin-source__data {
  display: grid;
  gap: var(--s-4);
}

@media (min-width: 62rem) {
  .admin-source:not(.admin-source--overview) {
    grid-template-columns: minmax(0, 1fr) minmax(16rem, 22rem);
    align-items: start;
  }

  .admin-source__data {
    grid-column: 1 / -1;
  }
}

.admin-token {
  display: grid;
  gap: var(--s-4);
  padding: var(--s-4);
  border-radius: var(--radius);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
}

.admin-token h3 {
  margin: 0 0 0.35rem;
  font-size: 1.05rem;
}

.admin-token p {
  margin: 0;
  color: var(--c-muted);
  max-width: 42rem;
}

.admin-token__meta {
  display: grid;
  grid-template-columns: 6.5rem minmax(0, 1fr);
  gap: 0.35rem 0.75rem;
  align-items: baseline;
  margin-top: 0.55rem;
  color: inherit;
}

.admin-token__meta span {
  color: var(--c-muted);
  font-size: var(--t-small);
}

.admin-token__meta code {
  overflow-wrap: anywhere;
  font-size: var(--t-small);
}

.admin-token__form {
  display: grid;
  gap: var(--s-3);
}

.admin-token__form label {
  display: grid;
  gap: 0.35rem;
}

.admin-token__label {
  font-size: var(--t-small);
  color: var(--c-muted);
}

.admin-token__form input[type="text"],
.admin-token__form input[type="password"],
.admin-token__form input[type="url"],
.admin-token__form select {
  width: min(100%, 28rem);
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-bg);
  color: inherit;
  font: inherit;
}

.admin-token__form input[type="url"] {
  width: 100%;
  max-width: 40rem;
}

.admin-token__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}

.admin-inline {
  display: inline-flex;
  margin: 0;
}

.admin-bootstrap {
  margin: 0 0 var(--s-4);
  padding: var(--s-3);
  border: 1px solid color-mix(in srgb, var(--c-primary) 40%, var(--c-border));
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--c-primary) 10%, var(--c-surface));
}

.admin-bootstrap h3 {
  margin: 0 0 var(--s-2);
}

.admin-bootstrap__secret {
  display: block;
  margin: var(--s-2) 0;
  padding: 0.55rem 0.7rem;
  font-size: 1.05rem;
  word-break: break-all;
}

.admin-body--login {
  display: grid;
  place-items: center;
  min-height: 100vh;
}

.admin-login {
  width: min(26rem, calc(100vw - 2rem));
  padding: var(--s-5);
  border: 1px solid var(--c-border);
  border-radius: var(--radius);
  background: var(--c-surface);
}

.admin-login h1 {
  margin: 0 0 var(--s-2);
}

@media (min-width: 62rem) {
  .admin-token {
    grid-template-columns: minmax(0, 1fr) minmax(16rem, 22rem);
    align-items: start;
  }

  .admin-token--url {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (min-width: 62rem) {
  .admin {
    grid-template-columns: var(--w-admin-nav) minmax(0, 1fr);
  }

  .admin__nav {
    position: sticky;
    top: 0;
    align-self: start;
    border-bottom: 0;
    border-right: 1px solid var(--c-border);
    padding: var(--s-3) var(--s-3) var(--s-4);
    gap: var(--s-2);
    background: var(--c-surface);
  }

  .admin-menu {
    overflow: visible;
    padding: 0;
  }

  .admin-menu__link {
    width: 100%;
    padding: 0.4rem 0.5rem;
  }

  .admin-menu__hint {
    display: block;
  }

  .admin__stage {
    padding: var(--s-6) var(--s-6) var(--s-7);
  }
}

/* --------------------------------------------------- 9. Bewegung und Druck */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  .site-header,
  .site-footer__nav,
  .layout__rail,
  .map,
  .search,
  .pagination,
  .skiplink,
  .admin__nav {
    display: none;
  }

  body {
    background: #fff;
    color: #000;
    background-image: none;
  }

  .layout {
    display: block;
    padding: 0;
  }

  .card,
  .message,
  .panel {
    border: 1px solid #999;
    box-shadow: none;
    break-inside: avoid;
  }
}

/* ------------------------------------------------------------ 10. Telefon
 *
 * Alles in dieser Abfrage gilt bis 61.98rem und keinen Pixel darüber. Der
 * Grund steht im Dateikopf: Die Telefon-Ansicht bekommt App-Charakter, ohne
 * dass die Desktop-Ansicht dafür angefasst wird (D-097). Eine max-width-
 * Abfrage macht diese Zusage prüfbar.
 *
 * Was sich ändert:
 *
 *   Der Kopf schrumpft von 224 auf 60 Pixel und klebt. Die sieben Punkte der
 *   Hauptnavigation brachen auf 360 Pixel Breite in vier Zeilen um; sie stehen
 *   jetzt als Tab-Leiste am unteren Rand, in Reichweite des Daumens.
 *
 *   Die Karte reicht von Rand zu Rand. Sie ist der Gegenstand dieser Website,
 *   und ein gerahmtes Kästchen mit 16 Pixel Luft an beiden Seiten hat auf einem
 *   Telefon niemandem genützt.
 *
 *   Jede Fläche, die ein Finger trifft, ist mindestens --tap hoch. Das betrifft
 *   Zeilenlisten, die Kartenleiste und den Fuß; sie lagen bei 32 bis 33 Pixeln.
 *
 *   Die Rangliste wird zur Kartenliste. Ihre Tabelle war 431 Pixel breit in
 *   einer Spalte von 358 und lief seitlich aus dem Bild.
 *
 *   Der Schnellzugriff wird umbrochen und aufklappbar statt waagrecht
 *   geschoben. Das war die erste Fassung dieses Abschnitts und war falsch: Ein
 *   Streifen mit weichen Kanten ist ein schöner Streifen und bleibt eine
 *   Auswahl, von der man nur den Anfang sieht und die man mit dem Daumen
 *   quer zur Leserichtung durchsuchen muss (D-098).
 */

/* Die Bauteile, die es nur auf dem Telefon gibt: die Tab-Leiste unten, die
   Pille „Alle Orte" am Ende der Städte und der Wähler der Meldungsarten. Sie
   stehen in jedem Dokument, sichtbar werden sie erst unten in der Abfrage.

   Diese Zeilen sind die einzigen des Abschnitts außerhalb davon: Sie sorgen
   dafür, dass die Bauteile auf dem Desktop nicht existieren – auch dann nicht,
   wenn jemand später ein Stylesheet dahinter hängt. */
.tabbar,
.rail__all,
.railpick {
  display: none;
}

@media (max-width: 61.98rem) {
  /* Sprungziele landen sonst unter dem klebenden Kopf. */
  html {
    scroll-padding-top: calc(var(--h-header-phone) + var(--s-2));
  }

  body {
    /* Das graue Aufblitzen beim Antippen kommt zusätzlich zu den eigenen
       Zuständen der Schalter und sieht daneben wie ein Fehler aus. */
    -webkit-tap-highlight-color: transparent;
    /* Die Farbwolken am Seitenkopf sind auf 34rem Höhe gerechnet. Auf einem
       Telefon ist das der halbe erste Bildschirm; kürzer gehalten enden sie
       wie gedacht vor dem ersten Absatz. */
    background-size: 100% 18rem;
  }

  /* ------------------------------------------------------ Kopf: eine Zeile

     Vorher: Marke und sieben Navigationspunkte in der ersten Zeile, das
     Suchfeld in der zweiten. Die Punkte brachen bei 360 Pixeln auf vier Zeilen
     um, der Kopf war 224 Pixel hoch – ein Viertel des Bildschirms, bevor der
     erste Inhalt begann – und klebte deshalb absichtlich nicht.

     Jetzt trägt der Kopf zwei Dinge: die Marke und die Suche. Die Navigation
     ist die Tab-Leiste am unteren Rand. Damit passt er in eine Zeile, und weil
     er in eine Zeile passt, darf er kleben. */
  .site-header {
    position: sticky;
    top: 0;
    background-color: color-mix(in srgb, var(--c-primary-soft) 62%, var(--c-surface));
    backdrop-filter: blur(14px) saturate(140%);
    /* Der Kopf fährt beim Herunterscrollen fort und beim Hochscrollen wieder
       ein. Den Zustand schreibt die Island an das Dokument; ohne JavaScript
       fehlt das Attribut, der Kopf bleibt stehen, und nichts geht verloren. */
    transition: transform 200ms ease;
  }

  html[data-chrome="away"] .site-header {
    transform: translateY(-100%);
  }

  .site-header__inner {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: "brand search";
    /* Die Höhe steht im Token, weil der klebende Schnellzugriff darunter
       genau diesen Abstand als top braucht. Ohne festen Wert bliebe zwischen
       beiden ein Streifen, durch den der Inhalt sichtbar hindurchzieht. */
    min-height: calc(var(--h-header-phone) - 1px);
    padding-block: 0;
    padding-inline: calc(var(--s-3) + var(--safe-l)) calc(var(--s-3) + var(--safe-r));
    gap: var(--s-3);
  }

  /* Das Suchfeld ist flacher als im Fließtext, damit es in die Kopfzeile
     passt. Die Schriftgröße bleibt bei 16 Pixeln – darunter zoomt iOS beim
     Fokussieren in die Seite hinein und kommt nicht von selbst zurück. */
  .site-header__search .search__input {
    padding-block: var(--s-2);
  }

  .brand {
    min-height: var(--tap);
  }

  /* Der Lupenknopf sitzt im Suchfeld und kann deshalb nicht 44 Pixel groß
     werden: ein Kreis dieser Größe füllte das 44 Pixel hohe Feld vollständig
     aus und sähe beim Berühren aus wie ein umgefärbtes Eingabefeld. Die
     Trefferfläche wächst stattdessen über das Bild hinaus. */
  .search__submit::after {
    content: "";
    position: absolute;
    inset: calc((var(--tap) - 2.25rem) / -2);
  }

  /* Unter 22rem reicht die Breite nicht für Wortmarke und ein brauchbares
     Suchfeld. Dann bleibt das Markenzeichen; es führt genauso zur Startseite.

     Die Grenze liegt bei 22rem und nicht bei 26, wie zuerst gerechnet: 26rem
     sind 416 Pixel, und damit wäre die Wortmarke auf jedem verbreiteten
     Telefon verschwunden – auch auf den 360 Pixeln, für die sie gedacht war.
     Dort bleiben dem Suchfeld 174 Pixel, genug für Feld und Lupe. */
  @media (max-width: 21.98rem) {
    .brand__text {
      position: absolute;
      width: 1px;
      height: 1px;
      margin: -1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }
  }

  /* Die Hauptnavigation ist auf dem Telefon die Tab-Leiste. display:none nimmt
     sie auch aus Tabulatorfolge und Vorleser – doppelt vorhandene Navigation
     wäre schlimmer als eine. Ihre sieben Ziele stehen vollständig in der
     Leiste, fünf offen und drei unter „Mehr", und der Fuß führt sie ohnehin
     ein zweites Mal. */
  .mainnav {
    display: none;
  }

  /* --------------------------------------------- Tab-Leiste am unteren Rand

     Sechs Felder gleicher Breite: fünf Ziele und „Mehr". Auf 360 Bildpunkten
     sind das 60 Pixel je Feld – knapp, aber die Beschriftung wächst mit der
     Breite mit und bleibt in einer Zeile.

     Sie liegt fest am unteren Rand statt klebend am Seitenende: Navigation im
     Daumenbereich ist der Grund, aus dem es diese Leiste gibt. */
  .tabbar {
    display: block;
    position: fixed;
    inset: auto 0 0;
    z-index: 50;
    border-top: 1px solid var(--c-border);
    background-color: color-mix(in srgb, var(--c-surface) 88%, transparent);
    backdrop-filter: blur(14px) saturate(140%);
    /* Der Gestenbalken von iOS liegt über dem unteren Rand. Ohne dieses
       Polster läge die Beschriftung darunter und die Felder wären halb so
       hoch, wie sie aussehen. */
    padding-bottom: var(--safe-b);
    padding-inline: var(--safe-l) var(--safe-r);
  }

  /* Dasselbe Verlaufsband wie an Kopf und Fuß, hier an der Oberkante: die
     Leiste ist Teil der Marke und nicht ein Kasten, der aufliegt. */
  .tabbar::before {
    content: "";
    position: absolute;
    inset: -1px 0 auto;
    height: 2px;
    background-image: var(--grad-brand);
    opacity: 0.85;
  }

  .tabbar__list {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .tabbar__item {
    min-width: 0;
  }

  .tabbar__tab {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.15rem;
    /* Die ganze Höhe der Leiste ist die Trefferfläche, nicht nur das Symbol. */
    min-height: var(--h-tabbar);
    padding: var(--s-1) 0.15rem;
    color: var(--c-muted);
    text-decoration: none;
    /* Der Marker des details-Elements. Ohne beide Zeilen steht auf dem
       „Mehr"-Feld ein Dreieck neben dem Symbol. */
    list-style: none;
    cursor: pointer;
  }

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

  .tabbar__icon {
    width: 1.35rem;
    height: 1.35rem;
    flex: none;
    color: inherit;
  }

  .tabbar__word {
    /* Die Beschriftung wächst mit der Breite, bleibt aber in einer Zeile.
       Umgebrochen wäre die Leiste je Seite unterschiedlich hoch. */
    font-size: clamp(0.5625rem, 2.4vw, 0.6875rem);
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: -0.01em;
    white-space: nowrap;
  }

  /* Das aktive Feld trägt Farbe und Symbol, nicht nur eine Unterstreichung:
     eine 2 Pixel hohe Linie unter einem 60 Pixel breiten Feld ist auf
     Armlänge nicht zu sehen. */
  .tabbar__tab[aria-current="page"],
  .tabbar__tab[data-current="page"] {
    color: var(--c-accent);
  }

  .tabbar__tab[aria-current="page"] .tabbar__icon,
  .tabbar__tab[data-current="page"] .tabbar__icon {
    color: var(--c-accent);
  }

  /* ---------------------------------------------------- Das Blatt zu „Mehr"

     Ein details-Element, dessen Inhalt über der Leiste aufgeht. Der Anfasser
     ist das sechste Feld, deshalb liegt hier keine eigene Form. */
  .tabmore {
    position: static;
  }

  .tabmore__sheet {
    position: absolute;
    inset: auto 0 100%;
    padding: var(--s-3) var(--s-4) var(--s-4);
    border-top: 1px solid var(--c-border);
    border-bottom: 1px solid var(--c-border);
    background: var(--c-surface);
    box-shadow: var(--shadow-lg);
  }

  .tabmore__title {
    margin: 0 0 var(--s-2);
    font-size: var(--t-small);
    font-weight: 650;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--c-muted);
  }

  .tabmore__list {
    display: grid;
    gap: var(--s-1);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .tabmore__link {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    min-height: var(--tap);
    padding: var(--s-2) var(--s-2);
    border-radius: var(--radius-sm);
    color: var(--c-text);
    text-decoration: none;
    font-weight: 550;
  }

  .tabmore__link .icon {
    width: 1.25rem;
    height: 1.25rem;
    flex: none;
    color: var(--c-muted);
  }

  .tabmore__link[aria-current="page"] {
    background: var(--c-primary-soft);
    color: var(--c-primary-strong);
  }

  /* Der Fuß endet nicht am Bildschirmrand, sondern über der Leiste. Ohne
     dieses Polster liegen die letzten Verweise dauerhaft darunter. */
  .site-footer__inner {
    padding-bottom: calc(var(--s-6) + var(--h-tabbar) + var(--safe-b));
  }

  /* ------------------------------------------------------- Seitenrand Gerüst

     Im Querformat eines Telefons mit Notch liegt der linke Rand unter der
     Kamera. Die Seitenränder des Gerüsts nehmen die Gerätemaße deshalb auf;
     im Hochformat sind sie null und es bleibt bei den 16 Pixeln. */
  .layout {
    padding-inline: calc(var(--s-4) + var(--safe-l)) calc(var(--s-4) + var(--safe-r));
    gap: var(--s-4);
  }

  /* ----------------------------------------------------- Schnellzugriff: Orte

     Die erste Fassung war ein waagrechter Schiebestreifen mit weichen Kanten.
     Der Fehler daran war nicht die Kante, sondern die Richtung: Wer eine Stadt
     sucht, musste quer zur Leserichtung schieben und sah dabei nie, wie viele
     noch kommen. Ein schöner Streifen ist immer noch ein Streifen (D-098).

     Jetzt bricht die Auswahl um wie Text. Vierzehn Städte brauchen umgebrochen
     sechs Reihen – 304 Pixel bei 360 Bildpunkten, gemessen – und schöben die
     Seitenüberschrift unter die Falz. Sichtbar sind deshalb die ersten fünf;
     mit der Pille „Alle Orte" füllen sie zwei Reihen. Die übrigen neun stehen
     im Fuß und im Verzeichnis, in das diese Pille führt.

     Der Streifen reichte bis an den Bildschirmrand, damit die letzte Pille
     nicht abgeschnitten wirkte. Umgebrochen endet keine Pille am Rand, und die
     Gruppe steht wieder in der Spalte wie jeder andere Block. */
  .rail {
    gap: var(--s-3);
  }

  .rail__title,
  .rail__scope {
    margin-bottom: var(--s-1);
  }

  .rail__list {
    flex-wrap: wrap;
    overflow: visible;
    padding-bottom: 0;
  }

  /* Gekappt wird nur, wo es einen Ausgang gibt: Die Regel greift an der Liste,
     die die Pille „Alle Orte" trägt, und nicht an dieser Pille selbst. Steht
     sie eines Tages nicht mehr im Markup, bricht die Gruppe vollständig um
     statt fünf Ziele zu verschweigen. */
  .rail__list:has(> .rail__all) > li:nth-of-type(n + 6):not(.rail__all) {
    display: none;
  }

  .rail__all {
    display: block;
  }

  /* Alle Pillen, überall: im Schnellzugriff, in der Kontextspalte, unter der
     Karte und im Fuß. Sie waren zwischen 37 und 39 Pixel hoch – knapp unter
     der Grenze und damit genau der Fall, in dem man daneben trifft. */
  .chip {
    min-height: var(--tap);
  }

  /* --------------------------------------------- Schnellzugriff: Meldungsarten

     Dieselbe Krankheit, andere Behandlung: Die neun Pillen tragen ihre Zahl mit
     und sind 111 bis 210 Pixel breit. Umgebrochen wären das ebenfalls sechs
     Reihen, und kappen lässt sich hier nichts – es gibt keine Seite, die „alle
     Meldungsarten zum Anklicken" führt, und die Art, die man sucht, ist bei
     jedem Besuch eine andere.

     Also ein Menü: geschlossen eine Zeile mit der Art, auf der man steht, offen
     eine Liste, in der jede Art eine ganze Zeile hat und ihre Zahl am rechten
     Rand steht. Untereinander gelesen statt nebeneinander gestaucht.

     Den Zustand hält die Adresszeile. Der Wähler zeigt auf die Gruppe, die
     Zeile am Ende zurück auf den Wähler; beide Zustände sind echte Sprungziele,
     :target macht daraus die Anzeige. Kein Skript hält etwas, das beim
     Seitenwechsel verloren gehen könnte. */
  .rail__group--menu .rail__list {
    display: none;
  }

  .rail__group--menu:target .rail__list {
    display: grid;
    gap: var(--s-1);
  }

  .railpick {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    min-height: var(--tap);
    padding: var(--s-2) var(--s-3);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    background: var(--c-surface);
    box-shadow: var(--shadow-sm);
    color: var(--c-text);
    text-decoration: none;
    font-size: var(--t-small);
    font-weight: 600;
  }

  /* Die Aufforderung steht rechts neben dem Wert und nicht davor: Gelesen wird
     zuerst, wo man ist. */
  .railpick__hint {
    margin-inline-start: auto;
    color: var(--c-muted);
    font-weight: 500;
  }

  .railpick__sign {
    width: 1.125rem;
    height: 1.125rem;
    flex: none;
    color: var(--c-muted);
  }

  .rail__group--menu:target .railpick:not(.railpick--shut) {
    display: none;
  }

  .railpick--shut {
    display: none;
    justify-content: center;
    color: var(--c-muted);
  }

  .rail__group--menu:target .railpick--shut {
    display: flex;
    margin-top: var(--s-1);
  }

  /* Die Pillen der offenen Liste sind Zeilen. Die Rundung bleibt die der
     Menüzeilen im Blatt der Tab-Leiste – dasselbe Bauteil, dieselbe Form. */
  .rail__group--menu .rail__list .chip {
    width: 100%;
    border-radius: var(--radius-sm);
  }

  .rail__group--menu .chip__count {
    margin-inline-start: auto;
    color: var(--c-muted);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
  }

  .rail__group--menu .chip--active .chip__count {
    color: color-mix(in srgb, #fff 78%, transparent);
  }

  /* ----------------------------------------------------- Karte: Rand zu Rand

     Die Karte ist der Gegenstand dieser Website. Als gerahmtes Kästchen mit
     16 Pixeln Luft an beiden Seiten verlor sie auf 360 Bildpunkten ein Zehntel
     ihrer Fläche an einen Zierrand.

     Die Höhe steigt von 48 auf 58 Prozent der Anzeige und rechnet in dvh: vh
     meint auf einem Telefon die Höhe ohne eingefahrene Adressleiste, und die
     Karte sprang beim Scrollen um deren Höhe. Die vh-Zeile bleibt als Rückfall
     für Browser ohne dvh stehen. */
  .map {
    --map-canvas-h: clamp(22rem, 58vh, 34rem);
    --map-canvas-h: clamp(22rem, 58dvh, 34rem);
    margin-inline: calc((var(--s-4) + var(--safe-l)) * -1) calc((var(--s-4) + var(--safe-r)) * -1);
    border-inline: 0;
    border-radius: 0;
  }

  /* Im Vollbild gilt der Rand des Bildschirms, nicht der der Spalte. */
  .map--full {
    margin-inline: 0;
  }

  /* Die Werkzeugleiste auf der Karte. 32 Pixel breite Knöpfe traf man mit dem
     Finger nur ungefähr; mit diesen beiden Maßen sind es 44. */
  .map__rail {
    --rail-icon: 1.5rem;
    --rail-pad: 0.625rem;
  }

  .map__rail--open {
    width: 13.5rem;
  }

  .map__rail-handle,
  .map__rail-item {
    min-height: var(--tap);
  }

  .map__radar-add {
    min-height: var(--tap);
  }

  /* „Alle" und „Keine" über der Legende sind Textknöpfe von 21 Pixeln Höhe.
     Sie schalten neun Ebenen auf einmal und sind damit die folgenreichsten
     Schalter der Karte. */
  .map__link {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap);
  }

  /* Die Auswahl der Meldungsarten unter der Karte: dieselbe Behandlung wie der
     Schnellzugriff. Die Pillen laufen bis an den Bildschirmrand und beginnen
     bündig mit der Beschriftung darüber. */
  .map__bar {
    padding-inline: 0;
  }

  .map__barhead,
  .map__note {
    padding-inline: var(--s-3);
  }

  /* Die Legende bricht um. Ab 45rem tat sie das ohnehin – der Schiebestreifen
     darunter war die Ausnahme, und er hatte denselben Fehler wie der
     Schnellzugriff: Man sah zwei von neun Ebenen und musste den Rest suchen.

     Umgebrochen sind es vier bis fünf Reihen unter der Karte. Das ist der
     Preis, und er wird hier bezahlt: Die Legende sagt nicht nur, was die Farben
     bedeuten, sie schaltet die Ebenen – eine Ebene, die man nicht sieht, kann
     man nicht ausschalten. */
  .map__legend {
    flex-wrap: wrap;
    padding-inline: var(--s-3);
    overflow-x: visible;
  }

  .map__legend-item {
    min-height: var(--tap);
  }

  /* ------------------------------------------------- Kopfstreifen /stau

     Die Kachel zerfiel auf dem Telefon in fünf gestapelte Kästen und war
     zusammen mit Bogen und Verlauf gut tausend Pixel hoch – für vier Zahlen
     und einen Tacho.

     Der Bogen steht jetzt neben seiner Stufe statt darüber, die vier Zahlen
     in zwei Spalten. Aus tausend Pixeln werden rund vierhundert. */
  .gauge {
    padding: var(--s-4);
  }

  .gauge__heading {
    margin-bottom: var(--s-4);
  }

  .gauge__arc {
    flex-direction: row;
    gap: var(--s-4);
    padding: var(--s-3) var(--s-4);
  }

  .gauge__arc svg {
    width: 8rem;
  }

  /* Zwei Spalten, nicht auto-fit: die Mindestbreite von 9.5rem ergab auf 326
     Pixeln Innenbreite genau eine Spalte. Zwei halbe Kästen nebeneinander sind
     schmaler, aber die Zahl darin ist kurz – „363 km", „169", eine Uhrzeit. */
  .gauge .gauge__facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-2);
  }

  .gauge__stat {
    padding: var(--s-2) var(--s-3);
  }

  /* Auf halber Spaltenbreite bricht „23.08.2026, 17:56 Uhr" in der großen
     Schrift dreimal um. Eine Stufe kleiner passt es in zwei Zeilen. */
  .gauge .gauge__facts dd {
    font-size: var(--t-body);
  }

  /* Der Tagesverlauf reicht von Kartenrand zu Kartenrand und wird waagrecht
     geschoben. Damit steht die Zeichnung in ihrer eigenen Größe da: die
     Achsenschrift ist neun Pixel groß, wie gezeichnet, und die Linie zwei
     Pixel stark. Gestaucht auf die Spaltenbreite waren es vier und ein
     Pixel – und die Zeichnung füllte nur 80 der 184 Pixel Höhe. */
  .gauge__spark {
    margin-inline: calc(var(--s-4) * -1);
    border-radius: 0;
  }

  .gauge__sparkbody {
    margin-inline: calc(var(--s-4) * -1);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    mask-image: linear-gradient(
      to right,
      transparent 0,
      #000 var(--s-3),
      #000 calc(100% - var(--s-3)),
      transparent 100%
    );
  }

  .gauge__sparkbody::-webkit-scrollbar {
    display: none;
  }

  /* 45rem sind die 720 Pixel der viewBox, 11rem ihre 176 – Maßstab eins. */
  .gauge__spark .spark {
    width: 45rem;
    height: 11rem;
  }

  /* ------------------------------------------- Rangliste: Zeilen zu Karten

     Fünf bis sechs Spalten mit Zahlen sind auf 358 Pixeln keine Tabelle. Sie
     war 431 Pixel breit und lief seitlich aus dem Bild – ohne Rollbereich, das
     ganze Dokument wurde breiter als der Bildschirm.

     Jede Zeile ist jetzt eine Karte: Rangabzeichen und Name in der ersten
     Reihe, die Zahlen darunter in zwei Spalten mit ihrer Spaltenüberschrift
     aus data-label. Der Kopf der Tabelle entfällt, weil er nichts mehr
     überschreibt. */
  .table {
    display: block;
    font-size: var(--t-body);
  }

  .table thead {
    display: none;
  }

  .table tbody {
    display: grid;
    gap: var(--s-2);
  }

  .table tbody tr {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: var(--s-4);
    row-gap: var(--s-3);
    padding: var(--s-3);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    background: var(--c-surface);
    box-shadow: var(--shadow-sm);
  }

  .table tbody td {
    display: block;
    padding: 0;
    border: 0;
    text-align: left;
  }

  /* Der Rang als Abzeichen. Als rechtsbündige Zahl in einer eigenen Spalte war
     er die schmalste und lauteste Spalte der Tabelle; hier ist er das, was er
     ist – eine Nummer am Namen. */
  .table tbody .table__pos {
    flex: none;
    display: grid;
    place-items: center;
    min-width: 1.875rem;
    min-height: 1.875rem;
    padding-inline: 0.35rem;
    border-radius: var(--radius-pill);
    background: var(--c-surface-2);
    color: var(--c-muted);
    font-weight: 700;
    font-size: var(--t-small);
  }

  /* Die Namenszelle ist die einzige ohne data-label und damit die Überschrift
     der Karte. Sie füllt die erste Reihe neben dem Abzeichen.

     Die 60 Prozent sind nicht die Breite, sondern der Grund für den Umbruch:
     Sie lassen neben Abzeichen und Namen zu wenig übrig für die erste Zahl, und
     damit beginnen die Zahlen alle in der zweiten Reihe. Mit einer schmaleren
     Grundbreite stand die erste Zahl neben dem Namen und die zweite darunter –
     dieselbe Kachel las sich dann in zwei Richtungen. */
  .table tbody td:not([data-label]):not(.table__pos) {
    flex: 1 1 60%;
    font-weight: 650;
  }

  /* Der Name ist der einzige Verweis der Karte. Er füllt seine Zeile
     senkrecht aus – als 21 Pixel hohe Textzeile war er das kleinste Ziel auf
     einer Seite, die aus fast fünfzig solchen Karten besteht. */
  .table tbody td:not([data-label]) a {
    display: flex;
    align-items: center;
    min-height: var(--tap);
    text-decoration: none;
  }

  /* Der Anteilsbalken lag als 6rem breiter Streifen hinter dem Namen. In der
     Karte hat er die ganze Breite und sagt damit mehr. */
  .table tbody .table__bar {
    display: block;
    width: auto;
    margin-top: var(--s-2);
  }

  /* Die Zahlen: zwei je Reihe. Aufgeteilt nach verfügbarem Platz wären es drei
     zu je hundert Pixeln, und „Zeitverlust im Stau" bräche darin um. */
  .table tbody td[data-label] {
    flex: 1 1 40%;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
  }

  .table tbody td[data-label]::before {
    content: attr(data-label);
    display: block;
    color: var(--c-muted);
    font-size: var(--t-small);
    font-weight: 450;
  }

  /* -------------------------------------------------------- Listen und Zeilen

     Die Zeilenlisten waren 33 Pixel hoch. Das ist die Höhe einer Textzeile und
     nicht die einer Fingerspitze; zwischen zwei Staus derselben Autobahn traf
     man den falschen. --tap sind die 44 Pixel, auf die sich beide
     Plattformrichtlinien geeinigt haben. */
  .tailbacks {
    font-size: var(--t-body);
  }

  /* „Stau auf der A2 bei Hamm" – zwei Verweise in einem Satz. Die Zeile ist
     45 Pixel hoch, damit über und unter dem Text je 12 Pixel liegen. */
  .tailbacks__item {
    min-height: var(--tap);
    padding-block: var(--s-3);
  }

  /* Die Verweise stehen mitten im Satz und können deshalb nicht selbst zu
     Blöcken werden. Ein senkrechtes Polster an einem Inline-Element verschiebt
     nichts im Layout, vergrößert aber die Trefferfläche – hier auf genau die
     12 Pixel, die die Zeile über und unter dem Text freihält. */
  .tailbacks__item a {
    padding-block: var(--s-3);
  }

  /* Dieselbe Rechnung für die kleine Schrift: eine Zeile aus --t-small ist 17
     Pixel hoch, 2 × 13,6 Pixel Polster machen daraus 44. Der Wert steht als
     Zahl da, weil er aus der Zeilenhöhe folgt und nicht aus der Abstandsreihe
     – --s-3 ergäbe 41 Pixel und damit knapp daneben. */
  .crumbs a,
  .meta a,
  .radar__empty a,
  .site-footer__note a,
  .site-footer__meta a,
  .map__scope-link {
    padding-block: 0.85rem;
  }

  /* Die kurze Rangliste in der Kontextspalte: Platz, Name, Wert in einer
     Zeile. Der Name ist der Verweis und füllt die Zeile senkrecht aus. */
  .rank {
    align-items: center;
  }

  .rank__name {
    display: flex;
    align-items: center;
    min-height: var(--tap);
  }

  .tailbacks__more {
    min-height: var(--tap);
    padding-block: var(--s-3);
  }

  .event-row__link {
    min-height: var(--tap);
    padding-inline: var(--s-2);
  }

  .message__title a {
    display: flex;
    align-items: center;
    min-height: var(--tap);
  }

  .site-footer__nav a {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap);
  }

  .panel__list li a,
  .panel__more a {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap);
  }

  /* Die Artikelliste unter „Wissen": Überschrift und Anriss stehen in einem
     Kasten, verweisen aber nur über die Überschrift. Auf dem Telefon ist der
     ganze Kasten das Ziel – die Fläche ist ohnehin da, und der Anriss gehört
     zur Überschrift und nicht daneben.

     Das geht mit einer Deckfläche, weil im Kasten nur dieser eine Verweis
     liegt. Käme ein zweiter hinzu, läge er darunter und wäre nicht mehr
     erreichbar. */
  .articles__item {
    position: relative;
  }

  .articles__title a::after {
    content: "";
    position: absolute;
    inset: 0;
  }

  .bar__link {
    min-height: var(--tap);
  }

  /* Der Meldungstext ist der Satz einer Landesmeldestelle und wird
     unterschiedlich lang: meist vier bis fünf Zeilen, gelegentlich zehn. Vier
     Zeilen reichen, um zu erkennen, worum es geht; der vollständige Wortlaut
     steht auf der Seite der Meldung, und die Überschrift führt dorthin.

     Das kürzt die Liste weniger, als es klingt – auf der Startseite waren es
     gemessen 250 Pixel über sieben gekappte Absätze. Der Gewinn liegt nicht in
     der Länge, sondern darin, dass alle Meldungen gleich hoch beginnen und man
     die Liste überfliegen kann. Der Text bleibt im Dokument; für Suchmaschinen
     ändert sich nichts. */
  .message__text {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    overflow: hidden;
  }

  /* ------------------------------------------------------- Kontextspalte

     Sie steht auf dem Telefon nicht neben dem Inhalt, sondern dahinter, und ist
     dort die längste Strecke der Seite. Engere Polster nehmen ihr ein paar
     hundert Pixel; kurz wird sie davon nicht. Kürzer würde sie nur, wenn Inhalt
     wegfiele – und das ist eine Entscheidung über die Seite, nicht über ihr
     Aussehen. */
  .aside {
    gap: var(--s-3);
  }

  /* Ein Rasterelement schrumpft von sich aus nicht unter den Mindestbedarf
     seines Inhalts. Die Verweisketten der Kontextspalte hatten als
     Schiebestreifen einen von 754 Pixeln, und die Spalte wurde dadurch doppelt
     so breit wie der Bildschirm. Die Streifen sind weg, die Zeile bleibt: Sie
     kostet nichts und deckt jeden Inhalt ab, der sich nicht kleinrechnen lässt. */
  .aside > * {
    min-inline-size: 0;
  }

  .panel {
    padding: var(--s-3) var(--s-4);
  }
}
