/**
 * Default-Theme (Akzent: Orange). Per-Gemeinde-Override unter
 * /assets/communities/{slug}/main.css.
 *
 * Enthaelt:
 *  - Tailwind-Config-Ueberschreibungen (Farben, Fonts)
 *  - Globale Styles die bisher inline in jeder .html-Datei standen
 *  - Utility-Classes (hide-scroll, nav-fixed)
 *
 * Gemeinde-spezifische Akzentfarben/Fonts/Sub-Element-Styles werden im
 * /assets/communities/{slug}/main.css ueberschrieben — die Layout-API
 * haengt den Pfad automatisch an assets.css an. Statusbar-Farbe
 * (PWA + <meta name="theme-color">) kommt zusaetzlich aus
 * community.theme_color, siehe app/manifest.php + app/index.php.
 */

/* Tailwind runtime-config wird per <script> im <head> gesetzt (siehe index.php).
   Hier nur statische Styles die kein Tailwind-Preset brauchen. */

/* ============================================================================
   Inter — self-hosted (DSGVO-konform, kein Google-Fonts-Roundtrip)
   ----------------------------------------------------------------------------
   Begruendung: Einbindung ueber fonts.googleapis.com sendet die IP-Adresse
   des Besuchers an Google. Das LG Muenchen hat das 2022 (Az. 3 O 17493/20)
   ohne Einwilligung als DSGVO-Verstoss gewertet — Schadenersatzpflicht.
   Selbst-Hosting der OFL-lizenzierten Inter-WOFF2-Files schaltet das aus.
   Quelle der Files: fonts.gstatic.com (Variable-Font, 1 Datei pro Subset
   enthaelt alle Gewichte 300-700). Lizenz: SIL Open Font License 1.1
   (siehe app/assets/fonts/inter/LICENSE.txt).

   font-display: optional — kein Layout-Shift waehrend des Lesens (siehe
   PR #821). Bei kaltem Cache rendert der Erst-Besucher mit System-Font,
   ab dem zweiten Pageview ist Inter aus dem Browser-Cache da.
   ============================================================================ */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 700;     /* Variable-Font: ein File deckt 300-700 ab */
  font-display: optional;
  src: url('/assets/fonts/inter/inter-latin.woff2') format('woff2');
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
    U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 700;
  font-display: optional;
  src: url('/assets/fonts/inter/inter-latin-ext.woff2') format('woff2');
  unicode-range:
    U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ============================================================================
   Design-Tokens (CSS-Variablen)
   ----------------------------------------------------------------------------
   Per-Gemeinde-Overrides liegen unter app/assets/communities/{slug}/main.css
   und werden NACH dieser Datei geladen (siehe erp/Classes/API/ApiLayout.php
   -> buildAssets). Eine Gemeinde stellt damit ihren Look mit wenigen Zeilen
   um, ohne dass Templates angefasst werden muessen.

   Defaults entsprechen 1:1 dem aktuellen Tailwind-basierten Design — Phase 1
   fuehrt die Variablen nur ein, sie werden noch nicht in Templates angewendet
   (das passiert in spaeteren Phasen, schrittweise pro Block-Typ).
   ============================================================================ */
:root {
  /* ---- Akzent-Farbe ---- */
  --accent:         #f97316;   /* tailwind orange-500 — primary */
  --accent-strong:  #ea580c;   /* tailwind orange-600 — hover/active */
  --accent-soft:    #fed7aa;   /* tailwind orange-200 — backgrounds */
  --accent-mute:    #fdba74;   /* tailwind orange-300 — disabled/indeterminate */
  --accent-on:      #ffffff;   /* Textfarbe AUF Akzent-Hintergrund */

  /* Fuellfarbe hinter der Handy-Statusleiste im nativen App-Shell.
     Seit 11.09.2026 WEISS wie der App-Header (User-Entscheidung: das
     blaue Band wirkte als leerer Streifen ueber dem weissen Kopf) —
     dazu passend UIStatusBarStyleDarkContent in mobile/ios Info.plist.
     Per-Gemeinde ueberschreibbar. Nur relevant wenn
     env(safe-area-inset-top) > 0, also App/Standalone-PWA. */
  --status-bar-bg:  #ffffff;
  --brand-blue:     #1f8ecf;   /* mitanand-Markenblau (Akzente, Install-Banner) */
  --nav-safe-bottom: calc(env(safe-area-inset-bottom, 0px) * 0.6); /* Bottom-Nav-Polster, s. .nav-fixed */

  /* ---- Status-Farben ---- */
  --status-open:    #16a34a;   /* green-600 */
  --status-closed:  #ef4444;   /* red-500 */
  --status-info:    #2563eb;   /* blue-600 */
  --status-warning: #d97706;   /* amber-600 */
  --status-error:   #dc2626;   /* red-600 */
  --status-muted:   #6b7280;   /* gray-500 — z.B. abgelehnte Mängel-Meldung */

  /* ---- Neutrale Text- und Flaechenfarben ---- */
  --text-strong:    #111827;   /* gray-900 — Headings, primaerer Text */
  --text-default:   #1f2937;   /* gray-800 — Fliesstext */
  --text-muted:     #6b7280;   /* gray-500 — sekundaerer Text */
  --text-faint:     #9ca3af;   /* gray-400 — Labels, Meta */
  --border-default: #e5e7eb;   /* gray-200 — Trenner, Card-Border */
  --surface-soft:   #f3f4f6;   /* gray-100 — Card-Background */
  --surface-toggle-off: #d1d5db; /* gray-300 — inaktiver Toggle-Track */

  /* ---- Section-Rhythmus ----
     Der section-divider (h-2 grau) ist heute fester Block-Trenner.
     --block-py/px sind vorbereitet, werden aber in Phase 1 noch nicht
     erzwungen — Templates regeln padding/margin weiterhin via Tailwind. */
  --block-py:        0;            /* spaeter z.B. 1.5rem */
  --block-px:        0;            /* spaeter z.B. 1rem (px-5 = 1.25rem heute) */
  --divider-height:  0.5rem;       /* h-2 (8px) — section-divider */
  --divider-color:   var(--border-default);

  /* ---- Radius ----
     Design-Kern: kantig. Bewusst auf 0 — bei Bedarf pro Gemeinde anhebbar. */
  --radius:          0;
  --radius-pill:     9999px;       /* Toggle-Knob, Avatare (full-round bleibt) */

  /* ---- Typografie: Basis ---- */
  --font-family:     'Inter', system-ui, sans-serif;
  --font-size-base:  1rem;         /* 16px */
  --line-height-base: 1.5;
  --font-smoothing:  antialiased;

  /* ---- H1 — Page-Titel (text-2xl font-bold tracking-tight) ----
     Verwendet in: page-header, event-hero, news/detail, tenants/profile-header */
  --h1-size:         1.5rem;       /* text-2xl */
  --h1-weight:       700;          /* font-bold */
  --h1-line:         1.25;         /* leading-tight */
  --h1-tracking:     -0.025em;     /* tracking-tight */
  --h1-color:        var(--text-strong);

  /* ---- H2 (Block-Title) — Block-Headline mit Akzent-Underline (text-lg font-bold) ----
     Verwendet in: home/vereine, news/list-horizontal, list, resource-* */
  --h2-size:         1.125rem;     /* text-lg */
  --h2-weight:       700;
  --h2-line:         1.5;
  --h2-color:        var(--text-strong);

  /* ---- H2-Label — Kapitel-Label ueber Block (text-xs uppercase tracking-wide gray-400) ----
     Bei weitem haeufigste Variante (~35 Templates) */
  --label-size:      0.75rem;      /* text-xs */
  --label-weight:    500;          /* font-medium */
  --label-color:     var(--text-faint);
  --label-tracking:  0.025em;      /* tracking-wide */
  --label-transform: uppercase;

  /* ---- H3 — Item-Titel in Listen (text-[15px] / text-lg semibold) ---- */
  --h3-size:         1.125rem;     /* text-lg (text-card-Titel) */
  --h3-weight:       600;          /* font-semibold */
  --h3-line:         1.5;
  --h3-color:        var(--text-strong);

  /* ---- Wichtig-Box (Banner) ----
     --waste-color steuert das Pastell-Schema. Default = Akzent;
     pro waste_type ueber .wichtig-box[style="--waste-color: #xxx"] (Template). */
  --waste-color:     var(--accent-strong);

  /* ---- Sektions-Ueberschrift (.section-heading) ----
     Farbiger Balken ueber Listen/Blocks. Default = Akzent der Gemeinde,
     pro Gemeinde einzeln umstellbar (z.B. auf --accent-strong, wenn der
     helle Akzent zu wenig Kontrast zu weisser Schrift hat). */
  --section-heading-bg: var(--accent);
  --section-heading-fg: var(--accent-on);
  /* Polsterung der Standard-Sections (px-5 py-5). Der Balken zieht sich per
     negativer Margin darueber hinaus: horizontal damit er randlos sitzt,
     vertikal damit er buendig am Sektions-Anfang klebt. */
  --section-pad-x:      1.25rem;
  --section-pad-y:      1.25rem;
}

body {
  /* font-size + line-height bewusst nicht hier setzen — sie werden via
     Tailwind-Preflight von <html> geerbt (line-height: 1.5). Ein expliziter
     line-height auf body sorgte beim Font-Swap (Inter laedt async) fuer
     einen sichtbaren Layout-Shift, weil der Browser den Wert dann mit den
     neuen Inter-Metriken neu berechnet. Variablen --font-size-base /
     --line-height-base bleiben deklariert fuer spaetere h1/h2-Regeln. */
  font-family: var(--font-family);
  padding-bottom: 2.5rem;
  -webkit-font-smoothing: antialiased;
}

/* ============================================================================
   .section-heading — Ueberschrift ueber Listen und Block-Inhalten
   ----------------------------------------------------------------------------
   Loest die frueher in ~37 Templates kopierte Utility-Kette
   "text-xs text-gray-500 font-medium uppercase tracking-wide mb-3" ab.
   Eine Klasse statt Copy-Paste: der Look ist an EINER Stelle aenderbar.

   Die Grundform ist bewusst dieselbe unauffaellige graue Zeile wie vorher —
   ueber einer Galerie oder einer Telefonliste soll nichts schreien. Der
   farbige Balken ist ein eigener Zustand (--bar) und steht nur dort, wo er
   etwas leistet: in der Suche, wo er die Trefferliste in Kategorien
   zerlegt.
   ============================================================================ */
