/* ============================================================
   Gemeinsame Basis der Auto-Apps von ManniMindset
   Tanken, Tempo, Parken (und was noch kommt: iq, oel)

   EINE Quelle fuer alles, was in allen Apps gleich aussieht. Vorher lag jeder
   Baustein dreimal vor: gemessen 738 von 2.406 CSS-Zeilen, rund 30 Prozent.
   Jede Aenderung musste dreimal gemacht werden, und genau dabei ist Parken
   auseinandergelaufen (Banner 4 px hoeher, einfarbig statt Verlauf, ohne
   Schimmer, Aktualisieren-Knopf grau statt blau).

   Eingebunden VOR der App-eigenen Datei:
     <link rel="stylesheet" href="/app/basis.css">
     <link rel="stylesheet" href="styles.css?v=N">
   Umgekehrt gewinnt die Basis und die App verliert ihre Eigenheiten.

   App-eigen bleibt: das Zeilenraster (.app grid-template-rows) und alles
   Fachliche (Preiszellen, Eingabefelder, Preisampel).

   FARBEN: App-eigene Farben bekommen app-eigene Namen, die hier nicht
   vorkommen. Muss doch einmal ein Token von hier ueberschrieben werden, dann
   ZWINGEND paarweise, also auch im eigenen @media (prefers-color-scheme: dark).
   @media erhoeht die Spezifitaet nicht: wer nur den Hellwert ueberschreibt, hat
   den Dunkelmodus kaputtgemacht und merkt es erst nachts im Auto.

   Kein ?v= noetig: /app/.htaccess schaltet das Caching in diesem Ordner ab,
   genau wie in den App-Ordnern. Eine Aenderung hier ist beim naechsten Start
   in allen Apps da.
   ============================================================ */

:root {
  --blau:   #0091c8;   /* PIG-Blau, nur fuer Bedienelemente */
  --gruen:  #17864a;   /* gut, guenstig, frei */

  --bg:     #f4f5f7;
  --karte:  #ffffff;
  --text:   #10131a;
  --leise:  #5b6472;
  --linie:  #d9dde4;
  --gedimmt:#8a93a1;

  /* Warnfarbe fuer veraltete Staende. Bewusst eigener Name statt --rot: jede App
     darf ihn auf ihre Skala legen (Tanken auf --rot, Parken auf --p4), ohne dass
     die Basis etwas davon wissen muss. */
  --warn:     #c02626;
  --auf-warn: #ffffff;
}

@media (prefers-color-scheme: dark) {
  :root {
    --gruen:  #3ddc84;

    --bg:     #0b0d10;
    --karte:  #171b21;
    --text:   #f2f4f7;
    --leise:  #98a2b3;
    --linie:  #2a3038;
    --gedimmt:#6b7480;

    /* Im Dunkeln ein helles Lachsrot, darauf ist Weiss unlesbar (2,76:1),
       deshalb wandert die Schriftfarbe mit. */
    --warn:     #ff6b6b;
    --auf-warn: #10131a;
  }
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-variant-numeric: tabular-nums;   /* Ziffern springen sonst beim Aktualisieren */
  -webkit-text-size-adjust: 100%;
}

/* Das Grundraster. Die ZEILEN setzt jede App selbst, sie hat unterschiedlich
   viele Kinder. Alles andere ist gleich. */
.app {
  display: grid;
  /* minmax(0, 1fr) statt einfach 1fr: sonst wird die Spalte so breit wie ihr
     breitester Inhalt und sprengt schmale Bildschirme. */
  grid-template-columns: minmax(0, 1fr);
  gap: 5px;
  height: 100dvh;
  height: -webkit-fill-available;   /* iOS-Ruecksicht, dvh kennt nicht jede Version */
  /* Unten knapper als oben: die Bedienleiste sitzt dicht am Rand, damit die
     Liste eine Karte mehr fasst. */
  padding: calc(env(safe-area-inset-top) + 5px) 8px calc(env(safe-area-inset-bottom) + 2px);
}

/* ---------- Band fuer den Pruefmodus ---------- */

.probe-band {
  position: fixed;
  top: 8px;
  left: 0;
  right: 0;
  z-index: 60;
  padding: 5px 8px;
  background: #c02626;
  color: #fff;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: .4px;
  text-align: center;
}

/* ---------- Ladebalken ---------- */

/* Ganz oben und dick genug, um im Augenwinkel aufzufallen, ohne dass man den
   Blick von der Strasse nehmen muss. */
.fortschritt {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 8px;
  background: var(--linie);
  z-index: 50;
  opacity: 1;
  transition: opacity .25s ease;
}
.fortschritt.weg { opacity: 0; }

.balken {
  height: 100%;
  width: 0;
  background: var(--blau);
  transition: width .3s ease;
}

/* ---------- Kopfzeile ----------

   ZWEI Zeilen: oben welche App das hier ist, darunter der Weg zu den anderen.
   Vorher stand alles nebeneinander, und bei zwei Pillen blieben dem Namen 102
   von 173 noetigen Pixeln: aus "Parken by ManniMindset" wurde "Parken by ...".
   Die eigene Marke abzuschneiden ist das Gegenteil von dem, was sie soll. */

.kopf {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 5px;
  padding: 0 2px;
  min-width: 0;
}

/* Zeile 1: das ist diese App. */
.kopf-ich {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  min-width: 0;
}
.kopf-zeichen { height: 38px; width: auto; flex: none; display: block; }
/* Name und Zweck untereinander. Beides zusammen bleibt niedriger als das
   Zeichen daneben, die Zeile wird dadurch keinen Pixel hoeher. */
