:root {
  --bg: #12151a; --panel: #1b2027; --line: #2c333d; --text: #e6e9ee;
  --muted: #9aa4b2; --accent: #4da3ff; --free: #3ecf8e; --busy: #f0a13a;
  /* „nicht verfügbar" liegt bewusst zwischen besetzt und unbekannt: gedämpftes Amber, also
     erkennbar „nicht nutzbar", aber ohne die Dramatik von Rot – Rot bleibt dem echten
     Defekt (Rohwert `faulted`) vorbehalten. */
  --unavail: #a3906a;
  --fault: #e5534b; --unknown: #6b7480;
  /* Zeitreise: violett, weil es als einzige Farbe hier nichts über die Belegung aussagt – die
     Anzeige ist „nicht jetzt", nicht „schlecht". */
  --past: #b18cff;

  /* --- Skala der Statistik-Heatmap ---------------------------------------------------
     Fünf diskrete Stufen je Kennzahl, EINFARBIG und luminanzmonoton (dunkel = wenig, hell =
     viel). Bewusst keine Rot-Grün- und keine Regenbogenskala: Eine Rampe, die nur über die
     Helligkeit trägt, bleibt bei jeder Farbfehlsichtigkeit und in Graustufen (Ausdruck)
     lesbar – und die Zellen tragen ohnehin zusätzlich Zahl und Vorlesetext, weil Farbe
     allein keine Information ist. */
  --occ-0: #1d2733; --occ-1: #234a72; --occ-2: #2a6ea8; --occ-3: #3d93d8; --occ-4: #7fc2ff;
  --full-0: #2b2118; --full-1: #533520; --full-2: #7d4d26; --full-3: #ad6a2d; --full-4: #f0a13a;
  --un-0: #2a261d; --un-1: #4c3f24; --un-2: #74602f; --un-3: #a3873f; --un-4: #d8b95c;

  /* „Jetzt"-Markierung. ⚠️ Bewusst eine Farbe, die in KEINER der drei Rampen vorkommt (blau /
     amber / oliv) – ein akzentblauer Rahmen auf blauen Zellen war unsichtbar. Weiß trägt über
     jeder Stufe und auf dem dunklen Grund, ohne eine BEDEUTUNG mitzubringen: Rot hieße in dieser
     Oberfläche „Störung", und „jetzt" ist keine. */
  --now: #fff;
}
* { box-sizing: border-box; }
body { margin: 0; font: 15px/1.45 system-ui, sans-serif; background: var(--bg); color: var(--text); }
/* dvh nach vh: auf dem Smartphone rechnet vh mit EINGEKLAPPTER Browserleiste, die Karte
   wäre also immer ein Stück höher als das Fenster und die Statuszeile unerreichbar. */
#layout { display: flex; height: 100vh; height: 100dvh; }
#map { flex: 1; min-width: 0; }
#side {
  width: 430px; flex: none; background: var(--panel); border-left: 1px solid var(--line);
  display: flex; flex-direction: column; overflow: hidden;
}
header { padding: 12px 16px; border-bottom: 1px solid var(--line); }
h1 { font-size: 16px; margin: 0 0 8px; }
#search { width: 100%; padding: 8px 10px; border-radius: 6px; border: 1px solid var(--line);
  background: #0f1216; color: var(--text); }
#results { max-height: 190px; overflow: auto; margin-top: 8px; }
#results button { display: block; width: 100%; text-align: left; background: none; border: 0;
  color: var(--text); padding: 6px 8px; border-radius: 4px; cursor: pointer; font: inherit; }
#results button:hover, #results button:focus-visible { background: #262d36; outline: none; }
#detail { flex: 1; overflow: auto; padding: 16px; }
.muted { color: var(--muted); font-size: 13px; }

/* --- Reiter (Standorte / Route) --------------------------------------------------
   Zwei Ansichten in derselben Leiste. Die Reiter sitzen ganz oben, damit sie auf dem Smartphone
   am Rand des Blattes liegen und mit dem Daumen erreichbar sind. */
#tabs { display: flex; border-bottom: 1px solid var(--line); flex: none; }
/* ⚠️ Seit dem vierten Reiter ist der Innenabstand schmaler: 430 px durch vier sind ~107 px je
   Reiter, und mit 8 px Polsterung bricht „Standorte" auf einem 360-px-Gerät um. */
.tab {
  flex: 1; background: none; border: 0; border-bottom: 2px solid transparent;
  color: var(--muted); font: inherit; font-size: 14px; padding: 10px 5px; cursor: pointer;
  white-space: nowrap;
}
.tab[aria-selected="true"] { color: var(--text); border-bottom-color: var(--accent); font-weight: 600; }
/* Beide Ansichten sind selbst Spalten und dürfen nur INNEN scrollen – sonst wandert die
   Statuszeile aus dem Blick. */