.section-heading {
  font-size: 0.75rem;
  line-height: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #6b7280;                 /* gray-500 */
  margin: 0 0 0.75rem;
}

/* ----------------------------------------------------------------------------
   Balken-Variante: Kategorie-Ueberschrift in der Suche.

   Sitzt randlos — die Standard-Sections haben px-5 (1.25rem), die negative
   Margin zieht den Balken darueber hinaus, und der Zug nach oben klebt ihn
   an den Sektions-Anfang, damit er zur Liste UNTER ihm gehoert. Steht die
   Ueberschrift nicht in einer gepolsterten Section (Overlay-Suche), nimmt
   --flush den Ueberhang zurueck.
   -------------------------------------------------------------------------- */
.section-heading--bar {
  background-color: var(--section-heading-bg);
  color: var(--section-heading-fg);
  padding: 0.25rem var(--section-pad-x);
  margin: 0 calc(-1 * var(--section-pad-x)) 0.5rem;
}

section > .section-heading--bar:first-child {
  margin-top: calc(-1 * var(--section-pad-y));
}

/* Ueberschrift ohne gepolsterten Eltern-Container (gallery-swiper, table,
   tracks, iframe fullWidth: <section class="py-5"> ohne px-5) — kein
   Ueberhang, aber der Einzug muss aus der Klasse selbst kommen. Vor dem
   Umbau trugen diese Ueberschriften px-5; ohne das Padding klebt der Text
   am Rand der App-Spalte und wirkt aus dem Boxed-Layout ausgebrochen.
   Fuer --bar ist das Padding identisch (dort kommt es schon aus der
   Balken-Regel), die Variante bleibt unveraendert. */
.section-heading--flush {
  margin-left: 0;
  margin-right: 0;
  padding-left: var(--section-pad-x);
  padding-right: var(--section-pad-x);
}

/* ============================================================================
   .anchor-flash — Fundstelle nach einem Sprung kurz hervorheben
   ----------------------------------------------------------------------------
   Gesetzt von anchor-highlight.js, wenn die URL auf eine Sprungmarke zeigt
   (Dokument-Treffer aus der Suche: /seite#datei-12). Nach gut einer Sekunde
   nimmt das JS die Klasse wieder weg — eine dauerhafte Markierung sieht aus
   wie ein Zustand, den man wegklicken muss.

   Farbe aus dem Akzent-Token, damit jede Gemeinde ihre eigene bekommt.
   Nur Farbe und Rahmen, keine Bewegung: das ist auch bei
   prefers-reduced-motion unproblematisch.
   ============================================================================ */
.anchor-flash {
  background-color: color-mix(in srgb, var(--accent) 14%, white) !important;
  box-shadow: inset 0 0 0 2px var(--accent);
  transition: background-color 0.4s ease, box-shadow 0.4s ease;
}

/* ============================================================================
   Tailwind-Bridge — Akzent-Klassen ans Token-System anbinden
   ----------------------------------------------------------------------------
   tailwind.css wird mit festen Hex-Werten aus tailwind.config.js gebaut
   (accent-500: #f97316 etc.). Ohne diesen Block wuerde ein Tenant-Override
   wie '.tenant-pfarrkirchen { --accent: #ea580c }' an den sichtbarsten
   Stellen verpuffen: H2-Block-Title-Underline (border-accent-500),
   Akzent-Buttons (bg-accent-500), Bottom-Nav-Active, Form-Focus-Rings.
   Diese Regeln ueberschreiben die kompilierten Hex mit var(--accent...),
   damit der Token-Override wirklich global durchschlaegt.

   Lade-Reihenfolge: tailwind.css (frueher) -> main.css (jetzt). Bei gleicher
   Spezifitaet (0,1,0) gewinnt die spaeter deklarierte Regel — also unsere.

   Stufung-Mapping (Tailwind-Step -> Token):
     50, 100         -> color-mix mit --accent (sehr helle Pastell-BGs)
     200, 300, 400   -> --accent-mute (mittel-helle Backgrounds)
     500             -> --accent (Default-Akzent)
     600, 700        -> --accent-strong (Hover-/Active-State)

   Wenn eine Gemeinde nur --accent + --accent-strong setzt, ziehen ALLE
   Stufen automatisch mit (color-mix berechnet die hellen Varianten).
   ============================================================================ */

/* Background — bg-accent-* */
.bg-accent-50          { background-color: color-mix(in srgb, var(--accent) 8%,  white); }
.bg-accent-100         { background-color: color-mix(in srgb, var(--accent) 18%, white); }
.bg-accent-500         { background-color: var(--accent); }
.bg-accent-600         { background-color: var(--accent-strong); }
.bg-accent-700         { background-color: color-mix(in srgb, var(--accent-strong) 88%, black); }

/* Text — text-accent-* */
.text-accent-500       { color: var(--accent); }
.text-accent-600       { color: var(--accent-strong); }
.text-accent-700       { color: var(--accent-strong); }
.text-accent-800       { color: color-mix(in srgb, var(--accent-strong) 80%, black); }

/* Border — border-accent-* */
.border-accent-200     { border-color: var(--accent-mute); }
.border-accent-300     { border-color: var(--accent-mute); }
.border-accent-500     { border-color: var(--accent); }
.border-accent-600     { border-color: var(--accent-strong); }

/* Hover-Varianten — gleiche Stufung, aber mit :hover */
.hover\:bg-accent-50:hover       { background-color: color-mix(in srgb, var(--accent) 8%,  white); }
.hover\:bg-accent-600:hover      { background-color: var(--accent-strong); }
.hover\:bg-accent-700:hover      { background-color: color-mix(in srgb, var(--accent-strong) 88%, black); }
.hover\:text-accent-500:hover    { color: var(--accent); }
.hover\:text-accent-600:hover    { color: var(--accent-strong); }
.hover\:text-accent-700:hover    { color: var(--accent-strong); }
.hover\:border-accent-200:hover  { border-color: var(--accent-mute); }
.hover\:border-accent-300:hover  { border-color: var(--accent-mute); }

/* Focus-Ring + Focus-Border — Form-Inputs (Tailwind nutzt --tw-ring-color
   als CSS-Variable, daher direkt setzen). */
.focus\:ring-accent-500:focus    { --tw-ring-color: var(--accent); }
.focus\:border-accent-500:focus  { border-color: var(--accent); }

/* App-Header: Sticky oben, faellt im nativen App-Shell (Android 15/16,
   edge-to-edge) hinter die transparente Statusleiste. padding-top haelt
   Wappen/Name unter der Statusleiste frei — mindestens 0.5rem (klassisch,
   iOS/Android ohne Notch melden env()=0), mehr wenn das Geraet eine
   Statusleisten-Inset meldet. Der weisse Header-Hintergrund fuellt so
   nahtlos bis zur Bildschirmoberkante. */
.app-header {
  padding-top: max(0.5rem, env(safe-area-inset-top));
}

/* Seiten OHNE App-Header (Portal-Landing, 404, 503): deren oberer Abstand
   muss die Statusleiste ebenso freihalten, sonst rutscht die Ueberschrift
   im iOS-Shell unter den blauen Streifen. Basis = pt-8 bzw. pt-10. */
.landing-top    { padding-top: calc(2rem + env(safe-area-inset-top, 0px)); }
.landing-top-lg { padding-top: calc(2.5rem + env(safe-area-inset-top, 0px)); }

/* ---------------------------------------------------------------------------
   Nativer App-Shell (Capacitor) — html.native-app wird von native-bridge.js
   gesetzt. Im Browser greift NICHTS davon.
   --------------------------------------------------------------------------- */

/* Statusleisten-Bereich in Markenblau fuellen — greift auf iOS (dort umfasst
   die Safe-Area die Systemleisten). ANDROID: env(safe-area-inset-*) meldet
   NUR den Display-Cutout, nicht Status-/Navigationsleiste — dieser Streifen
   bleibt dort 0 hoch. Die Leisten faerbt auf Android die native Seite
   (windowBackground/setStatusBarColor + adjustMarginsForEdgeToEdge=force,
   siehe mobile/android MainActivity + styles.xml). Im Emulator (API 30 + 36)
   nachgewiesen. Liegt UEBER allem (auch Fullscreen-Overlays, z-index 90);
   pointer-events:none — faengt keine Taps ab. */
html.native-app body::before {
  content: '';
  position: fixed;
  top: 0; left: 0; right: 0;
  height: env(safe-area-inset-top);
  background: var(--status-bar-bg);
  z-index: 200;
  pointer-events: none;
}

/* Overscroll-Stretch abschalten (Android 12+): beim Scroll-Anschlag dehnt
   die WebView sonst den GESAMTEN Viewport — inklusive der fixierten
   Bottom-Nav und des sticky Headers, die dabei sichtbar aus dem Bild
   wandern. `none` unterbindet den Effekt und die Pull-to-Refresh-Gestik;
   in der App will beides niemand (Inhalte laedt die App selbst nach).
   Nur nativ — im Browser bleibt Pull-to-Refresh erhalten. */
html.native-app,
html.native-app body {
  overscroll-behavior: none;
}

/* Fullscreen-Overlays (Suche, Menue, Login) liegen bei edge-to-edge unter
   den Systemleisten — ihr sticky Header bzw. Scroll-Ende braucht dieselbe
   Kompensation wie Seiten-Header und Bottom-Nav. */
html.native-app .overlay-header {
  padding-top: calc(0.75rem + env(safe-area-inset-top));
}
html.native-app .overlay-body {
  padding-bottom: env(safe-area-inset-bottom);
}

