/*
 * project.css — LimnoNetz Komponenten-Layer (UI-Foundation).
 * Wird NACH vendor/bootstrap und tokens.css geladen.
 *
 * Regel: ausschließlich var(--ln-*) / var(--bs-*) — keine Hex-Werte
 * (einzige Farbquelle ist tokens.css, Abnahmekriterium §6).
 * Klassennamen sind Vertrag mit den Templates/Partials (Ticket §3).
 */

/* ================================================================
 * Basis
 * ================================================================ */

/* Sticky-Footer-Layout: ohne das endet die Seite am Inhalt und darunter
   steht eine leere Fläche in Browser-Weiß statt in --ln-surface. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  background-color: var(--ln-surface);
  background-image: var(--ln-surface-wash);
  background-repeat: no-repeat;
  background-attachment: fixed;
  color: var(--ln-ink);
  font-family: var(--ln-font-sans);
}

body > main {
  flex: 1 0 auto;
}

/* Kompakte Dashboard-Skala (Ticket §2.3), auf das 14px-Grundmaß
   ausbalanciert — die alte Skala stammte vom 16px-Fließtext. */
/* h1 trägt seinen Rang über Gewicht und Farbe, nicht über Größe: 1,5rem bei
   Bootstraps Gewicht 500 war groß und zugleich kraftlos. Geist ist variabel
   (100–900), 650 ist dort satt ohne zu klotzen. Farbe --ln-primary-deep
   statt --ln-ink — misst 9,2:1 auf --ln-card, hebt die Überschrift also
   deutlich vom Fließtext ab, ohne die Kontrastprüfung zu gefährden. */
h1, .h1 {
  font-size: 1.3125rem;
  font-weight: 650;
  letter-spacing: -0.02em;
  color: var(--ln-primary-deep);
}
/* h2 rückt mit: bliebe es bei 1,25rem, lägen h1 und h2 einen Pixel
   auseinander und die Rangfolge hinge allein an Farbe und Gewicht. */
h2, .h2 {
  font-size: 1.125rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

h3, .h3 { font-size: 1rem; letter-spacing: -0.01em; }

/* Bootstrap-Buttons hängen an --bs-btn-font-size, nicht am Fließtext —
   ohne das blieben sie 16px neben 14px-Umgebung stehen. */
.btn {
  --bs-btn-font-size: 0.875rem;
}

.btn-sm {
  --bs-btn-font-size: 0.8125rem;
}

/* ---- Luft unter der Nav ----
   Gehört der Seite, nicht der einzelnen Komponente. Vorher trug jede
   Seite ihren Abstand selbst (.ln-page-head 2rem, sonst ad-hoc mt-3) —
   wer beides vergaß, klebte an der Navkante: Formularseiten und die
   Konto-Seiten taten genau das. Einmal hier, gilt überall. */
#ln-main {
  padding-block-start: 1.5rem;
}

/* ---- Fließtextseiten (Impressum, Datenschutz) ----
   Die kompakte Dashboard-Skala ist für Kacheln gemacht, nicht für
   Absätze: hier zählt Lesbarkeit, also mehr Zeilenhöhe und eine
   begrenzte Zeilenlänge. Rechtstexte werden gelesen, nicht gescannt. */
.ln-prose {
  max-width: 46rem;
  line-height: 1.65;
}

.ln-prose h2 {
  margin-top: 2.75rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--ln-line);
  margin-bottom: 0.75rem;
  font-size: 1.3rem;
}

.ln-prose p,
.ln-prose ul,
.ln-prose table {
  margin-bottom: 1rem;
}

/* ---- Info-Seiten (FAQ, Impressum, Datenschutz) ----
   Redaktioneller Kopf und ruhiger Abschnitts-Rhythmus statt nackter
   Browser-Typografie; die Trennlinie über jedem h2 kommt aus .ln-prose. */
.ln-info-kicker {
  margin: 0 0 0.75rem;
  color: var(--ln-primary);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.095em;
  line-height: 1.4;
  text-transform: uppercase;
}

.ln-prose .ln-info-head {
  margin-bottom: 2rem;
}

.ln-prose .ln-info-head h1 {
  margin-bottom: 0.5rem;
  font-size: clamp(2.1rem, 5vw, 2.9rem);
  letter-spacing: -0.015em;
}

.ln-prose .ln-info-lead {
  color: var(--ln-ink-muted);
  font-size: 1.05rem;
}

/* Erster Abschnitt direkt nach dem Kopf braucht keine Trennlinie. */
.ln-prose .ln-info-head + h2,
.ln-prose .ln-info-toc-wrap + h2,
.ln-prose .ln-info-cards + h2 {
  margin-top: 2rem;
  padding-top: 0;
  border-top: 0;
}

/* Sprungnavigation der FAQ: dezente Pillen, kein Kartenraster. */
.ln-info-toc-wrap {
  margin-bottom: 1rem;
}

.ln-info-toc {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ln-info-toc a {
  display: inline-block;
  padding: 0.28rem 0.8rem;
  border: 1px solid var(--ln-line);
  border-radius: 999px;
  background-color: var(--ln-card);
  color: var(--ln-primary-deep);
  font-size: 0.85rem;
  text-decoration: none;
}

.ln-info-toc a:hover,
.ln-info-toc a:focus-visible {
  border-color: var(--ln-primary);
  color: var(--ln-primary);
}

/* Adress- und Kontaktkarten des Impressums. */
.ln-info-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.ln-info-card {
  padding: 1.25rem 1.4rem;
  border-radius: var(--ln-radius);
  background-color: var(--ln-card);
  box-shadow: var(--ln-shadow);
}

.ln-prose .ln-info-card h2 {
  margin: 0 0 0.6rem;
  padding-top: 0;
  border-top: 0;
  font-size: 1rem;
}

.ln-info-card p:last-child {
  margin-bottom: 0;
}

/* ---- Seitenkopf ----
   Nur noch der Abstand nach unten; oben liefert #ln-main. */
.ln-page-head {
  margin-bottom: 1.75rem;
}

.ln-page-head h1 {
  margin-bottom: 0.15rem;
}

.ln-page-head p {
  margin-bottom: 0;
  color: var(--ln-ink-muted);
  font-size: 0.9rem;
}

/* ---- Abschnittsmarke ----
   Trägt den Rang, den ein nacktes h2 in einer Kachelwüste verliert:
   kleine Versalien plus auslaufende Linie. Ist selbst die Überschrift,
   steht also nie zusätzlich über einer zweiten. */
.ln-section-label {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  margin: 2.5rem 0 0.9rem;
  font-size: 0.72rem;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ln-ink-muted);
}

.ln-section-label::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--ln-line);
}

:focus-visible {
  outline: 2px solid var(--ln-focus);
  outline-offset: 2px;
}

/* Oberflächen, die der Browser stellt und die sonst aus keinem
   Designsystem stammen: Textmarkierung, Caret und Scrollbalken kommen
   aus denselben Tokens wie alles andere. Bewusst global, nicht je Seite
   — eine nur auf der Startseite grüne Markierung wäre der Bruch. */
::selection {
  background-color: color-mix(in srgb, var(--ln-primary) 22%, transparent);
  color: var(--ln-ink);
}

:root {
  caret-color: var(--ln-primary);
  scrollbar-color: var(--ln-line-strong) var(--ln-surface);
}

/* Zahlen ohne Zappeln bei htmx-Updates (Werte, Tabellen, Tooltips) */
.ln-tabular {
  font-variant-numeric: tabular-nums;
}

/* Icon-Grundmaß für Lucide-Sprite (<svg class="ln-icon"><use …></svg>) */
.ln-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
}

/* Bildmarke in der Nav: etwas größer als ein Fließtext-Icon, weil sie
   neben dem Wortzeichen steht und nicht in einer Textzeile. Farbe kommt
   über currentColor vom .navbar-brand. */
.ln-brand-mark {
  width: 1.35em;
  height: 1.35em;
  vertical-align: -0.2em;
}

/* Initialen-Kreis am Konto-Menü (UI-Foundation §3.1). Trägt keine
   Information, die nicht daneben steht — der Kurzname ist die
   Beschriftung, der Kreis nur der Wiedererkennungsanker; deshalb
   aria-hidden im Partial. */
.ln-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background-color: color-mix(in srgb, var(--ln-primary) 14%, var(--ln-card));
  color: var(--ln-primary);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1;
}

/* Bootstrap kompiliert Fokus-/Check-Farben ins CSS — hier auf Tokens
   zurückholen, damit kein Framework-Blau durchscheint: */
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
  border-color: color-mix(in srgb, var(--ln-primary) 55%, var(--ln-card));
  box-shadow: 0 0 0 0.25rem color-mix(in srgb, var(--ln-primary) 25%, transparent);
}

.form-check-input:checked {
  background-color: var(--ln-primary);
  border-color: var(--ln-primary);
}

/* Fokus-Ringe explizit setzen — Bootstrap gewinnt sonst per Spezifität
   (framework-blauer Ring, auf der dunklen Nav mit nur 1,31:1 Kontrast): */
.btn:focus-visible,
.nav-link:focus-visible,
.form-control:focus-visible,
.form-select:focus-visible,
.form-check-input:focus-visible,
.navbar-toggler:focus-visible {
  outline: 2px solid var(--ln-focus);
  outline-offset: 2px;
  box-shadow: none;
}

.ln-nav .nav-link:focus-visible,
.ln-nav .navbar-toggler:focus-visible {
  outline-color: var(--ln-nav-active);
}

/* Touch-Ziele: auf Touch-Geräten mind. 44px Höhe für Buttons; Desktop
   behält die kompakte Höhe. inline-flex + Zentrierung, damit die
   Mehrhöhe die Beschriftung nicht nach oben drückt (Bootstrap-.btn ist
   inline-block) und Buttons in .btn-group bündig ausgerichtet bleiben
   (.btn-group layoutet ihre Kinder per Flex — inline-flex-Kinder sind
   dort unkritisch, Radius-/Border-Regeln greifen unverändert). */