.pane { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
.pane[hidden] { display: none; }
h2 { font-size: 15px; margin: 0 0 4px; }

/* --- Routenplanung ---------------------------------------------------------------
   Der Inhalt ist eine lange Liste; Eingabe und Filter bleiben oben, gescrollt wird alles
   gemeinsam (die Liste ist der Grund, warum man hier ist). */
#routePanel { flex: 1; overflow: auto; padding: 12px 16px 16px; }
.rt-form { display: flex; flex-direction: column; gap: 8px; }
.rt-wp input { min-width: 0; }
/* Die beiden kleinen Schaltflächen je Wegpunkt (eigener Standort, entfernen): feste Breite, damit
   die Felder untereinander gleich lang bleiben – auch wenn ein Zwischenziel dazukommt. */
.rt-icon { flex: none; width: 36px; padding: 7px 0; text-align: center; line-height: 1.1; }
.rt-inline { display: inline; margin: 0; flex: none; }
.rt-narrow { width: 6.5em; flex: none; }
/* Treffer der Ortssuche: dieselbe Anmutung wie die Standortsuche oben. */
.rt-hits { display: flex; flex-direction: column; max-height: 180px; overflow: auto; }
.rt-hits button {
  text-align: left; background: none; border: 0; color: var(--text);
  padding: 6px 8px; border-radius: 4px; cursor: pointer; font: inherit; font-size: 13px;
}
.rt-hits button:hover, .rt-hits button:focus-visible { background: #262d36; outline: none; }
.rt-filters { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 8px; }
.rt-filters summary { cursor: pointer; color: var(--muted); font-size: 13px; }
.rt-grid { display: grid; grid-template-columns: auto 1fr; gap: 6px 8px; align-items: center; margin-top: 8px; }
.rt-checks { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 8px; }
.rt-check { display: flex; align-items: center; gap: 5px; margin: 0; font-size: 13px; color: var(--text); }
.rt-check input { flex: none; }
.rt-hint { color: var(--busy); font-size: 13px; }
/* --- Routenvarianten -------------------------------------------------------------
   Auswahl wie beim Navi: die Zeilen stehen untereinander, die gewählte ist markiert. Sie
   erscheinen nur, wenn der Router überhaupt mehr als eine Route geliefert hat. */
.rt-variants { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; }
.rt-variant {
  display: flex; flex-direction: column; gap: 1px; text-align: left; cursor: pointer;
  background: none; border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px;
  color: var(--text); font: inherit;
}
.rt-variant:hover { border-color: #3d4855; }
/* Markiert wie die Zeilen der Liste: Rahmen UND Balken – eine Farbfläche allein ist auf
   schlechten Displays im Auto kaum zu erkennen. */
.rt-variant.active {
  border-color: var(--accent); background: #1f2a36; box-shadow: inset 3px 0 0 var(--accent);
}
.rt-variant-head { font-weight: 600; font-variant-numeric: tabular-nums; }
.rt-variant .muted { font-size: 12px; }
.rt-variant-roads { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rt-summary { margin-top: 12px; font-size: 13px; }
.rt-list { margin-top: 8px; display: flex; flex-direction: column; gap: 8px; }
.rt-item { border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; cursor: pointer; }
/* Hervorgehobene Zeile = hervorgehobener Standort auf der Karte (siehe setActive()). Balken links
   UND Rahmen: eine Hintergrundfarbe allein ist auf schlechten Displays im Auto kaum zu sehen. */
.rt-item.active {
  border-color: var(--accent); background: #1f2a36;
  box-shadow: inset 3px 0 0 var(--accent);
}
.rt-item:hover { border-color: #3d4855; }
.rt-head { display: flex; gap: 8px; align-items: baseline; }
.rt-name {
  background: none; border: 0; padding: 0; color: var(--text); font: inherit; font-weight: 600;
  text-align: left; cursor: pointer; flex: 1;
}
.rt-name:hover { color: var(--accent); }
/* Kilometrierung tabellarisch: die Zahlen sollen von Zeile zu Zeile untereinander stehen. */
.rt-km { flex: none; font-variant-numeric: tabular-nums; }
.rt-line { font-size: 13px; margin-top: 2px; }
/* Belegung: das einzige Feld der Zeile, das sich live ändert (siehe noteAvailability()).
   Tabellarische Ziffern, damit die Zahl beim Wechsel nicht in der Zeile springt. */
.rt-free { font-variant-numeric: tabular-nums; color: var(--free); }
.rt-free.none { color: var(--busy); }
/* Wort UND Farbe, wie überall: „Abfahrt nötig" ist die Aussage, die man sucht. */
.rt-exit { color: var(--busy); font-weight: 600; }
.rt-direct { color: var(--free); font-weight: 600; }
.rt-warn { color: var(--unavail); }
/* Läuft gerade eine Umwegrechnung? Der Punkt pulsiert – bei einem Abruf pro Sekunde dauert eine
   lange Route Minuten, und ohne Lebenszeichen sieht das aus wie hängen geblieben. */
.rt-busy { color: var(--accent); }
.rt-busy::before { content: '●'; margin-right: 5px; animation: rt-pulse 1.1s ease-in-out infinite; }
@keyframes rt-pulse { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }
/* Sparsam für alle, die Bewegung nicht wollen. */
@media (prefers-reduced-motion: reduce) { .rt-busy::before { animation: none; } }
.rt-progress { font-size: 13px; margin: 8px 0 0; }
/* --- Verkehr (HERE-Fahrzeiten, Meldungen der Autobahn GmbH; siehe routeTraffic.mjs) --- */
/* Verzögerung durch Verkehr: dieselbe Warnfarbe wie „Abfahrt nötig" – es ist eine Zusatzzeit. */
.rt-delay { color: var(--busy); font-weight: 600; }
/* „mit Verkehr" an der Ankunftszeit: klein und leise, die Uhrzeit ist die Aussage. */
.rt-traffic-tag {
  font-size: 11px; color: var(--muted); border: 1px solid var(--line); border-radius: 4px;
  padding: 0 4px; margin-left: 4px; white-space: nowrap;
}
.rt-notes { margin-top: 4px; font-size: 12px; }
.rt-traffic { margin-top: 10px; font-size: 13px; }
.rt-traffic summary { cursor: pointer; color: var(--muted); }
.rt-tr-list { margin-top: 6px; display: flex; flex-direction: column; gap: 6px; }
/* Eine Meldung: Knopf, weil ein Tipp sie auf der Karte zeigt. Balken links in der Warnfarbe,
   bei Stau/Sperrung in Rot – Wort UND Farbe, wie überall. */
.rt-tr {
  display: block; width: 100%; text-align: left; font: inherit; color: var(--text);
  background: none; border: 1px solid var(--line); border-radius: 8px; padding: 6px 10px;
  box-shadow: inset 3px 0 0 var(--busy);
}
.rt-tr > summary { cursor: pointer; list-style: none; }
.rt-tr > summary::-webkit-details-marker { display: none; }
.rt-tr.severe { box-shadow: inset 3px 0 0 var(--fault); }
/* Baustellen: gestrichelter Balken wie die Markierung auf der Karte – „Baustelle", nicht „Stau". */
.rt-tr.works { box-shadow: none; border-left: 3px dashed #ffd23f; }
.rt-tr:hover { border-color: #3d4855; }
.rt-tr[open] { background: #1f2a36; }
/* Von der Karte aus gewählt: Rahmen in der Akzentfarbe, wie die gewählte Standortzeile. */
.rt-tr.active { border-color: var(--accent); }
.rt-tr-body { margin-top: 6px; font-size: 13px; }
.rt-tr-body p { margin: 4px 0 0; white-space: pre-line; }
.rt-tr-quiet { margin-top: 8px; }
.rt-tr-quiet > summary { cursor: pointer; font-size: 12px; }
.rt-tr-head { display: flex; gap: 8px; align-items: baseline; }
.rt-tr-head .rt-km { margin-left: auto; }
.rt-tr .rt-line { display: block; }
/* Blase auf der Route (HERE-Ereignis oder Stau): sitzt ÜBER dem Punkt, Spitze zeigt auf ihn.
   Der Anker selbst ist 0×0 (Leaflet setzt ihn auf den Punkt), die Blase wird darüber
   verschoben – so stimmt die Lage unabhängig davon, wie breit der Text ist. */
.rt-bubble-anchor { width: 0; height: 0; background: none; border: 0; }
.rt-bubble {
  position: absolute; left: 0; top: 0; transform: translate(-50%, calc(-100% - 9px));
  white-space: nowrap; font: 600 12px/1.2 system-ui, sans-serif; color: #1b1b1b;
  background: #ffcf4d; border: 2px solid #0b0e12; border-radius: 10px; padding: 3px 7px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .45); cursor: pointer;
}
.rt-bubble.severe { background: #ff4d42; color: #fff; }
.rt-bubble::after {
  content: ''; position: absolute; left: 50%; bottom: -9px; transform: translateX(-50%);
  border: 7px solid transparent; border-top-color: #0b0e12; border-bottom: 0;
}
/* Legende der Färbung: nur die Stufen, die auf der Route vorkommen. */
.rt-legend { display: flex; gap: 12px; margin-top: 4px; font-size: 12px; }
.rt-legend i {
  display: inline-block; width: 18px; height: 5px; border-radius: 2px; margin-right: 5px;
  vertical-align: middle; box-shadow: 0 0 0 1px #0b0e12;
}

/* --- Statistik (Reiter „Statistik" + Block im Standortdetail) ----------------------
   Eigene Klassenfamilie wie .rt-* und .tl-*: dieselbe Optik, aber die Statistik trägt Zustände
   (zu wenig Daten, geschlossen, Farbstufe), die es sonst nirgends gibt. Gemeinsame Klassen
   nähmen beim nächsten Umbau der einen Ansicht die andere mit. */
#statsPanel { flex: 1; overflow: auto; padding: 12px 16px 16px; }
.st-narrow { width: 6em; flex: none; }
/* ⚠️ Drei Auswahlfelder plus Knopf passen in 430 px NICHT mehr in eine Zeile – ohne Umbruch
   werden „Farbe" und „Zeigen" auf ein paar Zeichen gequetscht. Sie umbrechen deshalb paarweise:
   `gap` in beide Richtungen, und Beschriftung + Feld halten über `white-space` zusammen. */
.st-controls { align-items: baseline; flex-wrap: wrap; gap: 6px 10px; }
/* Beschriftung und Feld sind EIN Element – der Umbruch darf nur zwischen den Paaren fallen. */
.st-ctrl { display: inline-flex; align-items: baseline; gap: 6px; }
.st-ctrl label { margin: 0; flex: none; color: var(--muted); font-size: 13px; }
.st-ctrl select { min-width: 7em; }
.st-ctrl select.st-narrow { min-width: 0; width: 6em; }
.st-error { color: var(--fault); font-size: 13px; margin: 8px 0 0; }
.st-basis { margin: 10px 0 0; }
.st-h { font-size: 13px; font-weight: 600; margin: 16px 0 6px; color: var(--muted); }

/* Kacheln: der Wert groß, darunter das Wort. Tabellenziffern, damit die Spalte nicht zappelt. */
.st-tiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: 8px; margin-top: 10px;
}
.st-tile { border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; }
.st-tile-value { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
.st-tile-value.muted { font-size: 13px; font-weight: 400; }
.st-tile-label { font-size: 13px; margin-top: 2px; }
.st-tile-note { font-size: 11px; }

/* Die Matrix: je Zeile ein eigenes Grid mit identischen Spalten. Bewusst kein
   `display: contents` – dessen Verhalten gegenüber Vorleseprogrammen ist historisch
   unzuverlässig, die Spalten stehen so trotzdem genau untereinander.
   ⚠️ Waagerecht scrollen statt umbrechen: Ein umgebrochenes Wochenraster ist keins mehr. */
/* ⚠️ Die 2 px Innenabstand sind tragend, nicht kosmetisch: Ein Scroller schneidet an seiner
   PADDING-Kante ab, und die Zeilen sind genau so breit wie er. Ohne den Abstand fielen die
   SENKRECHTEN Seiten des Zeilenrahmens (unten, 1,5 px außerhalb der Zeile) genau in den
   abgeschnittenen Streifen – aus dem Rechteck würden zwei Striche ohne Seiten. */
.st-heat { overflow-x: auto; margin-top: 4px; padding: 2px; }
/* ⚠️ Eigenes Element zwischen Scroller und Zeilen: Die Spaltenmarkierung liegt absolut darüber
   und müsste sonst am SCROLLER hängen – dann bliebe sie beim Scrollen stehen, statt mitzuwandern. */
.st-grid { position: relative; min-width: 300px; }
/* Mit gedrückter Maustaste wandert das Fadenkreuz (`statsWire.mjs`) – ohne `user-select` markierte
   dasselbe Ziehen nebenbei die Zahlen in den Zellen als Text. */
.st-grid { user-select: none; -webkit-user-select: none; }
.st-row { display: grid; grid-template-columns: 2.4em repeat(24, 1fr); gap: 2px; }
.st-row + .st-row { margin-top: 2px; }
.st-day { font-size: 11px; color: var(--muted); align-self: center; }
.st-hour { font-size: 9px; color: var(--muted); text-align: center; }
.st-head { margin-bottom: 2px; }
.st-cell {
  aspect-ratio: 1; min-width: 9px; border-radius: 2px; background: #161b22;
  display: flex; align-items: center; justify-content: center; cursor: default;
}
.st-cell:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Das laufende Zeitfenster als FADENKREUZ: die ganze Zeile UND die ganze Spalte.
   Ein Ring um die eine Zelle war zwar korrekt, aber in einem 7×24-Raster nicht zu finden – mit
   Zeile und Spalte springt „jetzt" sofort ins Auge, und man kann an beiden entlanglesen.
   Die Zelle selbst bekommt deshalb KEINE eigene Markierung mehr; sie ist der Schnittpunkt.
   `box-shadow` statt `outline`, damit es nicht mit dem Fokusrahmen streitet. */
.st-row[data-now] { box-shadow: 0 0 0 1.5px var(--now); border-radius: 3px; }
/* Die Spalte: dieselbe Rasterdefinition, absolut über den Zeilen – dadurch sitzt sie exakt auf
   der Spalte und reicht über die volle Höhe. `margin: 0` hebt den Zeilenabstand auf, der die
   Überlagerung sonst um 2 px verschöbe. */
.st-nowcol { position: absolute; inset: 0; margin: 0; pointer-events: none; }
.st-nowcol .on { border: 1.5px solid var(--now); border-radius: 3px; }
.st-num { font-size: 9px; color: rgba(255,255,255,.72); font-variant-numeric: tabular-nums; }
/* ⚠️ Die Rampe ist luminanzmonoton – auf den hellen Stufen ist weiße Schrift nicht mehr lesbar.
   Ab Stufe 3 kippt die Tinte deshalb auf Dunkel. */
.st-cell[data-level="3"] .st-num,
.st-cell[data-level="4"] .st-num { color: rgba(0,0,0,.78); }

/* „Zu wenig Daten" und „geschlossen" werden GEMUSTERT, nicht eingefärbt: Ein Muster wirkt
   unabhängig von der Farbe und lässt sich mit keiner Stufe verwechseln. */
.st-cell[data-state="sparse"], .st-swatch[data-state="sparse"] {
  background: repeating-linear-gradient(45deg, #1b2027 0 3px, #2c333d 3px 6px);
}
.st-cell[data-state="closed"], .st-swatch[data-state="closed"] {
  background: repeating-linear-gradient(-45deg, #12151a 0 3px, #232833 3px 6px);
}

.st-heat[data-tone="occ"] [data-level="0"] { background: var(--occ-0); }
.st-heat[data-tone="occ"] [data-level="1"] { background: var(--occ-1); }
.st-heat[data-tone="occ"] [data-level="2"] { background: var(--occ-2); }
.st-heat[data-tone="occ"] [data-level="3"] { background: var(--occ-3); }
.st-heat[data-tone="occ"] [data-level="4"] { background: var(--occ-4); }
.st-heat[data-tone="full"] [data-level="0"] { background: var(--full-0); }
.st-heat[data-tone="full"] [data-level="1"] { background: var(--full-1); }
.st-heat[data-tone="full"] [data-level="2"] { background: var(--full-2); }
.st-heat[data-tone="full"] [data-level="3"] { background: var(--full-3); }
.st-heat[data-tone="full"] [data-level="4"] { background: var(--full-4); }
.st-heat[data-tone="unavail"] [data-level="0"] { background: var(--un-0); }
.st-heat[data-tone="unavail"] [data-level="1"] { background: var(--un-1); }
.st-heat[data-tone="unavail"] [data-level="2"] { background: var(--un-2); }
.st-heat[data-tone="unavail"] [data-level="3"] { background: var(--un-3); }
.st-heat[data-tone="unavail"] [data-level="4"] { background: var(--un-4); }
/* Die Legende steht außerhalb der Matrix und braucht die Stufenfarben deshalb noch einmal;
   voreingestellt ist die Skala der Belegung. */
.st-legend [data-level="0"] { background: var(--occ-0); }
.st-legend [data-level="1"] { background: var(--occ-1); }
.st-legend [data-level="2"] { background: var(--occ-2); }
.st-legend [data-level="3"] { background: var(--occ-3); }
.st-legend [data-level="4"] { background: var(--occ-4); }

.st-legend { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 8px; font-size: 11px; }
.st-key { display: inline-flex; align-items: center; gap: 4px; }
.st-swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }
/* Die Vorlesezeile: trägt den Satz der zuletzt berührten Zelle – und trägt auf schmalen
   Geräten die Zahl, die dann nicht mehr in die Zelle passt. */
.st-readout { margin: 8px 0 0; min-height: 2.6em; }

/* Diagramme (js/charts.mjs). Die Farben stehen NUR hier, nie im erzeugten SVG. */
.ch { width: 100%; height: auto; margin-top: 4px; }
.ch-grid { stroke: var(--line); stroke-width: 1; }
.ch-base { stroke: #3d4855; }
.ch-axis { fill: var(--muted); font-size: 8px; }
.ch-empty { fill: var(--muted); font-size: 10px; }
.ch-bar { fill: var(--accent); }
.ch[data-tone="full"] .ch-bar { fill: var(--busy); }
.ch[data-tone="unavail"] .ch-bar { fill: var(--unavail); }
/* Zu dünne Datenlage: gedämpft, damit sie nicht wie ein belastbarer Wert aussieht. */
.ch-bar.ch-thin { opacity: .38; }
/* Keine Daten: unsichtbarer Trefferbereich, damit der Tooltip trotzdem etwas sagt. */
.ch-none { fill: transparent; }
/* Die laufende Stunde: ein Rahmen um den ganzen Zeitschlitz, nicht um den Balken – hervorgehoben
   ist das Zeitfenster, nicht sein Wert. Bewusst nur eine Kontur: eine Füllung würde den Balken
   darunter verfälschen.
   ⚠️ Nicht `--accent`: Der Balken IST `--accent`. Ein blauer Rahmen um einen blauen Balken war
   nur dort zu sehen, wo er über den Balken hinausragte. `--now` trägt auf dem Balken UND auf dem
   dunklen Grund – und ist dieselbe Farbe wie das Fadenkreuz im Wochenprofil, damit „jetzt" in
   beiden Diagrammen dasselbe heißt. */
.ch-now { fill: none; stroke: var(--now); stroke-width: 1.5; }

/* Aufschlüsselung je Säule / Leistungsklasse. Zeilen zum Überfliegen, anklickbar zum Eingrenzen –
   Aufbau wie die Listen (.tl-item), aber eigene Klassen: hier trägt die Zeile einen Auswahlzustand,
   den es dort nicht gibt. */
.st-bdkinds { display: flex; gap: 6px; margin-top: 6px; }
.st-bdkind {
  background: none; border: 1px solid var(--line); border-radius: 6px; color: var(--muted);
  font: inherit; font-size: 12px; padding: 4px 10px; cursor: pointer;
}
.st-bdkind.active { border-color: var(--accent); color: var(--text); }
.st-bdhint { font-size: 12px; margin: 6px 0 0; }
.st-bdlist { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
.st-bdrow {
  display: block; width: 100%; text-align: left; background: none; color: var(--text);
  border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px; cursor: pointer;
  font: inherit;
}
.st-bdrow:hover { border-color: #3d4855; }
.st-bdrow.active { border-color: var(--accent); background: #1f2732; }
.st-bdhead { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; font-size: 13px; }
.st-bdvals { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 3px; font-size: 12px; }
.st-bdval { font-variant-numeric: tabular-nums; }
.st-bdline { display: block; font-size: 11px; margin-top: 2px; }

/* Die Zeile „Eingegrenzt auf …" samt Weg zurück. */
.st-scope { font-size: 13px; margin: 8px 0 0; }
.st-scope button { font-size: 12px; padding: 2px 8px; margin-left: 6px; }

.st-meaning, .st-axis-note { font-size: 12px; margin: 0 0 4px; }
/* Überschrift eines Diagramms plus SEIN Wähler. Der steht am Diagramm, nicht in der Bedienzeile:
   Beide Diagramme haben einen, aber getrennt – in einer gemeinsamen Zeile wäre nicht zu sehen,
   welcher zu welchem gehört, und vier Auswahlfelder sprengen die 430-px-Leiste. */
.st-hrow {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  flex-wrap: wrap;
}
.st-hrow .st-h { margin-bottom: 2px; }
.st-mini { font-size: 12px; padding: 2px 4px; }

.st-limits { margin-top: 14px; font-size: 13px; }
.st-limits summary { cursor: pointer; color: var(--muted); }
.st-limits ul { margin: 6px 0 0; padding-left: 18px; }
.st-limits li { margin-bottom: 5px; }

/* Der Block im Standortdetail – dieselben Bausteine, nur aufklappbar. */
.st-site { margin: 12px 0; }
.st-site summary { cursor: pointer; font-weight: 600; }

/* --- Listen (Reiter „Listen") -----------------------------------------------------
   Aufbau wie die Routenliste: oben die Auswahl, darunter Zeilen, die man überfliegt. Bewusst
   eigene Klassen statt .rt-*: Dieselbe Optik, aber die Routenliste trägt Umweg-Zustände
   (berechnet/unbekannt/Abfahrt), die es hier nicht gibt – gemeinsame Klassen würden beim
   nächsten Umbau der einen Ansicht die andere mitnehmen. */
#topPanel { flex: 1; overflow: auto; padding: 12px 16px 16px; }
/* Beschriftung vor dem Auswahlfeld: label ist sonst ein Block mit eigenem Abstand. */
.tl-inline { display: inline; margin: 0; flex: none; }
.tl-narrow { width: 4.5em; flex: none; }
.tl-hint { margin: 10px 0 0; }
.tl-summary { margin-top: 8px; font-variant-numeric: tabular-nums; }
.tl-error { color: var(--fault); font-size: 13px; margin: 8px 0 0; }
.tl-list { margin-top: 8px; display: flex; flex-direction: column; gap: 8px; }
.tl-item { border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; cursor: pointer; }
.tl-item:hover { border-color: #3d4855; }
.tl-item.active { border-color: var(--accent); background: #1f2732; }
.tl-head { display: flex; gap: 8px; align-items: baseline; }
/* Der Rang macht die Reihenfolge lesbar: „der drittgrößte Standort" ist die Aussage der Liste,
   und ohne Zahl zählt man sie ab. Tabellenziffern, damit die Spalte nicht zappelt. */
.tl-rank {
  flex: none; min-width: 1.6em; text-align: right; color: var(--muted);
  font-size: 12px; font-variant-numeric: tabular-nums;
}
.tl-name {
  background: none; border: 0; padding: 0; color: var(--text); font: inherit; font-weight: 600;
  text-align: left; cursor: pointer; flex: 1; min-width: 0;
}
.tl-name:hover { color: var(--accent); }
/* Die Angabe, nach der sortiert wurde – rechts, damit die Spalte von oben nach unten lesbar ist. */
.tl-lead { flex: none; font-size: 13px; color: var(--accent); font-variant-numeric: tabular-nums; }
.tl-line { font-size: 13px; margin-top: 2px; }
.tl-free { font-variant-numeric: tabular-nums; color: var(--free); }
.tl-free.none { color: var(--busy); }
/* Zustand des Livefeeds – bewusst getrennt von der Statuszeile der Pflege, die sonst
   Hinweise mit Schaltflächen verlieren würde. */
.live-on::before, .live-wait::before, .live-off::before {
  content: '●'; margin-right: 5px;
}
.live-on { color: var(--free); }
.live-wait { color: var(--busy); }
.live-off { color: var(--fault); }
label { display: block; font-size: 13px; color: var(--muted); margin: 12px 0 4px; }
input[type=text], input[type=number], select {
  width: 100%; padding: 7px 9px; border-radius: 6px; border: 1px solid var(--line);
  background: #0f1216; color: var(--text); font: inherit;
}
/* Nur die Anzeigereihenfolge steht in einem Zahlenfeld, und mehr als 99 Säulen hat kein
   Standort – schmal halten, der Platz fehlt sonst der Leistungsangabe daneben. Ohne die
   Pfeiltasten des Browsers (die je Browser 16–20px kosten) bleibt in 48px die Beschriftung
   „Nr" lesbar; hoch/runter über die Tastatur funktioniert weiterhin, und für die ganze Reihe
   gibt es „1…N durchnummerieren". */
input[type=number] { width: 48px; appearance: textfield; -moz-appearance: textfield; }
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button { appearance: none; margin: 0; }
button.primary { background: var(--accent); color: #06121f; border: 0; padding: 9px 14px;
  border-radius: 6px; font-weight: 600; cursor: pointer; }
button.primary:disabled { opacity: .5; cursor: default; }
button.ghost { background: none; border: 1px solid var(--line); color: var(--text);
  padding: 7px 11px; border-radius: 6px; cursor: pointer; }
/* Säulen sind Karten, keine Tabelle: die Ladepunkte tragen jetzt sichtbaren Text
   (Status, seit wann, Technik). In fünf Tabellenspalten wäre das – erst recht auf einem
   Smartphone – nicht lesbar unterzubringen. */
.pillar { border: 1px solid var(--line); border-radius: 8px; padding: 10px; margin-top: 8px; }
/* Kopfzeile der Säule: Kennung, Leistung und Überdachung sollen in EINE Zeile passen. Deshalb
   schmalerer Abstand und ein Auswahlfeld in Zeilenschriftgröße – mit den 15px des Fließtexts
   erschien „überdacht" ohnehin abgeschnitten. Was das Feld bedeutet, sagen Beschriftung
   (aria-label), title und der Hinweistext über den Säulen. */
.pillar select { width: 100px; flex: none; font-size: 13px; }
.pillar .row { flex-wrap: wrap; gap: 8px; }
.pillar .head { font-size: 13px; }
/* „max. 150 kW" ist eine Angabe und darf nicht auf zwei Zeilen zerfallen (die Zahl und die
   Einheit hält bereits ein geschütztes Leerzeichen zusammen, siehe withUnit()). */
.pillar .head .muted { white-space: nowrap; }
/* Hinweistext über den Säulen: drei kurze Absätze statt eines Blocks. Der Standardabstand von
   <p> (1 em) risse sie auseinander – sie gehören zusammen und sollen wie EIN Vorspann über der
   Liste stehen. */
.pillar-hint { margin: 6px 0 0; }
/* Ein Legendeneintrag (Form + Bezeichnung) darf nie umbrechen: eine Form am Zeilenende und ihr
   Wort auf der nächsten Zeile wäre genau die Zuordnung, die die Legende erklären soll. */
.shape-key { white-space: nowrap; }
/* DC = Quadrat (wie am E-Paper), AC = Dreieck. Bewusst keine Kreise, damit Karte,
   Detailansicht und Gerät dieselbe Formensprache benutzen. */
.dot { display: inline-block; width: 9px; height: 9px; margin-right: 5px; }
/* clip-path statt Border-Trick, damit die Statusfarbe (background) erhalten bleibt.
   Etwas breiter/höher als das Quadrat, weil ein Dreieck bei gleicher Bounding-Box nur
   halb so viel Fläche hat und sonst zu zierlich wirkt. */
.dot.ac {
  width: 11px; height: 10px;
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
/* CHAdeMO = gefüllter Kreis. In der Seitenleiste soll auf einen Blick erkennbar sein, dass
   an dieser Säule ein anderer Steckertyp hängt (Quadrat = CCS-DC, Dreieck = AC). Etwas
   größer als das Quadrat, weil ein Kreis bei gleicher Bounding-Box kleiner wirkt. */
.dot.chademo { width: 10px; height: 10px; border-radius: 50%; }
/* Eigener Standort: Kreis – bewusst die einzige runde Form auf der Karte. */
.dot.own {
  border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 1.5px #fff inset;
}
/* Ein Ladepunkt je Zeile, Text IMMER sichtbar. Vorher steckte alles in title=, was auf
   Touchgeräten grundsätzlich nicht abrufbar ist – die Angabe "besetzt seit 3 Std." ist aber
   genau die, die man unterwegs braucht. */
.points { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; }
.pt { display: flex; gap: 7px; align-items: flex-start;
  border-top: 1px solid var(--line); padding-top: 6px; }
.pt:first-child { border-top: 0; padding-top: 0; }
/* Die Form sitzt sonst auf der Textoberkante; margin-right der .dot-Grundform stört hier. */
.pt .dot { margin: 4px 0 0; flex: none; }
/* Steckernummer (Ende der evseId) vor der Form. Feste Breite und tabellarische Ziffern,
   damit die Formen darunter in einer Spalte stehen – auch wenn eine Nummer fehlt. */
.pt-no {
  flex: none; min-width: 1.1em; text-align: right; font-size: 13px;
  color: var(--muted); font-variant-numeric: tabular-nums;
}
.pt-head { font-size: 13px; }
.pt-tech { font-size: 12px; color: var(--muted); }
/* --- Öffnungszeiten --------------------------------------------------------------
   Die Woche als schmale zweispaltige Liste (Tag | Zeiten). Tabellarische Ziffern, damit die
   Uhrzeiten untereinander stehen; der heutige Tag ist hervorgehoben, weil man ihn zuerst
   sucht. Bei 24/7 entfällt die Liste ganz und es bleibt eine Zeile. */
.oh { margin-top: 4px; font-size: 13px; }
.oh-week { display: grid; grid-template-columns: 2.4em auto; gap: 1px 8px; margin-top: 3px; }
.oh-week > * { color: var(--muted); font-variant-numeric: tabular-nums; }
.oh-week .today { color: var(--text); font-weight: 600; }
/* Punkt wie beim Livezustand: Farbe allein ist keine Information, das Wort steht daneben. */
.oh-open::before, .oh-closed::before, .oh-unknown::before { content: '●'; margin-right: 5px; }
.oh-open { color: var(--free); }
.oh-closed { color: var(--unavail); }
.oh-unknown { color: var(--unknown); }
/* Verlauf je Ladepunkt: zugeklappt, damit die Liste der Ladepunkte lesbar bleibt. Die Daten
   werden erst beim Aufklappen geholt – sonst lägen bei 20 Ladepunkten 20 Abfragen an, von
   denen niemand eine braucht. */
.hist { margin-top: 3px; }
.hist summary { font-size: 12px; color: var(--muted); cursor: pointer; }
.hist ol { list-style: none; margin: 5px 0 2px; padding: 0; font-size: 12px; }
.hist li { display: flex; gap: 6px; padding: 2px 0; border-top: 1px dashed var(--line); }
.hist li:first-child { border-top: 0; }
/* Tabellarische Ziffern: sonst tanzen Zeitpunkt und Dauer von Zeile zu Zeile. */
.h-when, .h-dur { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.h-dur { margin-left: auto; }
.h-raw { color: var(--muted); }
/* Angaben aus dem Abgleich mit der öffentlichen EnBW-API. Wort UND Farbe, wie überall:
   Farbe allein ist keine Information. „unbekannt" ist ein eigener Zustand – die Quelle
   (DATEX II) kennt Autocharge nicht, der Abgleich kann noch ausstehen. */
.fact-yes { color: var(--free); }
.fact-no { color: var(--unavail); }
.fact-unknown { color: var(--unknown); }
/* Säulennummer der EnBW als Marke vor der evseId: unter dieser Nummer findet man die Säule
   in der EnBW-App/Website wieder. Bewusst NICHT die Anzeigereihenfolge (die steht links im
   Zahlenfeld und beschreibt die Aufstellung vor Ort). */
.enbw-no {
  flex: none; font-size: 12px; color: var(--muted);
  border: 1px solid var(--line); border-radius: 4px; padding: 0 4px;
  font-variant-numeric: tabular-nums;
}
.FREE { background: var(--free); } .OCCUPIED { background: var(--busy); }
.UNAVAILABLE { background: var(--unavail); }
.FAULT { background: var(--fault); } .UNKNOWN { background: var(--unknown); }
#status { padding: 10px 16px; border-top: 1px solid var(--line); font-size: 13px; min-height: 40px; }
#legend { position: absolute; left: 12px; bottom: 12px; background: rgba(27,32,39,.92);
  padding: 9px 12px; border-radius: 8px; font-size: 12px; z-index: 500; }
.row { display: flex; gap: 10px; align-items: center; }
.grow { flex: 1; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Auswahl schließen (x) in der Kopfzeile der Detailansicht. */
button.close {
  background: none; border: 1px solid var(--line); color: var(--muted);
  width: 26px; height: 26px; border-radius: 6px; cursor: pointer; font-size: 15px;
  line-height: 1; flex: none;
}
button.close:hover { color: var(--text); }
/* „Teilen" daneben: gleiche Familie wie die Schließen-Schaltfläche (dezent, am Rand der
   Kopfzeile), nur mit Wort statt Zeichen – „teilen" als Symbol ist auf jeder Plattform ein
   anderes und wäre hier ohne Beschriftung geraten.
   ⚠️ flex: none – sonst würde die Schaltfläche in der Kopfzeile schrumpfen, sobald ein langer
   Standortname danebensteht, und „Teilen" bräche um. */
button.share {
  background: none; border: 1px solid var(--line); color: var(--muted);
  height: 26px; padding: 0 9px; border-radius: 6px; cursor: pointer; font: inherit;
  font-size: 13px; line-height: 1; flex: none;
}
button.share:hover { color: var(--text); }
/* Schaltflächen über der Karte (Mein Standort, Leiste). Sie hängen als Leaflet-Control in der
   Ecke der KARTE (siehe dockMapButtons()) – am Fensterrand ausgerichtet lägen sie auf
   breiten Bildschirmen über der Seitenleiste und würden deren Kopfzeile verdecken.
   Der Behälter selbst ist für Klicks durchlässig, damit die Lücke zwischen den Schaltflächen
   weiter die Karte trifft.
   ⚠️ `position: absolute` ist Pflicht, obwohl Leaflet die Position übernimmt: Bis das Skript
   das Element in die Karte hängt, wäre es ein FLEX-ELEMENT in #layout und würde der Karte
   Breite wegnehmen – Leaflet vermisst die Karte beim Start und ließe rechts einen grauen
   Streifen stehen (dessen Breite mit der Zoomstufe wandert). Angedockt hebt die Regel
   darunter das wieder auf, wie bei der Legende. */
#mapControls {
  position: absolute; top: 12px; right: 12px; z-index: 600;
  display: flex; gap: 8px; pointer-events: none;
}
/* Angedockt positioniert Leaflet die Ecke selbst; eigene Koordinaten würden das aushebeln. */
#mapControls.leaflet-control { position: static; top: auto; right: auto; }
.map-btn {
  pointer-events: auto;
  background: rgba(27,32,39,.92); border: 1px solid var(--line); color: var(--text);
  border-radius: 6px; padding: 6px 10px; cursor: pointer; font: inherit; font-size: 13px;
}
.map-btn:disabled { opacity: .5; cursor: default; }
/* Dieselbe Optik als Link (die Anmeldung MUSS eine Navigation sein, siehe index.html). */
a.map-btn { display: inline-block; margin-top: 6px; text-decoration: none; }
/* ⚠️ Muss NACH der Zeile darüber stehen: Eine eigene display-Angabe schlägt das
   [hidden]-Verhalten des Browsers aus – der Knopf bliebe sonst auch unter /admin stehen,
   wo es nichts mehr anzumelden gibt. Gleiches Muster wie bei .pane[hidden] weiter oben. */
a.map-btn[hidden] { display: none; }
#layout.side-hidden #side { display: none; }

/* --- Zeitreise --------------------------------------------------------------------
   Wie #mapControls: absolut, bis Leaflet das Element in seine Ecke hängt (sonst nähme es der
   Karte als Flex-Element Breite weg). */
#travel {
  position: absolute; top: 56px; right: 12px; z-index: 600;
  display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
}
#travel.leaflet-control { position: static; top: auto; right: auto; }
#travel.active #travelToggle {
  border-color: var(--past); color: var(--past); font-weight: 600;
  box-shadow: 0 0 0 1px var(--past);
}
#travelPanel {
  width: 250px; background: rgba(27,32,39,.96); border: 1px solid var(--line);
  border-radius: 8px; padding: 4px 10px 10px;
}
#travelPanel[hidden] { display: none; }
#travelPanel label { margin-top: 6px; }
#travelPanel input { color-scheme: dark; }
.travel-steps { gap: 4px; margin: 8px 0; flex-wrap: wrap; }
.travel-steps button { flex: 1; padding: 5px 4px; font-size: 12px; }
#travelLive { width: 100%; }
#travelLive:disabled { opacity: .5; cursor: default; }
#travelPanel p { margin: 8px 0 0; font-size: 12px; }
.travel-note { color: var(--past); font-size: 13px; font-weight: 600; }
/* Zeitpunkt im Verlauf: eine Schaltfläche, die wie der Text davor aussieht. */
.h-jump {
  background: none; border: 0; padding: 0; font: inherit; cursor: pointer;
  text-decoration: underline dotted; text-underline-offset: 2px;
}
.h-jump:hover { color: var(--past); }
.hist li.h-at { box-shadow: inset 3px 0 0 var(--past); padding-left: 5px; }
/* Dauerhafte Beschriftung neben jedem Punkt: frei/gesamt. */
.leaflet-tooltip.lbl {
  background: rgba(18,21,26,.72); border: 0; box-shadow: none; color: var(--text);
  font-size: 11px; font-weight: 600; padding: 1px 4px; border-radius: 3px;
  pointer-events: none; white-space: nowrap;
}
.leaflet-tooltip.lbl::before { display: none; }   /* kein Zeiger-Dreieck */
/* Formenerklärung zusammenklappbar: auf dem Smartphone verdeckte die vollständige Legende
   einen guten Teil der Karte. Datenstand und Livezustand bleiben immer sichtbar. */
#legendKey summary { cursor: pointer; color: var(--muted); }
#legendKey[open] summary { margin-bottom: 3px; }
/* Angedockt (siehe dockLegend()): Leaflet positioniert die Ecke selbst, eigene Koordinaten
   würden das aushebeln. id+Klasse schlägt die #legend-Regeln, auch die in der Media Query. */
#legend.leaflet-control {
  position: static; top: auto; right: auto; bottom: auto; left: auto;
}

/* --- Schmale Bildschirme ---------------------------------------------------------
   Eine 430 px breite Spalte ist auf dem Smartphone fast der ganze Bildschirm. Deshalb wird
   die Seitenleiste dort zum Blatt am unteren Rand, das ÜBER der Karte liegt: die Karte
   behält ihre volle Fläche, das Blatt kommt nur bei Bedarf hoch.
   ⚠️ Die Grenze 820px steht auch im Skript (matchMedia) – beide gehören zusammen. */
@media (max-width: 820px) {
  /* ⚠️ z-index ÜBER 1000: Die Legende hängt auf schmalen Geräten als Leaflet-Control oben links
     (siehe dockLegend()), und Leaflet stapelt seine Control-Ecken auf 1000. Weil #map ein blankes
     Flex-Element ohne position/z-index ist, bildet es keinen eigenen Stapelkontext – die 1000
     tritt also direkt gegen diese Zahl an und zeichnete die Legende mitten in das aufgeklappte
     Blatt. Eine eigene Zahl an #legend hilft nicht: Der Container .leaflet-top ist mit seinem
     z-index selbst ein Stapelkontext, aus dem ein Kind nicht nach unten ausbrechen kann. */
  #side {
    position: fixed; left: 0; right: 0; bottom: 0; width: auto; z-index: 1100;
    max-height: 80vh; max-height: 80dvh;
    border-left: 0; border-top: 1px solid var(--line); border-radius: 14px 14px 0 0;
    box-shadow: 0 -8px 24px rgba(0,0,0,.5);
    transition: transform .18s ease-out, visibility .18s;
  }
  /* Eingeklappt nur wegschieben statt display:none, damit der Übergang sichtbar bleibt.
     visibility verhindert, dass die Felder im weggeschobenen Blatt den Tab-Fokus fangen. */
  #layout.side-hidden #side {
    display: flex; transform: translateY(100%); visibility: hidden;
  }
  header { padding: 10px 14px; }
  #results { max-height: 34vh; }
  #detail { padding: 14px; }
  #routePanel, #topPanel, #statsPanel { padding: 10px 14px 14px; }
  /* Auf schmalen Geräten ist in den Zellen kein Platz für Ziffern – die Vorlesezeile unter der
     Matrix trägt die Zahl dann allein. */
  .st-num { display: none; }
  /* Auf dem Blatt ist die Höhe knapp: Filter und Liste dürfen sich die Fläche teilen, ohne dass
     die Eingabefelder verschwinden. */
  .rt-checks { gap: 4px 10px; }
  /* Home-Indikator/Gestenleiste freihalten. */
  #status { padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px)); }
  /* Unter 16px zoomt iOS beim Fokussieren in das Feld hinein und die Karte verrutscht. */
  input[type=text], input[type=number], select { font-size: 16px; }
  /* Notch/Statusleiste des Geräts freihalten (Leaflet setzt nur den festen Innenabstand). */
  #mapControls { margin-top: env(safe-area-inset-top, 0px); }
  #travelPanel { width: min(250px, 70vw); }
  #travelPanel input { font-size: 16px; }
  /* Die Legende hängt hier in Leaflets Ecke oben links (dockLegend()) – unten läge sie hinter
     dem Blatt. Nur noch Größe: die Position macht Leaflet, sonst überdeckt sie die
     Zoom-Buttons, die in derselben Ecke sitzen. */
  #legend { font-size: 11px; padding: 7px 9px; max-width: 58vw; }
}
