:root {
    color-scheme: dark;
    --bg: #07111f;
    --panel: rgba(var(--panel-tint-rgb), 0.9);
    --panel-2: rgba(30, 41, 59, 0.95);
    /* Vollständig deckende Variante von --panel-2 (gleicher Blauton, ohne Alpha) - für Flächen, bei
       denen NICHTS durchscheinen darf (Bug-Report: mobiles Hamburger-Menü, siehe .nav-links unten).
       Eigene Variable statt eines hart codierten rgb(...)-Werts direkt in .nav-links, damit ein
       Farbschema wie "gold" sie zusammen mit --panel-2 mit überschreiben kann (sonst hätte das
       Hamburger-Menü im Schwarz/Gold-Schema weiterhin bläulich statt grau gewirkt). */
    --panel-2-solid: rgb(30, 41, 59);
    --text: #f8fafc;
    --muted: #94a3b8;
    --accent: #38bdf8;
    --accent-2: #8b5cf6;
    --danger: #fb7185;
    /* Nutzerwunsch: die Rot/Grün-Töne bei überfälligen/heutigen Aufgaben-Badges wirkten "sehr grell" -
       --success ist ein zu --danger stilistisch passendes, gedämpftes Pastellgrün (siehe
       .task-badge-* unten), analog zum bereits bestehenden gedämpften --danger statt eines kräftigen
       Basis-Rots. */
    --success: #86efac;
    --border: rgba(148, 163, 184, 0.25);
    /* Basisfarbe (ohne Alpha) für die vielen einzelnen Box-Hintergründe unten (Regel-/Konnektor-/
       Agenten-Karten, Kalenderzellen, ...), die alle denselben Blauton mit unterschiedlicher
       Deckkraft nutzen - als eigene Variable, damit ein Farbschema wie "gold" diese TÖNUNG mit
       überschreiben kann, nicht nur --panel/--panel-2. */
    --panel-tint-rgb: 15, 23, 42;
}
/*
 * Firmen-Farbschema-Preset "Schwarz/Gold metallisch" (settings.php > Firma > Firmendaten,
 * settings.theme). Überschreibt NUR die oben in :root definierten Variablen - der Rest des
 * Stylesheets baut praktisch überall auf var(--accent) usw. auf, ein zweites komplettes
 * Stylesheet ist deshalb nicht nötig. body[data-theme] wird von views/header.php gesetzt.
 * --border und --panel-tint-rgb bewusst NEUTRAL grau (nicht golden) - Trennlinien/Rahmen und die
 * vielen Karten-Hintergründe sollen nicht mitfärben, nur --accent/--accent-2 tragen den Goldton.
 * --panel/--panel-2 mit ähnlich hoher Deckkraft wie im Standard-Theme (vorher deutlich
 * transparenter als Blau/Schwarz - Nutzer-Feedback: Dropdowns/Modals wirkten dadurch zu
 * durchsichtig). Graubasis --panel/--panel-tint-rgb (#333, Container-Hintergründe) und --panel-2
 * (#444, Eingabefeld-Hintergründe) einmal nachträglich noch etwas verdunkelt (Nutzerwunsch).
 */
body[data-theme="gold"] {
    --bg: #282A2B;
    --panel: rgba(51, 51, 51, 0.85);
    --panel-2: rgba(68, 68, 68, 0.95);
    --panel-2-solid: rgb(68, 68, 68);
    --accent: #E0B054;
    --accent-2: #bd9548;
    --border: rgba(200, 200, 200, 0.25);
    --panel-tint-rgb: 51, 51, 51;
}
body[data-theme="gold"] {
    background: radial-gradient(circle at top, #3a3d3e 0%, var(--bg) 40%, #1a1b1c 100%);
}
* { box-sizing: border-box; }
/* Bug-Report: das "hidden"-Attribut hat dieselbe Selektor-Spezifitaet (0,1,0) wie eine einzelne
Klasse - eine spaetere Regel wie ".button-row { display: flex }" gewinnt sonst gegen die UA-Default-
Regel "[hidden] { display: none }" und das Element bleibt trotz hidden sichtbar (passiert bei jedem
Element, dessen Klasse selbst einen display-Wert setzt: .button-row/.stats-grid/... kombiniert mit
data-collapse-toggle o.ae.). Erzwingt hidden IMMER als Sieger, unabhaengig von der Klasse. */
[hidden] { display: none !important; }
body {
    margin: 0;
    font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    background: radial-gradient(circle at top, #10233d 0%, var(--bg) 40%, #020617 100%);
    color: var(--text);
    min-height: 100vh;
}
a { color: var(--accent); text-decoration: none; }
/* iOS-Bug-Report: Hamburger/Logo verschwanden im Hochformat unter der Kamera-/Notch-Aussparung,
im Querformat wurde der Inhalt links vom Kamera-Ausschnitt verdeckt - viewport-fit=cover
(views/header.php) allein reicht nicht, die App muss den sicheren Bereich selbst per env()
einrechnen. max() kombiniert mit dem bisherigen Padding, damit auf Geräten ohne Notch (env()
liefert dort 0) nichts schmaler wird als vorher. */
/* Nutzerwunsch: "am PC genutzt, gerade bei Buchhaltung darf es breiter sein ... am besten eigentlich
   volle width" - kein max-width-Deckel mehr, nur noch das Padding begrenzt den Abstand zum
   Viewport-Rand (einzige globale Breiten-Stellschraube, wirkt dank views/header.php/footer.php auf
   jede Seite konsistent - Komponenten wie .modal-box/.auth-card/.print-report haben bewusst eigene,
   unverändert schmale max-width-Werte und sind hiervon nicht betroffen). */
.app-shell { max-width: none; margin: 0 auto; padding: max(20px, env(safe-area-inset-top)) max(20px, env(safe-area-inset-right)) 20px max(20px, env(safe-area-inset-left)); }
/* Bug-Report: das Kopfzeilen-Menü (Konto-/Navigations-Dropdowns) verschwand hinter dem Seiteninhalt
   auf Seiten mit einem eigenen umrandeten Container direkt darunter (z. B. .task-desktop-page,
   Aufgabenverwaltung) - Ursache: backdrop-filter erzeugt einen eigenen Stacking-Context, .topbar
   selbst war aber position:static (z-index wirkt dort NICHT), wodurch der gesamte Dropdown-Inhalt
   trotz seines eigenen z-index als Einheit ganz normal in Dokumentreihenfolge einsortiert wurde -
   ein SPÄTERER, ebenfalls unpositionierter Container (z. B. .task-desktop-page) landete dadurch
   einfach optisch darüber. Fix: .topbar selbst bekommt jetzt position+z-index, wodurch ihr
   kompletter Inhalt (inkl. der bereits vorhandenen z-index:50-Dropdowns) als Einheit zuverlässig
   über JEDEM späteren Seiteninhalt liegt - unterhalb von .modal-overlay (100), oberhalb von allem
   anderen (Seiten-eigene fixe Elemente wie .task-fab/.task-bottom-nav bleiben bei 55/60). */
/* Nutzerwunsch: "Menü ganz oben soll fix sein, mit runterscrollt" (wie das bereits bestehende
.task-bottom-nav) - position:sticky statt fixed, damit .topbar in normalem Layout-Fluss bleibt
(kein kompensierendes Top-Padding an .content nötig) und trotzdem beim Scrollen oben kleben bleibt,
auf Mobile UND Desktop gleichermaßen. */
.topbar { position: sticky; top: max(12px, env(safe-area-inset-top)); z-index: 90; display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; background: var(--panel); border: 1px solid var(--border); border-radius: 18px; backdrop-filter: blur(18px); }
.brand { font-weight: 700; font-size: 1.1rem; display: inline-flex; align-items: center; }
.brand-logo { max-height: 36px; max-width: 220px; display: block; }
.person-search-results { display: flex; flex-direction: column; max-height: 220px; overflow-y: auto; }
/* Ergebnis-Einträge in Such-Dropdowns (Personen-/Konto-Suche, Buchungsassistent) erben sonst die
   pillenförmige .button-Form (border-radius: 999px) - bei vollbreiten Einträgen mit längerem Text
   (z. B. "1200 Bank") sieht das durch die starke Rundung an beiden Enden abgeschnitten aus. */
.person-search-result { text-align: left; border-radius: 10px; }
.person-search-widget { position: relative; z-index: 10; margin-bottom: 12px; }
.person-search-widget .person-search-results { position: absolute; z-index: 30; background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; padding: 6px; width: 100%; box-shadow: 0 12px 30px rgba(2, 6, 23, 0.35); }
.membership-filter-form { margin-bottom: 18px; padding: 16px; border: 1px solid var(--border); border-radius: 16px; background: rgba(var(--panel-tint-rgb), 0.35); }
/* Bug-Report: ".membership-filter-form" (die dominante Nicht-Popup-Formular-Klasse app-weit - u. a.
   jedes kleine "X anlegen"-Formular in settings.php/rechnungswesen_*.php) hatte nie display:flex/grid
   gesetzt, dadurch stapelten ihre Kind-Elemente (Label, abschliessender Button/button-row) als normale
   Blockboxen MIT NULL ABSTAND ineinander - per Messung (getBoundingClientRect) bestaetigt: jedes Feld
   beruehrte buendig das naechste, der "Speichern"/"Anlegen"-Button klebte direkt unter dem letzten Feld.
   Popups nutzen dagegen ".stack" (display:grid, siehe unten), das dieses Problem nie hatte - daher
   betraf der Bug ausschliesslich Nicht-Popup-Formulare. Bewusst ueber margin-bottom statt display:flex
   auf dem Formular selbst geloest: viele dieser Formulare tragen ein bereits bestehendes, bislang
   wirkungsloses (da der Elternkontext nie flex/grid war) "style='align-self: end'" auf ihrem
   Button-Wrapper - ein display:flex auf .membership-filter-form haette das plötzlich reaktiviert und
   Buttons ungewollt an den rechten Rand geschoben. */
.membership-filter-form > label,
.membership-filter-form > .membership-filter-grid {
    margin-bottom: 14px;
}
.membership-filter-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; align-items: start; }
/* Bug-Report: .person-search-widgets eigenes margin-bottom (fuer Abstand in Stack-Formularen
gedacht) verschob NUR dieses eine Feld innerhalb einer Filter-Grid-Zeile nach unten - hier gezielt
aufgehoben, andere Kontexte (z. B. "Neue Mitgliedschaft") behalten den Abstand. */
.membership-filter-grid > .person-search-widget { margin-bottom: 0; }
/* Dezentere/kleinere Variante der Filter-Leiste (Nutzerwunsch, gruppen.php: "sehr viel dezenter") -
   bewusst eine ZUSAETZLICHE Modifier-Klasse statt .membership-filter-form/-grid selbst zu aendern,
   die app-weit von vielen anderen Seiten in ihrer normalen Groesse genutzt werden. */
.filter-form-compact { padding: 10px 12px; }
.filter-form-compact .membership-filter-grid { gap: 8px; }
.filter-form-compact label,
.filter-form-compact .form-field { font-size: 0.8rem; gap: 4px; }
/* Wochentag-Unterueberschrift in der Gruppen-Uebersicht (gruppen.php, Nutzerwunsch: Sortierung nach
   Wochentag "im Idealfall mit Unterueberschriften") - eine volle, ueberspannende Tabellenzeile. */
.table-group-heading { background: rgba(var(--panel-tint-rgb), 0.35); font-weight: 600; color: var(--accent); font-size: 0.85rem; text-transform: uppercase; letter-spacing: .04em; }
.table-scroll { overflow-x: auto; }
.data-table { width: 100%; border-collapse: collapse; }
.data-table th, .data-table td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
.data-table th { color: var(--muted); font-weight: 600; font-size: 0.85rem; text-transform: uppercase; letter-spacing: .04em; }
/* Dichte Tabellenvariante fuers Buchhaltungsmodul (Nutzerwunsch: professionelles, tastaturfreundliches
   Power-User-Erlebnis wie Taxpool/BuchhaltungsButler statt der sonst ueberall spacious .data-table). */
.data-table.compact th, .data-table.compact td { padding: 4px 8px; font-size: 0.9rem; }
/* Umbrechende Tabelle fuer schmale Bildschirme (.data-table.stack-mobile).
   Eine vierspaltige Tabelle wird im Hochformat zu vier sehr schmalen Spalten, in denen jedes Wort
   einzeln umbricht (real gemeldet auf der Probestunden-Seite). Unter 760px wird deshalb jede Zeile
   zu einer eigenen Karte und jede Zelle zu einer vollbreiten Zeile darin. Die Kopfzeile entfaellt -
   sie liesse sich nur ueber data-label je Zelle retten, und die Inhalte sind hier selbsterklaerend.
   BEWUSST eine eigene Modifier-Klasse: .data-table wird app-weit von Dutzenden Tabellen benutzt,
   von denen viele (Buchhaltungs-Journal, Kontenbuch) gerade NICHT umbrechen sollen. */
@media (max-width: 760px) {
    .data-table.stack-mobile thead { display: none; }
    .data-table.stack-mobile tr {
        display: block;
        border: 1px solid var(--border);
        border-radius: 12px;
        padding: 10px 12px;
        margin-bottom: 10px;
        background: rgba(var(--panel-tint-rgb), 0.4);
    }
    .data-table.stack-mobile td {
        display: block;
        width: 100%;
        padding: 3px 0;
        border-bottom: 0;
    }
    /* Die Aktionsspalte bekommt eine sichtbare Trennung und darf umbrechen. */
    .data-table.stack-mobile td:last-child {
        margin-top: 8px;
        padding-top: 8px;
        border-top: 1px solid var(--border);
    }
    .data-table.stack-mobile td:last-child .button-row { flex-wrap: wrap; }
}
.sort-header-link { color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: 4px; }
.sort-header-link:hover { text-decoration: underline; }
/* Journal-Filterzeile (buchhaltung.php, Nutzerwunsch: "viel Dezenter ... in einer Reihe") - bewusst
   ohne die umrandete Box von .membership-filter-form, damit sie neben dem "Neue Buchung"-Popup nicht
   mit der Hauptaktion konkurriert. */
.accounting-journal-filter { display: flex; flex-wrap: wrap; align-items: end; gap: 10px 14px; margin-bottom: 16px; }
.accounting-journal-filter label { display: flex; flex-direction: column; gap: 3px; font-size: 0.75rem; color: var(--muted); }
.accounting-journal-filter input, .accounting-journal-filter select { font-size: 0.85rem; padding: 5px 8px; }
.accounting-journal-filter input[name="text"] { width: 220px; }
.accounting-journal-filter input[name="amount"] { width: 110px; }
.accounting-journal-filter input[type="date"] { width: 140px; }
.accounting-journal-filter .accounting-account-search { width: 260px; }
.accounting-journal-filter .accounting-account-search input[type="text"] { width: 100%; }

/* Generische, wiederverwendbare Variante von .accounting-journal-filter (Nutzerwunsch: "Filter
   dezenter in einer Zeile", vertraege.php) - bewusst ein eigener, modul-neutraler Klassenname statt
   des accounting-spezifischen oben (identische Regeln, aber nicht sinnvoll modulübergreifend über
   einen "accounting-*"-Namen zu teilen). */
.filter-bar-inline { display: flex; flex-wrap: wrap; align-items: end; gap: 10px 14px; margin-bottom: 16px; }
.filter-bar-inline label { display: flex; flex-direction: column; gap: 3px; font-size: 0.75rem; color: var(--muted); }
.filter-bar-inline input, .filter-bar-inline select { font-size: 0.85rem; padding: 5px 8px; }
.filter-bar-inline input[name="text"] { width: 220px; }

/* Belege zu Buchungen (Nutzerwunsch: "per Drag and Drop einen neuen hochladen") - erste
   Drag-and-Drop-Upload-Fläche dieser App, siehe initDragDropUpload() in assets/js/app.js. */
.accounting-document-dropzone { position: relative; border: 2px dashed var(--border); border-radius: 10px; padding: 18px 12px; text-align: center; cursor: pointer; transition: border-color 0.15s, background 0.15s; }
.accounting-document-dropzone:hover { border-color: var(--accent); }
.accounting-document-dropzone-active { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.accounting-document-dropzone input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.accounting-document-booking-diff-error { color: #ef4444; }
.accounting-journal-custom-range { display: flex; flex-wrap: wrap; gap: 10px 14px; }
/* Journal-Tabelle noch dichter als .data-table.compact (Nutzerwunsch: "Allgemein kann die Schrift in
   der Tabelle kleiner sein, damit man mehr sieht") - eigene Klasse statt .compact selbst zu ändern,
   damit andere Buchhaltungs-Tabellen (Kontenrahmen, Anlagen, ...) unverändert bleiben. */
.data-table.compact.accounting-journal-table th, .data-table.compact.accounting-journal-table td { font-size: 0.78rem; padding: 3px 6px; }
.accounting-journal-table .nowrap-cell { white-space: nowrap; }
.accounting-journal-table tbody tr[data-posting-row]:hover { background: rgba(var(--panel-tint-rgb), 0.35); }

/* Kontennachweis (Nutzerwunsch: GuV/EÜR "hübsch" wie ein DATEV/Taxpool-Export - je amtlicher Zeile
   die einzelnen einfließenden Konten aufführen statt nur die nackte Summe, app/accounting_report_
   helpers.php get_guv_report()/get_euer_report()). Bewusst NICHT nur auf .print-report beschränkt
   (anders als .print-sum-row) - soll auch in der normalen App-Ansicht direkt lesbar sein. */
.kontennachweis-table .kontennachweis-line-header td { font-weight: 700; padding-top: 14px; border-bottom: none; }
.kontennachweis-table .kontennachweis-line-account td { border-bottom: none; }
.kontennachweis-table .kontennachweis-line-account td:nth-child(2) { padding-left: 24px; color: var(--muted); }
/* Nutzerwunsch: "Summe"-Bezeichnungen der Zwischensummen entfallen (Kontext macht bereits klar, dass
   es die Summe ist) + "nur den Border-Top auf Zellenebene machen, nicht die ganze Zeile" - border-top
   sitzt deshalb NUR noch auf der Betragszelle, nicht auf jeder (teils jetzt leeren) Zelle der Zeile. */
.kontennachweis-table .kontennachweis-line-subtotal td { font-weight: 700; }
.kontennachweis-table .kontennachweis-line-subtotal td.num { border-top: 1px solid var(--border); }
/* Untergliederung der GuV-Position 8 "Sonstige betriebliche Aufwendungen" (Nutzerwunsch: "wie in
   der alten Buchhaltung ausführlicher" - siehe get_guv_position_8_subgroups()) - eine zusätzliche
   Verschachtelungsebene zwischen der amtlichen Kennziffer-Kopfzeile und den einzelnen Konten. */
.kontennachweis-table .kontennachweis-subgroup-header td { font-weight: 600; padding-top: 10px; padding-left: 12px; border-bottom: none; }
.kontennachweis-table .kontennachweis-line-account-nested td:nth-child(2) { padding-left: 36px; }
.kontennachweis-table .kontennachweis-subgroup-subtotal td { font-weight: 600; }
.kontennachweis-table .kontennachweis-subgroup-subtotal td.num { border-top: 1px dashed var(--border); }
/* Bilanzgliederung nach § 266 HGB (Nutzerwunsch: "ähnlich wie in der GuV auch die Bilanzen
   Kategorien in Aktiva und Passiva haben") - eine zusätzliche Sektions-Ebene (A./B./C.) ÜBER der
   bereits bestehenden .kontennachweis-line-header-Ebene (hier für die römische I./II./III.-
   Unterebene wiederverwendet), Konten selbst rutschen dadurch eine Stufe tiefer als bei GuV/EÜR -
   siehe app/accounting_report_helpers.php accounting_build_bilanz_lines(). */
.kontennachweis-table .kontennachweis-section-header td { font-weight: 700; font-size: 1.02em; padding-top: 20px; border-bottom: none; }
.kontennachweis-table .kontennachweis-section-subtotal td { font-weight: 700; }
.kontennachweis-table .kontennachweis-section-subtotal td.num { border-top: 2px solid var(--border); }
/* Nutzerwunsch (Auswertungen): "Zahlen in der rechten Spalte alle rechtsbündig" - gilt für alle
   views/auswertungen_report_content.php-Tabellen (Journal/Summen&Salden/Kontenblatt/Kontennachweis/
   Bilanz/BWA), nicht nur den Druck-/PDF-Kontext (dort zusätzlich `white-space: nowrap`, siehe
   app/accounting_report_pdf_helpers.php - verhindert, dass das Eurozeichen umbricht). */
th.num, td.num { text-align: right; }
.accounting-journal-table .account-cell-name { display: block; font-size: 0.85em; color: var(--muted); }
/* Zahlungskonten-Kontoauszug (buchhaltung_zahlungskonten.php, Nutzerwunsch: "negative Zahlen rot,
   positive Zahlen normal"). */
.accounting-journal-table .amount-negative { color: var(--danger); }
.accounting-journal-table .journal-lock-cell { text-align: center; }
/* Benachrichtigungs-Kanäle in "Mein Profil" (Nutzerwunsch: "Header sind die 3 Optionen, erste Reihe
besteht dann aus den 3 Checkboxes, jeweils zentriert darunter") - Checkbox-Zellen zentriert statt
der App-weit üblichen Linksbündigkeit. */
.notification-channel-table th, .notification-channel-table td { text-align: center; }
.accounting-journal-table td { vertical-align: middle; }
/* Ersetzt die frühere "Journal"-Überschrift (Nutzerwunsch: irreführend, da hier auch noch nicht
   festgeschriebene Stapelbuchungen stehen). Feste Mindesthöhe + visibility (statt hidden/display:none)
   hält den Platz IMMER reserviert - die Tabelle darunter rutscht beim Auswählen von Zeilen nicht nach
   unten, die Gruppenaktionen blenden nur innerhalb dieser Zeile ein/aus. */
.accounting-journal-bulk-bar { display: flex; align-items: center; gap: 10px; min-height: 34px; margin-bottom: 8px; visibility: hidden; }
.accounting-journal-bulk-bar.is-active { visibility: visible; }
/* Zeilenfarben im Journal (Nutzerwunsch: "Zeilen sollen farblich etwas abgehoben werden") - bewusst
   drei unabhängige, kombinierbare Zustände statt nur einer Klasse: stornierte Buchungen (dunklere/
   gedämpfte Schrift, sie sind durch ihre Gegenbuchung ohnehin nicht mehr relevant), noch nicht
   festgeschriebene Buchungen (minimal hellerer Hintergrund, "Stapel") und unvollständige/ungültige
   Buchungen (dezent roter Hintergrund - gewinnt optisch, da am wichtigsten für die Verwaltung). */
.accounting-journal-table tr.posting-row-open td { background: rgba(255, 255, 255, 0.035); }
.accounting-journal-table tr.posting-row-voided td { color: var(--muted); }
.accounting-journal-table tr.posting-row-invalid td { background: rgba(251, 113, 133, 0.1); }
/* Bug-Report: Splitbuchungen (mehrere Journalzeilen mit gemeinsamer split_group_id, z. B. aus einem
   DATEV-Import) waren nicht als zusammengehörig erkennbar - "Teil x/y"-Badge + ein farbiger Rahmen
   links an der ersten Spalte verbindet sie optisch, OHNE die Zeilen aufklappbar/kollabiert zu machen
   (Nutzerwunsch: "wäre nervig, wenn man immer aufklappen müsste") - jede Zeile bleibt einzeln
   auswähl-/bearbeitbar wie bisher. */
.accounting-journal-table tr.posting-row-split td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
/* "Belege neu durchnummerieren" (Nutzerwunsch, Vorbild-Screenshot: geänderte Zeilen farblich markiert) -
   generische Tabellenzeilen-Markierung, nicht auf .accounting-journal-table beschränkt. */
.data-table tr.beleg-renumber-changed td { background: rgba(34, 197, 94, 0.12); }
/* "Geplante Buchungen" (Nutzerwunsch: AfA/Saldenvortrag/unfertige Rechnungen als Vorschau im Journal
   zeigen, siehe app/accounting_planned_posting_helpers.php) - existieren nie real in accounting_
   postings, deshalb deutlich gedämpfter als selbst eine offene Stapel-Zeile (kursiv statt nur
   heller Hintergrund) und ohne den linken Rahmen der Split-Kennzeichnung. */
.accounting-journal-table tr.posting-row-planned td { color: var(--muted); font-style: italic; }
.split-badge { display: inline-flex; align-items: center; gap: 4px; margin-left: 6px; padding: 1px 6px; border-radius: 999px; background: rgba(56, 189, 248, 0.16); color: var(--accent); font-size: 0.72rem; font-weight: 600; white-space: nowrap; }
/* Beleg-Massenzuordnung (buchhaltung_belege_massenzuordnung.php) - eine Karte je Datei aus der
   hochgeladenen ZIP, Kopfzeile mit Dateiname/Trefferzahl/Überspringen-Toggle, 0-Treffer-Zeile gedämpft.
   also-matched-by-files ist seit der Mehrfach-Beleg-Ausbaustufe (Nutzerwunsch: "eine Buchung muss auch
   mehrere Belege bekommen können") bewusst neutral/informativ statt Warnfarbe - kein Konflikt mehr. */
.beleg-matching-file-group { margin-bottom: 16px; }
.beleg-matching-file-header { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.beleg-matching-no-match { margin: 0; }
.beleg-matching-multi-assign-hint { color: var(--muted); font-size: 0.82rem; }
/* Journal-Belegsymbol (buchhaltung.php) - kleine Trefferzahl-Ecke, wenn eine Buchung mehr als einen
   Beleg hat (Mehrfach-Beleg-Ausbaustufe). */
.beleg-count-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 15px; height: 15px; margin-left: 2px; padding: 0 3px; border-radius: 999px; background: var(--accent); color: #04121f; font-size: 0.62rem; font-weight: 700; vertical-align: super; }
/* Bearbeiten-Symbol im Journal (Nutzerwunsch: "Symbol zum Editieren der Buchung" links vom
   Schloss-Symbol) - bewusst kein .button (zu groß/aufdringlich neben dem reinen Anzeige-Icon
   daneben), reines minimalistisches Icon mit Klickfläche. */
.icon-link-button { background: none; border: none; padding: 2px 5px; margin-right: 2px; cursor: pointer; color: inherit; font-size: inherit; }
.icon-link-button:hover { color: var(--accent); }
/* Jahresauswahl/Journal-Stapel-Auswahl/Ungueltig-Checkbox/"Neue Buchung" sollen auf Desktop immer in
   einer Zeile bleiben und dabei deutlich kompakter als die Standard-.button-row-Elemente sein
   (Nutzerwunsch: "immer noch viel zu groß und mit Zeilenumbruch ... kleiner und nebeneinander") - der
   Selektor ist bewusst ".button-row.accounting-journal-header-actions" (statt nur die einzelne Klasse),
   da .button-row selbst WEITER UNTEN in dieser Datei steht und mit gleicher Spezifität sein
   `flex-wrap: wrap` sonst gewinnen würde - genau das war der eigentliche Grund, warum die Zeile trotz
   einer bereits vorhandenen früheren nowrap-Regel weiterhin umgebrochen ist. */
.button-row.accounting-journal-header-actions { flex-wrap: nowrap; gap: 8px; }
.accounting-journal-header-actions select { font-size: 0.8rem; padding: 4px 24px 4px 6px; width: auto; }
.accounting-journal-header-actions label.inline-checkbox { font-size: 0.85rem; white-space: nowrap; }
@media (max-width: 760px) {
    .button-row.accounting-journal-header-actions { flex-wrap: wrap; }
}
/* Kassenbuch (buchhaltung_kasse.php, Nutzerwunsch: "Auswahl Einnahme/Ausgabe am besten kein Dropdown,
   sondern horizontales 2-fach-Selekt" / Gegenkonto "am besten ein expanded dropdown, wo man nur ein
   Feld selektieren kann, wenn es bis zu 7 Optionen gibt") - Radiobuttons visuell als anklickbare
   Kacheln statt <select>, hier erstmalig in der Codebase eingeführt (wiederverwendbar für jede
   künftige "wenige Optionen, EINE Auswahl"-Stelle). */
.choice-tiles { display: flex; flex-wrap: wrap; gap: 8px; }
.choice-tile { display: flex; align-items: center; gap: 6px; padding: 8px 14px; border: 1px solid var(--border); border-radius: 8px; cursor: pointer; font-size: 0.9rem; }
.choice-tile input[type="radio"] { margin: 0; }
.choice-tile:has(input:checked) { border-color: var(--accent); font-weight: 600; }
.choice-tiles-label { font-size: 0.75rem; color: var(--muted); margin: 0 0 6px; }
/* Gegenkonto-Kacheln (Nutzerwunsch: "Umbruch nach Kontonummer, Bezeichnung heller aber kleiner ...
   auf mobilen Geräten besser die Optionen untereinander") - eigener Modifier statt am generischen
   .choice-tiles zu ändern, damit die Einnahme/Ausgabe-Kacheln (immer nur 2, sollen auch auf Mobile
   nebeneinander bleiben) unangetastet bleiben. */
.choice-tiles-accounts .choice-tile { align-items: flex-start; }
.choice-tile-text { display: flex; flex-direction: column; line-height: 1.3; }
.choice-tile-number { font-size: 0.9rem; }
.choice-tile-name { font-size: 0.72rem; color: var(--muted); }
@media (max-width: 600px) {
    .choice-tiles-accounts { flex-direction: column; }
    .choice-tiles-accounts .choice-tile { width: 100%; }
}
.pos-balance { font-size: 1.4rem; margin-bottom: 16px; }
.pos-balance strong { font-size: 1.6rem; }
.pos-form { max-width: 480px; }
.pos-amount-wrap { position: relative; display: block; }
.pos-amount-wrap .pos-amount-prefix { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--danger); font-weight: 700; display: none; pointer-events: none; }
.pos-amount-wrap.pos-amount-expense .pos-amount-prefix { display: block; }
.pos-amount-wrap.pos-amount-expense input { color: var(--danger); padding-left: 22px; }
/* Positionszeilen-Erfassung Angebote/Rechnungen (views/rechnungswesen_item_rows.php) - kompakte
   Inline-Inputs statt der App-weiten .stack-Volle-Breite-Felder, da mehrere pro Zeile nebeneinander
   stehen. Beschreibung wächst automatisch mit ihrem Inhalt (initInvoiceItemsForm() in app.js).
   Löschen sitzt direkt neben dem Mengenfeld statt eigener letzter Spalte - dadurch ist "Summe" die
   letzte Spalte und steht bündig über den rechtsbündigen Summen-Zeilen (.invoice-items-summary)
   darunter. table-layout:fixed hält alle Spalten auf ihren zugewiesenen Anteilen fest.
   Bug-Report: die Breiten waren bisher über :nth-child(N) (Spalten-POSITION) gesetzt - als die
   Buchungskonto-Spalte (nur bei Rechnungen, nicht Angeboten) eingeführt wurde, verschob das "Summe"
   auf eine neue Position, ohne dass die alten nth-child-Regeln nachgezogen wurden: zwei Spalten
   teilten sich dieselbe Breitenregel, der Rest lief ins Leere - sichtbar als überlappende
   Kopfzeilen-Beschriftung ("BUCHUNGSKSUMTOME"). Jetzt über feste Klassen je Spalte (unabhängig von
   Position/Spaltenanzahl), siehe rechnungswesen_item_rows.php. Die Steuer-Spalte ist bei Rechnungen
   nur noch eine schmale, reine Anzeige (kein Dropdown mehr, siehe .invoice-item-tax-display) und
   braucht deshalb deutlich weniger Platz als vorher. */
/* Nutzerwunsch (Nachtrag): "Felder Text größer machen, dann die Table Zeilen fixen" - deutlich
   größere Schrift/mehr Zeilenhöhe als die anfängliche, sehr kompakte Fassung (der Rest der Seite
   ist bewusst großzügig, die Tabelle wirkte dagegen gequetscht). vertical-align:middle statt des
   .data-table-Standards "top" behebt dabei den zweiten gemeldeten Effekt ("Striche sind komisch"):
   bei einer mehrzeilig gewachsenen Beschreibung (siehe autoGrowDescription()) hingen die kurzen
   Eingabefelder der Nachbarspalten sonst oben im spürbar höheren Zeilenrahmen, mit auffällig viel
   Leerraum bis zur eigentlichen unteren Trennlinie der Zeile - mittig ausgerichtet verschwindet
   dieser Effekt. */
.invoice-items-table { table-layout: fixed; width: 100%; }
.invoice-items-table th, .invoice-items-table td { padding: 10px 10px; vertical-align: middle; }
.invoice-items-table input, .invoice-items-table select, .invoice-items-table textarea { font-size: 1rem; padding: 8px 10px; width: 100%; }
/* Bug-Report: die untere Trennlinie einer Zeile wirkte "komisch"/versetzt, sobald die Beschreibung
   mehrzeilig gewachsen war - resize:vertical erlaubt ein manuelles Ziehen der Textarea PER Maus,
   unabhängig von autoGrowDescription()s eigener, laufend synchronisierter Höhenberechnung; die
   Tabellenzeile/-border reflowt nach einem manuellen Ziehen nicht zuverlässig sofort mit, wodurch die
   Textarea sichtbar über die eigentliche untere Zeilenlinie hinausragen konnte. autoGrowDescription()
   hält die Höhe bereits vollständig automatisch nach (jeder input-Tastenanschlag) - der native
   Resize-Griff ist dadurch redundant UND die Fehlerquelle, deshalb deaktiviert. */
.invoice-items-table textarea.invoice-item-description { resize: none; min-height: 40px; overflow: hidden; }
.invoice-item-col-title { width: 18%; }
.invoice-item-col-description { width: 27%; }
.invoice-item-col-quantity { width: 120px; }
.invoice-item-col-price { width: 130px; }
.invoice-item-col-account { width: 240px; }
.invoice-item-col-total { width: 120px; }
.invoice-item-quantity-cell { display: flex; align-items: center; gap: 6px; }
.invoice-item-quantity-cell .invoice-item-quantity { min-width: 0; flex: 1; }
.invoice-item-quantity-cell .invoice-item-remove { flex-shrink: 0; }
/* Nutzerwunsch: Steuer- UND Summen-Spalte rechtsbündig (Kopf- UND Inhaltszellen - beide Klassen
   sitzen bereits auf th UND td, siehe rechnungswesen_item_rows.php). */
.invoice-item-tax-col { width: 140px; text-align: right; }
.invoice-item-tax-key { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.invoice-item-tax-display { padding: 8px 0; font-size: 1rem; }
/* Bug-Report: Bestandsrechnungen ohne Buchungskonto hatten teils einen manuell gesetzten
   Steuerschlüssel - eine reine, konto-only abgeleitete Anzeige hätte diesen beim nächsten Speichern
   verloren. Select UND Anzeige stehen deshalb bei Rechnungen gleichzeitig im Markup (rechnungswesen_
   item_rows.php); .has-account (initInvoiceItemsForm() in app.js) schaltet zwischen beiden um: kein
   Konto -> Select sichtbar/wählbar (Default, deckt auch Angebote ab - dort existiert .has-account nie),
   Konto gewählt -> Anzeige sichtbar, Select verborgen. */
.invoice-item-tax-col .invoice-item-tax-key { display: inline-block; width: 100%; }
.invoice-item-tax-col .invoice-item-tax-display { display: none; }
.invoice-item-row.has-account .invoice-item-tax-col .invoice-item-tax-key { display: none; }
.invoice-item-row.has-account .invoice-item-tax-col .invoice-item-tax-display { display: inline-block; }
.invoice-item-col-total { text-align: right; }
.invoice-item-line-total { text-align: right; }
.invoice-items-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
/* Produkt-Positionen-Editor (rechnungswesen_produkte.php, views/rechnungswesen_product_positions_
   form.php) - Nutzerwunsch: pro Position zwei Zeilen statt einer gequetschten Einzeiler-Zeile.
   Bewusst CSS-Grid statt <table> (Div-Zeilen statt <tr>/<td>) - eine Grid-Zelle kann anders als eine
   Tabellenzelle über mehrere Grid-Zeilen spannen (Löschen-Icon vertikal zentriert über beide Zeilen,
   Buchungskonto spannt unter Preis+Modus), ohne die HTML-Tabellensemantik zu verbiegen. Solange nur
   eine Position existiert, liefert das Produkt selbst Titel/Beschreibung - Titel-/Beschreibungsfeld
   der Position bleiben dann verborgen (einfacher Einzeiler: Preis/Modus/Buchungskonto/Löschen), dafür
   zeigt der Kopf in diesem Fall zusätzlich ein "Buchungskonto"-Label. Ab zwei Positionen
   (initProductPositionsForm(), assets/js/app.js, schaltet .has-multiple frei) wird auf das
   Zwei-Zeilen-Layout umgeschaltet: Titel (Zeile 1) + Zusatzbeschreibung (Zeile 2) in Spalte 1, Preis/
   Modus in Zeile 1 der Spalten 2-3, Buchungskonto OHNE eigene Überschrift darunter (Zeile 2, spannt
   Spalten 2-3). */
.product-positions-header, .product-position-row {
    display: grid;
    grid-template-columns: 110px 110px 1fr 40px;
    grid-template-rows: auto;
    column-gap: 10px;
    row-gap: 6px;
    align-items: center;
}
.product-positions-header {
    padding: 0 0 8px;
    border-bottom: 1px solid var(--border);
    color: var(--muted);
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
}
.product-position-row { padding: 12px 0; border-bottom: 1px solid var(--border); }
.product-position-row input, .product-position-row select { font-size: 1rem; padding: 8px 10px; width: 100%; }
.product-position-title-cell, .product-position-description-cell { display: none; }
.product-position-price-cell { grid-column: 1; grid-row: 1; }
.product-position-mode-cell { grid-column: 2; grid-row: 1; }
.product-position-account-cell { grid-column: 3; grid-row: 1; }
.product-position-remove-cell { grid-column: 4; grid-row: 1; display: flex; align-items: center; justify-content: center; }
.product-positions-header .product-position-title-cell { display: none; flex-direction: column; gap: 2px; }
.product-positions-header .product-position-title-cell span:last-child { text-transform: none; font-weight: 400; font-size: 0.78rem; }

.product-positions-editor.has-multiple .product-positions-header,
.product-positions-editor.has-multiple .product-position-row {
    grid-template-columns: minmax(160px, 1.3fr) 110px 110px 40px;
}
.product-positions-editor.has-multiple .product-position-title-cell { display: block; grid-column: 1; grid-row: 1; }
.product-positions-editor.has-multiple .product-position-description-cell { display: block; grid-column: 1; grid-row: 2; }
.product-positions-editor.has-multiple .product-position-price-cell { grid-column: 2; grid-row: 1; }
.product-positions-editor.has-multiple .product-position-mode-cell { grid-column: 3; grid-row: 1; }
.product-positions-editor.has-multiple .product-position-account-cell { grid-column: 2 / span 2; grid-row: 2; }
.product-positions-editor.has-multiple .product-position-remove-cell { grid-column: 4; grid-row: 1 / span 2; }
.product-positions-editor.has-multiple .product-positions-header .product-position-title-cell { display: flex; }
.product-positions-editor.has-multiple .product-positions-header .product-position-account-cell { display: none; }
.invoice-items-summary { display: flex; flex-direction: column; gap: 4px; align-items: flex-end; margin-top: 12px; max-width: 320px; margin-left: auto; }
.invoice-items-summary div { display: flex; justify-content: space-between; width: 100%; gap: 16px; }
/* Nutzerwunsch: 2-spaltiger Kopfbereich (rechnung_bearbeiten.php) - Kunde+Adresse links, Datum/
   Fälligkeit rechts, kompakter als die vorherige durchgängige Ein-Spalten-Stapelung. Datum/
   Fälligkeit stehen dabei (Nachtrag) untereinander statt nebeneinander - bei zwei kurzen
   Datumsfeldern nebeneinander wirkte die rechte Spalte im Vergleich zur linken (langes Kunde-Select
   + Adresstext) unausgewogen breit/leer. */
.invoice-header-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start; }
.invoice-header-date-row { display: flex; flex-direction: column; gap: 12px; }
.invoice-client-address-preview { margin-top: 8px; }
@media (max-width: 760px) {
    .invoice-header-grid { grid-template-columns: 1fr; }
}
/* Statusänderung/"Rechnung buchen"/Zahlungen als kompakte Icon-Aktionen oben rechts statt eigener,
   textlastiger Abschnitte weiter unten (Nutzerwunsch). */
.invoice-header-actions { align-items: center; }
.invoice-header-status-form { display: inline-flex; align-items: center; gap: 6px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 999px; padding: 0 12px 0 14px; height: 34px; }
.invoice-header-status-form i { color: var(--muted); font-size: 0.8rem; }
/* Bug-Report: die aufgeklappte Options-Liste war fast unlesbar (helle Schrift auf hellem/weißem
   Popup-Hintergrund) - `background: transparent` auf dem <select> selbst entzog dem Browser den
   nötigen Hinweis, das native Dropdown im dunklen Farbschema zu rendern (der App-weite `:root {
   color-scheme: dark }` allein reicht dafür nicht, sobald ein Element seine eigene background-color
   explizit auf "transparent" setzt statt eine echte Farbe zu nennen - andere <select>s der App
   betrifft das nicht, die erben die normale, dunkle background-color aus der Basis-Regel oben). Jetzt
   dieselbe Panel-Farbe wie der umschließende Pill-Rahmen - im geschlossenen Zustand optisch identisch
   nahtlos, macht aber das Popup wieder korrekt dunkel/lesbar. */
.invoice-header-status-form select { background: var(--panel-2); border: 0; color: var(--text); font-size: 0.85rem; padding: 0; width: auto; }
.invoice-header-status-form select option { background: var(--panel-2); color: var(--text); }
/* .small-button.icon-button (statt nur .icon-button) für ausreichend Spezifität gegen .small-button
   (gleiche einzelne Klassen-Spezifität, würde sonst je nach Regel-Reihenfolge verlieren) - feste
   Breite/Höhe statt symmetrischem Padding, damit "Filtern" (fa-arrows-rotate) und "Zurücksetzen"
   (fa-xmark) trotz unterschiedlich breiter Icon-Glyphen exakt gleich groß wirken. */
.small-button.icon-button { padding: 0; width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; }
.accounting-account-search { position: relative; z-index: 10; display: block; }
.accounting-account-search [data-account-search-list] { position: absolute; z-index: 30; top: 100%; left: 0; right: 0; max-height: 260px; overflow-y: auto; background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; padding: 6px; margin-top: 4px; }
.split-line { position: relative; padding-bottom: 4px; border-bottom: 1px dashed var(--border); margin-bottom: 8px; }
.nav-links { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }
.nav-mobile-toggle { display: none; background: transparent; border: none; padding: 6px; color: var(--text); font-size: 1.15rem; cursor: pointer; }
.nav-back-toggle { display: none; background: transparent; border: none; padding: 6px; color: var(--text); font-size: 1.15rem; cursor: pointer; }
/* Bug-Report (Desktop): initMobileNavDrilldown() (assets/js/app.js) erzeugt diesen Button
IMMER (auch am PC), nur seine mobile Sichtbarkeits-/Positionierungsregel lag bisher ausschließlich
in der @media(max-width:760px)-Sektion - auf breiten Bildschirmen fiel er dadurch auf die globale
".button, button"-Regel zurück (großer Akzent-Pill) und tauchte als "seltsamer Zurück-Button"
mitten in der Desktop-Navigation auf. Jetzt app-weit standardmäßig ausgeblendet, die mobile Sektion
schaltet ihn per .nav-drilled-Zustand gezielt wieder sichtbar. */
.nav-drill-back { display: none; }
.content { padding-top: 20px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px; }
.card { background: var(--panel); border: 1px solid var(--border); border-radius: 22px; padding: 22px; box-shadow: 0 20px 50px rgba(2, 6, 23, 0.25); }
.hero { display: flex; justify-content: space-between; gap: 16px; align-items: center; }
.eyebrow { text-transform: uppercase; letter-spacing: .24em; color: var(--accent); font-size: 0.75rem; margin-bottom: 8px; }
h1, h2, h3, h4, p { margin-top: 0; }
.button, button { border: none; border-radius: 999px; padding: 12px 18px; font-weight: 600; cursor: pointer; background: linear-gradient(90deg, var(--accent), var(--accent-2)); color: white; }
.button.secondary { background: transparent; border: 1px solid var(--border); color: var(--text); }
/* .button.danger wurde an drei Stellen bereits benutzt (Widerrufen/Entfernen/Loeschen), hatte aber
   NIE eine Regel - die Knoepfe sahen dadurch aus wie ein normaler Bestaetigungsknopf. Genau bei
   einer Loeschbestaetigung untergraebt das den Zweck. */
.button.danger { background: var(--danger); color: #1c1017; }
.button.secondary.substitute-active { background: rgba(251, 191, 36, 0.25); border-color: #fbbf24; color: #fbbf24; }
.button-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.collapse-panel { margin-top: 8px; }
.stats-grid, .module-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; }
.stat-card strong { display: block; font-size: 2rem; margin-bottom: 4px; }
/* Nutzerwunsch (Zahlungskonten, "täglich hier gearbeitet"): "Abstand zwischen den Widgets Anfangssaldo/
   Endsaldo zu der Tabelle darunter ist etwas zu klein" - eigene Klasse statt die generische .stats-grid
   app-weit zu verändern (an vielen anderen Stellen genutzt, z. B. Dashboard). */
.zahlungskonten-stats-grid { margin-bottom: 24px; }
.module-card { display: block; }
/* Dashboard (Nutzerwunsch: "professionelles, grafisch ansprechendes" modul-bewusstes Startseiten-
   Dashboard, siehe index.php/app/dashboard_helpers.php) - eigenes, etwas reichhaltigeres Kachel-
   Vokabular als .stats-grid/.module-card (Icon-Kopfzeile, mehrere Kennzahlen-Zeilen, optionale
   Sparkline je Karte), ERGÄNZT diese beiden statt sie zu ersetzen (Schnellzugriff-Kachelreihe nutzt
   weiterhin .module-grid/.module-card unverändert). */
.dashboard-greeting { margin-bottom: 4px; }
.dashboard-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin-bottom: 20px; }
.dashboard-card { transition: transform 0.15s ease, box-shadow 0.15s ease; }
.dashboard-card:hover { transform: translateY(-3px); box-shadow: 0 26px 60px rgba(2, 6, 23, 0.35); }
.dashboard-card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; font-weight: 600; }
.dashboard-card-header i:first-child { color: var(--accent); margin-right: 8px; }
.dashboard-card-link { color: var(--muted); font-size: 0.85rem; flex-shrink: 0; }
.dashboard-card-link:hover { color: var(--accent); }
.dashboard-stat-value { display: block; font-size: 2rem; font-weight: 700; line-height: 1.15; margin-bottom: 2px; }
.dashboard-stat-trio { display: flex; gap: 8px; }
.dashboard-stat-trio-item { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 10px 4px; border-radius: 14px; text-align: center; transition: background 0.15s ease; }
.dashboard-stat-trio-item:hover { background: rgba(var(--panel-tint-rgb), 0.5); }
.dashboard-stat-trio-item .dashboard-stat-value { font-size: 1.5rem; }
.dashboard-stat-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 9px 0; margin-top: 4px; border-top: 1px solid var(--border); font-size: 0.88rem; color: var(--text); }
.dashboard-stat-row-alert { color: #fecdd3; }
.dashboard-stat-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 24px; padding: 2px 9px; border-radius: 999px; background: rgba(56, 189, 248, 0.18); color: var(--accent); font-weight: 600; font-size: 0.82rem; }
.dashboard-stat-badge-alert { background: rgba(251, 113, 133, 0.18); color: #fecdd3; }
.dashboard-sparkline { margin: 10px 0 6px; }
.dashboard-sparkline-svg { display: block; width: 100%; height: 32px; }
.dashboard-empty-state { color: var(--muted); font-size: 0.9rem; }
.dashboard-quick-links { margin-bottom: 20px; }
.dashboard-activity-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.dashboard-activity-list li { display: flex; align-items: center; gap: 10px; font-size: 0.9rem; padding: 10px 0; border-bottom: 1px dashed var(--border); }
.dashboard-activity-list li:last-child { border-bottom: none; padding-bottom: 0; }
.dashboard-activity-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dashboard-activity-time { font-size: 0.78rem; white-space: nowrap; }
@media (max-width: 760px) {
    .dashboard-stat-trio { gap: 4px; }
}
.auth-card { width: 100%; max-width: 480px; margin: 24px auto 0; }
.mfa-code-input { font-size: 1.4rem; letter-spacing: 0.3em; text-align: center; }
.stack { display: grid; gap: 12px; }
/* Trennlinie (Nutzer-Bug-Report 2026-08-18: "wo kommt der Punkt her?") - ohne eigene Regel behält
<hr> die Browser-Vorgabe margin-inline:auto. In einem Grid wie .stack verhindern die auto-Ränder das
Strecken, das Element schrumpft auf Breite 0 und erscheint als winziger zentrierter Punkt. */
hr { width: 100%; margin: 2px 0; border: none; border-top: 1px solid var(--border); }
/* Kleingedrucktes zum SEPA-Mandat im öffentlichen Aufnahme-Formular - dezent abgesetzt, damit es
als rechtlicher Hinweis erkennbar ist, ohne den Zahlungsschritt zu dominieren. */
.sepa-mandate-info { padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; font-size: 0.9rem; }
.sepa-mandate-info > strong { display: block; margin-bottom: 6px; }
.sepa-mandate-info .membership-list { margin-bottom: 0; }
/* Beschriftung links, Wert rechtsbündig - ohne das lagen lange Werte ("wird mitgeteilt") auf der
Beschriftung, weil .membership-row als Flexbox beide Kinder nach ihrem Inhalt bemisst. */
.sepa-mandate-info .membership-row { align-items: flex-start; gap: 16px; }
.sepa-mandate-info .membership-row > span:last-child { margin-left: auto; text-align: right; }
.small-stack { gap: 8px; }
/* Zwei zusammengehoerige Felder (z. B. Start-/Enddatum, Start-/Enduhrzeit, Terminart/Wochentag) in
   einer Zeile statt untereinander (Nutzerwunsch: mehr Platz sparen) - als EIN Kind-Element von
   .stack eingesetzt, bricht auf schmalen Bildschirmen bewusst wieder auf eine Spalte um. */
.form-row-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 760px) {
    .form-row-pair { grid-template-columns: 1fr; }
}
/* Formular mit mehreren Teilüberschriften (z. B. kunde_bearbeiten.php: Allgemein/Adresse/Kontakt/
   Steuer) - .form-grid-sections macht das umschliessende Element (i. d. R. ein <fieldset>) selbst zu
   einem .stack-artigen Grid, damit zwischen JEDEM Kind (Label, .form-row-pair, h3, Absatz, Button-
   Row) ein einheitlicher Grund-Abstand entsteht (ein <fieldset> ist sonst kein Grid, Kinder stapeln
   sich ohne jeden Abstand - genau der gemeldete "Speichern klebt am Feld"-Bug). .form-subsection-
   heading bekommt zusätzlich zum Grid-Gap noch etwas mehr margin-top, damit eine neue Teilüberschrift
   optisch klarer von der vorherigen Feldgruppe abgesetzt ist als zwei Felder derselben Gruppe
   voneinander (margins addieren sich zum Grid-Gap, da CSS-Grid-Items keine Margin-Collapsing
   betreiben). */
.form-grid-sections { display: grid; gap: 16px; }
.form-subsection-heading { margin-top: 8px; }
label { display: grid; gap: 8px; color: var(--muted); font-size: 0.95rem; }
input, textarea, select { width: 100%; border: 1px solid var(--border); border-radius: 12px; padding: 12px 14px; background: var(--panel-2); color: var(--text); font-size: 0.95rem; font-family: inherit; }

/* Datums-/Uhrzeitfelder liefen auf dem Handy über den rechten Rand ihres Popups hinaus (2026-08-16,
Nutzer-Screenshot "Termin verlegen": Datum/Startzeit/Endzeit deutlich breiter als die Auswahl- und
Textfelder daneben). Ursache ist NICHT die Breitenangabe oben - die stimmt und box-sizing ist global
auf border-box gesetzt. Diese Felder stehen fast immer in einem <label>, und label ist app-weit ein
Grid (siehe die Kommentare zu .inline-checkbox weiter unten); ein Grid-Element hat implizit
min-width: auto, also mindestens seine min-content-Breite. Bei einem nativen Datums-/Zeit-Widget ist
das die feste Breite des Systemfelds - unter iOS spürbar breiter als bei einem Textfeld -, und die
gewinnt gegen jedes width: 100%.

NACHTRAG (gleicher Tag): min-width: 0 allein hat es NICHT behoben - gegen den bereits deployten Stand
gegengeprüft, der Nutzer sah den Überlauf unverändert. Der tatsächliche Grund ist iOS-spezifisch: ein
Datums-/Zeitfeld wird dort als natives Systemsteuerelement gerendert und behält dessen feste
Eigenbreite, an der eine Breitenangabe gar nicht erst greift. Erst -webkit-appearance: none macht
daraus ein gewöhnliches Feld, an dem width: 100% wirkt; der Datumswähler beim Antippen bleibt davon
unberührt. min-width: 0/max-width: 100% bleiben zusätzlich stehen (decken den Grid- bzw. Flex-Fall auf
anderen Systemen ab), ebenso die Absicherung an den umgebenden Grids darunter. */
input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"] {
    -webkit-appearance: none;
    appearance: none;
    min-width: 0;
    max-width: 100%;
    /* Nebenwirkung von appearance:none (Nutzerhinweis 2026-08-16): ein LEERES Datumsfeld hat unter
       iOS dann keinen Inhalt mehr, der es aufspannt - es war dadurch flacher als die Felder daneben
       und wuchs erst, sobald ein Datum gewaehlt war. Die Hoehe entspricht der eines normalen Felds
       (0.95rem Zeilenhoehe + 2x12px Padding + Rahmen). */
    min-height: calc(0.95rem * 1.4 + 26px);
}
/* Der Überlauf pflanzt sich sonst über die ganze Grid-Kette fort (form.stack > label > input) - jedes
   Glied davon ist selbst wieder Grid-Element mit implizitem min-width: auto. */