@media (pointer: coarse) {
  .btn,
  .btn-sm {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
  }

  /* Diese vier fielen durch: sie sind weder .btn noch .ln-footer-links a.
     Impressum und Datenschutz waren damit die kleinsten Tippziele der
     ganzen Seite (14px hoch) — ausgerechnet die zwei Links, die § 5 DDG
     „leicht erkennbar und unmittelbar erreichbar" verlangt. */
  .ln-footer-legal a,
  .ln-home-faq-more a,
  .navbar-toggler {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* Die Adresse in der Kontaktzeile steht mitten im Satz — ein
     44px-Kasten zerrisse den Absatz. Stattdessen wächst die Trefferfläche
     über die Zeilenhöhe, ohne den Textfluss anzufassen. */
  .ln-home-contact-note {
    line-height: 1.9;
  }
}

/* Skip-Link (kombiniert mit Bootstrap .visually-hidden-focusable) */
.ln-skip:focus {
  position: fixed;
  top: 0.5rem;
  left: 0.5rem;
  z-index: 2000;
  background: var(--ln-card);
  color: var(--ln-ink);
  padding: 0.5rem 0.9rem;
  border-radius: var(--ln-radius-sm);
  outline: 2px solid var(--ln-focus);
}

/* ================================================================
 * Navigation (helle Papier-Nav, Uferlicht)
 * ================================================================ */

.ln-nav {
  background-color: var(--ln-nav-bg);
  color: var(--ln-nav-ink);
  /* Helle Nav auf heller Fläche: die Kante trennt, der Schatten hebt.
     Ohne die Linie verschwimmen Nav und Seite ineinander. */
  border-bottom: 1px solid var(--ln-nav-line);
  box-shadow: var(--ln-shadow);
  /* Bootstrap-Navbar-Variablen auf Tokens ziehen. Das Toggler-Icon selbst
     löst base.html via data-bs-theme="light" — kein url()-Icon hier: */
  --bs-navbar-color: var(--ln-nav-ink);
  --bs-navbar-hover-color: var(--ln-nav-active);
  --bs-navbar-active-color: var(--ln-nav-active);
  --bs-navbar-toggler-border-color: color-mix(in srgb, var(--ln-nav-ink) 40%, transparent);
}

.ln-nav .nav-link {
  color: inherit;
  opacity: 0.82;
  border-bottom: 2px solid transparent;
}

.ln-nav .nav-link:hover,
.ln-nav .nav-link:focus-visible {
  color: inherit;
  opacity: 1;
}

.ln-nav .nav-link.active {
  color: var(--ln-nav-active);
  opacity: 1;
  border-bottom-color: var(--ln-nav-active);
}

.ln-nav .navbar-brand {
  color: var(--ln-nav-ink);
  font-weight: 700;
}

/* Dropdown auf der Nav: Bootstrap-Dropdown-Variablen auf die Nav-Tokens
   ziehen — sonst öffnet sich ein Framework-Menü in Fremdfarben. */
.ln-nav .dropdown-menu {
  --bs-dropdown-bg: var(--ln-nav-bg);
  --bs-dropdown-link-color: var(--ln-nav-ink);
  --bs-dropdown-link-hover-color: var(--ln-nav-ink);
  --bs-dropdown-link-hover-bg: color-mix(in srgb, var(--ln-nav-ink) 12%, var(--ln-nav-bg));
  --bs-dropdown-border-color: color-mix(in srgb, var(--ln-nav-ink) 25%, var(--ln-nav-bg));
}

/* ----------------------------------------------------------------
 * Ebene 2 — Bereichsleiste (nav-revision-2026-08-05 §4)
 *
 * Die Ebenentrennung trägt eine FLÄCHE, kein Strich: der Seitengrund
 * statt des Nav-Papiers, dazu kleinere Schrift. Ein bloßer Trenner in
 * einer gemeinsamen Zeile war die verworfene Alternative — schwache
 * Ebenentrennung ist genau der Befund, den diese Leiste behebt.
 * ---------------------------------------------------------------- */

.ln-subnav {
  background-color: var(--ln-bg);
  border-bottom: 1px solid var(--ln-nav-line);
  font-size: 0.9375rem;
}

.ln-subnav-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 1.25rem;
  padding-block: 0.4rem;
}

/* Kontextzeile: nennt den Verein bzw. Verband, in dem die Einträge
   darunter gelten. Kein Link — sie ist die Antwort auf „wo bin ich",
   kein Ziel. Das Ziel heißt „Übersicht" und steht in der Liste. */
.ln-subnav-context {
  margin: 0;
  font-weight: 600;
  color: var(--ln-nav-ink);
  opacity: 0.72;
}

.ln-subnav-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.ln-subnav-link {
  display: inline-block;
  padding-block: 0.2rem;
  color: var(--ln-nav-ink);
  text-decoration: none;
  opacity: 0.82;
  border-bottom: 2px solid transparent;
}

.ln-subnav-link:hover,
.ln-subnav-link:focus-visible {
  color: var(--ln-nav-ink);
  opacity: 1;
}

.ln-subnav-link.active {
  color: var(--ln-nav-active);
  opacity: 1;
  border-bottom-color: var(--ln-nav-active);
}

.ln-subnav-link:focus-visible {
  outline-color: var(--ln-nav-active);
}

/* Schmal: Kontextzeile über die volle Breite, damit die Einträge nicht
   um den Vereinsnamen herumbrechen. */
@media (max-width: 575.98px) {
  .ln-subnav-context {
    flex-basis: 100%;
  }
}

/* ================================================================
 * Kacheln & KPIs
 * ================================================================ */

.ln-card {
  background-color: var(--ln-card);
  border: 1px solid var(--ln-line);
  border-radius: var(--ln-radius);
  box-shadow: var(--ln-shadow);
  padding: 1.1rem 1.2rem;
}

/* KPI-Reihe: mitwachsendes Raster statt Bootstrap-Dritteln. Drei Zahlen
   über die volle Zeilenbreite lassen die Kacheln leer wirken — hier
   bleiben sie so schmal, wie ihr Inhalt es rechtfertigt. */
.ln-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.9rem;
  max-width: 34rem;
}

/* .ln-kpi-*-Kacheln: Kachelrahmen kommt via .ln-card im Template */
.ln-kpi-num {
  font-size: var(--ln-size-kpi);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--ln-ink);
}

.ln-kpi-label {
  font-size: 0.78rem;
  color: var(--ln-ink-muted);
}

.ln-kpi--warn .ln-kpi-num {
  color: var(--ln-warn);
}

/* ---- Stations-Kachel ---- */

.ln-station {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  height: 100%;
}

/* Messwerte im festen Zweispalter: nebeneinander gestellte Zahlen sind
   zwischen Kacheln vergleichbar, ein Flex-Wrap ist es nicht. */
.ln-readings {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.35rem 1rem;
}

.ln-reading-val {
  font-size: var(--ln-size-metric);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  line-height: 1.2;
  color: var(--ln-ink);
}

.ln-reading-val .unit {
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--ln-ink-muted);
  margin-left: 0.15em;
}

.ln-reading-meta {
  font-size: 0.8rem;
  color: var(--ln-ink-muted);
}

/* ================================================================
 * Tabellen
 * Listenansichten sind Datenoberflächen, keine Linksammlungen: die
 * Zeile ist das Objekt, der Name ihr Titel — nicht ein blauer Link in
 * einer sonst leeren Fläche.
 * ================================================================ */

/* Rahmen für freistehende Tabellen (Listenseiten). NICHT verwenden,
   wenn die Tabelle schon in einer .ln-card sitzt — verschachtelte
   Karten sind immer falsch. overflow-x hält schmale Viewports am
   Scrollen, der Radius klippt Kopfzeile und letzte Zeile sauber. */
.ln-table-card {
  background-color: var(--ln-card);
  border: 1px solid var(--ln-line);
  border-radius: var(--ln-radius);
  box-shadow: var(--ln-shadow);
  overflow-x: auto;
  /* Explizit, nicht kosmetisch: sobald eine Achse nicht `visible` ist,
     rechnet CSS die andere auf `auto` — die visually-hidden-Caption
     ragt 1 px heraus und erzeugte damit einen Phantom-Scrollbalken. */
  overflow-y: hidden;
}

.ln-table {
  /* Bootstrap-Tabellenvariablen auf Tokens ziehen; ohne das kommen
     Streifen und Hover in Framework-Grau. */
  --bs-table-bg: transparent;
  --bs-table-color: var(--ln-ink);
  --bs-table-border-color: var(--ln-line);
  --bs-table-striped-bg: color-mix(in srgb, var(--ln-primary) 6%, var(--ln-card));
  --bs-table-striped-color: var(--ln-ink);
  --bs-table-hover-bg: color-mix(in srgb, var(--ln-primary) 12%, var(--ln-card));
  --bs-table-hover-color: var(--ln-ink);
  margin-bottom: 0;
}

/* Spaltenköpfe im Duktus der Abschnittsmarke — dieselbe Tonlage für
   dieselbe Aufgabe (etwas benennen, ohne es zu betonen). */
.ln-table > thead > tr > th {
  padding: 0.65rem 0.9rem;
  background-color: color-mix(in srgb, var(--ln-ink) 5%, var(--ln-card));
  border-bottom: 1px solid var(--ln-line);
  font-size: 0.72rem;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ln-ink-muted);
  white-space: nowrap;
}

.ln-table > tbody > tr > td {
  padding: 0.7rem 0.9rem;
  border-color: var(--ln-line);
  vertical-align: middle;
}

.ln-station-map-link {
  width: fit-content;
  color: var(--ln-ink);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentcolor 48%, transparent);
  text-underline-offset: 0.18em;
}

.ln-station-map-link:hover,
.ln-station-map-link:focus-visible {
  color: var(--ln-primary);
  text-decoration-color: currentcolor;
}

/* Die letzte Zeile braucht keinen Strich — den Abschluss macht der
   Kartenrand. */
.ln-table > tbody > tr:last-child > td {
  border-bottom-width: 0;
}

/* Erste Spalte trägt den Objektnamen: als Text gesetzt, nicht als Link
   lackiert. Die Unterstreichung kommt bei Hover und Fokus zurück,
   damit die Klickbarkeit nicht allein an der Farbe hängt. */
.ln-table-link {
  color: var(--ln-ink);
  font-weight: 600;
  text-decoration: none;
}

