/* ═══════════════════════════════════════════════════════════════════════════
   wetter.bymw.de — Grundlage: Tokens, Reset, Hülle, Kopf, Suche, Held, Fuß.
   Karten und das Transparenz-Panel liegen in karten.css bzw. details.css.

   ⚠️ HAUSREGELN (CLAUDE.md), die hier gelten:
   • Schriftgrößen, Radien und Abstände laufen AUSSCHLIESSLICH über die Tabellen
     unten (--fs-* / --r-* / --sp-*), je einmal für Handy, ab 1000 px und ab
     1600 px. Neue Regel = `font-size: var(--fs-14)`, nie `14px`. Nachjustiert
     wird nur in der Tabelle, nicht verteilt über hunderte Regeln.
   • Schatten kommen aus den --sh-*-Bausteinen, nicht aus eigenen rgba-Rezepten.
   • z-index NUR über die benannte Leiter --z-*. Keine nackten Zahlen.
   • Ränder respektieren die Safe Area (env(safe-area-inset-*)).
   • Farbpaare sind quantitativ gegen WCAG AA geprüft (siehe qa/wetter-kontrast).
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Farben ─────────────────────────────────────────────────────────────────
   Dunkel ist die Identität (wie Symbol und Startbild); Hell ist der zweite,
   gleichwertige Modus — nicht eine aufgehellte Notlösung. */
:root {
  --bg:      #070d22;
  --bg-2:    #0b1330;
  --flaeche: #101a3a;   /* Karten */
  /* 🚨 SCHWEBENDE OVERLAYS (Ortsliste, Modell-Dialog) sind DECKEND, nicht
     durchscheinend. Das „Milchglas" der Karten (--flaeche) liegt auf dem festen
     Himmel und trägt dort; ein Overlay liegt aber über BELIEBIGEM Inhalt (den
     Detailkarten, dem Helden), und dort scheint alles durch — auf dem echten
     Gerät fotografiert gemeldet, unlesbar. Deshalb ein eigener, deckender Ton. */
  --overlay: #131d40;
  --soft:    #16224a;   /* Chips, Eingabefelder */
  --line:    rgba(255, 255, 255, .10);
  --line-stark: rgba(255, 255, 255, .18);

  --ink:   #f2f6ff;     /* Überschriften, Zahlen */
  --body:  #c3cfe8;     /* Fließtext */
  --muted: #8b9ac0;     /* Beiwerk */

  --acc:   #4da3ff;     /* Marke: Himmelblau */
  --acc-2: #ffb347;     /* Marke: Sonne */
  --acc-ink: #071022;   /* Text AUF der Akzentfläche */
  --gut:   #46d39a;
  --warn:  #ffb347;
  --schlecht: #ff7a7a;
  --warn-extrem: #c58bff;   /* DWD-Warnstufe „extrem" (violett) */

  --glas: rgba(16, 26, 58, .72);
  --glas-kopf: rgba(7, 13, 34, .82);

  color-scheme: dark;
}


/* ═══════════════════════════════════════════════════════════════════════════
   🌤️ HIMMEL-SEITE — der iOS-Look: EIN durchgehender Himmel hinter der ganzen
   Seite, darauf durchscheinende „Milchglas"-Karten mit weißer Schrift.

   Gemeldet, mit Foto der iOS-Wetter-App: „In meiner Wetter-App ist das ganze so
   gelöst." Genau so — es gibt keinen abgeschnittenen Helden mehr, sondern einen
   festen Himmel (body::before, unten) und Karten, die darauf schweben.

   Umgesetzt über die TOKENS: Fast jede Karte färbt sich aus `--flaeche`, jede
   Schrift aus `--ink`/`--body`/`--muted`. Wir überschreiben diese Token für die
   ganze Seite auf Glas + Weiß — damit kippt die komplette Fläche auf einmal, ohne
   hunderte Regeln anzufassen. Nur die STARTSEITE (index.php) trägt die Klasse;
   `ueber.php` und die Karte behalten die normalen (deckenden) dunklen Karten.
   Der Selektor `:root.wx-himmel-seite` (0,2,0) schlägt das Basis-`:root` (0,1,0).

   ⚠️ KEIN `backdrop-filter` als Grundlage. Die Doku ist eindeutig: Weichzeichnen
      ist der teuerste Posten auf langsamen Geräten, der ganze Held wurde deshalb
      davon befreit. Das Glas trägt hier über eine DECKENDE dunkle Tönung
      (`--flaeche` = dunkles Transluzent) plus eine feine Lichtkante — nicht über
      Blur. Weißer Text steht damit auf einem garantiert dunklen Grund, egal wie
      hell der Himmel darunter ist. */
:root.wx-himmel-seite {
  --bg:      #0a1430;   /* Rückfall-Himmelfläche (body zeigt aber body::before) */
  --bg-2:    #0c1738;
  --flaeche: rgba(9, 15, 40, .46);    /* dunkles Milchglas */
  /* Auch im Himmel-Modus DECKEND (nur ein Hauch durchlässig, damit der
     Weichzeichner noch eine leise Tiefe gibt). Über beliebigem Inhalt muss die
     Ortsliste lesbar bleiben — 46 % reichten auf dem echten Gerät nicht. */
  --overlay: rgba(12, 19, 46, .975);
  --soft:    rgba(255, 255, 255, .15); /* Chips/Felder: helles Glas */
  --line:      rgba(255, 255, 255, .18);
  --line-stark: rgba(255, 255, 255, .30);

  --ink:   #ffffff;
  --body:  rgba(255, 255, 255, .90);
  --muted: rgba(255, 255, 255, .72);

  /* Akzente heller, damit sie auf dunklem Glas tragen (auf --flaeche gemessen). */
  --acc:   #a9d0ff;
  --acc-2: #ffcf8a;
  --acc-ink: #06122a;
  --gut:   #7bf0c0;
  --warn:  #ffd08a;
  --schlecht: #ffa0a0;
  --warn-extrem: #d6acff;

  --glas:      rgba(9, 15, 40, .46);
  --glas-kopf: rgba(7, 13, 34, .40);

  color-scheme: dark;   /* Formulare/Scrollbalken dunkel, der Himmel ist dunkel */
}

/* ── Schriftgrößen ──────────────────────────────────────────────────────────
   Name = Handy-Wert IN PIXELN bei Standardeinstellung. Kleine Schrift wächst
   stärker als große, sonst wirken Überschriften auf dem Desktop erschlagend.

   ⚠️ DIE WERTE STEHEN IN `rem`, NICHT IN `px` — und das ist kein Schönheits-
   fehler, sondern der Unterschied zwischen „unterstützt große Schrift" und
   „ignoriert sie". Wer im Browser eine größere Schrift einstellt, ändert damit
   die Grundgröße des Dokuments. Eine Tabelle in px nimmt davon nichts zur
   Kenntnis: Die Einstellung bleibt schlicht wirkungslos, und zwar lautlos —
   nichts sieht kaputt aus, es passiert nur nichts. Der Umrechnungsschlüssel ist
   16 px = 1rem, die Namen bleiben deshalb die Pixelzahlen von früher.

   ⚠️ Abstände (--sp-*) und Radien (--r-*) bleiben absichtlich in px. Raum ist
   eine körperliche Größe; ein Fingerziel und ein Kartenrand dürfen nicht mit
   der Schrift mitwachsen, sonst franst bei 150 % die ganze Fläche aus.

   ⚠️ 12 px ist die KLEINSTE ausgelieferte Größe — es gibt bewusst kein
   `--fs-11` mehr. Es hing an den Zeit- und Regenbeschriftungen im Stundenband,
   also an echten Daten, und die liest auf einem Handy niemand bequem. Wer eine
   noch kleinere Stufe vermisst, braucht meist weniger Text, nicht kleineren. */
:root {
  --fs-12: .75rem;    --fs-13: .8125rem;  --fs-14: .875rem;
  --fs-15: .9375rem;  --fs-16: 1rem;      --fs-18: 1.125rem;  --fs-20: 1.25rem;
  --fs-24: 1.5rem;    --fs-28: 1.75rem;   --fs-34: 2.125rem;  --fs-44: 2.75rem;
  --fs-64: 4rem;

  /* Radien */
  --r-6: 6px;   --r-10: 10px;  --r-14: 14px;  --r-18: 18px;  --r-24: 24px;
  --r-pill: 999px;

  /* Abstände. Werte unter 6 px bleiben absichtlich literal — sie dürfen nicht
     mitwachsen, sonst franst die Fläche aus. */
  --sp-6: 6px;   --sp-8: 8px;    --sp-10: 10px;  --sp-12: 12px;
  --sp-16: 16px; --sp-20: 20px;  --sp-24: 24px;  --sp-32: 32px;  --sp-48: 48px;
}

@media (min-width: 1000px) {
  :root {
    --fs-12: .84375rem; --fs-13: .9375rem;  --fs-14: 1rem;
    --fs-15: 1.0625rem; --fs-16: 1.09375rem; --fs-18: 1.25rem;  --fs-20: 1.375rem;
    --fs-24: 1.625rem;  --fs-28: 1.9375rem;  --fs-34: 2.375rem; --fs-44: 3.125rem;
    --fs-64: 5rem;

    --r-6: 7px;   --r-10: 11px;  --r-14: 16px;  --r-18: 20px;  --r-24: 26px;

    --sp-6: 6.5px;  --sp-8: 9px;    --sp-10: 11px;  --sp-12: 13px;
    --sp-16: 18px;  --sp-20: 22px;  --sp-24: 26px;  --sp-32: 35px;  --sp-48: 53px;
  }
}

@media (min-width: 1600px) {
  :root {
    --fs-12: .9375rem;  --fs-13: 1rem;       --fs-14: 1.09375rem;
    --fs-15: 1.15625rem; --fs-16: 1.1875rem; --fs-18: 1.3125rem; --fs-20: 1.46875rem;
    --fs-24: 1.75rem;   --fs-28: 2.0625rem;  --fs-34: 2.5625rem; --fs-44: 3.375rem;
    --fs-64: 6rem;

    --r-6: 8px;   --r-10: 12px;  --r-14: 17px;  --r-18: 22px;  --r-24: 28px;

    --sp-6: 7px;    --sp-8: 9.5px;  --sp-10: 12px;  --sp-12: 14px;
    --sp-16: 19px;  --sp-20: 24px;  --sp-24: 28px;  --sp-32: 38px;  --sp-48: 57px;
  }
}

/* ── Schatten-Bausteine ────────────────────────────────────────────────────
   Kombinierbar: box-shadow: var(--sh-2), var(--sh-glanz); */
:root {
  --sh-1: 0 1px 2px rgba(2, 8, 24, .28);
  --sh-2: 0 6px 18px rgba(2, 8, 24, .34);
  --sh-3: 0 14px 40px rgba(2, 8, 24, .42);
  --sh-4: 0 28px 70px rgba(2, 8, 24, .50);
  --sh-glanz: inset 0 1px 0 rgba(255, 255, 255, .07);
  --sh-saum:  inset 0 0 0 1px var(--line);
  --sh-ring-acc: 0 0 0 2px rgba(77, 163, 255, .55);
}