.nav-fixed {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 50;
  /* Horizontaler Abstand zu abgerundeten Bildschirmkanten (iOS + Android).
     Mindestens 17px (auch auf iPhone Portrait wo safe-area-inset = 0 meldet),
     mehr wenn das Geraet eine groessere Safe-Area meldet. */
  padding-left: max(17px, env(safe-area-inset-left));
  padding-right: max(17px, env(safe-area-inset-right));
  /* Safe-Area-Bottom: auf dem iPhone das Polster ueber dem Home-Indicator.
     Volle 34 pt wirkten als leerer Streifen unter den Tabs (User-Feedback
     11.09.2026) — 60 % reichen, der Indicator liegt in den unteren 8 pt.
     Android meldet hier 0 (Gestenleiste wird nativ per Margin ausgeglichen,
     adjustMarginsForEdgeToEdge=force), Desktop/Browser ebenfalls 0.
     Gleicher Wert in .overlay-panel.above-nav. */
  padding-bottom: var(--nav-safe-bottom);
}

.hide-scroll::-webkit-scrollbar { display: none; }
.hide-scroll { -ms-overflow-style: none; scrollbar-width: none; }

/* ---------------------------------------------------------------------------
   Formularfelder: gleiche Optik auf allen Systemen
   ---------------------------------------------------------------------------
   iOS rendert <select> und <input type="date|time|datetime-local"> nicht als
   normales Textfeld, sondern im Systemstil: das Auswahlfeld bleibt niedriger
   als die Textfelder daneben, das Datumsfeld nimmt eine Eigenbreite statt der
   Containerbreite und zentriert seinen Wert. Gemeldet am 18.09.2026 fuer
   /konto/daten (Geburtsdatum zu schmal, Dropdowns zu niedrig).

   appearance:none nimmt den Systemstil weg — danach greifen Padding, Rahmen
   und Schriftgroesse der Feldklasse wie bei jedem Textfeld. Den Pfeil des
   Auswahlfelds zeichnen wir selbst, als inline-SVG in einer data:-URI (kein
   externes Bild, DSGVO-Regel).

   Ausgenommen:
   - .text-center  = schmale Mengen-Auswahl in Buchungen; ein Pfeil am rechten
                     Rand waere dort nur im Weg, die bleibt nativ.
   - [multiple]    = Listenfeld, das ist kein Klappfeld.

   Der Hook .form-field haengt an den gemeinsamen $inputCls-Strings der
   Formular-Templates. Die Kombination select.form-field schlaegt Tailwinds
   .px-3 — sonst wuerde das Padding fuer den Pfeil wieder ueberschrieben.
   --------------------------------------------------------------------------- */
select.form-field:not(.text-center):not([multiple]),
.form-field[type="date"],
.form-field[type="time"],
.form-field[type="datetime-local"] {
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box;
  width: 100%;
  line-height: 1.25rem;                    /* text-sm; sonst rechnet iOS eigene Hoehen */
  min-height: calc(1.25rem + 1rem + 2px);  /* Zeile + py-2 + Rahmen */
  color: inherit;
}

select.form-field:not(.text-center):not([multiple]) {
  padding-right: 2rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%236b7280' stroke-width='1.8'%3E%3Cpath d='M6 8l4 4 4-4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.625rem center;
  background-size: 1rem 1rem;
}

/* Den Wert im Datums-/Zeitfeld rendert iOS in diesem Pseudo-Element: dort
   sitzt die Zentrierung, und ein leeres Feld faellt ohne Mindesthoehe
   zusammen. */
.form-field[type="date"]::-webkit-date-and-time-value,
.form-field[type="time"]::-webkit-date-and-time-value,
.form-field[type="datetime-local"]::-webkit-date-and-time-value {
  text-align: left;
  min-height: 1.25rem;
}

/* Gemeindename im Header-Prefix hervorheben. JSON liefert z.B.
   "Stadt <b>Pfarrkirchen</b>" — das <b> wird hier schwarz dargestellt,
   waehrend der Rest der Zeile grau bleibt. */