.ln-table-link:hover,
.ln-table-link:focus-visible {
  color: var(--ln-primary);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* Zeilensymbol: greift die Ikonografie der Empty-States auf (map-pin
   für Stationen, gauge für Geräte), damit Liste und Leerzustand
   dieselbe Sprache sprechen. */
.ln-table-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background-color: color-mix(in srgb, var(--ln-primary) 10%, var(--ln-card));
  color: var(--ln-primary);
}

.ln-table-icon .ln-icon {
  width: 0.95rem;
  height: 0.95rem;
  vertical-align: 0;
}

/* Name plus Symbol als eine Einheit */
.ln-table-name {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* Sekundärangaben in der Zeile (Gewässer, Station, Zeitstempel) */
.ln-table-muted {
  color: var(--ln-ink-muted);
}

/* ================================================================
 * Badges (Sichtbarkeit + Gerätezustand)
 * Rezept: Text = Vollton, Hintergrund 10 % Ton auf Card, Rahmen
 * 28–30 % Ton auf Card — Mischung gegen var(--ln-card) statt
 * transparent, damit das Rezept unterlagenunabhängig AA hält.
 * Sichtbarkeits-Badges nutzen die --ln-vis-*-Tokens (Ticket §2.2).
 * ================================================================ */

.ln-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  padding: 0.25em 0.7em;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 0.8125rem; /* Zustandstext darf nicht kleinste Schrift der Seite sein */
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
}

.ln-badge::before {
  content: "";
  flex: none;
  width: 0.5em;
  height: 0.5em;
  border-radius: 50%;
  background-color: currentColor;
}

.ln-badge--vis-private {
  color: var(--ln-vis-private);
  background-color: color-mix(in srgb, var(--ln-vis-private) 10%, var(--ln-card));
  border-color: color-mix(in srgb, var(--ln-vis-private) 30%, var(--ln-card));
}

.ln-badge--vis-aggregate {
  color: var(--ln-vis-aggregate);
  background-color: color-mix(in srgb, var(--ln-vis-aggregate) 10%, var(--ln-card));
  border-color: color-mix(in srgb, var(--ln-vis-aggregate) 28%, var(--ln-card));
}

.ln-badge--vis-public {
  color: var(--ln-vis-public);
  background-color: color-mix(in srgb, var(--ln-vis-public) 10%, var(--ln-card));
  border-color: color-mix(in srgb, var(--ln-vis-public) 28%, var(--ln-card));
}

.ln-badge--low {
  color: var(--ln-warn);
  background-color: color-mix(in srgb, var(--ln-warn) 10%, var(--ln-card));
  border-color: color-mix(in srgb, var(--ln-warn) 28%, var(--ln-card));
}

.ln-badge--ok {
  color: var(--ln-ok);
  background-color: color-mix(in srgb, var(--ln-ok) 10%, var(--ln-card));
  border-color: color-mix(in srgb, var(--ln-ok) 28%, var(--ln-card));
}

.ln-badge--stale {
  color: var(--ln-danger);
  background-color: color-mix(in srgb, var(--ln-danger) 10%, var(--ln-card));
  border-color: color-mix(in srgb, var(--ln-danger) 28%, var(--ln-card));
}

/* Lageunschärfe ist kein Schaden, sondern die Freigabe des Vereins
   (AD-10) — und die Karte zeichnet dieselbe Sache schon in diesem Ton.
   Als --ln-badge--stale stand sie in Alarmrot neben Koordinaten, die
   genau so gemeint waren, wie sie dastehen. */
.ln-badge--approximate {
  color: var(--ln-map-approximate);
  background-color: color-mix(
    in srgb,
    var(--ln-map-approximate) 10%,
    var(--ln-card)
  );
  border-color: color-mix(
    in srgb,
    var(--ln-map-approximate) 28%,
    var(--ln-card)
  );
}

/* ================================================================
 * Hinweis-/Warn-/Fehlerboxen
 * ================================================================ */

.ln-alertline {
  padding: 0.6rem 0.9rem;
  border: 1px solid color-mix(in srgb, var(--ln-warn) 30%, var(--ln-card));
  border-radius: var(--ln-radius-sm);
  background-color: color-mix(in srgb, var(--ln-warn) 9%, var(--ln-card));
  color: var(--ln-ink);
}

.ln-hintbox {
  padding: 0.75rem 1rem;
  border: 1px solid color-mix(in srgb, var(--ln-primary) 30%, var(--ln-card));
  border-radius: var(--ln-radius-sm);
  background-color: color-mix(in srgb, var(--ln-primary) 8%, var(--ln-card));
  color: var(--ln-ink);
}

.ln-dangerbox {
  padding: 0.75rem 1rem;
  border: 1px solid color-mix(in srgb, var(--ln-danger) 35%, var(--ln-card));
  border-radius: var(--ln-radius-sm);
  background-color: color-mix(in srgb, var(--ln-danger) 9%, var(--ln-card));
  color: var(--ln-ink);
  font-weight: 600;
}

/* ================================================================
 * Formulare
 * ================================================================ */

.ln-form-card {
  max-width: 640px;
  background-color: var(--ln-card);
  border: 1px solid var(--ln-line);
  border-radius: var(--ln-radius);
  box-shadow: var(--ln-shadow);
  padding: 1.5rem;
}

/* Radio-/Checkbox-Option als Rahmen-Kachel — Label-als-Wrapper-Muster:
   label.ln-radio-opt umschließt den Input (Klickfläche = ganze Kachel) */
label.ln-radio-opt {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--ln-line-strong);
  border-radius: var(--ln-radius-sm);
  background-color: var(--ln-card);
  cursor: pointer;
}

.ln-radio-opt input[type="radio"],
.ln-radio-opt input[type="checkbox"] {
  accent-color: var(--ln-primary);
  margin-top: 0.2em;
}

.ln-radio-opt:has(input:checked) {
  border-color: var(--ln-primary);
  background-color: color-mix(in srgb, var(--ln-primary) 5%, var(--ln-card));
}

/* Bedienelement-Rahmen: 3:1 gegen Card (WCAG 1.4.11) —
   --ln-line bleibt reinen Trennern vorbehalten */
.form-control,
.form-select,
.form-check-input,
.ln-radio-opt {
  border-color: var(--ln-line-strong);
}

/* ================================================================
 * Modal-Feinschliff
 * ================================================================ */

.ln-modal .modal-content {
  border-radius: var(--ln-radius);
  box-shadow: var(--ln-shadow-modal);
  border-color: var(--ln-line);
}

/* ================================================================
 * Toasts
 * ================================================================ */

#ln-toast-container {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 1100;
  display: grid;
  gap: 0.5rem;
  max-width: min(22rem, calc(100vw - 2rem));
  pointer-events: none;
}

/* Weiße Karte, Severity als farbiger Rahmen — nicht als Grundfarbe:
 * der Inhalt bleibt immer gleich lesbar, die Dringlichkeit trägt der
 * Rand (plus role=alert/status für Screenreader). */
.ln-toast {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  background-color: #fff;
  color: var(--ln-ink);
  border: 2px solid var(--ln-line);
  border-radius: var(--ln-radius-sm);
  box-shadow: var(--ln-shadow-modal);
  padding: 0.7rem 1rem;
  font-size: 0.9rem;
  pointer-events: auto;
  /* Abgang: kurzes Ausfaden mit leichtem Absacken via .ln-toast--leaving
   * (200 ms — muss zum remove()-Timeout in project.js passen). */
  transition:
    opacity 0.2s ease-in,
    transform 0.2s ease-in;
}

.ln-toast--success {
  border-color: var(--ln-ok);
}

.ln-toast--info {
  border-color: var(--ln-primary);
}

.ln-toast--warning {
  border-color: var(--ln-warn);
}

.ln-toast--error {
  border-color: var(--ln-danger);
}

.ln-toast--leaving {
  opacity: 0;
  transform: translateY(0.3rem);
}

/* ================================================================
 * Credential-Block (einmalig sichtbare Keys/Token)
 * ================================================================ */

.ln-credential code {
  display: block;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  font-size: 0.9em;
  color: var(--ln-ink);
  background-color: var(--ln-surface);
  border: 1px solid var(--ln-line);
  border-radius: var(--ln-radius-sm);
  padding: 0.6rem 0.8rem;
  overflow-x: auto;
  white-space: nowrap;
}

/* ================================================================
 * Payload-Abzug (letzter Uplink eines Geräts)
 * ================================================================ */

/* Umbruch statt Abschneiden: ein Payload ist zum Lesen da, und ein
   waagerechter Scrollbalken versteckt genau die Felder, wegen derer die
   Seite geöffnet wurde. Die Höhe ist gedeckelt, damit ein
   Dragino-Uplink mit gepufferten Vorgängersamples die Zuordnung darunter
   nicht aus dem Bild schiebt. */
.ln-payload {
  margin: 0;
  max-height: 22rem;
  overflow-y: auto;
  padding: 0.8rem 1rem;
  background-color: var(--ln-surface);
  border: 1px solid var(--ln-line);
  border-radius: var(--ln-radius-sm);
}

.ln-payload code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  font-size: 0.85rem;
  color: var(--ln-ink);
  white-space: pre-wrap;
  word-break: break-word;
}

/* ================================================================
 * Empty-State
 * ================================================================ */

.ln-empty {
  text-align: center;
  color: var(--ln-ink-muted);
  padding: 2.5rem 1.5rem;
  border: 1px dashed var(--ln-line);
  border-radius: var(--ln-radius);
}

/* ================================================================
 * htmx: globale Progressbar (Ticket §4.1, htmx-indicator-Konvention)
 * ================================================================ */

#ln-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background-color: var(--ln-primary);
  transform: scaleX(0);
  transform-origin: 0 50%;
  opacity: 0;
  transition: opacity 0.2s ease;
  z-index: 1090;
  pointer-events: none;
}

.htmx-request #ln-progress,
#ln-progress.htmx-request {
  opacity: 1;
  animation: ln-progress-slide 1.2s ease-in-out infinite;
}

@keyframes ln-progress-slide {
  0%   { transform: scaleX(0); transform-origin: 0 50%; }
  55%  { transform: scaleX(1); transform-origin: 0 50%; }
  56%  { transform: scaleX(1); transform-origin: 100% 50%; }
  100% { transform: scaleX(0); transform-origin: 100% 50%; }
}