/* ── Bewegung ──────────────────────────────────────────────────────────────
   Dauern und Beschleunigungen kommen aus dieser Tabelle, nicht aus einzelnen
   Regeln. Vorher standen über die drei Stylesheets verteilt `.12s`, `.16s`,
   `.2s`, `.22s`, `.5s` und drei verschiedene Kurven — jede für sich plausibel,
   zusammen ein Flimmern aus Tempi, das man nicht benennen, aber sehen kann.

   Die Werte folgen Material Motion:
   • --ease-standard beschleunigt kurz und bremst lang aus. Das ist die Kurve
     für alles, was seinen Platz behält (Farbe, Fläche, Breite eines Balkens).
   • --ease-rein bremst nur (Dinge, die HEREINkommen), --ease-raus beschleunigt
     nur (Dinge, die verschwinden) — Erscheinen darf Aufmerksamkeit kosten,
     Verschwinden nicht.
   • Dauern: 120 ms für Zustände unter dem Finger, 220 ms für Flächen,
     400 ms für lange Wege wie das Füllen eines Balkens. Alles darüber wirkt
     zäh, alles darunter hektisch. */
:root {
  /* ⚠️ EINE Zahl für alle Fingerziele. Vorher stand 44 an sechs Stellen und 34
     an zwei — und wer eine neue Schaltfläche baute, schrieb die Zahl ein neuntes
     Mal hin. 48 px ist der Richtwert von Material Design (und ein gutes Stück
     über Apples 44), bewusst in PIXELN: Ein Fingerziel ist eine körperliche
     Größe und darf nicht mit der Schriftgröße mitwachsen. */
  --ziel: 48px;

  --mo-kurz:   120ms;
  --mo-mittel: 220ms;
  --mo-lang:   400ms;
  --ease-standard: cubic-bezier(.2, 0, 0, 1);
  --ease-rein:     cubic-bezier(0, 0, 0, 1);
  --ease-raus:     cubic-bezier(.3, 0, 1, 1);
}

/* ── Ebenen-Leiter ─────────────────────────────────────────────────────────
   Neue gestapelte Fläche? HIER einordnen, nie „z-index: 999" raten. */
:root {
  --z-fx:      0;   /* Wetter-Leinwand im Hintergrund des Helden */
  --z-inhalt:  1;
  --z-sticky:  10;  /* klebender Abschnittskopf */
  --z-orte-schirm: 15;  /* Verdunkelung hinter den Orts-Karten — UNTER dem Kopf */
  --z-kopf:    20;  /* Seitenkopf */
  --z-blatt:   30;  /* Vorschlagsliste der Suche */
  --z-dialog:  40;
  --z-toast:   50;
  --z-ziehen:  60;  /* der Zeiger fürs Ziehen — über allem, auch über dem Kopf */
}

/* Safe Area einmal zentral einlesen — Canvas kann env() nicht lesen und holt
   sich den Wert über getComputedStyle (Muster _safeTop() in KelRaid). */
:root {
  --wx-sat: env(safe-area-inset-top, 0px);
  --wx-sab: env(safe-area-inset-bottom, 0px);
  --wx-sal: env(safe-area-inset-left, 0px);
  --wx-sar: env(safe-area-inset-right, 0px);

  /* ⚠️ Der Seitenrand wird JE SEITE gerechnet, nicht als Maximum beider Seiten.
     Ein iPhone im Querformat hat den Ausschnitt nur auf EINER Seite (links 47,
     rechts 0). `max(sp-16, sal, sar)` hätte beide Seiten auf 47 px geschoben —
     rechts also 31 px verschenkt, und das fällt genau dort auf, wo der Platz am
     knappsten ist. Zwei Variablen kosten nichts und stimmen immer.

     ⚠️ Und sie sind der GEMEINSAME Wert für Hülle und Roller: Das Stundenband
     zieht sich mit negativem Rand bis an die Kante und legt dieselbe Zahl als
     Polster wieder hinein. Stünde dort eine eigene Zahl, liefe das Band im
     Querformat unter den Ausschnitt — oder es bliebe ein Streifen stehen. */
  --wx-rand-l: max(var(--sp-16), var(--wx-sal));
  --wx-rand-r: max(var(--sp-16), var(--wx-sar));

  /* ⚠️ EINE Zahl für die Höhe des Kopfes — sie steht an drei Stellen, die
     zusammenpassen MÜSSEN: die Kopfzeile selbst, der Platzhalter im Helden (der
     sie freihält) und der negative Rand, mit dem der Held darunterrutscht.
     Liefen sie auseinander, läge entweder der Ortsname unter der Suche oder es
     klaffte ein Streifen Seitenhintergrund über dem Himmel. */
  --kopf-h: 64px;
}

/* ── Reset ─────────────────────────────────────────────────────────────────*/
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

/* 🚨 `none` STATT `contain` — GEMELDET MIT ZWEI FOTOS DERSELBEN APP, SEKUNDEN
   AUSEINANDER: „Warum der Unterschied beim Refresh? Mal zieht es die ganze
   Seite und mal sieht es besser aus." Auf dem einen Bild wich nur der
   Heldeninhalt aus (richtig), auf dem anderen hing die GANZE Seite am Finger,
   der Kopf hatte seine Fläche, und darüber stand die Seitenfarbe.

   `contain` unterbindet nur die WEITERGABE der Geste an den Browser, nicht das
   Gummiband des Dokuments selbst. Das musste bisher allein `preventDefault()`
   in js/ui-ziehen.js erledigen — und das kam in den ersten zehn Punkten jeder
   Geste zu spät (Begründung dort). `none` nimmt dem Dokument das Federn ganz
   und ist damit die strukturelle Hälfte der Reparatur: Sie greift auch dann,
   wenn ein Browser einen `touchmove` verschluckt oder anders taktet.
   ⚠️ Der Preis ist bewusst in Kauf genommen: Am Seitenende federt nichts mehr.
   Eine Seite, die beim Ziehen manchmal ganz wegrutscht, ist der schlechtere
   Tausch — und das Federn am Ende hat hier nie etwas mitgeteilt.

   ⚠️ Und es GEHÖRT ZUM ZIEHEN (js/ui-ziehen.js), ist also keine Kosmetik: Ohne
   diese Zeile macht Chrome auf Android SEINE eigene Aktualisierung zusätzlich
   zu unserer — die ganze Seite lädt neu, während unser Zeiger noch dreht. Zwei
   Antworten auf eine Geste sind schlimmer als keine. */
html {
  -webkit-text-size-adjust: 100%; scroll-behavior: smooth;
  overscroll-behavior-y: none;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  min-height: 100dvh;
  background: var(--bg);
  color: var(--body);
  font: 400 var(--fs-15)/1.6 -apple-system, BlinkMacSystemFont, "SF Pro Text",
        "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, sans-serif;
  font-variant-numeric: tabular-nums;
  overflow-x: hidden;              /* die Seite scrollt NIE waagerecht */
  -webkit-font-smoothing: antialiased;
}

/* Der Farbverlauf des Hintergrunds bleibt beim Scrollen stehen — sonst
   „wandert" er unter dem Inhalt weg und wirkt billig. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(90% 55% at 50% -8%, rgba(77, 163, 255, .16) 0%, rgba(7, 13, 34, 0) 70%),
    radial-gradient(70% 45% at 88% 4%, rgba(255, 179, 71, .10) 0%, rgba(7, 13, 34, 0) 68%),
    linear-gradient(180deg, var(--bg-2) 0%, var(--bg) 46%);
}

/* 🌤️ HIMMEL-SEITE: die Seite selbst ist durchsichtig, dahinter liegt der feste
   Himmel. Er folgt dem Wetter (`--himmel-*`, von app.js an die Wurzel gesetzt,
   wechselt mit Lage und Tageszeit — genau wie der Held). Fest positioniert, also
   scrollt der Inhalt darüber wie in einer nativen Wetter-App; ein leichtes
   Nachdunkeln unten gibt den unteren Karten sicheren Grund für weiße Schrift. */