.modal-box form, .modal-box label { min-width: 0; }

input:focus, textarea:focus, select:focus { outline: 2px solid rgba(56, 189, 248, 0.3); }
/* Nutzer-Bug-Report (mobile iOS-Screenshots, "überall in der App"): Checkboxen wirkten "schwarz mit
   seltsamen Rahmen" - Ursache ist `:root { color-scheme: dark }` (ganz oben in dieser Datei), das
   iOS Safari dazu bringt, native Checkbox-Widgets im dunklen System-Stil zu rendern (schwarze
   Füllung, kaum sichtbarer Rahmen auf dunklem Hintergrund) - betraf JEDE ungestylte `<input
   type="checkbox">` app-weit (z. B. `.inline-checkbox`/die Beauftragte-Personen-Liste im Aufgaben-
   Detailsheet), nicht nur die Aufgabenverwaltung. Eigenes, `appearance:none`-basiertes Erscheinungsbild
   passend zum sonstigen abgerundeten App-Stil - ersetzt die native Darstellung komplett, funktioniert
   dadurch identisch in hell/dunkel und auf jeder Plattform. */
input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 20px;
    height: 20px;
    min-width: 20px;
    flex-shrink: 0;
    border: 2px solid var(--border);
    border-radius: 6px;
    background: var(--panel-2);
    display: inline-grid;
    place-content: center;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}