/* Zustandsgebundener Bruder der htmx-Progressbar: läuft, solange das
 * Fragment ihn rendert (Bericht offen) — unabhängig von Request-Dauer,
 * daher auch auf schnellen Servern sichtbar. Sitzt per fixed an der
 * Viewport-Oberkante, deckungsgleich mit #ln-progress: nach außen ist es
 * derselbe Balken, nur mit anderem Auslöser. */
.ln-busy-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  /* Kometenschweif: tiefes Uferlicht-Grün läuft nach rechts in den
   * Sand-Hintergrund aus — der Balken verblasst statt hart zu enden. */
  background-image: linear-gradient(
    90deg,
    var(--ln-primary-deep),
    var(--ln-primary) 55%,
    var(--ln-surface)
  );
  transform-origin: 0 50%;
  animation: ln-progress-slide 1.2s ease-in-out infinite;
  z-index: 1090;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .htmx-request #ln-progress,
  #ln-progress.htmx-request {
    animation: none;
    transform: scaleX(1); /* statischer Balken statt Animation */
  }
}

/* ================================================================
 * Sparkline-Container (Mini-Charts in Kacheln)
 * ================================================================ */

/* Wrapper-Divs um die Canvas-Elemente: Chart.js (responsive) misst den
   Container und überschreibt CSS-Maße des Canvas selbst — die Höhe muss
   deshalb am Wrapper hängen, nie am <canvas>. */
.ln-spark {
  position: relative;
  width: 100%;
  height: 64px;
}

.ln-chart-canvas {
  position: relative;
  width: 100%;
  height: 280px;
}

/* Fehlermeldung aus ln.mountCharts (project.js): wird hinter dem
   Canvas-Wrapper eingefügt, wenn die Chart-Daten nicht ladbar sind.
   Hinweis-Optik im Warn-Ton analog .ln-alertline, nur kompakter —
   eine Fehlermeldung muss als solche erkennbar sein. */
.ln-chart-error {
  margin: 0.35rem 0 0;
  padding: 0.35rem 0.6rem;
  border: 1px solid color-mix(in srgb, var(--ln-warn) 30%, var(--ln-card));
  border-radius: var(--ln-radius-sm);
  background-color: color-mix(in srgb, var(--ln-warn) 9%, var(--ln-card));
  font-size: 0.85rem;
  color: var(--ln-ink);
}

/* ================================================================
 * CAP-4: Öffentliche Netzwerkkarte
 * ================================================================ */

.ln-map-workspace {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(30rem, 0.95fr);
  gap: 1.25rem;
  align-items: start;
  margin-top: 1.5rem;
}

.ln-map-panel {
  position: sticky;
  top: 1rem;
  min-width: 0;
}

.ln-station-list-panel {
  min-width: 0;
}

.ln-station-list-panel > h2 {
  margin-bottom: 0.8rem;
}

/* Filterleiste (Entwurf „Runde 4"): eine Zeile aus Suche und
   Mehrfachauswahl-Menüs, darunter die gewählten Filter als entfernbare
   Tags. Die Leiste wächst nicht mehr mit der Zahl der Messgrößen — das
   Menü tut es, und zwar nach unten über die Liste. */
.ln-station-filters {
  /* Eine Höhe für alle Controls der Leiste — der Befund „jeder Button
     anders groß" entstand aus btn-sm neben Bootstrap-Inputs neben
     nackten Checkbox-Labels. */
  --ln-control-height: 2.375rem;

  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin-bottom: 0.75rem;
  padding: 0.9rem;
  border: 1px solid var(--ln-line);
  border-radius: var(--ln-radius);
  background-color: var(--ln-card);
}

.ln-station-filters[hidden] {
  display: none;
}

.ln-station-filter-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* Die Suche nimmt den Rest der Zeile und bricht als Erste um: die zwei
   Menüknöpfe sind textbreit, das Eingabefeld ist es nie. */
.ln-station-filter-search {
  flex: 1 1 11rem;
  min-width: 0;
}

.ln-station-filters .form-control {
  min-height: var(--ln-control-height);
}

.ln-filter-menu {
  position: relative;
}

.ln-filter-menu-button {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  min-height: var(--ln-control-height);
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--ln-line-strong);
  border-radius: var(--ln-radius-sm);
  background-color: var(--ln-card);
  color: var(--ln-ink);
  font-size: 0.85rem;
  white-space: nowrap;
  cursor: pointer;
}

.ln-filter-menu-button:hover {
  border-color: var(--ln-primary);
  color: var(--ln-primary);
}

/* Gesetzter Filter: Rahmen, getönte Fläche UND — bei den Messgrößen —
   der Zähler. Die Farbe allein trüge den Zustand nicht. */
.ln-filter-menu-button[data-ln-filter-active="true"] {
  border-color: var(--ln-primary);
  background-color: color-mix(in srgb, var(--ln-primary) 8%, var(--ln-card));
  color: var(--ln-primary-deep);
  font-weight: 600;
}

.ln-filter-menu-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.15rem;
  height: 1.15rem;
  padding: 0 0.25rem;
  border-radius: 999px;
  background-color: var(--ln-primary);
  color: var(--ln-on-dark);
  font-size: 0.72rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.ln-filter-menu-count[hidden] {
  display: none;
}

.ln-filter-menu-chevron {
  flex: none;
  width: 0.8rem;
  height: 0.8rem;
  vertical-align: 0;
}

/* Das Menü legt sich über die Liste, statt sie nach unten zu schieben —
   sonst spränge bei jedem Öffnen die halbe Spalte. */
.ln-filter-menu-panel {
  position: absolute;
  z-index: 5;
  top: calc(100% + 0.35rem);
  right: 0;
  min-width: 12.5rem;
  padding: 0.5rem;
  border: 1px solid var(--ln-nav-line);
  border-radius: var(--ln-radius);
  background-color: var(--ln-card);
  box-shadow: var(--ln-shadow-raised);
}

.ln-filter-menu-panel[hidden] {
  display: none;
}

.ln-filter-menu-panel fieldset {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  margin: 0;
  padding: 0;
  border: 0;
}

.ln-filter-option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  padding: 0.3rem 0.5rem;
  border-radius: var(--ln-radius-sm);
  color: var(--ln-ink);
  font-size: 0.85rem;
  cursor: pointer;
}

.ln-filter-option:hover {
  background-color: color-mix(in srgb, var(--ln-primary) 6%, var(--ln-card));
}

.ln-filter-option:has(input:checked) {
  background-color: color-mix(in srgb, var(--ln-primary) 8%, var(--ln-card));
}

/* Echtes Kästchen statt nachgebautem: accent-color färbt es im Ton des
   Systems, Fokusring und Tastaturbedienung bleiben die des Browsers. */
.ln-filter-option input {
  flex: none;
  width: 0.95rem;
  height: 0.95rem;
  margin: 0;
  accent-color: var(--ln-primary);
}

.ln-station-filter-tags {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.ln-station-filter-tags[hidden] {
  display: none;
}

.ln-filter-tag-list {
  display: contents;
}

/* Gleiche Tönung wie die Wert-Pills der Karten: was aus demselben
   Primärton kommt, gehört sichtbar zusammen. */
.ln-filter-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.1rem 0.2rem 0.1rem 0.6rem;
  border: 1px solid color-mix(in srgb, var(--ln-primary) 28%, var(--ln-card));
  border-radius: 999px;
  background-color: color-mix(in srgb, var(--ln-primary) 7%, var(--ln-card));
  color: var(--ln-primary-deep);
  font-size: 0.78rem;
  font-weight: 600;
}

.ln-filter-tag-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.35rem;
  height: 1.35rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background-color: transparent;
  color: var(--ln-primary);
  cursor: pointer;
}

.ln-filter-tag-remove:hover {
  background-color: color-mix(in srgb, var(--ln-primary) 16%, var(--ln-card));
}

.ln-filter-tag-remove .ln-icon {
  width: 0.7rem;
  height: 0.7rem;
  vertical-align: 0;
}

.ln-filter-clear {
  padding: 0;
  border: 0;
  background-color: transparent;
  color: var(--ln-primary);
  font-size: 0.78rem;
  text-decoration: underline;
  cursor: pointer;
}

.ln-filter-clear:hover {
  color: var(--ln-primary-deep);
}