.kopf-text { display: flex; flex-direction: column; justify-content: center; min-width: 0; }
.kopf-name {
  font-size: 15px;
  font-weight: 800;
  letter-spacing: .2px;
  color: var(--leise);
  white-space: nowrap;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.kopf-name b { color: var(--blau); margin-left: 5px; font-size: 13px; }
/* Wofuer ist diese App gut? Ein Satz, der beim ersten Mal erklaert und danach
   nicht stoert, weil er leise gesetzt ist. */
.kopf-was {
  margin-top: 1px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--gedimmt);
  white-space: nowrap;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Zeile 2: EIN Weg zu allen anderen Apps, nicht je eine Pille.
   Frueher stand hier je ein Sprung in jede andere App. Mit drei Apps ging das
   noch, mit fuenf nicht mehr, und bei jeder neuen App haette jede andere
   angefasst werden muessen. Jetzt fuehrt ein Knopf auf die Uebersicht /apps.
   Ein Tipp mehr zum Ziel, dafuer sieht man beim Wechseln die Werbeflaeche, die
   diese Apps kostenlos haelt (Manni, 30.07.2026). */
.app-wechsel {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 34px;
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--karte);
  border: 2px solid var(--blau);
  color: var(--blau);
  font-size: 14px;
  font-weight: 800;
  text-decoration: none;
  white-space: nowrap;
}
.aw-zeichen { flex: none; display: flex; align-items: center; gap: 4px; }
.aw-zeichen img { height: 22px; width: auto; display: block; }
.aw-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.aw-pfeil { flex: none; font-size: 20px; font-weight: 700; line-height: 1; }

/* Sehr schmale Geraete: das Wort "wechseln" reicht, die Zeichen tragen den Rest. */
@media (max-width: 340px) {
  .app-wechsel { font-size: 13px; gap: 6px; }
  .aw-zeichen img { height: 20px; }
}

/* ---------- Formensprache ----------
   Eine App im Auto braucht eine klare Trennung: Was bedient wird, sieht anders
   aus als was informiert.

   Umschalter   = zusammenhaengende Leiste mit gleitendem Markierer
   Kippschalter = Schalter rechts, Beschriftung links
   Karte        = flach, ohne Rahmen, mit rundem Anfasser
   Aktion       = vollflaechig gefuellt
*/

/* Druckrueckmeldung fuer ALLES, was auf einen Tipp reagiert. Wichtigste einzelne
   Massnahme: die Hand bekommt sofort Antwort, lange bevor Daten da sind.

   touch-action: manipulation schaltet den Doppeltipp-Zoom ab. Ohne das zoomt das
   Handy, sobald man einen Plus-Knopf schnell zweimal antippt, und genau das
   passiert beim Einstellen einer Geschwindigkeit staendig. Das viewport-Attribut
   maximum-scale=1 hilft nicht: iOS Safari ignoriert es seit iOS 10 bewusst.
   Das Zoomen mit zwei Fingern bleibt erlaubt, nur der Doppeltipp faellt weg. */
.segment, .kippzeile, .karte, .knopf, .werbe-banner, .app-wechsel,
.d-los, .d-stopp, .ziel-eintrag, .gruss-weiter, .knopf-voll,
.zbtn, .chip, .fbtn {
  transition: transform .08s ease, filter .08s ease, background-color .15s ease;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
}
.segment:active, .kippzeile:active, .karte:active, .knopf:active,
.werbe-banner:active, .app-wechsel:active, .d-los:active, .d-stopp:active,
.ziel-eintrag:active, .zbtn:active, .chip:active, .fbtn:active {
  transform: scale(.97);
  filter: brightness(1.18);
}

/* ---------- Umschalter ---------- */

.umschalter {
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  padding: 5px;
  border-radius: 16px;
  background: var(--karte);
  border: 2px solid var(--linie);
  /* Innenschatten macht die Leiste als Rinne lesbar, in der etwas gleitet */
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, .18);
}

/* Der gleitende Markierer. Breite aus der Segmentzahl, verschoben per transform,
   deshalb ohne Nachmessen im Programm. */
.markierer {
  position: absolute;
  top: 5px;
  bottom: 5px;
  left: 5px;
  width: calc((100% - 10px) / 3);
  border-radius: 12px;
  background: var(--blau);
  box-shadow: 0 2px 8px rgba(0, 145, 200, .45);
  transition: transform .25s cubic-bezier(.4, 0, .2, 1);
  pointer-events: none;
}
.markierer.zwei { width: calc((100% - 10px) / 2); }
.markierer.drei { width: calc((100% - 10px) / 3); }
.markierer.vier { width: calc((100% - 10px) / 4); }
.markierer.fuenf { width: calc((100% - 10px) / 5); }

.segment {
  position: relative;
  z-index: 1;
  min-height: 50px;
  border: 0;
  background: transparent;
  color: var(--leise);
  font-size: 22px;
  font-weight: 700;
  font-family: inherit;
  letter-spacing: .3px;
  cursor: pointer;
  white-space: nowrap;
}
.segment[aria-pressed="true"] { color: #fff; font-weight: 800; }

/* Die haeufigste Wahl bekommt die groesste Flaeche, die seltenere weniger. */
.umschalter.gross .segment { min-height: 52px; font-size: 22px; }
.umschalter:not(.gross) .segment { min-height: 42px; font-size: 18px; }

/* Zusatzzeile unter einem Umschalter, passt in kein Segment */
.sort-zusatz {
  min-height: 17px;
  padding: 0 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--leise);
  text-align: center;
}