input[type="checkbox"]::after {
    content: "";
    width: 5px;
    height: 9px;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg) translate(-1px, -1px) scale(0);
    transition: transform 0.12s ease;
}
input[type="checkbox"]:checked { background: var(--accent); border-color: var(--accent); }
input[type="checkbox"]:checked::after { transform: rotate(45deg) translate(-1px, -1px) scale(1); }
input[type="checkbox"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
input[type="radio"] { width: auto; flex-shrink: 0; accent-color: var(--accent); }
/* Gleiches Problem wie bei Checkboxen (siehe CLAUDE.md): die globale input{width:100%}-Regel samt
12px/14px-Padding presst den nativen Farb-Swatch eines type="color"-Felds auf einen duennen,
kaum sichtbaren Streifen zusammen (Saal-/Raeumlichkeits-Farbfeld in saalverwaltung.php). Eigene,
kompakte Groesse ohne das Text-Padding - der Swatch selbst braucht keinen Rahmen-Innenabstand. */
input[type="color"] { width: 64px; height: 44px; padding: 4px; flex-shrink: 0; }
.alert { padding: 12px 14px; border-radius: 12px; background: rgba(251, 113, 133, 0.18); color: #fecdd3; }
.alert.success { background: rgba(34, 197, 94, 0.16); color: #bbf7d0; }
.alert.error { background: rgba(251, 113, 133, 0.18); color: #fecdd3; }
.alert.info { background: rgba(56, 189, 248, 0.16); color: #bae6fd; }
.alert.warning { background: rgba(251, 191, 36, 0.18); color: #fde68a; }
.highlight-total { padding: 12px 14px; border-radius: 12px; background: rgba(var(--panel-tint-rgb), 0.5); font-size: 1.05rem; }
.highlight-total strong { font-size: 1.3rem; color: var(--accent-2); }
.field-error { color: #fecdd3; }
.input-error { border-color: #fb7185 !important; box-shadow: 0 0 0 2px rgba(251, 113, 133, 0.2); }
.muted { color: var(--muted); }
.section-title { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 18px; }
.pill { display: inline-block; padding: 6px 10px; border-radius: 999px; background: rgba(56, 189, 248, 0.18); color: var(--accent); font-size: 0.85rem; }
.knowledge-box { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.knowledge-item { padding: 10px 0; border-bottom: 1px dashed var(--border); }
.knowledge-item-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.knowledge-item-title-group { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.knowledge-scope-tag { justify-self: start; font-size: 0.78rem; padding: 2px 8px; border-radius: 999px; background: rgba(56, 189, 248, 0.14); color: var(--accent); white-space: nowrap; }
.knowledge-delete-form { flex-shrink: 0; }
/* Wird mal als <div>, mal als <ul> verwendet (meine_mitgliedschaft.php, korrespondenz.php,
   mitglied_werden.php, probetraining_buchen.php). Als Liste erbte sie bisher die Browser-Vorgaben:
   ~40px Einzug plus Aufzaehlungspunkte - genau die "Einrueckung in den Abschnitten Beitraege,
   Gruppen, Rabatte", die der Nutzer 2026-08-18 bemaengelt hat. Da die Klasse ohnehin ein Grid ist,
   waren Punkte/Einzug an KEINER Stelle beabsichtigt. */
.membership-list { display: grid; gap: 2px; margin-bottom: 10px; padding-left: 0; list-style: none; }
.membership-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px dashed var(--border); }
.membership-row:last-child { border-bottom: none; }
.membership-row form { flex-shrink: 0; }
.membership-name { flex: 1; min-width: 0; }
.membership-row select { width: auto; }
/* Kontakt-Icons (Nutzerwunsch: statt eines "Telefonnummer anzeigen"-Buttons + Text-Popups direkt
große, verlinkte Anruf-/WhatsApp-Icons mit der Nummer darunter) - genutzt sowohl in einem zentrierten
Popup (probetrainings.php, .contact-icons-centered) als auch inline direkt unter einem Eintrag
(meine_gruppen.php Probetrainings-Bubble, ohne Centering-Modifier). */
.contact-icons { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.contact-icons-centered { align-items: center; text-align: center; padding: 8px 0; }
.contact-icons-buttons { display: flex; gap: 14px; }
.contact-icons-centered .contact-icons-buttons { justify-content: center; }
.contact-icons-buttons a { display: inline-flex; align-items: center; justify-content: center; width: 52px; height: 52px; border-radius: 50%; background: rgba(56, 189, 248, 0.16); color: var(--accent); font-size: 1.5rem; }
.contact-icons-buttons a:hover { background: rgba(56, 189, 248, 0.28); }
.contact-icons-buttons a.contact-icon-whatsapp { background: rgba(37, 211, 102, 0.18); color: #25d366; }
.contact-icons-buttons a.contact-icon-whatsapp:hover { background: rgba(37, 211, 102, 0.3); }
.contact-icons-number { font-size: 0.9rem; color: var(--muted); }
/* Kleiner Inline-WhatsApp-Knopf direkt hinter einem Namen (Probestunden-Übersicht: Gruppenleitung
   über den neuen Termin informieren). Bewusst NICHT .contact-icons-buttons a - das sind 52px große
   Kreise für ein Kontakt-Popup und würden eine Tabellenzeile sprengen. */
.leader-whatsapp-link { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 50%; background: rgba(37, 211, 102, 0.18); color: #25d366; font-size: 0.78rem; vertical-align: baseline; }
.leader-whatsapp-link:hover { background: rgba(37, 211, 102, 0.3); }
.membership-add-row { display: flex; align-items: center; gap: 8px; }
.membership-add-row select { width: auto; flex: 1; min-width: 0; }
.module-role-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 6px 0 10px 4px; font-size: 0.85rem; }
.module-role-label { flex-shrink: 0; }
.modal-section-divider { height: 1px; background: var(--border); margin: 18px 0 14px; }
.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.calendar-weekday-label { font-weight: 600; text-align: center; padding: 6px; color: var(--muted); font-size: 0.85rem; }
.calendar-cell { min-height: 92px; border: 1px solid var(--border); border-radius: 10px; padding: 6px; background: rgba(var(--panel-tint-rgb), 0.35); overflow: hidden; }
.calendar-cell-empty { background: transparent; border: none; }
.calendar-cell-today { border-color: var(--accent); }
.calendar-cell-date { font-size: 0.78rem; color: var(--muted); margin-bottom: 4px; }
.calendar-event { font-size: 0.72rem; padding: 2px 5px; border-radius: 5px; background: rgba(56, 189, 248, 0.16); color: var(--accent); margin-bottom: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calendar-event-time { font-weight: 600; margin-right: 4px; }
.calendar-event-cancelled { background: rgba(248, 113, 113, 0.16); color: #fca5a5; text-decoration: line-through; }
/* Wochenansicht (Nutzerwunsch: "Kalender View ... auf Wochenbasis machen") - nur 7 statt bis zu 35
Zellen sichtbar, deshalb spürbar mehr Platz je Tag als in der vorherigen Monatsansicht. */
.calendar-grid-week .calendar-cell { min-height: 220px; }
.calendar-grid-week .calendar-event { font-size: 0.78rem; padding: 4px 6px; white-space: normal; }
@media (max-width: 720px) {
    .calendar-grid { grid-template-columns: repeat(7, minmax(36px, 1fr)); }
    .calendar-cell { min-height: 56px; padding: 3px; }
    .calendar-grid-week .calendar-cell { min-height: 140px; }
    .calendar-event { font-size: 0.62rem; }
}
/* Tagesansicht (Nutzerwunsch): Termine EINES Tages nach Saal in nebeneinanderliegenden Spalten,
zusätzlich nach Räumlichkeit gruppiert (Räumlichkeit als Obertitel, Raumname dezent darunter), mit
einer vertikalen, äquidistant beschrifteten Uhrzeiten-Achse (0-24 Uhr). Ein einziger Container
übernimmt sowohl vertikales als auch horizontales Scrollen (kalender.php scrollt beim Laden per JS
zum ersten Termin des Tages, siehe initCalendarDayScroll() in assets/js/app.js) - Eckzelle/
Kopfzeilen/Zeit-Achse bleiben dabei per position:sticky sichtbar. kalender.php berechnet grid-column
UND grid-row für jede Zelle explizit (kein Vertrauen auf CSS-Grid-Auto-Placement über mehrere
Zeilen/Spalten-Gruppen hinweg). */
.calendar-day-scroll {
    --calendar-day-axis-width: 56px;
    --calendar-day-hour-height: 52px;
    --calendar-day-header-height: 34px;
    --calendar-day-subheader-height: 28px;
    overflow: auto;
    /* Nutzer-Feedback: bei ~17h gleichzeitig sichtbarer Höhe deckte das Fenster fast den ganzen Tag
    ab - der Auto-Scroll zum ersten Termin landete dadurch praktisch schon am oberen Rand des ohnehin
    fast vollständig sichtbaren Inhalts, es blieb kaum Raum zum Scrollen. Jetzt ca. 10 Stunden
    gleichzeitig sichtbar (520px = 10 * 52px Stundenhöhe, vh-Anteil proportional mitskaliert). Gilt
    nur für die breite/Desktop-Ansicht, die mobile Pager-Höhe (siehe @media weiter unten) bleibt
    unverändert. */
    max-height: min(44vh, 520px);
    border: 1px solid var(--border);
    border-radius: 10px;
}
.calendar-day-grid { display: grid; width: max-content; min-width: 100%; }
.calendar-day-corner { grid-column: 1; grid-row: 1 / span 2; position: sticky; top: 0; left: 0; z-index: 30; background: var(--panel-2-solid); border-bottom: 1px solid var(--border); border-right: 1px solid var(--border); }
.calendar-day-location-header { position: sticky; top: 0; z-index: 20; background: var(--panel-2-solid); border-bottom: 1px solid var(--border); border-left: 1px solid var(--border); padding: 6px 8px; font-weight: 600; font-size: 0.82rem; height: var(--calendar-day-header-height); box-sizing: border-box; display: flex; align-items: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calendar-day-room-header { position: sticky; top: var(--calendar-day-header-height); z-index: 20; background: var(--panel-2-solid); border-bottom: 1px solid var(--border); border-left: 1px solid var(--border); padding: 4px 8px; font-size: 0.74rem; color: var(--muted); height: var(--calendar-day-subheader-height); box-sizing: border-box; display: flex; align-items: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calendar-day-room-header-pseudo { font-style: italic; }
.calendar-day-time-axis { grid-column: 1; position: sticky; left: 0; z-index: 10; background: var(--panel-2-solid); border-right: 1px solid var(--border); height: calc(24 * var(--calendar-day-hour-height)); }
.calendar-day-hour-label { position: absolute; left: 0; right: 0; padding: 0 6px; font-size: 0.68rem; color: var(--muted); transform: translateY(-50%); white-space: nowrap; }
.calendar-day-room-body {
    position: relative;
    height: calc(24 * var(--calendar-day-hour-height));
    border-left: 1px solid var(--border);
    background-image: repeating-linear-gradient(to bottom, var(--border) 0, var(--border) 1px, transparent 1px, transparent var(--calendar-day-hour-height));
}
.calendar-day-event { position: absolute; min-height: 20px; overflow: hidden; padding: 2px 6px; border-radius: 5px; background: rgba(56, 189, 248, 0.16); color: var(--accent); font-size: 0.72rem; line-height: 1.25; box-sizing: border-box; }
/* Echter Cascade-Reihenfolge-Bug (Nutzer-Bug-Report: "in der mobilen Ansicht wird garnichts mehr
angezeigt") - diese Basisregel muss VOR der @media (max-width: 720px)-Regel stehen, die
.calendar-day-mobile mobil wieder auf "block" setzt (Zeile weiter unten). Stand die Basisregel danach
(wie ursprünglich gebaut), gewinnt sie bei gleicher Spezifität immer, unabhängig von der
Bildschirmbreite - .calendar-day-mobile blieb dadurch IMMER unsichtbar, auch mobil, während dieselbe
@media-Regel gleichzeitig den Desktop-Block ausblendete: zusammen blieb mobil buchstäblich nichts
sichtbar. */
/* Echter Bug (Nutzer-Bug-Report: "zeigt jetzt Navigation für Saal, aber keinen Inhalt darunter") -
--calendar-day-hour-height/--calendar-day-axis-width waren bisher NUR auf .calendar-day-scroll
deklariert. .calendar-day-mobile ist im HTML aber ein GESCHWISTERelement davon (kein Nachfahre) -
CSS-Custom-Properties vererben sich nur entlang der tatsächlichen DOM-Verschachtelung, nicht
zwischen Geschwistern. Dadurch war var(--calendar-day-hour-height) innerhalb der mobilen Pager-Seiten
undefiniert, height: calc(24 * var(--undefined)) damit ungültig und fiel auf auto zurück - da die
Stunden-Labels/Termin-Karten alle position:absolute sind (tragen nichts zur natürlichen Höhe bei),
kollabierte die komplette Zeit-Achse/Termin-Spalte auf 0px. Fix: dieselben mobilen Größenwerte direkt
hier deklarieren, damit sie an die eigenen Nachfahren (die Pager-Seiten) vererbt werden. */
.calendar-day-mobile {
    display: none;
    --calendar-day-axis-width: 44px;
    --calendar-day-hour-height: 40px;
}
/* Termin-Aktionsicons in der Tagesansicht (Nutzerwunsch: "die kleinen Symbole ... für die
Aktionen, die man ausführen kann") - kompakte Icon-Reihe direkt in der Terminkarte, wiederverwendet
render_occurrence_action_icons()s Buttons/Pillen (app/group_occurrence_view_helpers.php), nur in
Miniaturgröße. .calendar-day-event-has-actions gibt der Karte etwas mehr Mindesthöhe, damit auch ein
sehr kurzer Termin Platz für die Icon-Zeile bekommt (die Karte ragt dadurch bewusst geringfügig über
ihren exakten Zeit-Slot hinaus, wie in etablierten Kalender-UIs üblich). */
.calendar-day-event-has-actions { min-height: 46px; }
.calendar-day-event-actions { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 3px; }
.calendar-day-event-actions .button-row { display: contents; }
.calendar-day-event-actions .button,
.calendar-day-event-actions .status-pill { padding: 1px 5px; font-size: 0.62rem; line-height: 1.5; }
.calendar-day-event-actions form { display: contents; }

/* Sticky Datums-Navigation (Nutzerwunsch: "spätestens über 'Vorheriger Tag'/'Vorherige Woche'
fixiert ... ohne zu scrollen") - dockt direkt unter der App-Kopfzeile an (--main-topbar-height wird
bereits app-weit von initTaskStickyHeaders() gepflegt, siehe assets/js/app.js). Pfeile statt Text
(Nutzerwunsch: "weniger Text ... der Kontext ist klar"). */
/* Nutzer-Feedback: transparent statt var(--bg) - die Leiste soll sich in den .card-Container
einfügen statt als eigener, andersfarbiger Balken zu wirken (gleiches Muster wie .task-topbar/
.task-list-section-header, die aus demselben Grund bereits transparent sind). */
.calendar-nav-sticky { position: sticky; top: var(--main-topbar-height, 70px); z-index: 40; display: flex; align-items: center; justify-content: center; gap: 14px; margin-bottom: 16px; padding: 8px 0; background: transparent; }
.calendar-nav-arrow { width: 36px; text-align: center; }
.calendar-nav-label { font-weight: 600; }
@media (max-width: 760px) {
    .calendar-nav-sticky { top: 0; }
}

/* Dezentere Filterzeile (Nutzerwunsch: "Filter bitte minimal dezenter (kleinere Schriftarten
Dropdown) machen") - nur in der Wochenansicht gerendert, die Tagesansicht blendet den ganzen
Filter-Block aus (die Spaltenaufteilung nach Saal übernimmt dort bereits die Raum-Fokussierung). */
.calendar-filter-subtle label { font-size: 0.8rem; }
.calendar-filter-subtle select { font-size: 0.82rem; padding: 5px 8px; }

@media (max-width: 720px) {
    .calendar-day-scroll { --calendar-day-axis-width: 44px; --calendar-day-hour-height: 40px; max-height: 65vh; }
    .calendar-day-location-header { font-size: 0.72rem; }
    .calendar-day-room-header { font-size: 0.66rem; }
    .calendar-day-hour-label { font-size: 0.6rem; }
    .calendar-day-event { font-size: 0.62rem; padding: 1px 4px; }
    /* Mobiler Ein-Saal-Pager (Nutzerwunsch: "auf einem mobilen Gerät nur exakt 1 Saal angezeigt ...
    Pfeile links und rechts ... Wisch-Geste, die einrastet, nicht durchgängiges Scrollen") - ersetzt
    den mehrspaltigen Desktop-Grid komplett, gleiche Datengrundlage ($dayColumnsFlat, kalender.php).
    Die Wisch-/Einrast-Mechanik kommt vollständig von scroll-snap-type (kein eigenes
    Touch-Tracking-JS) - initCalendarDayMobilePager() (assets/js/app.js) steuert nur die
    Pfeil-Buttons + die Beschriftung/den Zähler. */
    .calendar-day-scroll { display: none; }
    .calendar-day-mobile { display: block; }
    .calendar-day-mobile-nav { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 8px; }
    /* 0.82rem war zu gross: "Kleiner Saal EG (Tanzstudio Fortuna) · 4/5" wurde am rechten Rand
    abgeschnitten - und ausgerechnet die Seitenzahl fiel weg (2026-08-16). Kleinere Schrift, und der
    Text darf jetzt auf zwei Zeilen umbrechen statt hart abzuschneiden. */
    .calendar-day-mobile-label { font-size: 0.72rem; font-weight: 600; text-align: center; flex: 1; min-width: 0; line-height: 1.3; overflow-wrap: anywhere; }
    .calendar-day-mobile-scroll { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; border: 1px solid var(--border); border-radius: 10px; }
    .calendar-day-mobile-page { flex: 0 0 100%; max-width: 100%; scroll-snap-align: start; }
    .calendar-day-mobile-page-scroll { overflow-y: auto; max-height: 65vh; }
    .calendar-day-mobile-page-inner { display: flex; }
    .calendar-day-mobile-page-inner .calendar-day-time-axis { position: relative; left: 0; flex-shrink: 0; width: var(--calendar-day-axis-width); }
    .calendar-day-mobile-page-inner .calendar-day-room-body { flex: 1; min-width: 0; }
}
.rules-list { display: grid; gap: 10px; margin: 10px 0; }
.rule-item { padding: 10px; border: 1px solid var(--border); border-radius: 12px; background: rgba(var(--panel-tint-rgb), 0.5); display: grid; gap: 8px; }
.rule-item.rule-inactive { opacity: 0.55; }
.rule-item-head { display: flex; align-items: center; gap: 10px; }
.rule-title-input { flex: 1; min-width: 0; background: transparent; border: 1px solid transparent; color: var(--text); font-weight: 600; padding: 4px 6px; border-radius: 8px; }
.rule-title-input:focus { border-color: var(--border); outline: none; }
.rule-item textarea { font-size: 0.9rem; }
.small-button { padding: 6px 10px; flex-shrink: 0; }
.rule-change-card .chat-diff-body { white-space: normal; font-family: inherit; }
.inline-checkbox { display: flex; gap: 8px; align-items: center; justify-content: flex-start; color: var(--text); text-align: left; }
.form-field { display: grid; gap: 8px; color: var(--muted); font-size: 0.95rem; }
.field-label { display: block; }
.connector-permission-list { display: grid; gap: 8px; }
.connector-permission-item { padding: 10px 12px; border: 1px solid var(--border); border-radius: 12px; background: rgba(var(--panel-tint-rgb), 0.35); display: grid; gap: 8px; justify-items: start; }
.connector-permission-item .inline-checkbox { color: var(--text); font-weight: 600; }
.connector-action-restriction { margin-left: 24px; padding: 8px 0 0 10px; border-left: 2px solid var(--border); display: grid; gap: 6px; width: 100%; }
.connector-action-restriction .restrict-toggle { font-size: 0.85rem; color: var(--muted); font-weight: 400; }
.connector-action-list { display: grid; gap: 4px; padding-left: 16px; }
.status-pill { display: inline-flex; align-items: center; gap: 6px; border-radius: 999px; padding: 6px 10px; font-size: 0.85rem; white-space: nowrap; justify-self: start; }
.status-connected { background: rgba(34, 197, 94, 0.16); color: #bbf7d0; }
.status-error { background: rgba(251, 113, 133, 0.18); color: #fecdd3; }
.status-pending { background: rgba(148, 163, 184, 0.18); color: var(--muted); }
.status-warning { background: rgba(251, 191, 36, 0.18); color: #fde68a; }
.log-list { margin-top: 8px; display: grid; gap: 4px; font-size: 0.95rem; color: #e2e8f0; }
.log-list-group-heading { font-size: 0.78rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--accent); padding: 4px 2px; }
/* Meine-Gruppen-Redesign (Nutzerwunsch): Tages-Kachel je Tag innerhalb einer Wochen-Gruppe -
grosse/blaue Wochentagsabkuerzung + Datum ohne Jahr, darunter alle Termine dieses Tages. */
/* Nutzerwunsch: "Kalender-Ansicht raus, dafür soll das Datum in der Listenansicht einen
Zeilenumbruch danach bewirken, damit die Termine volle Breite haben ... linksbündig, Datum direkt
hinter dem Wochentag ohne Umbruch davor, aber dezent wie bisher" - Wochentag+Datum standen bisher
als eigene 56px-Spalte NEBEN den Terminen (kostete auf schmalen Bildschirmen echte Breite); jetzt
eine eigene volle Zeile darüber, Termine darunter nutzen die komplette Breite. */
.log-day-group { display: block; padding: 6px 2px; }
.log-day-tile { display: flex; align-items: baseline; gap: 6px; margin-bottom: 6px; }
.log-day-weekday { font-size: 1.15rem; font-weight: 700; color: var(--accent); line-height: 1.1; }
.log-day-date { font-size: 0.72rem; color: var(--muted); }
.log-day-items { display: grid; gap: 6px; min-width: 0; }
/* Raum-/Ersatztermin-Hinweiszeilen (Nutzerwunsch: kein "Raum:"-Praefix, keine Klammern, eigene,
kleinere Zeile statt Pill) - etwas kleiner als die Standard-.muted-Zeile darueber. */
.log-item-detail { display: block; font-size: 0.8rem; }
.connector-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; }
.connector-card { padding: 18px; border: 1px solid var(--border); border-radius: 16px; background: rgba(var(--panel-tint-rgb), 0.55); display: grid; gap: 12px; align-content: start; }
.connector-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.connector-identity { display: flex; align-items: center; gap: 12px; }
.connector-avatar { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: 12px; background: rgba(56, 189, 248, 0.14); color: var(--accent); font-size: 1.2rem; flex-shrink: 0; }
.agent-avatar-photo { object-fit: cover; border-radius: 50%; }
.agent-avatar-upload-row { display: flex; align-items: center; gap: 12px; }
.agent-avatar-preview { width: 56px; height: 56px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.agent-avatar-placeholder { display: inline-flex; align-items: center; justify-content: center; background: rgba(56, 189, 248, 0.14); color: var(--accent); font-size: 1.4rem; }
.connector-card-footer { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; font-size: 0.9rem; }
.connector-error-note { padding: 10px 12px; border-radius: 10px; background: rgba(251, 113, 133, 0.12); color: #fecdd3; font-size: 0.9rem; }
.controls-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pill-muted { background: rgba(148, 163, 184, 0.18); color: var(--muted); }
.image-quality-pill { padding: 2px 8px; font-size: 0.72rem; vertical-align: middle; }
.is-disabled { opacity: 0.6; }

.switch { position: relative; display: inline-flex; align-items: center; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track { width: 42px; height: 24px; border-radius: 999px; background: rgba(148, 163, 184, 0.3); position: relative; transition: background .2s ease; flex-shrink: 0; }
.switch-track::after { content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 50%; background: white; transition: transform .2s ease; }
.switch input:checked + .switch-track { background: linear-gradient(90deg, var(--accent), var(--accent-2)); }
.switch input:checked + .switch-track::after { transform: translateX(18px); }

.account-menu { position: relative; }
/* Unsichtbare Brücke über die Lücke zwischen Trigger und Dropdown, sonst verliert die Maus beim
   Runterfahren kurz den Hover und das Menü schließt sich, bevor sie das Dropdown erreicht. */
.account-menu::after { content: ""; position: absolute; top: 100%; left: 0; right: 0; height: 10px; }
.account-button { background: transparent; border: none; padding: 4px; border-radius: 999px; cursor: pointer; position: relative; display: inline-flex; align-items: center; justify-content: center; }
.account-avatar { width: 36px; height: 36px; border-radius: 50%; background: linear-gradient(135deg, var(--accent), var(--accent-2)); color: white; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; font-size: 0.95rem; }
.person-avatar { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.person-avatar-fallback { background: linear-gradient(135deg, var(--accent), var(--accent-2)); color: white; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; font-size: 0.95rem; }
.person-avatar-lg { width: 56px; height: 56px; font-size: 1.4rem; }
.department-tag { display: inline-block; margin-left: 6px; padding: 2px 8px; border-radius: 999px; font-size: 0.68rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; background: rgba(139, 92, 246, 0.18); color: #ddd6fe; vertical-align: middle; justify-self: start; }
.notification-badge { position: absolute; top: -2px; right: -2px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px; background: var(--danger); color: white; font-size: 0.65rem; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; line-height: 1; }
.notification-badge.inline { position: static; margin-left: 8px; }
.account-dropdown { position: absolute; right: 0; top: calc(100% + 10px); min-width: 220px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 16px; box-shadow: 0 20px 50px rgba(2, 6, 23, 0.4); display: none; flex-direction: column; z-index: 50; overflow: hidden; padding: 6px; }
.account-dropdown.open { display: flex; }
/* Nutzerwunsch/Bug-Report: "wenn ich über das Menü drüberswipe, weil ich scrollen will, öffnet sich
random auch diese Menüs" + "wenn ich auf einen Menüpunkt mit Untermenü drücke ist ganz kurz noch das
alte Menü aufgeklappt" - diese reine CSS-Hover/Fokus-Regel ist unabhängig von der JS-Drilldown-Logik
(assets/js/app.js initMobileNavDrilldown()) und feuert auch mobil, sobald ein Touch/Scroll-Antippen
den Button kurz fokussiert (:focus-within reagiert auf Fokus, nicht nur echtes Hover) - dadurch
klappte das Untermenü kurz in seiner alten, nicht-drillenden Inline-Optik auf, bevor die JS-Klassen
(.nav-drilled/.open) übernehmen, bzw. komplett ungewollt beim bloßen Scroll-Antippen. Hover/Fokus-
Aufklappen ist ohnehin nur eine Desktop-Bequemlichkeit (Touch-Geräte kennen kein echtes Hover) - ab
sofort nur noch ab 761px aktiv, mobil regelt ausschließlich der explizite Klick über initMobileNav
Drilldown() die Sichtbarkeit (per .open-Klasse). */
@media (min-width: 761px) {
    .account-menu:hover .account-dropdown, .account-menu:focus-within .account-dropdown { display: flex; }
}
/* Nutzerwunsch: Untermenü-Einträge linksbündig (Icon direkt vor dem Text, intuitiver als über die
ganze Zeile verteilt) - NUR der Benachrichtigungs-Badge (falls vorhanden) soll weiterhin rechts
stehen, dafür sorgt margin-left:auto weiter unten statt justify-content:space-between hier. */
.account-dropdown a { display: flex; align-items: center; justify-content: flex-start; gap: 10px; padding: 10px 12px; border-radius: 10px; color: var(--text); font-size: 0.9rem; }
.account-dropdown a:hover { background: rgba(148, 163, 184, 0.12); }
.account-dropdown-label { padding: 8px 12px 2px; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.company-switch-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; text-transform: none; font-size: 0.85rem; color: var(--text); }
.company-switch-btn { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 8px; border: none; background: transparent; color: var(--muted); cursor: pointer; font-size: 0.8rem; }
.company-switch-btn:hover { background: rgba(148, 163, 184, 0.14); color: var(--text); }
.account-dropdown-form { display: contents; }
.account-dropdown-link-button { display: flex; width: 100%; align-items: center; justify-content: flex-start; gap: 10px; padding: 10px 12px; border-radius: 10px; color: var(--text); font-size: 0.9rem; background: transparent; border: none; text-align: left; cursor: pointer; font-weight: 400; }
.account-dropdown-link-button:hover { background: rgba(148, 163, 184, 0.12); }
/* Nutzer-Bug-Report 2026-08-18: der Zaehler stand "zu weit rechts" - mit `margin-left: auto` schob
   er sich bis an den Favoriten-Stern heran und schwebte dadurch mitten in der Zeile, weit weg von
   der Beschriftung, zu der er gehoert. Jetzt haengt er direkt am Text; den rechten Rand belegt
   weiterhin der Stern (der hat sein eigenes margin-left:auto). */
.account-dropdown a .notification-badge.inline, .account-dropdown-link-button .notification-badge.inline { margin-left: 8px; }
.account-dropdown-divider { height: 1px; background: var(--border); margin: 6px 4px; }
.nav-dropdown-toggle { background: transparent; padding: 0; border-radius: 0; color: var(--text); font-size: 1rem; font-weight: 400; gap: 6px; }
.nav-dropdown-toggle:hover { color: var(--accent); }
/* Menü-Favoriten/Shortcuts (Nutzerwunsch: eigene, direkt in den Modul-Untermenüs editierbare
Favoriten, die vorne im Menü als Direktlink erscheinen) - initMenuFavoriteToggles()
(assets/js/app.js) injiziert dieses Stern-Symbol per JS in jede [data-fav-key]-Zeile (grau,
standardmäßig unsichtbar, erscheint nur beim Überfahren mit der Maus) statt es serverseitig ~40x
manuell zu duplizieren. margin-left:auto sorgt für rechtsbündige Platzierung auch dort, wo keine
Benachrichtigungs-Badge (die ebenfalls margin-left:auto nutzt) den Rechts-Schub bereits übernimmt. */
.nav-fav-toggle { flex-shrink: 0; margin-left: auto; display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 6px; border: none; background: transparent; color: var(--muted); cursor: pointer; font-size: 0.8rem; opacity: 0; transition: opacity .1s ease, color .1s ease; }
.account-dropdown a:hover .nav-fav-toggle,
.account-dropdown-link-button:hover .nav-fav-toggle { opacity: 1; }
.nav-fav-toggle:hover { background: rgba(148, 163, 184, 0.18); color: var(--text); }
.nav-fav-toggle.favorited { opacity: 1; color: var(--accent); }
.nav-fav-toggle.favorited:hover { color: var(--accent); }
/* Die Shortcut-Zeile selbst (ganz vorne in #navLinks) - gleiche Optik wie die übrigen
Top-Level-Menüpunkte (Nutzerwunsch: "Schriftfarbe auch weiß wie alles andere im Menü, damit es gut
daneben aussieht, auch mit Icon"), reine <a>-Direktlinks statt eines Dropdown-Togglers. */
.nav-shortcuts { display: flex; gap: 14px; align-items: center; }
.nav-shortcut-link { display: inline-flex; align-items: center; gap: 6px; color: var(--text); font-size: 1rem; }
.nav-shortcut-link:hover { color: var(--accent); }
/* Nutzerwunsch: im mobilen Menü kein Avatar-Bild fürs Konto-Menü, stattdessen der Text "Konto" -
auf Desktop bleibt der Avatar wie gewohnt, dieses Label ist dort unsichtbar (siehe @media unten). */
.account-button-mobile-label { display: none; }
.account-button-mobile-icon { display: none; }
.account-button-mobile-chevron { display: none; }
/* Geschlossener Dropdown mit Mehrfachauswahl (initMultiselectDropdowns(), assets/js/app.js) -
gleiches Positionierungs-/Schatten-Muster wie .account-dropdown, aber per Klick statt Hover
gesteuert (Checkboxen im Panel brauchen Klicks, ein Hover-Schließen würde die Auswahl stören). */
.multiselect-dropdown { position: relative; z-index: 10; }
/* Nutzer-Bug-Report: zwei Multiselect-Dropdowns im selben Formular (z. B. "Gruppenleiter" nach
"Altersklassen") teilen denselben Basis-z-index - bei einer Überlappung gewinnt dann die
DOM-Reihenfolge, das später stehende Feld legt sich über das Panel des gerade geöffneten, früher
stehenden Felds. initMultiselectDropdowns() (assets/js/app.js) setzt diese Klasse nur auf den
Wrapper des GERADE GEÖFFNETEN Dropdowns - hebt dessen z-index unabhängig von der DOM-Reihenfolge
über jeden anderen (auch später stehenden) Dropdown an. */
.multiselect-dropdown.multiselect-dropdown-active { z-index: 50; }
.multiselect-dropdown-toggle { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 8px; border: 1px solid var(--border); border-radius: 12px; padding: 12px 14px; background: var(--panel-2); color: var(--text); font-size: 0.95rem; font-weight: 400; cursor: pointer; text-align: left; }
.multiselect-dropdown-toggle span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.multiselect-dropdown-panel { display: none; flex-direction: column; gap: 6px; position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 30; background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 20px 50px rgba(2, 6, 23, 0.4); padding: 10px; max-height: 240px; overflow-y: auto; }
.multiselect-dropdown-panel.open { display: flex; }
.multiselect-dropdown-controls { display: flex; gap: 6px; align-items: center; margin-bottom: 4px; position: sticky; top: 0; background: var(--panel-2); padding-bottom: 6px; }
.multiselect-dropdown-controls input[type="text"] { flex: 1; min-width: 0; }
.multiselect-dropdown-controls .button { flex-shrink: 0; white-space: nowrap; }
.multiselect-dropdown-controls [data-multiselect-clear] { padding-left: 10px; padding-right: 10px; font-size: 1.1rem; line-height: 1; }
.multiselect-dropdown-controls .multiselect-dropdown-apply { padding-left: 10px; padding-right: 10px; }
/* Modul-Rollen-Tabs im Personen-Bearbeiten-Modal (settings.php) - initInlineTabToggles(),
assets/js/app.js. */
.role-tab-panel { display: none; }
.role-tab-panel.active { display: block; }
.nav-dropdown .account-dropdown { right: auto; left: 0; min-width: 200px; }

.notification-row { display: flex; gap: 12px; padding: 14px; border: 1px solid var(--border); border-radius: 14px; background: rgba(var(--panel-tint-rgb), 0.4); align-items: flex-start; }
.notification-row.unread { border-color: rgba(56, 189, 248, 0.4); background: rgba(56, 189, 248, 0.05); }
.notification-row-body { flex: 1; min-width: 0; }
/* Hochformat: die Aktionen wandern UNTER den Text. Nebeneinander blieb fuer die Meldung selbst so
   wenig Breite, dass sie Wort fuer Wort umbrach (real gemeldet). Die Zeile wird dafuer zur Spalte -
   die Knoepfe stehen dann als eigene Reihe darunter, rechtsbuendig. */
@media (max-width: 760px) {
    .notification-row { flex-wrap: wrap; }
    .notification-row-body { flex: 1 1 100%; }
    .notification-row-actions { flex: 1 1 100%; justify-content: flex-end; margin-top: 10px; }
}
.notification-type-icon { width: 36px; height: 36px; border-radius: 12px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.notification-type-icon.type-info { background: rgba(56, 189, 248, 0.14); color: var(--accent); }
.notification-type-icon.type-success { background: rgba(34, 197, 94, 0.16); color: #bbf7d0; }
.notification-type-icon.type-error { background: rgba(251, 113, 133, 0.18); color: #fecdd3; }
.notification-type-icon.type-warning { background: rgba(234, 179, 8, 0.16); color: #fde68a; }
.notification-link-list, .notification-file-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.notification-link-chip, .notification-file-chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; border-radius: 999px; background: rgba(56, 189, 248, 0.12); color: var(--accent); font-size: 0.82rem; border: 1px solid rgba(56, 189, 248, 0.25); }
.notification-file-thumb { width: 40px; height: 40px; object-fit: cover; border-radius: 8px; }
.system-log-raw { margin: 4px 0 0; padding: 8px 10px; border-radius: 8px; background: rgba(var(--panel-tint-rgb), 0.6); border: 1px solid var(--border); font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: 0.82rem; white-space: pre-wrap; word-break: break-word; }
.category-tag { display: inline-block; margin-left: 6px; padding: 2px 8px; border-radius: 999px; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em; }
.category-tag.category-ai_model { background: rgba(139, 92, 246, 0.18); color: #ddd6fe; }
.category-tag.category-image_generation { background: rgba(236, 72, 153, 0.16); color: #fbcfe8; }
.category-tag.category-messaging { background: rgba(56, 189, 248, 0.14); color: var(--accent); }
.category-tag.category-filesystem { background: rgba(34, 197, 94, 0.16); color: #bbf7d0; }
.category-tag.category-calendar { background: rgba(250, 204, 21, 0.16); color: #fde68a; }
/* Nutzer-Bug-Report (Konnektoren-Redesign): category-accounting fehlte hier komplett - das
   "BUCHHALTUNG"-Badge blieb dadurch unstyled (kein Hintergrund/keine Akzentfarbe). */
.category-tag.category-accounting { background: rgba(45, 212, 191, 0.16); color: #99f6e4; }
.category-tag.category-tasks { background: rgba(96, 165, 250, 0.16); color: #bfdbfe; }
.category-tag.category-other { background: rgba(148, 163, 184, 0.18); color: #e2e8f0; }

.agent-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)); gap: 16px; }
.agent-card { padding: 18px; border: 1px solid var(--border); border-radius: 16px; background: rgba(var(--panel-tint-rgb), 0.55); display: grid; gap: 12px; align-content: start; }
.agent-card-head { display: flex; align-items: center; gap: 12px; }
.agent-card-description { color: var(--muted); font-size: 0.9rem; }
.agent-card-footer { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; }
.agent-detail-inner { display: grid; gap: 8px; }

.agent-log-list { display: grid; gap: 8px; margin-top: 8px; }
/* Nutzerwunsch: "Alles was nach Titel/Datum kommt (Funktionen, Warnungen) soll darunter stehen,
auch unter dem Icon, sodass das Icon nicht eine komplette Spalte auffrisst" - .agent-log-item ist
jetzt eine Spalte (Kopfzeile mit Icon+Titel+Zeit, darunter EINE volle Breite für alles Weitere)
statt einer Zeile mit dauerhaft reserviertem Icon-Rand über die volle Höhe. */
.agent-log-item { display: flex; flex-direction: column; gap: 6px; padding: 10px; border: 1px solid var(--border); border-radius: 12px; background: rgba(var(--panel-tint-rgb), 0.5); }
.agent-log-headline { display: flex; gap: 10px; align-items: center; }
.agent-log-icon { width: 32px; height: 32px; border-radius: 10px; background: rgba(56, 189, 248, 0.14); color: var(--accent); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.agent-log-title-group { display: grid; gap: 2px; font-size: 0.9rem; min-width: 0; }
/* Volle Breite für Warnungen/Pills/Funktionsknöpfe (Nutzerwunsch: "die Funktionen sollen alle in
eine Reihe passen") - ein einziger flex-wrap-Container, .button-row-Kindelemente bleiben darin
selbst kompakte Flex-Items statt eine eigene volle Zeile zu erzwingen. */
.agent-log-extra { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 0.9rem; }

/* Bug-Report: "Popups sind auf mobilen Geräten so hoch, dass sie im Bereich von Uhrzeit/
Batterieanzeige liegen - sollten eigentlich vertikal zentriert sein" - align-items:flex-start
pinnte jedes Modal (außer den eigenen .task-sheet-*-Bottom-Sheets, die dieses Verhalten separat per
.task-sheet-overlay überschreiben) an die Oberkante, nur durch ein festes 40px-Padding vom
Viewport-Rand getrennt - auf einem Gerät mit Notch/Dynamic Island reicht das nicht, um den
sicheren Bereich zu meiden. Vertikale Zentrierung vermeidet das unabhängig vom Gerät. */
/* Nutzer-Bug-Report: bei einem "langen" Popup (mehr Inhalt als Viewport-Höhe) ließ sich zwar
schön scrollen, das Popup war aber oben abgeschnitten und nie von ganz oben erreichbar - klassischer
Flexbox-Fallstrick: align-items:center zentriert ein zu großes Kind symmetrisch über die Ränder des
Containers hinaus, overflow-y:auto auf DIESEM Container kann den so entstandenen oberen Überhang
nicht mehr erreichen (nur den unteren). Fix: die Zentrierung wandert von align-items/justify-content
(am Container) zu margin:auto (am .modal-box selbst) - centert ein Popup weiterhin exakt gleich,
solange es in den Viewport passt (identisches Ergebnis zum vorherigen Fix oben), lässt ein zu großes
Popup aber ganz regulär oben im Scrollbereich beginnen, komplett scrollbar bis ganz nach oben. */
.modal-overlay { display: none; position: fixed; inset: 0; background: rgba(2, 6, 23, 0.82); z-index: 100; padding: 40px 16px; overflow-y: auto; }
.modal-overlay.open { display: flex; }
/* Volldeckend statt var(--panel-2) mit 0.95 Alpha (Nutzer-Screenshot 2026-08-19: "sollte nicht so
ausgegraut/transparent sein, weil ich den Hintergrund durchsehe") - bei einem langen Popup ueber einer
textreichen Seite schimmerte der Inhalt darunter sichtbar durch. */
.modal-box { background: var(--panel-2-solid); border: 1px solid var(--border); border-radius: 20px; padding: 24px; width: 100%; max-width: 560px; box-shadow: 0 30px 80px rgba(2, 6, 23, 0.5); margin: auto; }
.modal-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; }
.modal-close { background: transparent; border: none; color: var(--muted); font-size: 1.4rem; line-height: 1; padding: 4px 8px; cursor: pointer; }
.modal-close:hover { color: var(--text); }

.agent-preview-box { padding: 14px; border: 1px solid rgba(139, 92, 246, 0.35); border-radius: 14px; background: rgba(139, 92, 246, 0.08); margin: 8px 0; }
.play-button { background: linear-gradient(90deg, #22c55e, #16a34a); }

.chat-modal-box { max-width: 640px; display: grid; gap: 14px; }
.agent-edit-modal-box { max-width: 760px; max-height: calc(100vh - 80px); overflow-y: auto; }
.posting-modal-box { max-width: 900px; max-height: calc(100vh - 80px); overflow-y: auto; }
/* Korrespondenz-Vorgang-Popup (Nutzerwunsch: am PC breiter, da langfristig mehrere Kanäle/eine
   längere Chat-Historie derselben Person darin stehen) - schmal genug lassen, um auf schmalen
   Bildschirmen weiterhin normal (max-width:560px von .modal-box) zu bleiben. */
.correspondence-modal-box { max-width: 820px; max-height: calc(100vh - 80px); overflow-y: auto; }
/* "Abschließen" (vormals "Archivieren", Nutzerwunsch: grün markieren, da es ein Abschließen bedeutet)
   - gleiches Modifier-Muster wie .button.secondary.substitute-active (Basis-Klasse bleibt neutral,
   nur diese eine Aktion sticht farblich hervor). */
.button.secondary.correspondence-close-action { border-color: var(--success); color: var(--success); }

/* Beleg-Vorschau (Nutzerwunsch: "Spalte mit einer Kleinansicht des Belegs, quasi wie Vorschau" +
   "im Bearbeiten-Popup gerne dann etwas größere Ansicht ... links dann große Vorschau und rechts davon
   die Funktionen"), buchhaltung_belege.php. Für PDFs ein <embed> statt eines serverseitig gerenderten
   Rasterbilds (bräuchte Imagick+Ghostscript, bewusst nicht Teil dieser App, siehe CLAUDE.md) - Browser
   rendern die erste Seite bereits selbst über ihren eingebauten PDF-Viewer, pointer-events:none lässt
   Klicks zum umschließenden <a> durchreichen (ein <embed> würde Klicks sonst selbst abfangen). */
.accounting-document-thumb { display: inline-flex; align-items: center; justify-content: center; width: 42px; height: 54px; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; background: var(--panel); flex-shrink: 0; }
.accounting-document-thumb embed, .accounting-document-thumb img { width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.accounting-document-thumb i { color: var(--muted); font-size: 1.1rem; }
.accounting-document-preview-split { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
@media (max-width: 900px) {
    .accounting-document-preview-split { grid-template-columns: 1fr; }
}
.accounting-document-preview-large { width: 100%; height: 480px; border: 1px solid var(--border); border-radius: 12px; overflow: hidden; background: var(--panel); display: flex; align-items: center; justify-content: center; }
.accounting-document-preview-large embed, .accounting-document-preview-large img { width: 100%; height: 100%; object-fit: contain; }
.accounting-document-ai-proposal { border: 1px solid var(--accent); border-radius: 14px; padding: 14px; background: rgba(139, 92, 246, 0.08); }

/* Buchungsmaske-Redesign (Nutzerwunsch, Taxpool-Vorbild "schön angeordnet, nicht alles nebeneinander")
   - eigene Klasse statt .membership-filter-grid wiederzuverwenden, damit andere Seiten mit dieser
   generischen Klasse unangetastet bleiben. */
.accounting-booking-form-grid { display: flex; flex-direction: column; gap: 18px; }
.accounting-booking-form-row { display: grid; gap: 12px; align-items: start; }
.accounting-booking-form-row.cols-1 { grid-template-columns: 1fr; }
.accounting-booking-form-row.cols-2 { grid-template-columns: repeat(2, 1fr); }
.accounting-booking-form-row.cols-3 { grid-template-columns: repeat(3, 1fr); }
.accounting-booking-form-row .tax-preview-hint { display: block; margin-top: 4px; font-size: 0.82rem; }
@media (max-width: 760px) {
    .accounting-booking-form-row.cols-2, .accounting-booking-form-row.cols-3 { grid-template-columns: 1fr; }
}

/* Popup-/Formular-Redesign "Buchung bearbeiten"/"Neue Buchung" > Experte (Nutzerwunsch: 2-Spalten-
   Layout, Beleginformationen klar von Buchungsdetails/Kostenstellen/echter Beleg-Datei getrennt) -
   .accounting-booking-form-section bleibt bewusst allgemein (auch für Kostenstellen/Beleg-Datei
   genutzt), nicht nur für die 2-Spalten-Sektionen. */
.accounting-booking-form-columns { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: start; }
.accounting-booking-form-section { border: 1px solid var(--border); border-radius: 12px; padding: 14px; background: var(--panel-2); }
.accounting-booking-form-section-title { margin: 0 0 10px; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); }
.accounting-booking-form-section-hint { font-size: 0.82rem; margin: 0 0 10px; }
.accounting-booking-form-override-row { margin-top: 10px; }
.accounting-booking-form-override-row .inline-checkbox { font-size: 0.85rem; color: var(--muted); }
@media (max-width: 760px) {
    .accounting-booking-form-columns { grid-template-columns: 1fr; }
}
/* Bug-Report: bei "display: grid" streckt "align-content: normal" (verhaelt sich bei Grid wie
"stretch") die impliziten Zeilen ueber die gesamte Container-Hoehe, sobald .chat-transcript-page
(flex: 1) den Container auf volle verfuegbare Hoehe zieht - bei wenigen kurzen Nachrichten wirken
die Abstaende dadurch gleichmaessig ueber die ganze Flaeche verteilt statt am Inhalt orientiert.
"display: flex" mit "justify-content: flex-end" packt die Nachrichten stattdessen unten-buendig
zusammen, wie in jedem Messenger erwartet. */
.chat-transcript { max-height: 50vh; overflow-y: auto; display: flex; flex-direction: column; justify-content: flex-end; gap: 12px; padding: 4px 2px; }
.chat-empty-state { text-align: center; padding: 20px 0; }
.chat-message { display: flex; flex-direction: column; gap: 8px; max-width: 85%; }
.chat-message.chat-user { align-items: flex-end; margin-left: auto; }
.chat-message.chat-assistant { align-items: flex-start; margin-right: auto; }
.chat-bubble { padding: 10px 14px; border-radius: 16px; white-space: pre-wrap; font-size: 0.92rem; line-height: 1.4; }
.chat-bubble p { margin: 0 0 8px; }
.chat-bubble p:last-child { margin-bottom: 0; }
.chat-bubble ul, .chat-bubble ol { margin: 0 0 8px; padding-left: 20px; }
.chat-bubble ul:last-child, .chat-bubble ol:last-child { margin-bottom: 0; }
.chat-bubble li { margin-bottom: 2px; }
.chat-bubble code { background: rgba(var(--panel-tint-rgb), 0.35); padding: 1px 5px; border-radius: 5px; font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: 0.88em; }
.chat-bubble .chat-table-wrap { margin: 0 0 8px; border: 1px solid rgba(148, 163, 184, 0.45); border-radius: 10px; overflow: auto; }
.chat-bubble .chat-table-wrap:last-child { margin-bottom: 0; }
.chat-bubble .chat-table { width: 100%; border-collapse: collapse; font-size: 0.86em; }
.chat-bubble .chat-table th, .chat-bubble .chat-table td { padding: 7px 12px; border-bottom: 1px solid rgba(148, 163, 184, 0.3); }
.chat-bubble .chat-table th:not(:last-child), .chat-bubble .chat-table td:not(:last-child) { border-right: 1px solid rgba(148, 163, 184, 0.3); }
.chat-bubble .chat-table th { background: rgba(148, 163, 184, 0.22); font-weight: 700; border-bottom: 1px solid rgba(148, 163, 184, 0.45); }
.chat-bubble .chat-table tbody tr:last-child td { border-bottom: none; }
.chat-bubble .chat-table tbody tr:nth-child(even) { background: rgba(148, 163, 184, 0.08); }
.chat-user .chat-bubble .chat-table-wrap { border-color: rgba(255, 255, 255, 0.45); }
.chat-user .chat-bubble .chat-table th, .chat-user .chat-bubble .chat-table td { border-color: rgba(255, 255, 255, 0.3); }
.chat-user .chat-bubble .chat-table th { background: rgba(255, 255, 255, 0.16); }
.chat-user .chat-bubble .chat-table tbody tr:nth-child(even) { background: rgba(255, 255, 255, 0.08); }
.chat-user .chat-bubble { background: linear-gradient(90deg, var(--accent), var(--accent-2)); color: white; border-bottom-right-radius: 4px; }
.chat-assistant .chat-bubble { background: rgba(148, 163, 184, 0.14); color: var(--text); border-bottom-left-radius: 4px; }
.chat-timestamp { font-size: 0.72rem; color: var(--muted); margin-top: -4px; }
.chat-thinking-bubble { display: flex; align-items: center; gap: 10px; }
.chat-thinking-spinner { display: inline-flex; gap: 4px; flex-shrink: 0; }
.chat-thinking-spinner span { width: 6px; height: 6px; border-radius: 50%; background: var(--muted); animation: chat-thinking-bounce 1.2s infinite ease-in-out both; }
.chat-thinking-spinner span:nth-child(1) { animation-delay: -0.24s; }
.chat-thinking-spinner span:nth-child(2) { animation-delay: -0.12s; }
.chat-thinking-text { color: var(--muted); font-size: 0.88rem; font-style: italic; }
@keyframes chat-thinking-bounce {
    0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
    40% { transform: scale(1); opacity: 1; }
}
.chat-diff-card { width: 100%; border: 1px solid rgba(139, 92, 246, 0.35); border-radius: 12px; background: rgba(139, 92, 246, 0.08); overflow: hidden; }
.chat-diff-head { padding: 8px 12px; font-size: 0.8rem; font-weight: 600; color: #ddd6fe; border-bottom: 1px solid rgba(139, 92, 246, 0.25); display: flex; align-items: center; gap: 6px; }
.chat-diff-body { padding: 8px 12px; display: grid; gap: 2px; font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-size: 0.82rem; white-space: pre-wrap; }
.diff-line::before { display: inline-block; width: 1.2em; }
.diff-line.diff-same { color: var(--muted); }
.diff-line.diff-same::before { content: "  "; }
.diff-line.diff-added { color: #bbf7d0; background: rgba(34, 197, 94, 0.12); }
.diff-line.diff-added::before { content: "+ "; }
.diff-line.diff-removed { color: #fecdd3; background: rgba(251, 113, 133, 0.12); text-decoration: line-through; }
.diff-line.diff-removed::before { content: "− "; }
.telegram-send-card { border-color: rgba(34, 197, 94, 0.35); background: rgba(34, 197, 94, 0.08); }
.telegram-send-card .chat-diff-head { color: #bbf7d0; border-bottom-color: rgba(34, 197, 94, 0.25); }
.telegram-send-card.telegram-send-failed { border-color: rgba(251, 113, 133, 0.35); background: rgba(251, 113, 133, 0.08); }
.telegram-send-card.telegram-send-failed .chat-diff-head { color: #fecdd3; border-bottom-color: rgba(251, 113, 133, 0.25); }
.telegram-send-card .chat-diff-body { white-space: normal; font-family: inherit; }
.email-search-card { border-color: rgba(56, 189, 248, 0.35); background: rgba(56, 189, 248, 0.08); }
.email-search-card .chat-diff-head { color: #bae6fd; border-bottom-color: rgba(56, 189, 248, 0.25); }
.email-search-card.email-search-failed { border-color: rgba(251, 113, 133, 0.35); background: rgba(251, 113, 133, 0.08); }
.email-search-card.email-search-failed .chat-diff-head { color: #fecdd3; border-bottom-color: rgba(251, 113, 133, 0.25); }
.email-search-card .chat-diff-body { white-space: normal; font-family: inherit; display: grid; gap: 4px; }
.email-search-result-row { font-size: 0.85rem; }
.image-generation-card { border-color: rgba(139, 92, 246, 0.35); background: rgba(139, 92, 246, 0.08); }
.image-generation-card .chat-diff-head { color: #ddd6fe; border-bottom-color: rgba(139, 92, 246, 0.25); }
.image-generation-card.image-generation-failed { border-color: rgba(251, 113, 133, 0.35); background: rgba(251, 113, 133, 0.08); }
.image-generation-card.image-generation-failed .chat-diff-head { color: #fecdd3; border-bottom-color: rgba(251, 113, 133, 0.25); }
.image-generation-card .chat-diff-body { white-space: normal; font-family: inherit; }
.image-generation-preview { max-width: 100%; max-height: 320px; border-radius: 10px; display: block; }
.chat-composer { display: flex; flex-direction: column; gap: 6px; }
.chat-input-row { display: flex; gap: 10px; align-items: flex-end; }
.chat-input-row textarea { flex: 1; resize: vertical; }
.chat-input-row button { flex-shrink: 0; padding: 12px 16px; }
.chat-attach-btn { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 10px; border: none; background: transparent; color: var(--muted); cursor: pointer; }
.chat-attach-btn:hover { background: rgba(148, 163, 184, 0.1); color: var(--text); }
.chat-attach-btn.has-file { color: var(--accent); }
.chat-attach-preview { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; padding: 3px 10px; border-radius: 999px; background: rgba(148, 163, 184, 0.1); color: var(--muted); font-size: 0.78rem; }
.chat-attach-preview .remove-attachment { background: transparent; border: none; color: var(--muted); cursor: pointer; padding: 0 2px; font-size: 0.9rem; line-height: 1; }
.chat-attach-preview .remove-attachment:hover { color: var(--text); }
.chat-composer.drag-over { outline: 2px dashed var(--accent); outline-offset: 4px; border-radius: 14px; }

/* Personen-Chat: Bild-/Datei-Anhang in der Sprechblase, Zitat-Block bei "Antworten", Antworten-/
   Emoji-/Formatierungs-Composer-Erweiterungen (chat.php Personen-Zweig, initPersonChat() in
   assets/js/app.js). */
.chat-bubble-text p:first-child { margin-top: 0; }
.chat-reply-quote { display: flex; flex-direction: column; gap: 2px; padding: 6px 10px; margin-bottom: 6px; border-left: 3px solid var(--accent); border-radius: 6px; background: rgba(148, 163, 184, 0.12); font-size: 0.82rem; cursor: pointer; max-width: 100%; }
.chat-reply-quote-sender { font-weight: 600; color: var(--accent); }
.chat-reply-quote-snippet { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; max-width: 100%; }
.chat-user .chat-reply-quote { background: rgba(255, 255, 255, 0.16); border-left-color: rgba(255, 255, 255, 0.7); }
.chat-user .chat-reply-quote-sender { color: white; }
.chat-user .chat-reply-quote-snippet { color: rgba(255, 255, 255, 0.75); }

.chat-attachment-image { max-width: 100%; max-height: 280px; border-radius: 10px; display: block; margin-bottom: 6px; }
.chat-attachment-image-link { display: inline-block; }
.chat-attachment-file { display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; border-radius: 10px; background: rgba(148, 163, 184, 0.14); color: inherit; font-size: 0.86rem; margin-bottom: 6px; }
.chat-user .chat-attachment-file { background: rgba(255, 255, 255, 0.16); }

.chat-message-meta { display: flex; align-items: center; gap: 6px; margin-top: -4px; }
.chat-message.chat-user .chat-message-meta { flex-direction: row-reverse; }
.chat-reply-btn { background: transparent; border: none; color: var(--muted); cursor: pointer; padding: 2px 5px; font-size: 0.78rem; border-radius: 6px; opacity: 0.7; }
.chat-reply-btn:hover { opacity: 1; background: rgba(148, 163, 184, 0.14); color: var(--text); }

.chat-message-highlight { animation: chat-message-highlight-flash 1.5s ease; border-radius: 10px; }
@keyframes chat-message-highlight-flash {
    0% { background: rgba(56, 189, 248, 0.22); }
    100% { background: transparent; }
}

.chat-reply-preview { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border-radius: 10px; background: rgba(148, 163, 184, 0.1); color: var(--muted); font-size: 0.82rem; border-left: 3px solid var(--accent); }
.chat-reply-preview-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.chat-reply-preview-sender { font-weight: 600; color: var(--text); }
.chat-reply-preview-snippet { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-reply-preview .remove-reply-preview { background: transparent; border: none; color: var(--muted); cursor: pointer; font-size: 0.9rem; flex-shrink: 0; }
.chat-reply-preview .remove-reply-preview:hover { color: var(--text); }

/* Dezenter Composer (Nutzerfeedback: "die 5 Buttons dezenter", Formatierung nur bei markiertem
   Text, Anhang/Emoji als Overlay im Textfeld selbst statt eigener Zeile) - .chat-textarea-wrap
   ersetzt die Textarea als direktes Flex-Kind von .chat-input-row, Anhang/Emoji sitzen als kleines
   Overlay unten rechts IM Textfeld, die Formatierungs-Buttons erscheinen nur oberhalb, solange eine
   Textauswahl besteht (initPersonChat() misst textarea.selectionStart/End bei mouseup/keyup/select). */
.chat-textarea-wrap { position: relative; flex: 1; display: flex; flex-direction: column; min-width: 0; }
.chat-textarea-wrap textarea { width: 100%; box-sizing: border-box; resize: vertical; padding-right: 78px; }

/* Nutzer-Feedback nach Browser-Test: die Toolbar soll das Textfeld überlagern statt es beim
   Einblenden nach unten zu verschieben - jetzt wie .chat-inline-actions ein Overlay INNERHALB von
   .chat-textarea-wrap (position: relative), oben rechts statt unten rechts, mit deckendem
   Hintergrund (0.5 Alpha war über getipptem Text schwer lesbar) + Schatten zur Abhebung. */
.chat-format-toolbar { position: absolute; top: 6px; right: 6px; z-index: 5; display: flex; gap: 2px; width: fit-content; padding: 3px; border-radius: 8px; background: rgba(15, 23, 42, 0.94); border: 1px solid var(--border); box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35); }
.chat-format-toolbar .chat-format-btn { width: 26px; height: 26px; padding: 0; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; border-radius: 6px; border: none; background: transparent; color: var(--muted); cursor: pointer; font-size: 0.72rem; }
.chat-format-toolbar .chat-format-btn:hover { background: rgba(148, 163, 184, 0.18); color: var(--text); }

.chat-inline-actions { position: absolute; right: 4px; bottom: 4px; display: flex; align-items: center; gap: 2px; }
.chat-inline-actions .chat-attach-btn, .chat-inline-actions .chat-emoji-btn { width: 28px; height: 28px; padding: 0; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; border-radius: 7px; border: none; background: transparent; color: var(--muted); cursor: pointer; opacity: 0.7; font-size: 0.85rem; }
.chat-inline-actions .chat-attach-btn:hover, .chat-inline-actions .chat-emoji-btn:hover { opacity: 1; background: rgba(148, 163, 184, 0.16); color: var(--text); }
.chat-inline-actions .chat-attach-btn.has-file { color: var(--accent); opacity: 1; }

.chat-emoji-wrap { position: relative; flex-shrink: 0; }
.chat-emoji-picker { position: absolute; bottom: 34px; right: 0; width: 250px; max-height: 200px; overflow-y: auto; display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px; padding: 8px; border-radius: 12px; border: 1px solid var(--border); background: var(--panel-2); box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35); z-index: 40; }
.chat-emoji-option { background: transparent; border: none; font-size: 1.1rem; line-height: 1; padding: 4px; border-radius: 6px; cursor: pointer; }
.chat-emoji-option:hover { background: rgba(148, 163, 184, 0.16); }

/* Nutzerwunsch: Modul-Chrome ("Modul: Chat"/"Chats") + volle Konversationsliste sollen mobil nicht
   dauerhaft Platz wegnehmen (WhatsApp-Vorbild) - dezenter "Alle Chatpartner"-Pfeil statt einer
   permanenten Liste/Leiste über der aktiven Konversation. Nur relevant, wenn tatsächlich eine
   Konversation ausgewählt ist (.chat-has-selection, siehe chat.php) - ohne jede Konversation bleibt
   die Liste (mit dem "+"-Button) die sinnvollere Standardansicht. */
.chat-mobile-back { display: none; }

/* Volle-Höhe App-Pane statt einer mit dem Inhalt wachsenden Karte (Telegram-Web-Gefühl): fixe
   Gesamthöhe, Sidebar und Transkript scrollen jeweils unabhängig, Eingabezeile bleibt unten fix. */
.chat-page { display: flex; height: calc(100vh - 160px); min-height: 420px; padding: 0; overflow: hidden; }
.chat-sidebar { width: 280px; flex-shrink: 0; border-right: 1px solid var(--border); padding: 22px 16px 22px 22px; overflow-y: auto; }
.chat-agent-list { display: grid; gap: 6px; margin-top: 12px; }
.chat-agent-item { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 12px; color: var(--text); }
.chat-agent-item:hover { background: rgba(148, 163, 184, 0.1); }
.chat-agent-item.active { background: rgba(56, 189, 248, 0.14); }
.chat-agent-avatar { width: 36px; height: 36px; border-radius: 50%; background: rgba(56, 189, 248, 0.14); color: var(--accent); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; object-fit: cover; }
.chat-agent-info { display: grid; gap: 2px; min-width: 0; }
.chat-agent-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-agent-status { font-size: 0.78rem; }
.chat-main { flex: 1; display: flex; flex-direction: column; min-width: 0; padding: 22px 22px 16px; min-height: 0; }
.chat-main-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-shrink: 0; }
.chat-transcript-page { flex: 1; min-height: 0; height: auto; max-height: none; }
.chat-main .chat-composer { flex-shrink: 0; margin-top: 12px; }

/* Korrespondenz-Messenger-Übersicht (Nutzerwunsch: "wie ein Messenger... links eine Spalte mit den
   offenen Vorgängen, wenn man draufdrückt öffnet sich rechts der Verlauf") - gleiches Grundmuster wie
   .chat-page/.chat-sidebar (siehe Kommentar oben), bewusst eigene, PARALLELE Klassen statt Wieder-
   verwendung derselben Namen (Codebase-Konvention, siehe auch .settings-page/.settings-sidebar/
   .task-desktop-page - jede Split-View-Seite bekommt ihre eigenen Klassen, keine geteilten). */
.correspondence-page { display: flex; height: calc(100vh - 160px); min-height: 420px; padding: 0; overflow: hidden; }
.correspondence-sidebar { width: 320px; flex-shrink: 0; border-right: 1px solid var(--border); padding: 18px 14px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; }
.correspondence-sidebar-header { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.correspondence-sidebar-header h1 { margin: 0; font-size: 1.1rem; }
.correspondence-filter-bar { display: flex; flex-direction: column; gap: 6px; }
.correspondence-filter-bar .filter-row-inline { display: flex; gap: 6px; }
.correspondence-filter-bar select, .correspondence-filter-bar input[type="text"] { padding: 6px 10px; font-size: 0.82rem; }
.correspondence-thread-list { display: grid; gap: 3px; }
.correspondence-thread-row { display: block; padding: 9px 10px; border-radius: 10px; color: var(--text); }
.correspondence-thread-row:hover { background: rgba(148, 163, 184, 0.1); text-decoration: none; }
.correspondence-thread-row.active { background: rgba(56, 189, 248, 0.14); }
.correspondence-thread-row-top { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.correspondence-thread-subject { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0; }
.correspondence-thread-meta { font-size: 0.74rem; color: var(--muted); display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-top: 3px; }
.correspondence-thread-number { color: var(--muted); }
.correspondence-main { flex: 1; display: flex; flex-direction: column; min-width: 0; padding: 18px 22px 16px; min-height: 0; overflow-y: auto; }
.correspondence-main-placeholder { flex: 1; display: flex; align-items: center; justify-content: center; color: var(--muted); text-align: center; padding: 20px; }
.correspondence-mobile-back { display: none; }
/* Betreff inline editierbar (Punkt 7: Stift-Icon statt aufgeblähtem Popup). */
.correspondence-subject-edit { display: flex; align-items: center; gap: 6px; }
.correspondence-subject-edit h2 { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.correspondence-subject-edit-controls { display: flex; align-items: center; gap: 4px; flex: 1; min-width: 0; }
.correspondence-subject-input { flex: 1; min-width: 0; padding: 4px 8px; font-size: 1.05rem; font-weight: 600; }
/* Nutzer-Feedback: "kann kleiner sein und ohne Rahmen, einfach nur kleiner Stift" - ersetzt die
   vorherige .button.secondary.small-button.icon-button-Kombination (1px Rahmen, 32x32px). */
.correspondence-subject-edit-btn { flex-shrink: 0; width: 18px; height: 18px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border: none; background: transparent; color: var(--muted); cursor: pointer; font-size: 0.68rem; border-radius: 4px; }
.correspondence-subject-edit-btn:hover { color: var(--text); background: rgba(148, 163, 184, 0.16); }

.log-filter-row { display: flex; gap: 14px; flex-wrap: wrap; align-items: end; margin-bottom: 18px; }
.log-filter-row label { min-width: 160px; }
.list-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: nowrap; margin-bottom: 8px; }
.list-toolbar select { width: auto; padding: 6px 12px; flex-shrink: 0; }
.log-pagination { justify-content: center; align-items: center; margin-top: 16px; }
.button.disabled, span.button.disabled { opacity: 0.4; pointer-events: none; cursor: default; }

.file-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; }
.file-card { border: 1px solid var(--border); border-radius: 14px; background: rgba(var(--panel-tint-rgb), 0.5); overflow: hidden; display: grid; grid-template-rows: 140px auto; }
.file-card-preview { width: 100%; height: 140px; object-fit: cover; display: block; background: rgba(var(--panel-tint-rgb), 0.8); }
.file-card-preview-icon { display: flex; align-items: center; justify-content: center; font-size: 2.4rem; color: var(--muted); }
.file-card-body { padding: 10px 12px; display: grid; gap: 6px; align-content: start; }
.file-card-name { font-size: 0.88rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Einstellungen: Kategorien-Sidebar + Suchfeld (VS-Code-Stil), gleiches Grundmuster wie
   .chat-page/.chat-sidebar (eigenständig scrollende Spalten statt einer mit dem Inhalt
   wachsenden Karte). */
.settings-page { display: flex; min-height: 520px; padding: 0; overflow: hidden; }
.settings-sidebar { width: 260px; flex-shrink: 0; border-right: 1px solid var(--border); padding: 22px 14px 22px 22px; overflow-y: auto; }
.settings-search { margin-bottom: 14px; }
.settings-nav { display: grid; gap: 4px; }
.settings-nav-group-label { margin: 14px 0 2px; padding: 0 12px; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
.settings-nav-group-label:first-child { margin-top: 0; }
.settings-nav-item { display: block; width: 100%; text-align: left; padding: 10px 12px; border-radius: 12px; border: none; background: transparent; color: var(--text); font: inherit; cursor: pointer; }
.settings-nav-item:hover { background: rgba(148, 163, 184, 0.1); }
.settings-nav-item.active { background: rgba(56, 189, 248, 0.14); color: var(--accent); font-weight: 600; }
.settings-nav-empty { color: var(--muted); font-size: 0.85rem; padding: 10px 12px; }
/* Der Ein-/Ausklapp-Umschalter der Seitenleiste ist ein reines Mobil-Bedienelement -
   initSettingsSidebarCollapse() (assets/js/app.js) hängt ihn aber IMMER ein. Ohne diese Basisregel
   blieb er auf breiten Bildschirmen ein ungestylter <button>, den das App-Button-Styling als
   goldenen Pfeil ohne jede Funktion darstellte (real gemeldet). Die Media-Query weiter unten setzt
   ihn für schmale Bildschirme wieder auf `display: flex` - sie steht später in der Datei und
   gewinnt dort. */
.settings-sidebar-toggle { display: none; }
/* Nur die zugeklappte mobile Leiste zeigt den Anfangsbuchstaben statt der Beschriftung. */
.settings-nav-initial { display: none; }
.settings-main { flex: 1; min-width: 0; padding: 22px 26px; overflow-y: auto; }
.settings-panel { display: none; }
.settings-panel.active { display: block; }
.settings-panel h2 { margin-top: 0; }

.oauth-connect-box { margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--border); }
.redirect-uri-box { display: block; margin-top: 8px; padding: 10px 12px; border-radius: 10px; background: rgba(var(--panel-tint-rgb), 0.6); border: 1px solid var(--border); font-size: 0.85rem; word-break: break-all; color: var(--accent); }

@media (max-width: 760px) {
    .hero, .section-title { flex-direction: column; align-items: flex-start; }
    .app-shell { padding: max(12px, env(safe-area-inset-top)) max(12px, env(safe-area-inset-right)) 12px max(12px, env(safe-area-inset-left)); }
    /* Mobil: Logo mittig statt der vollen, umbrechenden Menüzeile (Nutzerwunsch - die bisherige
    "alles untereinander stapeln"-Regel für .topbar liess die vielen Nav-Dropdowns unübersichtlich
    wirken). .nav-links bleibt im Layout-Fluss vollständig unverändert für Desktop/Tablet
    (min-width: 761px) - nur hier per display:none ausgeblendet und über den neuen Hamburger-Button
    als eigenständiges Dropdown-Panel eingeblendet.
    WICHTIG: .nav-links hängt hier bewusst per position:absolute AUSSERHALB des normalen Flusses
    (wie .account-dropdown) statt die Zeile nach unten aufzustoßen - eine frühere Version ließ
    .topbar dafür mitwachsen (flex-wrap:wrap), wodurch der ebenfalls absolut positionierte
    #navMobileToggle (top:50%, relativ zur GESAMTEN, jetzt viel höheren .topbar-Box) mitten ins
    aufgeklappte Menü rutschte statt neben dem Logo zu bleiben - siehe Bug-Report "Menu Icon
    überlagert". Mit position:absolute bleibt die Höhe von .topbar konstant (nur die Logo-Zeile),
    wodurch top:50% zuverlässig auf dieser Zeile zentriert. */
    /* Nutzerwunsch/Bug-Report: "Menü oben ... scrollt mit nach oben ein paar Zentimeter, bis es
    oben andockt - das sollte nicht so sein, es sollte direkt schon stehen bleiben". position:sticky
    "andockt" erst, sobald die normale (nicht-gestickte) Position des Elements über seinen `top`-Wert
    hinausgescrollt wurde - lag hier bei ein paar Pixeln Differenz zwischen .app-shells Padding und
    dem sticky-`top`, sichtbar als kurzes Nachrutschen. position:fixed kennt dieses Problem
    prinzipiell nicht (immer exakt an derselben Bildschirmposition, ab dem allerersten Frame) -
    dasselbe, bereits bewährte Muster wie .task-bottom-nav/.task-fab. .app-shell bekommt dafür ein
    von initTaskStickyHeaders() (assets/js/app.js) gemessenes Top-Padding, damit nichts unter der
    jetzt aus dem Fluss genommenen Kopfzeile verschwindet. */
    .topbar {
        justify-content: center;
        position: fixed;
        left: max(12px, env(safe-area-inset-left));
        right: max(12px, env(safe-area-inset-right));
        top: max(12px, env(safe-area-inset-top));
    }
    .app-shell { padding-top: var(--mobile-top-reserved, 84px); }
    /* Seiten ohne Kopfzeile ($__suppressTopNav in views/header.php) brauchen den reservierten Platz
       nicht - sonst beginnt der Inhalt 84px unter dem oberen Rand, ohne dass dort etwas steht. */
    .app-shell-no-topbar { padding-top: max(12px, env(safe-area-inset-top)); }
    /* Login-/Aufnahme-Karte mobil dichter an den oberen Rand (Nutzer-Screenshot 2026-08-19): dort
       kommt zur Kartenmarge noch die Sicherheitszone des Geräts, zusammen war das eine leere
       Bildschirmhälfte. Auf dem Desktop bleibt der größere Abstand. */
    .auth-card { margin-top: 4px; }
    /* Aus-/Einblenden beim Scrollen (initTopbarScrollHide(), assets/js/app.js). Bewusst per
    transform statt display/visibility: das Element bleibt im Layout, der Platz darunter springt
    nicht, und die Animation läuft auf der Grafikkarte. */
    .topbar { transition: transform 0.22s ease, opacity 0.22s ease; }
    .topbar.topbar-hidden { transform: translateY(calc(-100% - max(12px, env(safe-area-inset-top)))); opacity: 0; pointer-events: none; }

    /* Zwischen Icon und Beschriftung fehlte im aufgeklappten Seitenmenü das Leerzeichen: der
    Textknoten davor wandert beim Kapseln mit in die Beschriftung, und führende Leerzeichen am
    Anfang eines inline-block werden verworfen. Deshalb inline (behält das Leerzeichen) plus ein
    fester Abstand am Icon, damit es unabhängig vom Markup stimmt. */
    .settings-page.settings-sidebar-open .settings-nav-item i { margin-right: 8px; }

    /* (Die mobilen Favoriten-Stern-Regeln stehen bewusst weiter unten in diesem Block - siehe die
       Begründung dort: eine spätere, gleich spezifische Regel hätte sie sonst überschrieben.) */
    /* Architektur-Umbau (Nutzer-Bug-Report, mehrere Runden): "über dem Gradienten sieht man dann doch
    wieder die Einträge" - der bisherige Ansatz (Kopfzeilen sticky/fixed ÜBER einer normal
    scrollenden Seite + ein Fade-Verlauf, der die durchscheinenden Zeilen kaschieren sollte) konnte
    prinzipiell nicht robust funktionieren: .task-topbar ist bewusst transparent (Nutzerentscheidung),
    jede Zeile, die beim Scrollen HINTER die Kopfzeile wandert, blieb dort trotz Fade voll sichtbar,
    da der Fade nur einen schmalen Streifen GENAU an der Kopfzeilen-Unterkante kaschierte, nicht die
    ganze (transparente!) Kopfzeilen-Fläche selbst. Nutzervorschlag, umgesetzt: die Liste bekommt
    einen EIGENEN, nach oben (Unterkante der Kopfzeilen) begrenzten Scroll-Container statt die ganze
    Seite scrollen zu lassen - genau das bereits bestehende Muster von .task-desktop-list-body (dort
    trat das Problem nie auf, siehe Nutzer-Beobachtung "in der breiten Ansicht ist das Problem nicht
    da"). .task-page wird dafür selbst zur fixen, nach oben/unten begrenzten Box - .task-topbar/
    .task-timeline-strip/ein evtl. Suchformular werden darin zu ganz normalen, NICHT mehr sticky/
    fixed positionierten Fluss-Kindern (sie können den scrollenden Bereich dadurch nie mehr
    überlappen, weil dieser body class="task-list-area" jetzt textuell UNTER ihnen beginnt und einen
    eigenen overflow:hidden-Rahmen hat) - nur .task-list-area selbst scrollt noch
    (overflow-y:auto). Die tatsächliche .task-page-Regel dafür steht bewusst NICHT hier, sondern erst
    NACH ihrer unconditional Basisregel (`position:relative; padding-bottom:100px;`) weiter unten im
    Stylesheet - exakt dieselbe Cascade-Reihenfolge-Falle wie bei .task-topbar/.task-list-section-
    header (siehe dortige Kommentare), ein früherer Versuch, die Regel hier direkt zu platzieren,
    wurde deshalb von der später folgenden Basisregel wieder verdrängt. */
    /* Nutzerwunsch: "Hamburger-Menü ... rechts anordnen, links oben Zurück-Button" (PWA-Standalone-
    Modus kennt keinen Browser-Zurück) - Hamburger wandert nach rechts, #navBackToggle (siehe
    initNavBackButton(), assets/js/app.js) erscheint links, sobald window.history.length > 1. */
    .nav-mobile-toggle { display: block; position: absolute; right: 14px; top: 50%; transform: translateY(-50%); }
    /* Bug-Report (Desktop): initNavBackButton() (assets/js/app.js) setzte bisher eine INLINE
    style.display, die schwerer wiegt als jede CSS-Regel - der Button blieb dadurch auch am PC
    sichtbar (kein absolutes Positionieren dort, landete einfach vorne in der Flex-Reihe, "Logo
    weiterhin linksbündig" war dadurch verletzt). Jetzt setzt die JS nur noch eine Klasse
    (.nav-back-visible), die NUR hier (innerhalb dieser Mobile-Media-Query) etwas bewirkt - am PC
    bleibt die app-weite Basisregel display:none unangetastet. */
    .nav-back-toggle.nav-back-visible { display: inline-flex; position: absolute; left: 14px; top: 50%; transform: translateY(-50%); }
    .nav-links {
        display: none;
        position: absolute;
        top: calc(100% + 10px);
        left: 0;
        right: 0;
        flex-direction: column;
        /* Echter Bug (Nutzer-Screenshot 2026-08-16): ein Untermenü mit vielen Einträgen brach in eine
        ZWEITE SPALTE rechts neben "Zurück" um, statt zu scrollen - und liess sich dann auch nicht
        mehr scrollen. Ursache ist die Basisregel `.nav-links { flex-wrap: wrap }` (dort sinnvoll für
        die waagerechte Desktop-Zeile): zusammen mit flex-direction:column und der max-height weiter
        unten weicht ein Flex-Container bei Platzmangel eben zur Seite aus, statt überzulaufen -
        overflow-y:auto konnte deshalb nie greifen, weil vertikal gar nichts überlief. Mit nowrap
        läuft die Liste wieder nach unten über und scrollt tatsächlich. Bei wenigen Einträgen fiel es
        nie auf, weil dort kein Umbruch nötig war. */
        flex-wrap: nowrap;
        align-items: stretch;
        gap: 4px;
        /* Nutzerwunsch/Bug-Report: "Hamburger-Menü sollte nicht transparent sein, man sieht die
        Schrift noch im Hintergrund dahinter" - var(--panel-2) ist bei 95% Deckkraft definiert
        (für die vielen anderen, kleineren Dropdowns der App gedacht) - für dieses große,
        vollflächige Menü reichte das nicht aus. --panel-2-solid (:root, dieselbe Farbe wie
        --panel-2 nur ohne Alpha) statt eines hart codierten Werts - Bug-Report: ein zunächst hart
        codiertes rgb(30,41,59) ignorierte das "Schwarz/Gold"-Farbschema (dessen --panel-2 ist grau,
        nicht blau) und wirkte dort "bläulich" statt zum Rest des Menüs passend. */
        background: var(--panel-2-solid);
        border: 1px solid var(--border);
        border-radius: 16px;
        box-shadow: 0 20px 50px rgba(2, 6, 23, 0.4);
        padding: 10px;
        z-index: 50;
        /* Nutzer-Bug-Report 2026-08-18: "es fehlen ein paar Pixel, 'Einstellungen' ist nicht
           sichtbar. Scrolle ich weiter, verschwindet das Menue, weil das System denkt ich scrolle
           den Hauptinhalt." - zwei Ursachen:
           (a) Das Menue haengt per position:absolute UNTER der Kopfzeile (top: 100% + 10px), die
               Hoehe wurde aber vom GANZEN Viewport abgezogen. Die 100px waren geraten und decken
               Kopfzeilenhoehe + Abstand + Sicherheitsbereich auf einem Geraet mit Aussparung nicht
               ab - der letzte Eintrag lag genau darunter. 100dvh statt 100vh rechnet zusaetzlich
               die ein- und ausfahrende Browserleiste mit, die auf iOS sonst dauerhaft fehlt.
           (b) Am Ende des inneren Scrollbereichs reicht die Geste an die Seite weiter
               (Scroll-Verkettung) - genau das Wegscrollen des Menues. `overscroll-behavior: contain`
               beendet die Geste stattdessen am Menue. */
        max-height: calc(100vh - 150px);
        max-height: calc(100dvh - 150px);
        overscroll-behavior: contain;
        /* Der letzte Eintrag soll nicht bündig am Rand kleben - sonst wirkt er abgeschnitten, auch
           wenn er vollständig da ist. */
        padding-bottom: 22px;
        overflow-y: auto;
    }
    .nav-links.nav-links-open { display: flex; }
    /* Jedes .account-menu (z.B. "Studioverwaltung", aber auch "Mein Konto"/"Ansicht als") bleibt auf
    Desktop ein per position:absolute schwebendes Flyout (.account-dropdown) - innerhalb des mobilen,
    bereits scrollenden .nav-links (overflow-y:auto oben) erzeugte das eine verschachtelte zweite
    Scrollbar (Bug-Report: "Dropdown im Menü löst Scrollbar im Container aus"). Mobil deshalb auf ein
    normales In-Flow-Element umgestellt, statt weiter absolut zu schweben - wächst dadurch einfach im
    ohnehin schon scrollenden .nav-links nach unten, nur noch EIN Scrollbereich.
    Nutzer-Bug-Report: "Mein Konto" klappte weiterhin als schwebende Karte mit Schatten auf, weil
    dieser Selektor zuvor `.nav-dropdown .account-dropdown` hieß - #accountMenu trägt aber BEWUSST
    KEIN `.nav-dropdown` (das steuert auf Desktop separat die Links-/statt Rechtsbündigkeit, siehe
    `.nav-dropdown .account-dropdown` weiter oben außerhalb dieser Media-Query - "Mein Konto" sitzt
    ganz rechts in der Topbar und braucht dort bewusst weiterhin rechtsbündig). Hier (nur mobil) auf
    das allgemeinere `.account-menu` umgestellt, damit die In-Flow-Drilldown-Optik AUSNAHMSLOS jedes
    Menü betrifft, ohne die Desktop-Ausrichtung von "Mein Konto" zu beeinflussen. Bewusst mit
    "#navLinks"-Präfix (nicht app-weit) - .account-menu/.account-dropdown werden inzwischen auch
    außerhalb der Kopfzeile wiederverwendet (z. B. das "..."-Menü im Aufgaben-Detail-Sheet), die
    dort soll ihr normales schwebendes Desktop-Verhalten behalten, auch mobil. */
    #navLinks .account-menu .account-dropdown {
        position: static;
        box-shadow: none;
        border: none;
        background: transparent;
        padding: 4px 0 4px 22px;
        min-width: 0;
        margin: 0;
    }
    #navLinks .account-menu .account-dropdown a,
    #navLinks .account-menu .account-dropdown-link-button { padding: 8px 10px; font-size: 0.85rem; }
    /* Nutzerwunsch: "Einträge sind zu dicht aneinander, mach den Abstand eher wie bei 'Aufgaben' und
    'Meine Gruppen'" - die Top-Level-Buttons (Agenten/Chat/.../Mein Konto) hatten mobil KEIN
    eigenes Zeilen-Padding (nur der 4px-gap von .nav-links trennte sie), im Gegensatz zu
    .nav-shortcut-link. Jetzt gleiche Zeilen-Optik: volle Breite, linksbündig, 10px/12px Padding. */
    .nav-links > .account-menu > .account-button { width: 100%; justify-content: flex-start; padding: 10px 12px; border-radius: 10px; font-size: 1rem; font-weight: 400; gap: 6px; }
    .nav-links > .account-menu > .account-button:hover { background: rgba(148, 163, 184, 0.12); }
    /* Nutzerwunsch: "die Texte der Obermenüpunkte sollten alle auf der gleichen Stelle stehen, im
    Moment sind sie verschieden eingerückt, weil die Icons verschieden breit sind" - jedes führende
    Icon bekommt einen festen Breiten-Container statt seiner natürlichen (je nach Glyph
    unterschiedlichen) Breite, damit die Beschriftung app-weit an derselben X-Position beginnt. Nur
    das FÜHRENDE Icon (:first-child) - der nachgestellte Pfeil rechts bleibt unangetastet. */
    .nav-links > .account-menu > .account-button > i:first-child { display: inline-block; width: 20px; text-align: center; flex-shrink: 0; }
    /* Nutzerwunsch: "der Pfeil nach unten wird angezeigt, was nicht sein sollte" - mobil ist das
    Verhalten ein Drilldown (neue Bildschirm-Ebene), kein Inline-Aufklappen, deshalb rechts- statt
    abwärtszeigender Pfeil (dieselbe Ikone um -90° gedreht statt eines zweiten Icon-Sets).
    Nutzerwunsch (Nachtrag): "ich finde es gut, dass der Pfeil bei 'Konto' rechtsbündig ist, das
    kann man bei allen übernehmen" - margin-left:auto schiebt den Pfeil app-weit an den rechten
    Rand der Zeile, statt direkt nach dem Label zu stehen (identisches Verhalten zu Konto weiter
    unten, das dasselbe schon vorher hatte). */
    .nav-dropdown-toggle .fa-chevron-down { display: inline-block; margin-left: auto; transform: rotate(-90deg); }
    /* Nutzerwunsch: "'Konto' soll die gleiche Größe haben wie die anderen Obermenüpunkte, auch mit
    einem passenden Icon" - eigenes, nur mobil sichtbares Icon (Desktop zeigt weiterhin den Avatar,
    siehe .account-button-mobile-icon-Basisregel neben .account-button-mobile-label). Gleiche feste
    Icon-Breite wie oben, damit "Konto" an derselben Stelle wie die übrigen Beschriftungen beginnt.
    Der 6px-gap oben auf .account-button gilt gleichermaßen fürs Konto-Icon (Konto nutzt nur
    .account-button, nicht .nav-dropdown-toggle, das seinen eigenen 6px-gap mitbringt - ohne den
    gemeinsamen gap oben stand "Konto" zuvor ohne jeden Abstand direkt am Icon, im Gegensatz zu den
    übrigen Menüpunkten). */
    #accountToggle .account-button-mobile-icon { display: inline-block; width: 20px; text-align: center; flex-shrink: 0; }
    /* Nutzerwunsch: fehlender Pfeil nach rechts bei "Mein Konto" (eigene Klasse statt .nav-dropdown-
    toggle, siehe views/header.php-Kommentar). */
    #accountToggle .account-button-mobile-chevron { display: inline-block; margin-left: auto; transform: rotate(-90deg); }
    /* Nutzer-Bug-Report 2026-08-18: der Zaehler stand hinter dem Pfeil ganz rechts oben. Auf dem
       Desktop ist .notification-badge bewusst `position: absolute`, weil er dort auf dem runden
       Avatar sitzt - im mobilen Menue ist derselbe Knopf aber eine volle Zeile, und die absolute
       Ecke ist dann die rechte obere Ecke DIESER Zeile. Hier deshalb normal im Fluss, direkt hinter
       dem Wort "Konto"; den rechten Rand behaelt der Pfeil. */
    #accountToggle .notification-badge { position: static; margin-left: 8px; }
    /* Drill-down-Navigation statt Akkordeon (Nutzerwunsch: "wie eine Navigation, dass dann im
    kompletten Menü nochnoch das Untermenü gezeigt wird, und ganz oben 'zurück'") -
    initMobileNavDrilldown() (assets/js/app.js) fügt .nav-drilled/.nav-drill-active hinzu, sobald ein
    Menüpunkt im offenen Hamburger-Menü angetippt wird, statt (wie initDropdownMenu() es sonst tut)
    das Untermenü nur inline aufzuklappen. Reine CSS-Sichtbarkeitsregeln hier, keine eigene
    Positionierung nötig - das aktive Untermenü nutzt bereits die obige .nav-dropdown .account-
    dropdown-Regel (position:static, wächst normal im Fluss von .nav-links). */
    /* Nutzerwunsch: "Zurück sollte nur ein Link sein, kein Button" - kein Vollbreite-/Rahmen-/
    Trennlinien-Look mehr (wirkte wie ein eigener Button in der Liste), stattdessen ein schlichter,
    inline stehender Link in der Akzentfarbe. */
    .nav-drill-back { display: none; align-items: center; gap: 6px; width: auto; padding: 8px 4px 10px; margin-bottom: 4px; background: none; border: none; box-shadow: none; color: var(--accent); font-size: 0.9rem; font-weight: 500; cursor: pointer; text-align: left; }
    .nav-links.nav-drilled > *:not(.nav-drill-active):not(.nav-drill-back) { display: none; }
    .nav-links.nav-drilled .nav-drill-back { display: flex; }
    .nav-links.nav-drilled .nav-drill-active > .account-button { display: none; }
    /* Das Untermenü ist im Drill-down-Zustand die einzig sichtbare Liste (kein eingerücktes
    Akkordeon mehr, siehe .nav-dropdown .account-dropdown oben) - deshalb hier die Einrückung/
    kleinere Schrift wieder auf normale Listen-Optik zurücksetzen (höhere Selektor-Spezifität
    gewinnt automatisch gegen die Regeln oben). */
    .nav-links.nav-drilled .nav-drill-active .account-dropdown { padding: 4px 0; }
    /* Nutzerwunsch 2026-08-16: die Schriftgroesse der Ebene 0 (1rem, .account-button oben) ist
       richtig, die Untermenue-Eintraege waren dagegen zu klein - sie sind hier die eigentlichen
       Navigationsziele und im Drill-down die einzige sichtbare Liste. Deshalb gleiche Groesse. */
    .nav-links.nav-drilled .nav-drill-active .account-dropdown a,
    .nav-links.nav-drilled .nav-drill-active .account-dropdown-link-button { font-size: 1rem; padding: 12px; }
    /* Konto-Menü im mobilen Hamburger-Menü: kein Avatar-Bild, stattdessen "Konto" als Text. */
    #accountToggle .account-avatar { display: none; }
    #accountToggle .account-button-mobile-label { display: inline; }
    /* Menü-Favoriten (Nutzerwunsch: "sollten im mobilen Menü NEBENeinander direkt stehen, nur die
    Icons und darunter in klein dezent das Label, z.B. 'Meine Gruppen'") - Icon-Kachel-Reihe statt
    der Desktop-Optik (Icon+Text nebeneinander in einer Zeile). Das Stern-Symbol selbst bleibt
    ausschließlich der Desktop-Bearbeitung vorbehalten (Nutzerwunsch: "Selektion ... soll direkt im
    Menü am PC editierbar sein") - auf Touch-Geräten gibt es ohnehin kein Hover zum Einblenden, ein
    unsichtbar-aber-antippbares Element würde dort nur ungewollt Klicks abfangen. */
    /* Nutzerwunsch: "Shortcut-Symbole etwas größer" + "Titel darunter keinen Umbruch, wie 'Meine
    Gruppen'" - Kachel-Breite von 60px auf 72px angehoben (reicht für "Meine Gruppen" einzeilig),
    Icon-Größe von 1.25rem auf 1.4rem, Label bewusst nowrap statt umzubrechen. */
    /* Nutzerwunsch (Nachtrag): "Shortcuts noch größer machen ... 'lieber Umbruch als „…“ schreiben,
    aber die Icons können noch gut 50% größer sein" - Icon von 1.4rem auf 2.1rem, Label darf wieder
    umbrechen (kein ellipsis/nowrap mehr) statt bei längeren Namen abgeschnitten zu werden. */
    .nav-shortcuts { flex-direction: row; flex-wrap: wrap; align-items: flex-start; gap: 10px; padding: 4px 4px 12px; }
    .nav-shortcut-link { flex-direction: column; gap: 4px; width: 84px; text-align: center; padding: 6px 2px; border-radius: 12px; }
    .nav-shortcut-link i { font-size: 2.1rem; }
    .nav-shortcut-label { font-size: 0.68rem; color: var(--muted); line-height: 1.2; white-space: normal; }
    .nav-shortcut-link:hover { background: rgba(148, 163, 184, 0.12); }
    /* Favoriten-Sterne sind seit 2026-08-16 auch mobil da (Nutzerwunsch) - vorher stand hier ein
    `display: none`, weil sie am PC nur beim Überfahren mit der Maus erscheinen und es dafür mobil
    keine Entsprechung gibt. Jetzt stattdessen dauerhaft sichtbar: dezent grau, gesetzt heller.
    WICHTIG: diese Regeln müssen hier unten stehen, NICHT weiter oben im selben @media-Block - bei
    gleicher Spezifität gewinnt die spätere Deklaration, genau daran ist der erste Anlauf
    gescheitert (das frühere `display: none` stand hinter der neuen Sichtbarkeitsregel und hat sie
    still wieder ausgehebelt). Dieselbe Cascade-Falle wie schon bei .task-topbar/.topbar. */
    .nav-fav-toggle { display: inline-flex; opacity: 0.5; width: 32px; height: 32px; }
    .nav-fav-toggle.favorited { opacity: 1; }
    .chat-page { flex-direction: column; height: calc(100vh - 140px); }
    .chat-sidebar { width: auto; flex-shrink: 0; border-right: none; border-bottom: none; padding: 14px; overflow-x: hidden; overflow-y: auto; }
    .chat-agent-list { grid-auto-flow: row; overflow-x: visible; margin-top: 0; }
    .chat-main { padding: 14px; display: flex; flex-direction: column; }
    /* "Modul: Chat"/"Chats"-Titel dezenter/ausgeblendet - der "+"-Button bleibt erreichbar
       (.section-title bleibt als App-weite Komponente unverändert, nur hier gescoped versteckt). */
    .chat-sidebar .section-title > div { display: none; }
    .chat-page.chat-has-selection .chat-sidebar { display: none; }
    .chat-page.chat-has-selection.chat-mobile-show-list .chat-sidebar { display: block; }
    .chat-page.chat-has-selection.chat-mobile-show-list .chat-main { display: none; }
    .chat-page.chat-has-selection .chat-mobile-back { display: flex; align-items: center; gap: 6px; align-self: flex-start; margin-bottom: 10px; padding: 0; border: none; background: transparent; color: var(--muted); font-size: 0.85rem; cursor: pointer; }
    .chat-page.chat-has-selection .chat-mobile-back:hover { color: var(--text); }
    .correspondence-page { flex-direction: column; height: calc(100vh - 140px); }
    .correspondence-sidebar { width: auto; flex-shrink: 0; border-right: none; border-bottom: none; padding: 14px; overflow-x: hidden; overflow-y: auto; }
    .correspondence-main { padding: 14px; }
    .correspondence-page.correspondence-has-selection .correspondence-sidebar { display: none; }
    .correspondence-page.correspondence-has-selection.correspondence-mobile-show-list .correspondence-sidebar { display: flex; }
    .correspondence-page.correspondence-has-selection.correspondence-mobile-show-list .correspondence-main { display: none; }
    .correspondence-page.correspondence-has-selection .correspondence-mobile-back { display: flex; align-items: center; gap: 6px; align-self: flex-start; margin-bottom: 10px; padding: 0; border: none; background: transparent; color: var(--muted); font-size: 0.85rem; cursor: pointer; }
    .correspondence-page.correspondence-has-selection .correspondence-mobile-back:hover { color: var(--text); }
    /* Mobiles Seitenmenü als überlagernde, aufklappbare Leiste (2026-08-16).
    Vorher stand die komplette Kategorienliste als Block ÜBER dem Inhalt - man musste nach jeder
    Auswahl an ihr vorbeiscrollen, um überhaupt zu sehen, was man ausgewählt hat (bei den 20+
    Kategorien in settings.php unbrauchbar). Jetzt bleibt links eine schmale Icon-Spalte stehen; der
    Pfeil klappt sie auf, und aufgeklappt legt sie sich ÜBER den Inhalt, statt ihn zu verschieben.
    Gilt für alle neun Seiten mit diesem Layout - Umschalter und Beschriftungs-Kapselung spendiert
    initSettingsSidebarCollapse() (assets/js/app.js) zur Laufzeit, damit keine der Seiten dafür
    angefasst werden muss. */
    .settings-page { flex-direction: row; min-height: 0; position: relative; --settings-rail: 52px; }
    .settings-sidebar { position: absolute; top: 0; left: 0; bottom: 0; z-index: 30; width: var(--settings-rail); border-right: 1px solid var(--border); border-bottom: none; padding: 10px 6px; overflow-x: hidden; background: var(--panel-2-solid); transition: width 0.18s ease; }
    .settings-main { padding: 14px 14px 14px calc(var(--settings-rail) + 14px); }

    .settings-sidebar-toggle { display: flex; align-items: center; justify-content: center; width: 100%; height: 36px; margin-bottom: 8px; padding: 0; border: none; border-radius: 10px; background: rgba(148, 163, 184, 0.12); color: var(--muted); cursor: pointer; }
    .settings-sidebar-toggle i { transition: transform 0.18s ease; }
    .settings-page.settings-sidebar-open .settings-sidebar-toggle i { transform: rotate(180deg); }

    /* Zugeklappt: nur Icon - bzw. der Anfangsbuchstabe, wo eine Seite ihren Menüpunkten gar keine
    Icons gibt (vier der neun tun das, z. B. "Personenbezogene Daten"). */
    .settings-sidebar .settings-search, .settings-sidebar .settings-nav-group-label, .settings-sidebar .settings-nav-label { display: none; }
    .settings-nav-item { padding: 10px 0; text-align: center; }
    .settings-nav-initial { display: inline; font-weight: 600; }

    .settings-page.settings-sidebar-open .settings-sidebar { width: 250px; padding: 10px; box-shadow: 12px 0 30px rgba(2, 6, 23, 0.5); }
    .settings-page.settings-sidebar-open .settings-search { display: block; }
    .settings-page.settings-sidebar-open .settings-nav-group-label { display: block; }
    .settings-page.settings-sidebar-open .settings-nav-label { display: inline; }
    /* Stempelkarten-Kennzeichen sind auf dem Handy zu wuchtig (Nutzerhinweis): kleiner und
       umbruchfähig, statt die Mitgliederzeile zu sprengen. */
    .membership-row .status-pill, .agent-log-extra .status-pill { font-size: 0.68rem; padding: 3px 7px; white-space: normal; }
    .settings-page.settings-sidebar-open .settings-nav-item { padding: 10px 12px; text-align: left; }
    .settings-page.settings-sidebar-open .settings-nav-initial { display: none; }
}

/* Druckansichten der Buchhaltungs-Auswertungen (buchhaltung_auswertungen.php?format=print) - bewusst
   ueber den nativen Browser-Druckdialog ("Als PDF speichern") statt einer neuen PDF-Bibliothek, siehe
   docs/module-buchhaltung.md (Zero-Dependency-Konvention dieser App, spiegelt Taxpools eigenes "PDF-
   Export aus der Druckvoransicht"). .print-report ist die eigentliche Berichtsseite (schlankes,
   eigenstaendiges Layout ohne App-Chrome), .no-print blendet z.B. den Drucken-Button beim Drucken aus. */
.print-report { max-width: 900px; margin: 0 auto; padding: 24px; color: #111; background: #fff; }
.print-report h1 { font-size: 1.3rem; margin-bottom: 4px; }
.print-report .print-meta { color: #555; font-size: 0.9rem; margin-bottom: 16px; }
.print-report table { width: 100%; border-collapse: collapse; margin-bottom: 16px; }
.print-report th, .print-report td { text-align: left; padding: 4px 8px; border-bottom: 1px solid #ccc; font-size: 0.85rem; }
.print-report th { text-transform: uppercase; letter-spacing: .03em; font-size: 0.75rem; color: #444; }
.print-report .print-sum-row td { font-weight: 700; border-top: 2px solid #333; }
/* Aufgabenverwaltung (aufgaben.php, Todoist-artiges Task-Modul) - eigenes, mobil-first gestaltetes
   Layout innerhalb von <main class="content">, bewusst NICHT über die generischen .card/.content-
   Grid-Regeln oben gestylt. WICHTIG (siehe docs/module-aufgaben.md): .task-bottom-nav (position:
   fixed) und die .task-sheet-overlay-Popups muessen im Markup GESCHWISTER bleiben, beide direkte
   Kinder von .task-page - keines darf im anderen verschachtelt sein, sonst wuerde das Bottom-Sheets
   transform: translateY() einen neuen Containing Block erzeugen und die fixe Positionierung der
   Bottom-Nav relativ zum Sheet statt zum Viewport verschieben. */
.task-page { position: relative; padding-bottom: 100px; }
/* Architektur-Umbau unter 761px (siehe ausführlichen Kommentar weiter oben, @media(max-width:760px)-
Block direkt nach .app-shell) - erst HIER, NACH der Basisregel platziert (Cascade-Reihenfolge-Falle,
siehe dortiger Kommentar). Zwischen 761-960px (Tablet) bleibt bewusst das ÄLTERE sticky/fixed-
Kopfzeilen-Modell bestehen (siehe .task-topbar-Basisregel + ihr eigener @media(max-width:760px)-
Override weiter unten) - dieser schmalere, unten begrenzte Umbau ist nur für "echtes Handy"-Breiten
gedacht, an denen der gemeldete Bug tatsächlich auftrat. */
@media (max-width: 760px) {
    .task-page {
        position: fixed;
        top: var(--mobile-top-reserved, 84px);
        left: max(12px, env(safe-area-inset-left));
        right: max(12px, env(safe-area-inset-right));
        bottom: 0;
        display: flex;
        flex-direction: column;
        padding-bottom: 0;
        overflow: hidden;
    }
}
/* Nutzerwunsch: "Die obersten Zeilen sollen ebenso fix sein wie das Menü oben - Titel, 'für alle/
für mich' und die Punkte" - .task-page ist ohnehin nur unterhalb 961px sichtbar (siehe @media
(min-width:961px) weiter unten), deshalb hier bewusst OHNE eigene Media-Query. `top` referenziert
die von initTaskStickyHeaders() (assets/js/app.js) zur Laufzeit gemessene Höhe der app-weiten,
selbst bereits sticky Kopfzeile (.topbar) - ein fester Pixelwert wäre geraten (hängt von Safe-Area-
Inset/Schriftgröße des Geräts ab). Gilt jetzt effektiv nur noch für die 761-960px-Tablet-Breite, siehe
Kommentar oben. */
/* Nutzerwunsch (Kehrtwende gegenüber dem vorherigen Blur-Hintergrund-Fix): "die Hauptüberschriften
(Heute/Demnächst) sind immer noch mit Hintergrund, ich meine nicht die Unterüberschriften, die sind
gut gefixt" - genau wie .task-list-section-header jetzt bewusst komplett transparent statt eines
getönten Blur-Hintergrunds. */
.task-topbar { position: sticky; top: var(--main-topbar-height, 70px); z-index: 40; background: transparent; display: flex; justify-content: space-between; align-items: center; padding: 4px 16px 12px; }
/* Zwei Zustände derselben Kopfzeile (Mehrfachauswahl, Nutzerwunsch) - beide Wrapper übernehmen exakt
   dasselbe Flex-Layout wie die vorherige direkte .task-topbar-Basisregel, da immer nur einer der
   beiden per [hidden] sichtbar ist (siehe views/task_page_content.php). */
.task-topbar-default, .task-topbar-selection { display: flex; justify-content: space-between; align-items: center; flex: 1; gap: 10px; }
.task-bulk-topbar-close { background: transparent; border: none; color: var(--text); font-size: 1.2rem; padding: 6px 10px; }
.task-topbar-selection-count { font-weight: 600; }
.task-bulk-select-all { background: transparent; border: none; color: var(--accent); font-size: 0.85rem; padding: 6px 4px; }
.task-topbar h1 { margin: 0; font-size: 1.4rem; }
/* Echter Bug gefunden+gefixt (Bug-Report: "Hauptüberschriften jetzt zu tief, viel Platz nach oben,
und beim Scrollen hinter statt direkt unter dem Hauptmenü"): diese .task-topbar-Basisregel (ohne
eigene Media-Query, siehe Kommentar oben) steht im Stylesheet NACH dem frühen mobilen
@media(max-width:760px)-Block, der .task-topbar eigentlich auf position:fixed umstellt (siehe dort) -
bei gleicher Spezifität gewinnt im CSS-Cascade die SPÄTERE Regel je Eigenschaft, wodurch die spätere,
aber unconditional sticky/top-Deklaration hier die frühere fixed/top-Deklaration mobil klammheimlich
wieder verdrängt hat (bereits vorhandene, aber bisher unbemerkte Cascade-Reihenfolge-Falle - .topbar
selbst ist NICHT betroffen, dessen Basisregel steht bereits VOR seinem mobilen Override). Fix: ein
neuer, eigener mobiler Block HIER (nach dieser Basisregel) erzwingt fixed/den richtigen top-Wert
zuverlässig. */
@media (max-width: 760px) {
    .task-topbar { position: fixed; top: var(--mobile-top-reserved, 84px); }
}
.task-options-open { background: transparent; border: none; color: var(--text); font-size: 1.2rem; padding: 6px 10px; }
/* transition nur für das Abdimmen beim AJAX-Tab-Wechsel (initTaskTabSwitch(), assets/js/app.js) -
   die Aufhellung selbst braucht keine eigene Transition, da .task-list-area nach dem Fragment-
   Austausch ein komplett neuer DOM-Knoten ohne die inline gesetzte opacity ist. */
.task-list-area { padding-bottom: 12px; transition: opacity .15s ease; }
/* Rückbau (Bug-Report auf echtem Gerät): der vorherige `mask-attachment: fixed`-Fade-Versuch war von
Anfang an als "bewusst experimentell, noch nicht auf einem echten Gerät verifiziert" gekennzeichnet -
ein echter Test zeigte jetzt, dass `mask-attachment: fixed` NICHT wie `background-attachment: fixed`
an der Bildschirmposition verankert, sondern (mangels verlässlicher Unterstützung) effektiv auf
`scroll` zurückfällt: die Maske bleibt dadurch an einem FESTEN PUNKT INNERHALB von .task-list-area
selbst (nicht am Viewport) picture-in-place - das erzeugte GENAU die gemeldeten Symptome: der obere,
maskierte/ausgeblendete Bereich der Liste (mangels mask-image außerhalb der positionierten
Maskengrafik = vollständig unsichtbar, nicht nur ausgeblendet) sah wie ein viel zu großer Leerraum
unter der Kopfzeile aus (betraf alle vier Tabs gleichermaßen, da diese Regel view-unabhängig für
.task-list-area galt) UND wanderte beim Seiten-Scrollen mit dem Inhalt statt an Ort und Stelle zu
bleiben ("der Gradient scrollt mit"). Komplett entfernt statt reparaturversucht - eine zuverlässige,
cross-browser-getestete Ersatzlösung für den ursprünglich gewünschten weichen Zeilen-Fade ist ein
eigenständiges, separat zu verifizierendes Vorhaben. */
.task-list-section { margin-bottom: 8px; }
/* Nutzerwunsch (Todoist-Vorbild): "die aktuellste Unterüberschrift bleibt fixiert, bis man zur
nächsten Gruppe scrollt, dann löst sie sich und die nächste bleibt stehen" - reines CSS
position:sticky OHNE JS: jede .task-list-section-header ist das erste Kind ihrer .task-list-section
(Überschrift + zugehörige Zeilen bilden EINEN Block), sticky-Elemente stapeln sich beim Scrollen
dadurch automatisch korrekt (die nächste Überschrift verdrängt die vorherige aus dem Bild, sobald
ihre eigene .task-list-section den sticky-Bereich erreicht) - kein Scroll-Listener nötig. */
/* Nutzerwunsch: "Titel wie 'Eingang'/'Haushalt' sind nach wie vor mit einem anderen Hintergrund,
das will ich nicht - kein spezieller Hintergrund, einfach so lassen wie die Listenelemente" - bewusst
komplett transparent (wie .task-row selbst), auch im sticky-Zustand. Nutzer nimmt dafür bewusst in
Kauf, dass beim Scrollen kurz Text darunter durchscheinen kann - explizit gewünscht. */
.task-list-section-header { position: sticky; top: calc(var(--main-topbar-height, 70px) + var(--task-topbar-height, 56px)); z-index: 30; background: transparent; padding: 10px 16px 6px; font-weight: 600; color: var(--muted); font-size: 0.85rem; text-transform: uppercase; letter-spacing: .04em; }
/* Architektur-Umbau (siehe ausführlicher Kommentar bei der neuen .task-page-Regel weiter oben,
@media(max-width:760px)-Block direkt nach .app-shell): .task-page ist jetzt selbst eine fixe, nach
oben/unten begrenzte Flex-Spalte, NICHT mehr die scrollende Seite selbst - .task-topbar/
.task-timeline-strip brauchen deshalb kein sticky/fixed mehr (sie sind normale, nie überlappte
Fluss-Kinder), NUR noch .task-list-area (weiter unten) ist der tatsächliche Scroll-Container.
WICHTIGE CSS-Cascade-Falle (bereits einmal bei .task-topbar dokumentiert, mir selbst schon einmal
trotzdem erneut passiert): diese Overrides stehen bewusst NACH den jeweiligen Basisregeln (gleiche
Selektor-Spezifität, spätere Regel gewinnt im Cascade unabhängig von der Media-Query). */
@media (max-width: 760px) {
    .task-topbar { position: static; }
    .task-timeline-strip { position: static; }
    .task-list-section-header { top: 0; }
    /* Echter Bug (Nutzer-Bug-Report: "am Mausrad kann man nicht mehr scrollen in der mobilen
    Ansicht"): #taskPageContent (der Swap-Wrapper aus der AJAX-Tab-Umschaltung, siehe aufgaben.php/
    initTaskTabSwitch()) sitzt im DOM ZWISCHEN .task-page (dem Flex-Container hier) und
    .task-list-area - `flex:1`/`overflow-y:auto` auf .task-list-area wirken aber NUR auf direkte
    Flex-Kinder ihres Flex-Containers, nicht auf Enkel. #taskPageContent selbst hatte keinerlei
    Höhen-/Overflow-Regel, wuchs deshalb ganz normal auf die volle Inhaltshöhe - .task-list-area
    bekam dadurch nie eine begrenzte eigene Box, hatte also auch nichts zum Scrollen (weder per
    Mausrad noch per Touch-Wisch - dieselbe Ursache hätte beides betroffen, nicht nur das Mausrad).
    Der Überschuss wurde einfach von .task-pages `overflow:hidden` weggeschnitten, unsichtbar UND
    unerreichbar. Fix: #taskPageContent wird auf dieser Breite zu `display:contents` - ein rein
    struktureller "Unsichtbar"-Wrapper ohne eigene Box, wodurch seine Kinder (.task-topbar/
    .task-timeline-strip/.task-list-area) direkt zu Flex-Kindern von .task-page werden, genau wie
    ursprünglich beabsichtigt. Der DOM-Baum selbst (für initTaskTabSwitch()s outerHTML-Austausch und
    JS-Selektoren) bleibt davon unberührt, nur das Layout-Verhalten ändert sich. */
    #taskPageContent { display: contents; }
    /* Der eigentliche, jetzt einzige Scroll-Container (siehe Kommentar oben) - flex:1 lässt ihn den
    kompletten restlichen Platz von .task-page (fixe Flex-Spalte) einnehmen, min-height:0 ist bei
    Flex-Kindern mit overflow zwingend nötig (sonst würde der Container einfach mitwachsen statt
    seinen eigenen Scrollbalken zu bekommen - klassische Flexbox-Falle). padding-bottom deutlich
    größer als der Basiswert (12px), damit die letzten Zeilen nicht hinter der schwebenden
    .task-bottom-nav-Pille verschwinden (übernimmt die Reservierung, die vorher .task-page selbst als
    padding-bottom:100px trug - jetzt sinnlos, da .task-page nicht mehr scrollt). */
    .task-list-area { flex: 1; min-height: 0; overflow-y: auto; padding-bottom: 100px; }
}
.task-row { display: flex; align-items: center; gap: 10px; padding: 10px 16px; border-bottom: 1px solid var(--border); background: none; border-left: none; border-right: none; border-top: none; width: 100%; text-align: left; }
a.task-row, a.task-row:hover { color: var(--text); text-decoration: none; }
.task-row-checkbox-form { display: flex; }
.task-row-checkbox { width: 22px; height: 22px; min-width: 22px; border-radius: 999px; border: 2px solid var(--accent); background: transparent; padding: 0; }
.task-row-checkbox.checked, .task-row-checkbox:hover { background: var(--accent); }
/* Auswahl-Checkbox (Mehrfachauswahl, Nutzerwunsch, Todoist-Vorbild) - ERSETZT (nicht ergänzt) die
   "Erledigt"-Checkbox während des Auswahlmodus (Bug-Report: beide nebeneinander sichtbar sahen wie
   zwei redundante Punkte aus) - beide Umschaltungen laufen über dieselbe body.task-selection-active-
   Klasse, ohne dass initTaskSelectionMode() das pro Zeile einzeln togglen müsste. Bewusst eckig
   (Nutzerwunsch: "eher rechteckig mit einem Häkchen drin") statt rund wie die Erledigt-Checkbox,
   damit beide Zustände auf den ersten Blick unterscheidbar bleiben - Häkchen selbst als reines
   CSS-Pseudo-Element (kein <input>, siehe Markup in views/task_row.php), erscheint erst bei .checked. */
.task-row-select-checkbox { display: none; width: 22px; height: 22px; min-width: 22px; border-radius: 6px; border: 2px solid var(--accent); background: transparent; padding: 0; position: relative; }
body.task-selection-active .task-row-select-checkbox { display: block; }
body.task-selection-active .task-row-checkbox-form { display: none; }
.task-row-select-checkbox.checked { background: var(--accent); }
.task-row-select-checkbox.checked::after { content: ''; position: absolute; left: 6px; top: 2px; width: 5px; height: 10px; border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg); }
.task-row-body { display: flex; flex-direction: column; gap: 4px; flex: 1; background: transparent; padding: 0; text-align: left; color: var(--text); font-weight: 400; }
.task-row-title { font-size: 0.98rem; }
.task-row-badges { display: flex; flex-wrap: wrap; gap: 6px; }
.task-badge { display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: 999px; background: rgba(var(--panel-tint-rgb), 0.5); color: var(--muted); font-size: 0.75rem; }
/* Nutzerwunsch: gedämpftere Rot/Grün-Töne statt der vorher kräftigen #22c55e/#ef4444 (siehe
   --success/--danger, oben in :root definiert) - "sehr grell", "weniger intensiv". */
.task-badge-today { color: var(--success); background: rgba(134, 239, 172, 0.14); }
/* Nutzerwunsch ("Durchsuchen" + "auch erledigte anzeigen"): grünes Erledigt-Datum statt der
   Fälligkeit bei bereits abgehakten Aufgaben (views/task_row.php) - gleicher Grünton wie
   .task-badge-today, eigene Klasse statt Wiederverwendung, da inhaltlich "erledigt" statt "heute
   fällig" gemeint ist. */
.task-badge-done { color: var(--success); background: rgba(134, 239, 172, 0.14); }
.task-badge-overdue { color: var(--danger); background: rgba(251, 113, 133, 0.14); }
.task-badge-upcoming { color: var(--accent); background: rgba(56, 189, 248, 0.14); }
.task-badge-priority-1 { color: var(--danger); }
.task-badge-priority-2 { color: #f97316; }
.task-badge-priority-3 { color: var(--accent); }
.task-badge-priority-4 { color: var(--muted); }
/* Nutzerwunsch (Todoist-Vorbild, Screenshot): farbiges Prioritäts-Dropdown statt des schlichten
   nativen <select> im Desktop-Panel/mobilen Sheet - Toggle-Optik von .task-schedule-trigger,
   Panel/Zeilen-Optik von .account-dropdown/.account-dropdown-link-button übernommen, Flaggenfarbe
   weiterhin über die bereits bestehenden .task-badge-priority-N-Klassen oben. */
.task-priority-picker { position: relative; }
.task-priority-picker-toggle { width: 100%; }
.task-priority-picker-toggle [data-task-priority-toggle-icon] { flex-shrink: 0; }
.task-priority-picker-dropdown { position: absolute; left: 0; top: calc(100% + 6px); min-width: 200px; background: var(--panel-2); border: 1px solid var(--border); border-radius: 16px; box-shadow: 0 20px 50px rgba(2, 6, 23, 0.4); display: none; flex-direction: column; z-index: 50; overflow: hidden; padding: 6px; }
.task-priority-picker-dropdown.open { display: flex; }
.task-priority-picker-option { display: flex; width: 100%; align-items: center; justify-content: flex-start; gap: 10px; padding: 10px 12px; border-radius: 10px; color: var(--text); font-size: 0.9rem; background: transparent; border: none; text-align: left; cursor: pointer; font-weight: 400; }
.task-priority-picker-option:hover { background: rgba(148, 163, 184, 0.12); }
.task-priority-picker-option-check { margin-left: auto; color: var(--accent); }
.task-row-project-link { justify-content: flex-start; gap: 8px; }

.task-fab { position: fixed; right: 20px; bottom: calc(96px + env(safe-area-inset-bottom)); width: 56px; height: 56px; border-radius: 999px; background: linear-gradient(90deg, var(--accent), var(--accent-2)); color: #fff; font-size: 1.3rem; box-shadow: 0 12px 30px rgba(2, 6, 23, 0.4); z-index: 60; display: flex; align-items: center; justify-content: center; }

/* Nutzerwunsch (Todoist-Vorbild): "die 4 Auswahlpunkte nicht starr unten am Rand, sondern mit
   rundem Rahmen minimal transparent, und bei Auswahl dann auch das ausgewählte rund" - schwebende
   Pill-Leiste statt einer randstaendigen Vollbreiten-Leiste, ausgewaehltes Item bekommt selbst eine
   runde, gefuellte Pille statt nur einer Farbaenderung. */
.task-bottom-nav { position: fixed; left: 12px; right: 12px; bottom: max(12px, env(safe-area-inset-bottom)); display: flex; justify-content: space-around; gap: 4px; background: rgba(var(--panel-tint-rgb), 0.85); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); border: 1px solid var(--border); border-radius: 999px; padding: 6px; z-index: 55; box-shadow: 0 12px 30px rgba(2, 6, 23, 0.35); }
.task-bottom-nav-item { display: flex; flex-direction: column; align-items: center; gap: 2px; color: var(--muted); font-size: 0.68rem; flex: 1; background: transparent; padding: 7px 4px; border-radius: 999px; transition: background 0.15s ease, color 0.15s ease; }
.task-bottom-nav-item i { font-size: 1.1rem; }
.task-bottom-nav-item.active { color: #fff; background: var(--accent); }

/* Massenbearbeitungsleiste (Mehrfachauswahl, Nutzerwunsch, Todoist-Vorbild) - EIN Basis-Look nach
   Vorbild der schwebenden .task-bottom-nav-Pille (nicht der inline über-der-Tabelle reservierten
   Journal-Leiste aus der Buchhaltung, siehe Umsetzungsplan), gemeinsam für Mobile UND Desktop.
   Standardmäßig unsichtbar, body.task-selection-active blendet je nach Ansicht die passende Variante
   ein und versteckt gleichzeitig .task-bottom-nav/.task-fab, damit während des Auswahlmodus nichts
   kollidiert. */
.task-bulk-bar { display: none; }
body.task-selection-active .task-bottom-nav, body.task-selection-active .task-fab { display: none; }
body.task-selection-active .task-bulk-bar-mobile, body.task-selection-active .task-bulk-bar-desktop { display: flex; }
.task-bulk-bar-mobile { position: fixed; left: 12px; right: 12px; bottom: max(12px, env(safe-area-inset-bottom)); justify-content: space-around; align-items: center; gap: 4px; background: rgba(var(--panel-tint-rgb), 0.85); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); border: 1px solid var(--border); border-radius: 999px; padding: 6px; z-index: 55; box-shadow: 0 12px 30px rgba(2, 6, 23, 0.35); }
.task-bulk-bar-mobile > button, .task-bulk-bar-mobile [data-task-bulk-more-toggle] { flex: 1; background: transparent; border: none; color: var(--text); font-size: 1.05rem; padding: 10px 4px; border-radius: 999px; }
.task-bulk-bar-mobile [data-task-bulk-more] { flex: 1; }
.task-bulk-bar-mobile [data-task-bulk-more-dropdown] { bottom: calc(100% + 10px); top: auto; }

/* Bottom-Sheet: Zusatzklasse NEBEN .modal-overlay/.modal-box (nicht ersetzend) - erbt Backdrop-Klick/
   .modal-close/Escape-Schliessen komplett aus der bereits bestehenden generischen Wiring
   (initModalHashReopen()-Aufrufer in assets/js/app.js), nur die Slide-Up-Optik + initTaskSheetSwipeDismiss()
   (Drag-Handle) sind neu. */
.task-sheet-overlay { align-items: flex-end; padding: 0; }
/* Nutzerwunsch: Detail-Popup "größer" (weniger abgeschnittene/eng gequetschte Felder auf mobil) -
   max-height von 88vh auf 94vh angehoben, max-width von 640 auf 720px. */
.task-sheet-box { width: 100%; max-width: 720px; max-height: 94vh; overflow-y: auto; border-radius: 20px 20px 0 0; margin: 0 auto; padding-bottom: max(20px, env(safe-area-inset-bottom)); transform: translateY(100%); transition: transform 0.25s ease; }
.task-sheet-overlay.open .task-sheet-box { transform: translateY(0); }
/* Nutzerwunsch (Todoist-Vorbild): "swipe bar näher am Rand" + "X-Zeichen größer" + "oberer Teil
des Popups nimmt zu viel Platz ein" - Griffleiste rutscht näher an die Sheet-Oberkante, die
Kopfzeile selbst braucht dadurch weniger vertikalen Leerraum. */
.task-sheet-drag-handle { width: 36px; height: 4px; border-radius: 999px; background: var(--border); margin: 6px auto 2px; }
.task-sheet-close { font-size: 1.7rem; }
/* Nutzerwunsch (wiederkehrende Aufgabe im Popup erledigen, ohne die Seite neu zu laden): neue
   "Erledigt"-Checkbox im Sheet-Kopf, direkt neben dem Schließen-Button statt über .modal-heads
   space-between mittig zwischen Schließen und "..."-Menü einsortiert zu werden. */
.task-sheet-head-start { display: flex; align-items: center; gap: 10px; }
.task-sheet-checkbox-form { display: flex; }
.task-sheet-menu-toggle { font-size: 1.1rem; color: var(--muted); }
.task-sheet-menu-toggle:hover { color: var(--text); }
.task-sheet-menu-dropdown .account-dropdown-link-button i { width: 16px; text-align: center; }
/* Long-Press-Menü (Mehrfachauswahl, Nutzerwunsch, Todoist-Vorbild, schlank gehalten - siehe
   Umsetzungsplan) - gleiche Zeilen-Optik wie .account-dropdown-link-button, aber ohne die
   Dropdown-Positionierung (das Long-Press-Menü selbst ist ja bereits das komplette Bottom-Sheet). */
.task-longpress-menu-box { padding-bottom: max(20px, env(safe-area-inset-bottom)); }
.task-longpress-menu-item { display: flex; width: 100%; align-items: center; gap: 10px; padding: 14px 20px; color: var(--text); font-size: 0.98rem; background: transparent; border: none; text-align: left; }
.task-longpress-menu-item:hover { background: rgba(148, 163, 184, 0.12); }
.task-longpress-menu-item i { width: 18px; text-align: center; }
.task-sheet-title-row { margin-bottom: 4px; }
.task-sheet-title-input { font-size: 1.1rem; font-weight: 600; border: none; background: transparent; padding: 4px 0; }
.task-sheet-add-subtask { display: flex; gap: 8px; padding: 6px 0; }
.task-sheet-add-subtask input { flex: 1; }

.task-comment { padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 0.9rem; }
.task-comment p { margin: 4px 0; }
.task-comment-form { display: flex; align-items: center; gap: 8px; padding-top: 10px; }
.task-comment-form input[type="text"] { flex: 1; }
.task-comment-attach { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 999px; background: rgba(var(--panel-tint-rgb), 0.5); cursor: pointer; flex-shrink: 0; }

/* Korrespondenz-Modul (korrespondenz.php): Nachrichten-Thread im Vorgangs-Popup - eingehende/
   ausgehende Nachrichten per farbigem linken Rahmen unterschieden, gleiches Prinzip wie die
   task-badge-*-Statusfarben app-weit. */
.correspondence-links { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
.correspondence-message-list { display: flex; flex-direction: column; gap: 10px; max-height: 360px; overflow-y: auto; margin: 12px 0; padding-right: 4px; }
.correspondence-message { padding: 8px 10px; border-radius: 8px; background: rgba(var(--panel-tint-rgb), 0.35); border-left: 3px solid var(--border); }
.correspondence-message-inbound { border-left-color: var(--accent); }
.correspondence-message-outbound { border-left-color: var(--success); }
/* Interne Notiz (Nutzerwunsch: "wenn es für interne Notizen gedacht ist, sollte das separat gehalten
   sein") - eigene, gelblich getönte Optik statt Ein-/Ausgehend-Farbe, damit eine Notiz auf den ersten
   Blick klar vom tatsächlichen Nachrichtenverlauf unterscheidbar bleibt. */
.correspondence-message-note { border-left-color: #eab308; background: rgba(234, 179, 8, 0.1); }
/* Erster Einsatz von .switch MIT Beschriftungstext danach (bisher nur der nackte Track, z. B.
   Konnektor-Aktiv-Umschalter in settings.php) - .switch selbst setzt keinen gap zwischen Track und
   etwaigem Folgeinhalt. */
.correspondence-mode-switch { gap: 8px; font-size: 0.88rem; }
.correspondence-message-meta { display: flex; justify-content: space-between; gap: 8px; font-size: 0.85rem; margin-bottom: 4px; }
/* Body wird seit dem Markdown-Rendering-Fix (render_chat_markdown()/renderChatMarkdown() statt
   nl2br(htmlspecialchars())) als echtes HTML mit <p>/<ul>/<ol> gerendert - kein pre-line mehr nötig,
   stattdessen dasselbe Absatz-Rand-Reset wie .chat-bubble-text (nur der erste Absatz verliert seinen
   oberen Rand, der untere Standard-Rand trennt mehrere Absätze weiterhin sichtbar). */
.correspondence-message-body { font-size: 0.92rem; }
.correspondence-message-body p:first-child { margin-top: 0; }
.correspondence-message-body p:last-child { margin-bottom: 0; }
.correspondence-message-files { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.correspondence-raw-source { white-space: pre-wrap; word-break: break-word; max-height: 420px; overflow-y: auto; background: rgba(var(--panel-tint-rgb), 0.45); border: 1px solid var(--border); border-radius: 8px; padding: 10px; font-size: 0.8rem; font-family: monospace; }

/* Schnelleingabe: EINZEILIGES <input> (Nutzerwunsch: keine Textarea, Enter soll direkt speichern -
   ein <input> in einem Formular loest das nativ schon aus, kein eigener JS-Submit-Code noetig) UEBER
   einem gestylten Overlay-Div mit identischer Schrift/Zeilenhoehe - klassische Technik fuer farbig
   hervorgehobenen Text in einem Formularfeld (native <input> kann kein Rich-Text). initTaskQuickAdd()
   (assets/js/app.js) haelt beide Ebenen synchron, inkl. horizontalem Scroll-Abgleich bei langem Text. */
.task-quickadd-input-wrap { position: relative; margin: 0 16px 8px; }
.task-quickadd-overlay, .task-quickadd-textarea { position: absolute; top: 0; left: 0; right: 0; width: 100%; height: 48px; padding: 12px 14px; font-size: 0.95rem; font-family: inherit; line-height: 1.4; white-space: pre; overflow: hidden; box-sizing: border-box; }
.task-quickadd-textarea { position: relative; background: transparent; color: transparent; caret-color: var(--text); }
.task-quickadd-overlay { pointer-events: none; border: 1px solid transparent; color: var(--text); }
.task-quickadd-token { color: #ef4444; font-weight: 600; }
/* Nutzerwunsch: schlichte Buttons für Projekt/Beauftragte Person/Datum-Uhrzeit im Schnelleingabe-
   Popup (fügen denselben Text-Baustein ein, den man auch selbst tippen würde) - bewusst KEINE neuen
   Button-Klassen, reine Wiederverwendung von .task-schedule-trigger/.task-field-toggle-button/
   .account-dropdown (bereits im Bearbeiten-Popup etabliert), damit .task-quickadd-submit weiterhin
   der einzige auffällige Akzent im Popup bleibt. */
.task-quickadd-fields { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 16px 8px; }
.task-quickadd-fields .task-schedule-trigger, .task-quickadd-fields .task-field-toggle-button { width: auto; }
/* Nutzer-Bug-Report: die Projekt-/Personen-Buttons sitzen nahe am linken Rand des schmalen
   Schnelleingabe-Popups - .account-dropdowns Standardregel (rechtsbündig, wächst dadurch nach
   LINKS) reichte dort über den Popup-Rand hinaus und verursachte ein sichtbares Verschieben des
   ganzen Popups. Öffnet hier stattdessen linksbündig (wächst nach rechts) - gleiches Korrektur-
   Muster wie das bereits bestehende .nav-dropdown .account-dropdown weiter oben. */
[data-task-quickadd-project-dropdown], [data-task-quickadd-assignee-dropdown] { right: auto; left: 0; }
.task-quickadd-bar { display: flex; align-items: center; gap: 8px; padding: 0 16px 16px; flex-wrap: wrap; }
.task-pill-row { display: flex; gap: 6px; flex-wrap: wrap; flex: 1; }
.task-pill { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 999px; background: rgba(var(--panel-tint-rgb), 0.5); font-size: 0.8rem; }
.task-pill-project { color: var(--accent); }
.task-pill-date { color: #f97316; }
.task-pill-person { color: #22c55e; }
.task-quickadd-submit { width: 40px; height: 40px; border-radius: 999px; background: linear-gradient(90deg, var(--accent), var(--accent-2)); color: #fff; flex-shrink: 0; padding: 0; }

.task-autocomplete-list { position: absolute; z-index: 30; left: 16px; right: 16px; bottom: 100%; margin-bottom: 6px; max-height: 260px; overflow-y: auto; background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; padding: 6px; box-shadow: 0 12px 30px rgba(2, 6, 23, 0.35); }
.task-autocomplete-group-label { padding: 6px 8px 2px; font-size: 0.72rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.task-autocomplete-item { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; background: transparent; border-radius: 8px; padding: 6px 8px; color: var(--text); }
.task-autocomplete-item.substitute-active, .task-autocomplete-item:hover { background: rgba(var(--panel-tint-rgb), 0.6); }

/* Nutzerwunsch (Kehrtwende): .task-row-title-overdue (roter Aufgaben-Titel bei Überfälligkeit) wieder
entfernt - "das Datum wird ja rot, wenn überfällig ... der Titel muss nicht auch extra rot sein, das
ist too much". Der Fälligkeits-Badge selbst (.task-badge-overdue) bleibt unverändert rot. */
/* Nutzerwunsch: Projekt-Label auf gleicher Höhe wie Datum/Wiederholung (bisher eine eigene dritte
Zeile zwischen Titel und Badges - vermutliche Ursache eines gemeldeten vorderen Zeilenumbruchs).
Badges links (dürfen wachsen/umbrechen), Projekt bleibt rechtsbündig und kürzt bei zu wenig Platz
mit Ellipsis, statt eine eigene Zeile zu erzwingen oder die Badges zu verdrängen. */
.task-row-meta-line { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.task-row-project-label { flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.72rem; color: var(--muted); display: inline-flex; align-items: center; gap: 3px; }
.task-row-assignee { flex-shrink: 0; }
/* Bug-Report: "Icon vom zugewiesenen Benutzer ist zu groß, führt dazu, dass das Projekt nicht steht,
was es tun sollte" - person_avatar_html() rendert bei einem echten Foto <img class="person-avatar
task-assignee-avatar">, .person-avatar selbst setzt bereits 36×36px mit GLEICHER Selektor-Spezifität
(eine Klasse) - je nach Deklarationsreihenfolge konnte das die hier beabsichtigten 26px überstimmen.
Mit dem Eltern-Kontext (.task-row-assignee) jetzt zwei Klassen tief, gewinnt garantiert. */
.task-row-assignee .task-assignee-avatar { width: 26px; height: 26px; font-size: 0.75rem; }
.task-department-chip { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 999px; color: #fff; font-size: 0.72rem; font-weight: 600; }

/* "für mich"/"für alle" (Nutzerwunsch: Umschalter, der Aufgaben auf Person-/Abteilungs-Zuweisung
   des aktuellen Nutzers einschränkt) - zwei nebeneinanderliegende Links statt eines echten
   Switch-Widgets, einfacher/robuster (reiner Seiten-Reload, kein State-JS nötig). */
.task-mine-toggle { display: flex; gap: 4px; background: rgba(var(--panel-tint-rgb), 0.4); border-radius: 999px; padding: 3px; font-size: 0.75rem; }
.task-mine-toggle a { padding: 4px 10px; border-radius: 999px; color: var(--muted); }
.task-mine-toggle a.active { background: var(--accent); color: #fff; }

/* Zeitstrahl oben in der Demnächst-Ansicht - horizontal scrollbarer Tagesstreifen, siehe
   aufgaben.php $timelineStripDays. */
.task-timeline-strip { display: flex; gap: 6px; overflow-x: auto; padding: 4px 16px 12px; }
.task-timeline-day { display: flex; flex-direction: column; align-items: center; gap: 2px; flex-shrink: 0; width: 40px; padding: 6px 0; border-radius: 12px; color: var(--muted); background: rgba(var(--panel-tint-rgb), 0.35); }
.task-timeline-day:hover { color: var(--text); }
.task-timeline-weekday { font-size: 0.68rem; text-transform: uppercase; letter-spacing: .03em; }
.task-timeline-daynum { font-size: 0.95rem; font-weight: 600; }
.task-timeline-day.has-tasks .task-timeline-daynum::after { content: ''; display: block; width: 4px; height: 4px; border-radius: 999px; background: var(--accent); margin: 2px auto 0; }
.task-timeline-day.active { background: linear-gradient(90deg, var(--accent), var(--accent-2)); color: #fff; }
.task-timeline-day.active .task-timeline-daynum::after { background: #fff; }

/* Swipe-Aktionen je Aufgaben-Zeile (Nutzerwunsch: nach links wischen legt schnell eine neue
   Fälligkeit fest, nach rechts wischen hakt schnell ab; weit genug gewischt löst die Aktion sofort
   aus, siehe assets/js/app.js initTaskRowSwipeActions()). .task-row bekommt hier bewusst einen
   blickdichten Hintergrund (vorher "none"), sonst schiene die dahinterliegende Aktion auch im
   Ruhezustand durch. */
.task-row-swipe-wrap {
    position: relative;
    overflow: hidden;
    border-radius: 12px;
    /* --task-swipe-progress-due/-done (0..1, live von initTaskRowSwipeActions() gepflegt) treiben
       das "Aufziehen" der Aktions-Buttons unten - Default 0, falls das Attribut (noch) nicht gesetzt
       ist (Server-Rendering, bevor JS geladen hat). */
    --task-swipe-progress-due: 0;
    --task-swipe-progress-done: 0;
    /* Bug-Report (Long-Press-Menü, iOS Safari): ein langer Druck löste zusätzlich zum eigenen
       Long-Press-Timer auch iOS' EIGENE native Text-Markierung (blaue Griffe/Zeile) samt dem
       systemeigenen "Kopieren/Nachschlagen"-Popup aus, direkt UNTER dem eigenen Menü. user-select
       verhindert die Markierung selbst, -webkit-touch-callout das native Kontextmenü,
       -webkit-tap-highlight-color den grauen Tap-Flash - alle drei nur auf den Zeilen selbst (nicht
       app-weit), da hier kein Fließtext markiert werden soll. */
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
    -webkit-tap-highlight-color: transparent;
}
/* Auswahl-Highlight auf Mobile (Mehrfachauswahl) - initTaskSelectionMode()/taskSelectionRender()
   (assets/js/app.js) togglet .selected direkt am Wrap, analog zum bestehenden .task-desktop-row.selected. */
.task-row-swipe-wrap.selected .task-row { background: rgba(148, 163, 184, 0.22); }
/* Nutzerwunsch: "Hintergrundfarbe der Listeneinträge komplett transparent, dann fällt eine
Überschneidung [mit dem darunterliegenden Swipe-Aktions-Button] auch nicht wirklich auf. Die
Trennlinie kannst du lassen" - vorher deckte --panel-2 (95% Deckkraft) die Zeile fast, aber nicht
ganz vollständig ab, wodurch der wachsende Aktions-Button während des Ziehens sichtbar "durchschien"
statt sauber dahinter zu liegen. border-bottom (von der Basisregel .task-row geerbt) bleibt. */
.task-row-swipe-wrap .task-row { position: relative; background: transparent; touch-action: pan-y; transition: transform 0.2s ease; }
/* Nutzerwunsch: der Aktions-Button soll von Breite/Größe 0 "aufziehen" statt als statische volle
   Fläche hinter der (zu 95% deckenden, siehe --panel-2) Zeile zu stehen - Skalierung+Deckkraft
   folgen den beiden Progress-Variablen, weich per transition außerhalb des aktiven Ziehens
   (.dragging schaltet die transition während des Touch-Move-Events selbst ab, damit der Finger
   ohne Verzögerung folgt). Abgerundete Ecken (Nutzerwunsch: "abgerundeter Button") statt der
   vorherigen randlosen vollen Fläche.
*/
.task-row-swipe-action {
    position: absolute;
    top: 4px;
    bottom: 4px;
    width: 88px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    color: #fff;
    font-size: 0.72rem;
    border-radius: 12px;
    transform-origin: center;
    opacity: 0;
    transform: scale(0.4);
    transition: transform 0.18s ease, opacity 0.18s ease;
}
.task-row-swipe-wrap.dragging .task-row-swipe-action { transition: none; }
.task-row-swipe-action-done { left: 4px; background: #22c55e; opacity: var(--task-swipe-progress-done); transform: scale(var(--task-swipe-progress-done)); }
.task-row-swipe-action-due { right: 4px; background: var(--accent); opacity: var(--task-swipe-progress-due); transform: scale(var(--task-swipe-progress-due)); }
.task-row-swipe-action i { font-size: 1.05rem; }
.task-row-swipe-wrap.revealed-due .task-row { transform: translateX(-96px); }
.task-row-swipe-wrap.revealed-done .task-row { transform: translateX(96px); }

/* Nutzerwunsch (Todoist-Vorbild): kein Seiten-Reload mehr beim Erledigen/Verschieben einer Aufgabe -
   die Zeile verschwindet per Fade-Out + vertikalem Zusammenziehen (removeTaskRowAnimated(),
   assets/js/app.js) bzw. erscheint per Fade-In + Aufziehen an der neuen Stelle
   (insertTaskRowAnimated()). Bewusst auf dem WRAP selbst (nicht auf .task-row), damit diese
   Transition unabhängig von der bestehenden horizontalen Swipe-transform-Transition oben bleibt. Die
   tatsächlichen max-height/opacity-Zahlenwerte setzt JS immer inline (aktuelle Höhe beim Entfernen/
   scrollHeight beim Einfügen) - diese beiden Klassen sind nur Zustands-Marker (verhindern
   Doppel-Tap/Swipe während der Animation). */
@media (prefers-reduced-motion: no-preference) {
    .task-row-swipe-wrap { transition: max-height .26s ease, opacity .2s ease; }
}
.task-row-swipe-wrap.task-row-removing,
.task-row-swipe-wrap.task-row-entering { pointer-events: none; }

/* Dokument-artige Titel/Beschreibung im Detailsheet (Nutzerwunsch: "wie in einem Texteditor, ohne
   Rahmen" - Beschreibung bleibt verborgen, bis der Titel/die Beschreibung selbst fokussiert wird ODER
   bereits ein echter Text drinsteht). */
.task-sheet-doc { margin-bottom: 4px; }
.task-sheet-description-wrap { display: none; align-items: flex-start; gap: 8px; margin-top: 6px; }
.task-sheet-doc.has-description .task-sheet-description-wrap,
.task-sheet-doc:focus-within .task-sheet-description-wrap { display: flex; }
.task-sheet-description-icon { color: var(--muted); font-size: 0.85rem; margin-top: 7px; flex-shrink: 0; }
.task-sheet-description-input { flex: 1; border: none; background: transparent; resize: none; padding: 4px 0; font: inherit; color: var(--text); }
.task-sheet-description-input:focus { outline: none; }
.task-sheet-description-input::placeholder { color: var(--muted); }

/* Fälligkeits-Anzeige-Button im mobilen Detailsheet (Todoist-Vorbild: "Das Datum wird erst mal
   angezeigt und ist nur editierbar, wenn man drauf drückt") - nutzt die geteilte .task-schedule-
   trigger-Basis (siehe unten, ursprünglich Desktop-only), hier voller Breite mit mehr Innenabstand. */
.task-sheet-schedule-trigger { display: flex; width: 100%; border-radius: 12px; padding: 12px 14px; margin: 4px 0 10px; font-size: 0.92rem; }

.task-reschedule-options { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.task-reschedule-quick, .task-schedule-modal-quick, .task-bulk-date-quick { background: rgba(var(--panel-tint-rgb), 0.5); border: 1px solid var(--border); border-radius: 999px; padding: 6px 12px; font-size: 0.85rem; color: var(--text); }
.task-reschedule-quick:hover, .task-schedule-modal-quick:hover, .task-bulk-date-quick:hover { background: rgba(var(--panel-tint-rgb), 0.75); }

/* Prioritäts-Schnellauswahl im Massenbearbeitungs-Picker (Mehrfachauswahl, Nutzerwunsch) - gleiche
   Pill-Optik wie .task-reschedule-quick, .task-badge-priority-N liefert bereits die passende
   Textfarbe je Priorität (siehe views/task_row.php). */
.task-bulk-priority-options { display: flex; flex-wrap: wrap; gap: 8px; }
.task-bulk-priority-quick { background: rgba(var(--panel-tint-rgb), 0.5); border: 1px solid var(--border); border-radius: 999px; padding: 6px 14px; font-size: 0.85rem; display: inline-flex; align-items: center; gap: 6px; }
.task-bulk-priority-quick:hover { background: rgba(var(--panel-tint-rgb), 0.75); }

/* Desktop-Massenbearbeitungsleiste (Mehrfachauswahl, Nutzerwunsch) - schwebend/fixiert nach Vorbild
   von .task-bottom-nav statt der inline über-der-Tabelle reservierten Journal-Leiste (Buchhaltung),
   da die Desktop-Aufgabenliste - anders als das Journal - keine Tabelle mit eigener Checkbox-Spalte
   ist (das Zeilen-Highlight selbst genügt laut Nutzervorgabe). */
/* Bug-Report: "display:flex" stand hier bisher UNBEDINGT in der Regel selbst - gleiche Selektor-
   Spezifitaet (0,1,0) wie die weiter oben stehende ".task-bulk-bar { display:none }"/body.task-
   selection-active-Bedingung, dadurch gewann diese SPAETERE, unbedingte Regel den display-Wert
   unabhaengig vom Auswahlmodus (die Leiste war immer sichtbar) - dieselbe bereits mehrfach in diesem
   Stylesheet dokumentierte Cascade-Reihenfolge-Falle. display selbst steht jetzt bewusst NICHT mehr
   hier, sondern ausschliesslich in der bedingten body.task-selection-active-Regel weiter oben. */
.task-bulk-bar-desktop { position: sticky; bottom: 16px; left: 0; right: 0; margin: 12px 16px 0; align-items: center; gap: 16px; background: rgba(var(--panel-tint-rgb), 0.92); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); border: 1px solid var(--border); border-radius: 16px; padding: 10px 16px; z-index: 55; box-shadow: 0 12px 30px rgba(2, 6, 23, 0.35); }
.task-bulk-bar-desktop .task-bulk-bar-count { font-weight: 600; white-space: nowrap; }
/* Bug-Report: das "..."-Untermenü klappte per Default nach UNTEN auf (Basisregel .account-dropdown),
   die Leiste selbst klebt aber am unteren Bildschirmrand (position:sticky; bottom:16px) - der
   Dropdown-Inhalt landete dadurch außerhalb des sichtbaren Bereichs. Gleiche Korrektur wie bereits
   für die mobile Bulk-Leiste (.task-bulk-bar-mobile [data-task-bulk-more-dropdown]). */
.task-bulk-bar-desktop [data-task-bulk-more-dropdown] { bottom: calc(100% + 10px); top: auto; right: 0; left: auto; }
.task-bulk-bar-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.task-bulk-bar-actions button, .task-bulk-bar-actions .account-button { display: inline-flex; align-items: center; gap: 6px; background: rgba(var(--panel-tint-rgb), 0.5); border: 1px solid var(--border); border-radius: 999px; padding: 8px 14px; font-size: 0.85rem; color: var(--text); }
.task-bulk-bar-actions button:hover, .task-bulk-bar-actions .account-button:hover { background: rgba(var(--panel-tint-rgb), 0.75); }

/* Todoist-artiger Monatskalender innerhalb von #taskScheduleModal (siehe aufgaben.php-Kommentar) -
   6x7-Gitter, Tageszellen mit Aufgaben-Anzahl + bis zu 4 Projekt-Farbpunkten (initTaskScheduleCalendar-
   Funktionen, assets/js/app.js). */
.task-schedule-modal-box { max-width: 400px; }
/* Nutzerwunsch: individuelle Wiederholungsperioden ("alle N Tage/Wochen/Monate/Jahre") - schmale
   Zeile unter dem Wiederholungs-Dropdown, nur sichtbar bei Auswahl "Individuell". */
.task-schedule-modal-custom-interval { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.task-schedule-modal-custom-interval input[type="number"] { max-width: 70px; }
.task-schedule-modal-custom-interval select { flex: 1; }
.task-schedule-calendar { margin: 4px 0 14px; }
.task-schedule-calendar-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; font-weight: 600; }
.task-schedule-calendar-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); text-align: center; font-size: 0.72rem; color: var(--muted); margin-bottom: 4px; }
.task-schedule-calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
.task-schedule-calendar-day { display: flex; flex-direction: column; align-items: center; gap: 2px; background: transparent; border: none; border-radius: 10px; padding: 5px 2px 6px; color: var(--text); min-height: 46px; }
.task-schedule-calendar-day:hover { background: rgba(var(--panel-tint-rgb), 0.4); }
.task-schedule-calendar-day-number { font-size: 0.82rem; }
.task-schedule-calendar-day-muted { color: var(--muted); opacity: 0.5; }
.task-schedule-calendar-day-today .task-schedule-calendar-day-number { color: var(--accent); font-weight: 700; }
.task-schedule-calendar-day-selected { background: var(--accent); }
.task-schedule-calendar-day-selected .task-schedule-calendar-day-number,
.task-schedule-calendar-day-selected.task-schedule-calendar-day-today .task-schedule-calendar-day-number { color: #fff; }
.task-schedule-calendar-day-count { font-size: 0.6rem; color: var(--muted); line-height: 1; }
.task-schedule-calendar-day-selected .task-schedule-calendar-day-count { color: rgba(255, 255, 255, 0.85); }
.task-schedule-calendar-day-dots { display: flex; gap: 2px; }
.task-schedule-calendar-day-dot { width: 5px; height: 5px; border-radius: 50%; }

/* Etiketten-Widget (Nutzerwunsch: dynamisch anlegbares Dropdown, kein separates Konfigurations-
   Formular) - .task-autocomplete-list/-item werden wiederverwendet (bereits vorhandenes
   Vorschlagslisten-Muster aus der Schnelleingabe). */
.task-label-widget { position: relative; margin-bottom: 8px; }
.task-label-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 6px; }
.task-label-chip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 8px; border-radius: 999px; color: #fff; font-size: 0.78rem; }
.task-label-chip button { background: transparent; border: none; color: #fff; padding: 0; line-height: 1; opacity: 0.8; }
.task-label-chip button:hover { opacity: 1; }
.task-label-input { width: 100%; }

.task-search-form { display: flex; align-items: center; gap: 8px; padding: 0 16px 10px; flex-wrap: wrap; }
.task-search-form input[type="text"] { flex: 1; min-width: 160px; }
.task-search-include-done { display: flex; align-items: center; gap: 4px; font-size: 0.78rem; color: var(--muted); white-space: nowrap; }
.task-search-form button[type="submit"] { width: 40px; height: 40px; border-radius: 999px; background: rgba(var(--panel-tint-rgb), 0.5); color: var(--text); flex-shrink: 0; padding: 0; }

/* Desktop-Ansicht (Nutzerwunsch: "am PC im Browser, wo nicht unten das Menü fixiert ist") - Vorbild
   das bereits bestehende .chat-page/.chat-sidebar-Grundmuster (Seitenleiste + Hauptbereich), hier um
   ein drittes rechtes Detail-Panel erweitert. Läuft PARALLEL zur mobilen .task-page (siehe
   docs/module-aufgaben.md) - genau eines von beiden ist je Viewport sichtbar, erstes min-width-Paar
   dieser gesamten App (bisher ausschließlich max-width-basiert). */
.task-desktop-page { display: flex; height: calc(100vh - 160px); min-height: 480px; border: 1px solid var(--border); border-radius: 18px; overflow: hidden; }

/* Nutzerwunsch: Hintergrund etwas dunkler - --panel (näher an --bg, siehe :root-Variablen) statt des
   helleren --panel-2 (das für Eingabefeld-/Karten-Flächen gedacht ist), in beiden Farbschemata. */
.task-desktop-sidebar { width: 260px; flex-shrink: 0; background: var(--panel); border-right: 1px solid var(--border); padding: 16px; overflow-y: auto; }
.task-desktop-nav { display: flex; flex-direction: column; gap: 2px; margin-bottom: 12px; }
.task-desktop-nav a { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 10px; color: var(--text); }
.task-desktop-nav a:hover { background: rgba(var(--panel-tint-rgb), 0.4); text-decoration: none; }
.task-desktop-nav a.active { background: var(--accent); color: #fff; }
.task-desktop-nav-count { margin-left: auto; font-size: 0.75rem; color: var(--muted); }
.task-desktop-nav a.active .task-desktop-nav-count { color: rgba(255, 255, 255, 0.85); }
.task-desktop-nav-group-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin: 14px 0 4px; padding: 0 10px; }
.task-desktop-nav-project-info { display: flex; flex-direction: column; line-height: 1.25; }
.task-desktop-nav-project-sub { font-size: 0.68rem; color: var(--muted); font-weight: 400; }
.task-desktop-nav a.active .task-desktop-nav-project-sub { color: rgba(255, 255, 255, 0.75); }
.task-desktop-nav-avatars { display: flex; margin-left: 6px; }
.task-desktop-nav-avatar { width: 18px; height: 18px; font-size: 0.6rem; margin-left: -6px; border: 2px solid var(--panel-2); }

.task-desktop-list { flex: 1; min-width: 0; display: flex; flex-direction: column; border-right: 1px solid var(--border); }
.task-desktop-list-topbar { display: flex; align-items: center; justify-content: space-between; padding: 16px; border-bottom: 1px solid var(--border); gap: 12px; flex-wrap: wrap; }
.task-desktop-list-topbar h1 { margin: 0; font-size: 1.3rem; }
.task-desktop-list-topbar-actions { display: flex; align-items: center; gap: 10px; }
.task-desktop-view-open { background: transparent; border: 1px solid var(--border); border-radius: 999px; padding: 6px 12px; color: var(--text); font-size: 0.82rem; }
.task-desktop-add-row { padding: 6px 16px; }
.task-desktop-add-row button { background: transparent; border: none; color: var(--accent); padding: 8px 0; display: flex; align-items: center; gap: 8px; font-size: 0.9rem; }
.task-desktop-list-body { flex: 1; overflow-y: auto; padding-bottom: 16px; }
.task-desktop-list-section { margin-bottom: 4px; }
/* Bug-Report (Desktop-Screenshot: "EINGANG" überlappte mitten im Listeninhalt statt oben zu stehen,
"erste Überschrift immer noch zu weit unten"): .task-list-section-header wird auf dem Desktop 1:1
wiederverwendet (aufgaben.php), trägt von dort aber weiterhin ihre MOBILE sticky-top-Formel
(main-topbar/task-topbar-Höhen der App-weiten fixen Kopfzeile) - die gilt hier gar nicht, da
.task-desktop-list-body sein EIGENER, unabhängiger Scroll-Container ist (eigenes overflow-y:auto,
siehe oben). Ein sticky-Element dockt relativ zum NÄCHSTEN scrollenden Vorfahren an, hier also relativ
zur Oberkante von .task-desktop-list-body selbst - der geerbte ~126px-Offset schob die Überschrift
dadurch weit in den sichtbaren Bereich hinein statt sie an dessen Oberkante zu docken. Eigene, auf den
Desktop-Kontext zugeschnittene Regel: dockt direkt an der Oberkante des eigenen Scroll-Containers
(top:0), mit blickdichtem Hintergrund (Zeilen sollen nicht durchscheinen, anders als im mobilen
transparenten Kopfzeilen-Stil - hier gibt es keine app-weite Kopfzeile, gegen die das abgestimmt sein
müsste). */
.task-desktop-list-body .task-list-section-header { position: sticky; top: 0; z-index: 20; background: var(--panel); }
.task-desktop-row { display: flex; align-items: center; gap: 10px; padding: 8px 16px; color: var(--text); border-bottom: 1px solid var(--border); border-left: 3px solid transparent; }
a.task-desktop-row:hover { background: rgba(var(--panel-tint-rgb), 0.3); text-decoration: none; color: var(--text); }
.task-desktop-row.active { background: rgba(var(--panel-tint-rgb), 0.55); }
/* Mehrfachauswahl-Highlight (Nutzerwunsch: "am PC reicht es, wenn die Zeile heller ist", kein
   Checkbox nötig) - koexistiert mit .active (einzeln im Detail-Panel geöffnete Zeile), zusätzlich
   über den Akzent-Rand unterscheidbar. Bug-Report: --panel-tint-rgb ist im Standard-Theme sehr dunkel
   (fast identisch zum Seitenhintergrund --bg), dadurch war der Unterschied zu unauffälligen Zeilen
   kaum sichtbar - --muted (148, 163, 184, ein neutrales Hellgrau, in BEIDEN Farbschemata identisch)
   liefert einen deutlich helleren, gleichzeitig themeneutralen Ton, gleiche Farbe wie die bereits
   bestehenden .account-dropdown-link-button:hover-Zeilen, nur höhere Deckkraft für die dauerhafte
   Markierung statt eines reinen Hover-Effekts. */
.task-desktop-row.selected { background: rgba(148, 163, 184, 0.22); border-left: 3px solid var(--accent); }
/* Zweizeiliges Layout (Nutzerwunsch): Titel+Avatar oben (bricht um, damit ein Avatar das Projekt-Label
   nie verdrängt), Fälligkeit+Wiederholung links / Projekt rechtsbündig ohne Umbruch unten. */
.task-desktop-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.task-desktop-row-title-line { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.task-desktop-row-title-line .task-row-title { flex: 1; min-width: 120px; }
/* Bugfix (Nutzer-Screenshot: Projekt-Label "HAUSHALT" überlappte den Fälligkeits-Badge-Text) - vorher
   konkurrierten `justify-content: space-between` UND `margin-left: auto` um denselben Rechtsbündig-
   Effekt, während weder .task-desktop-row-meta-left noch der Badge selbst einen expliziten
   Schrumpf-/Clip-Schutz hatten. Jetzt: links (Badge/Wiederholungs-Icon) schrumpft NIE
   (flex-shrink: 0), rechts (Projekt-Label) darf schrumpfen/kürzen (eigene, vollständige Regel statt
   nur auf die Kaskade der mobilen Basisregel zu vertrauen), `flex-wrap` als letztes Sicherheitsnetz. */
.task-desktop-row-meta-line { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 8px; font-size: 0.78rem; color: var(--muted); }
.task-desktop-row-meta-left { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.task-desktop-row-meta-line .task-row-project-label { white-space: nowrap; margin-left: auto; flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

.task-desktop-detail { position: relative; width: 380px; flex-shrink: 0; padding: 20px; overflow-y: auto; }
.task-desktop-detail-placeholder { text-align: center; margin-top: 60px; }
/* Das "Erledigt"-Mini-Formular ist bewusst ein Geschwister VOR dem Haupt-Detailformular (verschachtelte
   <form>s sind ungültiges HTML, siehe views/task_detail_panel.php-Kommentar) - hier per Absolute-
   Positionierung optisch trotzdem in dieselbe Kopfzeile wie der Schließen-Button gesetzt. */
.task-desktop-detail-checkbox-form { position: absolute; top: 20px; left: 20px; z-index: 1; }
.task-desktop-detail-head { display: flex; justify-content: flex-end; align-items: center; gap: 10px; margin-bottom: 12px; }
.task-desktop-detail-body { display: flex; flex-direction: column; gap: 16px; }
.task-desktop-meta-field { display: flex; flex-direction: column; gap: 4px; font-size: 0.85rem; color: var(--muted); }
.task-schedule-trigger { flex-direction: row !important; align-items: center; gap: 8px; background: transparent; border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; color: var(--text); font-size: 0.85rem; text-align: left; }
.task-schedule-trigger [data-task-schedule-recurrence-icon] { margin-left: auto; color: var(--muted); }
/* Nutzerwunsch: "Beauftragte Person soll auch ein Button sein" - Toggle-Button (rundes Pill-Design
   wie der Rest der App) klappt die Checkbox-Liste erst bei Bedarf auf (initTaskAssigneePicker(),
   assets/js/app.js), statt sie dauerhaft anzuzeigen (Ursache des gemeldeten Zeilenumbruch-/
   Breiten-Problems im schmalen mobilen Bottom-Sheet). */
.task-assignee-picker { margin-bottom: 14px; }
.task-field-toggle-button { display: inline-flex; align-items: center; gap: 8px; padding: 9px 16px; border-radius: 999px; border: 1px solid var(--border); background: var(--panel-2); color: var(--text); font-size: 0.85rem; }
.task-field-toggle-button:hover, .task-field-toggle-button:focus-visible { border-color: var(--accent); }
.task-assignee-checklist { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; padding: 12px 14px; border: 1px solid var(--border); border-radius: 14px; background: rgba(var(--panel-tint-rgb), 0.35); }
.task-assignee-checklist-row { flex-direction: row !important; align-items: center; gap: 8px; font-weight: 400; color: var(--text); }
.task-sheet-title-row { position: relative; display: flex; align-items: center; gap: 6px; }
.task-title-recognize-save { background: var(--accent); color: #fff; border: none; border-radius: 999px; width: 26px; height: 26px; flex-shrink: 0; }
.task-field-readonly { opacity: 0.55; pointer-events: none; }

@media (max-width: 960px) {
    .task-desktop-page { display: none; }
}
@media (min-width: 961px) {
    .task-page { display: none; }
}

@media (max-width: 760px) {
    .task-sheet-box { max-width: none; }
}

@media print {
    .no-print { display: none !important; }
    .print-report { max-width: none; padding: 0; }
}

/* Klimasteuerungs-Panel (views/climate_control_panel.php, Nutzerwunsch: "grafisch sollte allgemein
   die Gruppensteuerung aufgewertet werden", Vorbild ein MELCloud-App-Screenshot) - bewusst eine
   fest dunkle Farbgebung UNABHÄNGIG vom App-Theme (blue/gold), da es eine Geräte-"Fernbedienung"
   nachbildet, kein normales Formular. Wiederverwendet von saal_klimaanlagen.php (voller
   Funktionsumfang) UND meine_gruppen.php (eingeschränkte Gruppenleiter-Ansicht). */
.climate-panel { background: linear-gradient(160deg, #3e5871, #56728c); border-radius: 16px; padding: 16px; color: #fff; display: flex; flex-direction: column; gap: 14px; }
.climate-panel-power-row { display: flex; align-items: center; justify-content: space-between; font-weight: 600; }
.climate-panel-section-title { font-size: 0.78rem; text-transform: uppercase; letter-spacing: .04em; opacity: 0.8; margin-bottom: 6px; }
.climate-panel-mode-row { display: flex; gap: 6px; flex-wrap: wrap; }
.climate-mode-btn { flex: 1; min-width: 60px; display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 10px 6px; border-radius: 10px; background: rgba(255, 255, 255, 0.12); color: #fff; font-size: 0.72rem; border: 1px solid transparent; }
.climate-mode-btn i { font-size: 1.1rem; }
.climate-mode-btn.active { background: rgba(255, 255, 255, 0.35); border-color: rgba(255, 255, 255, 0.6); }
.climate-panel-temp-row { display: flex; align-items: center; gap: 12px; }
.climate-panel-temp-display { font-size: 1.6rem; font-weight: 700; min-width: 64px; }
.climate-panel-temp-row input[type="range"] { flex: 1; accent-color: #fff; }
.climate-panel-room-temp { margin: 4px 0 0; font-size: 0.8rem; color: rgba(255, 255, 255, 0.75) !important; }
.climate-panel-fan-row { display: flex; align-items: center; justify-content: center; gap: 16px; background: rgba(255, 255, 255, 0.12); border-radius: 10px; padding: 8px; }
.climate-panel-fan-row.is-auto { opacity: 0.5; }
.climate-fan-step { width: 34px; height: 34px; border-radius: 999px; background: rgba(255, 255, 255, 0.25); color: #fff; font-size: 1.1rem; }
.climate-panel-fan-display { min-width: 28px; text-align: center; font-size: 1.2rem; font-weight: 600; }
.climate-fan-auto-toggle { margin-top: 6px; }
.climate-fan-auto-toggle span { color: #fff; }
.climate-panel-select { width: 100%; background: rgba(255, 255, 255, 0.12); color: #fff; border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 8px; padding: 6px 8px; }
.climate-panel-select option { color: #0f172a; }

/* Digitale Mitgliedskarten/eCards (karte.php "Meine Karte", meine_gruppen.php Kamera-Scan) -
   bewusst eine eigene, kartenartige Optik (Farbverlauf, abgerundet) statt der generischen
   .membership-list-Zeilen, damit es wie ein echter Ausweis wirkt. */
.member-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; margin-top: 12px; }
.member-card { background: linear-gradient(160deg, var(--panel-2), var(--panel)); border: 1px solid var(--border); border-radius: 16px; padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.member-card-header { display: flex; align-items: center; gap: 10px; }
.member-card-header-text { display: flex; flex-direction: column; }
.member-card-avatar { width: 44px; height: 44px; border-radius: 999px; object-fit: cover; }
.member-card-punch-status { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.member-card-qr { display: flex; justify-content: center; padding: 10px; background: #fff; border-radius: 10px; }
.member-card-qr svg { width: 100%; max-width: 180px; height: auto; }

/* Kamera-Scan-Panel im Stempelkarten-Popup (meine_gruppen.php, assets/js/member_card_scan.js) */
.card-scan-panel { display: flex; flex-direction: column; gap: 10px; margin-bottom: 14px; padding: 10px; border: 1px solid var(--border); border-radius: 12px; background: rgba(0, 0, 0, 0.2); }
.card-scan-video { width: 100%; max-height: 260px; border-radius: 10px; background: #000; object-fit: cover; }
.card-scan-result { min-height: 24px; }
.card-scan-result .membership-row { flex-wrap: wrap; }

/* Vertragsvorlagen-Editor (vertragsvorlage_bearbeiten.php): Split-View Markdown-Quelltext/Live-
   Vorschau, bricht auf schmalen Bildschirmen auf eine Spalte um. */
.contract-template-chips { display: flex; flex-direction: column; gap: 8px; margin-bottom: 6px; }
.contract-template-chip-group { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.contract-template-chip-group-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); min-width: 90px; }
.contract-template-split { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
@media (max-width: 900px) {
    .contract-template-split { grid-template-columns: 1fr; }
    .contract-template-preview { height: 420px; }
    .contract-template-split-col .CodeMirror,
    .contract-template-split-col .CodeMirror-scroll { max-height: 420px; }
}
.contract-template-split-col { display: flex; flex-direction: column; gap: 6px; }
.contract-template-split-col textarea { font-family: "Courier New", monospace; font-size: 0.85rem; }
.contract-template-preview { border: 1px solid var(--border); border-radius: 10px; padding: 14px 16px; background: var(--panel-2); height: 640px; overflow-y: auto; line-height: 1.5; }
.contract-template-preview table { border-collapse: collapse; width: 100%; margin: 8px 0; }
.contract-template-preview table td, .contract-template-preview table th { border: 1px solid var(--border); padding: 4px 8px; }
/* {{seitenumbruch}}-Platzhalter (app/vertrag_template_helpers.php): im echten PDF ein unsichtbarer,
   erzwungener Seitenwechsel (app/vertrag_pdf_helpers.php) - eine scrollende Live-Vorschau kennt aber
   keine echten Seiten, deshalb hier stattdessen eine sichtbare, dezente Markierung, damit der
   Vorlagenautor sieht, wo im Text er einen Umbruch platziert hat. */
.contract-template-preview .vtr-page-break { position: relative; height: 0; margin: 18px 0; border-top: 1px dashed var(--border); }
.contract-template-preview .vtr-page-break::after { content: "Seitenumbruch"; position: absolute; top: -8px; left: 50%; transform: translateX(-50%); background: var(--panel-2); padding: 0 8px; font-size: 0.72rem; color: var(--muted); }

/* EasyMDE (assets/js/vendor/easymde.min.js) - vendorte Bibliothek mit eigenem, hell voreingestelltem
   CSS (assets/css/vendor/easymde.min.css) - hier NUR auf die App-Theme-Variablen umgefärbt (Dunkel/
   Hell/Gold, siehe :root-Definitionen oben), keine strukturellen Anpassungen - AUSSER der folgenden
   Höhenbegrenzung: der Editor selbst (`minHeight: '360px'` in initContractTemplateEditor(),
   assets/js/app.js) hatte bisher keine Obergrenze und wuchs mit dem Textinhalt unbegrenzt weiter,
   wodurch die Werkzeugleiste (.editor-toolbar, ein Geschwister-Element VOR dem scrollenden Bereich)
   beim Herunterscrollen aus dem Sichtbereich lief und die Live-Vorschau (.contract-template-preview,
   fest 640px) nie gleich hoch wirkte. 640px hier ist bewusst identisch mit .contract-template-preview
   gewählt (Nutzerwunsch: beide Felder sollen gleich groß sein) - ab dieser Höhe scrollt nur noch der
   Textbereich selbst intern (CodeMirrors eingebautes Scroll-Verhalten), Werkzeugleiste UND die
   "Variablen"-Chips darüber bleiben dadurch immer sichtbar, ganz ohne eine mit der bereits
   bestehenden `position: sticky`-Kopfzeile (.topbar) potenziell kollidierende eigene Sticky-Lösung. */
.contract-template-split-col .CodeMirror,
.contract-template-split-col .CodeMirror-scroll { max-height: 640px; }
.EasyMDEContainer .CodeMirror { background: var(--panel-2); color: var(--text); border: 1px solid var(--border); }
.EasyMDEContainer .CodeMirror-cursor { border-left-color: var(--text); }
.EasyMDEContainer .CodeMirror-selected { background: rgba(56, 189, 248, 0.25); }
.EasyMDEContainer .CodeMirror-placeholder { color: var(--muted) !important; }
.editor-toolbar { background: var(--panel-2); border-color: var(--border); }
.editor-toolbar .easymde-dropdown, .editor-toolbar button { color: var(--text); }
.editor-toolbar button.active, .editor-toolbar button:hover { background: rgba(56, 189, 248, 0.18); border-color: var(--accent); }
.editor-toolbar i.separator { border-left-color: var(--border); border-right-color: transparent; }
.editor-statusbar { color: var(--muted); }
.cm-s-easymde .cm-header-1, .cm-s-easymde .cm-header-2, .cm-s-easymde .cm-header-3,
.cm-s-easymde .cm-header-4, .cm-s-easymde .cm-header-5, .cm-s-easymde .cm-header-6 { color: var(--accent); }
.cm-s-easymde .cm-quote, .cm-s-easymde .cm-link, .cm-s-easymde .cm-url { color: var(--muted); }

details.collapse-toggle { border: 1px solid var(--border); border-radius: 10px; padding: 4px 14px; margin-bottom: 12px; background: var(--panel-2); }
details.collapse-toggle summary { cursor: pointer; padding: 8px 0; font-weight: 600; }
details.collapse-toggle[open] summary { margin-bottom: 4px; }

/* Signatur-Pad (vertrag_bearbeiten.php, eigene Verträge) */
.signature-pad-wrap { border: 1px dashed var(--border); border-radius: 10px; background: #fff; margin: 10px 0; touch-action: none; }
.signature-pad-wrap canvas { display: block; width: 100%; height: auto; cursor: crosshair; }

/* Fußzeile der seitenweise geladenen Terminlisten (meine_gruppen.php, 2026-08-11) - "Weitere
   Termine laden" + Ladeanzeige. Zentriert, damit der Knopf auch auf dem Handy gut erreichbar ist,
   und mit Abstand nach oben, damit er sich klar von der letzten Terminkarte absetzt. */
.occurrence-pager { display: flex; justify-content: center; align-items: center; gap: 8px; margin-top: 12px; }
.occurrence-pager-status { display: inline-flex; align-items: center; gap: 6px; font-size: 0.9em; }


/* Beitragsautomatik: mehrzeilige Aufschlüsselung (Summe je Einnahmekonto / je Zahlungsart) in einer
   Tabellenzelle - bewusst kein <br>-Geflecht im Markup, damit die Zeilen bei schmalen Spalten
   sauber umbrechen und in der Zelle vertikal gestapelt bleiben. */
.billing-breakdown { display: flex; flex-direction: column; gap: 2px; font-size: 0.86rem; }
.billing-breakdown span { white-space: nowrap; }
.data-table tr.is-selected-row > td { background: rgba(255, 255, 255, 0.04); }

/* Ersatz für <input type="month"> in Browsern ohne Unterstützung (Firefox) - zwei Auswahllisten
   nebeneinander, die sich wie ein einzelnes Feld verhalten. Siehe initMonthInputFallback(). */
.month-fallback { display: inline-flex; gap: 6px; width: 100%; }
.month-fallback select { flex: 1 1 auto; min-width: 0; }

/* PLZ ist kurz, der Ort braucht den Platz - deshalb kein 1:1-Paar wie sonst bei .form-row-pair. */
.form-row-pair.form-row-pair-postal { grid-template-columns: minmax(90px, 0.35fr) 1fr; }

/* Dezentes Info-Icon fuer eine hinterlegte interne Notiz in der Mitgliederuebersicht - bewusst kein
   .button, damit es die Namenszeile optisch nicht dominiert. */
.membership-note-icon { background: none; border: 0; padding: 0 2px; cursor: pointer; color: var(--accent); font-size: 0.95rem; line-height: 1; }
.membership-note-icon:hover { filter: brightness(1.25); }

/* Auftritts-Zusammenfassung: Besetzung / Musik / Notizen nebeneinander, dahinter EIN Stift-Icon.
   Die letzte Spalte ist bewusst schmal (nur das Icon) und rutscht auf schmalen Bildschirmen mit
   unter die gestapelten Spalten. */
.performance-summary-grid { display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 16px; align-items: start; }
.performance-summary-grid h4 { margin: 0 0 6px; }
.performance-summary-grid p { margin: 0; }
.performance-summary-edit { display: flex; justify-content: flex-end; }
@media (max-width: 900px) {
    .performance-summary-grid { grid-template-columns: 1fr; }
    .performance-summary-edit { justify-content: flex-start; }
}
.performance-song-list { display: flex; flex-direction: column; gap: 6px; }
.performance-song { display: flex; align-items: center; gap: 8px; }
.performance-song-name { display: inline-flex; align-items: center; gap: 6px; min-width: 0; overflow-wrap: anywhere; }
.performance-song-edit { display: grid; grid-template-columns: 80px 1fr auto; gap: 8px; align-items: center; }
@media (max-width: 620px) {
    .performance-song-edit { grid-template-columns: 1fr; }
}

/* Kreditor-Auswahl im KI-Vorschlag: Auswahlschaltflaeche links, zugehoeriges Feld direkt daneben. */
.creditor-choice-list { gap: 8px; }
.creditor-choice-row { display: grid; grid-template-columns: minmax(150px, auto) 1fr; gap: 10px; align-items: center; }
.creditor-choice-row > select, .creditor-choice-row > input { min-width: 0; }
@media (max-width: 620px) {
    .creditor-choice-row { grid-template-columns: 1fr; gap: 4px; }
}

/* Belege-Massenauswahl: schmale erste Spalte, Aktionsleiste mit fester Hoehe, damit die Tabelle
   beim Markieren nicht springt (gleiches Muster wie die Journal-Gruppenaktionen). */
.beleg-select-col { width: 34px; text-align: center; }
.beleg-bulk-actions { margin-bottom: 10px; min-height: 34px; align-items: center; flex-wrap: wrap; }
.beleg-bulk-actions [data-beleg-selection-count] { margin-right: 4px; font-size: 0.85rem; }

/* Regel-Editor: die drei Erfassungsarten nebeneinander. Das app-weite `label { display: grid }`
   stapelte Auswahlpunkt und Text sonst uebereinander (Nutzer-Screenshot 2026-08-13). */
.beleg-rule-modes { display: flex; flex-wrap: wrap; gap: 8px 18px; align-items: center; }

/* Stempelkarte aufnehmen: die Zahlen 0..N als Knopfraster, damit ein Gruppenleiter im Saal einmal
   tippt statt ein Feld auszufuellen (2026-08-16). auto-fill statt fester Spaltenzahl, damit eine
   10er- und eine 20er-Karte gleichermassen passen. */
.punch-adopt-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(56px, 1fr)); gap: 8px; margin-top: 8px; }
.punch-adopt-button { padding: 14px 0; font-size: 1.05rem; font-weight: 600; }

/* Schrittanzeige des oeffentlichen Aufnahme-Wizards (mitglied_werden.php, Nutzerwunsch 2026-08-16):
   1-2-3-4-5 als Kreise mit hervorgehobener aktueller Nummer, darunter die Bezeichnung des aktuellen
   Schritts als echte Ueberschrift. Vorher stand beides als dezenter grauer Fliesstext in einer Zeile
   und war praktisch nicht wahrnehmbar.

   Die Verbindungslinie liegt als ::after am Kreis (nicht als eigener Rahmen zwischen den <li>), damit
   sie unabhaengig von der Anzahl der Schritte funktioniert; der letzte Kreis bekommt keine.
   Bewusst ganz am Dateiende platziert: alle .wizard-*-Klassen sind neu, koennen dadurch von keiner
   spaeteren, gleich spezifischen Regel mehr still ueberschrieben werden (genau die Falle, die hier
   schon .topbar/.task-topbar/.nav-fav-toggle/.nav-links getroffen hat). */
.wizard-steps { display: flex; align-items: center; justify-content: center; gap: 0; list-style: none; margin: 18px 0 22px; padding: 0; }
.wizard-step { display: flex; align-items: center; }
.wizard-step-number {
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; border-radius: 999px;
    border: 1px solid var(--border); background: var(--panel-2);
    color: var(--muted); font-size: 0.9rem; font-weight: 600; text-decoration: none;
}
/* Verbindungslinie zum jeweils naechsten Kreis - der letzte Schritt bekommt keine (:last-child). */
.wizard-step:not(:last-child)::after { content: ''; width: 26px; height: 1px; background: var(--border); }
.wizard-step.done .wizard-step-number { color: var(--text); border-color: var(--accent); }
.wizard-step.done .wizard-step-number:hover { background: var(--accent); color: #06121f; }
.wizard-step.current .wizard-step-number {
    background: var(--accent); border-color: var(--accent); color: #06121f;
    box-shadow: 0 0 0 4px rgba(56, 189, 248, 0.18);
}
/* Abstände nach Nutzer-Screenshot 2026-08-19: die Überschrift klebte am Zahlenstrahl darüber und
am Fließtext darunter. */
.wizard-step-title { margin: 0 0 14px; font-size: 1.25rem; text-align: center; }
.wizard-step-counter { margin: 0 0 14px; text-align: center; color: var(--muted); font-size: 0.85rem; }
/* Titel und Trägerzeile des öffentlichen Aufnahme-Formulars zentriert (Nutzerwunsch 2026-08-18) -
sie gehören optisch zum Logo darüber und zur Schrittanzeige darunter, die beide zentriert stehen. */
.wizard-intro-centered { text-align: center; }

/* Variante mit ausgeschriebener Beschriftung neben der Zahl (probetraining_buchen.php).
   mitglied_werden.php nutzt weiterhin die reine Kreis-Reihe darueber und bleibt unberuehrt -
   diese beiden Klassen kommen dort nicht vor. */
.wizard-step-circle {
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; border-radius: 999px; flex: 0 0 auto;
    border: 1px solid var(--border); background: var(--panel-2);
    color: var(--muted); font-size: 0.82rem; font-weight: 600;
}
/* Deutlicher Abstand zwischen Zahl und Beschriftung - ohne ihn steht dort "1Wer kommt?". */
.wizard-step-label { margin-left: 9px; font-size: 0.95rem; color: var(--muted); }
.wizard-step.current .wizard-step-circle {
    background: var(--accent); border-color: var(--accent); color: #06121f;
}
.wizard-step.current .wizard-step-label {
    color: var(--text); font-weight: 600;
    text-decoration: underline; text-underline-offset: 5px;
}
.wizard-step.done .wizard-step-circle { color: var(--text); border-color: var(--accent); }
.wizard-step.done .wizard-step-label { color: var(--text); }
/* Luft um die Verbindungslinie, sonst klebt sie an Beschriftung und naechster Zahl. */
.wizard-steps .wizard-step:not(:last-child)::after { margin: 0 16px; }

@media (max-width: 640px) {
    /* Drei Schritte mit ausgeschriebener Beschriftung passen schmal nicht in eine Zeile -
       dort bleibt nur die Beschriftung des aktuellen Schritts stehen. */
    .wizard-steps .wizard-step:not(.current) .wizard-step-label { display: none; }
    .wizard-steps .wizard-step:not(:last-child)::after { margin: 0 8px; }
}

@media (max-width: 480px) {
    /* Auf schmalen Bildschirmen kuerzere Verbindungslinien und kleinere Kreise, damit die Reihe
       nicht umbricht - fuenf Kreise plus vier Linien muessen in eine Zeile passen. */
    .wizard-step-number { width: 30px; height: 30px; font-size: 0.85rem; }
    .wizard-step:not(:last-child)::after { width: 14px; }
    .wizard-step-title { font-size: 1.1rem; }
}

/* Durchsuchbares Auswahlfeld (initSearchableSelects(), assets/js/app.js) - erster Anlass ist das
   Land-Feld an jeder Adresse. Das native <select> bleibt im DOM (damit das Formular ohne JS
   weiterhin funktioniert) und wird nur ausgeblendet; sichtbar ist das Eingabefeld darueber.
   Optik bewusst identisch zu einem normalen Feld, damit sich in einem Formular nichts abhebt. */
.searchable-select { position: relative; }
.searchable-select-native { display: none; }
.searchable-select-list { position: absolute; left: 0; right: 0; top: 100%; z-index: 60; margin-top: 4px; max-height: 260px; overflow-y: auto; background: var(--panel-2); border: 1px solid var(--border); border-radius: 10px; padding: 4px; box-shadow: 0 12px 28px rgba(0, 0, 0, 0.35); }
.searchable-select-option { display: block; width: 100%; text-align: left; background: transparent; border: 0; color: var(--text); padding: 7px 10px; border-radius: 8px; font-size: 0.9rem; cursor: pointer; }
.searchable-select-option:hover, .searchable-select-option-active { background: rgba(56, 189, 248, 0.16); }
.searchable-select-empty { padding: 8px 10px; color: var(--muted); font-size: 0.85rem; }

/* Textfeld mit Formatierung (initRichTextFields(), assets/js/app.js) - Werbetexte im
   Marketing-Panel. Ohne JavaScript bleibt die <textarea> sichtbar; erst die Klasse
   .rich-text-field-ready blendet sie zugunsten des bearbeitbaren Feldes aus. */
.rich-text-field .rich-text-input { display: none; }
.rich-text-field-ready .rich-text-input { display: block; }
.rich-text-field-ready > textarea { display: none; }
.rich-text-toolbar { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 6px; }
.rich-text-input { min-height: 180px; max-height: 420px; overflow-y: auto; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel-2); color: var(--text); font-size: 0.95rem; line-height: 1.5; }
.rich-text-input:focus { outline: none; border-color: var(--accent); }
.rich-text-input p { margin: 0 0 10px; }
.rich-text-input p:last-child { margin-bottom: 0; }
.rich-text-input ul, .rich-text-input ol { margin: 0 0 10px; padding-left: 22px; }
.rich-text-input h3, .rich-text-input h4 { margin: 0 0 8px; }
.rich-text-input a { color: var(--accent); }

/* WhatsApp-Gruppenlink je Kursgruppe (Nutzerwunsch 2026-08-18) - eigene Klasse statt der
   .leader-whatsapp-link von der Gruppenleitung: dort ist es ein winziges Zeichen hinter einem Namen,
   hier ein eigenständiger Knopf in einer Aktionszeile, der die WhatsApp-Farbe deutlicher tragen darf. */
.whatsapp-group-link { color: #25d366; border-color: rgba(37, 211, 102, 0.4); }
.whatsapp-group-link:hover { color: #25d366; background: rgba(37, 211, 102, 0.18); }

/* Zwei-Spalten-Aufteilung auf "Meine Mitgliedschaft" (Nutzerwunsch 2026-08-18: mobiltauglicher).
   Bewusst NICHT .membership-filter-grid: das ist auf viele schmale Filterfelder ausgelegt
   (minmax(180px, 1fr)) und stellte deshalb auch zwei inhaltsschwere Spalten noch auf einem schmalen
   Telefon nebeneinander. Hier stapeln sie unterhalb von 760px vollständig. */
.membership-detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 24px; align-items: start; margin-top: 12px; }
@media (max-width: 760px) {
    .membership-detail-grid { grid-template-columns: 1fr; }
}

/* Kontotabelle auf "Meine Mitgliedschaft": bricht auf schmalen Bildschirmen in Blöcke um, statt
   horizontal zu scrollen (Nutzerwunsch: "die Zahlungen als Tabelle mit Umbrüchen machen, wenn zu
   schmal. Also Status unter Beitrag setzen"). Die Spaltenüberschrift wandert dabei über
   data-label in die Zeile - deshalb trägt jede Zelle in meine_mitgliedschaft.php ein data-label. */
@media (max-width: 760px) {
    .membership-account-table thead { display: none; }
    .membership-account-table, .membership-account-table tbody, .membership-account-table tr, .membership-account-table td { display: block; width: 100%; }
    .membership-account-table tr { border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; margin-bottom: 8px; }
    .membership-account-table td { border: none; padding: 3px 0; display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
    .membership-account-table td::before { content: attr(data-label); color: var(--muted); font-size: 0.78rem; flex-shrink: 0; }
    .membership-account-table td > * { text-align: right; }
}

/* Anwesenheitslisten (Nutzerwunsch 2026-08-19) --------------------------------------------------
Farben bewusst aus den bestehenden Zustandsfarben statt neuer Werte: gruen = --success, rot =
--danger, orange = die bereits fuer "Vertretung aktiv" genutzte Warnfarbe. */
.attendance-present { color: var(--success); }
.attendance-absent { color: var(--danger); }
.attendance-excused { color: #fbbf24; }
.attendance-unknown { color: var(--muted); }

/* Eintragen im Termin-Popup: Name links, vier Zustaende rechts. Die Radio-Knoepfe selbst sind
versteckt - bedient wird ueber die Label, damit die Trefferflaeche auf dem Handy gross genug ist.
Bewusst KEIN eigenes JavaScript: ein Radio-Satz je Mitglied plus ein Speichern-Knopf funktioniert
auch ohne. */
.attendance-entry-list { display: grid; gap: 4px; max-height: 55vh; overflow-y: auto; }
.attendance-entry-row { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px dashed var(--border); }
.attendance-entry-row:last-child { border-bottom: none; }
.attendance-entry-name { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; }
.attendance-history { display: inline-flex; gap: 4px; font-size: 0.8rem; opacity: 0.8; }
.attendance-choice { display: flex; gap: 4px; flex-shrink: 0; }
.attendance-choice-input { position: absolute; opacity: 0; pointer-events: none; }
.attendance-choice-label { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border: 1px solid var(--border); border-radius: 8px; cursor: pointer; background: var(--panel-2); }
.attendance-choice-input:checked + .attendance-choice-label { border-color: currentColor; background: rgba(255, 255, 255, 0.08); box-shadow: inset 0 0 0 1px currentColor; }
.attendance-choice-input:focus-visible + .attendance-choice-label { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Leseansicht: Namensspalte links fixiert, Kopfzeile oben fixiert, dazwischen wird waagerecht
gescrollt. Beides per position:sticky INNERHALB eines scrollenden Containers - dadurch bleibt es
eine einzige Tabelle, deren Zeilen zwangslaeufig auf gleicher Hoehe liegen (zwei nebeneinander
gestellte Tabellen waeren genau daran gescheitert). */
.attendance-table-scroll { max-height: 60vh; overflow: auto; border: 1px solid var(--border); border-radius: 10px; }
.attendance-table { border-collapse: separate; border-spacing: 0; width: max-content; min-width: 100%; font-size: 0.9rem; }
.attendance-table th, .attendance-table td { padding: 6px 10px; white-space: nowrap; border-bottom: 1px solid var(--border); }
.attendance-table tbody tr:last-child th, .attendance-table tbody tr:last-child td { border-bottom: none; }
.attendance-table thead th { position: sticky; top: 0; z-index: 2; background: var(--panel-2); text-align: center; font-weight: 600; }
.attendance-table tbody th { position: sticky; left: 0; z-index: 1; background: var(--panel-2); text-align: left; font-weight: 500; }
/* Die Ecke oben links muss beide Richtungen halten - sonst scrollt der Name unter die Kopfzeile. */
.attendance-table thead th:first-child { left: 0; z-index: 3; text-align: left; }
.attendance-table td { text-align: center; }

/* Termine, die weniger als zwei Tage zurueckliegen (Filter "Vergangene Termine anzeigen") - sichtbar
nachtragbar, aber optisch klar abgesetzt von dem, was noch bevorsteht. */
.occurrence-past { opacity: 0.6; }

/* Bestaetigungsseite der Probestunden-Buchung (Nutzer-Screenshot 2026-08-19): die zwei Spalten
rissen auf dem Handy mitten im Wort um. Jede Spalte darf jetzt umbrechen, Datum und Uhrzeit stehen
untereinander, und die Ueberschrift bekommt Luft nach unten. */
.trial-thanks-title { margin-bottom: 16px; }
.trial-booked-row { align-items: flex-start; gap: 16px; }
.trial-booked-course { flex: 1; min-width: 0; display: grid; gap: 2px; }
.trial-booked-when { display: grid; gap: 2px; text-align: right; margin-left: auto; white-space: nowrap; }

/* Kopfzeile der Anwesenheitsliste: links "Name / 04.08. / 11.08.", rechts der gerade offene Termin
(Nutzervorgabe 2026-08-19). */
.attendance-entry-head { border-bottom: 1px solid var(--border); padding-bottom: 6px; }
.attendance-history-head { font-size: 0.78rem; }
.attendance-choice-head { flex-shrink: 0; font-weight: 600; font-size: 0.85rem; text-align: right; }
.attendance-hint { font-size: 0.8rem; margin-top: 10px; }
.attendance-entry-row.is-saving { opacity: 0.55; }