.ln-station-results {
  margin: 0 0 0.55rem;
  color: var(--ln-ink-muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

/* Vertikaler Scrollcontainer der Split-Ansicht. Horizontal gibt es
   nichts zu scrollen: clip statt auto, damit der einstige
   Tabellen-Scroller nicht durch die Hintertür zurückkommt — die
   Karten umbrechen stattdessen (overflow-x: visible würde neben
   overflow-y: auto ohnehin zu auto berechnet). */
.ln-station-list-scroll {
  max-height: min(68vh, 46rem);
  overflow-x: clip;
  overflow-y: auto;
}

/* Stationsliste als Karten statt Tabelle: drei Zeilen pro Station —
   Name + Gewässer, Lage, Wert-Pills. Bricht auf jeder Breite um,
   statt horizontal zu scrollen. */
.ln-station-cards {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ln-station-card {
  /* flex: none, weil die Liste ein höhenbegrenzter Scroller ist: als
     schrumpffähige Flex-Kinder geben die Karten nach, sobald irgendetwas
     ihre automatische Mindesthöhe aufhebt, und schneiden dann ihre
     eigenen Messwerte ab. Der Scroller soll scrollen, nicht quetschen. */
  flex: none;
  min-width: 0;
  padding: 0.85rem 1rem;
  border: 1px solid var(--ln-line);
  border-radius: var(--ln-radius);
  background-color: var(--ln-card);
  box-shadow: var(--ln-shadow);
}

/* Bewusste Karte-/Listen-Kopplung (Zustand aus Zeigen, Fokus oder
   Kartenauswahl — map.js führt die drei zu einer Klasse zusammen).

   Fassung nach Entwurf 2a: eine 4px-Fahne an der linken Kante, die
   linken Ecken dafür eckig. Die Vorgängerfassung tönte die Fläche und
   setzte zwei Inset-Kanten oben und unten — die tiefere Fläche ließ die
   Karte beim Überfahren „einsinken", und die zwei Kanten zerschnitten
   sie waagerecht mitten im Messwert-Raster.

   Nicht allein Farbe: Kantenfahne und die eckigen Ecken tragen den
   Zustand auch dann, wenn der Farbunterschied nicht ankommt. */
.ln-station-card--highlighted {
  position: relative;
  border-color: color-mix(in srgb, var(--ln-primary) 42%, var(--ln-card));
  border-radius: 0 var(--ln-radius) var(--ln-radius) 0;
}

/* Negative Einzüge: absolute Kinder messen am Innenrand, die Fahne soll
   aber bis an die Außenkante laufen und den Rahmen dort ersetzen. Ohne
   Clipping — die linken Ecken sind hier eckig, es gibt nichts zu
   beschneiden. „overflow: hidden" wäre hier sogar schädlich: es hebt die
   automatische Mindesthöhe des Flex-Kindes auf. */
.ln-station-card--highlighted::before {
  position: absolute;
  top: -1px;
  bottom: -1px;
  left: -1px;
  width: 4px;
  background-color: var(--ln-series-level);
  content: "";
}

.ln-station-card-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.15rem 0.75rem;
}

.ln-station-card-title {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.3;
}

.ln-station-card-water {
  color: var(--ln-ink-muted);
}

.ln-station-card-location {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.25rem 0.5rem;
  margin: 0.35rem 0 0;
  color: var(--ln-ink-muted);
  font-size: 0.85rem;
}

.ln-station-card-empty {
  margin: 0.5rem 0 0;
  color: var(--ln-ink-muted);
}

/* Messwert-Raster: eine Spalte je Messgröße, farbige Kante links, die
   Zahl groß. Die Kantenfarbe ist die Serienfarbe der Charts — dieselbe
   Größe trägt auf Liste, Karte und Ganglinie denselben Ton. Sie ist
   Wiedererkennung, nicht Aussage: die Messgröße steht als Wort darüber.

   Das Alter hängt am einzelnen Messwert — Frische ist eine Eigenschaft
   des Readings, nicht der Station. */
.ln-station-metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.75rem 1rem;
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
}

.ln-metric {
  --ln-metric-tone: var(--ln-line-strong);

  min-width: 0;
  padding-left: 0.75rem;
  border-left: 3px solid var(--ln-metric-tone);
}

.ln-metric[data-parameter="water_level"] {
  --ln-metric-tone: var(--ln-series-level);
}

.ln-metric[data-parameter="water_temperature"] {
  --ln-metric-tone: var(--ln-series-temp);
}

.ln-metric[data-parameter="dissolved_oxygen"] {
  --ln-metric-tone: var(--ln-series-oxygen);
}

.ln-metric-label {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.72rem;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ln-ink-muted);
}

.ln-metric-icon {
  flex: none;
  width: 0.8rem;
  height: 0.8rem;
  vertical-align: 0;
  color: var(--ln-metric-tone);
}

.ln-metric-value {
  display: block;
  margin-top: 0.1rem;
  color: var(--ln-ink);
  font-size: var(--ln-size-metric);
  font-weight: 650;
  line-height: 1.15;
  letter-spacing: -0.01em;
}

.ln-metric-unit {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ln-ink-muted);
}

.ln-metric-age {
  display: block;
  color: var(--ln-ink-muted);
  font-size: 0.78rem;
}

/* MapLibre misst den Container und zeichnet sein Canvas hinein — die
   Höhe muss deshalb hier hängen, wie bei den Chart-Wrappern oben.
   Die Grundfarbe entspricht dem Hintergrund-Style aus map.js, damit
   der unkonfigurierte Zustand nicht als Loch wirkt. */
.ln-map {
  width: 100%;
  height: 60vh;
  min-height: 320px;
  border: 1px solid var(--ln-line);
  border-radius: var(--ln-radius);
  background-color: var(--ln-surface);
}

.ln-map-notice {
  margin-top: 0.75rem;
}

.ln-map-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  margin-top: 0.75rem;
  font-size: 0.9rem;
  color: var(--ln-ink);
}

.ln-legend-item {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  max-width: 42rem;
}

/* Exakte Station: Papierfläche + warmer Rand statt blauem Punkt auf
   blauem Wasser. Das lokale RadioTower-Icon benennt den Ort als aktive
   Messstation; der Zeiger hält die veröffentlichte Koordinate eindeutig. */
.ln-map-station-marker {
  position: relative;
  width: 36px;
  height: 43px;
  pointer-events: none;
  filter: drop-shadow(
    0 3px 4px color-mix(in srgb, var(--ln-primary-deep) 32%, transparent)
  );
  transition: filter 160ms ease;
}

.ln-map-station-marker.ln-map-station-marker--highlighted {
  z-index: 2;
  filter: drop-shadow(
    0 4px 7px color-mix(in srgb, var(--ln-primary-deep) 48%, transparent)
  );
}

.ln-map-station-marker.ln-map-station-marker--highlighted
  .ln-map-station-marker__badge {
  transform: scale(1.22);
}

.ln-map-station-marker::after {
  position: absolute;
  bottom: 0;
  left: 12px;
  width: 0;
  height: 0;
  border-right: 6px solid transparent;
  border-left: 6px solid transparent;
  border-top: 9px solid var(--ln-series-temp);
  content: "";
}

.ln-map-station-marker__badge {
  position: relative;
  z-index: 1;
  display: grid;
  box-sizing: border-box;
  width: 36px;
  height: 36px;
  place-items: center;
  border: 3px solid var(--ln-series-temp);
  border-radius: 50%;
  background-color: var(--ln-card);
  color: var(--ln-primary-deep);
  transition: transform 160ms ease;
}

.ln-map-station-marker__badge .ln-icon {
  width: 18px;
  height: 18px;
  stroke-width: 2.25;
}

/* Ungefähre Lage: dieselbe Station, deshalb dasselbe dunkelgrüne Icon.
   Die pfeillose Form und die gestrichelte Ocker-Kontur tragen die
   Präzisionsaussage auch ohne Farbe. */
.ln-map-station-marker--approximate {
  width: 34px;
  height: 34px;
  filter: drop-shadow(
    0 2px 3px color-mix(in srgb, var(--ln-primary-deep) 24%, transparent)
  );
}

.ln-map-station-marker--approximate::after {
  content: none;
}

.ln-map-station-marker--approximate .ln-map-station-marker__badge {
  width: 34px;
  height: 34px;
  border-color: var(--ln-map-approximate);
  border-style: dashed;
  background-color: color-mix(
    in srgb,
    var(--ln-map-approximate) 10%,
    var(--ln-card)
  );
}

/* Die beiden Zeichen unterscheiden sich in Form UND Größe, nicht nur in
   der Farbe (WCAG 1.4.1): Funk-Pin = genaue Lage, größerer offener Ring =
   Fläche, in der die Station irgendwo liegt. */
.ln-legend-dot {
  flex: none;
  align-self: center;
  border-radius: 50%;
}

.ln-legend-dot--exact {
  position: relative;
  display: grid;
  box-sizing: border-box;
  width: 22px;
  height: 22px;
  place-items: center;
  border: 2px solid var(--ln-series-temp);
  background-color: var(--ln-card);
  color: var(--ln-primary-deep);
  filter: drop-shadow(
    0 1px 2px color-mix(in srgb, var(--ln-primary-deep) 24%, transparent)
  );
}

.ln-legend-dot--exact::after {
  position: absolute;
  right: 5px;
  bottom: -6px;
  width: 0;
  height: 0;
  border-right: 4px solid transparent;
  border-left: 4px solid transparent;
  border-top: 6px solid var(--ln-series-temp);
  content: "";
}

.ln-legend-dot--exact .ln-icon {
  width: 12px;
  height: 12px;
  stroke-width: 2.25;
}

.ln-legend-dot--approximate {
  display: grid;
  box-sizing: border-box;
  width: 22px;
  height: 22px;
  place-items: center;
  border: 2px dashed var(--ln-map-approximate);
  background-color: color-mix(
    in srgb,
    var(--ln-map-approximate) 10%,
    var(--ln-card)
  );
  color: var(--ln-primary-deep);
  filter: drop-shadow(
    0 1px 2px color-mix(in srgb, var(--ln-primary-deep) 20%, transparent)
  );
}

.ln-legend-dot--approximate .ln-icon {
  width: 12px;
  height: 12px;
  stroke-width: 2.25;
}

/* Messwertliste im Popup (map.js baut ihn als DOM). Die Stationsliste
   setzt Werte als .ln-station-metrics, nicht als diese Liste. */
.ln-value-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Popup-Inhalt (map.js baut ihn als DOM). MapLibres eigenes CSS setzt
   einen weißen Grund; hier auf die Papier-Kachel der Farbwelt ziehen. */
.maplibregl-popup {
  max-width: min(320px, calc(100vw - 2rem)) !important;
}

.maplibregl-popup-content {
  overflow: hidden;
  padding: 0;
  border-radius: var(--ln-radius);
  background-color: var(--ln-card);
  color: var(--ln-ink);
  box-shadow: var(--ln-shadow-raised);
}

.ln-map-popup {
  max-height: min(60vh, 30rem);
  overflow-y: auto;
}

.maplibregl-popup-close-button {
  top: 0.6rem;
  right: 0.6rem;
  z-index: 2;
  display: grid;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  place-items: center;
  border: 1px solid var(--ln-line);
  border-radius: 50%;
  background-color: color-mix(in srgb, var(--ln-card) 92%, transparent);
  color: var(--ln-ink-muted);
  font-size: 1.15rem;
  line-height: 1;
  transition:
    border-color 120ms ease,
    background-color 120ms ease,
    color 120ms ease;
}

.maplibregl-popup-close-button:hover {
  border-color: var(--ln-line-strong);
  background-color: var(--ln-surface);
  color: var(--ln-ink);
}

.maplibregl-popup-close-button:focus-visible {
  outline: 3px solid color-mix(in srgb, var(--ln-focus) 34%, transparent);
  outline-offset: 2px;
}

.maplibregl-popup-anchor-top .maplibregl-popup-tip {
  border-bottom-color: var(--ln-card);
}

.maplibregl-popup-anchor-bottom .maplibregl-popup-tip {
  border-top-color: var(--ln-card);
}

.maplibregl-popup-anchor-left .maplibregl-popup-tip {
  border-right-color: var(--ln-card);
}