html.wx-himmel-seite body { background: transparent; }
html.wx-himmel-seite body::before {
  background:
    radial-gradient(120% 60% at 78% 2%, rgba(255, 214, 150, .14) 0%, rgba(7, 13, 34, 0) 55%),
    /* 🚨 DER FESTE HIMMEL MUSS `--himmel-3` ERREICHEN, BEVOR DER HELD ENDET —
       sonst gibt es eine harte Kante. Mehrfach gemeldet: „Man sieht immernoch
       die harte Kante im Farbverlauf!!!!!". Der Held ist ~78 svh hoch und
       blendet an seiner Unterkante auf `--himmel-3` aus (`.wx-held::after`).
       Stand hier `--himmel-3` erst bei 82 % des Viewports, war der feste Himmel
       GENAU UNTER dem Helden noch heller (der himmel-2→himmel-3-Auslauf) — ein
       hellblaues Band direkt unter der dunklen Held-Unterkante, und das ist die
       gemeldete Kante. Jetzt ist `--himmel-3` schon bei 58 % erreicht und bleibt
       bis unten flach: Wo immer der Held endet (Hochformat ~74–78 %), trifft
       seine himmel-3-Unterkante auf denselben himmel-3-Grund → nahtlos. Kein
       zusätzliches Nachdunkeln mehr nötig; himmel-3 (#0a1430) ist dunkel genug
       für die weiße Schrift der unteren Karten. */
    linear-gradient(180deg,
      var(--himmel-1, #1b3570) 0%,
      var(--himmel-2, #112450) 32%,
      var(--himmel-3, #0a1430) 58%,
      var(--himmel-3, #0a1430) 100%);
}

/* 🚨 DER HIMMEL LÄUFT BIS HINTER DIE NOTCH — randlos, wie eine native Wetter-App.
   Gemeldet, mit Foto: „Warum geht das nicht mehr hinter die Notch? Das sah
   Vollbild viel besser aus." Der Kopf ist auf dem Handy durchsichtig und liegt
   über dem Helden; reicht der Held einmal NICHT bis an die Oberkante (auf echten
   Geräten beobachtet, headless nicht reproduzierbar), scheint dahinter die
   blasse Seitenfläche durch — und der Kopf schwebt über Weiß statt über Himmel.

   Dieser feste Streifen füllt genau die Zone hinter Statusleiste und Kopf mit
   der AKTUELLEN Himmelsfarbe (`--himmel-*`, von app.js an die Wurzel gesetzt,
   Startwert dunkelblau). Er liegt auf z-index -1, also HINTER dem Helden: Wo
   der Held die Oberkante deckt, ist der Streifen unsichtbar; wo nicht, steht
   dort Himmel statt Weiß. Damit ist der Vollbild-Eindruck strukturell
   garantiert, unabhängig davon, ob die negative Verschiebung des Helden auf
   einem bestimmten Gerät exakt aufgeht.
   ⚠️ Nur auf dem Handy (dort schwebt der Kopf) und nur solange NICHT gerollt —
      beim Rollen bekommt der Kopf sein Glas und deckt die Zone selbst. */
/* Auch quer (kurzer Viewport): dort schwebt der Kopf ebenfalls über dem Himmel,
   siehe die OR-Bedingung an der Kopf-Regel unten. */
@media (max-width: 699px), (max-height: 520px) {
  :root.hat-himmel body::after {
    content: ""; position: fixed; top: 0; left: 0; right: 0;
    height: calc(var(--kopf-h) + var(--wx-sat) + 6px);
    z-index: -1; pointer-events: none;
    background: linear-gradient(180deg, var(--himmel-1) 0%, var(--himmel-2) 100%);
  }
  :root.ist-gerollt.hat-himmel body::after { display: none; }
}

img, svg, canvas { display: block; max-width: 100%; }
button, input, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: var(--acc); text-decoration: none; }
@media (hover: hover) and (pointer: fine) {
  a:hover { text-decoration: underline; }
}

/* ── Berührung ─────────────────────────────────────────────────────────────
   ⚠️ `touch-action: manipulation` ist hier die wichtigste Zeile: Ohne sie hält
   ein mobiler Browser nach JEDEM Antippen rund 300 ms inne, um abzuwarten, ob
   ein zweiter Tipp folgt (Doppeltipp = Hineinzoomen). Die App fühlt sich damit
   auf jedem Handy zäh an, ohne dass irgendetwas langsam wäre. Die Angabe
   verbietet nur den Doppeltipp-Zoom auf diesen Flächen — Zusammenkneifen bleibt
   auf der ganzen Seite erlaubt, sehbehinderte Nutzer verlieren nichts.

   ⚠️ Der graue Kasten (`tap-highlight`) verschwindet nur, WEIL jede dieser
   Flächen unten einen eigenen :active-Zustand hat. Ihn ersatzlos abzuschalten
   wäre keine Politur, sondern eine App, die auf Berührung nicht antwortet. */
a, button, input, summary, [role="button"] {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

h1, h2, h3 { color: var(--ink); line-height: 1.18; font-weight: 700; letter-spacing: -.015em; }

/* ══════════════════════════════════════════════════════════════════════════
   TOOLTIPS — nur dort, wo es einen Zeiger gibt
   ══════════════════════════════════════════════════════════════════════════
   Reine Symbolknöpfe (Standort, Darstellung, Abspielen) tragen ihre Bedeutung
   im `aria-label` — für Screenreader reicht das, für Sehende mit Maus nicht:
   Ein Piktogramm ohne Wort ist ein Rätsel, und man kann es nicht „ausprobieren",
   ohne die Handlung auszulösen.

   ⚠️ NICHT DAS `title`-ATTRIBUT. Es erscheint erst nach rund einer Sekunde,
      sieht auf jedem System anders aus, lässt sich nicht gestalten — und auf
      Berührungsgeräten löst es teils ein Kontextmenü aus.
   ⚠️ NUR bei `hover: hover` UND `pointer: fine`. Auf einem Touchscreen gäbe es
      kein Wegbewegen; die Blase bliebe nach dem Antippen stehen — derselbe
      Fehler, wegen dem alle `:hover`-Regeln unten gekapselt sind.
   ⚠️ `aria-hidden` am Pseudo-Element geht nicht, aber es ist auch nicht nötig:
      CSS-generierter Inhalt wird von Screenreadern verschieden behandelt, und
      der Text steht ohnehin schon im `aria-label`. Deshalb kommt er aus einem
      EIGENEN Attribut (`data-tip`) statt aus dem Label — sonst läse mancher
      Screenreader ihn doppelt vor. */
@media (hover: hover) and (pointer: fine) {
  [data-tip] { position: relative; }
  [data-tip]::after {
    content: attr(data-tip);
    position: absolute; top: calc(100% + 8px); left: 50%;
    transform: translateX(-50%) translateY(-4px);
    z-index: var(--z-toast);
    padding: 5px var(--sp-8);
    border-radius: var(--r-6);
    background: var(--ink); color: var(--flaeche);
    font-size: var(--fs-12); font-weight: 600; line-height: 1.3;
    white-space: nowrap;
    opacity: 0; pointer-events: none;
    transition: opacity var(--mo-kurz) var(--ease-standard),
                transform var(--mo-kurz) var(--ease-standard);
  }
  [data-tip]:hover::after,
  [data-tip]:focus-visible::after { opacity: 1; transform: translateX(-50%) translateY(0); }
  /* Am rechten Rand würde die Blase hinauslaufen — dort hängt sie rechtsbündig. */
  [data-tip][data-tip-links]::after { left: auto; right: 0; transform: translateY(-4px); }
  [data-tip][data-tip-links]:hover::after,
  [data-tip][data-tip-links]:focus-visible::after { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  [data-tip]::after { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   BERÜHRUNG
   ══════════════════════════════════════════════════════════════════════════
   ⚠️ `-webkit-tap-highlight-color` AUS: Der graue Kasten, den iOS und Android
      beim Antippen über das Element legen, hat mit der Gestaltung dieser App
      nichts zu tun und erscheint AUSSERDEM verzögert — man sieht ihn erst,
      wenn die Handlung schon läuft. Er wird ersetzt durch die eigenen
      `:active`-Zustände, die sofort da sind.
   ⚠️ `user-select: none` NUR auf Bedienelementen, nie auf Text: Wer eine
      Ortsangabe kopieren will, muss das können. */
button, [role="button"], .wx-ik, .wx-vs, .wx-tag, .wx-std-k, a.wx-cta {
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none; user-select: none;
}

/* 🚨 JEDE `:hover`-REGEL DIESER APP STEHT IN `@media (hover: hover) and
   (pointer: fine)` — UND ZWAR AUS EINEM GEMELDETEN FEHLER.
   „Der Standort-Button oben auf der Startseite sieht anders aus, nachdem er
   Fokus bekommen hat. Und das geht aber nicht wieder weg."

   Der Fokus war es nicht (`:focus-visible` steht unten und greift bei Berührung
   gar nicht). Es war `:hover`: Ein Berührungsbildschirm hat keinen Zeiger, den
   man wegbewegen könnte — iOS und Android legen beim Antippen trotzdem `:hover`
   an und lassen es liegen, bis irgendwo anders hingetippt wird. Der Knopf blieb
   also in seinem Zeiger-Zustand stehen, und für den Nutzer sah das aus wie ein
   Element, das hängengeblieben ist.

   Die Bedingung fragt BEIDES ab: `hover: hover` allein ist auf manchen
   Android-Geräten wahr (sie melden einen „hover-fähigen" Stift), erst zusammen
   mit `pointer: fine` bleiben Touchscreens zuverlässig draußen.

   ⚠️ Was daraus folgt: Eine Fläche darf ihre Rückmeldung NICHT allein aus
      `:hover` beziehen. Jedes Bedienelement braucht zusätzlich ein sichtbares
      `:active` — auf dem Handy ist das die einzige Antwort auf eine Berührung. */
:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; border-radius: var(--r-6); }

/* Nur für Screenreader. */
.wx-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ── Hülle ────────────────────────────────────────────────────────────────*/
.wx-wrap {
  width: min(1280px, 100%);
  margin-inline: auto;
  padding-left: var(--wx-rand-l);
  padding-right: var(--wx-rand-r);
}
@media (min-width: 1600px) { .wx-wrap { width: min(1480px, 100%); } }

/* Nur Atemraum unter dem Inhalt. Die SAFE-AREA am unteren Rand gehört dem
   Element, das dort wirklich sitzt — dem Fuß (`.wx-fuss`), nicht dem `main`
   drumherum. Sonst hängt der Home-Indikator-Abstand daran, dass zufällig der
   Fuß das letzte Kind ist. */
main { padding-bottom: var(--sp-48); }
/* ⚠️ Auf dem Handy ist der Kopf `fixed` und damit aus dem Fluss — ohne diesen
   Abstand läge der erste Inhalt darunter. Der HELD holt ihn sich mit seinem
   negativen Rand bewusst zurück (er soll ja unter dem Kopf durchlaufen);
   alles andere braucht ihn. Nur auf der Seite mit Himmel: Sonst klebt der Kopf
   wie überall, und der Abstand wäre eine Lücke. */
/* Quer (kurzer Viewport) schwebt der Kopf ebenfalls — dann braucht der Inhalt
   denselben Abstand (der Held holt ihn sich mit seinem negativen Rand zurück). */
@media (max-width: 699px), (max-height: 520px) {
  :root.hat-himmel main { padding-top: calc(var(--kopf-h) + var(--wx-sat)); }
}

/* ── Kopf ─────────────────────────────────────────────────────────────────*/
.wx-kopf {
  position: sticky; top: 0; z-index: var(--z-kopf);
  padding-top: var(--wx-sat);
  background: var(--glas-kopf);
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  transition: background-color var(--mo-mittel) var(--ease-standard),
              border-color var(--mo-mittel) var(--ease-standard);
}

/* ── Auf dem Handy schwebt der Kopf ÜBER dem Himmel ───────────────────────
   Solange die Seite ganz oben steht, hat er keine eigene Fläche: Der Himmel
   läuft ununterbrochen bis an die Oberkante, und Suche und Knöpfe liegen
   darauf. Erst beim Rollen bekommt er sein Glas — dann steht darunter nicht
   mehr der Himmel, sondern Text, und ohne Fläche wäre der Kopf unlesbar.

   ⚠️ Die Umschaltung hängt an einem 1-px-Fühler ganz oben und einem
   IntersectionObserver, nicht an einem Roll-Horcher: Ein `scroll`-Horcher
   feuert bei jeder Bewegung, hundertfach je Wisch — auf dem schwächsten Gerät
   genau dann, wenn es ohnehin knapp wird. */
/* 🚨 DIESER GANZE BLOCK GILT NUR, WO WIRKLICH EIN HIMMEL LIEGT — deshalb hängt
   er an `:root.hat-himmel`, und die Klasse setzt allein `index.php`. Der Grund
   ist ein gemeldeter Fehler: `ueber.php` teilt sich diese Kopfzeile, hat aber
   keinen Helden. Die Regeln unten machten seinen Kopf trotzdem durchsichtig und
   seine Schrift WEISS — auf einer hellen Seitenfläche. Auf dem Handy stand dort
   „Zur Vorhersage" als weißer Text auf hellem Blau, praktisch unsichtbar. Die
   Regeln waren nie falsch, sie galten nur an der falschen Stelle: Sie
   beschreiben „Kopf über Himmel", nicht „Kopf".

   ⚠️ Die Bedingung ist mit Absicht eine ZUSAGE, keine Ausnahme. Ein
   `:not(.text-seite)` hätte dasselbe bewirkt und die nächste neue Seite ohne
   Helden wieder weiß angemalt — wer opt-in schreibt, ist im Zweifel lesbar.

   Solange die Seite ganz oben steht, hat der Kopf keine eigene Fläche: Der
   Himmel läuft ununterbrochen bis an die Oberkante. Erst beim Rollen bekommt er
   sein Glas — dann steht darunter nicht mehr der Himmel, sondern Text.

   ⚠️ Die OR-Bedingung `(max-height: 520px)` zieht denselben schwebenden Kopf
      auch ins QUERFORMAT: Ein iPhone quer ist ≥700 px breit und fiele sonst in
      den Desktop-Kopf (feste Fläche) — dann wäre der Held ein gerundetes
      Kärtchen mit sichtbarer Abgrenzung statt eines randlosen Himmels wie
      hochkant (genau so gemeldet, fotografiert). */
@media (max-width: 699px), (max-height: 520px) {
  :root.hat-himmel .wx-kopf {
    position: fixed; left: 0; right: 0; top: 0;
    background: transparent; border-bottom-color: transparent;
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
  /* 🚨 EIN LEISER SCHIRM HINTER DEN BEDIENELEMENTEN — die Verteidigungslinie
     für den Fall, dass der Himmel den oberen Rand NICHT deckt. Genau das hat der
     Betreiber fotografiert: helle Bedienelemente auf blasser Seitenfläche,
     unlesbar. Es tritt im Betrieb auf, ohne dass eine Prüfung es sieht — beim
     ERSTEN Aufruf nach einem Deploy liefert noch der alte Service-Worker das
     alte Stylesheet zum neuen Markup (dokumentiert unter „ignoreSearch"), und
     eine im Stand­alone-Modus installierte App kann den Statusbalken-Bereich
     anders behandeln. In beiden Fällen liegt der Kopf kurz über hellem Grund.
     Der Verlauf von oben (dunkel) nach unten (durchsichtig) macht die weiße
     Schrift dann trotzdem lesbar; über dem echten dunklen Himmel ist er als
     sanfte Kopfvignette ohnehin ein Gewinn, kein Schaden.
     ⚠️ z-index unter die Bedienelemente, über den Himmel. Beim Rollen (Glas)
        wird er ausgeblendet, sonst läge er hinter der undurchsichtigen
        Glasfläche und wäre wirkungslos — aber sichtbar an der Unterkante. */
  :root.hat-himmel .wx-kopf::before {
    content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
    background: linear-gradient(to bottom,
      rgba(4, 10, 28, .52), rgba(4, 10, 28, .22) 62%, rgba(4, 10, 28, 0));
    transition: opacity var(--mo-kurz) var(--ease-standard);
  }
  :root.ist-gerollt.hat-himmel .wx-kopf::before { opacity: 0; }
  .wx-kopf-in { min-height: var(--kopf-h); padding-block: var(--sp-8); }
  /* Solange der Kopf auf dem Himmel liegt, gehören seine Bedienelemente optisch
     dorthin: helle Schrift auf dunklem Glas — unabhängig vom Hell/Dunkel-Modus,
     denn der Himmel ist in beiden dunkel. */
  :root.hat-himmel .wx-kopf .wx-marke,
  :root.hat-himmel .wx-kopf .wx-ik { color: #ffffff; }
  :root.hat-himmel .wx-kopf .wx-marke b { color: #ffd9a0; }
  :root.hat-himmel .wx-kopf .wx-suche-feld {
    background: rgba(8, 14, 32, .46); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .24);
  }
  :root.hat-himmel .wx-kopf .wx-suche input { color: #ffffff; }
  :root.hat-himmel .wx-kopf .wx-suche input::placeholder { color: rgba(255, 255, 255, .82); }
  :root.hat-himmel .wx-kopf .wx-suche-feld svg,
  :root.hat-himmel .wx-kopf .wx-suche-x { color: rgba(255, 255, 255, .86); }
  :root.hat-himmel .wx-kopf .wx-ik { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .24); }

  /* Gerollt: zurück zur Seitenpalette. */
  :root.ist-gerollt.hat-himmel .wx-kopf {
    background: var(--glas-kopf); border-bottom: 1px solid var(--line);
    backdrop-filter: saturate(140%) blur(14px);
    -webkit-backdrop-filter: saturate(140%) blur(14px);
  }
  :root.ist-gerollt.hat-himmel .wx-kopf .wx-marke { color: var(--ink); }
  :root.ist-gerollt.hat-himmel .wx-kopf .wx-marke b { color: var(--acc); }
  :root.ist-gerollt.hat-himmel .wx-kopf .wx-ik { color: var(--body); box-shadow: var(--sh-saum); }
  :root.ist-gerollt.hat-himmel .wx-kopf .wx-suche-feld { background: var(--soft); box-shadow: var(--sh-saum); }
  :root.ist-gerollt.hat-himmel .wx-kopf .wx-suche input { color: var(--ink); }
  :root.ist-gerollt.hat-himmel .wx-kopf .wx-suche input::placeholder { color: var(--muted); }
  :root.ist-gerollt.hat-himmel .wx-kopf .wx-suche-feld svg,
  :root.ist-gerollt.hat-himmel .wx-kopf .wx-suche-x { color: var(--muted); }
}
/* Der klebende Kopf zeichnet seinen Untergrund bei JEDER Rollbewegung neu
   weich. Auf einem langsamen Gerät ist ruhiges Scrollen mehr wert als ein
   Milchglaseffekt — die deckende Fläche sieht ohnehin fast gleich aus. */
:root.wx-sparsam .wx-kopf {
  backdrop-filter: none; -webkit-backdrop-filter: none;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.wx-kopf-in {
  display: flex; align-items: center; gap: var(--sp-10);
  min-height: 58px; padding-block: var(--sp-8);
}
@media (min-width: 1000px) { .wx-kopf-in { min-height: 68px; gap: var(--sp-16); } }
/* ⚠️ Ein Handy im QUERFORMAT hat rund 390 px Höhe, und die Bildschirmtastatur
   nimmt davon über die Hälfte. Ein 64 px hoher Kopf frisst dann ein Sechstel
   dessen, was übrig bleibt — deshalb legt er sich hier eng an das an, was er
   trägt (die 48 px hohe Suchpille). Der Kopf ist damit 52 statt 64 px hoch;
   an den Fingerzielen darin ändert sich nichts, die bleiben bei 44 px.
   Die Regel greift über die HÖHE, nicht über die Breite: Sie soll auf einem
   flachen Fenster am Desktop genauso wirken. */
@media (max-height: 500px) {
  .wx-kopf-in { min-height: 0; padding-block: 2px; }
}

.wx-marke {
  display: inline-flex; align-items: center; gap: var(--sp-8);
  color: var(--ink); font-weight: 700; font-size: var(--fs-18);
  letter-spacing: -.02em; flex: 0 0 auto;
  /* Feste Mindestgröße, bewusst KEINE --sp-Größe: Ein Fingerziel ist eine
     körperliche Größe und darf nicht mit der Schrift mitwachsen. Die BREITE
     zählt mit — auf sehr schmalen Geräten blendet die Regel unten die Wortmarke
     aus, und übrig bliebe sonst ein 30 px schmales Ziel. */
  min-height: var(--ziel); min-width: var(--ziel); justify-content: center;
}
@media (hover: hover) and (pointer: fine) {
  .wx-marke:hover { text-decoration: none; }
}
.wx-marke svg { width: 30px; height: 30px; flex: 0 0 auto; }
@media (min-width: 1000px) { .wx-marke svg { width: 34px; height: 34px; } }
.wx-marke b { color: var(--acc); font-weight: 700; }
/* Auf sehr schmalen Geräten hat die Suche Vorrang vor der Wortmarke. */
@media (max-width: 419px) { .wx-marke span { display: none; } }

.wx-kopf-tools { margin-left: auto; display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }

/* Runder Werkzeugknopf. Im Ruhezustand ohne Schlagschatten (Hausregel) — die
   Fläche grenzt sich über einen Innensaum ab.

   ⚠️ 44 px, nicht 40. Das ist keine gerundete Vorliebe, sondern die Zahl aus
   Apples und Googles Gestaltungsrichtlinien und entspricht ungefähr der
   Auflagefläche einer Fingerkuppe. Bei 40 px lagen die beiden Kopfknöpfe mit
   4 px Abstand nebeneinander — wer den Ort neu bestimmen wollte, traf das
   Thema. Bewusst eine feste Größe und KEINE --sp-Größe: Ein Fingerziel ist eine
   körperliche Größe und darf nicht mit der Schrift mitwachsen. */
.wx-ik {
  width: var(--ziel); height: var(--ziel); display: grid; place-items: center;
  border-radius: var(--r-pill); color: var(--body);
  box-shadow: var(--sh-saum);
  transition: background-color var(--mo-kurz) var(--ease-standard),
              color var(--mo-kurz) var(--ease-standard),
              transform var(--mo-kurz) var(--ease-standard);
}
@media (hover: hover) and (pointer: fine) {
  .wx-ik:hover { background: var(--soft); color: var(--ink); }
}
/* ⚠️ Der gedrückte Zustand ist DEUTLICH — er darf nicht aussehen wie ein bloßes
   Darüberfahren. Auf dem Handy gibt es kein :hover, das die Berührung quittieren
   könnte; die Fläche muss also von sich aus sichtbar antworten. Und sie muss
   sich auch von :hover abheben: Manche mobilen Browser legen beim Tippen zuerst
   :hover an — wäre :active dasselbe, bliebe die Berührung unbeantwortet. Deshalb
   die kräftigere Fläche UND das Einsinken. */
.wx-ik:active{ transform: scale(.92); background: var(--line-stark); color: var(--ink); }
.wx-ik svg { width: 20px; height: 20px; }
.wx-ik[aria-pressed="true"] { background: var(--acc); color: var(--acc-ink); }

/* ── Suche ────────────────────────────────────────────────────────────────*/
.wx-suche { position: relative; flex: 1 1 auto; min-width: 0; max-width: 460px; }
/* ⚠️ Das Polster liegt WAAGERECHT am Label und SENKRECHT am Eingabefeld. Vorher
   war es umgekehrt: 8 px oben und unten am Label, 32 px Feldhöhe — die Pille sah
   mit 48 px richtig aus, aber antippbar waren nur die mittleren 32. Ein Feld,
   das man am oberen Rand berührt und das dann nicht den Fokus bekommt, wirkt
   kaputt. Jetzt trägt das Feld die vollen 44 px und die Pille bleibt bei
   denselben 48 px Außenhöhe — optisch identisch, nur eben trefffähig. */
.wx-suche-feld {
  display: flex; align-items: center; gap: var(--sp-8);
  background: var(--soft); border-radius: var(--r-pill);
  padding: 2px var(--sp-12);
  box-shadow: var(--sh-saum);
  transition: box-shadow var(--mo-kurz) var(--ease-standard);
}
.wx-suche-feld:focus-within { box-shadow: var(--sh-saum), var(--sh-ring-acc); }
.wx-suche-feld svg { width: 18px; height: 18px; color: var(--muted); flex: 0 0 auto; }
.wx-suche input {
  flex: 1 1 auto; min-width: 0; background: none; border: 0;
  color: var(--ink);
  /* ⚠️ --fs-16 (also nie unter 16 px), nicht --fs-14. Safari auf dem iPhone
     zoomt die GANZE Seite hinein, sobald ein Eingabefeld mit weniger als 16 px
     den Fokus bekommt — und zoomt danach nicht von selbst zurück. Die Kopfzeile
     stünde also nach jeder Suche vergrößert und halb außerhalb. Der Weg über
     `maximum-scale=1` verbietet stattdessen jedes Zoomen und nimmt
     sehbehinderten Nutzern eine Grundfunktion — das ist keine Option.
     Die Stufe ist mit Bedacht gewählt: Sie ist auf jedem Bildschirm ≥ 16 px. */
  font-size: var(--fs-16);
  min-height: var(--ziel);
}
.wx-suche input::placeholder { color: var(--muted); }
.wx-suche input:focus { outline: none; }
/* Der Löschknopf steckt IN der Pille; ohne feste Größe war er ein 20 px großer
   Buchstabe. Der negative Rand hält die Pille dabei genau so breit wie vorher. */
.wx-suche-x {
  display: none; flex: 0 0 auto;
  width: 40px; height: var(--ziel); place-items: center;
  margin-inline-end: calc(-1 * var(--sp-10));
  border-radius: var(--r-pill);
  color: var(--muted); font-size: var(--fs-16); line-height: 1;
}
.wx-suche.hat-text .wx-suche-x { display: grid; }
.wx-suche-x:active{ color: var(--ink); background: var(--line); }
@media (hover: hover) and (pointer: fine) {
  .wx-suche-x:hover { color: var(--ink); background: var(--line); }
}

/* ⚠️ Die Höhe hängt an `dvh`, nicht an `vh` — und die Liste rollt in sich.
   `vh` meint auf dem Handy die GRÖSSTE Fensterhöhe, also die mit eingeklappter
   Adressleiste. Beim Tippen ist die Leiste aber ausgefahren UND die Tastatur
   steht davor: Die Liste war damit rechnerisch 60 % einer Höhe, die es gerade
   nicht gibt, und ihre unteren Einträge lagen hinter der Tastatur. `dvh` folgt
   der tatsächlichen Höhe; auf Browsern, die die virtuelle Tastatur zusätzlich
   aus dem Viewport rechnen (Chrome/Android), schrumpft die Liste sogar mit.
   ⚠️ Deshalb außerdem `overscroll-behavior: contain`: Am Ende der Liste soll
   die Geste NICHT die Seite darunter mitziehen — sonst rollt der Hintergrund
   weg, während die Vorschläge stehen bleiben. */
.wx-vorschlaege {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: var(--z-blatt);
  background: var(--overlay); border-radius: var(--r-14);
  box-shadow: var(--sh-3), var(--sh-saum);
  overflow: hidden; overflow-y: auto;
  max-height: min(60dvh, 420px);
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  /* Auf dem Handy schwebt der Kopf über dem Helden — das Milchglas (`--flaeche`
     transluzent) ließe den Wetterinhalt scharf DURCH die Liste durchscheinen,
     und Ortsname/Uhrzeit/Temperatur stünden über wechselndem Himmel und Zahlen.
     Ein kräftiger Weichzeichner smeart den Untergrund wie bei iOS, sodass die
     Zeilen ruhig lesbar bleiben. (Kein Grundlagen-Weichzeichner über der
     Animation — die Liste ist nicht der Held; siehe Kopfzeile/Ziehen-Kreis.) */
  backdrop-filter: saturate(140%) blur(20px);
  -webkit-backdrop-filter: saturate(140%) blur(20px);
}
/* Schwache Geräte: statt Weichzeichnen eine deckende Fläche (wie am Ziehen-Kreis). */
.wx-sparsam .wx-vorschlaege {
  background: rgba(9, 14, 32, .96);
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
.wx-vorschlaege[hidden] { display: none; }
/* Eine Zeile trägt ZWEI Schaltflächen nebeneinander (auswählen / vergessen) —
   nie ineinander, siehe die Begründung in index.php. */
.wx-vs-reihe { display: flex; align-items: stretch; border-bottom: 1px solid var(--line); }
.wx-vs-reihe:last-child { border-bottom: 0; }
@media (hover: hover) and (pointer: fine) {
  .wx-vs-reihe:hover { background: var(--soft); }
}

.wx-vs {
  display: flex; align-items: center; gap: var(--sp-8);
  flex: 1 1 auto; min-width: 0; min-height: var(--ziel);
  padding: var(--sp-10) var(--sp-12); text-align: left; color: var(--body);
}
.wx-vs:focus-visible, .wx-vs:active{ background: var(--soft); }
@media (hover: hover) and (pointer: fine) {
  .wx-vs:hover { background: var(--soft); }
}
/* Pin des aktuellen Standorts — die erste Zeile ist so als „mein Standort"
   erkennbar (er steht immer ganz oben). */
.wx-vs-hier { flex: 0 0 auto; display: grid; place-items: center; width: 20px; height: 20px; color: var(--acc); }
.wx-vs-hier svg { width: 100%; height: 100%; display: block; }
/* Name über Untertitel — bei gespeicherten Orten steht im Untertitel die
   aktuelle Ortszeit, rechts daneben die zuletzt gesehene Temperatur (iOS-Look). */
.wx-vs-txt {
  display: flex; flex-direction: column; justify-content: center; gap: 2px;
  flex: 1 1 auto; min-width: 0;
}
.wx-vs-txt b { color: var(--ink); font-size: var(--fs-14); font-weight: 600; white-space: nowrap; }
.wx-vs-txt > span {
  color: var(--muted); font-size: var(--fs-12);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wx-vs-wetter {
  flex: 0 0 auto; display: flex; align-items: center; gap: var(--sp-6);
  margin-left: var(--sp-8);
}
.wx-vs-sym { width: 28px; height: 28px; flex: 0 0 auto; }
.wx-vs-sym svg { width: 100%; height: 100%; display: block; }
.wx-vs-wetter b { color: var(--ink); font-size: var(--fs-20); font-weight: 600; }
.wx-vs-weg {
  flex: 0 0 auto; width: var(--ziel); display: grid; place-items: center;
  color: var(--muted); border-radius: var(--r-10);
}
.wx-vs-weg svg { width: 16px; height: 16px; }
.wx-vs-weg:active{ color: var(--schlecht); background: var(--soft); }
@media (hover: hover) and (pointer: fine) {
  .wx-vs-weg:hover { color: var(--schlecht); background: var(--soft); }
}

.wx-vs-kopf {
  padding: var(--sp-10) var(--sp-12) var(--sp-6); color: var(--muted);
  font-size: var(--fs-12); font-weight: 600; text-transform: uppercase; letter-spacing: .07em;
}
.wx-vs-leer { padding: var(--sp-12); color: var(--muted); font-size: var(--fs-13); }
/* In der Orts-Karten-Ansicht hat das Panel kein seitliches Polster mehr — die
   Überschrift muss ihr eigenes ablegen, sonst stünde sie 12 px weiter innen als
   die Karten darunter. */
.wx-vorschlaege.zeigt-karten .wx-vs-kopf { padding-inline: 0; }

/* ⚠️ QUERFORMAT MIT TASTATUR: Gemessen bleiben nur ~214 px sichtbar, davon ~142 px
   für die Liste (der Rest ist Kopf + Suchfeld). Mit dem normalen Kopf (35 px) und
   Zeilen (63 px) passt nur EIN gespeicherter Ort ganz hinein — qa/wetter-mobil.mjs
   verlangt im Querformat aber zwei. Im niedrigen Viewport werden Kopf und Zeilen
   deshalb enger; das 44-px-Fingerziel bleibt über `min-height: var(--ziel)`
   erhalten. So passen zwei Zeilen (Kopf ~23 + 2×~52 = 127 ≤ 142). */
@media (max-height: 520px) {
  .wx-vs-kopf { padding-top: var(--sp-6); padding-bottom: 2px; }
  .wx-vs { padding-top: var(--sp-6); padding-bottom: var(--sp-6); }
}

/* 🚨 DIE ORTS-KARTEN-LISTE BRICHT AUF VOLLE BREITE AUS. Das Vorschlags-Dropdown
   sitzt normal unter dem Suchfeld und ist nur so breit wie dieses (der Standort-
   Knopf stiehlt rechts Platz) — für schmale Suchtreffer richtig, für die
   vollflächigen Orts-Karten zu eng (Namen brachen ab, der Held schien seitlich
   durch). Mit `.zeigt-karten` (js/app.js setzt sie nur im Verlauf) liegt die
   Liste stattdessen viewport-fest und randbündig unter dem Kopf — eine ruhige,
   iOS-artige „Orte"-Fläche. Der Suchtreffer-Fall bleibt schmal unter dem Feld.
   DOM-Kind der Suche bleibt sie: Klick-außerhalb/Fokus-Logik (Containment) und
   `vorschlagHoehe()` (misst getBoundingClientRect) funktionieren unverändert. */
.wx-vorschlaege.zeigt-karten {
  position: fixed;
  /* DIESELBEN Seitenränder wie der übrige Seiteninhalt (`--wx-rand-*`, inkl.
     Safe-Area), damit die Karten bündig unter dem Held/den Tageskarten stehen.
     Vorher lag der Panel-Rand bei 16 px UND innen nochmal 12 px Polster — die
     Karten standen dadurch 12 px weiter innen als der Rest der Seite (gemeldet:
     „warum ist der padding im Dialog größer?"). Innen jetzt KEIN seitliches
     Polster mehr. */
  left: var(--wx-rand-l); right: var(--wx-rand-r);
  top: calc(var(--kopf-h) + var(--wx-sat) + var(--sp-8));
  /* Auf dem Desktop NICHT über die ganze Breite (die Karten würden sonst über
     1000 px breit) — gedeckelt und mittig als ruhige Ortsliste. Auf dem Handy
     greift die Deckelung nicht, dort füllt sie die Seitenränder. */
  max-width: 520px; margin-inline: auto;
  max-height: min(78dvh, 760px);
  padding: var(--sp-8) 0 var(--sp-12);
  /* KEIN eigener Kasten. Der Schirm dahinter ist der Untergrund; ein zusätzlicher
     Rahmen samt Hintergrund um die Karten ließ zwei ineinander liegende Ränder
     entstehen (gemeldet: „2 Karten mit border Stroke sehen blöd aus"). Die Karten
     stehen jetzt frei auf dem Schirm, jede mit ihrem eigenen Himmel und Schatten. */
  background: transparent;
  box-shadow: none;
  border-radius: 0;
  -webkit-backdrop-filter: none; backdrop-filter: none;
}

/* 🚨 Der VERDUNKELNDE SCHIRM hinter den Orts-Karten. Zwei gemeldete Mängel auf
   einmal: Das Panel schwebte ohne Trennung über der Seite (sah unfertig aus),
   UND die Seite ließ sich dahinter weiterscrollen. Ein Schirm über der ganzen
   Fläche trennt das Panel sichtbar, dimmt die unruhige Seite dahinter, fängt den
   Außenklick zum Schließen und sperrt — zusammen mit `:root.wx-orte-offen` — das
   Scrollen. Er liegt bewusst UNTER dem Kopf (`--z-orte-schirm` < `--z-kopf`):
   Suchfeld, Knöpfe und das Panel (Kind des Kopf-Stapelkontexts) bleiben hell
   darüber, nur der Seiteninhalt darunter wird gedimmt. `body::before`/`::after`
   sind beide vom festen Himmel belegt — daher ein eigenes Element, kein Pseudo. */
.wx-orte-schirm {
  position: fixed; inset: 0; z-index: var(--z-orte-schirm);
  /* NAHEZU DECKEND. Bei 55 % schien die unruhige Seite überall durch, und das
     Ganze wirkte „halb durchsichtig" (gemeldet). Ein dunkler, fast voller
     Untergrund lässt die Seite verschwinden — die Karten stehen dann auf einer
     ruhigen Fläche wie bei der iOS-Wetter-Ortsliste. Der kräftige Weichzeichner
     ist die Versicherung für die restlichen 4 %: im Tag-Modus (heller Held)
     bliebe sonst ein Geist erkennbar; verwischt wird daraus eine ruhige Fläche. */
  background: rgba(8, 13, 30, .96);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  backdrop-filter: blur(14px) saturate(120%);
  touch-action: none;               /* keine Wischgeste erreicht die Seite darunter */
  animation: wx-schirm-auf var(--mo-kurz) var(--ease-standard);
}
/* Schwache Geräte können kein backdrop-filter — dann voll deckend statt verwischt. */
.wx-sparsam .wx-orte-schirm {
  background: rgba(8, 13, 30, .99);
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
.wx-orte-schirm[hidden] { display: none; }
@keyframes wx-schirm-auf { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .wx-orte-schirm { animation: none; } }
/* Sperrt das Weiterscrollen der Seite, solange die Orts-Karten offen sind. */
:root.wx-orte-offen { overflow: hidden; }

/* ── Orts-Karten: ein Mini-Held je gespeichertem Ort ───────────────────────
   Der Verlauf (Suchfeld leer → gespeicherte Orte) zeigt je Ort eine Karte mit
   dem EIGENEN Himmel des Ortes, aktueller Temperatur, Zustand, Tagesspanne und
   Einigkeit — statt einer schmalen Zeile. Bewusst STATISCH (kein WXFx, keine
   Geste): eine kleine Karte mit Verlauf + Symbol wirkt nie „flach/tot" wie ein
   Vollbild-Panel, und es stehen nie zwei Himmel nebeneinander. Suchtreffer beim
   Tippen bleiben die schmalen `.wx-vs`-Zeilen (dort gibt es keine Wetterdaten).
   Ausarbeitung: wetter/ORTSWECHSEL-NEUPLAN.md. */
.wx-ok-reihe { position: relative; }
.wx-ok-reihe + .wx-ok-reihe { margin-top: var(--sp-8); }
.wx-ok {
  display: block; width: 100%; text-align: left; position: relative; overflow: hidden;
  min-height: 104px; border-radius: var(--r-18); color: #fff; font-family: inherit;
  padding: var(--sp-12) var(--sp-16);
  background: linear-gradient(158deg, var(--himmel-1, #1b3570) 0%,
              var(--himmel-2, #112450) 54%, var(--himmel-3, #0a1430) 100%);
  box-shadow: var(--sh-1), var(--sh-saum);
  transition: transform var(--mo-kurz) var(--ease-standard);
}
.wx-ok:active { transform: scale(.985); }
@media (prefers-reduced-motion: reduce) { .wx-ok { transition: none; } }
/* Leseschutz für weißen Text auf hellem Tag-Himmel */
.wx-ok::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(3, 8, 22, 0) 42%, rgba(3, 8, 22, .34) 100%); }
/* optionaler statischer Sonnenschein — nur bei klarer Tag-Klasse (.ist-sonnig) */
.wx-ok.ist-sonnig::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(130% 90% at 88% -18%, rgba(255, 241, 200, .26) 0%, rgba(255, 241, 200, 0) 46%); }
.wx-ok > * { position: relative; }

.wx-ok-top   { display: flex; align-items: flex-start; gap: var(--sp-8); padding-right: 30px; }  /* Platz für [✕] oben rechts */
.wx-ok-name  { min-width: 0; flex: 1 1 auto; display: flex; flex-direction: column; }
.wx-ok-titel { display: flex; align-items: center; gap: var(--sp-6); min-width: 0; }
.wx-ok-name b { font-size: var(--fs-20); font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wx-ok-pin  { flex: 0 0 auto; width: 16px; height: 16px; }
.wx-ok-pin svg { width: 100%; height: 100%; display: block; }
.wx-ok-sub  { font-size: var(--fs-13); color: rgba(255, 255, 255, .8); margin-top: 1px; }
.wx-ok-jetzt{ display: flex; align-items: center; gap: var(--sp-6); flex: 0 0 auto; }
.wx-ok-sym  { width: 34px; height: 34px; } .wx-ok-sym svg { width: 100%; height: 100%; display: block; }
.wx-ok-temp { font-size: var(--fs-28); font-weight: 200; font-variant-numeric: tabular-nums; }
.wx-ok-temp sup { font-size: .5em; font-weight: 400; vertical-align: super; margin-left: 1px; opacity: .85; }
.wx-ok-fuss { display: flex; align-items: center; gap: var(--sp-12); margin-top: var(--sp-8); flex-wrap: wrap; }
.wx-ok-spanne { font-size: var(--fs-14); color: rgba(255, 255, 255, .9); font-variant-numeric: tabular-nums;
  display: inline-flex; gap: var(--sp-12); }
.wx-ok-spanne i { font-style: normal; opacity: .7; margin-right: 2px; }
.wx-ok-leer { font-size: var(--fs-14); color: rgba(255, 255, 255, .62); }
/* Der aktuelle Standort trägt KEINEN Rahmen mehr — der 📍-Pin am Namen markiert
   ihn, und er steht ohnehin immer ganz oben. Ein zusätzlicher heller Ring um
   genau diese eine Karte las sich als Fehler, nicht als Hervorhebung (gemeldet). */

.wx-ok-weg { position: absolute; top: 8px; right: 8px; width: 30px; height: 30px;
  border-radius: 50%; display: grid; place-items: center;
  color: rgba(255, 255, 255, .62); background: rgba(3, 8, 20, .32); }
.wx-ok-weg::after { content: ""; position: absolute; inset: -8px; }   /* Trefferfläche ≥ 44 px */
.wx-ok-weg svg { width: 15px; height: 15px; }
.wx-ok-weg:active { color: #fff; background: rgba(3, 8, 20, .5); }
@media (hover: hover) and (pointer: fine) { .wx-ok-weg:hover { color: #fff; } }

@media (max-height: 520px) {   /* Handy quer / kurzer Viewport */
  .wx-ok { min-height: 76px; padding: var(--sp-8) var(--sp-12); }
  .wx-ok-name b { font-size: var(--fs-16); }
  .wx-ok-temp { font-size: var(--fs-24); } .wx-ok-sym { width: 28px; height: 28px; }
  .wx-ok-fuss { margin-top: var(--sp-6); }
}

/* ── Abschnitte ───────────────────────────────────────────────────────────*/
.wx-abschnitt { margin-top: var(--sp-32); }

/* ⚠️ `flex-wrap` UND `flex: 0 0 auto` an der Überschrift, sonst schrumpft sie
   zugunsten des Untertitels und „Sieben Tage" bricht auf dem Handy mitten
   entzwei — die Überschrift stand zweizeilig, der Untertitel einzeilig daneben.
   Jetzt: erst die ganze Überschrift, der Untertitel rutscht bei Platzmangel in
   die nächste Zeile. */
.wx-abschnitt-kopf {
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: 2px var(--sp-10);
  margin-bottom: var(--sp-12);
}
/* ⚠️ `flex: 0 1 auto` mit `min-width: 0`, nicht `0 0 auto`. Starr durfte die
   Überschrift nie schrumpfen — bei 150 % Systemschrift ragte „Die nächsten 48
   Stunden" damit auf einem 360-px-Gerät 71 px über den Bildschirmrand hinaus.
   Schrumpfen heißt hier nur: zwischen den WÖRTERN umbrechen. Der ursprüngliche
   Grund für die starre Angabe (die Überschrift wurde zugunsten des Untertitels
   gequetscht und brach mitten im Wort) bleibt gewahrt, weil der Untertitel
   `flex: 1 1 auto` hat und bei Platzmangel in die nächste Zeile rutscht. */
.wx-abschnitt-kopf h2 { flex: 0 1 auto; min-width: 0; font-size: var(--fs-20); }
/* ⚠️ `min-height`: Die Zeile ist im Auslieferungszustand LEER und wird erst
   gefüllt, wenn die Daten da sind. Ohne reservierten Platz wächst der
   Abschnittskopf in dem Moment um eine Zeile und schiebt alles darunter nach —
   derselbe Sprung, den der Held und die Karten längst nicht mehr machen. */
.wx-abschnitt-kopf p {
  flex: 1 1 auto; color: var(--muted); font-size: var(--fs-13);
  min-height: 1.45em;
}

/* ── Die Frische-Zeile IST der Knopf zum Neuladen ─────────────────────────
   ⚠️ `align-self: center` hebt das `align-items: baseline` der Reihe für
      dieses eine Kind auf. Ein 48 px hoher Knopf an der Schriftlinie
      ausgerichtet hinge um seine halbe Höhe nach unten und zöge die
      Überschrift optisch mit; die Überschrift selbst soll ihre Grundlinie
      behalten (dort steht auf anderen Abschnitten ein Untertitel daneben).
   ⚠️ Der negative Einzug links ist Absicht: Der TEXT soll auf derselben Kante
      stehen wie alles andere im Abschnitt, die anfassbare Fläche aber darf
      darüber hinausgehen. Ohne ihn steht die Zeile um zehn Punkte eingerückt
      neben einer Überschrift, die es nicht ist. */
/* ⚠️ EIGENE ZEILE, IMMER — nicht „wenn es nicht mehr passt". Der Knopf ist im
   Auslieferungszustand leer und schmal, stand also neben der Überschrift; mit
   den Daten wurde er breit und rutschte in die nächste Zeile. Gemessen sprang
   der Abschnittskopf dadurch von 48 auf 79 px, und alles darunter mit. Ob
   umbrochen wird, hing damit an der LÄNGE eines Textes, der je nach Alter und
   Zahl der antwortenden Modelle anders lang ist — also am Zufall. Dieselbe
   Lehre wie bei der Ortszeile im Helden und der Tagesspanne. */
/* ⚠️ ZWEI ELEMENTE, ZWEI AUFGABEN — und das ist keine Verschachtelung um ihrer
   selbst willen. Der KNOPF hält den Platz frei (zwei Zeilen, siehe unten); die
   FLÄCHE darin legt sich um den Text. Beides in einem Element hieße: entweder
   springt die Zeile, oder die Hover-Fläche zieht sich über die ganze Breite
   und ist bei einzeiligem Text sichtbar zu hoch — auf dem Desktop stand dort
   ein leerer Balken über die halbe Seite. */
.wx-stand {
  flex: 1 1 100%; min-width: 0;
  display: flex; align-items: center; justify-content: flex-start;
  margin-inline-start: calc(-1 * var(--sp-10));
  color: var(--muted); font-size: var(--fs-13); text-align: start;
  /* ⚠️ ZWEI ZEILEN WERDEN IMMER RESERVIERT, auch wenn auf einem breiten Schirm
     nur eine gebraucht wird. Diese Zeile ist der einzige Text der Seite, dessen
     LÄNGE sich im Betrieb ändert („gerade aktualisiert" gegen „vor 12 Minuten
     (gespeichert)", neun oder elf antwortende Modelle) — eine Reservierung, die
     von dieser Länge abhängt, ist geraten. In `em`, damit sie bei vergrößerter
     Systemschrift mitwächst; gemessen bleibt es damit auch bei 150 % auf einem
     360-px-Gerät bei zwei Zeilen. */
  min-height: calc(3.2em + 2 * var(--sp-6));
}
.wx-stand-in {
  display: flex; align-items: center; min-width: 0;
  min-height: var(--ziel); padding: var(--sp-6) var(--sp-10);
  border-radius: var(--r-pill);
  transition: background-color var(--mo-kurz) var(--ease-standard),
              color var(--mo-kurz) var(--ease-standard);
}
/* Das Symbol fließt IM Text mit, hinter dem letzten Wort — es ist deshalb
   `inline-block` in einem gewöhnlichen Textblock, kein eigenes Flex-Kind. */
.wx-stand svg {
  display: inline-block; width: 15px; height: 15px;
  vertical-align: -3px; margin-inline-start: var(--sp-6);
}
@media (hover: hover) and (pointer: fine) {
  .wx-stand:hover .wx-stand-in { background: var(--soft); color: var(--body); }
}
.wx-stand:active .wx-stand-in { background: var(--soft); transform: scale(.985); }
.wx-stand[data-laedt] { color: var(--body); }
.wx-stand[data-laedt] svg { animation: wx-dreh .85s linear infinite; }
@media (prefers-reduced-motion: reduce) {
  .wx-stand[data-laedt] svg { animation: none; }
}

/* ── Fuß ──────────────────────────────────────────────────────────────────*/
/* ══════════════════════════════════════════════════════════════════════════
   DER FUSS
   ══════════════════════════════════════════════════════════════════════════
   🚨 AUF DEM HANDY WAR ER STRUKTURLOS — fotografiert gemeldet. Drei Dinge
      liefen zusammen, und jedes hatte denselben Grund: Es gab keine sichtbare
      Gliederung, nur Abstände.

      • Die Datenquellen standen als Fließtext hintereinander („Open-Meteo
        (CC BY 4.0)  MET Norway / Bright Sky — Datenbasis Deutscher
        Wetterdienst"). Wo ein Name aufhörte und der nächste anfing, war nicht
        zu erkennen; die Lücken sahen aus wie Wortabstände. Jetzt trennt ein
        Mittelpunkt, und der steht als `::after` am Element — nicht als Zeichen
        im Markup, sonst müsste jemand beim Hinzufügen einer Quelle daran
        denken.
      • Die Quellen-LINKS sahen nicht wie Links aus (nur `--body`, keine
        Unterstreichung), während die OpenStreetMap-Nennung unterstrichen war.
        Zwei Erscheinungsbilder für dieselbe Sache im selben Absatz.
      • Die Navigation brach in zwei Reihen mit scheinbar riesigem Abstand um.
        Der Abstand war gar keiner: Jeder Link ist ein 44 px hohes Fingerziel,
        und zwei davon übereinander SIND 88 px. Als Reihe mit Umbruch sah das
        nach Fehler aus. Jetzt ein festes Raster — gleich hohe Zeilen, gleiche
        Spalten, und der Umbruch hängt nicht mehr an der Wortlänge.

   ⚠️ Der Fingerziel-Abstand bleibt bei 44 px. Er ist der Grund für die Höhe,
      nicht ein Versehen — auf dem Handy trifft man 14 px hohe Links
      nebeneinander schlicht nicht zuverlässig. */
.wx-fuss {
  margin-top: var(--sp-32);
  /* 🚨 SAFE-AREA am unteren Rand gehört HIER hin — der Fuß ist das unterste
     Element der Seite und muss den Home-Indikator selbst freihalten, statt sich
     auf das `padding-bottom` von `main` zu verlassen (gemeldet: „Safe Area im
     Footer fehlt komplett"). `--wx-sab` = `env(safe-area-inset-bottom)`; dieselbe
     Variable trägt schon oben die Notch, also greift sie hier verlässlich. */
  padding: var(--sp-20) 0 calc(var(--sp-24) + var(--wx-sab));
  border-top: 1px solid var(--line);
  color: var(--muted); font-size: var(--fs-12);
}
@media (min-width: 700px) {
  .wx-fuss { margin-top: var(--sp-48); padding: var(--sp-24) 0 calc(var(--sp-24) + var(--wx-sab)); }
}

/* 🚨 AUF DEM HANDY BEKOMMT JEDE QUELLE IHRE EIGENE ZEILE — KEINE TRENNER.
   Zwei Anläufe mit inline-Mittelpunkten sind daran gescheitert, dass die Liste
   umbricht: Als `::after` blieb der Punkt am Zeilenende hängen (hinter „MET
   Norway"), als `::before` stand er am Zeilenanfang und las sich wie ein
   Aufzählungszeichen. Beides ist dasselbe Problem — ein Trennzeichen zwischen
   zwei Dingen kann nicht funktionieren, wenn zwischen ihnen ein Zeilenumbruch
   liegt. Drei Quellen auf drei Zeilen brauchen gar keinen Trenner: Der Umbruch
   IST die Trennung, und man sieht sofort, wo eine Angabe aufhört.
   Ab 560 px passen sie nebeneinander — dort trennt der Punkt wieder, und zwar
   ohne umzubrechen. */
.wx-fuss-quellen {
  display: grid; gap: 2px;
  margin-bottom: var(--sp-16); line-height: 1.5;
}
@media (min-width: 560px) {
  .wx-fuss-quellen {
    display: flex; flex-wrap: wrap; align-items: baseline;
    gap: 4px var(--sp-8);
  }
}
/* Der erste Eintrag ist die Beschriftung — sie bekommt keinen Trenner davor
   und darf nicht mit den Quellen verwechselt werden. */
.wx-fuss-quellen > span:first-child { color: var(--ink); font-weight: 600; }
/* ⚠️ DER TRENNER STEHT VOR DEM EINTRAG, NICHT DAHINTER — und das ist der
   Unterschied zwischen „ordentlich" und „kaputt". Als `::after` gehörte er zum
   VORIGEN Element und blieb beim Umbruch als einsamer Punkt am Zeilenende
   hängen (im Bild hinter „MET Norway"). Als `::before` ist er Teil des
   FOLGENDEN Elements und kann von ihm nicht getrennt werden — inline-Inhalt
   eines Elements bricht nie vor seinem eigenen Anfang um.
   Am Element und nicht im Markup steht er, damit beim Ergänzen einer Quelle
   niemand daran denken muss.
   ⚠️ `nth-child(n+3)`: Kind 1 ist die Beschriftung „Datenquellen:", Kind 2 die
   erste Quelle — davor gehört kein Trenner. */
@media (min-width: 560px) {
.wx-fuss-quellen > :nth-child(n+3)::before {
  content: "·"; margin-inline-end: var(--sp-8); color: var(--line-stark);
  /* ⚠️ `display: inline-block` UND `text-decoration: none`: Das Pseudo-Element
     sitzt IM Link und erbt sonst dessen Unterstreichung — im Bild hatte der
     Trennpunkt einen eigenen Strich darunter und sah aus wie ein weiterer,
     kaputter Link. Die Eigenschaft allein genügt nicht, weil Unterstreichungen
     von Vorfahren durch Nachkommen hindurchgezeichnet werden; erst ein eigener
     Block unterbricht sie. */
  display: inline-block; text-decoration: none;
}
}
.wx-fuss-quellen a {
  /* ⚠️ `justify-self: start`: Im Raster wäre ein Link sonst zellenbreit, und
     die ganze Zeile bis zum rechten Rand reagierte auf Berührung — auch dort,
     wo gar kein Text steht. */
  justify-self: start;
  color: var(--body);
  text-decoration: underline; text-underline-offset: 2px;
  text-decoration-color: var(--line-stark);
  text-decoration-thickness: 1px;
}
/* ⚠️ Die Navigationslinks im Fuß sind eigenständige Ziele, keine Wörter in
   einem Satz — sie brauchen deshalb echte Antippfläche. Sie vorher von der
   Prüfung auszunehmen war bequem, aber auf dem Handy trifft man 14 px hohe
   Links nebeneinander schlicht nicht zuverlässig. */
/* ⚠️ KEIN negativer Anfangsrand mehr. Er sollte den ersten Link bündig zum
   Text darüber setzen, ließ dafür aber JEDES Ziel 8 px in seinen Nachbarn
   ragen — gemessen 3 px echte Überlappung zwischen „Impressum" und
   „Datenschutz". Wer dort zwischen zwei Links tippt, landet im falschen. Der
   Ausgleich läuft jetzt über die ganze Reihe, nicht über jedes Kind. */
.wx-fuss-geo {
  margin: 0 0 var(--sp-16); line-height: 1.5;
}
.wx-fuss-geo a {
  color: var(--body);
  text-decoration: underline; text-underline-offset: 2px;
  text-decoration-color: var(--line-stark); text-decoration-thickness: 1px;
}
/* Der Haftungshinweis ist die leiseste Zeile des Fußes und steht abgesetzt —
   er gehört weder zu den Quellen noch zur Navigation. */
.wx-fuss-hinweis {
  margin: 0; padding-top: var(--sp-12);
  border-top: 1px solid var(--line);
  line-height: 1.5; max-width: 62ch;
}

/* Auf dem Handy ein festes Zweispaltenraster: gleich hohe Zeilen, gleiche
   Breiten, und wo umbrochen wird, hängt nicht mehr an der Länge der Wörter.
   Ab Tablet wieder eine Reihe — dort ist Platz und ein Raster wäre Leerlauf. */
.wx-fuss-nav {
  display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--sp-6);
  margin-inline-start: calc(-1 * var(--sp-8));
  margin-bottom: var(--sp-12);
}
@media (min-width: 560px) {
  .wx-fuss-nav { display: flex; flex-wrap: wrap; gap: 2px var(--sp-6); }
}
.wx-fuss-nav a {
  display: inline-flex; align-items: center;
  min-height: var(--ziel); padding-inline: var(--sp-8);
  border-radius: var(--r-10);
  font-weight: 600;
}
.wx-fuss-nav a:active{ background: var(--soft); text-decoration: none; }
@media (hover: hover) and (pointer: fine) {
  .wx-fuss-nav a:hover { background: var(--soft); text-decoration: none; }
}

/* ── Meldungen ────────────────────────────────────────────────────────────*/
.wx-hinweis {
  display: flex; align-items: flex-start; gap: var(--sp-10);
  padding: var(--sp-12) var(--sp-16); border-radius: var(--r-14);
  background: var(--soft); box-shadow: var(--sh-saum);
  font-size: var(--fs-13); color: var(--body);
  margin-top: var(--sp-16);
}
.wx-hinweis svg { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 2px; color: var(--warn); }
.wx-hinweis.ist-fehler svg { color: var(--schlecht); }
.wx-hinweis[hidden] { display: none; }

.wx-toast {
  position: fixed; left: 50%; z-index: var(--z-toast);
  bottom: calc(var(--sp-20) + var(--wx-sab)); transform: translate(-50%, 12px);
  padding: var(--sp-10) var(--sp-16); border-radius: var(--r-pill);
  background: var(--flaeche); color: var(--ink); font-size: var(--fs-13);
  box-shadow: var(--sh-3), var(--sh-saum);
  opacity: 0; pointer-events: none;
  transition: opacity var(--mo-mittel) var(--ease-standard),
              transform var(--mo-mittel) var(--ease-standard);
  /* ⚠️ `100%` statt `100vw`: Für ein `position: fixed`-Element bezieht sich das
     Prozent auf den sichtbaren Bereich OHNE Rollbalken — `100vw` schließt ihn
     auf dem Desktop mit ein, und die Meldung ragte dort um dessen Breite über
     den Rand. Dazu die Safe Area je Seite, sonst liegt sie im Querformat unter
     dem Ausschnitt. */
  max-width: calc(100% - var(--wx-rand-l) - var(--wx-rand-r));
}
.wx-toast.an { opacity: 1; transform: translate(-50%, 0); }

/* ── Ziehen zum Aktualisieren ─────────────────────────────────────────────
   Der Zeiger liegt FEST am oberen Rand und wird vom Finger unter dem Kopf
   hervorgezogen. Zwei Dinge daran sind nicht beliebig:

   ⚠️ Er sitzt ÜBER dem Kopf (`--z-ziehen`, die oberste Sprosse). Auf dem Handy
      schwebt der Kopf ohne eigene Fläche über dem Himmel; läge der Zeiger
      darunter, verschwände er beim Herausziehen hinter dem Suchfeld — also
      genau dann, wenn er etwas zu sagen hat.

   ⚠️ Bewegt wird `translate`, nicht `top`. `top` löst bei jedem Bild ein neues
      Layout aus, und diese Bewegung folgt dem Finger — sie läuft also bei
      jedem einzelnen `touchmove`. `translate` bleibt beim Compositor. */
/* ⚠️ Die Vorgaben stehen an der WURZEL, nicht am Zeiger — js/ui-ziehen.js
   schreibt sie dort hin, weil außer dem Zeiger auch der Heldentext mit der
   Geste ausweicht (siehe karten.css). Stünden hier eigene Deklarationen,
   verdeckten sie die geschriebenen Werte, und der Zeiger bliebe stehen. */
:root { --wx-zieh: 0px; --wx-zieh-t: 0; }

.wx-ziehen {
  /* ⚠️ ER BEGINNT UNTER DER KOPFZEILE, NICHT AM BILDSCHIRMRAND. Zuerst stand er
     ganz oben und wanderte quer durch das Suchfeld — ein weißer Kreis, der über
     die Suchpille schrammt, sieht nach einem Fehler aus, nicht nach einer
     Geste. Von der Unterkante des Kopfes aus zieht er in den freien Himmel,
     also genau in die Fläche, die dafür da ist. */
  position: fixed; top: calc(var(--kopf-h) + var(--wx-sat)); left: 0; right: 0;
  z-index: var(--z-ziehen);
  display: flex; justify-content: center;
  pointer-events: none;
  opacity: 0;
  /* ⚠️ ER WÄCHST, ER WANDERT NICHT. Ein Zeiger, der den ganzen Ziehweg
     mitfährt, legt sich zwangsläufig auf den Ortsnamen — zwischen Kopfzeile
     und Name liegen elf Punkte, und der Kreis ist 38 hoch. Zwei Entwürfe sind
     daran gescheitert. Jetzt bleibt er dicht unter der Kopfzeile stehen und
     zeigt den Fortschritt über GRÖSSE und Deckkraft, während der Text unter ihm
     wegzieht. Das ist obendrein näher an dem, was Android beim Ziehen tut.
     Bezugsgröße ist `--wx-zieh-t` (0…1 bis zur Schwelle), nicht der rohe Weg:
     Jenseits der Schwelle passiert sonst weiter etwas, obwohl die Entscheidung
     längst gefallen ist. */
  transform: translate3d(0, calc(var(--wx-zieh-t) * 12px), 0);
}
.wx-ziehen.an { opacity: calc(0.25 + 0.75 * var(--wx-zieh-t)); }
/* Nach dem Loslassen federt er zurück; WÄHREND der Geste darf nichts
   nachlaufen, sonst hängt der Zeiger hinter dem Finger. */
.wx-ziehen:not(.an) {
  transition: transform var(--mo-mittel) var(--ease-standard),
              opacity var(--mo-kurz) var(--ease-standard);
}
/* ⚠️ DUNKLES GLAS, KEINE WEISSE SCHEIBE. Gezogen wird immer am Seitenanfang,
   und dort steht der Held — ein dunkler Himmel, in jedem Thema. Eine weiße
   Scheibe darauf ist der hellste Fleck der ganzen Seite und wirkt wie ein
   Fremdkörper aus einem anderen Programm. Dieselbe Glasfläche wie beim
   gerollten Kopf hält den Zeiger im Bild und im Ton der App. */
.wx-ziehen-kreis {
  position: relative;
  display: grid; place-items: center;
  width: 38px; height: 38px; border-radius: 50%;
  background: rgba(9, 14, 32, .55);
  color: #ffffff;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .26), var(--sh-2);
  backdrop-filter: saturate(150%) blur(12px);
  -webkit-backdrop-filter: saturate(150%) blur(12px);
  transform: scale(calc(.6 + .4 * var(--wx-zieh-t)));
  transition: box-shadow var(--mo-kurz) var(--ease-standard);
}

/* 🚨 EIN RING, DER SICH FÜLLT — die eigentliche Auskunft der Geste.
   Vorher zeigten Größe und Deckkraft den Fortschritt an, und der Pfeil drehte
   sich nebenher. Beides ist relativ: Man sieht, dass etwas passiert, aber nicht,
   WIE WEIT es noch ist — man zieht auf Verdacht, bis der Pfeil zufällig kopfüber
   steht. Ein gefüllter Ring beantwortet genau diese Frage, und zwar an der
   einzigen Stelle, an der man beim Ziehen ohnehin hinsieht.
   ⚠️ `from -90deg` lässt ihn oben beginnen — bei 12 Uhr, nicht bei 3 Uhr.
   ⚠️ Die Maske schneidet die Mitte heraus; ohne sie wäre es eine gefüllte
      Scheibe, die den Pfeil verdeckt.
   ⚠️ Das ist ein Neuzeichnen je Bild, kein Layout. Auf 46 × 46 Punkten ist das
      billig — anders als eine Änderung von `top` oder `width`, die bei jedem
      `touchmove` den ganzen Seitenaufbau anfassen würde. */
.wx-ziehen-kreis::before {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  background: conic-gradient(from -90deg,
    rgba(255, 255, 255, .92) calc(var(--wx-zieh-t) * 360deg),
    rgba(255, 255, 255, .16) 0);
  -webkit-mask: radial-gradient(closest-side, transparent 76%, #000 77%);
          mask: radial-gradient(closest-side, transparent 76%, #000 77%);
  pointer-events: none;
}
/* Auf schwachen Geräten nimmt das Stylesheet den Weichzeichner heraus — dann
   trägt eine deckende Fläche, damit der Pfeil nicht auf dem Himmel schwimmt. */
.wx-sparsam .wx-ziehen-kreis {
  background: rgba(9, 14, 32, .88);
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
.wx-ziehen-kreis svg { width: 19px; height: 19px; }
/* ⚠️ DER PFEIL KIPPT AN DER SCHWELLE, ER DREHT SICH NICHT MEHR MIT.
   Solange der Ring den Fortschritt nicht zeigte, war die stetige Drehung die
   einzige Rückmeldung — jetzt sind es zwei Antworten auf dieselbe Frage, und
   die stetige ist die schlechtere: Ein Pfeil, der bei 60 % schräg steht, sagt
   nichts Bestimmtes. Der Ring zeigt den WEG, der Pfeil den ZUSTAND: aufrecht
   heißt „zieh weiter", kopfüber heißt „loslassen". */
.wx-ziehen-kreis svg {
  transition: transform var(--mo-kurz) var(--ease-standard),
              opacity var(--mo-kurz) var(--ease-standard);
}
.wx-ziehen.bereit .wx-ziehen-kreis svg { transform: rotate(180deg); }
/* Die Schwelle ist erreicht: heller Ring und eine Spur größer. Die FARBE bleibt
   weiß — ein grüner Pfeil auf dunklem Glas hätte 3,1:1 und wäre ausgerechnet in
   dem Moment am schlechtesten zu sehen, in dem er etwas mitteilt. */
.wx-ziehen.bereit .wx-ziehen-kreis {
  box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, .72), var(--sh-3);
}
/* Es lädt: Aus dem gefüllten Ring wird ein umlaufender Bogen, der Pfeil geht.
   Ein stehender Pfeil neben einem drehenden Ring wären zwei Aussagen, von denen
   eine nicht mehr stimmt. */
.wx-ziehen.laedt .wx-ziehen-kreis::before {
  background: conic-gradient(from -90deg,
    rgba(255, 255, 255, .92) 100deg, rgba(255, 255, 255, .16) 0);
  animation: wx-dreh .85s linear infinite;
}
.wx-ziehen.laedt .wx-ziehen-kreis svg { opacity: 0; }
@keyframes wx-dreh { to { transform: rotate(360deg); } }
/* ⚠️ Wer Bewegung abbestellt hat, bekommt trotzdem eine Rückmeldung — nur eine
   ruhende. Ein Zeiger, der gar nichts tut, wäre kein Zustand, sondern ein
   Fehler. Der Ring bleibt also stehen und der Pfeil sichtbar; die Auskunft
   „wie weit noch" trägt der Ring auch unbewegt. */
@media (prefers-reduced-motion: reduce) {
  .wx-ziehen.laedt .wx-ziehen-kreis::before { animation: none; }
  .wx-ziehen.laedt .wx-ziehen-kreis svg { opacity: 1; }
  .wx-ziehen-kreis svg { transform: none; }
}

/* ── Ladezustand ──────────────────────────────────────────────────────────
   Ein Skelett statt eines Spinners: Die Seite zeigt sofort ihre Form, der
   Sprung beim Eintreffen der Daten fällt weg. */
.wx-skelett {
  position: relative; overflow: hidden;
  display: inline-block; vertical-align: middle;
  background: var(--soft); border-radius: var(--r-10); color: transparent;
}
/* Im Helden liegt das Skelett auf dem dunklen Himmel — `--soft` wäre dort eine
   fremde Farbe aus der Seitenpalette. */
.wx-held .wx-skelett { background: rgba(255, 255, 255, .16); }
.wx-held .wx-skelett::after { background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .13), transparent); }
/* Die Marke bleibt beim Laden weg: ein blinkender Umriss dort, wo gleich ein
   Wettersymbol steht, wirkt unruhiger als eine ruhige Fläche. */
.wx-jetzt-sym.wx-skelett, .wx-std-sym.wx-skelett, .wx-tag-sym.wx-skelett { border-radius: 50%; }
.wx-skelett::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .07), transparent);
  animation: wx-schimmer 1.4s linear infinite;
}
@keyframes wx-schimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .wx-skelett::after { animation: none; } }

/* ── Textseiten (ueber.php) ───────────────────────────────────────────────*/
.wx-text { max-width: 74ch; }
.wx-text h1 { font-size: var(--fs-34); margin-bottom: var(--sp-12); }
.wx-text h2 { font-size: var(--fs-24); margin-top: var(--sp-32); margin-bottom: var(--sp-10); }
.wx-text h3 { font-size: var(--fs-18); margin-top: var(--sp-20); margin-bottom: var(--sp-6); }
.wx-text p, .wx-text ul, .wx-text ol { margin-bottom: var(--sp-12); }
.wx-text ul, .wx-text ol { padding-left: 1.3em; }
.wx-text li { margin-bottom: 4px; }
.wx-text code {
  background: var(--soft); border-radius: var(--r-6); padding: 1px 5px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .92em; color: var(--ink);
}
.wx-text .wx-lead { font-size: var(--fs-18); color: var(--ink); }
.wx-tabelle { width: 100%; border-collapse: collapse; font-size: var(--fs-13); margin-bottom: var(--sp-16); }
.wx-tabelle th, .wx-tabelle td { padding: var(--sp-8) var(--sp-10); text-align: left; border-bottom: 1px solid var(--line); }
.wx-tabelle th { color: var(--ink); font-weight: 600; }
/* ⚠️ Eine `<caption>` ist standardmäßig ZENTRIERT und erbt die Textfarbe nicht —
   in einer Tabelle voller linksbündiger Spalten liest sie sich sonst wie eine
   Überschrift, die nicht dazugehört. */
.wx-tabelle > .wx-tab-cap {
  caption-side: top; text-align: left; color: var(--muted);
  font-size: var(--fs-12); padding: 0 var(--sp-10) var(--sp-8);
}
.wx-roll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