/* ---------- Kippschalter ---------- */

.kippzeile {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 48px;
  padding: 0 14px;
  border: 2px solid var(--linie);
  border-radius: 14px;
  background: var(--karte);
  color: var(--text);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.kz-text { display: flex; flex-direction: column; min-width: 0; }
.kz-titel { font-size: 17px; font-weight: 700; white-space: nowrap; }
.kz-unter { font-size: 13px; color: var(--leise); white-space: nowrap; }

/* Die Kippe selbst: das einzige Bedienelement, das jeder sofort erkennt. */
.kippe {
  position: relative;
  flex: none;
  width: 60px;
  height: 34px;
  border-radius: 999px;
  background: var(--gedimmt);
  transition: background-color .2s ease;
}
.kippe-knopf {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 2px 5px rgba(0, 0, 0, .35);
  transition: transform .2s cubic-bezier(.4, 0, .2, 1);
}
.kippzeile[aria-checked="true"] .kippe { background: var(--blau); }
.kippzeile[aria-checked="true"] .kippe-knopf { transform: translateX(26px); }

/* ---------- Liste und Karten ---------- */

/* Nur diese Liste scrollt, die Seite selbst nie. So bleiben Bedienleiste und
   Kopfzeile immer an ihrem Platz. */
.listenraum { position: relative; min-height: 0; }
.liste {
  display: grid;
  grid-auto-rows: max-content;
  gap: 5px;
  overflow-y: auto;
  min-height: 0;
  padding-right: 2px;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

/* Eine Karte je Treffer: Information, kein Knopf. Deshalb kein kraeftiger
   Rahmen, sondern eine flache Flaeche. */
.karte {
  border-radius: 14px;
  background: var(--karte);
  border: 1px solid transparent;
  border-left: 4px solid transparent;
  overflow: hidden;
  cursor: pointer;
}
.karte.auf { border-color: var(--blau); border-left-color: var(--blau); }

.hero-meldung {
  align-self: center;
  font-size: 26px;
  line-height: 1.35;
  text-align: center;
  color: var(--leise);
  padding: 10px;
}
.hero-meldung strong { color: var(--text); }

/* ---------- Knoepfe ---------- */

/* Voll ausgefuellt in Blau, wie das aktive Segment oben.
   Doppelte Klasse, weil die Grundregel .knopf darunter steht und den Hintergrund
   sonst wieder ueberschreibt. */
.knopf.knopf-breit {
  width: 100%;
  background: var(--blau);
  border-color: var(--blau);
  color: #fff;
  font-size: 24px;
  font-weight: 800;
}
.knopf.knopf-breit:disabled { opacity: .55; }

.knopf {
  min-height: 54px;
  border: 2px solid var(--linie);
  border-radius: 14px;
  background: var(--karte);
  color: var(--text);
  font-size: 21px;
  font-weight: 700;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 6px;
  overflow: hidden;
  /* Ohne das bricht ein laengerer Text wie "Aktualisieren" mitten im Knopf um. */
  white-space: nowrap;
}
.knopf span { white-space: nowrap; }
.knopf:disabled { opacity: .5; }
.knopf.hinweis { background: var(--blau); border-color: var(--blau); color: #fff; }
.knopf-icon { font-size: 28px; }
.knopf-voll { width: 100%; background: var(--blau); border-color: var(--blau); color: #fff; }

/* Zweizeilige Beschriftung: oben die Handlung, darunter der Datenstand. Er
   beantwortet genau die Frage, die man sich vor dem Tippen stellt: muss ich
   ueberhaupt neu laden? */
.knopf-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1.1;
  min-width: 0;
}
/* Eigenes Feld auf dem Knopf, nicht bloss hellere Schrift. Weiss direkt auf
   PIG-Blau kommt bei 13 px nur auf 3,57:1, gemessen, und das ist unter dem
   Minimum von 4,5:1. Die dunkle Auflage hebt es auf ueber 6:1. */
.btn-stand {
  margin-top: 1px;
  padding: 1px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, .3);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
}
/* Ehrlich bleiben: ein alter Stand wird als alter Stand gekennzeichnet. */
.btn-stand.warn { background: var(--warn); color: var(--auf-warn); }
/* Leer heisst: noch nie geladen. Dann soll die Zeile keinen Platz kosten. */
.btn-stand:empty { display: none; }

/* Der eine Knopf zum Losfahren, in der aufgeklappten Karte. Steht hier in der
   Basis, weil er in Tanken und Parken dasselbe tut und deshalb auch gleich
   aussehen muss (Manni, 30.07.2026): vorher war er hier 54 px hoch mit dem
   Maps-Zeichen, dort 46 px mit einem Pfeil aus der Schriftart.
   Das rot-weisse Zeichen sagt ohne Lesen, wohin der Tipp fuehrt. */
.d-los {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  margin: 8px 12px 12px;
  min-height: 54px;
  border-radius: 12px;
  background: var(--blau);
  color: #fff;
  font-size: 19px;
  font-weight: 800;
  text-decoration: none;
}
.d-los-pin { flex: none; }

/* ---------- Werbebanner ---------- */

.werbe-banner {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 38px;
  border-radius: 14px;
  text-decoration: none;
  color: #fff;
  font-size: 19px;
  font-weight: 800;
  letter-spacing: .6px;
  background: linear-gradient(100deg, #0091c8 0%, #00b4d8 45%, #ff7d0f 100%);
  box-shadow: 0 3px 14px rgba(0, 145, 200, .32);
}
/* Laufender Lichtstreifen, gleiche Handschrift wie die Werbeflaeche auf der
   Empfehlungsseite. Bewusst nur hier: in einer Auto-App darf sich sonst nichts
   von selbst bewegen, das lenkt ab. Dieses eine Feld ist Werbung und darf. */
.werbe-banner::before {
  content: '';
  position: absolute;
  top: -60%;
  left: -60%;
  width: 45%;
  height: 220%;
  background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.45) 50%, rgba(255,255,255,0) 100%);
  transform: rotate(18deg);
  animation: wbGlanz 3.6s ease-in-out infinite;
  pointer-events: none;
}
@keyframes wbGlanz {
  0%   { left: -60%; }
  55%  { left: 130%; }
  100% { left: 130%; }
}
.werbe-banner > * { position: relative; z-index: 1; }
.wb-funke { font-size: 18px; }
.wb-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.wb-pfeil { font-size: 25px; font-weight: 700; }

/* ---------- Fusszeile ---------- */

.fuss {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
  padding: 0 4px;
  margin-top: 2px;
  font-size: 13px;
  color: var(--leise);
}
.fuss .quelle { text-align: center; }
.fuss .quelle a { color: var(--leise); text-decoration: underline; }

/* ---------- Begruessung ---------- */

/* z-index MUSS sein: ohne ihn ist die Huelle zwar das letzte Element im
   Dokument, verliert aber gegen jedes positionierte Element mit z-index 1.
   Genau das war der Fall, Bedienknoepfe schienen durch die Verdunkelung
   (.segment und die Bannerinhalte tragen z-index 1). 100 liegt sicher ueber
   Ladebalken (50) und Pruefband (60). */
.huelle {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(0, 0, 0, .72);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.huelle.hidden { display: none; }
/* Allgemein nutzbar: alle Apps blenden etwas damit aus (Pruefband, Hinweiszeile,
   Begruessung). Stand vorher in jeder App einzeln. */
.hidden { display: none; }

.kasten {
  background: var(--karte);
  border-radius: 18px;
  padding: 24px;
  max-width: 420px;
}
/* Das App-Logo im Gruss. Weisse Platte, weil die Wortmarke eine weisse Flaeche
   mitbringt und die Schrift dunkelblau ist: ohne Platte waere sie im
   Dunkelmodus nicht lesbar. */
.gruss-logo {
  background: #fff;
  border-radius: 12px;
  padding: 10px 12px;
  margin: 0 0 14px;
}
.gruss-logo img { width: 100%; height: auto; display: block; }
.kasten h2 { margin: 0 0 10px; font-size: 30px; }
.kasten p  { margin: 0 0 14px; font-size: 19px; line-height: 1.45; }
.kasten .spruch { font-size: 22px; font-weight: 700; line-height: 1.35; }
.kasten .klein { font-size: 16px; color: var(--leise); }

/* Zwei Knoepfe: weiterblaettern oder loslegen. */
.gruss-knoepfe { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.gruss-weiter { background: transparent; border-color: var(--linie); color: var(--leise); font-size: 17px; }

/* ---------- Das Blatt ----------

   Ein Fenster, das von unten hereinfaehrt und die Einzelheiten zu einem
   Listeneintrag traegt. Es loest das Aufklappen IN der Liste ab, und zwar aus
   einem gemessenen Grund (Manni, 30.07.2026):

   Auf einem iPhone SE ist das Listenfenster der Park-App 79 px hoch. Eine
   aufgeklappte Karte braucht 254 bis 326 px. Der Knopf "Route über Google Maps"
   sass damit rund 162 px unterhalb der Kartenoberkante, also weit ausserhalb des
   sichtbaren Bereichs. Man musste scrollen, um ihn zu erreichen, und eine
   Scroll-Wache schloss die Karte nach 28 px wieder. Der Knopf war unerreichbar.

   Der Kniff ist nicht das Fenster an sich, sondern seine DREITEILUNG: Kopf und
   Fuss stehen fest, nur der Rumpf scrollt. Damit ist der Handlungsknopf im Fuss
   unabhaengig von Bildschirmhoehe und Textmenge immer sichtbar. Das ist die
   eigentliche Reparatur; die Scroll-Wache faellt ersatzlos weg. */

/* KEINE BEWEGUNG DARF DIE ERREICHBARKEIT BESTIMMEN. Das ist hier keine
   Feinheit, sondern die tragende Regel, und sie hat drei Anlaeufe gekostet:

   Anlauf 1, `animation: blattRein .24s both`: mit `both` haelt die Animation die
   Startlage fest, bevor sie laeuft. Gemessen hing das Blatt 337 px unterhalb des
   Bildschirms.
   Anlauf 2, eine Klasse, die im naechsten Bild per requestAnimationFrame wieder
   abgenommen wird: laeuft kein Bildaufbau, kommt kein naechstes Bild, und die
   Klasse bleibt kleben. Gemessen: dieselben 337 px.
   Anlauf 3, dieselbe Animation ohne fill-mode: auch das half nicht. Eine
   laufende, aber eingefrorene Animation haelt ihren ersten Bildpunkt fest, ganz
   gleich was fill-mode sagt. Wieder 337 px.

   Die Lehre ist nicht "ein anderer Trick", sondern: eine Bewegung, deren
   Startlage 337 px vom Ziel entfernt ist, darf es gar nicht geben. Jetzt sind es
   16 px. Friert die Animation ein, sitzt das Blatt 16 px tiefer als gedacht und
   ist vollstaendig bedienbar. Auf einem gesunden Geraet sieht man denselben
   Ruck nach oben wie vorher, nur kuerzer.
   Das Verblassen des Hintergrunds darf ruhig einfrieren: ein unsichtbarer
   Hintergrund verdeckt nichts. */
.blatt-huelle {
  position: fixed;
  inset: 0;
  z-index: 110;                 /* ueber der Begruessung (100), sonst nichts */
  display: flex;
  align-items: flex-end;
  background: rgba(8, 10, 13, .55);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  animation: blattGrund .18s ease-out;   /* ohne fill-mode, mit Absicht */
}
.blatt-huelle.zu { opacity: 0; transition: opacity .18s ease; }

.blatt {
  display: flex;
  flex-direction: column;
  width: 100%;
  /* 78 % statt 100 %: darueber bleibt ein Streifen der Liste sichtbar, und man
     sieht auf einen Blick, dass man in einem Fenster steht und nicht auf einer
     neuen Seite. Ein Fenster, das den Bildschirm fuellt, fuehlt sich an wie ein
     Seitenwechsel, und dann sucht man die Zurueck-Taste. */
  max-height: 78dvh;
  padding-bottom: env(safe-area-inset-bottom);
  border-radius: 20px 20px 0 0;
  background: var(--bg);
  box-shadow: 0 -8px 30px rgba(0, 0, 0, .38);
  transform: none;                       /* die Ruhelage ist die richtige Lage */
  animation: blattRein .24s cubic-bezier(.22, 1, .36, 1);   /* ohne fill-mode */
}
.blatt-huelle.zu .blatt {
  transform: translateY(100%);
  transition: transform .2s ease-in;
}

/* Der Griff. Kein Bedienelement, sondern ein Versprechen: hier laesst sich
   ziehen. Ohne ihn probiert es niemand. */
.blatt-griff {
  flex: none;
  width: 42px;
  height: 5px;
  margin: 8px auto 2px;
  border-radius: 999px;
  background: var(--linie);
}

.blatt-kopf {
  flex: none;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 6px 14px 10px;
  border-bottom: 1px solid var(--linie);
}
.blatt-titel { flex: 1; min-width: 0; }
.blatt-titel h3 {
  margin: 0;
  font-size: 19px;
  font-weight: 800;
  line-height: 1.2;
  color: var(--text);
}
.blatt-unter {
  display: block;
  margin-top: 3px;
  font-size: 14px;
  font-weight: 600;
  color: var(--leise);
}
.blatt-zu {
  flex: none;
  width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  border: 0;
  border-radius: 50%;
  background: var(--karte);
  color: var(--leise);
  font-family: inherit;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}

/* Der einzige Teil, der scrollt. min-height:0 ist Pflicht, sonst waechst ein
   Flex-Kind ueber seinen Anteil hinaus und der Fuss wird nach unten geschoben. */
.blatt-rumpf {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;   /* haelt das Scrollen im Blatt, nicht dahinter */
  padding: 12px 14px;
  display: grid;
  gap: 8px;
}

/* Der Fuss traegt die Handlung und steht fest. Genau dafuer ist das Blatt
   gebaut. Der obere Schatten setzt ihn ab, wenn darueber noch Text weiterlaeuft. */
.blatt-fuss {
  flex: none;
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--linie);
  background: var(--bg);
  box-shadow: 0 -6px 14px rgba(0, 0, 0, .07);
}
/* Im Blatt braucht der Losfahren-Knopf keinen eigenen Rand mehr, der Fuss hat
   schon einen. */
.blatt-fuss .d-los { margin: 0; width: 100%; }

/* SECHZEHN Pixel, nicht hundert Prozent. Die Begruendung steht oben bei
   .blatt-huelle: was einfrieren kann, darf nicht weit weg starten. */
@keyframes blattRein  { from { transform: translateY(16px); } to { transform: none; } }
@keyframes blattGrund { from { opacity: 0; } to { opacity: 1; } }

/* Waehrend das Blatt am Finger haengt, darf kein Uebergang dazwischenfunken,
   sonst laeuft es der Bewegung hinterher. */
.blatt.zieht { transition: none; animation: none; }

/* ---------- Werbestreifen ----------

   Die drei verkauften Plaetze IN der App (Manni, 30.07.2026). Gestapelt
   braeuchten sie 335 px, das geht auf einem 568-px-Handy nicht. Stattdessen eine
   Flaeche, die zwischen den gebuchten Anzeigen wechselt.

   Die Staffelung steckt in ZEIT und GROESSE: Platz 1 steht 10 Sekunden und ist
   44 px hoch, Platz 2 steht 6 Sekunden bei 38 px, Platz 3 steht 4 Sekunden bei
   32 px. Das sind 50, 30 und 20 Prozent der Sendezeit, und damit fast genau das
   Preisverhaeltnis 59 zu 39 zu 25 (48, 32, 20 Prozent). Die Sendezeit entspricht
   dem Preis, und das laesst sich nachrechnen.

   ENTSCHEIDEND gegen Gezappel: die AEUSSERE Flaeche behaelt immer die groesste
   Hoehe. Nur die Anzeige darin ist unterschiedlich hoch und sitzt mittig. Wuerde
   die Zeile selbst mitwachsen, ruckelte die Liste darueber alle paar Sekunden um
   12 px, und das waere schlimmer als jede Werbung. */

.wp-streifen {
  position: relative;
  display: flex;
  align-items: center;
  /* VERDOPPELT (Manni, 31.07.2026: "das ist noch zu klein da unten"). 88 statt
     44 px. Das ist verkaufte Flaeche, und eine Werbeflaeche, die man uebersieht,
     ist keinen Preis wert. Bezahlt wird sie aus der Fusszeile, die dafuer ganz
     entfaellt. */
  min-height: 88px;
  padding-left: 22px;            /* Platz fuer die Kennzeichnung am Rand */
}

/* Pflichtkennzeichnung nach Paragraf 6 TMG. Steht dauerhaft da, nicht nur beim
   Wechsel: eine Kennzeichnung, die mitwandert, ist keine. Senkrecht am Rand,
   damit sie keine Hoehe kostet. */
.wp-marke {
  position: absolute;
  left: 0; top: 50%;
  transform: translateY(-50%) rotate(180deg);
  writing-mode: vertical-rl;
  font-size: 8.5px;
  font-weight: 800;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--gedimmt);
  pointer-events: none;
}

.wp-anzeige {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  width: 100%;
  border-radius: 12px;
  color: #fff;
  text-decoration: none;
  font-weight: 800;
  letter-spacing: .4px;
  animation: wpRein .3s ease-out both;
}
/* ALLE DREI GLEICH GROSS (Manni, 31.07.2026). Der erste Entwurf staffelte auch
   die Hoehe, 44 / 38 / 32 px. Mit dem, was wir jetzt wissen, ist das falsch:
   auf 320 px Breite sieht man den Unterschied kaum, und wer Platz 3 bucht, soll
   keine sichtbar schlechtere Flaeche bekommen. Der Unterschied liegt allein in
   der ZEIT, und die ist ehrlich messbar.
   Die Farbe unterscheidet weiter, aber nur als Wiedererkennung zu den drei
   Plaetzen auf /neugierig und im Buchungsformular. */
.wp-anzeige { min-height: 88px; font-size: 20px; }
.wp-anzeige.p1 {
  background: linear-gradient(120deg, #ff9836 0%, #FF7D0F 45%, #e05f00 100%);
  box-shadow: 0 4px 16px rgba(255, 125, 15, .34); }
.wp-anzeige.p2 {
  background: linear-gradient(120deg, #35b4e0 0%, #0091C8 60%, #007099 100%);
  box-shadow: 0 4px 14px rgba(0, 145, 200, .28); }
.wp-anzeige.p3 {
  background: linear-gradient(120deg, #59626b 0%, #414952 60%, #2f353c 100%);
  box-shadow: 0 3px 10px rgba(0, 0, 0, .22); }

/* Der laufende Lichtstreifen, wie auf der Empfehlungsseite. Er ist hier NICHT
   Zierat, sondern Teil des Angebots (Manni, 31.07.2026: "der Kunde sieht auch
   direkt, was er bekäme, schön schimmern lassen"): ein freier Platz sieht damit
   aus wie eine gebuchte Anzeige, nur mit dem Preis darauf. Wer ihn bucht, weiss
   vorher genau, was er bekommt.
   Gestaffelt wie die Sendezeit: Platz 1 laeuft schnell und hell, Platz 3
   langsam und zart. */
.wp-anzeige::before {
  content: '';
  position: absolute; top: -60%; left: -60%;
  width: 45%; height: 220%;
  background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.42) 50%, rgba(255,255,255,0) 100%);
  transform: rotate(18deg);
  animation: wpGlanz 3.4s ease-in-out infinite;
  pointer-events: none;
}
.wp-anzeige.p2::before { animation-duration: 5.2s; opacity: .72; }
.wp-anzeige.p3::before { animation-duration: 7s;   opacity: .55; }
@keyframes wpGlanz {
  0%   { left: -60%; }
  55%  { left: 130%; }
  100% { left: 130%; }
}
.wp-anzeige > * { position: relative; z-index: 1; }

/* Ein FREIER Platz sieht aus wie eine gebuchte Anzeige, denn genau das ist er
   im Angebot. Kein gestrichelter Rahmen, keine blasse Farbe: das waere die
   Vorfuehrung eines schlechteren Produkts als das verkaufte.
   Unterschieden wird nur durch das Wort auf der Flaeche und durch die
   gepunktete Umrandung des Platzhalters, wo spaeter das Kundenlogo steht. */
.wp-frei-platz {
  display: inline-block;
  padding: 0 7px;
  border: 2px dashed rgba(255, 255, 255, .75);
  border-radius: 7px;
  font-size: .8em;
  animation: wpPuls 1.9s ease-in-out infinite;
}
@keyframes wpPuls {
  0%, 100% { border-color: rgba(255, 255, 255, .35); }
  50%      { border-color: rgba(255, 255, 255, .95); }
}

.wp-logo { flex: none; height: 24px; width: auto; border-radius: 5px; }
.wp-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wp-pfeil { flex: none; font-size: 22px; font-weight: 700; line-height: 1; }

/* ---------- Der ablaufende Zaehler ----------
   Manni, 31.07.2026: "damit es keine Streitereien gibt, dass man sehen kann,
   wie die Zeit herunterlaeuft."

   Das ist kein Zierat, sondern der Beleg fuer die gebuchte Leistung. Wer Platz 1
   bucht, kann in jeder App nachsehen, dass dort fuenf Minuten stehen, statt es
   glauben zu muessen. Beides kostet KEINE Hoehe: die Zahl sitzt in der Ecke, der
   Balken auf der Unterkante. */
.wp-rest {
  position: absolute;
  top: 5px; right: 9px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .3px;
  color: rgba(255, 255, 255, .85);
  font-variant-numeric: tabular-nums;   /* damit die Zahl nicht zappelt */
  pointer-events: none;
}
/* ---------- Der Balken als MINUTENTAKTE ----------

   Manni, 31.07.2026: "man erkennt nicht so gut, dass die Laufleiste langsam
   ablaeuft."

   Er hat recht, und es ist keine Geschmacksfrage, sondern Wahrnehmung. Ein
   durchgehender Balken bewegt sich ueber fuenf Minuten um 0,33 Prozent je
   Sekunde. Das liegt unter der Schwelle, ab der ein Auge Bewegung ueberhaupt
   bemerkt; man sieht erst nach einer halben Minute, DASS sich etwas geaendert
   hat, aber nie, dass es sich BEWEGT.

   Drei Aenderungen, jede mit einem eigenen Zweck:

   1. UNTERTEILUNG IN SECHS ABSCHNITTE. Statt eines langen Balkens sechs kurze,
      bei jedem Platz gleich viele. Drei, zwei und eine Minute gehen alle glatt
      durch sechs, ein Abschnitt dauert also 30, 20 oder 10 Sekunden.
      Es leert sich immer nur EIN Kaestchen, und das bewegt sich sechsmal
      schneller als der ganze Balken: 3,3 Prozent je Sekunde bei Platz 1 statt
      0,33. Dazu erlischt alle 30, 20 oder 10 Sekunden ein Kaestchen ganz, und
      das ist ein Sprung. Ein Sprung wird immer bemerkt, eine stetige Bewegung
      unterhalb der Schwelle nie.
   2. LEUCHTENDE KANTE. Das Auge liest die POSITION einer Kante viel genauer als
      die LAENGE einer Flaeche. Die Vorderkante des laufenden Kaestchens bekommt
      deshalb einen hellen Schein.
   3. MEHR KONTRAST UND HOEHE. 6 statt 3 px, dunklere Rinne, und abgesetzt vom
      Rand statt auf der Kante, wo die Rundung ihn frueher halb verschluckte. */
.wp-balken {
  position: absolute;
  left: 12px; right: 12px; bottom: 5px;
  display: flex;
  gap: 3px;
  height: 6px;
  pointer-events: none;
}
.wp-takt {
  flex: 1;
  min-width: 0;
  border-radius: 3px;
  /* Deutlich dunkler als vorher: die leere Rinne muss als leer lesbar sein,
     sonst sieht der ganze Balken immer gleich voll aus. */
  background: rgba(0, 0, 0, .28);
  overflow: hidden;
}
.wp-takt i {
  display: block;
  height: 100%;
  width: 100%;
  border-radius: 3px;
  background: #fff;
  /* Eine Sekunde linear: der Wert springt im Sekundentakt, die Bewegung
     glaettet ihn. Faellt sie aus, springt er eben, und die Zahl stimmt weiter. */
  transition: width 1s linear;
}
/* Nur das laufende Kaestchen leuchtet an der Kante. Ein Schein auf allen
   waere Zierat, hier ist er die Auskunft "genau hier passiert gerade etwas". */
.wp-takt.aktiv i {
  box-shadow: 1px 0 7px 1px rgba(255, 255, 255, .95);
}

@keyframes wpRein { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* ---------- Bewegung ---------- */

/* Kurzes Aufblenden nach dem Umschalten. */
@keyframes reinrutschen {
  from { opacity: .25; transform: translateY(9px); }
  to   { opacity: 1;   transform: none; }
}
.schwung .karte { animation: reinrutschen .3s ease-out both; }
.schwung .karte:nth-child(2) { animation-delay: .04s; }
.schwung .karte:nth-child(3) { animation-delay: .08s; }

@keyframes aufklappen {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* Wer Bewegung abgeschaltet hat, bekommt keine. Die Bedienung bleibt
   vollstaendig, nur ohne Gleiten, Blitzen und Pulsieren. */
@media (prefers-reduced-motion: reduce) {
  .schwung .karte,
  .karte.gefallen, .karte.gestiegen,
  .skelett, .zieh-anzeige.laeuft .zieh-ring { animation: none; }
  .werbe-banner::before { animation: none; opacity: 0; }
  .markierer, .kippe-knopf, .segment, .kippzeile, .karte, .knopf { transition: none; }
  /* Das Blatt faehrt nicht mehr herein, es ist einfach da. Und die Werbung
     wechselt nicht mehr: Bewegung im Blickfeld beim Fahren ist ohnehin heikel,
     wer sie abgeschaltet hat, meint es ernst.
     Die Ruhelage bleibt richtig, weil sie nicht an der Bewegung haengt. */
  .blatt, .blatt-huelle { transition: none; }
  .wp-anzeige, .wp-anzeige::before, .wp-frei-platz { animation: none; }
  .wp-frei-platz { border-color: rgba(255, 255, 255, .8); }
  /* Die Kaestchen springen dann im Sekundentakt statt zu gleiten, und die
     leuchtende Kante entfaellt. Zahl und Fuellstand bleiben: das ist Auskunft
     und keine Bewegung. */
  .wp-takt i { transition: none; }
  .wp-takt.aktiv i { box-shadow: none; }
}

/* ---------- Kleine Bildschirme ----------

   DREI Stufen statt einer (Manni, 30.07.2026: "das maximalste, was herausholbar
   ist"). Anlass war eine Messung an der Park-App auf einem iPhone SE, 320 x 568:

       feste Umgebung  488 px  (86 Prozent)
       Liste            79 px  (14 Prozent, also 1,2 Karten)

   Die alte einzige Stufe bei 700 px sparte ganze 12 px. Das ist nichts gegen
   488. Also: frueher greifen, tiefer greifen, und ueber clamp() dazwischen
   gleiten statt zu springen.

   Die eine Grenze, die NICHT verhandelbar ist: eine Trefferflaeche bleibt bei
   mindestens 36 px. Darunter tippt man am Steuer daneben, und dann ist die
   gewonnene Zeile teurer als die verlorene. */

/* Stufe "knapp": was frueher erst bei 700 px galt, greift jetzt ab 780 px.
   Damit sind auch die haeufigen 812er- und 844er-Geraete im Hochformat
   abgedeckt, sobald die Systemleisten ihren Teil genommen haben. */
@media (max-height: 780px) {
  .knopf { min-height: 52px; font-size: 20px; }
  .knopf.knopf-breit { font-size: 22px; }
  .werbe-banner { min-height: 36px; font-size: 18px; }
  .kopf-ich { min-height: 32px; }
  .kopf-zeichen { height: 32px; }
  .app-wechsel { min-height: 32px; }
}

/* Stufe "eng": ab hier zaehlt jede Zeile. Gekuerzt wird an Luft, Schrift und
   Doppelungen, nie am Inhalt. */
@media (max-height: 640px) {
  .app { gap: 4px; padding-top: calc(env(safe-area-inset-top) + 4px); }

  /* Die Kopfzeile wird EINZEILIG: Zeichen und Name links, der Weg zu den
     anderen Apps rechts als reine Zeichenreihe. Der Satz "Meine anderen
     kostenlosen Apps" faellt weg, die Zeichen sagen dasselbe kuerzer.
     Ersparnis 35 px, das ist eine halbe Karte. */
  .kopf { flex-direction: row; align-items: center; gap: 8px; }
  .kopf-ich { flex: 1; min-height: 34px; }
  .kopf-zeichen { height: 30px; }
  .kopf-name { font-size: 14px; }
  .kopf-name b { font-size: 12px; }
  .kopf-was { display: none; }        /* der Zweck steht auf /apps, hier stoert er */
  .app-wechsel { flex: none; min-height: 34px; padding: 2px 8px; gap: 5px; }
  .aw-text { display: none; }
  .aw-zeichen img { height: 21px; }

  /* Umschalter: 36 px ist die Untergrenze, nicht weniger. */
  .umschalter { padding: 4px; }
  .umschalter.gross .segment { min-height: 36px; font-size: 15px; }
  .umschalter:not(.gross) .segment { min-height: 36px; font-size: 14px; }
  .markierer { top: 4px; bottom: 4px; left: 4px; }

  .kippzeile { min-height: 40px; padding: 0 12px; }
  .kz-titel { font-size: 15px; }
  .kz-unter { font-size: 12px; }

  .knopf { min-height: 44px; font-size: 18px; }
  .knopf.knopf-breit { font-size: 19px; }
  .knopf-icon { font-size: 22px; }

  .sort-zusatz { min-height: 15px; font-size: 12px; }
  .fuss { font-size: 11px; }
  .fuss .quelle { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; }

  /* Der Werbestreifen behaelt seine Hoehe: er ist verkaufte Flaeche, und was
     verkauft ist, wird nicht heimlich kleiner. Nur die Schrift geht mit. */
  .wp-streifen { min-height: 80px; }
  .wp-anzeige { min-height: 80px; font-size: 18px; }

  /* Das Blatt darf hier hoeher werden: bei 568 px sind 78 Prozent nur 443 px,
     und davon gehen Kopf und Fuss ab. */
  .blatt { max-height: 86dvh; }
  .blatt-kopf { padding: 4px 12px 8px; }
  .blatt-titel h3 { font-size: 17px; }
  .blatt-rumpf { padding: 10px 12px; }

  /* Und die Liste selbst enger (Manni, 31.07.2026: "die einzelnen Zeilen müssen
     viel enger sein, damit mehr Platz geschaffen wird"). Der Zwischenraum ist
     der billigste Gewinn: bei sechs Karten sind fuenf Fugen, jede gesparte
     Zeile ist bares Blickfeld. */
  .liste { gap: 3px; }
  .karte { border-radius: 11px; border-left-width: 3px; }
}

/* Sehr flache Geraete, etwa ein SE im Querformat oder ein geteilter Bildschirm.
   Hier faellt zusaetzlich die Fusszeile weg: die Quellennennung steht dann im
   Blatt unter "Quelle", die Pflicht ist damit erfuellt, ohne dauerhaft eine
   Zeile zu kosten. */
@media (max-height: 470px) {
  .fuss { display: none; }
  .kopf-name { font-size: 13px; }
  .umschalter.gross .segment, .umschalter:not(.gross) .segment { min-height: 36px; }
}