.maplibregl-popup-anchor-right .maplibregl-popup-tip {
  border-left-color: var(--ln-card);
}

.ln-map-popup-station {
  padding: 1rem;
}

.ln-map-popup-header {
  display: grid;
  grid-template-columns: 2.25rem minmax(0, 1fr);
  gap: 0.65rem;
  align-items: start;
  padding-right: 2rem;
}

.ln-map-popup-icon {
  display: grid;
  box-sizing: border-box;
  width: 2.25rem;
  height: 2.25rem;
  place-items: center;
  border: 2px solid var(--ln-series-temp);
  border-radius: 50%;
  background-color: var(--ln-card);
  color: var(--ln-primary-deep);
}

.ln-map-popup-icon--approximate {
  border-color: var(--ln-map-approximate);
  border-style: dashed;
  background-color: color-mix(
    in srgb,
    var(--ln-map-approximate) 10%,
    var(--ln-card)
  );
}

.ln-map-popup-icon .ln-icon {
  width: 1.1rem;
  height: 1.1rem;
  stroke-width: 2.25;
}

.ln-map-popup-title {
  min-width: 0;
}

.ln-map-popup-title h3 {
  overflow-wrap: anywhere;
  margin: 0 0 0.1rem;
  font-size: 1rem;
  line-height: 1.25;
}

.ln-map-popup-water {
  overflow-wrap: anywhere;
  margin: 0 0 0.35rem;
  color: var(--ln-ink-muted);
  font-size: 0.875rem;
}

.ln-map-popup-precision {
  display: inline-flex;
  padding: 0.12rem 0.42rem;
  border-radius: 999px;
  background-color: color-mix(in srgb, var(--ln-series-temp) 9%, var(--ln-card));
  color: var(--ln-ink-muted);
  font-size: 0.72rem;
  font-weight: 650;
  line-height: 1.35;
}

.ln-map-popup-precision--approximate {
  background-color: color-mix(
    in srgb,
    var(--ln-map-approximate) 13%,
    var(--ln-card)
  );
  color: var(--ln-map-approximate);
}

.ln-map-popup-readings {
  margin-top: 0.85rem;
  border-top: 1px solid var(--ln-line);
}

.ln-map-popup-reading {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.2rem 0.75rem;
  align-items: baseline;
  padding: 0.65rem 0;
  border-bottom: 1px solid var(--ln-line);
}

.ln-map-popup-reading-label {
  min-width: 0;
  overflow-wrap: anywhere;
  color: var(--ln-ink-muted);
  font-size: 0.78rem;
  font-weight: 600;
}

.ln-map-popup-reading-number {
  min-width: 0;
  overflow-wrap: anywhere;
  text-align: right;
}

.ln-map-popup-reading-value {
  color: var(--ln-ink);
  font-size: 1.2rem;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.ln-map-popup-reading-unit {
  margin-left: 0.22rem;
  color: var(--ln-ink-muted);
  font-size: 0.78rem;
  font-weight: 600;
}

.ln-map-popup-muted {
  margin: 0.85rem 0 0;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--ln-line);
  border-radius: var(--ln-radius-sm);
  background-color: var(--ln-surface);
  color: var(--ln-ink-muted);
  font-size: 0.85rem;
}

/* Mehrere Stationen in EINEM Popup: bei ~715 m gezeichneter Unschärfe
   überlappen Flächen regelmäßig, und ein Klick darf nicht mehrere
   Popups stapeln (dann wäre die untere Station unerreichbar). Der
   Trennstrich ist die einzige Zutat, die dafür nötig ist. */
.ln-map-popup-rule {
  margin: 0;
  border: 0;
  border-top: 1px solid var(--ln-line);
  opacity: 1;
}

/* Alter je Messwert im Popup — eigene Zeile unter dem Wert, gedämpft.
   Die Tabelle stellt dasselbe als parallele Liste in der Spalte „Stand"
   dar; beide Flächen sagen damit dasselbe je Parameter. */
.ln-map-popup-age {
  display: inline-flex;
  grid-column: 1 / -1;
  justify-self: start;
  padding: 0.12rem 0.42rem;
  border-radius: 999px;
  background-color: var(--ln-surface);
  color: var(--ln-ink-muted);
  font-size: 0.72rem;
  line-height: 1.35;
}

@media (pointer: coarse) {
  .maplibregl-popup-close-button {
    top: 0.25rem;
    right: 0.25rem;
    width: 2.75rem;
    height: 2.75rem;
  }

  /* Die Filterleiste besteht aus Elementen, die keine .btn sind — die
     44px-Regel oben greift für sie nicht. Das X am Tag bleibt kleiner
     als 44px: es sitzt IN einem 44px hohen Tag, und ein 44er-Kreis
     spräng aus der Pille heraus. 2rem sind über dem Mindestmaß für
     Zielgrößen und mit dem Tag als Umgebung gut zu treffen. */
  .ln-filter-menu-button,
  .ln-station-filters .form-control {
    min-height: 44px;
  }

  .ln-filter-option {
    min-height: 2.75rem;
  }

  .ln-filter-tag {
    min-height: 44px;
    padding-left: 0.85rem;
  }

  .ln-filter-tag-remove {
    width: 2rem;
    height: 2rem;
  }

  .ln-filter-clear {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 0.35rem;
  }
}

@media (max-width: 991.98px) {
  .ln-map-workspace {
    grid-template-columns: minmax(0, 1fr);
  }

  .ln-map-panel {
    position: static;
  }

  /* Gestapelt unter der Karte trägt die Seite den Scroll, nicht der
     Container — eine Höhenkappe würde hier doppelt scrollen. */
  .ln-station-list-scroll {
    max-height: none;
  }
}

@media (max-width: 575.98px) {
  /* Auf schmalen Geräten bekommt die Suche ihre eigene Zeile, die zwei
     Menüknöpfe teilen sich die zweite — nebeneinander bliebe für das
     Eingabefeld sonst ein Stummel. */
  .ln-station-filter-search {
    flex-basis: 100%;
  }

  .ln-filter-menu {
    flex: 1 1 0;
  }

  .ln-filter-menu-button {
    width: 100%;
    justify-content: center;
  }

  /* Ein rechtsbündiges Menü liefe an der linken Spalte über den Rand. */
  .ln-filter-menu:first-of-type .ln-filter-menu-panel {
    right: auto;
    left: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ln-map-station-marker,
  .ln-map-station-marker__badge {
    transition: none;
  }
}

/* ================================================================
 * Öffentliche Startseite
 * ================================================================ */

/* Die Landingpage darf großzügiger sein als die Datenoberflächen, bleibt
   aber im selben Uferlicht-System: alle Flächen und Akzente kommen aus den
   bestehenden Tokens. Zwei wiederkehrende Eigenheiten tragen den Charakter
   (Redesign 2026-08-17):
   1. „Uferkante" — beide Fotos laufen randlos bis an die Außenkante des
      Viewports und tragen zur Seitenmitte hin eine große Rundung.
   2. „Ganglinie" — die zwei Messgrößen des Produkts (Wasserstand blau,
      Temperatur kupfer) erscheinen als gezeichnete Linien: als Trennband
      unter dem Hero und klein in der Vorteils-Kachel. */

.ln-home {
  display: grid;
  gap: clamp(4rem, 9vw, 7.5rem);
  padding-block: clamp(1rem, 3vw, 2.5rem) 0;
}

/* Die randlosen Bilder ragen mit calc(50% - 50vw) um die Scrollbarbreite
   über den Viewport hinaus — clip statt hidden, damit daraus kein
   horizontaler Scrollcontainer wird. */
.ln-home-page {
  overflow-x: clip;
}

/* Schlussband und Fußzeile stehen beide auf --ln-primary-deep und sollen
   als ein dunkler Abschluss wirken; das mt-5 aus base.html würde einen
   Sandstreifen dazwischenlegen. */
.ln-home-page .ln-footer {
  margin-top: 0 !important;
}

/* Lesegröße statt Datengröße: --bs-body-font-size ist mit 0,875rem für
   die Kacheln der Datenoberfläche gewählt (tokens.css). Auf der
   Landingpage ist der Fließtext der Inhalt, nicht die Beschriftung eines
   Messwerts — bei 14px lief die Prosa hier auf 85–100 Zeichen je Zeile
   und stand als Kleingedrucktes unter 2,35rem-Überschriften. Die Tokens
   bleiben unberührt, die Größe gilt nur innerhalb von .ln-home. */
.ln-home {
  font-size: 1rem;
}

.ln-home h1 {
  max-width: 12ch;
  /* Optisch, nicht metrisch: bei line-height 0,98 endet die Box über der
     Unterlänge von „zählt." — 1rem las sich als Kontakt zum Lead. */
  margin-bottom: 1.5rem;
  color: var(--ln-primary-deep);
  font-size: clamp(2.5rem, 7vw, 5rem);
  font-weight: 720;
  letter-spacing: -0.055em;
  line-height: 0.98;
  /* Kein text-wrap: balance — die 12ch-Grenze setzt den Umbruch schon
     bewusst („Jeder Bach / zählt."); balance verteilte auf „Jeder /
     Bach zählt." und nahm der ersten Zeile das Gewicht. */
}

.ln-home h2 {
  max-width: 30ch;
  margin-bottom: 1rem;
  color: var(--ln-primary-deep);
  font-size: clamp(1.55rem, 3vw, 2.35rem);
  font-weight: 680;
  letter-spacing: -0.035em;
  line-height: 1.08;
  text-wrap: balance;
}

.ln-home h3 {
  margin-bottom: 0.55rem;
  color: var(--ln-ink);
  font-size: 1.05rem;
  font-weight: 650;
  text-wrap: balance;
}

.ln-home p {
  line-height: 1.65;
  text-wrap: pretty;
}

/* Trägt die Überschrift ein eigenständiges Bauteil (Schrittkette,
   Vorteile, FAQ-Liste), steht sie weiter davon ab als von einem eigenen
   Einleitungssatz. Die Spezifität muss .ln-home h2 (0,1,1) schlagen —
   die frühere Ein-Klassen-Fassung wurde nie wirksam und der Rhythmus
   zerfiel in zwei verschiedene Abstände. */
.ln-home h2.ln-home-section-title {
  margin-bottom: 2rem;
}

/* ---- Hero ---- */

.ln-home-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(18rem, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
  min-height: min(38rem, calc(100dvh - 12rem));
}

.ln-home-hero-copy {
  min-width: 0;
}

.ln-home-lead {
  max-width: 40rem;
  margin-bottom: 1.6rem;
  color: var(--ln-ink);
  font-size: clamp(1.05rem, 1.8vw, 1.25rem);
}

.ln-home-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem;
  align-items: center;
}

