
  /* ══════════════ iPhone-Labor: Safe-Area (08.09.2026) ══════════════════
     EINE Quelle für die Sensorflaeche/Home-Balken-Aussparung: --pm-safe-top/
     --pm-safe-bottom. Auf echten Geraeten kommen sie aus env(safe-area-inset-*)
     (nur im Secure Context, Standalone/PWA); im ?preview=iphone-Labor
     (headless Chromium liefert env() nie -- Playwright hat keine Notch)
     werden sie stattdessen fest auf iPhone-15-Pro-Masse gesetzt: Statusleiste
     59px (Dynamic Island), Home-Indikator-Zone 34px. ALLE Stellen, die vorher
     env(safe-area-inset-top/bottom) direkt schrieben, haengen jetzt hier dran
     (grep: templates/*.html, static/css/*.css). */
  :root {
    --pm-safe-top: env(safe-area-inset-top, 0px);
    --pm-safe-bottom: env(safe-area-inset-bottom, 0px);
    --pm-safe-left: env(safe-area-inset-left, 0px);
    --pm-safe-right: env(safe-area-inset-right, 0px);
  }
  html.iphone-preview { --pm-safe-top: 59px; --pm-safe-bottom: 34px; }
  /* Querformat-Labor (09.09.2026, ?preview=iphone-quer): Dynamic Island wandert
     an den linken Rand, der Home-Indikator bleibt unten aber in einer
     schmaleren Zone -- die Kamera-Ausbuchtung frisst nur noch links/rechts
     Platz, nicht mehr oben. Werte 1:1 vom iPhone-15-Pro-Querformat (852x393pt,
     siehe shoot.py --quer). */
  html.iphone-preview-quer {
    --pm-safe-top: 0px; --pm-safe-bottom: 21px;
    --pm-safe-left: 59px; --pm-safe-right: 59px;
  }
  /* Zwei rein optische Overlays -- zeigen ehrlich, was iOS ueberdeckt. Nur im
     Labor sichtbar (html.iphone-preview), pointer-events:none, oberste Ebene. */
  html:not(.iphone-preview) .pm-lab-statusbar,
  html:not(.iphone-preview) .pm-lab-homebar { display: none !important; }
  /* Querformat-Overlays: Insel links vertikal (37x126, mittig in der linken
     59px-Zone), Uhr oben links in der Safe-Zone, Home-Balken unten kuerzer. */
  html.iphone-preview-quer .pm-lab-statusbar {
    flex-direction: column; align-items: flex-start; justify-content: flex-start;
    width: 59px; right: auto; height: 100%; padding: 10px 0 0 8px;
  }
  html.iphone-preview-quer .pm-lab-statusbar svg { display: none; }
  html.iphone-preview-quer .pm-lab-island {
    top: 50%; left: 11px; transform: translateY(-50%);
    width: 37px; height: 126px;
  }
  html.iphone-preview-quer .pm-lab-homebar { height: 21px; }
  html.iphone-preview-quer .pm-lab-homebar span { width: 134px; height: 5px; }
  .pm-lab-statusbar {
    position: fixed; top: 0; left: 0; right: 0; height: 59px; z-index: 99999;
    pointer-events: none; display: flex; align-items: flex-end;
    justify-content: space-between; padding: 0 22px 6px; box-sizing: border-box;
    color: #fff; font: 600 15px -apple-system, BlinkMacSystemFont, sans-serif;
  }
  .pm-lab-statusbar .pm-lab-island {
    position: absolute; top: 11px; left: 50%; transform: translateX(-50%);
    width: 126px; height: 37px; border-radius: 20px; background: #000;
  }
  .pm-lab-homebar {
    position: fixed; bottom: 0; left: 0; right: 0; height: 34px; z-index: 99999;
    pointer-events: none; display: flex; align-items: center; justify-content: center;
  }
  .pm-lab-homebar span {
    width: 134px; height: 5px; border-radius: 3px; background: rgba(255,255,255,.68);
  }

  /* ══════════════ Seitenmenue (19.08.2026) ══════════════════════════════
     Phil: "ich will geiles schoenes seitliches menu genau wie bei
     Theaterplaner, das oben ist einfach nicht schoen und frisst zu viel
     Platz". Muster 1:1 von dort uebernommen: feste Spalte am Desktop,
     einfahrende Schublade darunter, abdunkelndes Overlay, 44px Touchziele.

     Bewusst FIXED positioniert statt das Grundgeruest auf Flex umzubauen —
     base.html traegt jede Seite der App; ein Umbau der Seitenstruktur haette
     jede einzelne treffen koennen. So bleibt <main> unangetastet und bekommt
     nur einen linken Abstand. */
  :root { --pm-sidebar-w: 232px; }
  /* iPhone-Huelle-Welle (08.09.2026): Hoehe der unteren Tab-Leiste (#mobTabBar)
     als EINE Variable statt verstreuter 62px-Literale (Phil, TG 17:05: Raender
     unten/Menue 1:1 von PMS uebernehmen — dort ist die Leiste 74px, siehe
     pms/sonos-bridge/templates/index.html:23 --bottomnav). Jede Stelle, die
     bisher "62px" schrieb (Grep: templates/*.html, static/css/*.css), haengt
     jetzt hier dran. */
  :root { --pm-bottomnav: 74px; }
  /* iPhone-Kosmetik (09.09.2026): Hoehe der Onboarding-Leiste (.pm-onboarding,
     volle Breite ueber der Tab-Leiste, Hoch- UND Querformat) als EINE Variable
     statt verstreuter 40px-Literale -- so bleibt der zusaetzliche Boden-Abstand
     (body.pm-onb-leiste, static/css/style.css) exakt an der tatsaechlichen
     Leistenhoehe haengen, egal wo sie noch auftaucht. */
  :root { --pm-onb-leiste: 40px; }
  .pm-sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; width: var(--pm-sidebar-w);
    z-index: 1030; display: flex; flex-direction: column;
    padding: 16px 12px 12px; overflow-y: auto;
    background: #0a0e1a; border-right: 1px solid #1a2744;
  }
  .pm-sidebar .pm-sb-marke {
    display: flex; align-items: center; gap: 9px; padding: 2px 8px 16px;
    text-decoration: none; color: #e5e7eb; font-weight: 700;
  }
  .pm-sidebar .pm-sb-marke img { width: 30px; height: 30px; }
  .pm-sb-item {
    display: flex; align-items: center; gap: 11px; padding: 9px 12px;
    border-radius: 9px; color: #cbd5e1; text-decoration: none; border: 0;
    background: none; width: 100%; text-align: left; font-size: 0.875rem;
    font-weight: 500; margin-bottom: 2px; min-height: 40px;
    transition: background .12s, color .12s;
  }
  .pm-sb-item:hover { background: rgba(255,255,255,.06); color: #fff; }
  .pm-sb-item.active { background: var(--pm-accent-2, #56d6b0); color: #06231c; font-weight: 600; }
  .pm-sb-item .bi { font-size: 1rem; width: 20px; text-align: center; flex-shrink: 0; }
  .pm-sb-spacer { flex: 1 1 auto; min-height: 10px; }

  /* ── Schmaler Modus: nur Symbole (Phil, 22.08.2026) ───────────────────────
     Die Beschriftungen sind seit derselben Welle in <span class="pm-sb-text">
     gefasst — vorher waren es blanke Textknoten, und die kann CSS nicht
     ausblenden. Deshalb ging es davor nicht ohne diese Vorarbeit.

     Der Zustand haengt an einer Klasse auf <html>, nicht an der Leiste selbst:
     der Inhalt daneben braucht denselben linken Abstand, und der wird ueber
     dieselbe Variable gesteuert. Eine Klasse an der Leiste wuerde <main> nicht
     erreichen. */
  html.pm-menue-schmal { --pm-sidebar-w: 64px; }
  html.pm-menue-schmal .pm-sb-text,
  html.pm-menue-schmal .pm-sb-marke span:not(.pm-logo),
  html.pm-menue-schmal .pm-sb-abschnitt,
  html.pm-menue-schmal .pm-sb-chev,
  html.pm-menue-schmal .pm-sb-trial { display: none; }
  html.pm-menue-schmal .pm-sidebar { padding-left: 8px; padding-right: 8px; }
  html.pm-menue-schmal .pm-sb-item {
    justify-content: center; gap: 0; padding-left: 0; padding-right: 0;
  }
  html.pm-menue-schmal .pm-sb-item .bi { width: auto; font-size: 1.15rem; }
  /* Untermenues klappen im schmalen Modus nicht auf — dort waere nur eine
     Spalte namenloser Punkte zu sehen. */
  html.pm-menue-schmal .pm-sb-untergruppe { display: none !important; }
  html.pm-menue-schmal .pm-sb-marke { justify-content: center; padding: 2px 0 16px; }

  /* Schmale Leiste + Fussleiste (Welle wave/konto-avatar, 25.08.2026):
     nebeneinander braeuchten Avatar und die beiden Werkzeuge 44+44+44 = 132px,
     die Leiste ist im schmalen Zustand aber nur 64px breit — sie waeren
     herausgelaufen. Darum untereinander, alle drei mittig. Die 44x44 bleiben
     dabei unberuehrt (nur die Anordnung aendert sich). */
  html.pm-menue-schmal .pm-sb-werkzeuge {
    flex-direction: column; gap: 2px; padding-left: 0; padding-right: 0;
  }
  html.pm-menue-schmal .pm-sb-wz-gruppe { flex-direction: column; margin-left: 0; }
  /* Das Menue kippt nach RECHTS aus der Leiste heraus statt an ihrer linken
     Kante zu kleben — sonst laege es fast vollstaendig ausserhalb. */
  html.pm-menue-schmal .pm-sb-kontomenue { left: calc(100% + 4px); bottom: 0; }

  /* Umschalter: sitzt unten in der Leiste, dreht sich mit dem Zustand. */
  .pm-sb-schmal-knopf {
    display: flex; align-items: center; justify-content: center; gap: 8px;
    width: 100%; min-height: 40px; margin-top: 4px; padding: 8px;
    background: none; border: 0; border-radius: 9px;
    color: #7c8ba1; font-size: 0.8rem; cursor: pointer;
  }
  .pm-sb-schmal-knopf:hover { background: rgba(255,255,255,.06); color: #fff; }
  html.pm-menue-schmal .pm-sb-schmal-knopf .bi { transform: rotate(180deg); }
  .pm-sb-trenner { height: 1px; background: #1a2744; margin: 8px 4px; }
  /* Untereintraege (Calc, Alerts) — eingerueckt, kleiner, ohne eigenes Icon-Feld */
  .pm-sb-item.pm-sb-unter { padding-left: 43px; font-size: 0.82rem; min-height: 36px; }

  /* ── Klapp-Gruppen ──────────────────────────────────────────────────────
     Der Pfeil sitzt rechts und dreht sich beim Aufklappen. Zu ist die Gruppe
     hoehe 0 statt display:none — nur so laesst sich die Bewegung zeigen. */
  .pm-sb-pfeil { margin-left: auto; font-size: .72rem !important; width: auto !important;
                 opacity: .55; transition: transform .18s ease; }
  .pm-sb-klapp[aria-expanded="true"] .pm-sb-pfeil { transform: rotate(180deg); opacity: .9; }
  /* grid-template-rows 0fr→1fr klappt ohne feste Hoehe auf. Der Trick braucht
     GENAU EINE Zeile — die Links stecken darum in einem inneren Kasten, sonst
     bekommt jeder Link eine eigene Zeile und nur die erste wuerde schrumpfen. */
  .pm-sb-untergruppe {
    display: grid; grid-template-rows: 0fr;
    transition: grid-template-rows .18s ease;
  }
  .pm-sb-untergruppe > div { overflow: hidden; min-height: 0; }
  .pm-sb-untergruppe.offen { grid-template-rows: 1fr; }

  /* Zaehler (Digest, Alerts, News): rechtsbuendig statt schwebend wie in der
     alten Kopfzeile — in einer Zeile ist "am Rand" die klarere Stelle. */
  .pm-sb-badge {
    margin-left: auto; font-size: .62rem; font-weight: 700; line-height: 1;
    padding: 3px 6px; border-radius: 999px; flex-shrink: 0;
  }
  /* Steht ein Pfeil daneben, darf nur EINER den Rest schlucken. */
  .pm-sb-badge + .pm-sb-pfeil { margin-left: 8px; }

  /* BETA-Pille (25.08.2026, Phil: "die Module sind unfertig"). Bewusst GRAU und
     ohne Farbakzent: sie ist ein Hinweis, keine Auszeichnung -- ein buntes
     Abzeichen liest sich wie "neu und toll" statt wie "noch nicht fertig".
     Nutzt dieselbe Grundform wie .pm-sb-badge, aber OHNE margin-left:auto --
     die Pille gehoert direkt an den Namen, nicht an den Zeilenrand, sonst
     sieht sie aus wie ein Zaehler. */
  .pm-sb-beta {
    display: inline-block; margin-left: 6px; font-size: .58rem; font-weight: 700;
    line-height: 1; padding: 2px 5px; border-radius: 999px; letter-spacing: .04em;
    text-transform: uppercase; vertical-align: middle; flex-shrink: 0;
    background: rgba(148,163,184,.18); color: #94a3b8;
    border: 1px solid rgba(148,163,184,.30);
  }
  /* Im hellen Thema traegt derselbe Grauton zu wenig Kontrast. */
  @media (prefers-color-scheme: light) {
    html:not([data-theme="dark"]) .pm-sb-beta {
      background: rgba(100,116,139,.12); color: #475569;
      border-color: rgba(100,116,139,.28);
    }
  }
  html[data-theme="light"] .pm-sb-beta {
    background: rgba(100,116,139,.12); color: #475569;
    border-color: rgba(100,116,139,.28);
  }

  /* Digest-Flyout: faehrt seitlich neben der Spalte auf, nicht darunter. */
  .pm-sb-drop { position: relative; }
  .pm-sb-drop .dropdown-toggle::after { display: none; }

  /* ── Fussleiste: Avatar links, Werkzeuge rechts ────────────────────────
     Welle wave/konto-avatar, 25.08.2026. Vorher fuellten zwei Bootstrap-
     Knoepfe (btn-outline-secondary, flex:1) die ganze Breite — mit Rahmen,
     also optisch zwei Kaesten. Jetzt: der Avatar steht linksbuendig, die
     beiden Werkzeuge ruecken per margin-left:auto nach rechts. */
  .pm-sb-werkzeuge {
    display: flex; align-items: center; gap: 6px; padding: 6px 4px 2px;
  }
  .pm-sb-wz-gruppe { display: flex; align-items: center; gap: 2px; margin-left: auto; }

  /* ── Auge-Knopf, appweit (Phil, 01.09.2026) ────────────────────────────────
     "auge taste: haesslich, ganze app aendern, rahmen weg, nur das auge, wenn
     aktiv farbig, wenn inaktiv grau."
     EINE Handschrift fuer jedes Auge, das etwas sichtbar/unsichtbar schaltet
     (Passwoerter, API-Secrets). Kein Rahmen, kein Kasten, kein Hintergrund --
     nur das Glyph. Die Trefferflaeche bleibt trotzdem >= 36px, dasselbe
     Muster wie .pm-sb-wz darueber und .watchlist-toggle-btn in positions.html:
     sichtbar klein, anfassbar gross.
     ZUSTAND: grau = verborgen, farbig = sichtbar. Wie beim Watchlist-Auge
     haengt der Zustand NICHT allein an der Farbe -- das Glyph wechselt
     zusaetzlich die Form (bi-eye <-> bi-eye-slash), damit es auch ohne
     Farbunterscheidung ablesbar bleibt.
     !important gegen Bootstrap: der Knopf sitzt in einer .input-group, deren
     .btn-Regeln sonst Rahmen und Flaeche zurueckholen. */
  .pm-auge-btn {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 36px; min-height: 36px; padding: 0 8px;
    border: 0 !important; background: none !important; box-shadow: none !important;
    color: #6b7280 !important; font-size: 1rem; line-height: 1; cursor: pointer;
    transition: color .12s ease;
  }
  .pm-auge-btn:hover { color: #9ca3af !important; }
  .pm-auge-btn.ist-an { color: var(--pm-accent-2, #56d6b0) !important; }
  .pm-auge-btn:focus-visible {
    outline: 2px solid var(--pm-accent-2, #56d6b0); outline-offset: 2px; border-radius: 6px;
  }

  /* Die Werkzeug-Knoepfe: klein und ruhig, aber das TIPPZIEL bleibt 44x44.
     Genau der Fehler ist hier am 31.07. schon einmal passiert (gemessen: 31px
     hoch). Darum wird nur das Symbol klein — die Flaeche bleibt gross. */
  .pm-sb-wz {
    display: inline-flex; align-items: center; justify-content: center;
    position: relative;
    min-width: 44px; min-height: 44px; padding: 0;
    background: none; border: 1px solid transparent; border-radius: 9px;
    color: #7c8ba1; font-size: 0.95rem; line-height: 1; cursor: pointer;
    transition: background .12s ease, color .12s ease;
  }
  .pm-sb-wz:hover { background: rgba(255,255,255,.07); color: #fff; }
  .pm-sb-wz:focus-visible { outline: 2px solid var(--pm-accent-2, #56d6b0); outline-offset: 1px; }
  /* Der Zaehler klebte vorher per Inline-Stil am Bootstrap-Knopf. Gleiche
     Optik, jetzt an der neuen, groesseren Flaeche ausgerichtet. */
  .pm-sb-wz-zaehler {
    position: absolute; bottom: 6px; right: 4px;
    font-size: 0.5rem; font-family: monospace; color: #6b7280;
    background: #0a0f1a; border-radius: 2px; padding: 0 2px; line-height: 1.4;
  }

  /* Avatar-Knopf + sein Menue. */
  .pm-sb-konto { position: relative; }
  .pm-sb-avatar-knopf {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; min-height: 44px; padding: 0;
    background: none; border: 0; border-radius: 50%; cursor: pointer;
  }
  .pm-sb-avatar-knopf:hover .pm-avatar { box-shadow: 0 0 0 2px rgba(255,255,255,.22); }
  .pm-sb-avatar-knopf:focus-visible { outline: 2px solid var(--pm-accent-2, #56d6b0); outline-offset: 2px; }
  .pm-sb-avatar-knopf[aria-expanded="true"] .pm-avatar {
    box-shadow: 0 0 0 2px var(--pm-accent-2, #56d6b0);
  }

  /* Das Menue faehrt NACH OBEN auf (die Leiste endet direkt darunter) und
     bleibt an der linken Kante, damit es im schmalen Modus nicht aus der
     64px-Leiste heraus nach links kippt. */
  .pm-sb-kontomenue {
    position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 1055;
    min-width: 208px; padding: 6px;
    background: #0f1626; border: 1px solid #1f2f4d; border-radius: 12px;
    box-shadow: 0 12px 30px rgba(0,0,0,.5);
  }
  .pm-sb-kontomenue[hidden] { display: none; }
  .pm-sb-kontokopf {
    display: flex; flex-direction: column; gap: 1px;
    padding: 7px 10px 9px; margin-bottom: 4px; border-bottom: 1px solid #1a2744;
  }
  .pm-sb-kontoname { color: #fff; font-weight: 600; font-size: 0.85rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .pm-sb-kontologin { color: #7c8ba1; font-size: 0.72rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .pm-sb-menuepunkt {
    display: flex; align-items: center; gap: 9px;
    min-height: 40px; padding: 8px 10px; border-radius: 8px;
    color: #c7d2e0; font-size: 0.85rem; text-decoration: none;
  }
  .pm-sb-menuepunkt:hover { background: rgba(255,255,255,.07); color: #fff; }
  .pm-sb-menuepunkt .bi { font-size: 0.95rem; width: 18px; text-align: center; }
  .pm-sb-menuepunkt-warn { color: #f2917c; }
  .pm-sb-menuepunkt-warn:hover { background: rgba(239,68,68,.14); color: #ffb3a1; }
  .pm-sb-menuetrenner { height: 1px; background: #1a2744; margin: 4px 6px; }

  /* ── Runder Kontoavatar (Welle wave/konto-avatar, 25.08.2026) ──────────
     EINE Klasse fuer beide Einsatzorte: die Konto-Karte in den Einstellungen
     und die Fussleiste der Seitenleiste. Ohne eigenes Bild steht die
     Hintergrundfarbe im style-Attribut (serverseitig aus dem Kontonamen
     abgeleitet, siehe konto_module.avatar_farbe) und die Initialen als Text.
     Mit Bild fuellt ein <img> denselben Kreis — darum object-fit: cover,
     sonst verzerrt ein nicht-quadratisches PNG das Gesicht. */
  .pm-avatar {
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; border-radius: 50%; overflow: hidden;
    flex-shrink: 0; color: #fff; font-weight: 600; font-size: 0.72rem;
    line-height: 1; letter-spacing: .02em; user-select: none;
    background: #334155;
  }
  .pm-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .pm-avatar-lg { width: 52px; height: 52px; font-size: 1.15rem; }

  /* ── Seitenkopf: Titel und Werkzeuge ──────────────────────────────────
     18 Vorlagen bauen ihren Kopf gleich: links Titel plus Untertitel, rechts
     eine Knopfreihe, dazwischen justify-content-between — aber OHNE Umbruch.
     Am Handy gewinnt die Knopfreihe: gemessen war "Accounts" auf 79px
     gequetscht und brach auf zwei Zeilen um, "Alert History" auf 78px.

     Darum eine Klasse statt 18 Flicken. Am Desktop aendert sich nichts (es
     wird nur umbrechen erlaubt, und dort passt alles in eine Zeile); am Handy
     bekommt der Titel die volle Zeile und die Werkzeuge stehen darunter. */
  .pm-seitenkopf { flex-wrap: wrap; row-gap: .55rem; }
  @media (max-width: 767.98px) {
    .pm-seitenkopf > *:first-child { flex: 1 0 100%; min-width: 0; }
    .pm-seitenkopf > *:not(:first-child) { flex: 0 1 auto; }
  }

  /* ── Reihen, die am Handy ueber den Rand liefen ───────────────────────
     Gemessen am 19.08.2026 auf 390px, je mit Elternkasten und Ursache. Alle
     vier haben denselben Kern: eine Reihe darf nicht umbrechen, oder ein Kind
     darf nicht schrumpfen. Kein Verstecken, kein Zwang zur Breite — nur
     umbrechen lassen, was umbrechen will. */
  @media (max-width: 767.98px) {
    /* Transaktionen, Werkzeugleiste: Spalten-Menue lief 36px hinaus (nowrap). */
    .txn-toolbar { flex-wrap: wrap; row-gap: .4rem; }
    /* Auswahlfelder mit fester Breite (z.B. 160px im Alert-Verlauf) sprengen
       jede enge Zeile. Am Handy zaehlt die Zeile, nicht die Wunschbreite. */
    .pm-seitenkopf .form-select,
    .pm-seitenkopf select { width: auto !important; max-width: 100%; flex: 1 1 auto; }
    /* Bootstrap-Zeilen tragen negative Aussenabstaende (halbe Rinne je Seite).
       Fehlt dem Elternkasten das passende Innenpolster, ragt die Zeile genau
       um diese halbe Rinne hinaus — gemessen 13px bei g-3. */
    /* Kacheln in Rasterzeilen duerfen kleiner werden als ihr Inhalt, sonst
       sprengen lange Zahlen die Spalte. */
    .kpi-row > * { min-width: 0; }
    /* Dieselbe Rinnen-Rechnung wie bei .news-main: eine Bootstrap-Zeile ragt
       ohne passendes Innenpolster um eine halbe Rinne hinaus (g-2 = 8px). */
    .txn-screen { padding-left: .4rem; padding-right: .4rem; }
    /* Auch der zweite Teil des Seitenkopfs darf die Zeile nicht sprengen —
       und die Werkzeugreihe darin muss umbrechen duerfen. Ohne das schiebt
       sie ihre Knoepfe einfach ueber den Rand (gemessen: "Aktive Alerts"
       28px hinaus, nachdem der Kasten selbst schon passte). */
    .pm-seitenkopf > * { max-width: 100%; }
    .pm-seitenkopf .d-flex { flex-wrap: wrap; row-gap: .4rem; }
  }

  /* ── Tippziele: die kleinen Knoepfe der ganzen App ────────────────────
     Gemessen am 20.08.2026 auf 390x844 ueber alle Module: die Reste unter
     44px waren fast alle vom selben Typ — Bootstraps .btn-sm ist 31px hoch,
     die News-Reiter 35px. Das betraf 33 Ziele auf /transactions, 17 auf
     /coin-calculator, 16 auf /preisabgleich, 13 auf /dca-simulator.

     Eine Regel statt Einzelfixes je Seite. Am Desktop aendert sich nichts —
     dort zielt eine Maus, keine Fingerkuppe.

     NICHT betroffen: Verweise im Fliesstext (z.B. im Handbuch). Ein Link
     mitten im Satz auf 44px aufzublasen zerreisst den Absatz; dafuer gilt die
     Regel bewusst nur fuer Knoepfe und knopfartige Elemente. */
  @media (max-width: 767.98px) {
    .btn, .btn-sm, .btn-pm, .btn-pm-sm, .reiter, .pa-filter-btn,
    .kopf-schalter, .sek-filter, .einstieg {
      min-height: 44px;
    }
    /* Knoepfe ohne Text (nur Symbol) brauchen die Breite ebenso. */
    .btn:has(> i:only-child), .btn-pm:has(> i:only-child) { min-width: 44px; }
    /* Kaestchen zum Ankreuzen waren 12x12 — die Trefferflaeche waechst per
       Polster, das Kaestchen selbst bleibt klein. */
    input[type="checkbox"].form-check-input,
    input[type="radio"].form-check-input {
      padding: 15px; margin: -15px; background-clip: content-box;
    }
  }

  /* ── Spalten, die am Handy wegfallen ──────────────────────────────────
     Phil, 19.08.2026: "tabellen nur mit dem wichtigsten in mobile, vor allem
     der thesenreiter ist überladen".

     EINE Regel fuer die ganze App. Eine Tabelle mit zehn Spalten auf 390px
     wird sonst zur Seitwaertsrolle, und was rechts liegt, sieht nie jemand.

     Bedingung fuers Weglassen: die Angabe muss auf demselben Weg noch
     erreichbar sein — ueber die Detailansicht der Zeile. Sonst ist es kein
     Kuerzen, sondern Verstecken. */
  @media (max-width: 767.98px) {
    .mobile-hide { display: none !important; }
    /* Enge Spalten brauchen engere Zellen — sonst nuetzt das Weglassen wenig.
       Gemessen: vier Spalten belegten auf 390px trotzdem 720px. */
    .pm-table th, .pm-table td { padding: 6px 7px; }
    .pm-table { font-size: .82rem; }
    /* Knoepfe IN Tabellen: das Standardpolster von Bootstrap bestimmt sonst
       die Spaltenbreite. Gemessen: 245px Spalte fuer ein Wort. */
    .pm-table .tb-knopf { padding: 3px 8px; font-size: .72rem; white-space: nowrap; }
    /* Lange Asset-Namen kuerzen statt die Zeile sprengen. */
    .these-asset-name { max-width: 92px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* Die Werkzeugzeile der Spalte (Auffrischen, Hilfe-Birne) kam mit 31px
       Hoehe — gemessen am 19.08. als kleinstes Tippziel auf JEDER Seite.
       Das waren Bootstrap-Knoepfe, die die 44px-Regel fuer .pm-sb-item nicht
       erben. Seit dem Umbau der Fussleiste (25.08.2026) tragen sie die Klasse
       .pm-sb-wz und bringen ihre 44x44 selbst mit — die Regel bleibt als
       Riegel stehen, damit die Flaeche nicht beim naechsten Feinschliff
       wieder wegschrumpft. */
    .pm-sb-werkzeuge .btn,
    .pm-sb-wz,
    .pm-sb-avatar-knopf { min-height: 44px; min-width: 44px; }
  }

  /* Overlay: nur in der Schubladen-Ansicht sichtbar. */
  .pm-sidebar-overlay { display: none; }

  /* ── Desktop: Spalte steht, Inhalt rueckt nach rechts ── */
  @media (min-width: 992px) {
    body { padding-left: var(--pm-sidebar-w); }
  }

  /* ── Schublade (unter 992px) ── */
  @media (max-width: 991.98px) {
    .pm-sidebar {
      transform: translateX(-100%); transition: transform .22s ease;
      width: 258px; box-shadow: 2px 0 24px rgba(0,0,0,.5);
    }
    body.pm-sidebar-offen .pm-sidebar { transform: translateX(0); }
    .pm-sidebar-overlay {
      display: block; position: fixed; inset: 0; z-index: 1020;
      background: rgba(0,0,0,0); pointer-events: none; transition: background .22s ease;
    }
    body.pm-sidebar-offen .pm-sidebar-overlay { background: rgba(0,0,0,.45); pointer-events: auto; }
    body.pm-sidebar-offen { overflow: hidden; }
    /* Das Onboarding-Faehnchen klebt fest unten rechts (position:fixed,
       bottom:76px) und lag damit UEBER den Werkzeugknoepfen der offenen
       Schublade — gemessen am 20.08.2026 auf 390px. Solange die Schublade
       offen ist, hat es dort nichts zu suchen: sie ist eine Vollansicht,
       kein Beiwerk. */
    body.pm-sidebar-offen .pm-onboarding { display: none; }
    /* 44px Touchziel — auch fuer die Untereintraege. Die brauchen den Zusatz
       ausdruecklich: .pm-sb-item.pm-sb-unter ist spezifischer als .pm-sb-item
       und haette seine 36px behalten. Am Handy gemessen: 38px statt 44px. */
    .pm-sb-item, .pm-sb-item.pm-sb-unter { min-height: 44px; }
    /* Die Marke steht in der Schublade sonst UNSICHTBAR hinter der Kopfzeile
       und schiebt die Eintraege nur zufaellig an die richtige Stelle. Also
       bewusst ausblenden (macht der Theaterplaner genauso) und die Eintraege
       gezielt unterhalb der Kopfzeile beginnen lassen. */
    .pm-sidebar .pm-sb-marke { display: none; }
    /* Oben die Kopfzeile, unten die feste Tab-Leiste (#mobTabBar, 62px + Safe
       Area, z-index 1040 — also UEBER der Schublade). Ohne den unteren Abstand
       verschwindet der letzte Eintrag dahinter; am iPhone gemessen: "Settings"
       lag zur Haelfte unter der Leiste. */
    .pm-sidebar {
      /* Rueckfall 62px nur, solange die Messung (--pm-topbar-h, gesetzt vom
         Hoehen-Skript direkt nach <body>) noch nicht gelaufen ist. */
      padding-top: calc(var(--pm-topbar-h, 62px) + 6px);
      padding-bottom: calc(var(--pm-bottomnav) + var(--pm-safe-bottom) + 12px);
    }
  }
  html.embed-mode .pm-sidebar, html.embed-mode .pm-sidebar-overlay { display: none !important; }
  html.embed-mode body { padding-left: 0 !important; }

  html.embed-mode .pm-navbar,
  html.embed-mode #mobTabBar,
  html.embed-mode .pm-footer,
  /* Trennstrich zwischen den Boersen in der Fussleiste — bewusst duenn und
     leise: er soll gruppieren, nicht auffallen. */
  .sb-mkt-trenner {
    display: inline-block; width: 1px; height: 11px; vertical-align: -1px;
    margin: 0 8px; background: rgba(255,255,255,.16);
  }
  html.embed-mode .pm-ticker-tape,
  html.embed-mode #offlineBanner,
  html.embed-mode #matrixBackground {
    display: none !important;
  }
  /* pm-main hatte Navbar/Bottom-Tab-Bar als Nachbarn eingeplant (Top-Padding,
     Body-Padding fuer die feste Bottom-Tab-Bar) — im Embed-Modus fehlen beide,
     also Platz zurueckgeben statt Leerraum oben/unten im kleinen Modal-Iframe. */
  html.embed-mode .pm-main {
    padding-top: 0.85rem !important;
  }
  html.embed-mode body {
    padding-bottom: 0 !important;
  }

  /* ══ Ticker-Vorschlagsliste ═══════════════════════════════════════════════
     Gehoert zu setupTickerAutocomplete() in app/static/js/app.js — der EINEN
     Vorschlagsliste der App (14 Aufrufstellen in 9 Templates, alle ueber
     /api/search). Bis zum 25.08.2026 steckte ihr komplettes Aussehen als
     inline style="…" in einem JS-Template-Literal, samt fester Hex-Farben und
     zwei Inline-Handlern (onmouseover/onmouseout) fuer den Hover. Beides liegt
     jetzt hier — der Hover ist ein CSS-Fall (:hover), kein JS-Fall.
     (Bewusst ohne Gleichheitszeichen geschrieben: test_csp_sperrklinke.py
     maskiert Kommentare nur in Skript-Bloecken, nicht in Stil-Bloecken, und
     wuerde diesen Erklaertext sonst als zwei neue Handler zaehlen.)
     ⚠️ Und aus demselben Grund stehen die Woerter "Skript"/"Stil" hier
     ausgeschrieben statt als Tag: test_sprache_chartengine.py schneidet die
     Skriptbloecke der Seite mit einem Muster heraus, das auf den oeffnenden
     Tag anspringt -- ein Tag im Kommentar liess es mitten im Stil-Block
     beginnen und "node --check" fiel ueber den Fliesstext. Gemessen, nicht
     vermutet: genau dieser Kommentar hat den Test am 25.08. rot gemacht.)

     ── Die gemessene Ursache der Ueberlappung ──────────────────────────────
     Phils Screenshot zeigte Typ-Pille UEBER Quellen-Pille. Nachgemessen im
     Browser mit dem alten Markup, Feldbreite 240 px:

         Treffer     Typ-Pille rechts   Quellen-Pille links   Zeilenhoehe
         BTC-USD          117 px             119 px            67 px (Umbruch)
         BTNBRX=X         145 px             119 px            50 px
         BTCUSD=X         145 px             119 px            50 px
         BTC=F            149 px             118 px            50 px

     Die naheliegende Erklaerung — "die Quellen-Pille schrumpft, ihr
     nowrap-Text laeuft aus dem Kasten" — ist FALSCH: gemessen stand sie mit
     122 px Kasten zu 120 px Text auf voller Breite. Ein Flex-Element hat
     min-width:auto (= min-content), nowrap-Text schuetzt sich damit selbst.

     Geschrumpft ist die LINKE Spalte (flex:1; min-width:0). Die hatte
     overflow:visible und nichts, was kuerzt — ihr Ueberschuss (die
     nowrap-Typ-Pille, der unbrechbare Monospace-Ticker) wurde also ueber den
     Nachbarn gemalt. Wo der Ticker eine Trennstelle hatte ("BTC-USD"), brach
     er stattdessen um und rutschte unter die Pille.

         Eine Spalte, die schrumpfen DARF (min-width:0), aber weder klippt
         (overflow:hidden) noch kuerzt (text-overflow), gibt ihren
         Ueberschuss nicht auf — sie malt ihn ueber den Nachbarn.

     ── Was daraus folgt ────────────────────────────────────────────────────
     Der Riegel ist overflow:hidden + Kuerzung auf .pl-ac-haupt, NICHT
     kleinere Schrift: die Pillen bleiben bei 0.65rem lesbar (test_… deckelt
     das nach unten). Kompakter wird die Zeile ueber weniger Polsterung,
     kuerzere Quellen-Etiketten ("Yahoo · CCY" statt "Yahoo Finance (CCY)")
     und die zweite Spalte, in der Typ- und Quellen-Pille UNTEREINANDER
     stehen und gar nicht erst um dieselbe Zeile konkurrieren.

     Farben kommen aus tokens.css (Hell/Dunkel dort gemessen, WCAG AA), jede
     var() mit dem alten Hex-Wert als Rueckfall. Waechter:
     app/test_tickersuche_liste.py. */

  .pl-ac-liste {
    position: absolute; top: 100%; left: 0; right: 0; z-index: 9999;
    margin-top: 2px;
    background: var(--pm-card, #141d2e);
    border: 1px solid var(--pm-border, #1f2937);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .34);
    max-height: 280px; overflow-y: auto; overflow-x: hidden;
    display: none;
  }
  .pl-ac-liste.pl-ac-offen { display: block; }

  .pl-ac-zeile {
    display: flex; align-items: center; gap: 8px;
    padding: 4px 9px;
    cursor: pointer;
    border-bottom: 1px solid var(--pm-border, #1f2937);
    border-left: 3px solid transparent;
  }
  .pl-ac-zeile:last-child { border-bottom: 0; }
  .pl-ac-zeile:hover,
  .pl-ac-zeile.pl-ac-aktiv { background: var(--pm-card-2, #1f2937); }
  /* Der per Pfeiltaste markierte Eintrag bekommt zusaetzlich einen Rahmen.
     Der Hintergrund allein reicht nicht: im hellen Thema liegt --pm-card-2
     (#f2f2ee) auf --pm-card (#ffffff) — gemessen kaum zu sehen. Und er soll
     sich vom blossen Mauszeiger-Hover unterscheiden. */
  .pl-ac-zeile.pl-ac-aktiv {
    outline: 1px solid var(--pm-accent, #60a5fa);
    outline-offset: -1px;
  }

  /* Treffer aus dem eigenen Bestand: gruener Balken + gruener Grund. Die
     Information haengt NICHT allein an der Farbe — die Zeile traegt
     data-portfolio="1" und zeigt sichtbar die Quellen-Pille "★ Portfolio". */
  .pl-ac-zeile[data-portfolio="1"] {
    border-left-color: var(--pm-green, #22c55e);
    background: rgba(34, 197, 94, .05);
    background: color-mix(in srgb, var(--pm-green, #22c55e) 7%, transparent);
  }
  .pl-ac-zeile[data-portfolio="1"]:hover,
  .pl-ac-zeile[data-portfolio="1"].pl-ac-aktiv {
    background: rgba(34, 197, 94, .14);
    background: color-mix(in srgb, var(--pm-green, #22c55e) 16%, transparent);
  }

  /* DIE Stelle, an der die Ueberlappung stirbt (siehe Messung oben):
     schrumpfen ja — aber dann auch klippen, nicht ueber den Nachbarn malen. */
  .pl-ac-haupt {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
  }
  .pl-ac-ticker,
  .pl-ac-name {
    display: block;
    min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .pl-ac-ticker {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-weight: 700; font-size: 0.84rem; line-height: 1.35;
    color: var(--pm-text, #e5e7eb);
  }
  .pl-ac-name {
    font-size: 0.72rem; line-height: 1.35;
    color: var(--pm-muted, #9ca3af);
  }

  /* Zweite Spalte: Typ oben, Quelle darunter. Weil die beiden Pillen nie in
     derselben Zeile stehen, koennen sie einander auch dann nicht verdraengen,
     wenn spaeter jemand am Klippen der Textspalte dreht. */
  .pl-ac-marken {
    flex: 0 0 auto;
    max-width: 46%;
    min-width: 0;
    display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
  }
  .pl-ac-pille {
    max-width: 100%;
    font-size: 0.65rem; line-height: 1.5;   /* nicht kleiner — Lesbarkeit */
    padding: 0 5px;
    border-radius: 3px;
    border: 1px solid currentColor;
    background: transparent;
    /* currentColor kommt aus .pl-ac-typ/.pl-ac-quelle unten — Rand und Grund
       leiten sich davon ab, statt drei Farben pro Pille zu pflegen. */
    border-color: color-mix(in srgb, currentColor 38%, transparent);
    background: color-mix(in srgb, currentColor 13%, transparent);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  /* Typfarben: die gemessenen Token statt der alten Dunkel-Hex-Werte
     (#60a5fa & Co. lagen im hellen Thema unter der AA-Schwelle). */
  .pl-ac-typ                        { color: var(--pm-muted,    #9ca3af); }
  .pl-ac-typ[data-typ="crypto"]     { color: var(--pm-accent,   #60a5fa); }
  .pl-ac-typ[data-typ="stock"]      { color: var(--pm-green,    #34d399); }
  .pl-ac-typ[data-typ="etf"]        { color: var(--pm-purple,   #a78bfa); }
  .pl-ac-typ[data-typ="metal"]      { color: var(--pm-gold,     #fbbf24); }
  .pl-ac-typ[data-typ="commodity"]  { color: var(--pm-gold,     #fb923c); }
  .pl-ac-typ[data-typ="forex"]      { color: var(--pm-accent-2, #2dd4bf); }

  /* Die Quelle ist die leiseste Information der Zeile und bleibt gedaempft —
     ausser beim eigenen Bestand, der auch hier gruen bleibt. */
  .pl-ac-quelle                             { color: var(--pm-muted, #9ca3af); }
  .pl-ac-quelle[data-quelle="portfolio"]    { color: var(--pm-green, #22c55e); }
  .pl-ac-quelle[data-quelle="coingecko"]    { color: var(--pm-gold,  #fbbf24); }