header p b { color: #000; font-weight: 600; }

/* User-Agent [hidden] wird von Tailwinds .flex / .grid / .block-Utilities
   ueberschrieben — deshalb hier explizit mit !important erzwingen.
   Filter-JS setzt element.hidden = true auf versteckten Items. */
[hidden] { display: none !important; }

/* ---- Fullscreen-Overlays (Suche / Inbox) ---- */
.overlay-panel {
  position: fixed; inset: 0; z-index: 90;
  background: white;
  display: flex; flex-direction: column;
  transform: translateY(12px);
  opacity: 0;
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.overlay-panel.open { transform: translateY(0); opacity: 1; }

/* Inbox-Variante: nicht fullscreen, sondern 80% Breite rechts angedockt
   wie ein klassisches Notification-Panel. Schatten zur visuellen Abgrenzung. */
#overlay-inbox.overlay-panel {
  left: auto;
  right: 0;
  width: 80vw;
  max-width: 480px;
  box-shadow: -8px 0 24px rgba(0, 0, 0, 0.15);
}

/* Overlay-Variante, die ueber der Bottom-Nav endet, damit die Nav
   sichtbar und bedienbar bleibt (z.B. Menue per "Mehr"-Tab schliessen).
   z-index unter .nav-fixed (50), damit die Nav-Buttons obenauf liegen.
   bottom = Nav-Content-Hoehe (3.5rem) + Nav-Safe-Area (vgl. .nav-fixed). */
.overlay-panel.above-nav {
  bottom: calc(3.5rem + var(--nav-safe-bottom));
  z-index: 40;
}
.overlay-header {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid #e5e7eb;
  position: sticky; top: 0;
  background: white; z-index: 1;
}
.overlay-body { flex: 1; overflow-y: auto; }

/* ---- Toggle-Switch (.gem-toggle) ----
   Eigene CSS-Implementierung statt Tailwind peer-checked:-Variants — der
   Tailwind-Build im Repo enthaelt diese Variants nicht (peer-checked: kommt
   beim Conditional-Rebuild offenbar nicht durch), deshalb fielen Subscriptions-
   Toggles visuell stumm aus. Markup:
     <label class="gem-toggle [is-locked]">
       <input type="checkbox" class="gem-toggle__input" [checked] [disabled]>
       <span class="gem-toggle__track"></span>
       <span class="gem-toggle__knob"></span>
     </label>
   Drei sichtbare Zustaende ueber den input:
     :checked         -> orange Track + Knob nach rechts
     :indeterminate   -> mattes Orange + Knob in der Mitte (Master-Mixed)
     is-locked        -> ausgegraut, Track in mattem Orange, Knob rechts
*/
.gem-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  user-select: none;
}
.gem-toggle__input {
  position: absolute;
  width: 1px; height: 1px;
  margin: 0; padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.gem-toggle__track {
  display: inline-block;
  width: 2.5rem; height: 1.25rem;
  background: var(--surface-toggle-off);
  border-radius: var(--radius-pill);
  transition: background-color 0.15s;
}
.gem-toggle__knob {
  position: absolute;
  left: 0.125rem; top: 0.125rem;
  width: 1rem; height: 1rem;
  background: #fff;
  border-radius: var(--radius-pill);
  pointer-events: none;
  transition: transform 0.15s;
}
.gem-toggle__input:checked ~ .gem-toggle__track   { background: var(--accent); }
.gem-toggle__input:checked ~ .gem-toggle__knob    { transform: translateX(1.25rem); }
.gem-toggle__input:indeterminate ~ .gem-toggle__track { background: var(--accent-mute); }
.gem-toggle__input:indeterminate ~ .gem-toggle__knob  { transform: translateX(0.625rem); }
.gem-toggle.is-locked { cursor: not-allowed; opacity: 0.65; }
.gem-toggle.is-locked .gem-toggle__track { background: var(--accent-mute); }
.gem-toggle.is-locked .gem-toggle__knob  { transform: translateX(1.25rem); }
.gem-toggle__input:focus-visible ~ .gem-toggle__track {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---- Status-Farben fuer Oeffnungszeiten / Verfuegbarkeit / etc. ----
   Werden von Templates via class="status-{state}" bzw. "status-dot-{state}"
   angewendet. Pro Gemeinde in assets/communities/<gemeinde>/main.css
   ueberschreibbar. */
.status-open      { color: var(--status-open); }
.status-closed    { color: var(--status-closed); }
.status-always    { color: var(--status-open); }
.status-info      { color: var(--status-info); }
.status-warning   { color: var(--status-warning); }
.status-muted     { color: var(--status-muted); }

.status-dot-open      { background-color: var(--status-open); }
.status-dot-closed    { background-color: var(--status-closed); }
.status-dot-always    { background-color: var(--status-open); }
.status-dot-info      { background-color: var(--status-info); }
.status-dot-warning   { background-color: var(--status-warning); }
.status-dot-muted     { background-color: var(--status-muted); }

/* ---- Wichtig-Box Farbschema (aus --waste-color abgeleitet) ----
   Home-Banner faerbt sich aus der CSS-Variable --waste-color, die das
   Template per inline-style setzt (Hex aus DB). Pastell-Varianten
   (Banner-BG, Border, Icon-BG, Label-Color, Arrow) entstehen per
   color-mix(). Fehlt die Variable, wird Orange als Default verwendet. */

.wichtig-box {
  /* Default = Akzent. Templates koennen pro Banner ueber inline-style
     "--waste-color: #xxx" einen anderen Hex setzen (waste-type-Farbe). */
  --waste-color: var(--accent-strong);
  background-color: color-mix(in srgb, var(--waste-color) 10%, white);
  border: 1px solid color-mix(in srgb, var(--waste-color) 22%, white);
}
.wichtig-box .wichtig-icon  {
  background-color: color-mix(in srgb, var(--waste-color) 28%, white);
  color: color-mix(in srgb, var(--waste-color) 85%, black);
}
.wichtig-box .wichtig-label { color: color-mix(in srgb, var(--waste-color) 80%, black); }
.wichtig-box .wichtig-arrow { color: color-mix(in srgb, var(--waste-color) 55%, white); }

/* ---- Umfrage-Balken Fade-In von links ---- */
@keyframes poll-bar-grow {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
.poll-bar {
  transform-origin: left center;
  animation: poll-bar-grow 700ms cubic-bezier(.22,.61,.36,1);
}
.poll-bar.poll-bar--no-anim { animation: none; }

/* ---- Polyline-Endpunkte auf Leaflet-Karten ----
   WICHTIG: Map-Pins werden vom Backend pro Track gesteuert (items[*].color
   im tracks.json — Redaktion setzt die Farbe pro Wanderweg/Route). Wenn
   das JSON eine Farbe liefert, uebersteuert map.js diese Default-CSS-Regel
   inline. Diese Regel hier greift NUR als Fallback wenn die JSON-color
   fehlt. Daher absichtlich NICHT an --accent gekoppelt — sonst wuerde
   ein Gemeinde-Theme-Override die fachlich zugeordnete Track-Farbe
   verfaelschen. Wenn du die Default-Pin-Farbe global aendern willst,
   hier den Hex anpassen, NICHT die Theme-Tokens. */
.gem-map-endpoint {
  width: 26px; height: 26px;
  background: #ea580c;
  color: #ffffff;
  border-radius: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: 'Inter', system-ui, sans-serif;
  font-weight: 700; font-size: 13px;
  box-shadow: 0 1px 4px rgba(0,0,0,0.25);
}

/* ---- Lightbox (Galerie-Viewer) ---- */
.gem-lightbox {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(0,0,0,0.92);
  display: none;
  align-items: center; justify-content: center;
  user-select: none;
}
.gem-lightbox.open { display: flex; }
.gem-lightbox-stage {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.gem-lightbox-img {
  max-width: 100%; max-height: 100%;
  object-fit: contain;
  transition: transform 0.15s ease-out;
  transform-origin: center center;
  touch-action: none;
  cursor: zoom-in;
}
.gem-lightbox-close,
.gem-lightbox-prev,
.gem-lightbox-next {
  position: absolute;
  background: rgba(255,255,255,0.12);
  color: white; border: 0;
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  font-size: 24px;
  cursor: pointer;
  z-index: 101;
}
.gem-lightbox-close { top: 12px; right: 12px; }
.gem-lightbox-prev  { left: 8px; top: 50%; transform: translateY(-50%); }
.gem-lightbox-next  { right: 8px; top: 50%; transform: translateY(-50%); }
.gem-lightbox-counter {
  position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%);
  color: white; font-size: 13px; font-family: Inter, system-ui, sans-serif;
  background: rgba(255,255,255,0.12);
  padding: 4px 10px;
}

/* ---- Galerie-Swiper: Klick-Pfeile ----
   Der Swiper (templates/elements/gallery-swiper.php) laesst sich per Touch
   wischen, mit der Maus aber nicht ziehen — die Pfeile sind dort neben den
   Dots die einzige Navigation. Bewusst handgeschriebenes CSS statt Tailwind:
   tailwind.css ist vorgebaut, neue Utility-Klassen in Templates landen erst
   mit dem naechsten Build im Stylesheet. */
.gem-swiper-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  border: 0;
  background: rgba(255,255,255,0.85);
  color: #374151;
  cursor: pointer;
  transition: opacity .15s, background-color .15s;
}
.gem-swiper-nav:hover     { background: #ffffff; }
.gem-swiper-nav svg       { width: 20px; height: 20px; }
.gem-swiper-prev          { left: 0; }
.gem-swiper-next          { right: 0; }
/* Am Anfang/Ende ausblenden statt entfernen — kein Layout-Sprung. */
.gem-swiper-nav[disabled] { opacity: 0; pointer-events: none; }
/* Touch-Geraete: dort ist Wischen die Geste, Pfeile wuerden nur Bild verdecken. */
@media (hover: none) and (pointer: coarse) {
  .gem-swiper-nav { display: none; }
}

/* ---- Hero-Overlay: Boxen im Bild mit 20% Transparenz ----
   Nur der Hintergrund wird durchscheinend (Bild schimmert durch),
   Text/Icons bleiben voll deckend. Aeusseres color-mix mischt die
   fertige Pastellfarbe mit 20% transparent. */
.hero-overlay .wichtig-box {
  background-color: color-mix(in srgb,
    color-mix(in srgb, var(--waste-color) 10%, white) 80%, transparent);
}
.hero-overlay .wichtig-box.alert-important {
  background-color: rgba(254, 242, 242, 0.8);
}
/* Die Kacheln im Hero brechen ihren Text nicht um: Die Box wird so breit,
   wie der Text braucht, und schiebt sich nach links unter den Pfeil —
   statt dass "Gelber Sack" in zwei Zeilen faellt und die Box springt.
   Laenger als die 85 % des Stapels darf sie nicht werden; dann wird
   mit Punkten gekuerzt. */
.hero-overlay .wichtig-box p {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---- Wichtige News/Alerts ---- */
.wichtig-box.alert-important          { background-color: #fef2f2; border-color: #fecaca; }
.wichtig-box.alert-important .wichtig-icon  { background-color: #fecaca; color: #991b1b; }
.wichtig-box.alert-important .wichtig-label { color: #dc2626; }
.wichtig-box.alert-important .wichtig-arrow { color: #fca5a5; }

/* ---- Wetter-Effekte (Hero-Bild + Wichtig-Fallback-Box) ----
   Der Container bekommt .weather-fx + .weather-fx-<bright-sky-key> (via
   weatherFxClass() in hero.php / wichtig.php), darin liegt ein leerer
   <span class="weather-fx-layer">. Dessen ::before/::after tragen die
   Effekte als reine CSS-Gradients + Keyframes — kein JS, keine Bilder.

   Zwei Kontexte:
   - .hero-weather (hero.php): Schicht liegt ueber dem ganzen Hero-Bild,
     die Overlay-Boxen bleiben darueber (absolute, spaeter im DOM).
     Helle Partikel, groessere Wolken (--wfx-scale), Nacht dunkelt das
     Foto ab.
   - .wichtig-box (wichtig.php ohne Hero): Schicht hinter dem Inhalt,
     dunklere Partikel auf dem hellen Pastell-Grund.

   Keys: clear-day, clear-night, partly-cloudy-day/-night, cloudy, fog,
   wind, rain, sleet, snow, hail, thunderstorm (unbekannt = kein Effekt).
   Gewitter blitzt 2x pro 7 s (WCAG 2.3.1: unter 3 Blitzen/s).
   prefers-reduced-motion: Effekte bleiben als Standbild, ohne Animation. */

.weather-fx {
  position: relative; overflow: hidden; isolation: isolate;
  /* Farben fuer die Pastell-Box (Default) */
  --wfx-scale:      1;
  --wfx-rain:       rgba(59, 130, 246, .6);
  --wfx-flake:      rgba(100, 116, 139, .6);
  --wfx-hail:       rgba(148, 163, 184, .9);
  --wfx-fog:        rgba(148, 163, 184, .6);
  --wfx-cloud:      rgba(255, 255, 255, .95);
  --wfx-cloud-edge: rgba(148, 163, 184, .35);
  --wfx-cloud-back: rgba(100, 116, 139, .4);
  --wfx-wind:       rgba(100, 116, 139, .55);
  --wfx-night:      rgba(30, 41, 59, .16);
  --wfx-flash:      rgba(253, 224, 71, .55);
}
.hero-weather.weather-fx {
  /* Container-Query-Einheiten (cqw) fuer die Laufwege der Figuren:
     damit koennen Wolken/Blaetter per transform bis zur Hero-Breite
     laufen (subpixelgenau, GPU) statt ueber `left` (rastet auf ganze
     Pixel, ruckelt bei langsamen Wolken sichtbar). */
  container-type: inline-size;
  /* Farben ueber dem Foto: hell, damit sie auf Landschaft/Himmel lesbar sind */
  --wfx-scale:      1.8;
  --wfx-rain:       rgba(219, 234, 254, .8);
  --wfx-flake:      rgba(255, 255, 255, .9);
  --wfx-hail:       rgba(255, 255, 255, .95);
  --wfx-fog:        rgba(255, 255, 255, .6);
  --wfx-cloud:      rgba(255, 255, 255, .55);
  --wfx-cloud-edge: rgba(255, 255, 255, .2);
  --wfx-cloud-back: rgba(226, 232, 240, .3);
  --wfx-wind:       rgba(255, 255, 255, .75);
  --wfx-night:      rgba(15, 23, 42, .38);
  --wfx-flash:      rgba(255, 255, 255, .7);
}
.weather-fx-layer {
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none; overflow: hidden;
}
.weather-fx-layer::before,
.weather-fx-layer::after {
  content: ""; position: absolute; inset: 0;
}
/* Box-Kontext: statischer Inhalt (Icon, Text, Pfeil) ueber die Schicht heben. */
.wichtig-box.weather-fx > :not(.weather-fx-layer) { position: relative; z-index: 1; }

/* NAHTLOSE LOOPS: jede Endlos-Animation verschiebt exakt ein
   Vielfaches ihrer Kachel (background-size) — sonst springt das Bild
   beim Neustart sichtbar zurueck. Schichten mit anderer Kachelgroesse
   brauchen deshalb eigene Keyframes. */

/* Regen: Tropfen als schmale senkrechte Striche (Ellipse 1x5px in einer
   16x28-Kachel), zwei Schichten mit unterschiedlicher Groesse/Tempo fuer
   Tiefe. Die Ebene ist um 12deg gedreht und ueberdimensioniert, damit
   die Striche schraeg fallen. */
.weather-fx-rain .weather-fx-layer::before,
.weather-fx-rain .weather-fx-layer::after,
.weather-fx-sleet .weather-fx-layer::before,
.weather-fx-thunderstorm .weather-fx-layer::before {
  inset: -60% -30%;
  transform: rotate(12deg);
  background-image: radial-gradient(1px 5px at 50% 40%,
    var(--wfx-rain) 0 90%, transparent 100%);
  background-size: 16px 28px;
  animation: weather-fx-fall-28 0.65s linear infinite;
}
.weather-fx-rain .weather-fx-layer::after {
  background-size: 22px 40px;
  background-position: 9px 0;
  opacity: .55;
  animation: weather-fx-fall-40 1.3s linear infinite;
}
@keyframes weather-fx-fall-28 {
  from { background-position-y: 0; }
  to   { background-position-y: 28px; }
}
@keyframes weather-fx-fall-40 {
  from { background-position: 9px 0; }
  to   { background-position: 9px 40px; }
}

/* Schnee: Flocken unregelmaessig in einer 96x96-Kachel verteilt (kein
   sichtbares Raster), fallen langsam mit leichtem Seitendrift (1:3).
   Loop = eine Kachelbreite seitlich, drei Kachelhoehen senkrecht.
   Zweite Schicht: 128er-Kachel, groessere Flocken, langsamer. */
.weather-fx-snow .weather-fx-layer::before,
.weather-fx-snow .weather-fx-layer::after,
.weather-fx-sleet .weather-fx-layer::after {
  background-image:
    radial-gradient(circle at 12px  8px, var(--wfx-flake) 0 1.6px, transparent 2.4px),
    radial-gradient(circle at 50px 20px, var(--wfx-flake) 0 1.2px, transparent 2px),
    radial-gradient(circle at 80px 40px, var(--wfx-flake) 0 1.8px, transparent 2.6px),
    radial-gradient(circle at 30px 55px, var(--wfx-flake) 0 1.4px, transparent 2.2px),
    radial-gradient(circle at 66px 70px, var(--wfx-flake) 0 1.6px, transparent 2.4px),
    radial-gradient(circle at  8px 82px, var(--wfx-flake) 0 1.2px, transparent 2px),
    radial-gradient(circle at 90px 90px, var(--wfx-flake) 0 1.4px, transparent 2.2px);
  background-size: 96px 96px;
  animation: weather-fx-snow-96 24s linear infinite;
}
.weather-fx-snow .weather-fx-layer::after {
  background-image:
    radial-gradient(circle at  20px  14px, var(--wfx-flake) 0 2.2px, transparent 3.2px),
    radial-gradient(circle at  74px  30px, var(--wfx-flake) 0 1.8px, transparent 2.8px),
    radial-gradient(circle at 110px  62px, var(--wfx-flake) 0 2.4px, transparent 3.4px),
    radial-gradient(circle at  44px  84px, var(--wfx-flake) 0 2px,   transparent 3px),
    radial-gradient(circle at  92px 110px, var(--wfx-flake) 0 1.8px, transparent 2.8px),
    radial-gradient(circle at  10px 120px, var(--wfx-flake) 0 2.2px, transparent 3.2px);
  background-size: 128px 128px;
  opacity: .7;
  animation: weather-fx-snow-128 40s linear infinite;
}
.weather-fx-sleet .weather-fx-layer::after { opacity: .7; }
@keyframes weather-fx-snow-96 {
  from { background-position: 0 0; }
  to   { background-position: -96px 288px; }
}
@keyframes weather-fx-snow-128 {
  from { background-position: 0 0; }
  to   { background-position: -128px 384px; }
}

/* Hagel: groessere Koerner, unregelmaessig verteilt, fallen schnell
   und gerade (Loop = eine Kachelhoehe). */
.weather-fx-hail .weather-fx-layer::before,
.weather-fx-hail .weather-fx-layer::after {
  background-image:
    radial-gradient(circle at 10px 10px, var(--wfx-hail) 0 1.8px, transparent 2.8px),
    radial-gradient(circle at 46px 30px, var(--wfx-hail) 0 1.5px, transparent 2.5px),
    radial-gradient(circle at 76px 56px, var(--wfx-hail) 0 2px,   transparent 3px),
    radial-gradient(circle at 26px 74px, var(--wfx-hail) 0 1.6px, transparent 2.6px),
    radial-gradient(circle at 62px 88px, var(--wfx-hail) 0 1.8px, transparent 2.8px);
  background-size: 96px 96px;
  animation: weather-fx-hail-96 1.4s linear infinite;
}
.weather-fx-hail .weather-fx-layer::after {
  background-position: 48px 40px;
  opacity: .6;
  animation: weather-fx-hail-96b 1.9s linear infinite;
}
@keyframes weather-fx-hail-96 {
  from { background-position: 0 0; }
  to   { background-position: 0 96px; }
}
@keyframes weather-fx-hail-96b {
  from { background-position: 48px 40px; }
  to   { background-position: 48px 136px; }
}

/* Nebel: zwei breite, weiche Schwaden, die gegenlaeufig hin- und herziehen. */
.weather-fx-fog .weather-fx-layer::before,
.weather-fx-fog .weather-fx-layer::after {
  inset: 0 -50%;
  background-image: linear-gradient(90deg,
    transparent 0, var(--wfx-fog) 35%, var(--wfx-fog) 65%, transparent 100%);
  background-size: 60% 100%;
  background-repeat: repeat-x;
  animation: weather-fx-drift 9s ease-in-out infinite alternate;
}
.weather-fx-fog .weather-fx-layer::after {
  inset: 45% -50% 0;
  opacity: .8;
  background-position: 30% 0;
  animation-duration: 12s;
  animation-direction: alternate-reverse;
}
@keyframes weather-fx-drift {
  from { transform: translateX(-12%); }
  to   { transform: translateX(12%); }
}
/* Nebel im Hero: nur ein Effekt — das Foto selbst verliert langsam
   Kontrast (1 -> 0.4 und zurueck, 4,5 s je Richtung), wie ausbleichender
   Dunst. Keine Schwaden, kein Weichzeichner, kein Schleier; die
   Nebelbaender bleiben der Box-Variante vorbehalten. */
.hero-weather.weather-fx-fog .weather-fx-layer::before,
.hero-weather.weather-fx-fog .weather-fx-layer::after {
  background-image: none; animation: none;
}
.hero-weather.weather-fx-fog > img {
  animation: weather-fx-fog-contrast 4.5s ease-in-out infinite alternate;
}
@keyframes weather-fx-fog-contrast {
  from { filter: contrast(1); }
  to   { filter: contrast(.4); }
}

/* Wolken: weiche Wolkenballen (radial-gradients, Groesse ueber
   --wfx-scale), die langsam von links nach rechts ziehen. "Bewoelkt"
   hat zwei Schichten, "teils bewoelkt" eine plus Sonne bzw. Mond. */
.weather-fx-cloudy .weather-fx-layer::before,
.weather-fx-cloudy .weather-fx-layer::after,
.weather-fx-partly-cloudy-day .weather-fx-layer::after,
.weather-fx-partly-cloudy-night .weather-fx-layer::after {
  inset: 0 -100% 0 0;
  background-image:
    radial-gradient(ellipse calc(48px * var(--wfx-scale)) calc(16px * var(--wfx-scale)) at 22% 26%,
      var(--wfx-cloud) 30%, var(--wfx-cloud-edge) 65%, transparent 80%),
    radial-gradient(ellipse calc(30px * var(--wfx-scale)) calc(12px * var(--wfx-scale)) at 32% 18%,
      var(--wfx-cloud) 30%, var(--wfx-cloud-edge) 65%, transparent 80%),
    radial-gradient(ellipse calc(60px * var(--wfx-scale)) calc(18px * var(--wfx-scale)) at 70% 74%,
      var(--wfx-cloud) 30%, var(--wfx-cloud-edge) 65%, transparent 80%),
    radial-gradient(ellipse calc(38px * var(--wfx-scale)) calc(14px * var(--wfx-scale)) at 82% 64%,
      var(--wfx-cloud) 30%, var(--wfx-cloud-edge) 65%, transparent 80%);
  background-size: 50% 100%;
  background-repeat: repeat-x;
  animation: weather-fx-clouds 28s linear infinite;
}
.weather-fx-cloudy .weather-fx-layer::after {
  background-image:
    radial-gradient(ellipse calc(54px * var(--wfx-scale)) calc(18px * var(--wfx-scale)) at 50% 50%,
      var(--wfx-cloud-back) 30%, transparent 75%),
    radial-gradient(ellipse calc(36px * var(--wfx-scale)) calc(14px * var(--wfx-scale)) at 8% 82%,
      var(--wfx-cloud-back) 30%, transparent 75%);
  animation-duration: 40s;
}
@keyframes weather-fx-clouds {
  from { transform: translateX(-50%); }
  to   { transform: translateX(0); }
}

/* Sonne: warmer Lichtschein oben rechts, der leicht atmet; bei klarem
   Himmel zusaetzlich blasse, langsam rotierende Strahlen. */
.weather-fx-clear-day .weather-fx-layer::before,
.weather-fx-partly-cloudy-day .weather-fx-layer::before {
  background-image:
    radial-gradient(circle at 88% 10%, rgba(251, 191, 36, .55), rgba(251, 191, 36, .2) 28%, transparent 60%);
  animation: weather-fx-glow 4s ease-in-out infinite alternate;
}
/* Strahlen: quadratische 1400px-Box, deren Mitte exakt auf der Sonne
   (88 % / 10 %) liegt — die Drehung laeuft damit um die Sonne, und jeder
   Strahl faechert vom Sonnenpunkt aus auf. Vorher lag das Zentrum rechts
   ausserhalb des Bildes, und ein Strahl wurde beim Drehen zum breiten
   Keil quer ueber das Bild. Weiche Strahlkanten (Farbverlauf statt
   harter Stops). Gilt fuer klaren Himmel und "teils bewoelkt" im Hero;
   background-size/-repeat zuruecksetzen, weil die Gradient-Wolken-Regel
   der Box-Variante sie fuer teils bewoelkt auf 50 %/repeat-x stellt. */
.weather-fx-clear-day .weather-fx-layer::after,
.hero-weather.weather-fx-partly-cloudy-day .weather-fx-layer::after {
  inset: auto;
  width: 1400px; height: 1400px;
  left: calc(88% - 700px); top: calc(10% - 700px);
  background-image: repeating-conic-gradient(from 0deg at 50% 50%,
    transparent 0deg, rgba(251, 191, 36, .14) 3deg, rgba(251, 191, 36, .14) 6deg, transparent 9deg, transparent 24deg);
  background-size: auto; background-repeat: no-repeat;
  animation: weather-fx-spin 60s linear infinite;
}
@keyframes weather-fx-glow {
  from { opacity: .75; }
  to   { opacity: 1; }
}
@keyframes weather-fx-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* Nacht: dunkelblauer Schleier, Mondschein oben rechts und Sterne, die
   in zwei Gruppen versetzt funkeln. */
.weather-fx-clear-night .weather-fx-layer,
.weather-fx-partly-cloudy-night .weather-fx-layer {
  background-color: var(--wfx-night);
}
.weather-fx-clear-night .weather-fx-layer::before,
.weather-fx-partly-cloudy-night .weather-fx-layer::before {
  background-image:
    radial-gradient(circle at 88% 14%, rgba(226, 232, 240, .7), rgba(199, 210, 254, .25) 22%, transparent 48%),
    radial-gradient(circle at 30% 30%, rgba(255, 255, 255, .9) 0 1px, transparent 1.8px),
    radial-gradient(circle at 55% 70%, rgba(255, 255, 255, .9) 0 1px, transparent 1.8px),
    radial-gradient(circle at 70% 25%, rgba(255, 255, 255, .9) 0 1px, transparent 1.8px),
    radial-gradient(circle at 42% 18%, rgba(255, 255, 255, .8) 0 .8px, transparent 1.5px);
  animation: weather-fx-twinkle 3s ease-in-out infinite alternate;
}
.weather-fx-clear-night .weather-fx-layer::after {
  background-image:
    radial-gradient(circle at 20% 65%, rgba(255, 255, 255, .9) 0 1px, transparent 1.8px),
    radial-gradient(circle at 62% 45%, rgba(255, 255, 255, .9) 0 1px, transparent 1.8px),
    radial-gradient(circle at 78% 78%, rgba(255, 255, 255, .8) 0 .8px, transparent 1.5px),
    radial-gradient(circle at 48% 85%, rgba(255, 255, 255, .8) 0 .8px, transparent 1.5px),
    radial-gradient(circle at 12% 22%, rgba(255, 255, 255, .8) 0 .8px, transparent 1.5px),
    radial-gradient(circle at 36% 48%, rgba(255, 255, 255, .8) 0 .8px, transparent 1.5px);
  animation: weather-fx-twinkle 3s ease-in-out infinite alternate-reverse;
}
@keyframes weather-fx-twinkle {
  from { opacity: .35; }
  to   { opacity: 1; }
}

/* Wind: kurze waagrechte Boeen in Zeilen, ziehen zuegig nach rechts
   (Verschiebung = Kachelbreite, deshalb eigene Keyframes je Schicht). */
.weather-fx-wind .weather-fx-layer::before,
.weather-fx-wind .weather-fx-layer::after {
  background-image: radial-gradient(22px 0.8px at 50% 50%,
    var(--wfx-wind) 0 90%, transparent 100%);
  background-size: 90px 26px;
  opacity: .7;
  animation: weather-fx-gust-90 0.8s linear infinite;
}
.weather-fx-wind .weather-fx-layer::after {
  background-image: radial-gradient(34px 0.8px at 50% 50%,
    var(--wfx-wind) 0 90%, transparent 100%);
  background-size: 140px 38px;
  opacity: .45;
  animation: weather-fx-gust-140 1.5s linear infinite;
}
@keyframes weather-fx-gust-90 {
  from { background-position: 0 0; }
  to   { background-position: 90px 0; }
}
@keyframes weather-fx-gust-140 {
  from { background-position: 40px 13px; }
  to   { background-position: 180px 13px; }
}
/* Wind im Hero: vier Blaetter (Inline-SVG aus hero.php) treiben in
   Wellen von links nach rechts, drehen sich dabei und laufen zeitlich
   versetzt. Der Weg (560px) ist laenger als die Hero-Breite, der Rest
   wird von overflow:hidden geschluckt. Farben wie die Lottie-Vorlage. */
.weather-fx-leaf {
  position: absolute; left: -32px; width: 20px; height: 20px;
  opacity: .95;
  animation: weather-fx-leaf 5.2s linear infinite;
}
.weather-fx-leaf-1 { top: 22%; color: #99d0b3; width: 22px; height: 22px; }
.weather-fx-leaf-2 { top: 52%; color: #ef7a8e; width: 24px; height: 24px; animation: weather-fx-leaf-loop 7s linear infinite; animation-delay: -2.1s; }
.weather-fx-leaf-3 { top: 36%; color: #ffe6a4; width: 16px; height: 16px; animation-duration: 4.6s; animation-delay: -3.4s; }
.weather-fx-leaf-4 { top: 70%; color: #99d0b3; width: 20px; height: 20px; animation: weather-fx-leaf-loop 8s linear infinite; animation-delay: -5.2s; }
/* Blaetter 2 und 4 schlagen mitten im Flug einen Looping: zwischen 36 %
   und 64 % des Wegs eine Kreisbahn (Radius 30px, Kreis nach oben) mit
   einer vollen, tangential mitlaufenden Drehung, waehrend `left`
   gleichmaessig weiterlaeuft. Acht Stuetzpunkte auf dem Kreis, damit
   die Bahn rund und die Drehung fluessig ist. */
@keyframes weather-fx-leaf-loop {
  0%    { left: -32px; transform: translate(0, 0) rotate(0deg); }
  18%   { transform: translate(0, -12px) rotate(25deg); }
  36%   { transform: translate(0, 0) rotate(0deg); }
  39.5% { transform: translate(21px, -9px) rotate(45deg); }
  43%   { transform: translate(30px, -30px) rotate(90deg); }
  46.5% { transform: translate(21px, -51px) rotate(135deg); }
  50%   { transform: translate(0, -60px) rotate(180deg); }
  53.5% { transform: translate(-21px, -51px) rotate(225deg); }
  57%   { transform: translate(-30px, -30px) rotate(270deg); }
  60.5% { transform: translate(-21px, -9px) rotate(315deg); }
  64%   { transform: translate(0, 0) rotate(360deg); }
  82%   { transform: translate(0, -14px) rotate(390deg); }
  100%  { left: 100%; transform: translate(0, 4px) rotate(360deg); }
}
/* Laufweg ueber `left` von -32px bis 100 % der Hero-Breite: das Blatt
   ist damit auf jeder Bildbreite komplett draussen, bevor der Loop neu
   startet (feste px-Wege brachen auf breiten Heros vorher ab). */
@keyframes weather-fx-leaf {
  0%   { left: -32px; transform: translateY(0) rotate(0deg); }
  25%  { transform: translateY(-16px) rotate(50deg); }
  50%  { transform: translateY(8px) rotate(-25deg); }
  75%  { transform: translateY(-12px) rotate(35deg); }
  100% { left: 100%; transform: translateY(6px) rotate(0deg); }
}

/* Wolken im Hero (Inline-SVG aus hero.php, gleiches Strich-Design wie
   die Blaetter): vier Ebenen — vorne gross, deckend und schnell, hinten
   klein, blass und langsam. Jede Wolke startet links ausserhalb und
   laeuft rechts hinaus (820px > Hero-Breite), der Neustart ist damit
   unsichtbar. Negative Delays verteilen sie beim ersten Anzeigen ueber
   das Bild. Im Hero ersetzen sie die Gradient-Wolken der Box-Variante. */
.hero-weather.weather-fx-cloudy .weather-fx-layer::before,
.hero-weather.weather-fx-cloudy .weather-fx-layer::after,
.hero-weather.weather-fx-partly-cloudy-night .weather-fx-layer::after {
  background-image: none; animation: none;
}
/* Teils bewoelkt im Hero: Sonne genau wie bei klarem Himmel (Schein in
   ::before + rotierende Strahlen in ::after, Regel oben bei "Sonne"),
   die Wolken kommen als SVG dazu. */
.weather-fx-cloud {
  position: absolute; left: -170px;
  fill: rgba(255, 255, 255, .92); color: #cfe3f0;
  will-change: translate, transform;
  animation: weather-fx-cloud linear infinite;
}
/* Zwei Animationen je Wolke: `left` fuer den Zug nach rechts, dazu ein
   sanftes Auf und Ab per translateY (je Ebene anderes Tempo). */
.weather-fx-cloud-1 { top: 8%;  width: 150px; opacity: .95; animation: weather-fx-cloud 16s  linear infinite -4s,  weather-fx-cloud-bob 7s   ease-in-out infinite alternate; }
.weather-fx-cloud-2 { top: 34%; width: 100px; opacity: .75; animation: weather-fx-cloud 48s  linear infinite -30s, weather-fx-cloud-bob 11s  ease-in-out infinite alternate -3s; }
.weather-fx-cloud-3 { top: 20%; width: 70px;  opacity: .55; animation: weather-fx-cloud 110s linear infinite -70s, weather-fx-cloud-bob 15s  ease-in-out infinite alternate -8s; }
.weather-fx-cloud-4 { top: 48%; width: 120px; opacity: .85; animation: weather-fx-cloud 28s  linear infinite -12s, weather-fx-cloud-bob 9s   ease-in-out infinite alternate -5s; }
@keyframes weather-fx-cloud {
  from { left: -170px; }
  to   { left: 100%; }
}
@keyframes weather-fx-cloud-bob {
  from { transform: translateY(-12px); }
  to   { transform: translateY(12px); }
}

/* Fluessige Bewegung: in Browsern mit Container-Query-Einheiten laufen
   Wolken und Blaetter ueber die Einzel-Eigenschaft `translate` (Zug
   nach rechts, subpixelgenau auf der GPU) statt ueber `left`; `transform`
   bleibt frei fuer Wippen, Looping und Drehung. Die Keyframes hier
   ueberschreiben die `left`-Varianten oben (spaetere Definition gewinnt).
   Ohne cqw-Support bleibt die `left`-Version als Fallback aktiv. */
@supports (width: 1cqw) {
  @keyframes weather-fx-cloud {
    from { translate: 0 0; }
    to   { translate: calc(100cqw + 170px) 0; }
  }
  @keyframes weather-fx-leaf {
    0%   { translate: 0 0; transform: translateY(0) rotate(0deg); }
    25%  { transform: translateY(-16px) rotate(50deg); }
    50%  { transform: translateY(8px) rotate(-25deg); }
    75%  { transform: translateY(-12px) rotate(35deg); }
    100% { translate: calc(100cqw + 32px) 0; transform: translateY(6px) rotate(0deg); }
  }
  @keyframes weather-fx-leaf-loop {
    0%    { translate: 0 0; transform: translate(0, 0) rotate(0deg); }
    18%   { transform: translate(0, -12px) rotate(25deg); }
    36%   { transform: translate(0, 0) rotate(0deg); }
    39.5% { transform: translate(21px, -9px) rotate(45deg); }
    43%   { transform: translate(30px, -30px) rotate(90deg); }
    46.5% { transform: translate(21px, -51px) rotate(135deg); }
    50%   { transform: translate(0, -60px) rotate(180deg); }
    53.5% { transform: translate(-21px, -51px) rotate(225deg); }
    57%   { transform: translate(-30px, -30px) rotate(270deg); }
    60.5% { transform: translate(-21px, -9px) rotate(315deg); }
    64%   { transform: translate(0, 0) rotate(360deg); }
    82%   { transform: translate(0, -14px) rotate(390deg); }
    100%  { translate: calc(100cqw + 32px) 0; transform: translate(0, 4px) rotate(360deg); }
  }
}

/* Wind im Hero: nur die Blaetter, keine Boeen-Striche (die bleiben der
   Box-Variante vorbehalten). */
.hero-weather.weather-fx-wind .weather-fx-layer::before,
.hero-weather.weather-fx-wind .weather-fx-layer::after {
  background-image: none; animation: none;
}

/* Regen im Hero: sieben Tropfen (Inline-SVG aus hero.php) statt der
   Gradient-Striche — verteilt ueber die Breite, verschiedene Groessen,
   Tempi und Startzeiten; fallen von oben ausserhalb bis unter den
   Bildrand, der Neustart ist damit unsichtbar. Gilt auch fuer
   Schneeregen (plus Flocken) und Gewitter (plus Blitz). */
.hero-weather.weather-fx-rain .weather-fx-layer::before,
.hero-weather.weather-fx-rain .weather-fx-layer::after,
.hero-weather.weather-fx-sleet .weather-fx-layer::before,
.hero-weather.weather-fx-thunderstorm .weather-fx-layer::before,
.hero-weather.weather-fx-hail .weather-fx-layer::before,
.hero-weather.weather-fx-hail .weather-fx-layer::after {
  background-image: none; animation: none;
}
.weather-fx-drop {
  position: absolute; top: -36px; width: 14px; height: 19px;
  animation: weather-fx-drop 1.8s ease-in infinite;
}
.weather-fx-drop-1 { left: 8%;  animation-delay: -0.3s; }
.weather-fx-drop-2 { left: 21%; width: 11px; height: 15px; animation-duration: 2.2s; animation-delay: -1.4s; }
.weather-fx-drop-3 { left: 34%; width: 16px; height: 21px; animation-duration: 1.6s; animation-delay: -0.9s; }
.weather-fx-drop-4 { left: 48%; animation-duration: 2s;   animation-delay: -1.9s; }
.weather-fx-drop-5 { left: 61%; width: 12px; height: 16px; animation-duration: 2.4s; animation-delay: -0.6s; }
.weather-fx-drop-6 { left: 75%; width: 17px; height: 23px; animation-duration: 1.5s; animation-delay: -1.1s; }
.weather-fx-drop-7 { left: 89%; animation-duration: 2.1s; animation-delay: -1.7s; }
@keyframes weather-fx-drop {
  from { transform: translateY(0); }
  to   { transform: translateY(260px); }
}

/* Hagel im Hero: Asteroiden (Inline-SVG aus hero.php, 48px, Fels unten
   rechts im Bild, Schweif nach oben links) fallen im 45-Grad-Winkel
   170px weit und verschwinden beim Aufprall; am Einschlagpunkt (Start
   + 170px + Felsmitte 36px) blitzt ein Ring auf, der sich ausdehnt.
   Drei Stueck, zeitlich versetzt; die Hagel-Koerner laufen weiter. */
.weather-fx-asteroid {
  position: absolute; top: -40px; width: 48px; height: 48px;
  opacity: 0;
  animation: weather-fx-asteroid 3.6s ease-in infinite;
}
/* Tabler-Meteor: Schweif oben rechts, Fels unten links (Mitte ~19/29px
   der 48px-Box) -> Flug von rechts oben nach links unten (-170/+170px).
   Einschlagmitte = Start + (-170, 170) + (19, 29); Ring 40px zentriert. */
.weather-fx-impact {
  position: absolute; top: 139px; width: 40px; height: 40px;
  border-radius: 50%;
  border: 2px solid rgba(251, 191, 36, .9);
  background: radial-gradient(circle, rgba(254, 243, 199, .9), rgba(251, 191, 36, .5) 45%, transparent 70%);
  opacity: 0;
  transform: scale(0);
  animation: weather-fx-impact 3.6s linear infinite;
}
/* Positionen vom rechten Rand aus (Prozent), damit sich die Einschlaege
   auf jeder Hero-Breite ueber das Bild verteilen. Ring: right = Start-
   right + 48px (Box) + 171px (Flug + Felsmitte) - 40px (Ringbox). */
.weather-fx-asteroid-1 { right: 2%; }
.weather-fx-impact-1   { right: calc(2% + 179px); }
.weather-fx-asteroid-2 { right: 30%; animation-delay: -1.3s; }
.weather-fx-impact-2   { right: calc(30% + 179px); animation-delay: -1.3s; }
.weather-fx-asteroid-3 { right: 58%; animation-delay: -2.5s; }
.weather-fx-impact-3   { right: calc(58% + 179px); animation-delay: -2.5s; }
@keyframes weather-fx-asteroid {
  0%   { transform: translate(0, 0); opacity: 0; }
  4%   { opacity: 1; }
  55%  { transform: translate(-170px, 170px); opacity: 1; }
  56%  { transform: translate(-170px, 170px); opacity: 0; }
  100% { transform: translate(-170px, 170px); opacity: 0; }
}

/* Schnee im Hero: acht Schneeflocken (Tabler-Pfad, Inline-SVG aus
   hero.php) statt der Gradient-Punkte — sinken langsam, pendeln dabei
   seitlich und drehen sich; verschiedene Groessen, Tempi, Startzeiten. */
.hero-weather.weather-fx-snow .weather-fx-layer::before,
.hero-weather.weather-fx-snow .weather-fx-layer::after,
.hero-weather.weather-fx-sleet .weather-fx-layer::after {
  background-image: none; animation: none;
}
.weather-fx-flake {
  position: absolute; top: -30px; width: 18px; height: 18px;
  color: #dbeefb; opacity: .95;
  animation: weather-fx-flake 7s linear infinite;
}
.weather-fx-flake-1 { left: 6%;  animation-delay: -1.2s; }
.weather-fx-flake-2 { left: 18%; width: 24px; height: 24px; color: #fff;    animation-duration: 6s;   animation-delay: -4.1s; }
.weather-fx-flake-3 { left: 30%; width: 14px; height: 14px; opacity: .75;   animation-duration: 8.5s; animation-delay: -2.6s; }
.weather-fx-flake-4 { left: 43%; width: 20px; height: 20px; color: #b3dcf5; animation-duration: 6.6s; animation-delay: -5.3s; }
.weather-fx-flake-5 { left: 55%; width: 12px; height: 12px; opacity: .7;    animation-duration: 9s;   animation-delay: -0.8s; }
.weather-fx-flake-6 { left: 67%; width: 22px; height: 22px; color: #fff;    animation-duration: 6.3s; animation-delay: -3.4s; }
.weather-fx-flake-7 { left: 79%; width: 16px; height: 16px; color: #b3dcf5; animation-duration: 7.8s; animation-delay: -6.2s; }
.weather-fx-flake-8 { left: 91%; width: 19px; height: 19px;                 animation-duration: 6.9s; animation-delay: -2s; }
@keyframes weather-fx-flake {
  0%   { transform: translate(0, 0) rotate(0deg); }
  25%  { transform: translate(10px, 65px) rotate(90deg); }
  50%  { transform: translate(-6px, 130px) rotate(180deg); }
  75%  { transform: translate(10px, 195px) rotate(270deg); }
  100% { transform: translate(0, 260px) rotate(360deg); }
}
@keyframes weather-fx-impact {
  0%, 54% { transform: scale(0); opacity: 0; }
  56%     { transform: scale(.3); opacity: 1; }
  80%     { transform: scale(1.8); opacity: 0; }
  100%    { transform: scale(1.8); opacity: 0; }
}

/* Gewitter: Regen (::before, s. o.) plus kurzes Aufblitzen in ::after. */
.weather-fx-thunderstorm .weather-fx-layer::after {
  background-color: var(--wfx-flash);
  opacity: 0;
  animation: weather-fx-flash 7s linear infinite;
}
@keyframes weather-fx-flash {
  0%, 38%, 41%, 43%, 46%, 100% { opacity: 0; }
  39%, 44% { opacity: 1; }
  40%      { opacity: .4; }
}

@media (prefers-reduced-motion: reduce) {
  .weather-fx-layer::before,
  .weather-fx-layer::after,
  .hero-weather.weather-fx-fog > img { animation: none; }
  .weather-fx-thunderstorm .weather-fx-layer::after { opacity: 0; }
}

/* ---- POI: Karten-Marker + Listen-Item Highlight ----
   .poi-marker = Leaflet divIcon-Wrapper aus map.js (poiIcon()).
   .is-active wird vom Marker-Click-Handler bzw. Listen-Item-Klick gesetzt:
   Skaliert den Pin etwas, legt einen Akzent-Ring darum und einen
   weicheren Drop-Shadow drunter. Auf der Liste markiert .is-active die
   zuletzt angeklickte/fokussierte Row mit Akzent-Border + leichtem Bg.
   Beides nutzt CSS-Transition damit der Zustandswechsel sanft kommt. */
.poi-marker > span {
  transition: transform .15s ease, box-shadow .15s ease;
}
.poi-marker.is-active > span {
  transform: scale(1.15);
  box-shadow: 0 0 0 3px #f97316, 0 4px 10px rgba(0,0,0,.25);
  z-index: 600;
}
.poi-list-item.is-active {
  background-color: #fff7ed;       /* orange-50 */
  border-color: #fdba74;           /* orange-300 */
}

/* DSGVO-Datenexport: beim Drucken / "als PDF speichern" nur die Daten-Ansicht
   ausgeben, nicht die ganze Konto-Seite (Header/Nav/Formular). JS
   (personal-data-export.js) setzt body.printing-data-export vor window.print()
   und entfernt die Klasse via afterprint wieder. Technik: alles unsichtbar,
   nur die gerenderte Export-Ansicht sichtbar + nach oben links gezogen. */
@media print {
  body.printing-data-export * {
    visibility: hidden !important;
  }
  body.printing-data-export [data-export-result],
  body.printing-data-export [data-export-result] * {
    visibility: visible !important;
  }
  body.printing-data-export [data-export-result] {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    padding: 1rem;
  }
}

/* Cookie-/Speicher-Hinweis (TDDDG §25). index.php bindet das Banner nur ein,
   solange der gem_consent-Cookie fehlt; cookie-banner.php setzt ihn beim
   "Verstanden"-Klick. Eigenstaendiges CSS (kein Tailwind-Rebuild noetig).
   Sitzt fix unten ueber der Bottom-Nav, mobile-first, kantig. */
.cookie-banner {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 9999;
  background: #ffffff;
  border-top: 2px solid #e5e7eb;          /* gray-200 */
  box-shadow: 0 -4px 12px rgba(0, 0, 0, .08);
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
}
.cookie-banner__inner {
  max-width: 640px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.cookie-banner__text {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: #374151;                         /* gray-700 */
}
.cookie-banner__link { color: #ea580c; text-decoration: underline; }
.cookie-banner__btn {
  align-self: flex-end;
  background: #ea580c;                     /* accent-600 */
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  padding: 8px 22px;
  border: 0;
  border-radius: 0;
  cursor: pointer;
}
.cookie-banner__btn:hover { background: #c2410c; }   /* accent-700 */
@media (min-width: 480px) {
  .cookie-banner__inner { flex-direction: row; align-items: center; }
  .cookie-banner__text { flex: 1; }
  .cookie-banner__btn { align-self: auto; white-space: nowrap; }
}

/* ============================================================
   Einfaerbbares Graustufen-Hintergrundmotiv (Content-Bloecke)
   ------------------------------------------------------------
   Der Block-Container (.cp-has-bg-motif) bekommt einen Bild-Layer
   HINTER dem Inhalt. Das Graustufen-Motiv wird per SVG-Filter
   (#gem-bg-tint, siehe helpers/backgrounds.php) in die Gemeinde-
   farbe eingefaerbt: dunkel = Farbe, weiss = transparent.

   Position (unten), Groesse (contain) und Deckkraft sind hier FIX
   vorgegeben — es gibt bewusst KEINE per-Block-Regler.

   Bewusst <img> statt background-image: SVG filter:url() auf einem
   <img>-Element ist auf iOS-Safari robuster als auf einem
   Hintergrundbild.
   ============================================================ */
.cp-has-bg-motif {
  position: relative;
  overflow: hidden;
}
.cp-bg-motif {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: auto;
  max-height: 100%;
  object-fit: contain;
  object-position: bottom;
  opacity: 0.38;                 /* fixe Deckkraft-Vorgabe */
  filter: url(#gem-bg-tint);
  pointer-events: none;
  z-index: 0;
}
.cp-bg-content {
  position: relative;
  z-index: 1;
}

/* ============================================================================
   Boxed-Layout ab Tablet-Breite (> 800px)
   ----------------------------------------------------------------------------
   Mobile-first bleibt unangetastet: bis 800px aendert sich NICHTS. Ab 801px
   wird die App als zentrierter "Geraete"-Kasten mit max. 800px Breite
   dargestellt, die Flaeche aussenrum grau. Der Body traegt den Inhalt als
   Box; der sticky Header liegt im normalen Fluss und wird dadurch automatisch
   mitbegrenzt. Die viewport-fixierten Chrome-Elemente (Bottom-Nav, Overlays,
   Lightbox, Cookie-Banner) spannten sonst ueber den ganzen Desktop — sie
   werden per --app-inset an dieselbe Box gekoppelt (left/right statt
   transform, damit die Overlay-Open/Close-Animation frei bleibt).
   ============================================================================ */
@media (min-width: 801px) {
  :root {
    --app-max: 800px;
    /* Abstand Viewport-Rand -> zentrierte Box (nie negativ) */
    --app-inset: max(0px, calc(50% - (var(--app-max) / 2)));
  }

  html { background: #dfe1e5; }              /* Flaeche ausserhalb der Box */

  body {
    max-width: var(--app-max);
    margin-inline: auto;
    min-height: 100vh;                       /* Box fuellt die Hoehe */
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05),
                0 8px 40px rgba(0, 0, 0, 0.12);
  }

  /* Viewport-fixierte Chrome-Elemente an die Box-Kanten koppeln. */
  .nav-fixed,
  .cookie-banner,
  .gem-lightbox,
  .overlay-panel {
    left: var(--app-inset);
    right: var(--app-inset);
  }

  /* Inbox dockt rechts an die Box-Kante (nicht an den Viewport-Rand). */
  #overlay-inbox.overlay-panel {
    left: auto;
    right: var(--app-inset);
  }
}

/* ---------------------------------------------------------------------------
   Anbieterkennzeichnung (templates/ui/anbieterkennzeichnung.php)

   <details>/<summary> statt Overlay: nativ zugaenglich und ohne JavaScript.
   Der Default-Marker der Browser wird ausgeblendet — Tailwinds list-none
   deckt WebKit nicht ab, deshalb hier explizit.
--------------------------------------------------------------------------- */
.imprint-toggle::-webkit-details-marker { display: none; }
.imprint-toggle { list-style: none; }

/* Chevron dreht sich beim Aufklappen. Bewusst hier statt als
   group-open:-Variante — die steht im vorgebauten Tailwind nicht drin. */
details[open] > .imprint-toggle .imprint-chevron { transform: rotate(180deg); }
.imprint-chevron { transition: transform .15s ease; }

/* ============================================================================
   App-Install-Banner (app-install-banner.js) — Hinweis "gibt's auch als App"
   auf Mobilgeraeten im Browser. Erscheint nur wenn eine Store-URL hinterlegt
   ist (Api::ANDROID_STORE_URL/IOS_STORE_URL, leer = aus) und nie im nativen
   Shell. Liegt UEBER der Bottom-Nav (z-index 60 > 50), kantig wie der Rest.
   ============================================================================ */
.gem-app-banner {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 60;
  display: flex; align-items: center; gap: 12px;
  background: #ffffff;
  border-top: 2px solid var(--brand-blue, #1f8ecf);
  box-shadow: 0 -4px 12px rgba(0, 0, 0, .12);
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
}
.gem-app-banner__icon { width: 40px; height: 40px; flex: 0 0 auto; }
.gem-app-banner__text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.gem-app-banner__text strong { font-size: 14px; color: #111827; }
.gem-app-banner__text span   { font-size: 12px; color: #6b7280; }
.gem-app-banner__cta {
  flex: 0 0 auto;
  background: var(--brand-blue, #1f8ecf);
  color: #ffffff; text-decoration: none;
  font-size: 14px; font-weight: 600;
  padding: 10px 16px;
}
.gem-app-banner__close {
  flex: 0 0 auto;
  background: none; border: 0; cursor: pointer;
  font-size: 22px; line-height: 1; color: #9ca3af;
  padding: 4px 6px;
}

/* ---------------------------------------------------------------------------
   Karten-Klick-zum-Laden (assets/js/map.js)

   Liegt im Kartencontainer, bis der Nutzer die Karte ausdruecklich laedt.
   Erst dann geht ein Request an OpenStreetMap — vorher verlaesst keine
   IP-Adresse die eigene Domain. Das ist der Grund, warum die App ohne
   Einwilligungsdialog auskommt.
--------------------------------------------------------------------------- */
.map-gate,
.embed-gate {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 16px;
  text-align: center;
  background: #f9fafb;                     /* gray-50 */
}
.map-gate__text,
.embed-gate__text {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: #4b5563;                          /* gray-600, 7,6:1 auf gray-50 */
  max-width: 34ch;
}
.map-gate__btn,
.embed-gate__btn {
  background: #ea580c;                     /* accent-600 */
  color: #fff;
  border: 0;
  font-size: 14px;
  font-weight: 600;
  padding: 10px 20px;
  cursor: pointer;
}
.map-gate__btn:hover,
.embed-gate__btn:hover { background: #c2410c; }
.map-gate__btn:focus-visible,
.embed-gate__btn:focus-visible { outline: 2px solid #1f2937; outline-offset: 2px; }

/* Platzhalter fuer eingebettete Videos — gleiche Optik wie der Karten-Gate,
   damit "hier wird etwas von Dritten nachgeladen" in der App immer gleich
   aussieht. Das Icon ersetzt das frueher von Google geladene Vorschaubild. */
.embed-gate { background: #f3f4f6; }        /* gray-100, etwas dunkler als die Karte */
.embed-gate__icon { width: 40px; height: 40px; color: #6b7280; }

/* Eigenes Vorschaubild (BlockYoutube data.poster_image_id). Ersetzt nur die
   graue Flaeche — Icon, Hinweistext und Button bleiben darueber stehen,
   sonst waere die Zwei-Klick-Loesung nicht mehr erkennbar. Das Bild kommt
   same-origin von /images/..., nie von YouTube.

   Der Scrim (::after, 65 % #111827) ist der Preis fuer Lesbarkeit: selbst auf
   einem komplett weissen Bild kommt weisser Text damit noch auf 5,5:1 und
   liegt ueber der WCAG-AA-Schwelle von 4,5:1 (gemessen, nicht geschaetzt).
   Wer den Scrim aufhellt, muss den Kontrast neu rechnen. */
.embed-gate--poster { position: relative; background: #111827; }
.embed-gate__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}
.embed-gate--poster::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(17, 24, 39, .65);
  z-index: 1;
}
/* Inhalt ueber Bild + Scrim heben. Ohne das eigene Stacking waeren Text und
   Button vom ::after verdeckt. */
.embed-gate--poster > .embed-gate__icon,
.embed-gate--poster > .embed-gate__text,
.embed-gate--poster > .embed-gate__btn { position: relative; z-index: 2; }
.embed-gate--poster .embed-gate__icon { color: #ffffff; }
.embed-gate--poster .embed-gate__text {
  color: #ffffff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
}

/* SPA-Navigation (assets/js/spa-nav.js): duenner Ladebalken am oberen
   Rand waehrend ein Content-Fragment geholt wird. Farbe = Accent; per
   Gemeinde-Theme-CSS ueberschreibbar (.spa-progress { background: ... }). */
.spa-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  width: 0;
  background: #f97316;                     /* accent-500 */
  z-index: 9999;
  opacity: 0;
  pointer-events: none;
  transition: width .3s ease, opacity .2s ease;
}
.spa-progress.active { opacity: 1; }