.ln-home-actions .btn {
  display: inline-flex;
  gap: 0.5rem;
  align-items: center;
  padding: 0.72rem 1.1rem;
  font-weight: 650;
}

.ln-home-actions .btn:active {
  transform: translateY(1px);
}

/* Das Kuvert am mailto-Knopf ist die einzige Vorwarnung, dass ein
   Mailprogramm aufgeht — es trägt Bedeutung und darf nicht kleiner
   werden als die Beschriftung daneben. */
.ln-home-actions .btn .ln-icon {
  width: 1.1em;
  height: 1.1em;
  flex: none;
  vertical-align: 0;
}

.ln-home-contact-note {
  max-width: 56ch;
  margin: 0.9rem 0 0;
  color: var(--ln-ink-muted);
  font-size: 0.9rem;
  line-height: 1.55;
}

.ln-home-contact-note a {
  text-underline-offset: 0.2em;
}

.ln-home-contact-note a:hover {
  text-decoration-thickness: 2px;
}

/* ---- Bilder mit Uferkante ---- */

.ln-home-image {
  overflow: hidden;
  margin: 0;
  background-color: var(--ln-card);
  box-shadow: var(--ln-shadow-raised);
}

.ln-home-image img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

.ln-home-image-end {
  margin-right: calc(50% - 50vw);
  border-radius: clamp(2.5rem, 5vw, 4.5rem) 0 0 var(--ln-radius);
}

.ln-home-image-start {
  margin-left: calc(50% - 50vw);
  border-radius: 0 clamp(2.5rem, 5vw, 4.5rem) var(--ln-radius) 0;
}

/* ---- Ganglinien-Band + Grundsätze ---- */

.ln-home-facts {
  display: grid;
  gap: 1.75rem;
}

.ln-home-pegel {
  display: block;
  width: 100%;
  height: clamp(2.5rem, 6vw, 4rem);
}

/* Kein vector-effect hier: non-scaling-stroke rechnet die Strichelung im
   Bildschirmraum und bricht damit die pathLength-Normierung der
   Zeichen-Animation (Linie endete bei ~50 %). Die leichte anisotrope
   Skalierung des Strokes durch preserveAspectRatio="none" ist bei fast
   waagerechten Linien unsichtbar. */
.ln-home-pegel path {
  fill: none;
}

.ln-home-pegel-level {
  stroke: var(--ln-series-level);
  stroke-width: 2.5;
}

.ln-home-pegel-temp {
  stroke: var(--ln-series-temp);
  stroke-width: 1.75;
  opacity: 0.75;
}

/* Die Linien zeichnen sich beim Laden einmal wie eine entstehende
   Messreihe — nur wenn Bewegung erwünscht ist. */
@media (prefers-reduced-motion: no-preference) {
  .ln-home-pegel path {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
    animation: ln-home-draw 1.4s cubic-bezier(0.33, 1, 0.68, 1) 0.2s forwards;
  }

  .ln-home-pegel-temp {
    animation-delay: 0.45s;
  }
}

@keyframes ln-home-draw {
  to {
    stroke-dashoffset: 0;
  }
}

.ln-home-facts-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.25rem, 3vw, 2.5rem);
  margin: 0;
}

.ln-home-fact dt {
  margin-bottom: 0.3rem;
  color: var(--ln-primary-deep);
  font-size: 1rem;
  font-weight: 680;
}

.ln-home-fact dd {
  margin: 0;
  color: var(--ln-ink-muted);
  font-size: 0.9rem;
  line-height: 1.55;
}

/* ---- Problemabschnitt: Editorial statt Panel ---- */

.ln-home-problem h2 {
  max-width: 20ch;
  font-size: clamp(1.9rem, 4vw, 3rem);
}

/* Asymmetrischer Weißraum: die Prosa rückt gegenüber der Überschrift ein.
   Das Zeilenmaß steht in ch statt rem — rem hing an der Wurzelgröße und
   lief mit dem Fließtext auseinander; ch bindet die Breite an die
   tatsächliche Schrift des Absatzes. */
.ln-home-problem-prose {
  max-width: 68ch;
  margin-left: clamp(0rem, 12vw, 9rem);
}

.ln-home-problem-lead {
  font-size: clamp(1.05rem, 1.8vw, 1.3rem);
}

.ln-home-problem-prose p:last-child {
  margin-bottom: 0;
  color: var(--ln-ink-muted);
}

/* ---- Schritte: Übertragungsstrecke statt Kartenreihe ---- */

.ln-home-steps {
  --ln-step-gap: clamp(1.5rem, 4vw, 3rem);

  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--ln-step-gap);
  margin: 0;
  padding: 0;
  list-style: none;
}

.ln-home-steps li {
  position: relative;
  min-width: 0;
}

.ln-home-step-node {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  margin-bottom: 1.1rem;
  border: 1px solid var(--ln-line-strong);
  border-radius: 50%;
  background-color: var(--ln-card);
  box-shadow: var(--ln-shadow);
  color: var(--ln-primary);
}

.ln-home-step-node .ln-icon {
  width: 1.35rem;
  height: 1.35rem;
}

/* Gestrichelte Verbindung von Knoten zu Knoten, über die Grid-Lücke
   hinweg — die Daten wandern vom Sensor zur Karte. */
.ln-home-steps li:not(:last-child)::before {
  content: "";
  position: absolute;
  top: 1.5rem;
  right: calc(-1 * var(--ln-step-gap));
  left: 3.6rem;
  border-top: 2px dashed var(--ln-line-strong);
}

.ln-home-steps p {
  margin-bottom: 0;
  color: var(--ln-ink-muted);
}

.ln-home-sensorfinder {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 1.5rem;
  align-items: center;
  justify-content: space-between;
  margin-top: 2.4rem;
  padding: 1.1rem 1.4rem;
  border: 1px solid var(--ln-line);
  border-radius: var(--ln-radius);
  background-color: var(--ln-card);
  box-shadow: var(--ln-shadow);
}

.ln-home-sensorfinder p {
  max-width: 58ch;
  margin: 0;
  color: var(--ln-ink-muted);
}

.ln-home-sensorfinder p b {
  color: var(--ln-ink);
}

/* ---- Vorteile: asymmetrisches Raster statt drei gleicher Spalten ---- */

.ln-home-benefits {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.25rem;
}

.ln-home-benefit {
  min-width: 0;
  padding: clamp(1.5rem, 3vw, 2.25rem);
  border-radius: calc(var(--ln-radius) * 2);
  background-color: var(--ln-card);
  box-shadow: var(--ln-shadow);
  transition: box-shadow 0.2s ease;
}

.ln-home-benefit:hover {
  box-shadow: var(--ln-shadow-raised);
}

.ln-home-benefit > .ln-icon {
  width: 1.8rem;
  height: 1.8rem;
  margin-bottom: 1.2rem;
  color: var(--ln-primary);
}

.ln-home-benefit p {
  margin-bottom: 0;
  color: var(--ln-ink-muted);
}

.ln-home-benefit-featured {
  display: flex;
  flex-direction: column;
  grid-column: 1 / -1;
  background-color: color-mix(in srgb, var(--ln-series-level) 9%, var(--ln-card));
}

.ln-home-benefit-featured p {
  max-width: 68ch;
}

.ln-home-sparkline {
  position: relative;
  width: 100%;
  height: clamp(4rem, 8vw, 6rem);
  margin-top: auto;
}

/* ---- Community ---- */

.ln-home-community {
  display: grid;
  grid-template-columns: minmax(18rem, 0.9fr) minmax(0, 1.1fr);
  gap: clamp(2rem, 6vw, 5rem);
  align-items: center;
}

.ln-home-community-copy {
  max-width: 62ch;
}

.ln-home-community-copy p:last-child {
  margin-bottom: 0;
  color: var(--ln-ink-muted);
}

/* ---- Schlussband: läuft randlos und in die Fußzeile über ---- */

/* Beispielmonat: zwei Chart.js-Charts (oben Temperatur + Sauerstoff mit
   zwei y-Achsen, darunter der Pegel) — dieselbe Bibliothek und Farbwelt
   wie die Dashboards; gebaut von ln.mountMonthChart() in project.js. */
.ln-home-month-intro {
  max-width: 68ch;
  /* Die Einleitung gehört zur Überschrift, die Figur ist das nächste
     Bauteil — enger nach oben, großzügiger nach unten. */
  margin-bottom: 2rem;
  color: var(--ln-ink-muted);
}

.ln-home-month-figure {
  margin: 0 0 1.75rem;
  padding: clamp(1rem, 2.5vw, 2rem) clamp(1rem, 2.5vw, 2rem) 0.75rem;
  border-radius: calc(var(--ln-radius) * 2);
  background-color: var(--ln-card);
  box-shadow: var(--ln-shadow);
}

.ln-home-month-canvas {
  position: relative;
}

/* Feste Pixelhöhen statt clamp(): Chart.js liest die Container-Höhe aus
   der *angegebenen* Stilangabe und parst sie mit parseFloat — aus
   "clamp(250px, 34vw, 340px)" wurde 250. Beide Charts zeichneten
   dadurch auf der clamp-Untergrenze und ließen zusammen rund 140px
   Leerraum in der Karte stehen, mit einem sichtbaren Loch zwischen den
   beiden Plots. Die Stufen liegen auf den Umbruchpunkten der Seite. */
.ln-home-month-canvas-main {
  height: 340px;
}

.ln-home-month-canvas-level {
  height: 180px;
  margin-top: 0.75rem;
}

.ln-home-month-caption {
  margin-top: 0.6rem;
  color: var(--ln-ink-muted);
  font-size: 0.85rem;
}

/* Nummerierte Ereignisliste, Zähler passend zu den Markern im Diagramm. */
.ln-home-month-events {
  /* inkl. der 2,6rem Einzugsspalte für die Zähler */
  max-width: 76ch;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: ln-month-event;
  display: grid;
  gap: 0.9rem;
}

.ln-home-month-events li {
  counter-increment: ln-month-event;
  position: relative;
  padding-left: 2.6rem;
  color: var(--ln-ink-muted);
  line-height: 1.6;
}

.ln-home-month-events li b {
  color: var(--ln-ink);
}

.ln-home-month-events li::before {
  content: counter(ln-month-event);
  position: absolute;
  left: 0;
  top: 0.1rem;
  display: inline-grid;
  place-items: center;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  background-color: var(--ln-primary-deep);
  color: var(--ln-on-dark);
  font-size: 0.9rem;
  font-weight: 700;
}

/* Netzwerkkarten-Fenster: echter Produkt-Screenshot (Beispieldaten) als
   Link auf die öffentliche Karte. */
.ln-home-map-figure {
  max-width: 60rem;
  margin: 0;
}

.ln-home-map-figure img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: calc(var(--ln-radius) * 2);
  box-shadow: var(--ln-shadow-raised);
}

/* FAQ-Teaser: editoriale Liste mit Trennlinien — bewusst kein weiteres
   Kartenraster, das Redesign hat die Karten-Monotonie gerade entfernt. */
.ln-home-faq-list {
  max-width: 54rem;
  margin: 0;
  border-bottom: 1px solid var(--ln-line);
}

.ln-home-faq-item {
  display: grid;
  grid-template-columns: minmax(0, 16rem) minmax(0, 1fr);
  gap: 0.5rem clamp(1.5rem, 4vw, 3.5rem);
  padding-block: 1.4rem;
  border-top: 1px solid var(--ln-line);
}

.ln-home-faq-item dt {
  color: var(--ln-primary-deep);
  font-size: 1.05rem;
  font-weight: 680;
}

.ln-home-faq-item dd {
  margin: 0;
  color: var(--ln-ink-muted);
  line-height: 1.6;
}

.ln-home-faq-more {
  margin: 1.5rem 0 0;
  font-weight: 600;
}

/* Browser-Standardunterstreichung sitzt auf der Grundlinie und schneidet
   die Unterlängen von g/ß/ü — dieselbe Absetzung wie in der Fußzeile. */
.ln-home-faq-more a {
  text-underline-offset: 0.2em;
}

.ln-home-faq-more a:hover {
  text-decoration-thickness: 2px;
}

.ln-home-final {
  padding-block: clamp(3rem, 7vw, 5.5rem);
  background-color: var(--ln-primary-deep);
  /* Randloser Hintergrund ohne den Container zu verlassen: border-image
     mit horizontalem Outset malt die Fläche in einer Ebene weiter (eine
     box-shadow+clip-path-Variante hinterließ eine Subpixel-Naht an der
     Oberkante). Horizontal begrenzt overflow-x: clip auf dem body. */
  border-image: conic-gradient(var(--ln-primary-deep) 0 0) fill 0 / 0 / 0 100vw;
  color: var(--ln-on-dark);
}

.ln-home-final h2 {
  max-width: 18ch;
  color: var(--ln-on-dark);
  font-size: clamp(2rem, 4.5vw, 3.4rem);
  /* wie h1: die ch-Grenze bestimmt den Umbruch, nicht der Ausgleich */
  text-wrap: initial;
}

.ln-home-final-copy {
  max-width: 48rem;
  margin-bottom: 2rem;
}

.ln-home-final-copy p {
  margin-bottom: 0;
  color: color-mix(in srgb, var(--ln-on-dark) 82%, var(--ln-primary-deep));
  font-size: 1.05rem;
}

/* Der Fokusring erbte var(--ln-focus) (#2a6f5f) und lag durch
   outline-offset ganz auf dem Band (#1c4f43) — gemessen 1,57:1, also
   unter dem 3:1-Minimum für Nicht-Text (WCAG 2.2 SC 2.4.13). Die
   Fußzeile löst dasselbe Problem seit jeher mit --ln-on-dark und kommt
   auf 9,34:1; hier fehlte die Regel nur, weil das Schlussband nicht in
   .ln-footer liegt. */
.ln-home-final :focus-visible {
  outline-color: var(--ln-on-dark);
}

.ln-home-final .ln-home-contact-note {
  color: color-mix(in srgb, var(--ln-on-dark) 82%, var(--ln-primary-deep));
}

.ln-home-final .ln-home-contact-note a {
  color: var(--ln-on-dark);
}

/* Buttons auf dunklem Grund: gleiche Klassen, kontextabhängige Werte —
   alle Farben bleiben Token-Ableitungen. */
.ln-home-final .btn-primary {
  --bs-btn-color: var(--ln-primary-deep);
  --bs-btn-bg: var(--ln-card);
  --bs-btn-border-color: var(--ln-card);
  --bs-btn-hover-color: var(--ln-primary-deep);
  --bs-btn-hover-bg: var(--ln-on-dark);
  --bs-btn-hover-border-color: var(--ln-on-dark);
  --bs-btn-active-color: var(--ln-primary-deep);
  --bs-btn-active-bg: var(--ln-on-dark);
  --bs-btn-active-border-color: var(--ln-on-dark);
}

.ln-home-final .btn-outline-primary {
  --bs-btn-color: var(--ln-on-dark);
  --bs-btn-border-color: color-mix(in srgb, var(--ln-on-dark) 55%, transparent);
  --bs-btn-hover-color: var(--ln-primary-deep);
  --bs-btn-hover-bg: var(--ln-on-dark);
  --bs-btn-hover-border-color: var(--ln-on-dark);
  --bs-btn-active-color: var(--ln-primary-deep);
  --bs-btn-active-bg: var(--ln-on-dark);
  --bs-btn-active-border-color: var(--ln-on-dark);
}

/* ---- Responsiv ---- */

@media (max-width: 991.98px) {
  .ln-home-hero,
  .ln-home-community {
    grid-template-columns: minmax(0, 1fr);
  }

  .ln-home-hero {
    min-height: auto;
  }

  /* Unterhalb des Zweispalters bluten die Bilder auf beiden Seiten aus. */
  .ln-home-image-end,
  .ln-home-image-start {
    margin-inline: calc(50% - 50vw);
    border-radius: 0;
  }

  .ln-home-community .ln-home-image {
    order: 1;
  }

  .ln-home-problem-prose {
    margin-left: 0;
  }

  .ln-home-month-canvas-main {
    height: 300px;
  }

  .ln-home-month-canvas-level {
    height: 160px;
  }
}

@media (max-width: 767.98px) {
  .ln-home {
    gap: 4rem;
  }

  .ln-home-facts-list,
  .ln-home-benefits,
  .ln-home-faq-item {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Schritte untereinander: Knoten links, Text rechts, senkrechte
     Verbindung entlang der Knotenspalte. */
  .ln-home-steps {
    grid-template-columns: minmax(0, 1fr);
  }

  .ln-home-steps li {
    display: grid;
    grid-template-columns: 3rem minmax(0, 1fr);
    column-gap: 1.1rem;
  }

  .ln-home-steps li > h3,
  .ln-home-steps li > p {
    grid-column: 2;
  }

  .ln-home-step-node {
    grid-row: 1 / 3;
    margin-bottom: 0;
  }

  .ln-home-steps li:not(:last-child)::before {
    top: 3.4rem;
    right: auto;
    bottom: calc(-1 * var(--ln-step-gap));
    left: 1.5rem;
    border-top: 0;
    border-left: 2px dashed var(--ln-line-strong);
  }
}

@media (max-width: 575.98px) {
  .ln-home h1 {
    font-size: clamp(2.35rem, 14vw, 3.5rem);
  }

  .ln-home-actions {
    align-items: stretch;
    flex-direction: column;
  }

  .ln-home-actions .btn {
    width: 100%;
  }

  .ln-home-month-canvas-main {
    height: 250px;
  }

  .ln-home-month-canvas-level {
    height: 130px;
  }
}

/* ================================================================
 * Seitenfuß
 * ================================================================ */

.ln-footer {
  background-color: var(--ln-primary-deep);
  color: var(--ln-on-dark);
}

.ln-footer a {
  color: var(--ln-on-dark);
  text-underline-offset: 0.2em;
}

.ln-footer a:hover {
  color: var(--ln-on-dark);
  text-decoration-thickness: 2px;
}

.ln-footer a:focus-visible {
  outline-color: var(--ln-on-dark);
}

.ln-footer-information {
  padding-block: clamp(2.5rem, 6vw, 4rem);
}

.ln-footer-information-grid {
  display: grid;
  grid-template-columns: minmax(16rem, 0.85fr) minmax(22rem, 1.15fr);
  gap: clamp(2rem, 8vw, 6rem);
}

.ln-footer-brand-link {
  display: inline-flex;
  gap: 0.8rem;
  align-items: center;
  font-size: 1.25rem;
  font-weight: 720;
  text-decoration: none;
}

.ln-footer-brand-link img {
  width: 3rem;
  height: 3rem;
  padding: 0.35rem;
  border-radius: 50%;
  background-color: var(--ln-card);
}

.ln-footer-brand p {
  max-width: 34rem;
  margin: 1rem 0 0;
  color: color-mix(in srgb, var(--ln-on-dark) 80%, var(--ln-primary-deep));
  line-height: 1.6;
}

.ln-footer-links {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.5rem;
}

.ln-footer-links h2 {
  margin: 0 0 0.8rem;
  color: color-mix(in srgb, var(--ln-on-dark) 72%, var(--ln-primary-deep));
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.ln-footer-links ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.ln-footer-links a {
  display: block;
  width: fit-content;
  padding-block: 0.28rem;
}

.ln-footer-legal {
  padding-block: 1rem;
  border-top: 1px solid color-mix(in srgb, var(--ln-on-dark) 22%, transparent);
  color: color-mix(in srgb, var(--ln-on-dark) 78%, var(--ln-primary-deep));
}

@media (max-width: 767.98px) {
  .ln-footer-information-grid,
  .ln-footer-links {
    grid-template-columns: minmax(0, 1fr);
  }

  .ln-footer-links {
    gap: 1.25rem;
  }
}

@media (pointer: coarse) {
  .ln-footer-links a {
    display: inline-flex;
    min-height: 44px;
    align-items: center;
  }
}
