:root {
  /* ===== Design-Variablen =====
     Eingefuehrt als Schritt 1 der Design-Ueberarbeitung (2026-09-10): zuerst wertgleich (nichts
     sah anders aus), danach wurden die Werte zwischen den Stufen auf die naechste Stufe gezogen
     (Schrift und Radius bei Gleichstand kleiner, Abstaende groesser). Bewusst fest geblieben:
     50%-Kreise, der 68px-Platzhalterbuchstabe, Farben einzelner Komponenten (Bongo-Cat-Blase,
     Kategoriefarben, Video-Schwarz), Ringe per box-shadow, Endlos-Animationen und Hairlines (1px).
     Neue Regeln nehmen ihre Werte von hier. Braucht man einen Wert, den es hier nicht gibt, erst
     pruefen, ob eine vorhandene Stufe reicht -- genau so sind die 40 Schriftgroessen entstanden.
     Breakpoints koennen keine Variablen sein (@media kennt kein var()). Es gibt fuenf Stufen, und
     neue Regeln nehmen eine davon: 560px (kleines Handy), 720px (Handy: Dialoge, Event-Ansicht),
     880px (Tablet -- ab hier Handy-Leiste statt Hauptnavigation, auch isMobileViewport() in app.js),
     1300px (Laptop: Dashboard einspaltig, Reiter nur als Icon), 1440px (Desktop). Dazu 2560/3840px
     fuer 4K-Bildschirme. Bis 2026-09 waren es 17 verschiedene Grenzen (480 bis 1400px). */

  /* --- Flaechen und Text, von hinten nach vorn --- */
  /* Seit 2026-09-13 liegen die Flaechen TIEFER als der Grund (Nutzerwunsch: "mache die
     backgrounds der kacheln und leisten auch etwas dunkler damit sie sich bisschen besser vom
     background an sich abheben") -- wie in der Vorlage in _Uploads, wo die Karten fast schwarz auf
     einem helleren Grund stehen. Der Grund selbst ist dafuer eine Spur heller geworden. */
  /* Die dunklen Standardfarben sind die vom Nutzer eingestellte Palette (2026-09-16 als Screenshot
     seiner Konfiguration geliefert: "kannst du die als default machen?") -- Flaeche #121317, Grund
     #0b0c0f, Kachel #1f2024, Kachel 2 #1a1b1e, Hover #2b2d33, Aktiv #3a3d55, Muster #151c29.
     Sie stehen als feste Werte da, nicht als Mischung: eine Mischung kaeme nie genau auf diese
     Toene (Kachel 2 liegt zum Beispiel DUNKLER als die Kachel). Waehlt eine Organisation eine
     eigene Flaechenfarbe, leitet wendeOrgFarbenAn() die Stufen wieder ab.
     Neutral statt blaustichig (2026-09-16: "bitte mache die dark defaults eher in richtung schwarz
     anstatt blau") -- die ganze dunkle Leiter laeuft jetzt ueber Grautoene mit nur einem Hauch
     Kaelte, der Grund liegt am dunklen Ende.
     Der Grund liegt UNTER den Flaechen -- er ist die Unterlage, nicht die Buehne. Bis 2026-09-16
     war er heller als --panel (#1c1f26 gegen #151a23): die Flaechen sassen wie dunkle Loecher auf
     einem hellen Grund, und dieselbe Umkehrung machte auch die Fenster flach. Jetzt eine deutliche
     Stufe darunter, der Verlauf mit ihm (gleiche Ordnung wie im hellen Modus, wo der Grund
     ebenfalls unter der weissen Flaeche liegt). Geprueft in TEST=vorschlagfarben. */
  --bg: #0b0c0f;
  --zone-bg: #0d0e11;
  /* Deckend. Die Flaechen waren kurzzeitig zu 92 % durchsichtig mit Weichzeichner darunter --
     auf Nutzerwunsch wieder zurueckgenommen ("entferne die durchsichtigkeit"). Halbdurchsichtig
     bleibt nur, was ein Titelbild durchscheinen lassen MUSS (siehe .agenda-item-mit-bild und die
     Kacheln der Event-Ansicht). */
  /* Neue Standardflaeche seit 2026-09-16 (Nutzerwunsch: "mache 151a23 als die neue default farbe")
     -- Kachel- und Hover-Ton wandern eine Stufe mit, damit der Abstand zwischen Flaeche, Karte und
     Hover derselbe bleibt. */
  --panel: #121317;
  --panel-2: #1a1b20;
  /* Hover auf --panel/--panel-2; ein paar Kopfleisten (Agenda, Raumliste) nutzen denselben Ton */
  --panel-hover: #2b2d33;
  /* Der gerade geoeffnete Eintrag (Album, Notiz, Raum): eine Stufe HELLER als seine Nachbarn plus
     ein Hauch Akzent. Vorher lag dort --accent-soft, also eine Toenung der Akzentfarbe -- je nach
     Akzent war der aktive Eintrag damit dunkler als die uebrigen (2026-09-16 gemeldet: "in den
     alben ist irgendwie das aktive dunkler als das nicht ausgewaehlte album"). */
  --aktiv-kachel: #3a3d55;
  /* Schrift auf dem aktiven Eintrag -- normalerweise die uebliche Textfarbe, aber eigens
     einstellbar: eine kraeftige Aktiv-Flaeche braucht oft eine andere Schrift (2026-09-16). */
  --aktiv-text: var(--text);
  /* Die INHALTSKACHELN: was auf einer Flaeche liegt -- Events ohne Titelbild (auch in der
     Agenda-Spalte und in den Widget-Listen), Bongo-Cat-Koerper, Countdown-Zeile. Das ist der
     einzige Ton, den die Kachelfarbe der Organisation setzt; Flaechen (Widgets, Spalten, Fenster)
     und Zwischentoene (--panel-2: Chips, Eingabefelder, Raumzeilen) haengen an der Flaechenfarbe.
     Ohne eigene Kachelfarbe wird der Ton aus --panel-2 gemischt, damit die Inhaltskachel immer
     eine sichtbare Stufe ueber ihrer Unterlage liegt. */
  /* Aufgehellt wird mit einem BLAUGRAU, nicht mit reinem Weiss: Weiss entsaettigt, die Karten
     wirkten dadurch grau statt wie eine hellere Stufe derselben Farbe (2026-09-16 mit Bild
     gemeldet). Derselbe Ton nimmt auch der Kopfbalken. */
  --aufhellung: #8e9199;
  --inhalt-kachel: #1f2024;
  /* Kopfbalken/Hover der Inhaltskachel: ein HELLERER Akzent ihrer eigenen Farbe, aber nur eine
     halbe Stufe (2026-09-16: "mache das zwar heller als die gewaehlte farbe aber dunkler als es
     jetzt ist") -- vorher sprang der Kopfbalken so weit auf, dass er wie eine eigene Farbe wirkte. */
  --inhalt-kachel-hover: #1a1b1e;
  /* Flaeche einer Inhaltskachel unter dem Mauszeiger. Standard: derselbe Ton wie der Kopfbalken,
     also eine Stufe ueber der Kachel. Setzt die Organisation eine Hover-Farbe, gilt DIESE (siehe
     wendeOrgFarbenAn) -- vorher hing der Karten-Hover als einziger nicht am Regler (2026-09-16:
     "die hover farbe greift noch nicht bei agenda eintraegen"). */
  --kachel-hover: var(--inhalt-kachel-hover);
  --border: #24262b;
  --text: #e9eaee;
  --text-dim: #9ea1a8;
  /* Text auf farbigen Flaechen oder Bildern: Badges, aktive Knoepfe, Schrift ueber dem Titelbild */
  --text-on-color: #fff;
  /* Echte weisse Flaechen: der 2FA-QR-Code muss weiss sein, die Bongo-Cat-Sprechblase ist es */
  --white: #fff;
  /* --accent ueberschreibt applyAccentColor() (settings.js) mit der Akzentfarbe der Organisation.
     Alles Indigo leitet sich davon ab: die Toenungen darunter stehen hier auf :root, und weil
     applyAccentColor() --accent auf demselben Element (html) setzt, rechnen sie mit der Farbe der
     Organisation. Bis 2026-09 standen feste Indigo-Toene im Code, die bei eigener Akzentfarbe
     indigo blieben (Hover der Links, aktive Reaktion, Suchfeld-Fokus). */
  /* Verlauf statt einer flachen Flaeche (2026-09-13, Nutzerwunsch "mache den hintergrund der
     gesamten seite in einem dunklen oder hellen gradient je nach light oder dark mode"). Liegt
     fest im Sichtfenster (background-attachment), damit er beim Scrollen nicht mitwandert und die
     Kopfzeile denselben Ausschnitt zeigt wie die Seite darunter. */
  --bg-verlauf: linear-gradient(157deg, #131418 0%, #0e0f13 50%, #08090b 100%);
  /* Widgets liegen eine Spur dunkler als der Grund (2026-09-14, Nutzerwunsch) -- als Schleier ueber
     demselben Verlauf, damit sie das Raster weiterhin luecklenlos abdecken. */
  --widget-schleier: rgba(0, 0, 0, 0.22);
  /* Glaskante und -schatten der Kacheln (siehe .widget) */
  --glas-kante: rgba(255, 255, 255, 0.07);
  --glas-schatten: rgba(0, 0, 0, 0.35);
  /* EINE Definition der Flaeche fuer die ganze App (2026-09-14, Nutzerwunsch: "mache zusaetzlich
     die anderen seiten angepasst an das dashboard ... desweiteren fehlt ueberall dieser glass
     look ... das alles gleich ist und das als default festlegen"). Jede Inhaltsflaeche -- Kachel,
     Notiz-Spalte, Raumliste, Tools-Panel, Album -- nimmt diese drei Werte, nichts setzt seine
     Flaeche mehr selbst (siehe Abschnitt "Einheitliche Flaechen" am Ende der Datei). */
  /* 78% statt 58%: seit der Seitengrund unter den Flaechen liegt (2026-09-16), zog eine stark
     durchsichtige Flaeche den dunklen Grund mit hoch -- die Spalten wirkten fast schwarz, und alles
     darauf (Listenzeilen, Info-Kaesten) stand als heller Kasten davor statt als leichte Stufe
     (gemeldet mit Bild: "diese zwei bereiche wirken irgendwie noch falsch"). */
  --flaeche-fuellung: color-mix(in srgb, var(--panel) 78%, transparent);
  /* Fenster tragen dieselbe Glasflaeche, nur dichter (2026-09-15: "mache alle fenster die sich
     oeffnen weniger durchsichtig") -- hinter einem Fenster steht Inhalt, der sonst durchliest. */
  --flaeche-fuellung-fenster: color-mix(in srgb, var(--panel) 88%, transparent);
  /* ---- Die vier einstellbaren Farben einer Organisation (2026-09-16) ----
     Flaeche  = --panel      : Menuespalten, Fenster, Leisten, Hauptflaechen
     Kachel   = --inhalt-kachel : NUR die Inhaltskacheln, die auf einer Flaeche liegen -- Events
                               ohne Titelbild, Bongo-Cat-Koerper, Countdown-Zeile, die Zeilen der
                               Widgets. Widgets selbst gehoeren zur FLAECHE (2026-09-16:
                               "wir brauchen einen regler fuer hintergruende von flaechen wie
                               widgets ... dann einen regler fuer inhaltskacheln wie die events
                               die kein titelbild haben + bongo cat + countdown")
     --panel-2               : Zwischenton der Flaeche -- Chips, Eingabefelder, Raum-/Listenzeilen.
                               Haengt an der Flaechenfarbe, NICHT an der Kachelfarbe
     Knopf    = --knopf      : Knoepfe (bleibt bewusst UNABHAENGIG von der Kachelfarbe --
                               sonst faerbt eine helle Kachelfarbe auch alle Knoepfe um)
     Hintergrund = --bg      : der Grund hinter allem
     Gesetzt werden sie in wendeOrgFarbenAn() (settings.js); ohne Wahl gelten diese Vorgaben. */
  --knopf: #232529;
  --knopf-hover: var(--panel-hover);
  --flaeche-unschaerfe: blur(20px) saturate(1.5);
  --flaeche-schatten: inset 0 1px 0 var(--glas-kante), 0 6px 20px var(--glas-schatten);
  /* Feines Raster hinter der Inhaltsflaeche (2026-09-14, Nutzerwunsch: "die haupt area mit so einem
     kleinen gestrichelten grid hinterlegen ... nur ganz leicht sichtbar"). Es liegt unter dem
     Inhalt; wo ein Widget steht, deckt dessen Flaeche es ab -- so braucht kein Widget eine eigene
     Farbe und man sieht trotzdem, wo eines ist. */
  /* Farben des Wellen-Musters hinter der ganzen Seite (siehe body::before). Seit 2026-09-16 aus dem
     Seitengrund gemischt statt in festem Blaugruen (Nutzerwunsch: "koennen wir die
     hintergrundpattern an der farbe des hintergrundes anpassen nur immer dunkler damit man sie
     noch sieht?") -- immer DUNKLER als der Grund, in beiden Modi und bei jeder gewaehlten
     Hintergrundfarbe. Zwei Stufen, damit der Verlauf im Muster erhalten bleibt. */
  --wellen-a: #151c29;
  --wellen-b: color-mix(in srgb, #151c29 70%, var(--bg));
  /* Im Dunkeln voll deckend: zwischen einem dunklen Grund und Schwarz liegen wenige Stufen, eine
     halbdurchsichtige Schicht davon waere schlicht nicht mehr zu sehen. */
  --wellen-deckkraft: 1;
  --gitter-schritt: 56px;
  /* ---- Kachelraster des Dashboards (2026-09-14) ----
     Nutzerwunsch: "ein komplett funktionierendes grid an kacheln die groessentechnisch gleich
     sind". Eine Kachel ist ein Vielfaches EINER Einheit; zwischen zwei Kacheln liegt genau ein
     Rasterschritt. Damit endet jede Kachel wieder auf einer Rasterlinie, egal wie viele Einheiten
     sie hoch ist:  Hoehe(n) = n * (Einheit + Abstand) - Abstand.
     Einheit = 2 Schritte, Abstand = 1 Schritt -> 1 Einheit = 2, 2 = 5, 3 = 8 Schritte.
     Wer eine neue Kachelgroesse braucht, setzt --kachel-einheiten auf dem Widget -- nie eine feste
     Pixelhoehe, sonst bricht die Ausrichtung der Nachbarspalten. */
  /* Der Abstand zwischen zwei Kacheln ist klein, die Kachel dafuer hoch (2026-09-14: "die
     abstaende der widgets deutlich verringern indem man sie groesser macht"). Wichtig ist nur,
     dass Zeilenhoehe + Abstand ein Vielfaches des Rasterschritts ergeben -- hier 3 Schritte:
     144 + 24 = 168 = 3 x 56. Damit sitzt jede Kachelunterkante weiter auf einer Rasterlinie. */
  /* Kleinerer Abstand zwischen den Kacheln = breitere Kacheln (2026-09-16: "koennen wir bitte auf
     dem dashboard die widgets einen ticken breiter machen, so dass sich die abstaende etwas
     verringern"). Die Zeilenhoehe rechnet mit diesem Wert, das Raster bleibt also stimmig. */
  --kachel-abstand: 16px;
  --kachel-einheit: calc(var(--gitter-schritt) * 3 - var(--kachel-abstand));
  /* Daempfung des Titelbilds hinter Agenda-Karten (siehe .agenda-item-mit-bild) */
  --agenda-bild-filter: blur(2px) brightness(0.5) saturate(1.1);
  --accent: #6366f1;
  --accent-soft: color-mix(in srgb, var(--accent) 12%, transparent);   /* Hover, leichte Markierung */
  --accent-tint: color-mix(in srgb, var(--accent) 20%, transparent);   /* gewaehlt, aktiv */
  --accent-strong: color-mix(in srgb, var(--accent) 35%, transparent); /* Rand, deutliche Flaeche */
  --accent-light: color-mix(in srgb, var(--accent) 65%, white);        /* Akzent als Schrift auf dunkel */

  /* --- Toenungen und Abdunklung. Weiss auf dunklem Grund hebt an (Hover, leichte Flaeche);
     --scrim liegt hinter Dialogen und Seitenleisten; --on-image-bg traegt Knoepfe und Badges, die
     auf einem Bild liegen, wo keine Panel-Farbe lesbar waere. --- */
  --tint-subtle: rgba(255,255,255,0.04);
  --tint: rgba(255,255,255,0.06);
  --tint-hover: rgba(255,255,255,0.1);
  /* Abdunkelnde Toenung fuer den Hover der Widgets -- Weiss waere dort aufhellend, gewuenscht ist
     "etwas dunkler" (2026-09-13). */
  --tint-dunkel: rgba(0,0,0,0.3);
  --scrim: rgba(0,0,0,0.6);
  --scrim-strong: rgba(0,0,0,0.9);   /* Lightbox, aufgeklapptes Watch-Together */
  --on-image-bg: rgba(0,0,0,0.6);
  --on-image-bg-hover: rgba(0,0,0,0.8);

  /* --- Statusfarben. "on-..." ist die Schrift auf einer Flaeche dieser Farbe --- */
  --success: #22c55e;
  --success-hover: #16a34a;
  --success-text: #4ade80;  /* heller, fuer gruene Schrift auf dunklem Grund */
  --on-success: #05230f;
  --warning: #f59e0b;
  --warning-hover: #d97706;
  --on-warning: #2b1900;
  --danger: #ef4444;
  --danger-hover: #dc2626;
  --highlight: #fbbf24;     /* angeheftet, "bald", aktueller Suchtreffer */
  --heart: #ef4676;         /* Herz / "will ich spielen" */
  --youtube: #ff0000;       /* Markenfarbe im Watch-Together */

  /* --- Radien. --radius ist die Karte; 50% (Kreis) bleibt als Wert stehen --- */
  --radius-xs: 4px;
  --radius-sm: 8px;
  --radius-md: 12px;
  --radius: 16px;
  --radius-xl: 20px;
  --radius-full: 999px;

  /* --- Schriftgroessen, klein nach gross. --fs-md ist die gewoehnliche Oberflaechenschrift --- */
  --fs-2xs: 0.7rem;        /* kleinste Schrift (11.2px): Badges, Versal-Labels. Bis 2026-09 gab es noch 0.62rem */
  --fs-xs: 0.72rem;
  --fs-sm: 0.78rem;
  --fs-md: 0.85rem;
  --fs-lg: 0.9rem;
  --fs-body: 1rem;
  --fs-xl: 1.1rem;
  --fs-2xl: 1.3rem;
  --fs-3xl: 1.6rem;
  --fs-4xl: 2rem;           /* grosses Datum der Event-Ansicht; die Luecke 1.6 -> 2.4 war zu gross */
  --fs-display: 2.4rem;
  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;

  /* --- Abstaende fuer padding, margin und gap: --space-N = N x 2px. Der Bestand liegt auf einem
     2px-Raster (6px und 10px sind haeufig), darum keine 4px-Stufen. Ungerade Werte stehen noch fest. */
  --space-1: 2px;
  --space-2: 4px;
  --space-3: 6px;
  --space-4: 8px;
  --space-5: 10px;
  --space-6: 12px;
  --space-7: 14px;
  --space-8: 16px;
  --space-10: 20px;
  --space-12: 24px;
  --space-16: 32px;

  /* --- Bausteine: gemeinsame Hoehe fuer Knoepfe und Felder (Schritt 2). Knoepfe und Felder, die
     nebeneinander stehen, sind damit gleich hoch -- Speichern und Loeschen waren es frueher nicht. */
  --control-h: 32px;
  --control-h-sm: 28px;     /* Icon-Knoepfe, Pillen */
  /* Auf Touch-Geraeten gelten groessere Werte, siehe @media (pointer: coarse) direkt nach :root. */

  /* --- Schatten --- */
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.2);
  --shadow-md: 0 6px 20px rgba(0,0,0,0.35);
  --shadow-popover: 0 8px 24px rgba(0,0,0,0.45);
  --shadow-lg: 0 10px 30px rgba(0,0,0,0.5);

  /* --- Ebenen (z-index), von unten nach oben. Werte 0-5 bleiben fest: sie ordnen nur innerhalb
     einer Komponente. Was knapp ueber einer Ebene liegen muss, rechnet von ihr aus
     (Suchleiste = --z-modal + 1), damit die Reihenfolge beim Verschieben erhalten bleibt. --- */
  --z-header: 10;
  --z-sticky: 90;           /* Kalender-Kopfzeilen, Tages-Vorschau, Menue-Hintergrund */
  --z-banner: 150;          /* PWA-Hinweis */
  --z-tab-bar: 160;         /* Handy-Leiste unten */
  --z-modal: 200;
  --z-over-modal: 250;      /* Bestaetigungsdialog, offener Raum auf dem Handy */
  --z-popover: 300;
  --z-menu: 310;            /* Erstellen-Menue, Icon-Auswahl, Wikilinks */
  --z-hover-preview: 320;
  --z-theater: 450;         /* aufgeklapptes Watch-Together */
  --z-overlay: 500;         /* Lightbox, Erinnerungsbanner */
  --z-toast: 600;

  /* --- Bewegung --- */
  --duration-fast: 0.15s;
  --duration-base: 0.2s;
  --duration-slow: 0.3s;
  --ease-sheet: cubic-bezier(0.32, 0.72, 0, 1);    /* iOS-artiges Einfahren von unten */
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* leichtes Ueberschwingen (Toast) */

  /* ===== Layout ===== */
  /* Einheitliche feste Maximalhoehe fuer die Haupt-Inhaltsflaeche jeder Seite (Dashboard-Agenda +
     Widget-Bereich, Kalender, Compendium, Medien, Notizen, Dateien, Links) -- bewusst ein
     einziger fester Wert statt an der Fensterhoehe (100vh) oder dynamisch pro Seite/Tab-Inhalt
     berechnet (beides gab es zwischenzeitlich, wirkte aber unvorhersehbar -- je nach Browserfenster
     bzw. Tagesinhalt sprang die Hoehe). Inhalt, der ueberlaeuft, scrollt intern statt die Seite zu
     strecken. Beide Spalten einer Seite nutzen dieselbe Variable, sind also garantiert gleich lang.
     Der Wert ist auf Nutzerwunsch groesszuegiger als die zuvor exakt austarierten 916px (bei denen
     das Widgets-Ein/Aus-Symbol pixelgenau die untere rechte Ecke bildete) -- bei 1010px bleibt
     noch mehr Luft unter dem Symbol. */
  --fixed-content-height: 1010px;
  /* Hoehe JEDER Inhaltsflaeche (2026-09-14: "bitte mache alle main areas so gross wie bei rooms
     oder notizen, medien z.b. ist groesser"). Gerechnet wie bisher bei Notizen/Dateien: alles unter
     der Kopfzeile minus dem Abstand nach unten. Wer eine neue Ansicht baut, nimmt diese Variable --
     eigene calc()-Formeln waren genau der Grund, warum Rooms 100px kuerzer und das Album groesser
     als der Rest war. */
  /* Genauso hoch wie die zweite Menuespalte links (2026-09-14: "wenn es keine suchleiste gibt mache
     den inhalt rechts genauso hoch wie die leiste 2 links") -- dieselbe Rechnung wie dort:
     Fensterhoehe minus Kopfzeile und oberem Abstand, minus dem unteren Rand der Spalten. */
  /* Wo der Inhalt beginnt (oberer Abstand von main plus die Kopfzeile der Seite) -- daran haengen
     die Linien des Rasters, siehe main::before. */
  --gitter-anker: calc(var(--space-10) + var(--gitter-schritt));
  --inhalt-hoehe: calc(100vh - var(--sticky-header-height, 78px) - var(--space-10) - var(--schale-gap) - var(--hub-titel-hoehe));
  /* "vh" schrumpft unter CSS-zoom NICHT mit (siehe die zoom-Regeln bei main{...}) -- auf sehr
     breiten Bildschirmen muss die Hoehe deshalb durch denselben Faktor geteilt werden, sonst
     laeuft jede Ansicht unten aus dem Bild. Stand bisher nur bei Rooms, jetzt an der Variablen
     und damit fuer alle. */
  /* Breiten der beiden Menuespalten links (2026-09-13, siehe .app-nav). Die erste ist seit der
     Ueberarbeitung nach der Vorlage in _Uploads eine reine Icon-Leiste ("links eine kleine icon
     leiste"): 64px fassen den 44px-Knopf samt Luft, der Name klappt beim Ueberfahren daneben auf,
     ohne die Leiste zu verbreitern. Die Kontextspalte steht auf jeder Seite offen. */
  --nav-breite: 64px;
  --kontext-breite: 380px;
  /* Hoehe der Kopfzeile: links der ausgeschriebene Hub-Name, rechts die Statusleiste. Die
     Kontextspalte beginnt darunter (und endet unten um denselben Betrag frueher, damit sie
     gleichmaessig im Bild steht). */
  --hub-titel-hoehe: 40px;
  /* Abstand der schwebenden Flaechen zum Fensterrand und zueinander (2026-09-13, Vorlage in
     _Uploads): Icon-Leiste, Kontextmenue und Inhalt stehen als eigene Karten nebeneinander,
     nichts klebt an der Kante. Eine Variable, damit body-Padding und beide Leisten synchron
     bleiben. */
  --schale-gap: 12px;
  /* Nutzer-Feedback: "mache den dashboard bereich von der höhe ruhig bis zum ende vom bildschirm,
     gerade ist da noch ein gap unten" -- nur das Dashboard (nicht die anderen ~8 Seiten, die
     bewusst die feste --fixed-content-height oben nutzen, siehe deren Kommentar) bekommt eine
     echte, an die tatsaechliche Fensterhoehe gekoppelte Hoehe. 90px = Abstand zwischen Header-
     Unterkante und Grid-Oberkante (35px Luecke + 21px .dashboard-toolbar + 10px Luecke) plus 24px
     Luft am unteren Rand, per Messung im Chromium-Sandkasten ermittelt. */
  --dashboard-content-height: calc(100vh - var(--sticky-header-height, 78px) - 90px);
  /* Hoehe der iOS-artigen Bottom-Tab-Bar (nur Mobile, siehe .mobile-tab-bar) -- eigene Variable,
     damit Seiteninhalt (body padding-bottom) und die PWA-/Push-Hinweisleiste (.pwa-banner) sich
     zuverlaessig danach richten koennen, statt zwei Stellen mit demselben Zahlenwert von Hand
     synchron zu halten. */
  --mobile-tab-bar-height: 58px;
  /* Nutzer-Feedback: "die mobile bar unten bitte floating und nicht bis zu den rändern und
     weiter oben" -- Abstand der schwebenden Bar zum Bildschirmrand (unten/links/rechts), auch
     fuer body-Padding und den PWA-Banner-Abstand mitverwendet, damit alle drei synchron bleiben. */
  --mobile-tab-bar-gap: 14px;
  /* Breite der Inhaltsflaeche. Die schwebende Statusleiste rechnet mit demselben Wert, damit sie
     auf breiten Bildschirmen exakt so breit ist wie main darunter (Nutzerwunsch 2026-09-13:
     "mache mir die menueleiste oben genauso breit wie den inhalt") -- main zieht sein Padding von
     dieser Breite ab, die Leiste muss es deshalb ebenfalls abziehen. */
  --inhalt-max: 1680px;
}

/* ===== Heller Modus (2026-09-13) =====
   Nutzerwunsch: "wir machen einen umschalter dazu von dark mode und light mode. wobei light mode
   eher wie ein papier nicht 100% white ist" -- die Flaechen sind warme Papiertoene (#f1eee8 als
   Grund, #fbfaf7 als Karte), nicht Reinweiss. Es wird NUR die Farbliste getauscht: Abstaende,
   Radien, Schrift und alle Regeln weiter unten bleiben, weil sie ueber diese Variablen laufen.
   applyTheme() (app.js) setzt data-theme am <html>; ohne Attribut gilt der dunkle Modus von oben.
   --accent steht bewusst nicht hier: die Akzentfarbe gehoert der Organisation und wird von
   applyAccentColor() als Inline-Stil auf demselben Element gesetzt (schlaegt beide Bloecke). */
:root { color-scheme: dark; }
html[data-theme="hell"] {
  color-scheme: light;
  --bg: #e7e2d8;
  --bg-verlauf: linear-gradient(157deg, #efebe2 0%, #e7e2d8 48%, #ddd7ca 100%);
  /* Auf Papier reicht ein kleinerer Schritt: dieselbe Mischung wie im Dunkeln wuerde als grauer
     Balken ueber dem hellen Grund liegen. Die Richtung bleibt dieselbe -- dunkler als der Grund. */
  --wellen-a: color-mix(in srgb, var(--bg) 80%, #000);
  --wellen-b: color-mix(in srgb, var(--bg) 90%, #000);
  --wellen-deckkraft: 0.6;
  --widget-schleier: rgba(35, 32, 25, 0.05);
  --glas-kante: rgba(255, 255, 255, 0.65);
  --glas-schatten: rgba(35, 32, 25, 0.07);
  /* Auf Papier wird das Bild aufgehellt statt abgedunkelt -- die Schrift darueber ist dunkel. */
  --agenda-bild-filter: blur(2px) brightness(1.45) saturate(0.75) opacity(0.55);
  --zone-bg: #ddd8cd;
  --panel: #fdfcf9;
  --panel-2: #f6f3ec;
  --panel-hover: #eeeae1;
  /* Auf Papier liegt die Flaeche schon fast auf Weiss -- aufhellen bringt dort nichts sichtbares,
     die Inhaltskachel muss also eine Spur DUNKLER liegen. Richtung umgedreht, Schritt gleich gross
     wie im dunklen Modus. */
  /* Auf Papier wird abgedunkelt -- mit einem warmen Braungrau statt reinem Schwarz, sonst kippt
     der warme Ton der Flaeche ins Graue. */
  /* Gegenstuecke zu den festen dunklen Werten (2026-09-16): Knopf und Aktiv-Flaeche stehen im
     dunklen Block als feste Toene, ohne diese Zeilen zoege der helle Modus sie mit. */
  --knopf: #f6f3ec;
  --aktiv-kachel: #dcd9ef;
  --aufhellung: #6b6254;
  --inhalt-kachel: color-mix(in srgb, var(--panel-2) 92%, var(--aufhellung));
  --inhalt-kachel-hover: color-mix(in srgb, var(--panel-2) 88%, var(--aufhellung));
  --border: #e0dbd0;
  --text: #232019;
  --text-dim: #736d63;
  /* Akzent als SCHRIFT: auf hellem Grund muss er abgedunkelt werden statt aufgehellt. */
  --accent-light: color-mix(in srgb, var(--accent) 78%, black);
  /* Toenungen kommen auf hellem Grund von Schwarz, nicht von Weiss. */
  --tint-subtle: rgba(35,32,25,0.035);
  --tint: rgba(35,32,25,0.055);
  --tint-hover: rgba(35,32,25,0.09);
  --tint-dunkel: rgba(35,32,25,0.09);
  --scrim: rgba(35,32,25,0.32);
  --success-text: #15803d;
  --highlight: #a9700a;
  /* Bernstein als SCHRIFT ist auf Papier praktisch unlesbar (Kontrast 1,5 gegen eine helle Kachel,
     2026-09-16 im Kontrastlauf gefunden: "morgen" in der Agenda). Derselbe dunklere Ton wie
     --highlight; als Flaeche bleibt er kraeftig genug, die Schrift darauf ist ohnehin weiss. */
  --warning: #a9700a;
  --warning-hover: #8a5a08;
  /* Schatten duerfen auf Papier nur andeuten -- die dunklen Werte von oben wirken hier wie Russ. */
  --shadow-sm: 0 1px 2px rgba(35,32,25,0.07);
  --shadow-md: 0 6px 20px rgba(35,32,25,0.10);
  --shadow-popover: 0 8px 24px rgba(35,32,25,0.14);
  --shadow-lg: 0 10px 30px rgba(35,32,25,0.16);
}


/* Touch-Geraete (Finger statt Maus): Knoepfe und Felder 44px hoch -- die Groesse, die Apple fuer
   Tippflaechen empfiehlt. Nach dem Eingabegeraet statt nach der Fensterbreite, damit auch ein
   Tablet im Querformat (ueber 880px) sie bekommt und ein schmales Desktop-Fenster nicht. */
@media (pointer: coarse) {
  :root { --control-h: 44px; --control-h-sm: 36px; }
}

/* ---------- Schrift: Inter ----------
   Seit 2026-09 eine feste Oberflaechenschrift statt der Systemschrift des Geraets (Segoe UI, San
   Francisco, Roboto ... sahen je nach Geraet verschieden aus und liefen verschieden breit). Inter ist
   fuer Bildschirm-Oberflaechen gebaut und die Empfehlung fuer ruhige dunkle Werkzeug-Oberflaechen.
   Selbst ausgeliefert statt von Google Fonts: keine Nutzer-IP an Dritte, funktioniert auch ohne
   Internet. Variable Schrift (eine Datei fuer alle Staerken), aufgeteilt wie bei fontsource in
   Lateinisch (reicht fuer Deutsch) und Lateinisch-erweitert, der Browser laedt nur, was er braucht.
   font-display: swap zeigt bis zum Laden die Systemschrift statt unsichtbarem Text.
   Lizenz: SIL Open Font License, siehe /fonts/OFL-Inter.txt. */
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("/fonts/inter-latin-wght-5.3.0.woff2") format("woff2-variations"), url("/fonts/inter-latin-wght-5.3.0.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("/fonts/inter-latin-ext-wght-5.3.0.woff2") format("woff2-variations"), url("/fonts/inter-latin-ext-wght-5.3.0.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

* { box-sizing: border-box; }

* { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
/* scrollbar-gutter:stable NICHT mehr global -- es reserviert Platz auch bei overflow:hidden,
   das technisch nie einen Scrollbar zeigen kann. Genau dieses Muster wurde in dieser Runde
   bereits sechsmal einzeln nachgepatcht (.widget, .agenda-list, .widget-chat, .agenda-item,
   .links-list, .activity-list, .tile-card), zuletzt kamen mit .boards-game-row (Compendium),
   .media-game-tile-grid (Medien) und .modal (Event-/Geburtstag-Formular) weitere unbemerkte
   Faelle dazu. Statt jede neue Stelle einzeln zu flicken: der globale Default ist jetzt "auto"
   (kein Bug mehr moeglich), die einzelnen scrollbar-gutter:auto-Overrides von vorher bleiben
   bestehen (jetzt redundant, aber harmlos). Einziger Kompromiss: Container mit echtem
   overflow:auto/scroll bekommen keinen reservierten Platz mehr, ein Scrollbar-Erscheinen kann
   dort also einen minimalen 1-Frame-Reflow verursachen -- deutlich selteneres/kleineres Problem
   als die bisherige, auf jeder Karte sichtbare tote Zone. */
/* Schmalerer Track ohne border-left/background-clip-Kombination -- die alte Variante
   erzeugte in manchen Chromium-Versionen einen sichtbaren Rendering-Fehler (Knick/Riss)
   an den abgerundeten Enden des Thumbs. */
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--border); border-radius: var(--radius-xs); }
*::-webkit-scrollbar-thumb:hover { background: var(--text-dim); }
*::-webkit-scrollbar-corner { background: transparent; }

@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
@keyframes modalPop {
  from { opacity: 0; transform: scale(0.96) translateY(8px); }
  to { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes overlayFade {
  from { opacity: 0; }
  to { opacity: 1; }
}
/* Nutzer-Feedback: "bitte komplett durch die bank animationen wie bei ios apps hinzufügen, wenn
   man fenster wechselt" -- die vollflaechigen Mobile-"Seiten" (Event-Vorschau, Medien-Ordner,
   Medien-Upload, Profil, siehe jeweiliges @media (max-width: 720px) weiter unten) nutzten bisher
   dieselbe generische modalPop-Animation (leichtes Einskalieren) wie kleine Desktop-Popups -- fuer
   eine seitenfuellende Ansicht wirkt das schwach/unpassend. iosSheetIn ahmt stattdessen das native
   iOS-Sheet nach: die "Seite" faehrt von unten komplett ins Bild, mit der leicht ueberschiessenden
   Ease-Kurve (cubic-bezier), die auch iOS fuer Sheet-Praesentationen verwendet.
   */
@keyframes iosSheetIn {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* Defensives Sicherheitsnetz gegen horizontales Scrollen/Verschieben der ganzen Seite (Nutzer-
   Feedback: Kopfzeile+Agenda auf echten Geraeten rechts "abgeschnitten") -- betrifft nur die
   SEITE selbst, einzelne Container mit eigenem overflow-x:auto (z.B. .agenda-list-main,
   .modal-category-tabs) scrollen innerhalb ihrer eigenen Box weiterhin normal. */
/* Nutzer-Feedback: "die menüleiste unten sollte wirklich fest sein. ab und an bewegt sie sich
   noch" -- klassisches iOS-Safari-Problem: Rubber-Band-Overscroll am oberen/unteren Rand
   verschiebt kurzzeitig den kompletten sichtbaren Viewport samt fixed-positionierter Elemente
   (siehe .mobile-tab-bar), bis die Seite zurueckschnellt. overscroll-behavior-y:none
   unterbindet dieses Ueberziehen, statt nur das Symptom an der Tab-Bar selbst zu kaschieren.
   Bugfix: gehoert auf <html>, nicht <body> -- die Seite hat keinen eigenen Body-Scrollcontainer
   (kein overflow-y auf body gesetzt), im Standards-Mode ist <html> das tatsaechliche Root-
   Scroll-Element. overscroll-behavior auf dem falschen (nicht scrollenden) Element fuehrte in
   manchen mobilen Browsern dazu, dass Touch-Scrollen auf der Seite komplett ausfiel, bis ein
   Reflow (z.B. durch das Oeffnen eines Modals) es wieder "reparierte" (Nutzer-Feedback: "das
   dashboard lässt sich irgendwie nur scrollen wenn eine event übersicht geöffnet ist"). Einzelne
   scrollbare Container innerhalb der Seite (z.B. .modal-body) sind davon unberuehrt und behalten
   ihr eigenes Scroll-Verhalten. */
html { overflow-x: hidden; overscroll-behavior-y: none; }
/* Bugfix (Nutzer-Feedback: "android kann ich nur auf dem iphone die agenda scrollen. android geht
   nicht und firefox geht auch nicht wenn ich mobil schaue") -- body hatte zusaetzlich zu <html>
   noch sein EIGENES overflow-x:hidden (reine Redundanz, siehe Kommentar oben: <html> ist bereits
   das tatsaechliche Root-Scroll-Element, <body> hat gar keinen eigenen Scrollcontainer). Setzt ein
   Browser aber ueberhaupt einen expliziten overflow-Wert auf <body>, kann das je nach Engine die
   Ermittlung des Root-Scroll-Elements beeinflussen (dieselbe Kategorie Bug wie beim bereits
   dokumentierten overscroll-behavior-Fall oben, nur diesmal offenbar bei Chromium/Android und
   Gecko/Firefox statt bei den dort betroffenen Browsern). Entfernt -- <html> allein reicht bereits
   fuer den beabsichtigten Zweck (kein horizontales Scrollen der Seite). */
body {
  margin: 0;
  /* Firefox kennt das WebKit-eigene "-apple-system" nicht und faellt auf macOS bisher direkt auf
     Helvetica durch (uebrige Kette Segoe UI/Roboto ist dort nicht installiert) -- Safari/Chrome/Edge
     nutzen dagegen San Francisco. Verschiedene Schriftart != nur "Rendering" -- andere Zeichenbreiten/
     x-Hoehe erklaeren "verschobene"/"kleinere" Labels zwischen den Browsern. Der Standard-Keyword
     system-ui (von Firefox seit v92 unterstuetzt) faengt das ab und liefert ueberall die native
     UI-Schrift des Betriebssystems. */
  /* Inter zuerst (siehe @font-face oben), die Systemschrift-Kette dahinter bleibt als Rueckfall. */
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg-verlauf) var(--bg) fixed;
  color: var(--text);
  /* Ohne das rendern WebKit/Blink (Safari, Edge, Chrome) helle Schrift auf dunklem Hintergrund
     duenner/heller als Firefox (Gecko) -- macht v.a. kleine Labels (0.6-0.78rem) dort
     wirkungslos kleiner/verwaschener, obwohl Layout und Pixelmasse identisch sind. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}
/* Inter braucht in grossen Graden etwas engere Laufweite, sonst wirken Titel luftig (Empfehlung der
   Schrift selbst: je groesser, desto enger). Nur fuer die grossen Titel, Fliesstext bleibt normal. */
h1, h2, .sammlung-heading, .event-preview-hero-date { letter-spacing: -0.01em; }
/* Knoepfe, Felder und Auswahllisten erben die Schrift in Chromium NICHT, der Browser gibt ihnen seine
   eigene Systemschrift -- ohne diese Regel liefen z.B. die Reiter der Kopfleiste nach der Umstellung
   auf Inter weiter in der alten Schrift (gemessen: Titel schmaler, Reiter pixelgleich wie vorher).
   Bewusst nur die Familie, nicht "font: inherit": das wuerde auch die Groesse erben lassen und
   Knoepfe ohne eigene Groesse veraendern. */
button, input, select, textarea { font-family: inherit; }
::selection { background: var(--accent-strong); color: var(--text); }

/* Muster hinter der GANZEN Seite (2026-09-15: "koennen wir so ein pattern subtil nachbauen und
   ueberall als background machen. nicht nur in der main area?"). Die Wellen sind eine SVG und
   dienen nur als MASKE -- die Farbe kommt aus dem Verlauf darunter und passt sich damit Modus und
   Organisationsfarbe an. Fixiert und hinter allem (z-index: -1).

   FEST, nicht bewegt (2026-09-16: "wir machen bitte den hintergrund nicht beweglich mache ihn
   einfach als pattern so wie er ist fest"). Die Bewegung hat spuerbar Leistung gekostet: ueber dem
   Muster liegen ueberall Glasflaechen mit backdrop-filter, und jedes Bild, in dem sich das Muster
   bewegt, zwingt den Browser, deren Weichzeichner neu zu rechnen -- eine Stufenanimation hat das
   gedaempft, ein stehendes Muster kostet gar nichts. Wer das je wieder bewegen will: nicht
   mask-position animieren (Neuzeichnen jedes Bild), sondern die Lage per transform schieben, und
   die Kosten vorher mit TEST=hintergrundlast messen.

   Die Lage ist jetzt genau bildschirmgross (im Stehen braucht es keine zweite Kachel zum
   Hineinschieben mehr) -- die Maske kachelt trotzdem in x, die SVG ist dafuer periodisch gebaut
   (Summen ganzzahliger Harmonischer). */
body::before {
  content: ""; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
  z-index: -1; pointer-events: none;
  /* Verlaufsgeometrie ausdruecklich: derselbe Ausschnitt gilt auch fuer den Streifen in der
     Kopfleiste, der nur wenige Pixel hoch ist -- ohne feste Groesse haette er einen anderen
     Farbverlauf und der Uebergang waere zu sehen. */
  background: linear-gradient(120deg, var(--wellen-a), var(--wellen-b)) 0 0 / 100vw 100vh no-repeat;
  opacity: var(--wellen-deckkraft);
  -webkit-mask-image: url("/img/wellen-hinten.svg"); mask-image: url("/img/wellen-hinten.svg");
  /* HOEHE vorgeben, Breite mitwachsen lassen: die SVG traegt preserveAspectRatio="none" und wurde
     mit "100vw 100vh" auf das Fenster gezerrt -- auf einem hohen, schmalen Handy quetschte das die
     Wellen zusammen (2026-09-16: "der background auf dem handy ist irgendwie cropped. er sieht so
     zusammengequetscht aus"). Mit auto-Breite behaelt das Muster sein Seitenverhaeltnis und
     kachelt waagerecht weiter (die SVG ist in x periodisch, die Naht faellt nicht auf). */
  -webkit-mask-size: auto 100vh; mask-size: auto 100vh;
  -webkit-mask-repeat: repeat-x; mask-repeat: repeat-x;
  -webkit-mask-position: 0 0; mask-position: 0 0;
}

/* R61 Nutzer-Feedback: "Menüleiste 2" (.status-ticker) sollte ueberall fest bleiben statt
   mitzuscrollen -- statt eines fragilen festen top-Offsets auf .status-ticker (die Topbar kann
   bei schmalen Fenstern auf zwei Zeilen umbrechen, ihre Hoehe ist also nicht konstant) sind
   .topbar + .status-ticker jetzt gemeinsam in einem sticky-Wrapper, siehe index.html -- beide
   bleiben dadurch immer als Einheit oben, egal wie hoch die Topbar gerade ist. .topbar selbst
   braucht deshalb kein eigenes position:sticky mehr. */
/* width:100% explizit -- Nutzer-Feedback samt Screenshot: auf echtem iOS Safari war der Hintergrund
   von Topbar+Ticker sichtbar schmaler als der Inhalt darunter (etwa bis 2/3 der Breite), obwohl
   getBoundingClientRect() im Chromium-Sandbox-Test bereits korrekt volle Breite meldete -- ein
   bekannter WebKit-Eigenheit: position:sticky-Elemente verlassen sich dort nicht zuverlaessig auf
   die Block-Element-Default-Breite (100% des Containers), sondern koennen sich stattdessen an
   ihrem INHALT orientieren. Explizites width:100% erzwingt die korrekte Breite unabhaengig vom
   Rendering-Engine-Verhalten. */
.app-header-sticky { position: sticky; top: 0; z-index: var(--z-header); width: 100%; }
/* Kopfleiste in EINER Zeile von 881px (darunter die Handy-Leiste) bis ganz breit. Bis 2026-09
   brauchte sie 1513px dafuer und brach bei 1440px in zwei, bei 1180px in drei Zeilen um -- dafuer
   jetzt kompaktere Reiter, der Profilknopf ohne Namen unter 1440px und die Reiter nur als Icon
   unter 1300px (siehe .main-nav unten). Kein flex-wrap mehr: solange Umbrechen erlaubt war,
   schrumpfte nichts, und ein langer Organisationsname haette die Leiste wieder zweizeilig
   gemacht. Stattdessen behalten Navigation und rechte Gruppe ihre Breite und der Titel kuerzt
   sich mit "...". */
.topbar {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  padding: var(--space-5) var(--space-12);
  /* Links/rechts wächst das Padding auf sehr breiten Screens mit, damit die Menüpunkte an
     derselben Stelle wie main (max-width:1680px, siehe unten) beginnen, statt bei jeder
     Breite flach bei 24px zu bleiben. */
  padding-left: max(var(--space-12), calc((100% - 1680px) / 2 + var(--space-12)));
  padding-right: max(var(--space-12), calc((100% - 1680px) / 2 + var(--space-12)));
  background: var(--panel);
  flex-wrap: nowrap;
}

/* Die Reiter standen bis 2026-09-13 als Faehnchen an einer Farblinie in dieser Leiste. Nutzerwunsch
   danach: "das menue komplett aendern oben. das hervorgehobene nicht so mit einem trapez. und wir
   machen das menue ganz anders. modern linke seite von oben nach unten" -- Navigation, Org-Auswahl
   und die Knoepfe rechts sind in die Spalte .app-nav gewandert (siehe dort), die Kopfleiste ist am
   Desktop ganz ausgeblendet und traegt auf dem Handy weiter Titel und Knoepfe. */
.topbar h1 { font-size: var(--fs-xl); margin: 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.main-nav, .user-menu { flex-shrink: 0; }

nav { display: flex; gap: var(--space-3); }

/* #nav-toggle-btn statt nur .nav-toggle-btn -- als reine Klassenregel wurde sie von der
   spaeter im Stylesheet stehenden .icon-btn-Regel (ebenfalls display, gleiche Spezifitaet)
   ueberschrieben und blieb dadurch auf JEDER Fensterbreite sichtbar, nicht nur mobil. */
#nav-toggle-btn.nav-toggle-btn { display: none; }
.nav-backdrop {
  display: none;
  position: fixed; inset: 0; background: var(--scrim);
  z-index: var(--z-sticky);
}
.nav-backdrop.open { display: block; animation: overlayFade var(--duration-fast) ease both; }

@media (max-width: 880px) {
  /* Nutzer-Feedback: "nur die agenda ... alles andere kann mobil weg" -- die Bottom-Tab-Bar
     (Agenda + Einstellungen, siehe #mobile-tab-bar) ersetzt jetzt vollstaendig das Hamburger-Menü
     als mobile Navigation, statt nur eine schnellere Abkuerzung dazu zu sein. Hamburger-Button UND
     Drawer sind daher auf dieser Breite komplett ausgeblendet, nicht mehr nur ausgelagert. */
  #nav-toggle-btn.nav-toggle-btn { display: none; }
  .main-nav { display: none; }
  /* Etwas flacher (2026-09-15: "mache mobil die menueleiste oben etwas schmaler") -- die Hoehe
     kommt fast nur aus diesem Innenabstand, die Knoepfe selbst bleiben in Tippgroesse.
     --sticky-header-height misst live mit (syncStickyHeaderHeight()), die Vollbild-Flaechen
     darunter ruecken also automatisch nach. */
  .topbar { gap: var(--space-6); padding: var(--space-3) var(--space-7); }
  .topbar h1 { font-size: var(--fs-body); }
  /* .org-title-switch-wrap traegt hier das flex:1 (statt vorher direkt auf h1) -- damit fuellt der
     WRAPPER die verfuegbare Breite (verdraengt #mobile-refresh-btn etc. weiterhin nach rechts),
     waehrend h1 selbst NICHT waechst (siehe Basis-Regel bei .org-title-switch-wrap) und dadurch nur
     so breit wie sein Text bleibt -- der Pfeil-Button sitzt so direkt hinter dem sichtbaren Text
     statt an den rechten Rand des ganzen Wrappers verdrängt zu werden. */
  .org-title-switch-wrap { flex: 1; min-width: 0; }
  .topbar h1 { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* Nutzer-Feedback: "bitte auch in der tittelleiste eig alles ausblenden und in die leiste unten
     bringen. ich brauche agenda, kalender, profil ... und angeheftet." -- Kopfzeile blieb auf
     Mobile nur noch Org-Icon+Name. Nutzer-Feedback (neueste Runde): "jetzt da wo der button
     angeheftet weg ist kommt wieder der profil button da hin" -- #pinned-panel-btn ist jetzt auf
     Mobile ausgeblendet (zieht stattdessen in die Agenda-Kachel, siehe #agenda-pinned-btn), der
     freigewordene Platz in der Kopfzeile geht an #mobile-topbar-profile-btn.
     #settings-btn braucht !important: fuer Admins setzt JS dort eine INLINE style.display (siehe
     applyPermissions() in app.js), die eine reine Klassenregel sonst nicht zuverlaessig schlagen
     wuerde. */
  /* Mobil-Feedback (aktuelle Runde): "bitte entferne den profil button unten aus der mobile-
     menüleiste und tu ihn wieder nach oben rechts in die titelleiste also menü 1" -- Kehrtwende,
     #profile-menu-btn ist daher aus dieser Ausblend-Liste raus, zeigt aber wie im Chevron-Zustand
     unten kompakter (nur Avatar, kein Name/Chevron -- fuer den knappen Platz in der Kopfzeile). */
  #settings-btn { display: none !important; }
  #profile-menu-btn #user-display-name,
  #profile-menu-btn .mdi-chevron-down { display: none; }
  #profile-menu-btn { padding: var(--space-2); }
  #lang-toggle-btn { display: none; }
  /* Mobil-Feedback: "das such icon mobil in der menüleiste ausblenden und dafür dort einen refresh
     button einbauen der die seite neu lädt" */
  #search-sidebar-btn { display: none; }
  /* .mobile-refresh-btn-Sichtbarkeit steht bewusst NICHT hier, sondern weiter unten direkt neben
     ihrer Basis-Ausblend-Regel (Reihenfolge im Stylesheet ist bei gleicher Spezifitaet + !important
     entscheidend, siehe dortiger Kommentar). */
  /* Der Knopf steht seit 2026-09-15 auf JEDER Breite in der Agenda-Kachel (der Zwilling in der
     Leiste links ist entfallen) -- die Regel hier ist damit gegenstandslos, bleibt aber als
     Erinnerung stehen, dass er mobil zu den drei Nachbarknoepfen gehoert. */
  /* Mobil-Feedback: "die buttons für hinzufügen, vergangene events und filter hinter einem drei
     punkte button machen, wie das erstellen sub menü" -- die drei Original-Buttons bleiben im DOM
     (ihre Popover-Logik wird von showAgendaMoreMenu() in app.js direkt wiederverwendet), werden
     hier nur optisch durch den einen Mehr-Button ersetzt. */
  /* #agenda-add-btn braucht !important: applyPermissions() (app.js) setzt dort eine INLINE
     style.display je nach can_create-Recht, die eine reine Klassenregel sonst nicht zuverlaessig
     schlagen wuerde (gleiches Muster wie bei #settings-btn oben). */
  #agenda-past-btn, #agenda-filter-btn { display: none; }
  #agenda-add-btn { display: none !important; }
  /* .agenda-more-mobile-btn-Sichtbarkeit steht bewusst NICHT hier, siehe Kommentar bei ihrer
     Basis-Ausblend-Regel weiter unten im Stylesheet. */
  /* Nutzer-Feedback: "das sollte aber auch nicht mobil sichtbar sein" (Anleitung-Button).
     !important noetig: die unconditional .guide-btn-Basisregel (display:flex) steht WEITER UNTEN
     im Stylesheet und wuerde eine reine Klassenregel hier sonst per Quellreihenfolge ueberstimmen
     (gleiches Muster wie beim .status-ticker-Fix an anderer Stelle in dieser Datei). */
  .guide-btn { display: none !important; }
}


.tab-btn {
  background: var(--knopf);
  border: none;
  color: var(--text-dim);
  padding: var(--space-4) var(--space-7);
  border-radius: var(--radius);
  cursor: pointer;
  font-size: var(--fs-lg);
}
.tab-btn { transition: background var(--duration-fast) ease, color var(--duration-fast) ease, transform var(--duration-fast) ease; }
.tab-btn:hover:not(.active) { background: var(--panel-hover); color: var(--text); transform: translateY(-1px); }
/* Auswahl ist Toenung, nicht volle Akzentfarbe: die bleibt Aktionen (.primary-btn) vorbehalten,
   sonst konkurrieren gewaehlter Reiter und Hauptknopf um den Blick. Die Hauptnavigation oben hat
   ihr eigenes Faehnchen (.main-nav .tab-btn.active), die Handy-Leiste ihre eigene Farbe. */
.tab-btn.active { background: var(--accent-tint); color: var(--text); }
/* Nutzer-Feedback: "das gradient auch in der 1. menüleiste bei den buttons (dashboard, kalender)
   hinzufügen" -- gleicher Gradient wie die Heute-Datums-Zahl im Kalender und die Agenda-Kategorie-
   Leisten, nur fuer die Hauptnavigation oben (nicht die .tab-btn-Basisklasse selbst, die auch von
   Filter-Pillen/Media-Upload-Umschaltern geteilt wird). */
/* Bugfix: "animation: ... both" (fill-mode "both") liess die "to"-Keyframe (transform:
   translateY(0)) DAUERHAFT auf .tab-content stehen, auch lange nach Animationsende -- ein
   transform-Wert ungleich "none" erzeugt aber IMMER einen neuen Containing Block fuer
   position:fixed-Nachfahren, selbst bei translateY(0) (0px Offset macht dabei keinen
   Unterschied). Betraf konkret das Rooms-Detail-Sheet (.boards-main.mobile-room-open,
   position:fixed;inset:0 -- verschachtelt in #tab-chatrooms, einem .tab-content), dessen
   Groesse dadurch faelschlich relativ zur (nicht bildschirmfuellenden) .tab-content-Box statt
   zum echten Viewport berechnet wurde -- das Sheet blieb dadurch auf einen Bruchteil des
   Bildschirms verkleinert, mutmasslich (mit-)ursaechlich fuer die "scrollen geht nicht"/
   Layout-Berichte auf Mobil. Ohne "both" verschwindet der Transform nach Animationsende wieder
   (Element faellt zurueck auf seinen unanimierten, transformlosen Stand) -- optisch identisch
   (translateY(0) == kein Transform), aber ohne den Containing-Block-Seiteneffekt. */
.tab-content { animation: fadeInUp var(--duration-base) ease; }

/* Top-Nav-Icons: outline (mdi) statt farbiger Emoji, gross und leicht schraeg gestellt,
   bleiben aber vollstaendig innerhalb der Button-Flaeche (kein Ueberlappen des Rands). */
/* ---------- Menuespalte links (2026-09-13) ----------
   Nutzerwunsch: "modern linke seite von oben nach unten ... wenn ich bspw. auf notizen oder rooms
   klicke sollte das dann im linken menue erscheinen". Zwei feste Spalten am linken Rand: .app-nav
   mit Org-Auswahl, den Reitern und unten den Knoepfen der frueheren Kopfleiste, daneben
   .app-context mit der Liste des aktiven Bereichs oder einer Sprung-/Filterliste. Beide sind
   position:fixed, den Platz dafuer schafft das padding-left am body -- so bleiben sie beim
   Scrollen stehen, ohne dass jede Ansicht ihre Hoehe kennen muss. Unter 881px sind sie
   ausgeblendet (JS haengt die Knoepfe zurueck in die Kopfleiste, siehe applyNavPlacement()).

   Optik nach den Vorlagen in _Uploads (2026-09-13, "orientieren wir uns sehr stark an diesen
   designs"): keine Kaesten um Listen, sondern Gruppen mit kleiner Ueberschrift und duenner
   Trennlinie; Eintraege sind Zeilen, der gewaehlte eine gefuellte Pille in der Akzentfarbe. */
.app-nav, .app-context {
  /* Beide Leisten beginnen dort, wo rechts der Inhalt beginnt (Nutzerwunsch 2026-09-13: "lasse die
     seitenleiste erst am inhalt der sachen rechts anfangen ... das beim namen mehr platz darunter
     ist") -- also unter der Kopfzeile plus dem oberen Innenabstand von main.
     --sticky-header-height haelt die tatsaechliche Hoehe der Kopfzeile live nach. */
  position: fixed; z-index: var(--z-header);
  top: calc(var(--sticky-header-height, 78px) + var(--space-10));
  bottom: calc(var(--schale-gap) + var(--hub-titel-hoehe));
  background: var(--panel); border-radius: var(--radius-xl); box-shadow: var(--shadow-md);
  display: flex; flex-direction: column; min-width: 0;
}
/* Die Icon-Leiste liegt ueber der Kontextspalte -- die aufklappenden Namen ragen in sie hinein. */
.app-nav {
  left: var(--schale-gap); width: var(--nav-breite); z-index: calc(var(--z-header) + 1);
  align-items: center; gap: var(--space-4); padding: var(--space-6) var(--space-5);
  /* Kein Zuschneiden: die Namensfahnen der Reiter stehen ausserhalb der Leiste. */
  overflow: visible;
}
.app-nav[hidden], .app-context[hidden] { display: none; }
body.hat-navspalte { padding-left: calc(var(--nav-breite) + var(--schale-gap) * 2); }
body.hat-navspalte.hat-kontextspalte { padding-left: calc(var(--nav-breite) + var(--kontext-breite) + var(--schale-gap) * 3); }
/* Der Name des Hubs steht ausgeschrieben in der Kopfzeile ueber der Kontextspalte (2026-09-13,
   Nutzerwunsch "schreibe mir den titel des hub immer aus"): in die 64px-Leiste passt er nicht, und
   als blosses Logo war nicht zu erkennen, in welchem Hub man steckt. Es ist derselbe Knoten wie auf
   dem Handy (applyNavPlacement() haengt ihn um), Klick wechselt weiterhin die Organisation. */
.app-hub-titel {
  /* Ganz links oben (Nutzerwunsch 2026-09-13) -- ueber BEIDEN Leisten, nicht erst ueber der
     zweiten. Die Breite endet genau an der Kante der zweiten Spalte: der Name der offenen Seite
     steht rechtsbuendig darin, die zwei Schraegstriche dahinter ragen in den Inhaltsbereich. */
  position: fixed; z-index: var(--z-header);
  left: var(--schale-gap); top: var(--schale-gap);
  width: calc(var(--nav-breite) + var(--kontext-breite) + var(--schale-gap));
  height: var(--hub-titel-hoehe);
  /* Rechts kein Innenabstand: die Gruppe "Seitenname //" soll buendig ueber der Kante der zweiten
     Spalte enden, nicht ein paar Pixel davor. */
  display: flex; align-items: center; gap: var(--space-6); padding: 0 0 0 var(--space-4); min-width: 0;
}
/* Name der offenen Seite in der Farbe der Organisation, mit den zwei Schraegstrichen dahinter
   rechtsbuendig ueber der zweiten Spalte (Nutzerwunsch 2026-09-13: "mache mir die ueberschrift
   bspw dashboard // direkt rechtsbuendig ueber die zweite linke seitenleiste"). Beide stehen im
   Fluss -- die Gruppe endet dadurch genau an der Kante der Spalte darunter. */
.app-hub-titel .ticker-seitenname { margin-left: auto; }
.app-hub-titel .app-hub-trenner { margin-left: calc(var(--space-6) * -1 + var(--space-4)); }
.app-hub-trenner {
  font-size: var(--fs-2xl); font-weight: var(--fw-bold); letter-spacing: -0.06em;
  color: var(--text-dim); opacity: 0.85; line-height: 1;
}
.app-hub-titel[hidden] { display: none; }
.app-hub-titel .org-title-switch-wrap { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.app-hub-titel #org-title {
  display: flex; align-items: center; gap: var(--space-5); min-width: 0; margin: 0;
  font-size: var(--fs-2xl); font-weight: var(--fw-semibold); cursor: pointer;
}
.app-hub-titel #org-title .org-title-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-hub-titel #org-title .mdi { font-size: var(--fs-2xl); line-height: 1; color: var(--accent-light); }
.app-hub-titel #org-title .org-logo-img { width: 26px; height: 26px; border-radius: var(--radius-sm); }
.app-hub-titel .org-title-switch-btn { color: var(--text-dim); }
.app-nav-tabs { min-width: 0; width: 100%; overflow: visible; }
.main-nav { flex-direction: column; align-items: center; gap: 0; }
/* Gruppe = ein paar Reiter, getrennt durch eine feine Linie; die Ueberschrift der Gruppe passt
   nicht in die Icon-Leiste und ist dort ausgeblendet. */
.nav-gruppe {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  padding-bottom: var(--space-4); margin-bottom: var(--space-4); border-bottom: 1px solid var(--border);
}
.nav-gruppe:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
.nav-gruppe-titel { display: none; }
.main-nav .tab-btn {
  position: relative;
  /* Die Reiter standen bis 2026-09-16 ohne Flaeche in der Leiste; seitdem tragen sie die
     KNOPFfarbe der Organisation ("bitte die buttons auf der linken menueleiste in der button farbe
     machen") -- nicht die Flaeche der .tab-btn-Basisklasse, die an --panel-2 haengt und sich mit
     der Flaechenfarbe mitbewegen wuerde. */
  background: var(--knopf);
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0;
  border-radius: var(--radius-md);
  font-size: var(--fs-lg); font-weight: var(--fw-medium); letter-spacing: 0; text-transform: none;
  color: var(--text-dim);
  transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.main-nav .tab-btn .nav-icon { font-size: var(--fs-2xl); line-height: 1; color: inherit; opacity: 0.9; }
.main-nav .tab-btn:hover { background: var(--knopf-hover); color: var(--text); transform: none; }
/* Die Beschriftung klappt beim Ueberfahren als Fahne neben dem Icon auf (Nutzerwunsch nach der
   Vorlage: "beim hover die icons eine aufklapp animation mit dem jeweiligen namen des menuepunktes
   ... auf den ich dann klicken kann"). Sie steckt IM Knopf und ist deshalb mitklickbar, ohne dass
   die Leiste breiter wird oder das Layout springt. */
.main-nav .tab-btn > span {
  position: absolute; left: calc(100% + var(--space-3)); top: 50%;
  display: flex; align-items: center; height: 38px; padding: 0 var(--space-8);
  border-radius: var(--radius-md); background: var(--panel-2); color: var(--text);
  box-shadow: var(--shadow-popover); white-space: nowrap;
  font-size: var(--fs-lg); font-weight: var(--fw-medium);
  opacity: 0; visibility: hidden; transform: translate(-8px, -50%) scale(0.96); transform-origin: left center;
  transition: opacity var(--duration-fast) ease, transform var(--duration-fast) var(--ease-spring),
              visibility var(--duration-fast);
}
.main-nav .tab-btn:hover > span, .main-nav .tab-btn:focus-visible > span {
  opacity: 1; visibility: visible; transform: translate(0, -50%) scale(1);
}
/* Gewaehlt: gefuellte Pille in der Akzentfarbe wie in der Vorlage. */
.main-nav .tab-btn.active {
  background: var(--accent); color: var(--text-on-color); font-weight: var(--fw-semibold);
  box-shadow: var(--shadow-sm);
}
.main-nav .tab-btn.active .nav-icon { color: var(--text-on-color); opacity: 1; }
.main-nav .tab-btn.active:hover { background: var(--accent); color: var(--text-on-color); }
/* Unten die Symbolknoepfe, ganz unten das Profil -- beides mittig in der schmalen Leiste. */
.app-nav-bottom {
  margin-top: auto; display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  width: 100%; padding-top: var(--space-4); border-top: 1px solid var(--border);
}
.app-nav-icons { display: flex; flex-direction: column; align-items: center; gap: var(--space-1); }
.app-nav-icons .icon-btn, .app-nav-icons .guide-btn {
  /* relative, nicht static: der rote Punkt am Changelog-Knopf (.changelog-badge, position:absolute)
     braucht seinen Knopf als Bezug -- ohne den sass er in der oberen Ecke der ganzen Leiste.
     inset:auto muss mit: Anleitung und Changelog sind ausserhalb der Leiste schwebende Rundknoepfe
     (position:fixed, left/bottom gesetzt) -- als relative Elemente haetten dieselben Werte sie um
     16px verschoben, mitten ueber die Nachbarknoepfe. */
  /* Gleiche Groesse wie der Profilknopf darunter (2026-09-16: "die unteren buttons linke
     menueleiste sollten alle so gross sein wie das profil icon") -- vorher 34px neben dessen 40px,
     was in einer Spalte aus lauter gleichen Flaechen sofort auffaellt. */
  position: relative; inset: auto; width: 40px; height: 40px; border-radius: var(--radius-md);
  /* Knopffarbe wie die Reiter darueber (2026-09-16) statt durchsichtig. */
  background: var(--knopf); border-color: transparent; color: var(--text-dim); font-size: var(--fs-lg);
}
.app-nav-icons .icon-btn:hover, .app-nav-icons .guide-btn:hover {
  background: var(--knopf-hover); color: var(--text);
}
/* Der Sprachumschalter ist ein Textknopf ("DE") -- gleiche Flaeche wie die Symbolknoepfe daneben. */
.app-nav-icons .lang-toggle-btn { width: 40px; padding: 0; font-size: var(--fs-2xs); font-weight: var(--fw-semibold); }
.app-nav-bottom .profile-menu-btn {
  background: var(--knopf); border-color: transparent; justify-content: center;
  width: 40px; height: 40px; padding: 0; border-radius: var(--radius-md);
}
.app-nav-bottom .profile-menu-btn:hover { background: var(--knopf-hover); }
/* In der Icon-Leiste steht nur der Avatar -- fuer den Namen ist kein Platz. */
.app-nav-bottom #profile-menu-btn #user-display-name { display: none; }
/* Niedrige Fenster (Laptop im Querformat, ~720px): alles eine Spur kompakter, damit die Leiste
   ohne Scrollen auskommt -- sie darf nicht zuschneiden, sonst waeren die Namensfahnen weg. */
@media (max-height: 820px) {
  .app-nav { gap: var(--space-2); padding: var(--space-5) var(--space-5); }
  .main-nav .tab-btn { width: 40px; height: 40px; }
  .nav-gruppe { padding-bottom: var(--space-3); margin-bottom: var(--space-3); }
  /* Niedriges Fenster: alle vier eine Stufe kleiner, aber weiterhin gleich gross. */
  .app-nav-icons .icon-btn, .app-nav-icons .guide-btn,
  .app-nav-icons .lang-toggle-btn, .app-nav-bottom .profile-menu-btn { width: 34px; height: 34px; }
}

.app-context {
  left: calc(var(--nav-breite) + var(--schale-gap) * 2); width: var(--kontext-breite);
  padding: var(--space-8) var(--space-6) var(--space-6); gap: var(--space-4);
}
/* Die Bereichslisten bringen ihr eigenes Markup mit (sie ziehen ja nur um) -- Breite, Kante und
   Flaeche kommen ab hier von der Spalte. outline statt border: die Seitenkarten tragen ihre Kante
   seit der Politur 2026-09-10 als 1px-outline, die ein border:none nicht mit abraeumt -- ohne die
   Zeile stand die Liste als zweiter Rahmen in der ohnehin gerahmten Spalte ("mache noch die doppel
   umrandung weg bspw. ist die agenda innerhalb nochmal umrandet"). */
/* Die feste Rasterhoehe der Dashboard-Widgets gilt hier nicht: in der Spalte fuellt die Agenda die
   ganze Hoehe (sonst waere die Liste nach zwei Karten zu Ende). */
.app-context .widget { height: auto; min-height: 0; }
.app-context > .wiki-sidebar, .app-context > .boards-sidebar,
.app-context > .dashboard-col-agenda-fixed, .app-context .widget-agenda-main {
  width: auto; max-width: none; min-height: 0;
  border: none; outline: none; box-shadow: none; background: transparent; padding: 0; border-radius: 0;
  /* Auch den Weichzeichner abraeumen (2026-09-14 gemeldet: "medien ist der einzige zweite linke
     reiter der keinen doppelten background hat"). Eine durchsichtige Flaeche reicht nicht: liegt
     ein zweiter backdrop-filter ueber dem der Spalte, wird der Grund darunter ein zweites Mal
     weichgezeichnet -- sichtbar als hellere Flaeche in der Flaeche. Medien fiel nur deshalb nicht
     auf, weil seine Liste in keiner dieser Klassen steckt. */
  -webkit-backdrop-filter: none;
          backdrop-filter: none;
}
.app-context > .wiki-sidebar, .app-context > .boards-sidebar,
.app-context > .dashboard-col-agenda-fixed { flex: 1 1 auto; }
/* Die Agenda-Spalte des Dashboards klebt in der Ansicht per position:sticky an der Kopfzeile und
   hat eine an die Fensterhoehe gekoppelte Hoehe -- in der Kontextspalte gibt die Spalte selbst die
   Hoehe vor, sonst ragt die Agenda unten heraus. */
.app-context > .dashboard-col-agenda-fixed { position: static; height: auto; }
/* Nur senkrecht scrollen -- waagerecht braucht es keine Leiste, der Hover-Schub hat oben seinen
   eigenen Platz bekommen. "clip" statt "hidden": clip schneidet zwar auch, aber erst ausserhalb
   des Paddings, und macht die Liste nicht zusaetzlich zum waagerechten Scroll-Container. */
.app-context .wiki-list, .app-context .boards-game-list {
  overflow-y: auto; overflow-x: clip; scrollbar-gutter: auto; min-height: 0;
}
/* Kopfzeile einer Liste (NOTIZEN, DATEIEN, ROOMS, MEDIEN, AGENDA): Versalien wie die
   Gruppen-Ueberschriften der Menuespalte, darunter eine Linie -- kein eigener Kasten mehr. */
.app-context :is(.wiki-toolbar, .boards-sidebar-heading-row, .widget-header) {
  /* Etwas mehr Luft unter der Trennlinie (2026-09-14: "erhoehe den abstand in der linken leiste
     zwei bei notes nach dem ueberschrifts platzhalter linie etwas mehr das es so ist wie bei
     rooms") -- Raeume und Alben bringen ihren Abstand ueber die Zeilenhoehe ihrer Eintraege mit,
     die schmalen Notiz-/Ordnerzeilen nicht. */
  padding: 0 var(--space-2) var(--space-4); margin-bottom: var(--space-5);
  border-bottom: 1px solid var(--border); background: transparent; min-height: 0;
}
.app-context :is(.wiki-toolbar, .boards-sidebar-heading-row, .widget-header) :is(h2, h3) {
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-dim);
}
/* Eintraege sind Zeilen mit Trennlinie, keine Kaesten ("viel weniger umrandungen um solche menues
   sondern eher linien zwischen den sachen"). Gilt fuer Raeume, Notizen, Ordner und Medien-Alben;
   die Agenda-Karten des Kalenders behalten ihre Form -- sie tragen Datum, Bild und Kategorie-
   Faehnchen und sind damit Inhalt, keine Menuezeile. */
/* Der Beitreten-Knopf im kleinen Handy-Fenster nimmt die ganze Breite (2026-09-16). */
.chatroom-join-modal #chatroom-join-modal-btn { width: 100%; justify-content: center; }
.chatroom-join-modal h2 { margin-bottom: var(--space-3); }

/* Kehrtwende 2026-09-16: die Zeilen waren durchsichtig und gingen dadurch in der Flaeche der
   Spalte unter ("rooms medien und notes eintraege haben den selben hintergrund wie der background
   selbst und somit sieht man ihn nicht. mache die hintergrundfarbe wie in der agenda") -- sie
   tragen jetzt dieselbe Kartenflaeche wie die Agenda-Eintraege, die Trennlinie entfaellt dafuer. */
.app-context :is(.chatroom-row, .wiki-list-item, .media-liste-zeile) {
  position: relative;
  border: none !important; outline: none !important; box-shadow: none !important;
  background: var(--panel-2) !important; border-radius: var(--radius-md);
}
/* !important, weil die Grundflaeche dieser Zeilen daneben ebenfalls !important traegt -- sonst
   gewinnt sie und der Hover ist gar nicht zu sehen. --panel-hover statt --tint: eine Toenung folgt
   keiner gewaehlten Hover-Farbe (2026-09-16: "die hover farbe greift noch nicht ... bei room
   eintraegen links zweite leiste"). */
.app-context :is(.chatroom-row, .wiki-list-item, .media-liste-zeile):hover { background: var(--panel-hover) !important; }
.app-context :is(.chatroom-row.active, .wiki-list-item.active, .media-liste-zeile.active) { background: var(--aktiv-kachel) !important; color: var(--aktiv-text); }
.app-context :is(.chatroom-row.active .chatroom-row-topic, .wiki-list-item.active) { color: var(--aktiv-text); }
/* Raumkacheln tragen seit 2026-09-16 dieselbe Anatomie wie die Agenda-Kacheln: Faehnchen-Leiste
   oben (Art des Raums, immer in der Hub-Farbe -- "aber einfach nur in der hub farbe keine extra
   farben"), rechts daneben "Erstellt vor ...", und unten rechts ausgeschrieben Aktiv/Inaktiv. */
/* .chatroom-row davor, sonst gewinnt die weiter unten stehende Grundregel von .agenda-typ-leiste
   (gleiche Spezifitaet, spaetere Quellposition) und die Leiste traegt --panel-hover DECKEND --
   dann hatte die Kachel drei Toene statt zwei (2026-09-16: "chatraeume haben jetzt irgendwie in
   der auswahlkachel links 3 farben. ich moechte aber nur zwei eine oben eine unten"). */
.chatroom-row .chatroom-row-typ {
  --typ-farbe: var(--accent);
  /* Immer die Kachel-2-Farbe (2026-09-16: "mache die trapez leisten in raeumen immer in kachel 2
     farbe") -- derselbe Ton wie der Kopfbalken der Agenda-Karten. */
  background: var(--inhalt-kachel-hover);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
/* "Erstellt vor ..." sitzt einen Tick tiefer als die Oberkante des Faehnchens (2026-09-16). */
.chatroom-row-typ .chatroom-row-time { margin-top: 3px; }
.chatroom-row-status {
  margin-left: auto; display: inline-flex; align-items: center; gap: var(--space-3);
  font-size: var(--fs-xs); color: var(--text-dim); white-space: nowrap; flex-shrink: 0;
}
.chatroom-row-status-inaktiv { color: var(--warning); }
.app-context .chatroom-row-typ { padding-left: var(--space-6); }

/* Die Raumkarte war fuer die 300px-Spalte der Ansicht gebaut: zweigeteilt mit eigener Kopfleiste,
   "Beigetreten" als Wort und Loeschknopf daneben. In der Menuespalte ist sie eine Zeile -- Kopf
   ohne Flaeche, der gruene Haken ohne Wort, Loeschen erst beim Ueberfahren. */
/* Der dunklere Bereich reicht vom Faehnchen bis zur Video-/Spielzeile -- nur die letzte Zeile
   (Mitglieder, Aktiv) sitzt auf der normalen Kartenflaeche (2026-09-16: "mache die dunklere flaeche
   die jetzt nur ueber dem erstellt vor x bereich ist bis zum video oder spiel was verknuepft ist.
   also alles ist dunkler bis auf die letzte zeile"). */
.app-context .chatroom-row-header-bar { padding: var(--space-4) var(--space-6) var(--space-4); }
.app-context .chatroom-row-content-row { padding: var(--space-4) var(--space-6); gap: var(--space-3); }
/* Titel wie in der Agenda -- gleiche Groesse und gleiches Gewicht, in voller Textfarbe
   ("mache den titel des raumes wie bei der agenda das man ihn besser lesen kann"). */
.app-context .chatroom-row-topic { font-size: var(--fs-lg); font-weight: var(--fw-semibold); color: var(--text); }
.app-context .chatroom-row-joined-badge { font-size: 0; gap: 0; }
.app-context .chatroom-row-joined-badge .mdi { font-size: var(--fs-lg); }
.app-context .chatroom-row-delete-btn { opacity: 0; transition: opacity var(--duration-fast) ease; }
.app-context .chatroom-row:hover .chatroom-row-delete-btn,
.app-context .chatroom-row-delete-btn:focus-visible { opacity: 1; }
.app-context .widget-agenda-main { flex: 1 1 auto; }
/* Ohne ihre Seitenleiste sind die Ansichten einspaltig -- sonst bliebe deren 300px-Spalte leer. */
body.hat-kontextspalte .wiki-layout, body.hat-kontextspalte .boards-layout,
body.hat-kontextspalte .dashboard-grid-3col {
  grid-template-columns: 1fr;
}
/* ---------- Medien in der Menuespalte (2026-09-13) ----------
   Die Alben stehen als schmale Zeilen in der Spalte, das geoeffnete Album fuellt die Flaeche
   daneben. Kopf, Suche und Filter kommen als dieselben Knoten aus der Uebersicht mit. */
/* Senkrecht scrollen, waagerecht 3px Platz fuer den Hover-Schub (2026-09-16). */
.app-context > .media-ordner-liste { flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: clip; scrollbar-gutter: auto; padding-right: 3px; display: flex; flex-direction: column; gap: 2px; }
.app-context > #media-search { width: 100%; }
.app-context > .media-filter-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
/* In der Spalte sieht der Hochladen-Knopf aus wie die anderen Kopf-Knoepfe (Notizen "+", Raeume
   "+") -- als gefuellter Akzent-Knopf stach er dort heraus (2026-09-14: "das plus icon in medien
   links ist gelb. mache es so wie die anderen"). */
.app-context .wiki-toolbar-actions #media-add-btn {
  width: var(--control-h-sm); height: var(--control-h-sm); padding: 0;
  background: var(--knopf); border: 1px solid var(--border); color: var(--text-dim);
  box-shadow: none;
}
.app-context .wiki-toolbar-actions #media-add-btn:hover { background: var(--panel-hover); color: var(--text); }
.media-liste-jahr {
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-dim); padding: var(--space-5) var(--space-3) var(--space-2);
}
.media-liste-zeile {
  display: flex; align-items: center; gap: var(--space-5); width: 100%; text-align: left;
  padding: var(--space-3); border: none; background: transparent; color: var(--text); cursor: pointer;
  font-family: inherit; border-radius: var(--radius-md);
  transition: background var(--duration-fast) ease;
}
/* Dieselbe Rueckmeldung wie bei den Agenda-Karten (2026-09-16: "bitte mache die hover animation von
   den agenda eintraegen auch fuer eintraege in medien, notes und rooms") -- Flaeche in der
   Hover-Farbe und ein kleiner Schub nach rechts. Der Uebergang steht an den Elementen selbst. */
.media-liste-zeile:hover, .wiki-list-item:hover, .chatroom-row:hover {
  background: var(--panel-hover); transform: translateX(2px);
}
.media-liste-zeile, .wiki-list-item, .chatroom-row {
  transition: background var(--duration-fast) ease, transform var(--duration-fast) ease,
              border-color var(--duration-fast) ease;
}
.media-liste-zeile.active { background: var(--aktiv-kachel); color: var(--aktiv-text); }
.media-liste-bild {
  width: 38px; height: 38px; flex-shrink: 0; border-radius: var(--radius-sm); overflow: hidden;
  display: flex; align-items: center; justify-content: center; background: var(--panel-2); color: var(--text-dim);
}
.media-liste-bild img { width: 100%; height: 100%; object-fit: cover; }
.media-liste-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.media-liste-titel { font-size: var(--fs-md); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.media-liste-meta { font-size: var(--fs-2xs); color: var(--text-dim); }
/* Das Album ist weiterhin dasselbe Overlay wie auf dem Handy -- am Desktop deckt es aber nur die
   Inhaltsflaeche ab statt des ganzen Fensters, damit die Albenliste daneben bedienbar bleibt. */
@media (min-width: 881px) {
  body.hat-kontextspalte #media-game-detail.modal-overlay {
    left: calc(var(--nav-breite) + var(--kontext-breite) + var(--schale-gap) * 3);
    /* Der Kasten beginnt UNTER der Suchzeile, nicht mit Innenabstand darueber hinweg: ein
       .modal-overlay zeichnet einen Weichzeichner ueber alles, was es bedeckt -- die Suche darin
       war dadurch unscharf (2026-09-14 gemeldet: "die suchleiste bei medien ist ausgeblurred"). */
    top: calc(var(--sticky-header-height, 50px) + var(--space-10) + var(--gitter-schritt));
    padding: 0 var(--space-12) 60px; background: transparent; align-items: stretch;
  }
  body.hat-kontextspalte #media-game-detail .modal.modal-xl {
    /* Genauso breit wie Notizen/Raeume (2026-09-14: "der medienordner ist zu breit mache ihn so
       wie notizen oder rooms") -- die liegen in main, das bei sehr breiten Fenstern auf
       --inhalt-max begrenzt und zentriert ist. Das Overlay reicht dagegen bis an den Fensterrand,
       sein Fenster muss die Begrenzung deshalb selbst mitbringen. */
    max-width: calc(var(--inhalt-max) - var(--space-12) * 2); width: 100%; margin: 0 auto;
    height: var(--inhalt-hoehe); max-height: none; min-height: 0;
  }
}
/* Sprung-/Filterliste fuer Bereiche ohne eigene Seitenleiste (Tools, Sammlung). */
.app-context-title { font-size: var(--fs-2xs); font-weight: var(--fw-semibold); letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); padding: 0 var(--space-3) var(--space-2); }
.app-context-link {
  display: flex; align-items: center; gap: var(--space-5); width: 100%;
  padding: var(--space-4) var(--space-5); border: none; background: transparent; cursor: pointer;
  color: var(--text-dim); font-family: inherit; font-size: var(--fs-md); text-align: left;
  border-radius: var(--radius-md); transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.app-context-link:hover { background: var(--panel-hover); color: var(--text); }
.app-context-link.active { background: var(--aktiv-kachel); color: var(--text); }
.app-context-link .mdi { font-size: var(--fs-lg); }
/* Eine einzeln gewaehlte Sammlungs-Spalte bekommt die volle Breite. */
.sammlung-columns-einzeln { grid-template-columns: 1fr; }
/* Sprungziele nicht unter die klebende Statuszeile scrollen lassen. */
.sammlung-column-heading-row { scroll-margin-top: 84px; }
@media (max-width: 1440px) {
  #profile-menu-btn #user-display-name { display: none; }
  #profile-menu-btn { padding: var(--space-2) var(--space-3); }
}
/* Am Desktop navigiert die Leiste links -- die Kopfleiste braucht es dort nicht mehr, oben bleibt
   nur die Statuszeile. Auf dem Handy ist sie unveraendert die Titelleiste. */
@media (min-width: 881px) {
  .topbar { display: none; }
  /* Nutzerwunsch: "die titelleiste mit den infos wie wetter sollte jetzt nicht mehr ueber die
     ganze seite gehen, die machen wir round und so schwebend oben mittig so breit wie der inhalt
     selbst" -- die Zeile schwebt als abgerundete Leiste ueber der Inhaltsflaeche, exakt so breit
     wie main darunter. Der Traeger behaelt den Seitengrund, damit beim Scrollen nichts durch die
     Luecke ueber der Leiste laeuft. */
  .app-header-sticky {
    padding: var(--schale-gap) var(--space-12) var(--space-5);
    /* Der Traeger deckt durchlaufenden Inhalt ab -- ohne ihn schoben sich beim Scrollen
       Kachelkoepfe in den Streifen ueber der Leiste. Damit dabei nicht der Hintergrund fehlt
       ("man sieht eine klare kante wo der background der durchlaeuft endet"), traegt er den
       Seitengrund UND denselben Wellenausschnitt (::before darunter). */
    background: var(--bg-verlauf) var(--bg) fixed;
  }
  /* Derselbe Wellenzug wie hinter der Seite, nur auf die Hoehe der Kopfleiste beschnitten: gleiche
     Groesse, gleiche Position, gleiche Animation -- dadurch laufen die Wellen ohne Bruch durch den
     Streifen hindurch. position:fixed bezieht sich aufs Sichtfenster (der sticky Traeger bildet
     keinen Bezugsrahmen), clip-path schneidet den Rest weg; die Hoehe haelt
     --sticky-header-height live nach (syncStickyHeaderHeight() in app.js). */
  .app-header-sticky::before {
    content: ""; position: fixed; top: 0; left: 0; width: 100vw; z-index: 0; pointer-events: none;
    /* Nur so hoch wie die Kopfleiste -- Maske und Verlauf behalten aber die Masse der ganzen Seite
       (mask-size/background-size), sodass hier exakt deren oberer Streifen steht. Weil beide Lagen
       stillstehen, liegen sie ohne weiteres Zutun genau uebereinander. */
    height: var(--sticky-header-height, 78px);
    background: linear-gradient(120deg, var(--wellen-a), var(--wellen-b)) 0 0 / 100vw 100vh no-repeat;
    opacity: var(--wellen-deckkraft);
    -webkit-mask-image: url("/img/wellen-hinten.svg"); mask-image: url("/img/wellen-hinten.svg");
    -webkit-mask-size: auto 100vh; mask-size: auto 100vh;
    -webkit-mask-repeat: repeat-x; mask-repeat: repeat-x;
    -webkit-mask-position: 0 0; mask-position: 0 0;
  }
  /* Titelleiste und Statuszeile ueber den Wellenausschnitt heben (der ist positioniert und laege
     sonst ueber ihnen). */
  .app-header-sticky > * { position: relative; z-index: 1; }
  .status-ticker {
    /* Exakt die Breite des Inhalts darunter: main begrenzt seine AUSSEN-Breite auf --inhalt-max und
       zieht sein Padding davon ab, die Leiste steht dagegen schon innerhalb des Traeger-Paddings --
       deshalb hier dieselbe Rechnung von Hand. */
    max-width: calc(var(--inhalt-max) - var(--space-12) * 2); margin: 0 auto;
    /* Wenig Innenabstand: die Module sollen an den Kanten der Leiste stehen, in der Mitte ist
       ohnehin Platz (Nutzerwunsch "schiebe den inhalt der leiste jeweils nach rechts und
       linksbuendig"). Genug bleibt, dass die Schrift die runden Enden nicht beruehrt. */
    padding: var(--space-4) var(--space-7);
    border-radius: var(--radius-full); border-top: none;
  }
  /* Dieselbe Flaeche wie Menuespalte, Widgets und Fenster (2026-09-16: "die obere menueleiste
     sollte die farbe haben die bei flaechenfarbe gewaehlt wurde"). Vorher trug die Leiste --panel
     deckend: technisch derselbe Farbwert, optisch aber eine andere Flaeche, weil alles andere die
     Glasfuellung aus derselben Farbe zeigt. Mit dem Traeger davor, weil die Basisregel von
     .status-ticker WEITER UNTEN in der Datei steht und bei gleicher Spezifitaet sonst gewinnt --
     derselbe Fallstrick wie bei der Handy-Regel weiter unten. */
  .app-header-sticky .status-ticker {
    background: var(--flaeche-fuellung);
    -webkit-backdrop-filter: var(--flaeche-unschaerfe);
            backdrop-filter: var(--flaeche-unschaerfe);
    box-shadow: var(--flaeche-schatten);
  }
}

.user-menu { margin-left: auto; display: flex; align-items: center; gap: var(--space-5); }
#user-display-name { color: var(--text-dim); font-size: var(--fs-md); }
/* ---------- Baustein Nebenknopf ----------
   Gleiche Hoehe und Rundung wie .primary-btn (weiter unten), damit beide nebeneinander passen.
   Der Hover war bis 2026-09 fuer ALLE .text-btn rot -- gedacht fuer Loeschen/Verlassen, getroffen
   hat er aber vor allem neutrale Knoepfe (Abbrechen, Verbindung testen, Rechte bearbeiten), die
   deshalb reihenweise eigene Hover-Ausnahmen bekamen. Jetzt ist der Hover neutral, und nur die
   wirklich zerstoererischen Knoepfe in der Liste darunter werden rot. Ein neuer solcher Knopf
   gehoert in diese Liste. */
.text-btn {
  background: var(--knopf); border: 1px solid var(--border); color: var(--text);
  min-height: var(--control-h); padding: var(--space-2) var(--space-7); border-radius: var(--radius-md);
  cursor: pointer; font-size: var(--fs-md); font-weight: var(--fw-medium); line-height: 1.2;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-3);
  transition: background var(--duration-fast) ease, border-color var(--duration-fast) ease, color var(--duration-fast) ease;
}
.text-btn:hover { background: var(--knopf-hover); border-color: color-mix(in srgb, var(--text-dim) 35%, var(--border)); }
.text-btn:is(#chatroom-leave-btn, #profile-logout-btn, #profile-2fa-disable-toggle-btn, #profile-push-clear-all-btn,
             #org-edit-vault-clear-btn, .unassigned-row-delete-btn, .org-delete-btn, .chatroom-invite-decline-btn):hover {
  background: var(--danger); border-color: var(--danger); color: var(--text-on-color);
}
.text-btn:disabled, .primary-btn:disabled, .danger-btn:disabled, .danger-btn-solid:disabled { opacity: 0.5; cursor: not-allowed; }
/* Tastatur-Fokus fuer alle Knopf-Bausteine. :focus-visible statt :focus, damit der Ring nur bei
   Tastaturbedienung erscheint und nicht nach jedem Mausklick stehen bleibt. */
:is(.primary-btn, .text-btn, .danger-btn, .danger-btn-solid, .icon-btn, .filter-btn, .category-tab,
    .tab-btn, .settings-tab-btn, .modal-close):focus-visible {
  outline: 2px solid var(--accent-light); outline-offset: 2px;
}
/* R61 Nutzer-Feedback: neue "Medien"-Zeile in der Event-Vorschau (ersetzt den kleinen Icon-Button
   oben) -- eigener Hover statt des .text-btn-Standard-Rot (das ist fuer Loeschen/Verlassen-artige
   Aktionen gedacht, hier sind beide Buttons neutrale/positive Aktionen). */
.event-preview-media-actions { display: flex; gap: var(--space-4); flex-wrap: wrap; }
.event-preview-media-actions .text-btn:hover { background: var(--accent); border-color: var(--accent); color: var(--text-on-color); }
/* Nutzer-Feedback: "es sollte nur nach rechts vershcobnen werden neben event erstellen ... diese
   beiden buttons bitte den neuen button styles anpassen wie ich nehme teil etc" -- Beitreten- und
   Event-erstellen-Button der Sammlung-Detailansicht (Spiel/Film/Serie/LAN-Party) im gleichen
   Pill-Stil wie der Interessiert/Ich-nehme-teil/Mit-Vorbehalt-Toggle der Event-Vorschau. */
.compendium-pill-btn {
  background: var(--tint); border: none; border-radius: var(--radius-full); color: var(--text-dim);
  font-size: var(--fs-sm); font-weight: var(--fw-semibold); padding: var(--space-3) var(--space-6); display: inline-flex; align-items: center; gap: var(--space-2);
  cursor: pointer; transition: background var(--duration-fast) ease, color var(--duration-fast) ease; white-space: nowrap;
}
.compendium-pill-btn:hover { background: var(--accent); color: var(--text-on-color); }
.compendium-pill-btn.attending { background: var(--success); color: var(--on-success); }
.compendium-pill-btn.attending:hover { background: var(--success); color: var(--on-success); }
/* Nutzer-Feedback: "den like button bitte auch neben das in besitz icon als gleiche schaltflaeche
   'wuerde ich gerne spielen' tun" -- Herz-Toggle im Spiele-Detail-Popup (#boards-heart-btn), gleicher
   Pill-Stil wie #boards-join-btn, mit eigener "aktiv"-Farbe statt dem gruenen .attending. */
.compendium-pill-btn.hearted { background: var(--heart); color: var(--text-on-color); }
.compendium-pill-btn.hearted:hover { background: var(--heart); color: var(--text-on-color); }

.profile-menu-btn {
  display: inline-flex; align-items: center; gap: var(--space-4);
  background: var(--knopf); border: 1px solid var(--border); color: var(--text);
  padding: var(--space-3) var(--space-5) var(--space-3) var(--space-3); border-radius: var(--radius-xl); cursor: pointer; font-size: var(--fs-md);
  transition: background var(--duration-fast) ease, border-color var(--duration-fast) ease;
}
.profile-menu-btn:hover { background: var(--panel); border-color: var(--accent); }
.profile-menu-btn .mdi-chevron-down { font-size: var(--fs-lg); color: var(--text-dim); }
#profile-menu-avatar img, .profile-avatar-img-small {
  width: 24px; height: 24px; border-radius: 50%; object-fit: cover; display: block;
}
#profile-menu-avatar .avatar-placeholder { font-size: var(--fs-2xl); line-height: 1; color: var(--text-dim); }
.profile-avatar-row { display: flex; align-items: center; gap: var(--space-6); margin-bottom: var(--space-8); }
.profile-avatar-circle {
  width: 64px; height: 64px; border-radius: 50%; overflow: hidden; flex-shrink: 0;
  background: var(--panel-2); border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-4xl); color: var(--text-dim); position: relative; cursor: pointer;
}
.profile-avatar-circle img { width: 100%; height: 100%; object-fit: cover; }
.profile-avatar-hover-icon {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--bg) 55%, transparent); color: var(--text-on-color); font-size: var(--fs-2xl);
  opacity: 0; transition: opacity var(--duration-fast) ease;
}
.profile-avatar-circle:hover .profile-avatar-hover-icon,
.profile-avatar-circle:focus-visible .profile-avatar-hover-icon { opacity: 1; }
/* R56: Profilbanner-Upload -- wiederverwendet .title-image-picker (gleiches Drag&Zoom-Muster wie
   Event-Titelbilder, siehe setupCoverDrag() in app.js), Groesse entspricht 1:1 der Avatar-Hover-
   Karte (260px breite feste Kartenbreite, siehe .avatar-hover-card.icon-dropdown-popover), damit
   die Vorschau beim Hochladen exakt zeigt, wie der Ausschnitt spaeter angezeigt wird. */
/* R60 Nutzer-Feedback: sass vorher als eigene, per "margin:0 auto" zentrierte Zeile unter dem
   Profilbild ("komisch mittig") -- ist jetzt ein zweites Flex-Kind in .profile-avatar-row,
   rechts neben dem Profilbild. */
.profile-banner-row { flex: 1; min-width: 0; }
/* Kombinierter Selektor (statt nur .title-image-picker-banner) noetig, damit die min-height:160px
   der generischen .title-image-picker-Basisregel (kommt spaeter in dieser Datei, gleiche
   Spezifitaet gewinnt sonst per Quellreihenfolge) hier zuverlaessig ueberschrieben wird. */
.title-image-picker.title-image-picker-banner { width: 260px; height: 110px; min-height: 0; margin: 0; }
.title-image-picker-banner img { height: 100%; }
.profile-hover-preview-label { margin-bottom: var(--space-4); }
.profile-modal-divider { border: none; border-top: 1px solid var(--border); margin: var(--space-10) 0; }
.profile-logout-btn { margin-top: var(--space-8); }
#profile-2fa-section h3 { margin: 0 0 var(--space-4); font-size: var(--fs-body); }
#profile-2fa-section .form-actions { margin-top: var(--space-4); }
#profile-webauthn-section h3 { margin: 0 0 var(--space-4); font-size: var(--fs-body); }
.webauthn-credential-list { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-6); }
.webauthn-credential-row {
  display: flex; align-items: center; gap: var(--space-5);
  background: var(--inhalt-kachel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-4) var(--space-6);
}
.webauthn-credential-row > .mdi { font-size: var(--fs-2xl); color: var(--accent); flex-shrink: 0; }
.webauthn-credential-info { display: flex; flex-direction: column; gap: var(--space-1); flex: 1; min-width: 0; }
.webauthn-credential-label { font-size: var(--fs-lg); font-weight: var(--fw-semibold); }
.webauthn-credential-meta { font-size: var(--fs-xs); color: var(--text-dim); }
.webauthn-credential-remove-btn { flex-shrink: 0; }
.two-factor-qr {
  display: block; width: 180px; height: 180px; margin: var(--space-4) auto;
  background: var(--white); padding: var(--space-4); border-radius: var(--radius-md);
}

/* ---------- Organisations-Dropdown ---------- */
/* Nutzer-Feedback: "mache aus dem titel mit logo die auswahl der organisation ... bitte das im
   desktop auch so machen" -- der separate Pillenbutton ist jetzt auf JEDER Breite ausgeblendet,
   #org-title-switch-wrap (Titel+Pfeil) erfuellt diese Aufgabe ueberall (siehe dort). !important
   noetig, da renderOrgDropdown() (app.js) weiterhin eine INLINE style.display setzt (die Elemente
   bleiben im DOM/JS bestehen, falls der Pillenbutton spaeter mal wieder gebraucht wird). */
.org-dropdown { display: none !important; }
.org-dropdown { position: relative; }
.org-dropdown-btn {
  background: var(--knopf); border: 1px solid var(--border); color: var(--text);
  padding: var(--space-3) var(--space-6); border-radius: var(--radius-xl); cursor: pointer; font-size: var(--fs-md);
  display: flex; align-items: center; gap: var(--space-3);
}
.org-dropdown-btn:hover { background: var(--panel-hover); }
.org-dropdown-btn::after { content: "▾"; color: var(--text-dim); font-size: var(--fs-2xs); }
.org-dropdown-list {
  display: none;
  position: absolute; top: calc(100% + 6px); right: 0;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  min-width: 200px; z-index: 50; overflow: hidden;
  animation: fadeInUp var(--duration-fast) ease both;
}
.org-dropdown.open .org-dropdown-list { display: block; }
.org-dropdown-item {
  display: flex; align-items: center; gap: var(--space-4); padding: var(--space-5) var(--space-7);
  cursor: pointer; font-size: var(--fs-lg); color: var(--text);
}
.org-dropdown-item:hover { background: var(--panel-hover); }
.org-dropdown-item.active { background: var(--aktiv-kachel); color: var(--accent); font-weight: var(--fw-semibold); }
/* Nutzer-Feedback: "mache aus dem titel mit logo die auswahl der organisation, ein kleiner pfeil
   rechts daneben sollte anzeigen das man da klicken kann ... bitte das im desktop auch so machen"
   -- Titel+Pfeil bilden auf JEDER Breite den Organisations-Wechsler (Klick-Handler auf beiden
   Elementen, siehe showOrgSwitchMobileMenu() in app.js). Der Wrapper packt beide eng zusammen
   (kleiner gap statt der groesseren .topbar-gap), damit der Pfeil sichtbar an der Ueberschrift
   "klebt" statt weit rechts bei den anderen Icons zu landen. */
.org-title-switch-wrap { display: flex; align-items: center; gap: var(--space-1); cursor: pointer; min-width: 0; }
.org-title-switch-btn {
  background: none; border: none; color: var(--text-dim); cursor: pointer;
  padding: var(--space-2); font-size: var(--fs-body); line-height: 1; flex-shrink: 0;
}

/* R68 Nutzer-Feedback: "die jahreszahl buttons deutlich unauffälliger einfach nur pfeile bitte und
   nicht so weit weg von der zahl" -- keine eigene Box/Rahmen mehr, nur schlichte Pfeil-Glyphen
   direkt neben der Jahreszahl. */
.year-nav { display: flex; align-items: center; gap: var(--space-2); margin-left: -6px; }
.year-header-toolbar { display: flex; align-items: center; gap: var(--space-4); }
.year-nav button {
  display: flex; align-items: center; justify-content: center;
  background: none;
  border: none;
  color: var(--text-dim);
  width: 26px; height: 26px;
  padding: 0;
  cursor: pointer;
  font-size: var(--fs-xl);
}
.year-nav button:hover { color: var(--text); }
#year-label { font-weight: var(--fw-semibold); font-size: var(--fs-xl); min-width: 60px; text-align: center; display: inline-block; }
/* Nutzer-Feedback (naechste Runde): "mache mir den kalender auf dem handy so, dass er nicht
   monatsweise gescrollt werden kann sondern wie der ios kalender ein infinite scroll ist" -- die
   fruehere Monats-fuer-Monat-Navigation (.year-mobile-month-nav mit Pfeilen) entfaellt komplett,
   Mobile zeigt jetzt wie Desktop alle Monate durchgehend (siehe updateMobileMonthView() in
   app.js, zeigt jetzt einfach alle Kacheln). Die bestehenden Jahres-Pfeile (#year-prev/#year-next)
   bleiben dafuer jetzt auch auf Mobile sichtbar, exakt wie am Desktop. */
.year-mobile-month-nav { display: none; }

/* Oben ein voller Rasterschritt, damit die erste Widget-Reihe auf einer Rasterlinie beginnt. */
main {
  /* Oben genau so viel Abstand wie die beiden Menuespalten (2026-09-14: "lasse den gesamten inhalt
     auf jeder seite oben auf selber hoehe wie die spalte 2 links anfangen") -- vorher ein ganzer
     Rasterschritt, dadurch begann der Inhalt 36px tiefer als die Spalte daneben. Die Kopfzeile der
     Seite (Suche/Begruessung) ist weiterhin einen Rasterschritt hoch, die Flaechen darunter sitzen
     damit wieder auf einer Rasterlinie. */
  padding: var(--space-10) var(--space-12) 60px; max-width: var(--inhalt-max); margin: 0 auto; position: relative;
  /* Mindestens fensterhoch: sonst endet das Raster dort, wo der Inhalt endet -- auf dem Handy war
     darunter eine leere, rasterlose Flaeche (2026-09-14 gemeldet). */
  min-height: calc(100vh - var(--sticky-header-height, 78px));
}
/* Das frueher hier gezeichnete Raster (Punkte auf den Kreuzungspunkten plus Schraffur, main::before)
   ist 2026-09-15 entfallen: seit das Wellen-Muster hinter der ganzen Seite liegt (body::before)
   waren beide gleichzeitig zu sehen ("entferne aber dafuer das alte pattern jetzt sind beide da").
   Die Rastermasse (--gitter-schritt/--gitter-anker) bleiben -- sie bestimmen weiterhin die
   Kachelgroessen des Dashboards, nur gezeichnet wird das Raster nicht mehr. */
/* Der Inhalt bleibt in seiner eigenen Ebene -- so liegt nichts hinter dem Muster der Seite. */
main > * { position: relative; z-index: 1; }
@media (max-width: 880px) {
  main { padding: var(--space-7) var(--space-6) 40px; }
  .dashboard-toolbar { flex-wrap: wrap; gap: var(--space-4); }
}
/* Nutzer-Feedback: "mache alle tabs responsive ... auf 4k ist das dash z.b. viel zu klein ... die
   anordnung sollte sich nicht verändern, es sollte nur skalieren" -- das Layout ist app-weit fuer
   ~1680px Breite gebaut (main{max-width:1680px}); auf sehr breiten Bildschirmen bei 100%
   Betriebssystem-Skalierung (z.B. ein 27-32" 4K-Monitor ohne Windows-Skalierung) bleibt dadurch
   alles bei absolut gleicher Pixelgroesse wie auf einem kleinen Laptop-Display, wirkt entsprechend
   winzig, umgeben von riesigen leeren Raendern. "zoom" auf body verhaelt sich wie der eingebaute
   Browser-Zoom (Strg +) -- vergroessert wortwoertlich ALLES (Schrift, Icons, Abstaende, Karten,
   auch Modals/Popover, da die im DOM Kind-Elemente von body sind) gleichmaessig, OHNE dass sich am
   Grid/Flex-Layout selbst etwas aendert (gleiche Spaltenanzahl, gleiche relative Anordnung) --
   deckt sich exakt mit "nur skalieren, Anordnung nicht veraendern". Bewusst gestaffelt statt
   stufenlos (kein calc(100vw/...)-Zoom): einfacher nachvollziehbar/wartbar, und vermeidet
   Rundungs-Artefakte bei krummen Zwischenwerten.
   Wichtige Ausnahme (live an #tab-chatrooms gefunden, siehe dortige Korrektur weiter unten):
   anders als bei echtem Browser-Zoom (Strg +) SCHRUMPFT der "vh"-Bezugsrahmen unter CSS "zoom"
   NICHT mit -- 100vh bleibt exakt die wahre Fensterhoehe, wird aber wie jede andere Laenge
   zusaetzlich mit dem Zoom-Faktor multipliziert gerendert. Ein reines "calc(100vh - Npx)" (fuer
   "genau bis zum Fensterrand" gedachte Boxen) rendert dadurch am Ende (100vh - Npx) * Zoom hoch --
   staerker als die eigentlich gewuenschte Fensterhoehe, die Box laeuft ueber den sichtbaren
   Bereich hinaus. Betroffene Regeln muessen ihr Ergebnis vorab durch den Zoom-Faktor teilen (siehe
   #tab-chatrooms .boards-layout / .user-rights-popover in den beiden Bloecken unten), damit nach
   der Zoom-Multiplikation wieder exakt die wahre Fensterhoehe minus Offset herauskommt. Die
   Korrektur-Regeln selbst stehen NICHT hier, sondern jeweils direkt NACH der Original-Regel
   weiter unten im File (#tab-chatrooms .boards-layout / .user-rights-popover) -- bei gleicher
   Selektor-Spezifitaet gewinnt sonst die spaeter im Quelltext stehende Regel unabhaengig davon,
   welche im spezifischeren @media-Block steht (derselbe wiederkehrende Bug wie an mehreren
   anderen Stellen dieser App, siehe z.B. .status-ticker/.dashboard-col-agenda-fixed-Fixes). */
@media (min-width: 2560px) and (max-width: 3839px) {
  body { zoom: 1.18; }
  :root { --inhalt-hoehe: calc((100vh - var(--sticky-header-height, 78px) - var(--space-10) - var(--schale-gap) - var(--hub-titel-hoehe)) / 1.18); }
}
@media (min-width: 3840px) {
  body { zoom: 1.45; }
  :root { --inhalt-hoehe: calc((100vh - var(--sticky-header-height, 78px) - var(--space-10) - var(--schale-gap) - var(--hub-titel-hoehe)) / 1.45); }
}
/* Nutzer-Feedback: "blende auch das greeting aus" -- auf Mobile ist die Agenda der einzige
   Dashboard-Inhalt (s.o.), die Begruessungszeile nimmt dort nur unnoetig Platz weg. */
@media (max-width: 720px) {
  .dashboard-greeting { display: none; }
}

.tab-content { display: none; }
.tab-content.active { display: block; }

/* Masse siehe .sammlung-toolbar (gemeinsame Regel) -- hier nur noch, was das Dashboard extra hat. */
.dashboard-greeting { font-weight: var(--fw-semibold); color: var(--text); cursor: pointer; }
.dashboard-greeting:hover { color: var(--accent); }
/* "Message of the Day"-Banner (R36) -- rechtsbuendig in derselben Zeile wie die Begruessung
   (dashboard-toolbar ist bereits justify-content:space-between), gelb wie ein Hinweis-Chip.
   Bleibt so lange sichtbar, bis ein Admin den Text in den Organisations-Settings wieder leert. */
.motd-banner {
  display: inline-flex; align-items: center; gap: var(--space-3); max-width: 60%;
  padding: var(--space-2) var(--space-6); border-radius: var(--radius-xl); font-size: var(--fs-sm); font-weight: var(--fw-medium);
  background: color-mix(in srgb, var(--highlight) 18%, transparent); color: var(--highlight); border: 1px solid color-mix(in srgb, var(--highlight) 35%, transparent);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.motd-banner .mdi { flex-shrink: 0; }
@media (max-width: 880px) {
  .motd-banner { max-width: 100%; white-space: normal; }
}
.greeting-popover {
  position: fixed; z-index: var(--z-popover); width: 300px; max-width: calc(100vw - 24px);
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-6) var(--space-7); box-shadow: var(--shadow-lg); animation: fadeInUp var(--duration-fast) ease both;
}
.greeting-popover h4 { margin: 0 0 var(--space-3); font-size: var(--fs-lg); }
.greeting-popover p { margin: 0; font-size: var(--fs-sm); color: var(--text-dim); line-height: 1.4; }

/* ---------- Dashboard layout: fixe Agenda links + Widgets-Bereich rechts ---------- */
/* Das Raster gilt nur noch fuer schmale Fenster ohne Kontextspalte: am Desktop steht die Agenda
   im Menue links, das Dashboard ist dort einspaltig (siehe body.hat-kontextspalte weiter oben). */
.dashboard-grid-3col {
  display: grid;
  grid-template-columns: 1.3fr 3.3fr;
  gap: var(--space-10);
  align-items: start;
}
@media (max-width: 1300px) {
  .dashboard-grid-3col { grid-template-columns: 1fr; }
  /* border-right/padding-right stehen NICHT mehr hier -- die spaeter im Stylesheet stehende,
     unconditional Basisregel von .dashboard-col-agenda-fixed gewann bei gleicher Spezifitaet
     immer per Quellreihenfolge (Nutzer-Feedback: "neben der agenda ist noch die trennline"),
     siehe die Regel direkt NACH der Basisregel weiter unten. */
  .dashboard-col-agenda-fixed { position: static !important; height: auto !important; }
}
.dashboard-col-agenda-fixed {
  position: sticky;
  /* R61 Nutzer-Feedback: "Menüleiste 2" (.status-ticker) ist jetzt Teil des permanent sichtbaren
     Sticky-Headers (siehe .app-header-sticky) statt beim Scrollen wegzulaufen -- fixe 78px waren
     nur fuer die (variabel hohe, kann bei schmalen Fenstern umbrechende) Topbar allein kalibriert
     und ueberlappten jetzt mit dem insgesamt hoeheren Header. --sticky-header-height wird per JS
     (syncStickyHeaderHeight() in app.js) live auf die tatsaechliche Hoehe gesetzt. */
  top: var(--sticky-header-height, 78px);
  /* Nutzer-Feedback: "passe das dashboard an die höhe des bildschirms an, derzeit ist das an den
     widgets angepasst" -- keine per JS live gemessene Inhaltshoehe mehr (die fruehere
     syncDashboardHeightToWidgets()-Mechanik ist entfernt). Spaeteres Nutzer-Feedback: "gerade ist
     da noch ein gap unten" -- die zunaechst wiederhergestellte feste var(--fixed-content-height,
     1010px) reichte auf groesseren Bildschirmen nicht bis zum Fensterrand; jetzt echte
     var(--dashboard-content-height) (siehe :root), an die tatsaechliche Fensterhoehe gekoppelt,
     nur fuers Dashboard (die anderen Seiten behalten bewusst die statische Variable, siehe deren
     Kommentar). Die Agenda-Liste (.agenda-list-main, flex:1 + overflow-y:auto) scrollt intern bei
     Ueberlauf. */
  height: var(--dashboard-content-height);
  display: flex;
  /* Bugfix (Nutzer-Feedback von echten iOS-/Android-Geraeten: "die Agenda rechts abgeschnitten"):
     dasselbe WebKit/Mobile-Chromium-Verhalten wie bei .app-header-sticky weiter oben (dort schon
     einmal mit width:100% behoben) -- ein position:sticky-Element ohne EXPLIZITE Breite stretcht
     sich auf manchen mobilen Browsern nicht zuverlaessig auf die volle Breite seiner Grid-Spalte,
     der Rest bleibt dann sichtbar leer/"abgeschnitten". In diesem Chromium-Sandkasten nicht
     reproduzierbar (dort stretcht sticky bereits korrekt), daher width:100% hier vorsorglich
     analog zur bereits bestaetigten Loesung ergaenzt. min-width:0 zusaetzlich als Sicherheitsnetz,
     falls doch einmal Inhalt (Tag-Chips, lange Datumsspannen) die Spalte ueber die Breite hinaus
     zwingen sollte (gleiches Muster wie bei .dashboard-col weiter oben). */
  width: 100%;
  min-width: 0;
}
@media (max-width: 1300px) {
  /* Nutzer-Feedback: "neben der agenda ist noch die trennline. bitte mobil ausblenden" -- gleicher
     Breakpoint wie .dashboard-grid-3col's Umschalten auf einspaltig weiter oben (die Trennlinie
     zur Widget-Spalte ergibt nur im zweispaltigen Layout Sinn). Muss NACH der Basisregel stehen
     (gleiche Klassen-Spezifitaet, spaetere Quellposition gewinnt sonst -- dasselbe Muster wie beim
     .status-ticker-Fix an anderer Stelle). */
  .dashboard-col-agenda-fixed { border-right: none; padding-right: 0; }  /* seit 2026-09 ohnehin ohne Linie, siehe unten */
}
/* Zonen-Container fuer alle Widget-Spalten -- eigene, leicht hellere Flaeche gegenueber der
   Seite (analog zur Agenda-Zone), mit eigener "Widgets"-Ueberschrift auf Hoehe der
   Agenda-Ueberschrift, damit die erste Widget-Kachel dort beginnt, wo auch die erste
   Agenda-Kachel beginnt.
   Echte, an die Fensterhoehe gekoppelte var(--dashboard-content-height) (siehe .dashboard-col-
   agenda-fixed weiter oben fuer die Begruendung/den Feedback-Verlauf). overflow-y:auto laesst den
   Widget-Bereich bei zu vielen/eingeblendeten Widgets intern scrollen. */
/* Keine feste Hoehe mehr (2026-09-14 gemeldet: "die widgets werden abgeschnitten wenn die seite
   zu kurz ist") -- der Bereich waechst mit seinem Inhalt, gescrollt wird die Seite. */
.dashboard-widgets-area { display: flex; flex-direction: column; min-width: 0; }
.dashboard-widgets-heading { flex-shrink: 0; margin-bottom: var(--space-5); }
.dashboard-widgets-heading h3 { margin: 0; font-size: var(--fs-lg); }
/* Kachelraster (2026-09-14): so viele Spalten, wie hineinpassen -- wird das Fenster schmaler,
   rutschen die Kacheln von selbst um ("sie sollten einen festen platz haben und verschoben werden
   wenn die seite kleiner ist"). Die Hoehe kommt aus den Rasterzeilen (grid-auto-rows = eine
   Einheit) mal den Einheiten der Kachel, "dense" fuellt die Luecken, die der hoehere Kalender
   sonst hinterlaesst. */
.dashboard-widgets-grid {
  display: grid;
  grid-auto-rows: var(--kachel-einheit);
  grid-auto-flow: row dense;
  align-items: stretch;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  /* Zeilenabstand = ein Rasterschritt, sonst laege die zweite Widget-Reihe nicht mehr auf einer
     Rasterlinie. Seitlich darf er kleiner sein -- das betrifft die Ausrichtung nicht und macht die
     Kacheln breiter (2026-09-14: "die widgets breiter bitte der abstand zwischen ihnen ist zu
     gross"). */
  row-gap: var(--kachel-abstand);
  column-gap: var(--kachel-abstand);
}

@media (max-width: 880px) {
  /* Nutzer-Feedback: "nur die agenda ... alles andere kann mobil weg" -- auf dem Handy ist der
     Dashboard-Tab jetzt reine Agenda-Liste, alle anderen Dashboard-Widgets (Mini-Kalender,
     Geburtstage, Angeheftet, Releases, Medien, Chat, Notizen) inkl. "Widgets"-Ueberschrift
     entfallen komplett statt nur gestapelt zu werden. */
  .dashboard-widgets-area { display: none; }
}
/* Nutzer-Feedback: "der widget ein ausblenden button ist jetzt nicht mehr ganz unten angeheftet
   sondern zu weit oben" -- margin-top:8px allein platziert den Footer nur direkt UNTER dem
   Widget-Grid-Inhalt, nicht an der Unterkante von .dashboard-widgets-area. Seit die Flaeche eine
   echte, an die Fensterhoehe gekoppelte Hoehe hat (var(--dashboard-content-height)), bleibt je
   nach Bildschirm/Widget-Anzahl Luft zwischen Inhalt und Flaechen-Unterkante -- margin-top:auto in
   der Flex-Spalte schiebt den Footer zuverlaessig ganz nach unten, unabhaengig davon wie viel Luft
   bleibt. */
/* Etwas Luft ueber dem Knopf (2026-09-14: "das icon widgets ein ausblenden sollte etwas weiter
   runter es hat kaum abstand") -- er klebte sonst direkt unter der letzten Kachelreihe. */
.dashboard-widgets-footer { display: flex; justify-content: flex-end; margin-top: auto; padding-top: var(--space-8); flex-shrink: 0; }
.dashboard-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  /* Bugfix: als Grid-Item ohne min-width:0 sizt ein Flex-Container standardmaessig auf seinen
     Content-min-Width (min-width:auto) -- ein einzelnes ueberbreites Widget (z.B. Releases mit
     langen Titeln) sprengte dadurch die ganze Spalte samt Nachbarspalten statt sich an die
     1.35fr/1fr-Aufteilung zu halten. Die Kind-Elemente truncaten selbst schon korrekt
     (.agenda-title hat min-width:0), es fehlte nur dieses Glied in der Kette. */
  min-width: 0;
}
/* scrollbar-gutter:auto (statt globalem stable): die meisten Widgets (Geburtstage, Links,
   Notizen, Screenshots) zeigen nur 2-5 kurze Eintraege und laufen praktisch nie ueber --
   die permanente Gutter-Reservierung war reine Verschwendung rechts im Widget (vierter Fund
   desselben Musters in dieser Runde: .widget-chat, .agenda-list, .agenda-item/.tile-card, jetzt
   hier auf Widget-Ebene). */
.widget {
  position: relative;
  /* Keine eigene Panel-Flaeche mehr (die war dem Nutzer zu dunkel), sondern die leichte Toenung
     aus der Regel weiter unten -- die Ueberschrift samt Linie gliedert, kein Kasten. */
  border: none;
  border-radius: var(--radius);
  /* Mehr Luft in den Karten (2026-09-13, nach den Vorlagen in _Uploads: dort traegt der Weissraum
     die Struktur, nicht die Kante) -- vorher --space-4 rundum. Nicht mehr: das Dashboard hat vier
     Spalten nebeneinander, jedes zusaetzliche Pixel Innenabstand fehlt dem Inhalt. */
  padding: var(--space-6) var(--space-6) var(--space-5);
  /* Dieselbe Einblendung wie jede Seite (.tab-content) -- mit --duration-slow lief das Dashboard
     sichtbar traeger an als die uebrigen Seiten (2026-09-15: "ich habe das gefuehl es gibt
     verschiedene [seiten animationen] - mache sie gleich ueberall"). */
  animation: fadeInUp var(--duration-base) ease both;
  overflow: auto;
  scrollbar-gutter: auto;
  /* Groesse aus dem Kachelraster: so viele Rasterzeilen, wie die Kachel Einheiten hoch ist, und so
     viele Spalten, wie ihr Inhalt braucht -- was nicht hineinpasst, scrollt im Widget. */
  grid-row: span var(--kachel-einheiten, 2);
  grid-column: span var(--kachel-spalten, 1);
  height: auto; min-height: 0;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.widget-header { flex-shrink: 0; }
/* Die Politur 2026-09 gab allen Karten eine feine helle Kante (outline mit --tint). 2026-09-13 ist
   sie auf Nutzerwunsch gefallen: "viel weniger umrandungen ... eher linien zwischen den sachen" --
   die Vorlagen in _Uploads trennen Karten allein ueber die Flaeche. Im dunklen Modus reicht der
   Helligkeitsunterschied Karte/Grund dafuer, im hellen uebernimmt ein sehr weicher Schatten, sonst
   verschwimmt Papier auf Papier. */
html[data-theme="hell"] :is(.wiki-sidebar, .wiki-main, .boards-sidebar, .boards-main, .sammlung-column, .year-calendar-area) {
  box-shadow: var(--shadow-sm);
}
/* Widgets bewusst NICHT mehr: seit sie den Seitenverlauf tragen (und damit unsichtbar sind, ausser
   dass sie das Raster abdecken), waere der Schatten die einzige sichtbare Kante -- genau die sollte
   weg (2026-09-14: "entferne auch die umrandung der widgets"). */
/* ... aber nicht in der Menuespalte: dort ist die Liste Teil der Spalte, ein eigener Schatten
   waere wieder der Kasten im Kasten, den der Nutzer weghaben wollte. Diese Regel muss NACH der
   Theme-Regel stehen -- sie hat sonst die schwaechere Spezifitaet. */
html[data-theme="hell"] .app-context :is(.widget, .wiki-sidebar, .boards-sidebar) { box-shadow: none; }
/* Kein Rahmen, aber ein eigener Hintergrund pro Widget (var(--panel)) -- eine Stufe heller
   als der Zonen-Hintergrund (var(--zone-bg)) und eine Stufe dunkler als die Item-Karten darin
   (.agenda-item/.chat-message nutzen var(--panel-2)), damit Widget und Zone unterscheidbar
   bleiben, ohne dass Widget- und Item-Ebene farblich verschmelzen. */
/* Seit 2026-09 (Schritt 6 der Design-Ueberarbeitung) drei statt vier Ebenen: Seite -> Karte
   (Agenda und Widgets gleich, var(--panel)) -> Eintrag (var(--panel-2)). Vorher lagen die Widgets
   zusaetzlich in einer eigenen Zonenflaeche und die Agenda-Karte hatte einen anderen Grauton als die
   Widgets daneben; dazu trennte eine Linie beide Spalten, obwohl der Spaltenabstand das schon tut. */
.widget-agenda-main {
  display: flex; flex-direction: column; flex: 1; min-height: 0; min-width: 0; overflow: hidden;
  background: var(--panel); border: none;
  padding: var(--space-4);
}
/* Nutzer-Feedback: "mache mir die agenda und kalender umrandung bei der mobile ansicht weg und
   passe die breite an den screen des mobilgeräts an ... so habe ich ein paar pixel mehr" -- auf
   Mobile ist die Agenda ohnehin der einzige Dashboard-Inhalt (siehe .dashboard-widgets-area
   @media 880px), die eigene Karten-Flaeche (Hintergrund+Padding) grenzt sie dann gegen nichts
   mehr ab, kostet aber Breite. main{padding} bleibt als einziger Seitenabstand bestehen. Der
   .year-agenda-col-Zwilling dieser Klasse ist bei dieser Breite ohnehin ausgeblendet (siehe
   dort), die Regel trifft hier also nur die Dashboard-Agenda. */
/* Ohne eigene Flaeche auch ohne Kante -- sonst rahmt die Linie die Eintraege buendig ein. */
@media (max-width: 880px) { .widget-agenda-main { background: none; padding: 0; outline: none; } }
.dashboard-widgets-area { background: none; border-radius: var(--radius); padding: 0; }
/* Verschieben heisst tauschen (2026-09-14): die gezogene Kachel wird blass, die Kachel unter dem
   Zeiger zeigt einen Tauschpfeil, und beim Loslassen gleiten beide an ihren neuen Platz
   (siehe tauscheKacheln() in app.js). */
.widget.wird-gezogen { opacity: 0.4; }
.widget.kachel-tauschziel { outline: 2px dashed var(--accent-strong); outline-offset: -2px; }
.kachel-tauschpfeil {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: var(--space-3);
  pointer-events: none; z-index: 3;
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  border-radius: inherit; font-size: var(--fs-4xl); color: var(--accent-light);
  animation: kachelTauschPuls var(--duration-slow) ease both;
}
/* Zweites Ziel je Kachel: im unteren Drittel haengt sich die gezogene Kachel DARUNTER statt zu
   tauschen (2026-09-14: "lasse mich noch widgets unterhalb von anderen widgets ziehen"). Statt der
   gestrichelten Umrandung des Tauschziels zeigt ein kraeftiger Balken an der Unterkante, wo die
   Kachel landet -- dieselbe Sprache wie eine Einfuegemarke in einer Liste. */
.widget.kachel-einfuegeziel { outline: none; }
.widget.kachel-einfuegeziel::after {
  content: ""; position: absolute; left: var(--space-5); right: var(--space-5); bottom: 0; height: 4px;
  background: var(--accent-strong); border-radius: var(--radius-full); z-index: 4;
}
/* Die Beschriftung sitzt beim Einfuegen unten an der Kante, nicht mittig -- sie gehoert zum Balken. */
.kachel-tauschpfeil.kachel-pfeil-einfuegen {
  justify-content: flex-end; padding-bottom: var(--space-6); gap: var(--space-2);
  background: linear-gradient(to bottom, transparent 30%, color-mix(in srgb, var(--accent) 26%, transparent) 100%);
}
/* Die Beschriftung im Pfeil-Overlay (Name des Tauschpartners plus Nebenwirkung) ist seit 20.09
   entfallen -- "beim verschieben von widgets steht oft zu viel da mache einfach nur die switch
   anzeige". Die zugehoerigen Regeln sind damit weg. */
@keyframes kachelTauschPuls { from { opacity: 0; scale: 0.85; } to { opacity: 1; scale: 1; } }
/* Nutzerwunsch 2026-09-14: "zeige mir auch an wenn ein groesseres widget zwei kleinere verschieben
   wuerde" -- jede Kachel, die durch den Tausch ihren Platz raeumen muesste, wird waehrend des
   Ziehens mitmarkiert. Bewusst schwaecher als das Tauschziel (gepunktet statt gestrichelt, halbe
   Deckkraft), damit klar bleibt, wo man loslaesst. Siehe kachelnDieMitwandern() in app.js. */
.widget.kachel-wandert-mit { outline: 2px dotted color-mix(in srgb, var(--accent-strong) 55%, transparent); outline-offset: -2px; }
.kachel-wandert-hinweis {
  /* Unten rechts statt oben rechts: oben sitzen in mehreren Widgets die Kopfknoepfe (Kalender,
     Geburtstage vergroessern), der Hinweis lag sonst darauf. */
  position: absolute; bottom: var(--space-3); right: var(--space-3); z-index: 3; pointer-events: none;
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--text-dim);
  background: var(--panel); padding: var(--space-1) var(--space-3); border-radius: var(--radius-full);
  animation: kachelTauschPuls var(--duration-slow) ease both;
}
/* Waehrend des Tauschs gleiten ALLE Kacheln, nicht nur die beiden -- durch das dichte Raster
   ruecken auch die uebrigen nach. Groesse und Position werden zusammen animiert, damit man einen
   Groessenwechsel ueberhaupt sieht. */
.widget.kachel-tauscht {
  transition: translate var(--duration-slow) var(--ease-sheet), scale var(--duration-slow) var(--ease-sheet);
  z-index: 2;
}
/* Nach dem Tausch bleiben die beiden Partner kurz markiert -- sonst sucht man, was sich geaendert
   hat, vor allem wenn eine grosse und eine kleine Kachel getauscht haben. */
.widget.kachel-getauscht { animation: kachelGetauscht 1.4s ease both; }
@keyframes kachelGetauscht {
  0% { box-shadow: inset 0 0 0 2px var(--accent), 0 6px 20px var(--glas-schatten); }
  70% { box-shadow: inset 0 0 0 2px var(--accent), 0 6px 20px var(--glas-schatten); }
  100% { box-shadow: inset 0 1px 0 var(--glas-kante), 0 6px 20px var(--glas-schatten); }
}
.widget[draggable="true"] .widget-header h3 { cursor: grab; }
.widget[draggable="true"]:active .widget-header h3 { cursor: grabbing; }
.widget-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  /* Ueberschrift und Inhalt stehen weiter auseinander, dazwischen eine feine Linie statt einer
     zweiten Flaeche -- dasselbe Muster wie die Gruppen der Menuespalte. */
  margin-bottom: var(--space-6);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--border);
}
/* R68 Nutzer-Feedback: "mache bei allen dashboard widgets die überschriften so subtil wie das
   'august 2026' beim kalender ... icons entfernen die schrift überall gleich groß und immer
   capslock machen aber etwas kleiner noch" -- klein, gedimmt, Grossbuchstaben statt fettem Weiss,
   keine Icons mehr vor dem Text (siehe index.html, dort direkt entfernt). */
.widget h3 { margin: 0; font-size: var(--fs-2xs); font-weight: var(--fw-semibold); color: var(--text-dim); letter-spacing: 0.04em; text-transform: uppercase; }
.widget-header-actions { display: flex; align-items: center; gap: var(--space-3); }
/* Die Knoepfe einer Dashboard-Kachel stehen unten rechts, nicht in der Ueberschrift (2026-09-15:
   "mache die buttons der widgets unten an die widgets rechtsbuendig und nicht oben") -- die Zeile
   oben traegt dann nur noch den Namen der Kachel. Ausgenommen die Agenda-Kachel: ihre Leiste ist
   zugleich die (auf dem Handy oben klebende) Kopfzeile der Agenda, und der Mini-Kalender, dessen
   Zeile die Monatsnavigation ist. */
.widget:has(> .widget-header:not(.mini-cal-header) > .widget-header-actions):not(.widget-agenda-main) {
  position: relative; padding-bottom: calc(var(--space-6) + var(--control-h-sm));
}
.widget:not(.widget-agenda-main) > .widget-header:not(.mini-cal-header) > .widget-header-actions {
  position: absolute; right: var(--space-6); bottom: var(--space-5); z-index: 2;
}
/* Nutzer-Feedback: "den angeheftete events button zu den 3 anderen buttons der agenda schieben" --
   (bis 2026-09-15 nur mobil, siehe Regel darunter). */
/* Frueher nur mobil sichtbar (Desktop hatte #pinned-panel-btn in der Leiste links) -- seit
   2026-09-15 ueberall, der Zwilling in der Leiste ist weg ("entferne den angeheftet button aus der
   linken menueleiste unten"). */
.agenda-pinned-mobile-btn { display: inline-flex; }
/* Nur auf Mobile sichtbar (ersetzt dort Hinzufuegen/Vergangene-Events/Filter-Buttons durch ein
   einziges Dropdown, siehe @media weiter oben + showAgendaMoreMenu() in app.js). */
/* !important noetig: .icon-btn (weiter unten im Stylesheet, gleiche Spezifitaet) setzt selbst
   display:inline-flex und wuerde diese Basis-Ausblend-Regel sonst je nach Regel-Reihenfolge
   ueberschreiben. */
.agenda-more-mobile-btn { display: none !important; }
.mobile-refresh-btn { display: none !important; }
/* Mobil-Override hier (statt nur im @media-Block ganz am Dateianfang) platziert, damit sie im
   Stylesheet SPAETER als die obige Basis-Regel steht -- bei gleicher Selektor-Spezifitaet UND
   !important auf beiden Seiten entscheidet sonst rein die Reihenfolge im Stylesheet, nicht ob eine
   Regel in einem gerade zutreffenden @media-Block steht. */
@media (max-width: 880px) {
  .agenda-more-mobile-btn, .mobile-refresh-btn { display: inline-flex !important; }
}
/* Mini-Bongo-Cat-Badge in der mobilen Agenda-Kopfzeile, gleiches Nur-auf-Mobile-Muster wie
   .agenda-pinned-mobile-btn oben -- Nutzer-Feedback: "als mini version einfach nur die cat mit
   code mobil anzeigen" -> "den background weg machen und einfach nur den code, das reicht" ->
   "achso aber nicht die cat löschen, mach die kleine katze oben drüber über den code" -- Katze
   und Code gestapelt (Katze oben, Code darunter) statt nebeneinander, weiterhin ohne Pillen-
   Hintergrund. Die Katzengrafik (bongocat.png) ist selbst leicht schief gezeichnet/zugeschnitten --
   rotate() gleicht das fuer diese kleine Groesse optisch aus, im grossen Dashboard-Widget bleibt
   sie unangetastet. */
.agenda-bongocat-mini {
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 0;
  background: none;
  border: none;
  padding: 0 var(--space-2);
  cursor: pointer;
  color: var(--text);
  font-family: "JetBrains Mono", monospace;
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
  max-width: 100px;
}
.agenda-bongocat-mini-img { width: 24px; height: 24px; object-fit: contain; flex-shrink: 0; transform: rotate(-13deg); margin-bottom: -4px; }
#agenda-bongocat-mini-code { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.agenda-bongocat-mini-code-empty { color: var(--text-dim); font-weight: var(--fw-medium); font-style: italic; }
/* Muss NACH der ".agenda-bongocat-mini { display: none }"-Basisregel oben stehen, nicht im
   @media-Block ganz am Dateianfang (wo .agenda-pinned-mobile-btn seine Sichtbarkeits-Regel hat) --
   bei gleicher Spezifitaet gewinnt sonst die spaeter im Dokument stehende Basisregel unabhaengig
   von der Bildschirmbreite (verifiziert: .agenda-pinned-mobile-btn "funktioniert" dort nur zufaellig,
   weil .icon-btn weiter unten in der Datei ohnehin display:inline-flex erzwingt). */
@media (max-width: 880px) {
  .agenda-bongocat-mini { display: inline-flex; }
}
/* R43: Filter-/Hinzufuegen-Icons der Dashboard-Agenda sollen dauerhaft sichtbar sein, genau wie
   die aequivalenten Icons im Kalender-Tab (.year-agenda-controls, nie per Hover ausgeblendet) --
   die vorherige Hover-Only-Regel wich damit unnoetig vom Kalender-Verhalten ab. */
.icon-btn {
  background: var(--knopf);
  border: 1px solid var(--border);
  color: var(--text);
  width: var(--control-h-sm); height: var(--control-h-sm);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: var(--fs-body);
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  flex-shrink: 0;
  transition: background var(--duration-fast) ease, transform var(--duration-fast) ease;
}
.icon-btn:hover { background: var(--knopf-hover); }
/* Nutzer-Feedback: "mache anstatt dem angeheftet icon mit der zahl einfach wenn etwas angehefett
   ist das logo davon gelb" -- ersetzt das vorherige Zaehler-Badge am globalen Pin-Icon
   (#pinned-panel-btn), gleiche Gelbton wie ueberall sonst bei angehefteten/aktiven Zustaenden
   (siehe .pin-toggle-btn.pinned weiter unten). Von updatePinnedCountBadge() (app.js) gesetzt. */
/* Gilt jetzt fuer den Knopf in der Agenda (2026-09-15 gemeldet: "der events angeheftet button in
   der agenda leuchtet nicht") -- die Regel hing zuvor an der ID des entfallenen Leisten-Knopfs. */
#agenda-pinned-btn.pin-btn-has-items { background: color-mix(in srgb, var(--highlight) 18%, transparent); border-color: var(--highlight); color: var(--highlight); }
.icon-btn-with-badge { position: relative; overflow: visible; }
/* Kleines Zaehler-Badge (z.B. an anderer Stelle wiederverwendbar) oben rechts am Icon-Button --
   nur sichtbar, wenn per JS ein Wert > 0 gesetzt wird. */
.icon-btn-badge {
  position: absolute; top: -5px; right: -5px;
  min-width: 15px; height: 15px; padding: 0 var(--space-2);
  border-radius: var(--radius-full);
  background: var(--accent); color: var(--text-on-color);
  font-size: var(--fs-2xs); font-weight: var(--fw-bold); line-height: 15px;
  text-align: center;
  border: 1.5px solid var(--panel);
}
@media (max-width: 880px) {
  .icon-btn { width: var(--control-h); height: var(--control-h); font-size: var(--fs-body); }
}
/* Touch: Icon-Knoepfe, Pillen und kleine Umschalter bleiben sichtbar kompakt (36px bzw. ihre eigene
   Groesse), bekommen aber eine unsichtbare Trefferflaeche von mindestens 44x44px -- sonst wuerden
   Leisten mit vier Icon-Knoepfen klobig. Das ::after braucht einen positionierten Knopf; :where()
   gibt dem position:relative die Spezifitaet null, damit jede andere Positionsangabe (ein Knopf,
   der ueber eine zweite Klasse absolut sitzt) gewinnt und nichts verrutscht. Gemessen mit
   TEST=touch (tippt virtuell 22px um jede Knopfmitte). */
@media (pointer: coarse) {
  .icon-btn { width: var(--control-h-sm); height: var(--control-h-sm); }
  .filter-btn, .category-tab, .event-preview-attendance-btn { min-height: var(--control-h-sm); }
  /* button.wiki-tb-btn statt .wiki-tb-btn: die Grundregel (28px) steht weiter hinten im Stylesheet
     und gewann bei gleicher Spezifitaet. */
  /* 32px breit statt 36: mit 36px brach die Editor-Leiste auf dem Handy in eine zweite Zeile um.
     Die Trefferflaeche waechst dafuer nach oben und unten auf 44px (siehe ::after unten). */
  button.wiki-tb-btn { width: 32px; height: var(--control-h-sm); }
  .settings-tab-btn { min-height: var(--control-h); }
  /* .chip-create-btn (das "+" in Tag-/Teilnehmer-Auswahl, 26px) seit 2026-09-11: TEST=touch sah es erst,
     seit die Tags im Handy-Formular nach oben gewandert sind -- vorher lagen sie ausserhalb des Messbilds. */
  :where(.icon-btn, .filter-btn, .category-tab, .event-preview-attendance-btn, .wiki-tb-btn, .date-today-btn,
         .mini-cal-arrow, .event-preview-media-folder-onoff-seg, #year-prev, #year-next, #profile-menu-btn,
         #agenda-bongocat-mini, #event-preview-attend-btn, #event-preview-header-chat-btn, .chip-create-btn) { position: relative; }
  :is(.icon-btn, .filter-btn, .category-tab, .event-preview-attendance-btn, .wiki-tb-btn, .date-today-btn,
      .mini-cal-arrow, .event-preview-media-folder-onoff-seg, .modal-close, #year-prev, #year-next, #profile-menu-btn,
      #agenda-bongocat-mini, #event-preview-attend-btn, #event-preview-header-chat-btn, .chip-create-btn)::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    width: max(100%, 44px); height: max(100%, 44px); transform: translate(-50%, -50%);
  }
  /* Knoepfe, die dicht nebeneinander stehen (Editor-Leiste, An/Aus-Umschalter): Trefferflaeche nur nach
     oben und unten erweitern -- zur Seite wuerde sie den Nachbarn ueberdecken und dessen Flaeche kleiner
     machen statt die eigene groesser. */
  button.wiki-tb-btn::after, .event-preview-media-folder-onoff-seg::after { width: 100%; }
  /* Wo sich Trefferflaechen sonst ueberlappen wuerden, mehr Abstand bzw. echte Groesse (TEST=touch zeigte
     43px zwischen Pin- und Mehr-Knopf der Agenda, "Wiederholen" ueber der Flaeche von "Anheften"). */
  .widget-header-actions { gap: var(--space-4); }
  .event-preview-media-folder-onoff-seg { min-width: 44px; }
  /* Album/Hochladen stehen seit 2026-09-11 bei den Foto-Kacheln und damit auch auf dem Handy offen da
     (vorher nur im dort ausgeblendeten Funktionen-Raster) -- volle Hoehe statt Trefferflaeche, die zwei
     stehen dicht nebeneinander. */
  .event-preview-media-actions .event-preview-funktion-btn { min-height: var(--control-h); }
}
.icon-btn:active { transform: scale(0.92); }
.lang-toggle-btn { width: auto; padding: 0 var(--space-4); font-size: var(--fs-xs); font-weight: var(--fw-bold); letter-spacing: 0.03em; }
/* Top-Nav-Icon-Cluster (Org-Umschalter, Sprache, Pin, Settings, Profil) randlos, wie .status-ticker
   -- Hintergrund bleibt zur Abgrenzung, aber ohne sichtbaren Rahmen. */
.user-menu .icon-btn, .user-menu .org-dropdown-btn, .user-menu .profile-menu-btn { border: none; }
/* Seit 2026-09-11 (Variante "A getönt", siehe .main-nav .tab-btn.active) auch ohne Flaeche -- gefuellte
   Kaestchen neben den flachen Reitern waren zwei Knopfsprachen in einer Leiste. Hover zeigt die Flaeche;
   das gelbe Pin-Icon (#pinned-panel-btn.pin-btn-has-items) gewinnt per ID weiter. */
.user-menu .icon-btn { background: transparent; color: var(--text-dim); }
.user-menu .icon-btn:hover { background: var(--panel-hover); color: var(--text); }
.user-menu .profile-menu-btn:hover { background: var(--panel); }

/* ---------- Status-Ticker: schmale Leiste unter der Menüleiste (Uhrzeit, Datum, Wetter) ---------- */
.status-ticker {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  font-size: var(--fs-sm);
  color: var(--text-dim);
  white-space: nowrap;
  /* Seit 2026-09 im selben Ton wie .topbar, getrennt nur durch eine feine Linie -- Kopfleiste und
     Statuszeile wirken so als eine ruhige Einheit statt als zwei verschieden graue Baender. */
  background: var(--panel); border-top: 1px solid var(--tint);
  /* Die Zeile nutzt die volle Breite der Leiste: Uhr, Datum und der Wetter-/Changelog-Platz stehen
     ganz links, die Anwesenden ganz rechts (2026-09-16: "mache uhr datum, und das wetter changelog
     ding linksbuendig an der leiste und die online teilnehmer rechtsbuendig aber schon wirklich
     weiter links und rechts als jetzt"). Frueher wuchs das Padding auf breiten Bildschirmen mit der
     1680px-Spalte von main mit -- dadurch klebte alles in der Mitte. */
  padding: var(--space-4) var(--space-12);
  overflow-x: auto;
}
/* Name der offenen Seite in der Farbe der Organisation. Stand bis 2026-09-13 in der Statusleiste,
   seitdem rechtsbuendig neben dem Hub-Namen (Nutzerwunsch). --accent-light statt --accent: die
   volle Akzentfarbe ist auf dunklem Grund als Schrift zu dunkel (siehe Farbregeln oben). */
.ticker-seitenname {
  font-size: var(--fs-xl); font-weight: var(--fw-semibold); color: var(--accent-light);
  white-space: nowrap;
}
.ticker-seitenname:empty { display: none; }
.status-ticker #clock-time { font-weight: var(--fw-bold); color: var(--text); font-variant-numeric: tabular-nums; }
.status-ticker #clock-date { text-transform: capitalize; }
.topbar-clock-sep { opacity: 0.5; }
.ticker-module { display: flex; align-items: center; gap: var(--space-3); }
.ticker-divider { width: 1px; align-self: stretch; background: var(--border); flex-shrink: 0; }
.ticker-online { margin-left: auto; gap: var(--space-6); }
/* R60 Nutzer-Feedback: Wetter/letzte-Änderung-Crossfade -- nur das aktive Element ist sichtbar.
   Bugfix (Nutzer-Feedback: "die menüleiste 2 ändert ihre größe wenn zwischen wetter und log
   gewechselt wird"): die fruehere display:none/flex-Umschaltung nahm das inaktive Element
   komplett aus dem Layout -- die Stack-Hoehe (und damit die ganze .status-ticker-Leiste) richtete
   sich dadurch immer nur nach dem GERADE sichtbaren Element, sprang also bei unterschiedlich hohem
   Inhalt (z.B. Wetter mit 3-Tage-Mini-Vorschau vs. kuerzerer Log-Zeile) beim Wechsel um. Jetzt
   liegen beide Elemente uebereinander in derselben Grid-Zelle (grid-area:1/1) und bleiben BEIDE
   permanent Teil des Layouts (nur opacity/visibility wechseln) -- die Stack-Groesse ergibt sich
   dadurch aus dem hoeheren/breiteren der beiden Eintraege und bleibt beim Umschalten stabil. */
.ticker-crossfade-stack { display: inline-grid; align-items: center; }
/* display/align-items/gap kommen weiterhin von der geteilten .ticker-module-Klasse (beide
   Crossfade-Items tragen sie zusaetzlich in index.html) -- hier NICHT erneut setzen, sonst
   ueberstimmt diese spaeter im File stehende Regel bei gleicher Spezifitaet deren gap:6px. */
.ticker-crossfade-item {
  grid-area: 1 / 1;
  opacity: 0; visibility: hidden; transition: opacity var(--duration-slow) ease; pointer-events: none;
}
.ticker-crossfade-item.active { opacity: 1; visibility: visible; pointer-events: auto; }
/* Button-artiges Pill statt reinem Inline-Text -- Hintergrund statt Rahmen (passt zum Rest der
   randlosen Topbar-Optik), Hover-Profilkarte reagiert jetzt auf das GANZE Pill statt nur auf das
   winzige 18px-Avatarbild (data-avatar-* Attribute sitzen zusaetzlich auf diesem Element, siehe
   loadOnlineMembers() in app.js). */
.ticker-online-member {
  display: flex; align-items: center; gap: var(--space-3); cursor: pointer;
  background: var(--tint); border-radius: var(--radius-full); padding: var(--space-2) var(--space-5) var(--space-2) var(--space-2);
  transition: background var(--duration-fast) ease;
}
.ticker-online-member:hover { background: var(--tint-hover); }
.ticker-online-avatar { width: 18px; height: 18px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.ticker-online-avatar-placeholder { font-size: var(--fs-xl); line-height: 1; color: var(--text-dim); flex-shrink: 0; }
.ticker-online-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--success); flex-shrink: 0; box-shadow: 0 0 4px color-mix(in srgb, var(--success) 70%, transparent); }
/* R60 Nutzer-Feedback: gleiche Pill-Optik wie .ticker-online-member oben, aber ohne Online-Punkt
   -- fuer alle anderen Stellen, wo eine Person passiv angezeigt wird (renderUserPillHtml()). */
.user-pill {
  display: inline-flex; align-items: center; gap: var(--space-3); cursor: pointer;
  background: var(--tint); border-radius: var(--radius-full); padding: var(--space-2) var(--space-5) var(--space-2) var(--space-2);
  transition: background var(--duration-fast) ease; font-size: var(--fs-md); color: var(--text);
}
.user-pill:hover { background: var(--tint-hover); }
.user-pill-avatar { width: 18px; height: 18px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.user-pill-avatar-placeholder { font-size: var(--fs-xl); line-height: 1; color: var(--text-dim); flex-shrink: 0; }
.weather-inline { display: flex; align-items: center; gap: var(--space-3); }
.weather-inline .weather-icon { font-size: var(--fs-xl); }
.weather-inline .weather-forecast-mini { display: flex; gap: var(--space-4); margin-left: var(--space-5); }
.weather-inline .weather-forecast-mini span { font-size: var(--fs-sm); }
.weather-configure-hint {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--text-dim); font-size: var(--fs-sm); text-decoration: underline;
}
.weather-configure-hint:hover { color: var(--accent); }
@media (max-width: 880px) {
  /* Nutzer-Feedback: "keine Menüleiste 2" -- die komplette zweite Leiste (Uhrzeit/Wetter/Ticker)
     entfaellt auf dem Handy komplett statt nur einzelne Module darin auszublenden. Muss HIER
     (nach der Basis-.status-ticker-Regel weiter oben) stehen -- bei gleicher Klassen-Spezifitaet
     gewinnt sonst die spaeter im Stylesheet stehende Regel, unabhaengig von der Media Query. */
  .status-ticker { display: none; }
}
@media (max-width: 560px) {
  /* Nutzer-Feedback: laengere Ticker-Eintraege (z.B. "X hat Event ... erstellt") wurden auf
     schmalen Breiten mitten im Wort hart abgeschnitten, ohne jeden Hinweis, dass per Wischen nach
     rechts noch mehr folgt (overflow-x:auto ist zwar da, aber unsichtbar). Weicher Ausblend-
     Farbverlauf am rechten Rand statt eines harten Schnitts signalisiert "hier geht's weiter". */
  .status-ticker {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
  }
}

/* ---------- Mini-Kalender (Dashboard-Widget) ---------- */
/* Das Widget oeffnet den grossen Kalender -- ohne Rueckmeldung beim Ueberfahren sieht man ihm das
   nicht an (Nutzerwunsch 2026-09-13: "mache mir eine kleine hover animation beim kalender das man
   den anklicken kann sonst checkt man das nicht"). Die Kachel hebt sich leicht an, bekommt eine
   Akzent-Kante, und der Aufklapp-Knopf oben rechts faerbt sich mit. */
/* Milchglas im iOS-Stil (2026-09-14): kraeftiger Weichzeichner mit angehobener Saettigung, die
   Kachelfarbe nur zu zwei Dritteln deckend, dazu ein hauchduenner heller Streifen an der Oberkante
   (inset-Schatten statt Rahmen -- ein echter Rahmen wirkte als Kasten) und ein weicher Schatten
   darunter, der die Kachel vom Raster abhebt. */
.widget {
  /* Flaeche aus den gemeinsamen Variablen -- dieselbe wie auf allen anderen Seiten, siehe
     Abschnitt "Einheitliche Flaechen" am Ende der Datei. */
  transition: translate var(--duration-base) ease, box-shadow var(--duration-base) ease;
}
/* Nur die anklickbare Kachel gibt eine Rueckmeldung -- und zwar ohne Flaeche. Die
   Geburtstags-Kachel gehoert seit 2026-09-16 nicht mehr dazu ("entferne das klickbare geburtstags
   widget - geburtstage an sich sollen weiterhin klickbar sein"): dort fuehren nur noch die
   einzelnen Karten und der Knopf oben rechts irgendwohin. */
[data-widget-id="minical"]:hover { translate: 0 -2px; }
[data-widget-id="minical"] { cursor: pointer; }
/* Nicht jeder Inhalt ist eine schmale Liste (2026-09-14): ein Monatsraster mit sieben Spalten, eine
   Reihe Vorschaubilder und die Bongo-Cat-Zeile (Sprechblase neben Katze) brauchen Breite statt
   Hoehe. Alle anderen Kacheln bleiben eine Spalte breit -- Textlisten gewinnen dadurch nichts. */
[data-widget-id="minical"] { --kachel-einheiten: 3; --kachel-spalten: 2; }
[data-widget-id="media"] { --kachel-spalten: 2; }
[data-widget-id="bongocat"] { --kachel-einheiten: 1; }
/* Nutzerwunsch 2026-09-14: "das in chatrooms kann um die haelfte in der hoehe reduziert werden.
   genauso wie angepinnte links und neuste notes", direkt danach auch "das zuletzt geaenderte
   widget kann auch um die haelfte verkuerzt werden" -- vier kurze Listen, die ihre zwei
   Rasterzeilen nie gefuellt haben. Eine Zeile (144px) reicht, der Rest scrollt im Widget. */
[data-widget-id="chatroom-presence"],
[data-widget-id="links"],
[data-widget-id="wiki-recent"],
[data-widget-id="activity"],
/* "Neueste in Sammlung" zeigt zwei Zeilen -- eine Einheit reicht (2026-09-14). */
[data-widget-id="pinned-games"] { --kachel-einheiten: 1; }
/* ... dafuer muessen die beiden Zeilen aber auch hineinpassen: mit den 56px der Standardkarte
   wurden sie abgeschnitten (2026-09-15 gemeldet: "das neuste in sammlungen widget ist zu klein
   selbst zwei eintraege werden abgeschnitten"). Kleineres Vorschaubild und weniger Innenabstand
   ergeben 36px je Karte -- zwei davon plus Abstand passen in die 81px der Kachel. */
#pinned-games-widget-list .agenda-body-thumb-left { padding: var(--space-2) var(--space-4); gap: var(--space-4); }
#pinned-games-widget-list .agenda-thumb, #pinned-games-widget-list .agenda-thumb-placeholder {
  width: 28px; height: 28px; font-size: var(--fs-md);
}
#pinned-games-widget-list .agenda-title { font-size: var(--fs-md); }
/* Countdown: zwei Eintraege sollen hineinpassen (2026-09-14) -- bei zwei Rastereinheiten teilen
   sich zwei Countdowns die Flaeche genau, einer allein fuellt sie ganz. */
[data-widget-id="countdown"] { --kachel-einheiten: 2; }
/* Passt nur EINE Spalte ins Raster (schmales Fenster), darf keine Kachel zwei belegen -- sonst
   entsteht eine zweite, unsichtbare Spalte und die Seite laeuft quer. */
@media (max-width: 1100px) {
  .widget { --kachel-spalten: 1; }
}
/* Angehoben wird ueber "translate", nicht ueber "transform": .widget laeuft mit
   animation: fadeInUp ... both, und ein Animations-Keyframe schlaegt jede normale
   transform-Deklaration -- die Kachel blieb dadurch messbar stehen (TEST=test). */

[data-widget-id="minical"]:hover #mini-cal-open-btn { background: var(--accent-tint); color: var(--accent-light); }
[data-widget-id="minical"] #mini-cal-open-btn { transition: background var(--duration-fast) ease, color var(--duration-fast) ease; }
/* Die Tage darin bleiben eigenstaendig bedienbar (Termin oeffnen, Tagesvorschau) -- der
   Zeigefinger der Kachel darf daher nicht ueber ihren eigenen Zustand luegen. */
[data-widget-id="minical"] .year-week-bar { cursor: pointer; }
/* Vier Spalten auf dem Dashboard lassen dem Widget wenig Breite: Monatsnavigation und die beiden
   Knoepfe passen nicht immer in eine Zeile -- umbrechen statt rechts abgeschnitten zu werden. */
/* Rhythmus-Auswahl im Formular, erscheint nur bei aktivem "Wiederholen". Beide Felder stehen
   UNTEREINANDER (2026-09-14, Nutzerwunsch: "hier sollte der weekly knopf unter dem wiederholen
   knopf sein und wiederum darunter das bis feld. dann wird nichts verschoben") -- auf dem Handy
   ueber die volle Breite der Optionen, am Desktop in der Spalte des Wiederholen-Knopfes (siehe
   @media weiter oben). */
/* Seit 2026-09-15 ein kleines Fenster ueber dem Knopf statt einer Zeile darunter ("mache den
   wiederholen button, so dass ein kleines popup kommt ... und es nicht nach unten aufklappt") --
   absolut positioniert, verschiebt also nichts mehr. Der Anker ist der Wrapper um den Knopf. */
.event-form-option-anker { position: relative; display: block; }
.event-form-option-anker > .pin-toggle-btn { width: 100%; }
.event-form-wiederholung {
  position: absolute; top: calc(100% + var(--space-3)); left: 0; z-index: var(--z-popover);
  min-width: 230px; display: flex; flex-direction: column; gap: var(--space-3); align-items: stretch;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-6); box-shadow: var(--shadow-lg);
  animation: fadeInUp var(--duration-fast) ease both;
}
/* [hidden] braucht die eigene Regel, weil die Basisregel ein display setzt (bekannter Fallstrick
   in dieser Datei). */
.event-form-wiederholung[hidden] { display: none; }
.event-form-wiederholung-aus { justify-content: center; font-size: var(--fs-sm); }
.event-form-wiederholung select { width: 100%; min-height: var(--control-h); }
/* flex-direction MUSS hier stehen: .modal label stapelt Beschriftung und Feld untereinander (das
   uebliche Formularmuster dieser App) -- genau dadurch war diese Zelle 52px hoch statt 32 und das
   Datumsfeld auf 22px zusammengequetscht. */
.event-form-wiederholung label.event-form-wiederholung-bis {
  display: flex; flex-direction: row; align-items: center; gap: var(--space-3);
  min-width: 0; flex-wrap: nowrap; font-size: var(--fs-sm); color: var(--text-dim);
}
.event-form-wiederholung-bis input { flex: 1 1 0; min-width: 0; min-height: var(--control-h); }
.mini-cal-header { margin-bottom: var(--space-3); flex-wrap: wrap; gap: var(--space-3); }
.mini-cal-header .widget-header-actions { margin-left: auto; }
.mini-cal-nav { display: flex; align-items: center; gap: var(--space-2); }
/* Monatsname stand frueher im Widget-Titel selbst (ersetzte "Kalender" komplett) -- jetzt ein
   eigenes kleines Label in der Nav-Zeile, Titel bleibt statisch "Kalender" wie bei den anderen
   Widgets. R68 Nutzer-Feedback: "august 2026 (kalender jahres switcher am dashboard) daran
   anpassen" -- gleicher Stil wie die neuen .widget h3-Ueberschriften (klein, gedimmt, Grossbuchstaben). */
.mini-cal-month-label { font-size: var(--fs-2xs); font-weight: var(--fw-semibold); color: var(--text-dim); letter-spacing: 0.04em; text-transform: uppercase; white-space: nowrap; margin: 0 var(--space-1); }
/* R68 Nutzer-Feedback: "die icons der pfeile so machen wie auf dem großen kalender" -- gleicher
   schlichter Stil wie .year-nav button im Kalender-Tab (kein Hintergrund/Rahmen, nur ein duennes
   Pfeil-Glyph). */
.mini-cal-arrow {
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; color: var(--text-dim);
  width: 20px; height: 20px; padding: 0;
  cursor: pointer; font-size: var(--fs-lg);
}
.mini-cal-arrow:hover { color: var(--text); }
/* Nutzt dieselbe Bau-Logik wie der grosse Kalender (buildYearWeekRow()/.year-timeline-month-card,
   siehe buildMiniMonthCard() in app.js) statt eines eigenen Punkte/Balken-Rasters -- "genau gleich
   wie der normale Kalender", hier rein per CSS auf Widget-Groesse verkleinert. Sticky wird nicht
   gebraucht (das Widget scrollt selbst nicht), deshalb position:static statt der grossen Variante. */
.mini-cal-dow-header { position: static; margin-bottom: var(--space-3); padding: var(--space-2); box-shadow: none; }
.mini-cal-dow-header span { font-size: var(--fs-2xs); }
/* Ohne eigene Flaeche: die Monats-Kachel erbt sonst das sehr dunkle --panel der grossen
   Kalender-Kacheln und steht als schwarzer Block im Widget (2026-09-13 gemeldet: "das kalender
   widget background ist zu dunkel"). Mit ID, weil die Basisregel .year-timeline-month-card weiter
   unten im Stylesheet steht und bei gleicher Spezifitaet sonst gewinnt -- genau daran ist der
   erste Versuch gescheitert. */
.mini-cal-card { padding: 0 var(--space-2) var(--space-1); margin-bottom: var(--space-4); }
#mini-calendar .mini-cal-card { background: none; }
/* Sechs Wochenzeilen fuellen die Kachel genau aus (2026-09-14, siehe buildMiniMonthCard()) -- die
   Zeilen teilen sich dafuer die verbleibende Hoehe, statt jede ihre Inhaltshoehe zu nehmen und
   unten einen leeren Streifen zu lassen. */
#mini-calendar { flex: 1; min-height: 0; display: flex; flex-direction: column; }
#mini-calendar .mini-cal-card { flex: 1; min-height: 0; display: flex; flex-direction: column; }
#mini-calendar .mini-cal-card .year-week-block { flex: 1; min-height: 0; justify-content: flex-start; }
#mini-calendar .mini-cal-card .year-week-lanes { min-height: 0; }
.mini-cal-card:last-child { margin-bottom: 0; }
/* R68 Nutzer-Feedback: "das datum und jahr im kalender widget unten drunter also im kalender
   selbst kann dann auch entfernt werden es ist ja oben im switcher sichtbar" -- das Monats-Label
   IN der Kachel (buildMiniMonthCard() haengt es weiterhin an, hier nur ausgeblendet) ist redundant
   zum Monats-/Jahres-Label oben in .mini-cal-nav. */
.mini-cal-card .year-timeline-month-label { display: none; }
.mini-cal-card .year-week-daynum { font-size: var(--fs-2xs); padding: 1px 0; }
.mini-cal-card .year-week-lanes { grid-auto-rows: 13px; gap: var(--space-1) var(--space-2); min-height: 45px; }
/* Nutzer-Feedback: "im kalender widget auf dem dashboard die titel der kalender einträge im
   balken dafür zentrieren" -- nur im Mini-Widget, der grosse Kalender bleibt linksbuendig. */
.mini-cal-card .year-week-bar { font-size: var(--fs-2xs); padding: 0 var(--space-2); margin: 0 var(--space-1); justify-content: center; }
/* Nutzer-Feedback: "geburtstage im kalender widget sind noch nicht zentriert von der schrift" --
   text-overflow:ellipsis (Basis-Regel, siehe .year-week-bar-label weiter unten) verankert
   ueberlaufenden Text trotz text-align:center faktisch am linken Rand (bekannte CSS-
   Inkompatibilitaet zwischen ellipsis und center bei nowrap-Text) -- betrifft v.a. Geburtstage,
   deren Balken nur einen einzigen Tag breit ist und bei laengeren Namen fast immer ueberlaeuft.
   text-overflow:clip statt ellipsis laesst den Browser wieder symmetrisch um die Mitte clippen. */
.mini-cal-card .year-week-bar-label { text-align: center; text-overflow: clip; }

/* Der Changelog-Rundknopf (.changelog-btn) samt Popup ist 2026-09-15 entfallen -- seit Changelog
   und Anleitung in einem Fenster stecken, war er auf beiden Breiten ausgeblendet. Der Hilfe-Knopf
   traegt jetzt allein das Abzeichen. */
.guide-btn {
  /* --z-sticky statt --z-popover: der Rundknopf ist Moebel der Seite, kein Popup. Mit 300 lag er
     UEBER jedem Fenster -- in der Event-Ansicht deckte er unten links "Titelbild ansehen" ab und
     haette dessen Klicks geschluckt (2026-09-16 im Testlauf gefunden, TEST=eventansicht meldete es
     als "unter den Rundknoepfen"). */
  position: fixed; left: 16px; bottom: 62px; z-index: var(--z-sticky);
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--knopf); border: 1px solid var(--border); color: var(--text-dim);
  font-size: var(--fs-xl); cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.guide-btn:hover { background: var(--panel); color: var(--text); }
.guide-modal-inner { max-width: 980px; width: 92vw; height: 82vh; max-height: 820px; }
.guide-body { display: flex; gap: var(--space-12); align-items: flex-start; }
.guide-toc {
  flex: 0 0 190px; position: sticky; top: 0; display: flex; flex-direction: column; gap: var(--space-1);
  border-right: 1px solid var(--border); padding-right: var(--space-8);
}
.guide-toc a {
  color: var(--text-dim); text-decoration: none; font-size: var(--fs-md); padding: var(--space-3) var(--space-4);
  border-radius: var(--radius); line-height: 1.3;
}
.guide-toc a:hover { background: var(--panel-hover); color: var(--text); }
/* Nutzer-Feedback: "zeige bei der anleitung noch links hervorgehoben an wo man gerade links ist
   wenn man den text liest" -- Scrollspy (siehe initGuideScrollspy() in app.js), markiert den Link
   des gerade sichtbaren Abschnitts. Dieselbe Markierung wie der offene Eintrag in der zweiten
   Menuespalte (Raum, Notiz, Album): getoente Flaeche in der Akzentfarbe, keine eigene Optik mehr
   (2026-09-15: "dieses design vom hervorheben gibt es glaube ich nirgends passe es an"). */
.guide-toc a.active { background: var(--aktiv-kachel); color: var(--text); font-weight: var(--fw-semibold); }
.guide-content { flex: 1; min-width: 0; }
.guide-content section { margin-bottom: var(--space-12); scroll-margin-top: 8px; }
.guide-content section:last-child { margin-bottom: 0; }
.guide-content h3 { margin: 0 0 var(--space-4); font-size: var(--fs-body); }
.guide-content p { margin: 0 0 var(--space-5); color: var(--text-dim); line-height: 1.55; font-size: var(--fs-lg); }
.guide-content p:last-child { margin-bottom: 0; }
.guide-content ul { margin: 0 0 var(--space-5); padding-left: var(--space-10); color: var(--text-dim); line-height: 1.55; font-size: var(--fs-lg); }
.guide-content li { margin-bottom: var(--space-2); }
.guide-content b { color: var(--text); }
.guide-content code { background: var(--panel-2); padding: 1px var(--space-3); border-radius: var(--radius-xs); font-size: 0.85em; }
@media (max-width: 720px) {
  .guide-body { flex-direction: column; }
  .guide-toc { position: static; flex: none; border-right: none; border-bottom: 1px solid var(--border); padding-right: 0; padding-bottom: var(--space-6); flex-direction: row; flex-wrap: wrap; }
}
.changelog-badge {
  position: absolute; top: -2px; right: -2px; width: 10px; height: 10px; border-radius: 50%;
  background: var(--danger); border: 2px solid var(--bg);
}
.changelog-version { margin-bottom: var(--space-4); border-bottom: 1px solid var(--border); padding-bottom: var(--space-4); }
.changelog-version:last-child { border-bottom: none; }
.changelog-version summary { cursor: pointer; display: flex; align-items: baseline; gap: var(--space-4); list-style: none; }
.changelog-version summary::-webkit-details-marker { display: none; }
.changelog-version summary::before { content: "▸"; color: var(--text-dim); font-size: var(--fs-2xs); margin-right: var(--space-1); }
.changelog-version[open] summary::before { content: "▾"; }
.changelog-version h4 { margin: 0; font-size: var(--fs-lg); }
.changelog-version .changelog-date { color: var(--text-dim); font-size: var(--fs-xs); }
.changelog-version ul { margin: var(--space-3) 0 0; padding-left: var(--space-12); font-size: var(--fs-sm); color: var(--text-dim); }
.changelog-version li { margin-bottom: var(--space-2); }
/* R42: Hauptversionen (z.B. "39.x") als aeussere, fett hervorgehobene Gruppe -- die bisherigen
   .changelog-version-Bloecke (jetzt eine Ebene tiefer verschachtelt) bleiben optisch/strukturell
   unveraendert, nur eingerueckt. */
.changelog-group { margin-bottom: var(--space-6); border-bottom: 1px solid var(--border); padding-bottom: var(--space-5); }
.changelog-group:last-child { border-bottom: none; }
.changelog-group > summary { cursor: pointer; display: flex; align-items: baseline; gap: var(--space-4); list-style: none; }
.changelog-group > summary::-webkit-details-marker { display: none; }
.changelog-group > summary::before { content: "▸"; color: var(--text-dim); font-size: var(--fs-sm); margin-right: var(--space-1); }
.changelog-group[open] > summary::before { content: "▾"; }
.changelog-group > summary h4 { margin: 0; font-size: var(--fs-body); }
.changelog-group-summary { margin: var(--space-3) 0 var(--space-5); padding-left: var(--space-10); font-size: var(--fs-md); color: var(--text-dim); line-height: 1.4; }
.changelog-group .changelog-version { margin-left: var(--space-10); }
.unread-badge {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--danger); margin-right: var(--space-3); vertical-align: middle; flex-shrink: 0;
  animation: unread-pulse 1.6s ease-in-out infinite;
}
@keyframes unread-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger) 55%, transparent); }
  50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--danger) 0%, transparent); }
}
/* R57: das alte "gefaltete Eck" (Dreieck) wirkte zu aufdringlich -- ersetzt durch einen kleinen
   Zahlen-Kreis oben rechts an der GANZEN Karte (.agenda-item), im selben Stil wie der
   Angeheftet-Zaehler-Badge in der ersten Menueleiste (.icon-btn-badge, siehe #pinned-count-badge).
   R58 Nutzer-Feedback: das Badge sass bisher INNEN in der Karte und ueberlappte dort andere
   Elemente (z.B. ein verknuepftes Profilbild oben rechts) -- .agenda-item/.chatroom-row haben
   beide overflow:hidden (fuer die abgerundeten Ecken von Streifen/Thumbnails), ein Kind davon
   kann also nicht wirklich ueber den Kartenrand hinausragen. Das Badge ist deshalb jetzt kein
   Kind der Karte mehr, sondern haengt als Geschwister-Element in einem eigenen, nicht
   geclippten .agenda-item-wrap daneben (siehe renderAgendaItems()/renderChatRoomList()) -- so
   kann es tatsaechlich ein kleines Stueck ueber die Kartenecke hinausragen. Rot statt der
   Akzentfarbe, damit es als "ungelesen"-Hinweis staerker auffaellt (gleiche Farbe wie
   .unread-badge/.changelog-badge an anderer Stelle der App). */
.agenda-item-wrap { position: relative; }
.agenda-unread-badge {
  position: absolute; top: -5px; right: -5px; z-index: 4;
  min-width: 17px; height: 17px; padding: 0 var(--space-2); box-sizing: border-box;
  border-radius: var(--radius-full); background: var(--danger); color: var(--text-on-color);
  font-size: var(--fs-2xs); font-weight: var(--fw-bold); line-height: 17px; text-align: center;
  box-shadow: var(--shadow-sm);
  pointer-events: none;
}
/* Horizontales Padding (14px) passend zu .chat-messages/.chat-form darunter (siehe dort), damit
   die Ueberschrift trotz fehlendem eigenen Karten-Hintergrunds buendig mit dem Chat-Inhalt
   abschliesst statt zu verrutschen. */
.event-preview-chat-header {
  display: flex; align-items: center; gap: var(--space-4); padding: var(--space-4) var(--space-7) var(--space-4);
}
/* Nutzer-Feedback (Kehrtwende): "entferne die überschrifts balken in der event übersicht bei chat
   und notiz wieder ... nimm auch die icons raus und mache die überschrift im stil wie bei den
   widget überschriften" -- kein eigener Karten-Hintergrund mehr (siehe .widget h3 fuer denselben
   kleinen, gedimmten Grossbuchstaben-Stil). */
.event-preview-chat-header .event-preview-section-label {
  margin: 0; font-size: var(--fs-2xs); font-weight: var(--fw-semibold); color: var(--text-dim);
  letter-spacing: 0.04em; text-transform: uppercase;
}
/* Nutzer-Feedback: "fühlt sich eher an wie ein vollwertiger Chat und weniger wie eine
   Kommentarfunktion ... keinen extra background mehr bei chat nachrichten, nur etwas freien
   platz zwischen user nachrichten" -- Discord-Stil statt der bisherigen Bubble-Karten pro
   Nachrichtengruppe: kein .chat-message-Hintergrund mehr (siehe dort), stattdessen groesserer
   Abstand HIER zwischen den Gruppen (verschiedene Absender/Zeit-Abschnitte), waehrend
   .chat-message-lines (mehrere Nachrichten derselben Person kurz hintereinander) eng
   zusammenbleibt (siehe dort). */
.chat-messages { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-7); max-height: 320px; min-height: 160px; overflow-y: auto; padding: var(--space-5) var(--space-7); overscroll-behavior: contain; }
.chat-message { position: relative; display: flex; gap: var(--space-6); align-items: flex-start; width: 100%; font-size: var(--fs-md); }
.chat-message.new-message { animation: fadeInUp var(--duration-base) ease both; }
/* Nutzer-Feedback: "discord like etwas größere profilbilder neben dem chat" -- vorher 22px. */
.chat-avatar { width: 38px; height: 38px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.chat-avatar-placeholder { font-size: var(--fs-display); line-height: 1; color: var(--text-dim); }
/* Phase 2 Nutzer-Feedback: gruene Online-Punkte auf Avataren (Chat/Kommentare/Mitgliederlisten,
   siehe renderAvatarHtml()-opts.onlineDot in app.js) -- echter Wrapper statt ::after direkt auf
   dem Avatar-<img>, da generierter Inhalt auf "replaced elements" wie <img> laut Spec nicht
   rendert. inline-flex sizt sich automatisch auf die Avatar-Groesse, flex-shrink:0 verhaelt sich
   dadurch identisch zum vorher direkten Avatar-Element in umgebenden Flex-Zeilen. */
.avatar-online-wrap { position: relative; display: inline-flex; flex-shrink: 0; vertical-align: middle; }
.avatar-online-dot {
  position: absolute; right: -1px; bottom: -1px; width: 9px; height: 9px; border-radius: 50%;
  background: var(--success); border: 2px solid var(--panel); pointer-events: none;
}
.chatroom-watch-bot-avatar { width: auto; height: auto; font-size: var(--fs-display); line-height: 1; color: var(--youtube); }
[data-avatar-name] { cursor: pointer; }
/* Schmalere, horizontale Karte: groesseres Bild links, Name+Bio als Textblock rechts daneben
   (vorher: schmale zentrierte Spalte mit Bild oben, Text darunter). Zusammengesetzter Selektor
   noetig, da .icon-dropdown-popover (width:280px/padding:10px) sonst gleich spezifisch ist und
   je nach Quellreihenfolge gewinnen wuerde. */
/* R50 Nachtrag: flex-direction von row auf column -- die Karte kann jetzt optional einen
   Chatroom-Abschnitt unter der Bild+Text-Kopfzeile zeigen (siehe .avatar-hover-card-chatroom),
   die Kopfzeile selbst behaelt ihr horizontales Layout ueber einen eigenen inneren Flex-Container. */
.avatar-hover-card.icon-dropdown-popover {
  width: 260px; text-align: left; padding: 0; overflow: hidden;
}
/* R56: Banner jetzt als eigenes <img> (object-position/scale() per Drag&Zoom einstellbar, siehe
   setupCoverDrag() in app.js) statt eines statischen CSS background-image -- der abgedunkelte
   Verlauf sitzt als eigener Layer darueber, der eigentliche Inhalt (Bild/Name/Bio) nochmal darueber
   in .avatar-hover-card-content. Ohne Banner bleiben beide Layer einfach ungenutzt/nicht im DOM. */
.avatar-hover-card-banner-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
.avatar-hover-card-banner-overlay {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(color-mix(in srgb, var(--bg) 55%, transparent), color-mix(in srgb, var(--bg) 85%, transparent));
}
.avatar-hover-card-content { position: relative; z-index: 2; padding: var(--space-6); display: flex; flex-direction: column; }
.avatar-hover-card-img { width: 68px; height: 68px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.avatar-hover-card-placeholder { font-size: 68px; line-height: 1; color: var(--text-dim); flex-shrink: 0; }
.avatar-hover-card-text { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.avatar-hover-card-name { font-weight: var(--fw-semibold); font-size: var(--fs-lg); }
.avatar-hover-card-bio { font-size: var(--fs-sm); color: var(--text-dim); }
/* Bild/Name/Bio bleiben durch den Verlauf unabhaengig von der Banner-Helligkeit lesbar, deshalb
   hier feste helle statt der themenabhaengigen gedimmten Textfarbe. */
.avatar-hover-card.avatar-hover-card-has-banner .avatar-hover-card-name,
.avatar-hover-card.avatar-hover-card-has-banner .avatar-hover-card-bio,
.avatar-hover-card.avatar-hover-card-has-banner .avatar-hover-card-chatroom-heading {
  color: var(--text-on-color);
}
.avatar-hover-card.avatar-hover-card-has-banner .avatar-hover-card-img {
  border: 2px solid rgba(255,255,255,0.85);
}
/* R56: Statische Vorschau-Variante in den Profil-Einstellungen -- gleiche Optik wie die
   schwebende Karte, aber im normalen Dokumentfluss statt position:fixed (die .icon-dropdown-
   popover-Basisklasse setzt sonst position:fixed, "relative" haelt den Positionierungskontext
   fuer die absolut positionierten Banner-Layer trotzdem aufrecht). */
.avatar-hover-card-preview { position: relative !important; box-shadow: none; margin-bottom: var(--space-8); }
/* Kompaktes Klick-Popup fuer Geburtstage (statt der vollen Event-Vorschau mit Chat/Teilnahme)
   -- etwas breiter als die Avatar-Hover-Karte (200px), da hier zusaetzlich Datum/Alter und ggf.
   eine Bio stehen. */
.birthday-popup.icon-dropdown-popover { width: 260px; }
/* Eigene Breite fuer das verknuepfte Spiel/Film-Popup (R35) statt die geteilte
   .birthday-popup-Breite zu vergroessern, was auch das Geburtstags-Popup betreffen wuerde. */
.linked-media-popup.icon-dropdown-popover { width: 330px; }
.birthday-popup-name { font-weight: var(--fw-semibold); font-size: var(--fs-lg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.birthday-popup-dateline { margin-top: var(--space-2); font-size: var(--fs-sm); color: var(--text-dim); }
/* Nutzer-Feedback: "'spiel' neben dem namen stehen" -- Typ-Label (Spiel/Film/Serie) steht im
   Verknuepft-Popup (showLinkedMediaPopup()) in einer Zeile neben statt unter dem Namen, dafuer statt
   .birthday-popup-dateline (das Geburtstags-Popup behaelt dessen Stapel-Optik unveraendert). */
.linked-media-popup-header { display: flex; align-items: center; gap: var(--space-4); min-width: 0; }
.linked-media-popup-header .birthday-popup-name { flex: 1; min-width: 0; }
.linked-media-popup-type-badge {
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold); padding: var(--space-1) var(--space-4); border-radius: var(--radius-full); flex-shrink: 0;
  background: color-mix(in srgb, var(--badge-color) 20%, transparent); color: var(--badge-color);
}
/* Wiederverwendet dieselben Klassen wie das Datum auf der Agenda-Kachel (agenda-header-date-soon
   /-today), damit die "bald/heute"-Faerbung im Popup identisch zur Kachel selbst aussieht --
   die Farbe/font-weight kommen direkt aus diesen geteilten Klassen (style.css agenda-header-date-*). */
.birthday-popup-avatar { width: 46px; height: 46px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.birthday-popup-icon { display: flex; align-items: center; justify-content: center; font-size: var(--fs-xl); }
.birthday-popup-profile-box {
  display: flex; align-items: center; gap: var(--space-5); margin-top: var(--space-6); padding: var(--space-5);
  border-radius: var(--radius-md); background: var(--inhalt-kachel);
}
.birthday-popup-profile-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.birthday-popup-profile-name { font-weight: var(--fw-semibold); font-size: var(--fs-md); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.birthday-popup-bio { font-size: var(--fs-sm); line-height: 1.4; color: var(--text-dim); }
.birthday-popup-bio-empty { font-style: italic; opacity: 0.7; }
/* Termin-Popup (abgespeckte Kategorie, siehe showTerminPopup) -- an die Birthday-Popup-Optik
   angelehnt, aber mit Bearbeiten/Loeschen-Aktionsleiste statt reiner Kurzinfo. */
.termin-popup.icon-dropdown-popover { width: 280px; }
.termin-popup-name { font-weight: var(--fw-semibold); font-size: var(--fs-lg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.termin-popup-dateline { margin-top: var(--space-2); font-size: var(--fs-sm); color: var(--text-dim); }
.termin-popup-location { margin-top: var(--space-4); display: flex; align-items: center; gap: var(--space-3); font-size: var(--fs-md); color: var(--text-dim); }
.termin-popup-description { margin-top: var(--space-4); font-size: var(--fs-md); line-height: 1.4; }
.termin-popup-actions { margin-top: var(--space-6); display: flex; gap: var(--space-4); }
.termin-popup-actions .primary-btn, .termin-popup-actions .danger-btn { flex: 1; padding: var(--space-3); font-size: var(--fs-sm); }
.chat-message-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }
/* Zeitstempel steht jetzt neben dem Namen in einer eigenen Kopfzeile statt hinter dem
   Nachrichtentext -- vorher lief die Zeit direkt in den Fliesstext, wirkte wie ein Teil davon. */
.chat-message-head { display: flex; align-items: center; gap: var(--space-2); }
.chat-message .chat-author { font-weight: var(--fw-semibold); color: var(--accent); }
.chat-message .chat-time { color: var(--text-dim); font-size: var(--fs-2xs); }
/* Mehrere kurz hintereinander geschriebene Nachrichten derselben Person (siehe
   CHAT_GROUP_WINDOW_MS in app.js) landen als mehrere .chat-message-line-Eintraege in EINER
   gemeinsamen .chat-message-Box -- Avatar/Name/Zeit werden dabei nur einmal oben angezeigt
   statt pro Nachricht wiederholt. */
/* Enger Abstand hier (anders als zwischen verschiedenen Absendern, siehe .chat-messages oben) --
   das sind mehrere kurz hintereinander gesendete Nachrichten DERSELBEN Person. */
.chat-message-lines { display: flex; flex-direction: column; gap: var(--space-2); }
.chat-message-line { position: relative; border-radius: var(--radius-sm); transition: background var(--duration-fast) ease; min-width: 0; }
/* Nutzer-Feedback: "wenn im chat ein link ist ist er ohne umbruch und verlängert den chat
   unendlich" -- ein langer, leerzeichenloser String (URL) bricht per Default-Textverhalten nicht
   um und sprengt dadurch die Chat-Spalte horizontal. overflow-wrap:anywhere bricht NUR wenn nötig
   (normale Woerter/Leerzeichen bleiben unangetastet), min-width:0 auf der Zeile selbst ist noetig,
   damit der Flex-Container (.chat-message-body/-lines) ueberhaupt schrumpfen darf statt sich an der
   intrinsischen Breite des unbrechbaren Inhalts festzuhalten (klassische Flexbox-Falle). */
.chat-message-text { overflow-wrap: anywhere; }
.chat-message-line.new-message { animation: fadeInUp var(--duration-base) ease both; }
/* Nutzer-Feedback: "bei chat nachrichten hover bitte die nachricht mit einem background
   hervorheben leicht" -- dezenter Hover-Hintergrund pro Zeile, macht deutlicher welche Nachricht
   gerade die "..."-Toolbar/Kontextmenue anspricht. */
.chat-message-line:hover { background: var(--tint-subtle); }
/* Nutzer-Feedback: "bei polls die nachricht nicht bei hover hinterlegen das sieht komisch aus,
   hinterlege eher den poll selbst" -- der pauschale Zeilen-Hover-Hintergrund passt optisch nicht
   zur eigenen Box des Umfrage-Widgets (eigener Rahmen/Radius, ragt ueber die Zeilenkanten hinaus).
   Bei Umfrage-Zeilen bleibt die Zeile selbst transparent, stattdessen hebt sich das Widget direkt
   per Rahmen-Glow hervor. */
.chat-message-line:has(.poll-widget):hover { background: transparent; }
.chat-message-line:hover .poll-widget { box-shadow: 0 0 0 1px rgba(255,255,255,0.12); }
/* Nutzer-Feedback: "reaktionsbutton kommt weg und wird durch nachrichtenhover wie in screenshot 3
   ersetzt" -- ersetzt sowohl den frueheren einzelnen Reaktions-Button als auch das Loeschen-X:
   eine kleine schwebende Toolbar oben rechts an der Nachrichtenzeile, nur bei Hover sichtbar
   (Reagieren/Bearbeiten[nur eigene]/"..." fuer das Kontextmenue, siehe renderChatLineActionsHtml()
   in app.js). Loeschen liegt jetzt im "..."-Kontextmenue statt eines eigenen Buttons. */
.chat-line-actions {
  position: absolute; top: -10px; right: 4px; display: flex; gap: var(--space-1);
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--space-1);
  box-shadow: var(--shadow-sm);
  opacity: 0; pointer-events: none; transition: opacity var(--duration-fast) ease;
}
.chat-message-line:hover .chat-line-actions { opacity: 1; pointer-events: auto; }
.chat-line-action-btn {
  width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-xs); background: transparent; border: none; color: var(--text-dim);
  cursor: pointer; font-size: var(--fs-md); padding: 0; transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.chat-line-action-btn:hover { background: var(--panel-hover); color: var(--text); }
/* Inline-Bearbeiten ersetzt .chat-message-text direkt in der Zeile (siehe startInlineMessageEdit()
   in app.js) -- gleiche Optik wie das Nachrichten-Eingabefeld unten, damit der Uebergang nicht wie
   ein Fremdkoerper wirkt. */
.chat-line-edit-input {
  display: block; width: 100%; resize: vertical; min-height: 34px;
  background: var(--panel-2); border: 1px solid var(--accent); color: var(--text);
  border-radius: var(--radius-sm); padding: var(--space-3) var(--space-4); font-size: var(--fs-md); font-family: inherit; line-height: 1.4;
}
.chat-edited-label { font-size: var(--fs-2xs); color: var(--text-dim); font-weight: normal; }
/* Phase 2 Nutzer-Feedback: "Nachrichten anpinnen" -- kleines Pin-Icon neben dem Text (gleiche
   Optik/Position wie das "(bearbeitet)"-Label direkt daneben, siehe applyPinnedLabelToLine() in
   app.js), dezente linke Akzentlinie an der ganzen Zeile macht angepinnte Nachrichten beim
   Scrollen durch den Verlauf leichter erkennbar. */
.chat-pinned-label { font-size: var(--fs-2xs); color: var(--highlight); margin-left: var(--space-2); }
/* Nutzer-Feedback: "umrande angeheftete nachrichten im chat wie angeheftete events in der agenda,
   die ganze nachricht auch bei gifs/mehreren hintereinander" -- zurueck zu einem echten Rahmen
   (gleiche Optik wie .agenda-item.pinned), diesmal aber direkt an der GANZEN Zeile statt nur am
   .chat-message-text-Span dahinter: die vorherige Variante faerbte nur den Text-Container ein,
   Bild-/GIF-Anhaenge (eigenes .chat-message-image daneben) blieben unmarkiert, und bei einer
   leeren .chat-message-text (reine Bildnachricht) war ueberhaupt nichts sichtbar. Ein Rahmen an
   .chat-message-line-pinned selbst deckt Text, Bild UND Umfrage-Widget gleichermassen ab, und jede
   einzelne angepinnte Zeile bekommt ihn unabhaengig -- auch wenn mehrere direkt hintereinander von
   derselben Person kommen (jede Zeile ist ein eigenes .chat-message-line, siehe applyPinnedLabelToLine()
   in app.js, die die Klasse jetzt ebenfalls unabhaengig von einer vorhandenen .chat-message-text
   setzt statt vorher bei Bild-/Umfrage-Nachrichten ganz auszubleiben).
   Folge-Feedback: "wenn mehrere nachrichten hintereinander mache nur eine umrandung um alle" -- bei
   direkt benachbarten angepinnten Zeilen (echte DOM-Geschwister innerhalb derselben .chat-message-lines-
   Gruppe) wird die gemeinsame Trennkante entfernt, sodass der Rahmen wie EINE Box um die ganze Serie
   wirkt statt mehrerer einzelner. Rein CSS via Sibling-Selektoren, kein JS-Grouping noetig. */
.chat-message-line-pinned { border: 1px dashed var(--highlight); border-radius: var(--radius-sm); padding: var(--space-2) var(--space-3); margin: -3px -6px; }
.chat-message-line-pinned + .chat-message-line-pinned { border-top: none; border-top-left-radius: 0; border-top-right-radius: 0; }
.chat-message-line-pinned:has(+ .chat-message-line-pinned) { border-bottom: none; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
/* Nutzer-Feedback: "beim anpinnen auch den namen mit umranden, der ist im moment abgeschnitten" --
   ist die angepinnte Zeile die ERSTE Nachricht einer Gruppe (direkt unter Name/Zeit), wird der Rahmen
   ueber den .chat-message-head hochgezogen und die Trennkante zwischen Kopf und Zeile entfernt, damit
   Name+Nachricht wie eine zusammenhaengende Box wirken statt der Rahmen mitten im Namen abzuschneiden. */
.chat-message-head:has(+ .chat-message-lines > .chat-message-line-pinned:first-child) {
  border: 1px dashed var(--highlight); border-bottom: none; border-radius: var(--radius-sm) var(--radius-sm) 0 0; padding: var(--space-2) var(--space-3); margin: -3px -6px;
}
.chat-message-body:has(> .chat-message-head + .chat-message-lines > .chat-message-line-pinned:first-child) > .chat-message-lines > .chat-message-line-pinned:first-child {
  border-top: none; border-top-left-radius: 0; border-top-right-radius: 0;
}
/* Kurzes Aufblitzen, wenn man von der Angepinnt-Liste zur Original-Nachricht springt (siehe
   showPinnedMessagesPanel() in app.js) -- macht sichtbar, WELCHE der ggf. mehreren Zeilen in der
   Box gemeint ist, ohne die Liste dauerhaft zu veraendern. */
.chat-message-line-highlight { animation: chat-line-flash 1.6s ease; }
@keyframes chat-line-flash {
  0%, 100% { background: transparent; }
  15% { background: color-mix(in srgb, var(--highlight) 25%, transparent); }
}
/* Nutzer-Feedback: "suche im chat zusaetzlich zum anpinnen und glocke button" -- inline
   Ctrl+F-artige Suchleiste, die zwischen Chat-Header und Nachrichtenliste einblendet statt eines
   Popups, damit man beim Tippen sofort die eingefaerbten Treffer im Verlauf sieht. */
.chat-search-bar { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-6); border-bottom: 1px solid var(--border); background: var(--panel-2); }
.chat-search-bar-icon { color: var(--text-dim); font-size: var(--fs-body); }
.chat-search-input { flex: 1; min-width: 0; background: transparent; border: none; color: var(--text); font-size: var(--fs-md); outline: none; }
.chat-search-count { font-size: var(--fs-xs); color: var(--text-dim); white-space: nowrap; }
.chat-search-highlight { background: var(--accent-strong); border-radius: var(--radius-xs); }
.chat-search-highlight-current { background: var(--highlight); color: #1a1a1a; }
/* Nutzer-Feedback: "Umfragen" im Chat-"+"-Menü -- eine Umfrage ist eine ganz normale Chat-Zeile
   (siehe renderPollWidgetHtml() in app.js), nur mit diesem Widget statt Fliesstext. Balken pro
   Option fuellt sich per Breite anteilig am Stimmenanteil, gleiche Klickflaeche wie ein Button. */
.poll-widget { max-width: 340px; background: var(--inhalt-kachel); border: 1px solid var(--border); border-radius: 10px; padding: var(--space-5) var(--space-6); }
.poll-question { font-weight: var(--fw-semibold); display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-4); }
.poll-options { display: flex; flex-direction: column; gap: var(--space-3); }
.poll-option {
  position: relative; display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  width: 100%; text-align: left; padding: var(--space-4) var(--space-5); border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--panel); color: var(--text); cursor: pointer; font-size: var(--fs-md); overflow: hidden;
}
.poll-option-fill { position: absolute; inset: 0; width: 0; background: var(--accent-tint); transition: width var(--duration-slow) ease; z-index: 0; }
/* Nutzer-Feedback: "kannst du bitte noch den background der ausgewählten polls auf die gewählte
   farbe anpassen?" -- der Fuellbalken der selbst gewaehlten Option war bisher fest indigo
   (rgba(99,102,241,...)) statt wie Rahmen/Haekchen/Label (siehe .poll-option-voted-Regeln unten)
   der (pro Organisation anpassbaren) --accent-Farbe zu folgen. Nur die eigene Stimme faerbt sich
   um, die uebrigen Optionen bleiben beim neutralen Indigo-Ton (Balken zeigen dort ja nur den
   allgemeinen Stimmenanteil, nicht "meine Wahl"). */
.poll-option-voted .poll-option-fill { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.poll-option-label { position: relative; z-index: 1; }
/* Nutzer-Feedback: "möglich machen das man bei x stimmen drauf drücken kann und dann sehen kann
   wer abgestimmt hat" -- Prozent-/Stimmenanzeige ist jetzt ein eigener kleiner Button (siehe
   showPollVotersPopover() in app.js), .poll-option selbst blieb dafuer ein <div> statt <button>
   (verschachtelte Buttons sind ungueltiges HTML), das Abstimmen haengt per JS-Klick-Handler direkt
   am div, der Klicks auf diesen Button gezielt ignoriert. */
.poll-option-pct {
  position: relative; z-index: 1; color: var(--text-dim); font-size: var(--fs-sm); flex-shrink: 0;
  background: none; border: none; font-family: inherit; padding: var(--space-1) var(--space-3); border-radius: var(--radius-xs); cursor: pointer;
}
.poll-option-pct:hover:not(:disabled) { background: var(--tint-hover); color: var(--text); }
.poll-option-pct:disabled { cursor: default; }
.poll-option-voted { border-color: var(--accent); }
.poll-option-voted .poll-option-label { color: var(--accent); font-weight: var(--fw-semibold); }
.poll-meta { margin-top: var(--space-4); font-size: var(--fs-xs); color: var(--text-dim); }
/* Nutzer-Feedback: "bei den polls als einstellung wie lange die aktiv sind und dann am ende
   sollte man ein ergebnis sehen" -- nach Ablauf werden die Optionen zu reinen <div>s (siehe
   renderPollWidgetHtml()), diese Regeln nehmen ihnen den Klick-Cursor/Hover, den "Beendet"-Badge
   sitzt direkt neben der Frage. */
.poll-widget-closed { opacity: 0.82; }
.poll-option-closed { cursor: default; }
.poll-closed-badge {
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim);
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-xs); padding: 1px var(--space-3);
  margin-left: auto;
}
/* Nutzer-Feedback: "bitte alle polls in einem chat in einem button oben wie die pinned nachrichten
   anzeigen und dort behalten" -- eigenes Popover (showAllPollsPanel() in app.js), breiter als das
   Angepinnt-Popover, da hier ganze Umfrage-Widgets (max-width 340px) statt schmaler Textzeilen
   drinstehen. */
.all-polls-popover { width: 380px; }
.all-polls-panel-list { display: flex; flex-direction: column; gap: var(--space-5); padding: var(--space-5); }
.all-polls-panel-item .poll-widget { max-width: none; }
.poll-create-options-label { font-size: var(--fs-sm); color: var(--text-dim); margin: var(--space-4) 0 var(--space-2); }
.poll-create-options { display: flex; flex-direction: column; gap: var(--space-3); }
/* Nutzer-Feedback: "den mehrfachauswahl button in den polls schöner machen vllt oben drüber über
   die fragen" -- gleiches Pillen-Toggle-Muster wie der Passwort-Schalter im Chatroom-Erstellen
   (.chatroom-password-toggle-btn) statt nackter Checkbox+Label-Zeile, jetzt ueber statt unter den
   Optionen. */
.poll-create-multiple-toggle-btn { display: inline-flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-5); }
/* Popover mit den Namen aller, die fuer eine Option gestimmt haben (showPollVotersPopover()) --
   gleiches Grundmuster wie .pinned-panel-popover/.online-users-panel-list. */
.poll-voters-popover { width: 220px; max-height: 280px; overflow-y: auto; }
.poll-voters-panel-list { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-4); }
.poll-voters-panel-row { display: flex; align-items: center; gap: var(--space-4); padding: var(--space-2) var(--space-3); }
.poll-voters-panel-avatar { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.poll-voters-panel-name { font-size: var(--fs-md); color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Nutzer-Feedback: "bei den abstimmungen wo man sieht wer abgestimmt hat auch noch sieht wer für
   was gestimmt hat" -- das Umfrage-Popover gruppiert jetzt nach Option (showPollVotersPopover()).
   Eigene Klasse statt .poll-voters-popover zu verbreitern: dieselbe Klasse tragen auch die
   Wunschlisten- und Besitzer-Popover, die schmal bleiben sollen. */
.poll-breakdown-popover { width: 260px; max-height: 340px; }
.poll-voters-group { border-radius: var(--radius-sm); padding: var(--space-1) 0 var(--space-2); }
.poll-voters-group + .poll-voters-group { border-top: 1px solid var(--border); border-top-left-radius: 0; border-top-right-radius: 0; }
.poll-voters-group-active { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.poll-voters-group-head { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-3) var(--space-2); font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--text-dim); }
.poll-voters-group-active .poll-voters-group-head { color: var(--text); }
.poll-voters-group-head .mdi { color: var(--accent); }
.poll-voters-group-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.poll-voters-group-count { flex-shrink: 0; font-weight: var(--fw-regular); }
.poll-voters-panel-empty { padding: 0 var(--space-3) var(--space-1); font-size: var(--fs-sm); color: var(--text-dim); }
.poll-meta-votes-btn {
  background: none; border: none; font: inherit; color: inherit; cursor: pointer;
  padding: 1px var(--space-2); margin: 0 -4px; border-radius: var(--radius-xs);
}
.poll-meta-votes-btn:hover { background: var(--tint-hover); color: var(--text); }
.poll-create-option-row { display: flex; gap: var(--space-3); align-items: center; }
.poll-create-option-input {
  flex: 1; background: var(--panel-2); border: 1px solid var(--border); color: var(--text);
  border-radius: var(--radius-sm); padding: var(--space-3) var(--space-4); font-size: var(--fs-md); font-family: inherit;
}
.poll-create-add-option-btn { margin-top: var(--space-3); }
/* Phase 2 Nutzer-Feedback: "@-mentions in chat" -- Erwaehnungen bekommen dieselbe Akzentfarbe/
   Optik wie andere interaktive Chips in der App (Tags, Reaktions-Pillen), eine Erwaehnung des
   AKTUELL angemeldeten Nutzers selbst faellt zusaetzlich staerker auf (Hintergrund statt nur Text),
   damit man sie beim schnellen Ueberfliegen des Verlaufs nicht uebersieht. */
.chat-mention { color: var(--accent); font-weight: var(--fw-semibold); }
.chat-mention-self { background: var(--accent-tint); border-radius: var(--radius-xs); padding: 0 var(--space-2); }
/* Nutzer-Feedback: "im chat brauche ich ein @here" -- eigene Farbe (orange statt Accent-Blau),
   damit ein Broadcast-@here optisch von einer einzelnen Namens-Erwaehnung unterscheidbar bleibt. */
.chat-mention-here { color: var(--warning); background: color-mix(in srgb, var(--warning) 16%, transparent); border-radius: var(--radius-xs); padding: 0 var(--space-2); }
/* Autocomplete-Popover beim Tippen von "@" (setupMentionAutocomplete() in app.js) -- teilt sich
   die .create-menu-Basisoptik mit allen anderen Klick-Popovern der App. */
.mention-autocomplete-popover { min-width: 180px; max-height: 220px; overflow-y: auto; }
.mention-autocomplete-item.active { background: var(--aktiv-kachel); color: var(--accent); }
.comment-item { display: flex; gap: var(--space-4); }
.comment-item-body { flex: 1; min-width: 0; }
/* Discord-artige Link-Vorschau unter Chat-/Kommentar-Nachrichten und Beschreibungen --
   siehe attachLinkEmbeds() in app.js. Kompakte Zeile statt grosser Karte: Text links, ein
   kleines Vorschaubild (falls vorhanden) rechts oben. Hintergrund bewusst als helles Overlay
   (rgba) statt eines Theme-Panel-Werts, damit sie sich in JEDEM Kontext (Chat-Bubble, Notizen-
   Box, ...) sichtbar vom Hintergrund abhebt statt in gleichfarbigen Umgebungen zu verschwinden. */
.link-embed-card {
  display: flex; align-items: flex-start; gap: var(--space-4); position: relative; margin-top: var(--space-3);
  max-width: 320px; max-height: 84px; overflow: hidden;
  border-left: 3px solid var(--accent); border-radius: var(--radius-xs); background: var(--tint-subtle);
  padding: var(--space-3) var(--space-16) var(--space-3) var(--space-4); text-decoration: none; color: inherit;
}
.link-embed-card:hover { background: var(--tint-hover); }
/* Gleicher subtiler Stil wie .chat-message-delete-btn (kein Hintergrund, nur Icon) --
   vorher dunkles Kreis-Badge, das zudem fast am Vorschaubild klebte (1px Abstand bei
   22px card-padding-right). Jetzt mehr Luft zum Bild (card-padding-right 30px, Button
   bei right:6px = 6px Abstand statt 1px). */
.link-embed-close {
  position: absolute; top: 3px; right: 6px; width: 18px; height: 18px; display: flex;
  align-items: center; justify-content: center; border-radius: var(--radius-xs); background: transparent;
  border: none; color: var(--text-dim); cursor: pointer; font-size: var(--fs-xs); padding: 0; z-index: 1;
}
.link-embed-close:hover { background: transparent; color: var(--danger); }
.link-embed-body { flex: 1; min-width: 0; }
.link-embed-site { font-size: var(--fs-2xs); color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: var(--space-1); }
.link-embed-title { font-weight: var(--fw-semibold); font-size: var(--fs-sm); color: var(--link-accent, var(--accent)); margin-bottom: var(--space-1); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; }
.link-embed-desc { font-size: var(--fs-xs); color: var(--text-dim); line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.link-embed-image { width: 48px; height: 48px; object-fit: cover; border-radius: var(--radius-xs); flex-shrink: 0; }
/* Discord-artige Trennzeile zwischen Tagen (siehe renderChatDayDividerHtml() in app.js) --
   volle Breite, Linie links/rechts vom Label. */
.chat-day-divider {
  display: flex; align-items: center; gap: var(--space-5); width: 100%;
  color: var(--text-dim); font-size: var(--fs-xs); font-weight: var(--fw-semibold); text-transform: uppercase;
  letter-spacing: 0.03em; margin: var(--space-2) 0;
}
.chat-day-divider::before, .chat-day-divider::after { content: ""; flex: 1; height: 1px; background: var(--border); }
/* Nutzer-Feedback: "die schreibe leiste unten sollte wie in discord etwas höher sein" -- mehr
   vertikales Padding als vorher (6px -> 12px). */
.chat-form { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-6) var(--space-7); border-top: 1px solid var(--border); }
.chat-form-row { display: flex; gap: var(--space-4); align-items: center; }
/* Discord-artiges Plus-Symbol links (loest den Datei-Upload aus, siehe setupImageAttach() in
   app.js) -- ersetzt die vorherige Büroklammer, ersetzt auch die alte .chat-icons-col-Spalte
   (Emoji/GIF steht jetzt als eigenes Icon rechts neben .chat-input-wrap statt links davon). */
.chat-plus-btn {
  flex-shrink: 0; width: 30px; height: 30px; border-radius: 50%;
  background: var(--knopf); color: var(--text-dim); font-size: var(--fs-xl);
}
.chat-plus-btn:hover { background: var(--panel); color: var(--text); }
.chat-form input[type="text"] {
  flex: 1; background: var(--panel-2); border: 1px solid var(--border); color: var(--text);
  padding: var(--space-5) var(--space-6); border-radius: var(--radius-md); font-size: var(--fs-md); font-family: inherit;
}
.chat-input-wrap { position: relative; flex: 1; min-width: 0; }
.chat-input-wrap input[type="text"] { width: 100%; padding-right: 34px; }
/* Nutzer-Feedback: "der nachricht absenden button fliegt raus ... das nachricht schreiben feld
   auf die gesamte breite des chats und der emoji button dann in das feld rein da wo quasi der
   senden button war" -- Senden nur noch per Enter (Formular-Submit), Emoji/GIF-Button uebernimmt
   die Position/Optik des entfernten .chat-send-btn. */
.chat-input-emoji-btn {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  background: none; border: none; color: var(--text-dim); cursor: pointer;
  width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-body); border-radius: var(--radius-sm);
}
.chat-input-emoji-btn:hover { background: var(--tint-hover); color: var(--text); }
/* Bugfix (Nutzer-Feedback: "im chat kann ich jetzt zwar auf den emoji button klicken aber dann
   rutscht er runter und macht nix") -- die geteilte .icon-btn:active-Regel (transform:scale(0.92))
   ist spezifischer als das einfache .chat-input-emoji-btn{transform:translateY(-50%)} oben (zwei
   Klassen+Pseudoklasse schlaegt eine Klasse) und ueberschreibt beim Klicken dessen vertikale
   Zentrierung komplett -- der Button "springt" dadurch beim Mousedown aus der Mitte nach unten,
   sodass der Mouseup (und damit der Klick) oft daneben statt auf dem Button landet. Eigene
   :active-Regel mit derselben Spezifitaet (Klasse+Pseudoklasse) haelt die Zentrierung waehrend
   des Klicks aufrecht. */
.chat-input-emoji-btn:active { transform: translateY(-50%) scale(0.92); }
/* Nutzer-Feedback (2026-09-11): "bitte füge im mobile chat immer einen absende button hinzu - wenn man
   nämlich eine tastatur hat ohne enter kann man keine nachrichten senden" -- der Senden-Knopf war auf
   "der nachricht absenden button fliegt raus" hin entfallen (siehe oben); auf Handy und Touch-Geraeten
   steht er wieder rechts neben dem Feld, am Desktop bleibt es bei Enter. type=submit loest denselben
   Formular-Submit aus wie Enter. */
.chat-send-btn { display: none; }
/* Kurzlabel des Verknuepfen-Knopfs nur auf dem Handy (siehe @media(max-width:720px) beim Event-Formular). */
.event-link-btn-kurz, .event-title-image-btn-kurz, .event-title-image-entf-kurz { display: none; }
@media (max-width: 720px), (pointer: coarse) {
  .chat-send-btn {
    display: flex; flex-shrink: 0; align-items: center; justify-content: center;
    width: 36px; height: 36px; border-radius: 50%; border: none;
    background: var(--accent); color: var(--text-on-color); font-size: var(--fs-lg);
  }
}
.chat-pending-image { display: flex; align-items: center; gap: var(--space-4); background: var(--inhalt-kachel); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); }
.chat-pending-image img { width: 36px; height: 36px; object-fit: cover; border-radius: var(--radius-sm); }
.chat-pending-image .chat-pending-remove { margin-left: auto; width: 22px; height: 22px; font-size: var(--fs-md); }
.chat-message img.chat-message-image { max-width: 220px; max-height: 220px; border-radius: var(--radius-md); display: block; margin-top: var(--space-2); cursor: pointer; }
.comment-item img.comment-image { max-width: 220px; max-height: 220px; border-radius: var(--radius-md); display: block; margin-top: var(--space-3); cursor: pointer; }

/* Frueher als absolut positioniertes Overlay oben rechts an der jeweiligen .chat-message-line
   verankert -- das ragte bei laengerem Nachrichtentext ueber den Text (Overlay kennt den
   Textfluss nicht, Text lief einfach darunter weiter). Jetzt stattdessen ein normaler Flex-Kind
   im .chat-message-head (Name/Zeit-Kopfzeile, siehe setChatGroupReaction() in app.js) --
   dadurch bleibt sie immer an derselben Stelle oben rechts, unabhaengig davon, wie lang die
   Nachricht wird, und es gibt automatisch nur noch EINE Reaktionsleiste pro Chatbox statt einer
   pro einzelner Zeile (auch bei mehreren kombinierten Nachrichten). margin-left:auto schiebt sie
   im Kopf ganz nach rechts, waehrend Name/Zeit links bleiben. */
/* Nutzer-Feedback: "reaktionen ... unter die nachrichten" -- steht jetzt am Ende von
   .chat-message-body (siehe setChatGroupReaction() in app.js) statt oben im Kopf neben Name/Zeit,
   daher links ausgerichtet statt nach rechts geschoben, mit etwas Abstand zum Text darueber. */
.reaction-bar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-start; gap: var(--space-2);
  margin-top: var(--space-2); max-width: 100%;
}
/* Ohne Rahmen, aber blickdichter Hintergrund (vorher halbtransparent -- schlecht lesbar vor
   wechselndem Nachrichten-/Bild-Hintergrund), nur ein dezenter Schatten zur Abgrenzung. */
.reaction-pill {
  display: inline-flex; align-items: center; gap: var(--space-2); background: var(--panel-2); border: none;
  border-radius: var(--radius-full); padding: var(--space-1) var(--space-4); font-size: var(--fs-xs); cursor: pointer; color: var(--text);
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--duration-fast), background var(--duration-fast);
}
.reaction-pill:hover { box-shadow: var(--shadow-sm), 0 0 0 1px color-mix(in srgb, var(--accent) 50%, transparent); }
.reaction-pill.active { background: var(--accent-strong); box-shadow: var(--shadow-sm), 0 0 0 1px color-mix(in srgb, var(--accent) 50%, transparent); }
.reaction-pill .reaction-count { font-size: var(--fs-2xs); color: var(--text-dim); }
.reaction-pill.active .reaction-count { color: var(--accent); }
.reaction-add-btn {
  display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px;
  border-radius: 50%; border: none; background: var(--knopf); color: var(--text-dim);
  box-shadow: var(--shadow-sm);
  cursor: pointer; font-size: var(--fs-md); opacity: 0; transition: opacity var(--duration-fast), box-shadow var(--duration-fast), color var(--duration-fast);
}
.chat-message-group:hover .reaction-add-btn, .comment-item:hover .reaction-add-btn, .reaction-bar:focus-within .reaction-add-btn {
  opacity: 1;
}
.reaction-add-btn:hover { box-shadow: var(--shadow-sm), 0 0 0 1px color-mix(in srgb, var(--accent) 50%, transparent); color: var(--accent); }

/* ---------- Agenda widget ---------- */
/* padding-right zusätzlich zu scrollbar-gutter, damit Inhalte auch bei Overlay-
   Scrollbalken (macOS/manche Linux-Desktops) nicht darunter verschwinden. */
/* scrollbar-gutter:stable (globale *-Regel) reserviert zusaetzlich zum eigenen padding-right
   dauerhaft Scrollbar-Breite, selbst wenn die Liste (noch) gar nicht ueberläuft -- addiert sich
   zum ohnehin schon vorhandenen padding-right zu einer unnoetig breiten toten Zone rechts, in
   jedem Browser identisch (kein Rendering-Unterschied). Gleicher Bug wie zuvor bei .widget-chat,
   hier bisher nicht mitgezogen. */
/* padding-right war 14px, obwohl der eigentliche Scrollbar nur 8px breit ist (siehe
   *::-webkit-scrollbar) -- reine Reserve-Verschwendung, besonders da scrollbar-gutter jetzt
   schon auto ist. Auf 6px reduziert (genug Abstand, wenn tatsaechlich gescrollt wird). */
/* padding-top: das erste Kachel-Unread-Badge ragt per negativem top ueber die Kartenecke hinaus
   (siehe .agenda-unread-badge) -- ohne diesen Platz wuerde overflow-y:auto es am oberen Rand
   der Liste abschneiden, sobald ganz nach oben gescrollt ist. */
/* Bugfix (Nutzer-Feedback: erst "in firefox kann ich scrollen bis ich das fenster auf mobile
   verkleinere, dann geht es nicht mehr", danach zusaetzlich "wenn ich jetzt ueber der agenda mit
   der maus bin scrollt die gesamte Seite nicht mehr" -- auch am Desktop). Root Cause: die Liste
   trug overscroll-behavior:contain. Das sollte eigentlich nur verhindern, dass die interne
   Listen-Scrollgrenze (bei echter fester Spaltenhoehe) das Scrollen der Seite dahinter ausloest
   -- tatsaechlich unterbindet es aber JEDE Scroll-Weiterleitung an <html>, sobald die Liste
   scrollHeight==clientHeight hat (kein eigenes Overflow), egal ob das an der auto-Hoehe auf
   Mobile liegt ODER schlicht daran, dass wenige Agenda-Eintraege die feste Desktop-Box gar nicht
   fuellen -- Mausrad/Touch-Scroll ueber der Liste tut dann buchstaeblich gar nichts mehr, weder
   in der Liste noch auf der Seite. Ersatzlos entfernt statt bedingt ueberschrieben (siehe Git-
   Historie fuer den vorherigen, nur auf Mobile beschraenkten Versuch) -- der Vorteil von contain
   (kein Scroll-Bleed am Ende der Liste in die Seite) ist es nicht wert, dass die Seite bei kurzen
   Listen komplett unscrollbar wird. */
/* Links und rechts gleich viel Luft (2026-09-16: "dann machen wir bei den widgets die kacheln so
   breit, dass sie links und rechts den gleichen abstand haben") -- vorher stand rechts ein
   Innenabstand fuer die Bildlaufleiste, wodurch die Karten schmaler und aus der Mitte geruckt waren
   und das Datum in den schmalen Kacheln eine Zeile tiefer rutschte. */
/* padding-right: 3px gibt dem Hover-Schub Platz -- mit overflow-x: hidden allein wurde die rechte
   Kante der Karte abgeschnitten (2026-09-16: "die hover animation schneidet das objekt rechts ab"). */
.agenda-list { display: flex; flex-direction: column; gap: var(--space-2); flex: 1; min-height: 0; overflow-y: auto; overflow-x: clip; padding-top: var(--space-3); padding-inline: 0 3px; scrollbar-gutter: auto; }
/* Etwas mehr Luft zwischen den Karten (2026-09-14: "mache den abstand in der agenda zwischen
   events einen kleinen ticken groesser") -- nur in der grossen Agenda: in den Widget-Kacheln ist
   der Platz knapp, dort bliebe sonst eine Karte weniger uebrig. */
.agenda-list-main { max-height: none; flex: 1; min-height: 0; overflow-y: auto; overflow-x: clip; padding-right: var(--space-3); scrollbar-gutter: auto; gap: var(--space-5); }
.agenda-date-header {
  font-size: var(--fs-xs); font-weight: var(--fw-medium); color: var(--text-dim); opacity: 0.75;
  text-transform: uppercase; letter-spacing: 0.03em;
  margin: var(--space-7) 0 var(--space-2);
}
.agenda-date-header:first-child { margin-top: 0; }
/* Folgt der Schriftfarbe, nicht der Akzentfarbe (2026-09-16: "in dem widget vergangene events ist
   noch der button x weitere events vorhanden der uebernimmt aber nicht die textfarbe") -- es ist
   eine Nebenzeile wie Datum und Ort, kein Akzent. */
.agenda-more-link {
  display: block; text-align: center; color: var(--text-dim); font-size: var(--fs-sm);
  padding: var(--space-4) 0 var(--space-1); cursor: pointer;
}
.agenda-more-link:hover { color: var(--text); text-decoration: underline; }
/* Angeheftet, "heute" und "privat" stehen als kleine Symbole unten rechts in der Karte
   (2026-09-15: "mache anstatt den dreiecken unten wie bei dem schloss icons hin. eine pin nadel
   fuer angeheftet und fuer heute ein icon das passt. mit hover"). Davor waren es ein gestrichelter
   Rahmen (2026-09-13), dann ein Dreieck an der Ecke (2026-09-14) -- das Dreieck sass auf Karten MIT
   Titelbild an der falschen Stelle, weil dort ".agenda-item-mit-bild > *" jedem direkten Kind
   position:relative gibt und damit das absolute Positionieren aushebelte (siehe Ausnahme dort).
   Als echte Elemente tragen die Symbole ihren Hinweis im title -- Pseudoelemente koennen das nicht,
   und sie brauchen pointer-events, sonst zeigt der Zeiger nichts an. */
.agenda-item.pinned, .agenda-item.due-today { border-color: transparent; }
.agenda-marker {
  position: absolute; right: var(--space-4); bottom: var(--space-2); z-index: 2;
  display: flex; align-items: center; gap: var(--space-3); line-height: 1;
}
/* Nur ein Hinweis beim Ueberfahren, kein Klickziel und kein Frage-Cursor (2026-09-15: "ich will
   nicht drauf klicken ich will einfach nur einen hover. auch kein fragezeichen der maus"). */
.agenda-marker-icon { display: inline-flex; line-height: 1; cursor: default; }
.agenda-marker-icon .mdi { font-size: var(--fs-md); }
.agenda-marker-pin { color: var(--highlight); }
.agenda-marker-privat { color: var(--danger); }
/* Die Symbole liegen ueber dem Inhalt -- die letzte Zeile der Beschreibung bekommt deshalb eine
   Rinne, damit Text und Symbol sich nie beruehren (drei Symbole plus Abstaende ~60px). */
.agenda-item:has(.agenda-marker) .agenda-desc-row { padding-right: 60px; }
/* In den flachen Widget-Karten (Thumb links, zwei Zeilen) ist die Titelzeile die unterste. Sie
   reservierte rechts erst die Breite der Symbolreihe -- dadurch endete die Teilnehmerzahl sichtbar
   vor dem Datum darueber (2026-09-15: "tags und teilnehmeranzahl ist noch nicht rechtsbuendig").
   Jetzt laeuft die Zeile bis an den Rand und die Karte bekommt unten Platz fuer die Symbolecke. */
.agenda-item:has(.agenda-marker) .agenda-body-thumb-left,
.agenda-item:has(.agenda-marker) .agenda-body > .agenda-header-bar:last-child {
  padding-bottom: 18px;
}
/* Tags stehen in diesen Karten in derselben Zeile wie Titel und Zahl: Titel | Tags | Zahl
   (2026-09-15). Die Tags schieben sich ans rechte Ende, die Zahl folgt direkt dahinter. */
/* Drei Dinge in einer Zeile passen nur ohne Umbruch -- der Titel schrumpft stattdessen und wird
   gekuerzt (er hat ohnehin ellipsis). Gilt in den Widget-Karten UND in der grossen Agenda
   (2026-09-15: "mache es einfach wie in den angehefteten events"). */
.agenda-title-row { flex-wrap: nowrap; }
.agenda-title-row > .agenda-title { flex: 1 1 60px; min-width: 0; }
.agenda-title-row > .agenda-tags { margin-left: auto; flex-wrap: nowrap; flex-shrink: 0; }
.agenda-title-row > .agenda-tags + .agenda-status-row { margin-left: var(--space-3); }

/* In der Event-Ansicht fehlte bis 2026-09-15 jeder Hinweis darauf, dass ein Event privat ist -- die
   Agenda-Karte zeigte das Schloss, die geoeffnete Ansicht nicht. Gleiche Farbe wie dort. */
.event-preview-hero-privat { color: var(--danger); }
/* Das "Scheinen" von der Ecke bleibt den beiden Zustands-Symbolen erhalten (2026-09-14: "animiere
   die ecken mit einem kleinen scheinen") -- nur Helligkeit und ein weicher Schein, keine Bewegung;
   "heute" versetzt, damit nicht alles im Gleichtakt atmet. Das Schloss ist eine Eigenschaft des
   Events, kein Zustand, und bleibt ruhig. */
.agenda-marker-pin { animation: eckeScheinen 4s ease-in-out infinite; }
@keyframes eckeScheinen {
  0%, 100% { opacity: 0.7; filter: none; }
  50% { opacity: 1; filter: drop-shadow(0 0 5px currentColor); }
}
@media (prefers-reduced-motion: reduce) {
  .agenda-marker-icon { animation: none; opacity: 1; }
}
/* scrollbar-gutter:stable (globale *-Regel) reserviert Scrollbar-Breite auch bei overflow:hidden,
   obwohl dort *niemals* ein Scrollbar erscheinen kann (geclipter Inhalt ist einfach unsichtbar,
   keine Scroll-UI) -- reine Verschwendung von ~12px am rechten Rand jeder Karte, in jedem Browser
   gleich (dritter Fund dieses Musters in dieser Runde: .widget-chat, .agenda-list, jetzt hier).
   Gilt fuer jede Karte mit overflow:hidden + Rundung -- siehe auch .tile-card weiter unten. */
.agenda-item {
  display: flex;
  /* Kein 1px-Rahmen mehr (2026-09-14 gemeldet: "bei den eintraegen jeweils so ein kleiner
     schwarzer balken aussenrum") -- angeheftet/heute/privat zeigen seit 2026-09-15 Symbole unten
     rechts (.agenda-marker), kein Rahmen mehr, hier braucht es also keinen Platzhalter. */
  border: none;
  /* Spalte statt Zeile, seit die Kategorie nicht mehr als senkrechter Streifen links steht,
     sondern als Faehnchen-Leiste ueber dem Kopfbalken (siehe .agenda-typ-leiste). */
  flex-direction: column;
  align-items: stretch;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  background: var(--inhalt-kachel);
  overflow: hidden;
  scrollbar-gutter: auto;
  cursor: pointer;
  transition: background var(--duration-fast) ease, transform var(--duration-fast) ease;
  position: relative;
}
/* Etwas heller als der Kopfbalken (#262b36), damit beide beim Hover klar unterscheidbar
   bleiben statt optisch zu verschmelzen (vorher identischer Hex-Wert -- echter Bug). */
.agenda-item:hover { background: var(--kachel-hover); transform: translateX(2px); }
/* Kategorie als Trapez-Faehnchen an einer Farblinie oben (Nutzer-Feedback 2026-09-13: "die gleichen
   trapeze bei den agenda eintraegen und den widgets einfuehren die jetzt gerade diese flags auf der
   linken seite haben") -- gleiche Form, Versalien und Abdunklung wie das Art-Faehnchen der
   Event-Ansicht (.event-typ-flagge weiter unten). Anders als dort steht das Faehnchen hier links
   statt mittig (Nutzerwunsch, siehe unten) und haengt nicht ueber dem Inhalt, sondern steht in einer
   eigenen Zeile im Fluss: die Agenda-Karten sind flach, ein ueberlappendes Faehnchen wuerde Datum
   oder Titel verdecken. Damit zwischen Linie und Kopfbalken
   kein andersfarbiger Streifen entsteht, traegt die Leiste dessen Farbe (--inhalt-kachel-hover) -- bei den
   Karten ohne Kopfbalken (Layout thumbLeft, z.B. Releases-Widget) stattdessen die Kartenfarbe. */
.agenda-typ-leiste {
  --typ-flaeche: color-mix(in srgb, var(--typ-farbe) 82%, black);
  /* Das Datum steht seit 2026-09-13 rechtsbuendig in dieser Zeile (vorher eine eigene Zeile
     darunter, die links neben dem Faehnchen fast leer war). */
  /* Die Farblinie liegt als ::before auf den oberen 3px -- genau so viel Padding bekommt die Zeile,
     damit das Faehnchen direkt unter der Linie sitzt: mehr ergibt einen sichtbaren Spalt
     ("die trapeze sind immer noch zu weit unten"), weniger schneidet die Linie ihm die Ecken ab. */
  /* flex-start, weil rechts jetzt zwei Zeilen stehen (Datum, darunter Status und Teilnehmerzahl):
     das Faehnchen soll trotzdem oben an der Linie kleben. */
  /* Umbrechen erlaubt: in den schmalen Dashboard-Spalten passen Faehnchen und Datum sonst nicht
     nebeneinander und beides schnitte ab. Das Faehnchen sitzt etwas weiter rechts als der uebrige
     Karteninhalt (2026-09-14: "auf allen kacheln sollte das trapez etwas weiter rechts sein"). */
  /* Kein Umbruch (2026-09-16: "mache keinen umbruch bei den kacheln der ersten zeile") -- wird es
     eng, kuerzt das DATUM, nicht das Faehnchen ("ich meinte nicht das die trapeze abgeschnitten
     werden sollen sondern das datum"). */
  position: relative; flex-shrink: 0; display: flex; align-items: flex-start; flex-wrap: nowrap;
  gap: var(--space-2) var(--space-5);
  padding: 3px var(--space-5) 3px var(--space-10);
  /* Standard: Flaechenton -- die Raumzeilen (.chatroom-row-typ) benutzen dieselbe Leiste und
     gehoeren zur Flaeche. Nur in einer Inhaltskachel kommt die Farbe aus deren Ton (Regel unten). */
  background: var(--panel-hover);
}
.agenda-item > .agenda-typ-leiste,
.agenda-item .agenda-body > .agenda-typ-leiste { background: var(--inhalt-kachel-hover); }
.agenda-typ-leiste .agenda-header-date { margin-left: auto; text-align: right; }
/* Das Datum gibt nach, wenn die Zeile zu schmal wird: es schrumpft und kuerzt mit Ellipsis. */
.agenda-typ-leiste .agenda-kopf-rechts {
  flex-shrink: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.agenda-typ-leiste .agenda-header-date { overflow: hidden; text-overflow: ellipsis; }
/* Nutzer-Feedback direkt nach der Umstellung: "die faehnchen die jetzt neu aussehen schon auf der
   linken seite lassen" -- das Faehnchen steht linksbuendig auf Hoehe von Datum und Titel darunter,
   nicht mittig wie in der Event-Ansicht. Die Linie laeuft dafuer nach rechts aus (statt durchgehend
   voller Farbe) wie die Linie der Menueleiste oben: sonst wirkt die Karte nach rechts hin
   abgeschnitten, weil die volle Farbe dort ohne Faehnchen endet. Als ::before statt border-top,
   weil ein Farbverlauf keine Rahmenfarbe sein kann. */
.agenda-typ-leiste::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px; pointer-events: none;
  background: linear-gradient(90deg, var(--typ-flaeche) 0%, var(--typ-flaeche) 45%, transparent 88%);
}
.agenda-item:has(.agenda-body-thumb-left) .agenda-typ-leiste { background: transparent; }
.agenda-typ-flagge {
  /* Bleibt immer vollstaendig -- gekuerzt wird das Datum daneben. */
  flex-shrink: 0; padding: 0 var(--space-6) 1px; background: var(--typ-flaeche); color: var(--text-on-color);
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold); letter-spacing: 0.06em; text-transform: uppercase;
  line-height: 1.5; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  clip-path: polygon(0 0, 100% 0, calc(100% - 8px) 100%, 8px 100%);
}
.agenda-body { display: flex; flex-direction: column; flex: 1; min-width: 0; }
/* Karten ohne Content-Row (kein Bild, keine Beschriftung, siehe agenda.js): der Kopfbalken fuellt die
   Karte, damit unter ihm kein dunklerer Rest bleibt, wenn die Karte hoeher ist als ihr Inhalt. */
.agenda-body > .agenda-header-bar:last-child { flex: 1; }
/* Voller Kopfbalken (Datum+Tags, dann Titel/Icons), etwas heller als der Kartenhintergrund
   (.agenda-item nutzt var(--panel-2)) -- macht das Datum Teil jeder Karte statt nur
   des (jetzt quartalsweisen) Gruppen-Headers. */
/* Kopfbalken und Fahnenzeile gehoeren zur Inhaltskachel: sie muessen aus DEREN Farbe kommen, sonst
   traegt die Karte oben die Flaechen- und unten die Kachelfarbe (2026-09-16 im Aufbau des Nutzers
   sichtbar: graue Flaeche, lila Kachel). Eine Stufe ueber der Karte -- beim Hover gleicht sich die
   Karte an den Kopfbalken an, wie vorher mit --panel-2/--panel-hover. */
.agenda-header-bar { background: var(--panel-hover); padding: var(--space-3) var(--space-5) var(--space-2); display: flex; flex-direction: column; gap: var(--space-1); }
.agenda-item .agenda-header-bar { background: var(--inhalt-kachel-hover); }
.agenda-header-top-row { display: flex; align-items: baseline; gap: var(--space-4); }
/* Die Tags stehen am rechten Ende der Zeile. */
.agenda-header-top-row > .agenda-tags { margin-left: auto; }
/* Ohne min-width:0 kann das Datum (ein einzelnes nicht umbrechbares Token) die Zeile ueberlaufen
   lassen, sobald wenig Platz ist -- der Tag-Chip (letztes Flex-Kind, per margin-left:auto ganz
   rechts) wird dann vom overflow:hidden der Karte lautlos abgeschnitten. Chrome/Firefox messen
   Text-Breite minimal unterschiedlich (Subpixel), daher kippt das je nach Fensterbreite mal im
   einen, mal im anderen Browser -- kein browserspezifischer Bug, nur ungeschuetzter Wettlauf um
   Platz. Jetzt weicht das Datum aus (Ellipsis), der Tag-Chip bleibt garantiert sichtbar. */
/* Deutlicher als frueher (2026-09-13, "mache es sichtbarer"): groessere Schrift, volle Textfarbe
   statt gedimmt. Die Signalfarben fuer heute/bald/vergangen darunter gewinnen weiterhin. */
.agenda-header-date { font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.agenda-header-date-soon { color: var(--highlight); font-weight: var(--fw-semibold); }
.agenda-header-date-today { color: var(--danger); font-weight: var(--fw-bold); }
/* Nutzer-Feedback: "vor x tagen oder gestern etc. in einem schön lesbaren lila" -- eigene Farbe
   fuer die vergangenheitsbezogene Zusatzangabe im Vergangene-Events-Widget (siehe pastRelative-
   Option in renderAgendaItems()), damit sie sich klar von der Indigo-Akzentfarbe (var(--accent))
   und den zukunftsbezogenen gelb/rot-Signalfarben oben abhebt. */
.agenda-header-date-past { color: #c084fc; }
.agenda-header-top-row .agenda-tags { margin: 0 0 0 auto; flex-shrink: 0; }
/* Rechts in der Faehnchen-Zeile: Datum, darunter der Status ("laeuft gerade", "in 3 Tagen",
   "heute") und die Teilnehmerzahl daneben (2026-09-14, Nutzerwunsch). Das Datum steht dabei ein
   paar Pixel tiefer als das Faehnchen -- sonst klebt es an der Farblinie ganz oben. */
/* Datum und "in 2 Monaten" stehen seit 2026-09-14 NEBENeinander in der Faehnchen-Zeile
   (Nutzerwunsch) -- vorher untereinander, was die Karte eine Zeile hoeher machte. */
.agenda-kopf-rechts {
  margin-left: auto; display: flex; flex-direction: row; align-items: baseline; justify-content: flex-end;
  gap: var(--space-4); padding-top: var(--space-2); min-width: 0; flex-wrap: wrap;
}
/* Status und Zaehler stehen seit 2026-09-14 rechts in der Titelzeile -- der Titel dazwischen darf
   dafuer kuerzen, die Statusgruppe nicht. */
.agenda-status-row {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: nowrap; flex-shrink: 0;
  justify-content: flex-end; font-size: var(--fs-sm); margin-left: auto;
}
/* Gleiche Groesse wie das Datum daneben (2026-09-14: "das in 2 monaten oder in x tagen ... ist viel
   zu gross mache es so wie den rest") -- ohne eigene Angabe erbte es die groessere Grundschrift der
   Karte und stach dadurch aus der Zeile heraus. */
.agenda-status-faellig { font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--text); }
/* Das Datum steht gross am rechten Rand, mittig ueber beide Zeilen der Karte (2026-09-15) --
   oben rechts bleiben Uhrzeit und "heute / in x Tagen". Die Zeilen links bekommen dafuer eine
   Rinne, damit Titel, Tags und Zahl nicht darunter laufen. */
/* Nur die Geburtstagskachel traegt ihr Datum rechts NEBEN dem Namen, in derselben Groesse
   (2026-09-16: "wir machen bitte das datum eine zeile hoeher in die gleiche zeile wie der name.
   und einfach genauso gross"); oben rechts steht weiterhin "heute / in x Tagen". */
.agenda-datum-gross {
  margin-left: auto; font-size: var(--fs-lg); font-weight: var(--fw-semibold); color: var(--text);
  font-variant-numeric: tabular-nums; white-space: nowrap; flex-shrink: 0;
}
/* Titel und Kopfzeile bleiben einzeilig -- sonst waechst die Karte um eine Zeile; der Titel
   schrumpft stattdessen, das Datum nicht. */
.agenda-item-mit-datum .agenda-title-row { flex-wrap: nowrap; }
.agenda-item-mit-datum .agenda-kopf-rechts { flex-wrap: nowrap; white-space: nowrap; }
.agenda-item-mit-datum .agenda-title { flex: 0 1 auto; min-width: 0; }

/* Je naeher der Termin, desto deutlicher (2026-09-15: "wenn etwas ab in 5 tagen ist ist die in x
   tagen schrift gelb. wenn es heute ist ist sie rot - wie frueher auch"). */
/* Zwei Spalten im gemeinsamen Fenster von angehefteten und vergangenen Events (2026-09-15) --
   beide Listen scrollen fuer sich, das Fenster bleibt so hoch wie der Bildschirm es zulaesst. */
.agenda-archiv-spalten { display: flex; gap: var(--space-6); min-height: 0; }
.agenda-archiv-spalte { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; min-height: 0; }
.agenda-archiv-spalte-kopf {
  display: flex; align-items: center; gap: var(--space-3); flex-shrink: 0;
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold); letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-dim);
  padding-bottom: var(--space-3); margin-bottom: var(--space-4); border-bottom: 1px solid var(--border);
}
.agenda-archiv-spalte .agenda-list { overflow-y: auto; min-height: 0; max-height: 60vh; }
/* Auf dem Handy ist das Fenster eine volle Seite mit zwei Bloecken uebereinander, die jeder fuer
   sich scrollen (2026-09-16: "koennen wir das fullscreen machen mit zwei spalten uebereinander die
   man individuell scrollen kann?") -- nebeneinander war in 390px nichts zu lesen. */
@media (max-width: 880px) {
  #agenda-archiv-modal.modal-overlay {
    padding: 0; align-items: stretch;
    bottom: calc(var(--mobile-tab-bar-height) + var(--mobile-tab-bar-gap) * 2);
  }
  #agenda-archiv-modal .modal {
    width: 100%; max-width: none; height: 100%; max-height: none; border-radius: 0; box-shadow: none;
  }
  #agenda-archiv-modal .modal-body { display: flex; flex-direction: column; min-height: 0; }
  .agenda-archiv-spalten { flex-direction: column; flex: 1; min-height: 0; }
  .agenda-archiv-spalte { flex: 1 1 0; min-height: 0; }
  .agenda-archiv-spalte .agenda-list { max-height: none; flex: 1; min-height: 0; }
}

.agenda-status-faellig-bald { color: var(--warning); font-weight: var(--fw-semibold); }
.agenda-status-faellig-heute { color: var(--danger); font-weight: var(--fw-semibold); }
/* Die farbige Pille von 2026-09-14 ist wieder raus ("das läuft gerade und in x monaten sollte
   einfach auf den kacheln normaler text sein nicht so umrandet einfach text wie zuvor") -- die
   Angabe ist ueberall schlichter Text, vergangene Termine gedimmt (.agenda-header-date-past). */
.agenda-status-wiederholt { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--text-dim); }
.agenda-status-wiederholt .mdi { font-size: var(--fs-md); }
.agenda-status-row .agenda-badges { display: flex; gap: var(--space-2); }
.agenda-content-row { display: flex; gap: var(--space-5); align-items: flex-start; padding: var(--space-4) var(--space-5); flex: 1; min-width: 0; }
/* Ohne Titelbild gehen Kopfbalken und Beschreibungsteil ineinander ueber -- eine kaum sichtbare
   Linie trennt beide (2026-09-15: "mache bei event kacheln ohne titelbild vor dem zweiten reiter
   wo die beschreibung drin steht eine gaaanz leicht angedeutete trennline"). Mit Titelbild macht
   der Verlauf des Bildes die Trennung schon. */
.agenda-item:not(.agenda-item-mit-bild) .agenda-content-row {
  border-top: 1px solid color-mix(in srgb, var(--border) 45%, transparent);
}
/* Das Datum auf Geburtstagskarten steht fett (2026-09-15) -- dort ist der Tag die Information,
   nicht der Titel. */
#birthdays-list .agenda-header-date, .agenda-item-geburtstag .agenda-header-date {
  font-weight: var(--fw-bold); color: var(--text);
}
/* Teilnehmer-/Mitspielerzahl war als graue Mini-Pille kaum zu sehen -- jetzt getoente Flaeche in
   der Akzentfarbe und volle Textfarbe (Nutzerwunsch: "die teilnehmer sollten etwas sichtbarer sein
   grad ist es unscheinbar"). */
.agenda-badge {
  background: var(--accent-tint); color: var(--text); font-size: var(--fs-sm);
  font-weight: var(--fw-medium); padding: 1px var(--space-4); border-radius: var(--radius-full);
}
.agenda-badge .mdi { font-size: var(--fs-md); }
/* Titelbild als Hintergrund der Karte (2026-09-13, Nutzerwunsch "das titelbild als gradient der
   background mit verlauf ... wie bei der event uebersicht"): dieselbe Form wie dort -- das Bild
   steht am linken Rand, laeuft nach rechts aus und ist so weit gedaempft, dass die Schrift darueber
   lesbar bleibt. Im hellen Modus wird es aufgehellt statt abgedunkelt (dunkle Schrift auf Papier),
   im dunklen abgedunkelt -- der Wert steht als --agenda-bild-filter in der jeweiligen Farbliste. */
.agenda-item-mit-bild::before {
  /* Eigene Rundung statt sich auf das overflow:hidden der Karte zu verlassen: durch den filter
     bildet das Pseudoelement eine eigene Ebene, deren Ecken die Karte in Chromium NICHT mehr
     beschneidet -- die Karte sah oben links eckig aus (2026-09-14 auf dem Handy gemeldet). */
  border-radius: inherit;
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 62%;
  background-image: var(--agenda-bild); background-size: cover;
  background-position: var(--agenda-bild-pos, center);
  filter: var(--agenda-bild-filter);
  -webkit-mask-image: linear-gradient(to right, #000 30%, transparent 100%);
          mask-image: linear-gradient(to right, #000 30%, transparent 100%);
  pointer-events: none; z-index: 0;
}
/* Inhalt ueber das Bild -- ohne das lieferte der Stapel je nach Reihenfolge mal Bild, mal Text oben. */
/* Der Inhalt liegt ueber dem Titelbild. Die Marker unten rechts sind ausgenommen: sie sind
   absolut positioniert, ein position:relative von hier wuerde sie in den Fluss zurueckholen
   (2026-09-15 gemeldet: "die dreiecke waren total verschoben irgendwo anders"). */
.agenda-item-mit-bild > *:not(.agenda-marker) { position: relative; z-index: 1; }
/* Und die Flaechen IM Inhalt durchscheinend: der Kopfbalken traegt sonst seine eigene deckende
   Farbe ueber das Bild und man sieht davon gar nichts (genau daran ist der erste Versuch
   gescheitert -- das Bild lag korrekt da, nur verdeckt). Gleiches Muster wie in der Event-Ansicht,
   wo die Kacheln mit Titelbild auf color-mix(...) umschalten. */
.agenda-item-mit-bild :is(.agenda-header-bar, .agenda-typ-leiste) { background: color-mix(in srgb, var(--inhalt-kachel-hover) 45%, transparent); }
/* Mit Titelbild bleiben die Leisten durchscheinend, sonst waere vom Bild nichts zu sehen -- aber
   in derselben Farbe wie ohne Bild. */
.chatroom-row.agenda-item-mit-bild :is(.agenda-header-bar, .agenda-typ-leiste, .chatroom-row-typ, .chatroom-row-header-bar) {
  background: color-mix(in srgb, var(--inhalt-kachel-hover) 55%, transparent);
}
.agenda-item-mit-bild .agenda-content-row { background: transparent; }
.agenda-thumb { width: 36px; height: 36px; border-radius: var(--radius-sm); object-fit: cover; object-position: center; flex-shrink: 0; }
.agenda-thumb-placeholder { display: flex; align-items: center; justify-content: center; font-size: var(--fs-xl); }
/* Mit einem Nutzer verknuepfter Geburtstag zeigt dessen Profilbild -- rund statt der sonst
   eckigen Thumb-Form, damit es klar als Portraet erkennbar ist. */
.agenda-thumb-avatar { border-radius: 50%; }
.agenda-title .agenda-icon img { width: 1rem; height: 1rem; border-radius: 50%; object-fit: cover; vertical-align: -2px; }
/* Screenshots-Widget: Bild fuellt den ganzen unteren Bereich statt kleinem Thumbnail + Text
   daneben -- nur dort verwendet, alle anderen Agenda-Karten (Events/Releases/Geburtstage/Spiele)
   nutzen weiterhin den kleinen 36x36-Thumb neben dem Text (.agenda-thumb oben). */
.agenda-content-row-fill { padding: 0; gap: 0; }
.agenda-thumb-fill { width: 100%; height: 84px; object-fit: cover; display: block; }
/* Alternatives Kartenlayout (aktuell nur Releases-Widget): Thumb steht LINKS neben Datum+Titel
   statt darunter -- macht die Karte flacher, da keine eigene Content-Row-Zeile mehr fuers Bild
   noetig ist. Kein separater dunkler Header-Balken mehr (ergab bei dieser Anordnung zwei sichtbare
   Kaesten nebeneinander) -- Datum/Titel/Meta liegen direkt auf der normalen Kartenfarbe.
   Nutzer-Feedback: "mache die logos/covers des spiele widgets auch etwas kleiner" -- 40px statt
   der vorherigen 52px (davor schon von den urspruenglichen 36px vergroessert). */
.agenda-body-thumb-left { flex-direction: row; align-items: center; gap: var(--space-5); padding: var(--space-4) var(--space-5); }
.agenda-body-thumb-left .agenda-thumb,
.agenda-body-thumb-left .agenda-thumb-placeholder { width: 40px; height: 40px; font-size: var(--fs-xl); }
.agenda-body-thumb-left .agenda-info { display: flex; flex-direction: column; gap: var(--space-1); }
/* Nutzer-Feedback: "mache die releases und latest notes inhalte in den widgets ruhig noch etwas
   schmaler wie die birthdays" -- Birthdays ist ohne Thumb ohnehin am kompaktesten; hier per ID
   gezielt nur fuer diese zwei Widgets (nicht Wishlist/Sammlung, die dieselbe .agenda-body-thumb-
   left-Klasse nutzen, aber nicht genannt wurden) Thumb und Padding verkleinert. ID-Selektor
   gewinnt automatisch gegen die obige Klassenregel, kein !important noetig.
   Nutzer-Feedback (2026-09-11): Vergangene-Events-Kacheln "nicht zu groß" -- gleiche Masse wie
   Releases, dessen Vorbild das Widget ohnehin ist (siehe loadPastEventsWidget()). */
#releases-widget-list .agenda-body-thumb-left,
#past-events-widget-list .agenda-body-thumb-left,
#wiki-recent-widget-list .agenda-body-thumb-left { padding: var(--space-3) var(--space-4); gap: var(--space-4); }
/* Vier statt drei vergangene Events (2026-09-15: "passt nicht noch ein 4. event in den vergangenen
   widget?") -- mit 55px pro Karte blieb zusammen mit der Zeile "X weitere" kein Platz fuer die
   vierte. Zwei Pixel weniger Innenabstand oben und unten reichen: 4x51 + 3x4 Abstand + 29 fuer die
   Mehr-Zeile passen in die 249px der Kachel. */
#past-events-widget-list .agenda-body-thumb-left { padding-block: var(--space-2); }
/* Dasselbe bei den Notizen: mit dem vollen Innenabstand waren zwei Zeilen drei Pixel zu hoch und die
   Kachel liess sich minimal scrollen (2026-09-16 gemeldet: "der inhalt im neuste notizen widget ist
   noch etwas zu gross und laesst minimal scrollen"). */
#wiki-recent-widget-list .agenda-body-thumb-left { padding-block: var(--space-2); }
/* Im Widget "Vergangene Events" sagt die Pin-Nadel nichts mehr -- die Kachel zeigt ohnehin nur
   Abgelaufenes, und angeheftet wird ein Event dort spaetestens am Tag danach ohnehin geloest
   (_angeheftete_abschalten() in main.py). 2026-09-16: "hier sollte kein angeheftet icon mehr zu
   sehen sein". */
#past-events-widget-list .agenda-marker-pin { display: none; }
#releases-widget-list .agenda-thumb, #releases-widget-list .agenda-thumb-placeholder,
/* Der Titel traegt hier dieselbe ruhige Schrift wie die Eintraege der anderen Widgets
   (2026-09-15: "mache die schrift des titels wie alle anderen titel das ist zu fett"). */
#past-events-widget-list .agenda-title { font-size: var(--fs-md); font-weight: var(--fw-medium); }
#past-events-widget-list .agenda-thumb, #past-events-widget-list .agenda-thumb-placeholder,
#wiki-recent-widget-list .agenda-thumb, #wiki-recent-widget-list .agenda-thumb-placeholder { width: 32px; height: 32px; font-size: var(--fs-lg); }
.agenda-info { flex: 1; min-width: 0; }
/* Titel und Status teilen sich eine Zeile; wird es zu eng (schmale Dashboard-Spalte), rutscht der
   Status in die naechste Zeile, statt den Titel auf "Enshrou..." zu kuerzen. */
.agenda-title-row { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; }
.agenda-title { font-size: var(--fs-lg); font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 130px; min-width: 0; }
.agenda-title .agenda-icon { display: inline-flex; font-size: var(--fs-body); margin-right: var(--space-2); vertical-align: -2px; }
.agenda-badges { display: flex; gap: var(--space-3); align-items: center; flex-shrink: 0; }
.agenda-badge { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--fs-2xs); color: var(--text-dim); }
.agenda-badge .mdi { font-size: var(--fs-md); }
.agenda-badge.recurring-badge { color: var(--success); }
.agenda-badge.agenda-badge-attending { color: var(--success); }
.agenda-meta { font-size: var(--fs-xs); color: var(--text-dim); margin-top: var(--space-1); }
/* Nutzer-Feedback: "birthday widget verkleinern ... 'wird x jahre alt' neben den namen schreiben"
   -- steht jetzt inline in der Titel-Zeile statt in einer eigenen .agenda-meta-Zeile darunter,
   siehe renderAgendaItems() in app.js (dortige Content-Row entfaellt fuer Geburtstage komplett). */
/* Das Alter stand hier bis 2026-09-15 in der Kopfzeile ("wird 34 Jahre alt"). Die Karte zeigt jetzt
   nur noch Name und Datum ("einfach der name und das datum") -- das Alter steht im Popup. */

/* Am Geburtstag selbst rieselt Konfetti in der Karte ("wenn es heute ist mache konfetti innenrein").
   Es liegt ueber dem Inhalt, nicht dahinter: .agenda-header-bar traegt die sichtbare Flaeche der
   Karte und wuerde alles darunter verdecken. Damit der Name lesbar bleibt, sind die Schnipsel nur
   halb deckend. Die Karte schneidet ohnehin ab (.agenda-item hat overflow: hidden), das Konfetti
   bleibt also drin; unter den Eck-Symbolen (z-index 2) bleibt es ebenfalls. */
.agenda-konfetti { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.agenda-konfetti i {
  position: absolute; height: 8px; border-radius: 1px; opacity: 0.5;
  animation: kartenKonfetti linear infinite;
}
/* Eigener Name: "konfettiFall" gehoert dem Konfetti ueber der ganzen Seite (weiter unten, faellt
   105vh weit) -- gleicher Name haette die Karten-Schnipsel quer ueber den Bildschirm geschickt.
   Die Fallhoehe steckt hier in "top" (Prozent vom Karteninneren) statt in translateY: eine
   Prozent-Verschiebung bezoege sich auf den Schnipsel selbst (8px) und reichte nie ueber die
   Karte. Die Drehung bleibt beim transform. */
@keyframes kartenKonfetti {
  0% { top: -16%; transform: rotate(0deg); }
  100% { top: 104%; transform: rotate(520deg); }
}
/* Wer Bewegung reduziert haben moechte, bekommt die Schnipsel still verteilt statt fallend. */
@media (prefers-reduced-motion: reduce) {
  .agenda-konfetti i { animation: none; top: 30%; }
}
/* Nutzer-Feedback: steht jetzt als eigene Zeile direkt unter dem Titel im Kopfbalken (siehe
   renderAgendaItems() in app.js), nicht mehr inline in .agenda-meta neben Ort/Alter -- daher
   eigenes margin-top statt des Meta-Zeilen-Abstands. Spaeteres Nutzer-Feedback: "die ortsanzeige
   in den ersten abschnitt neben film/spiel/serie" -- die Zeile kann jetzt mehrere ".agenda-linked-
   media-chip"-Eintraege (Verknuepfung + Ort) nebeneinander zeigen statt nur eines einzelnen Icons. */
.agenda-linked-media { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); font-size: var(--fs-sm); color: var(--text); margin-top: var(--space-1); }
.agenda-linked-media-chip { display: inline-flex; align-items: center; gap: var(--space-2); }
.agenda-linked-media-chip .mdi { font-size: var(--fs-sm); }
/* R61 Nutzer-Feedback: doch wieder neben dem Thumb in .agenda-info statt einer eigenen vollen
   Zeile am Kartenende -- die volle-Breite-Variante liess den Thumb oben allein/abgeschnitten
   wirken, sichtbar in der Hauptagenda. Zitat-Icon + IMMER gerendert (auch ohne Text) bleiben
   aber erhalten, damit alle Karten trotzdem dieselbe Hoehe haben. Nur EINE Zeile reserviert
   (nicht die vollen 2 vom -webkit-line-clamp) -- die meisten Beschreibungen sind kurz, 2 Zeilen
   Reserve liess Karten mit kurzem Text unnoetig viel freie Flaeche zeigen (Nutzer-Feedback);
   Beschreibungen mit 2 Zeilen lassen die Karte einfach etwas hoeher werden. Bei Geburtstagen
   entfaellt die Zeile komplett (siehe renderAgendaItems() in app.js) -- kein Kommentar-Konzept
   dort, nur das "wird X Jahre alt" in .agenda-meta. */
/* Eine Zeile Platz, egal wie lang der Text ist -- sonst ist jede Karte anders hoch (2026-09-15).
   Seit dem Nachtrag ("mache die beschreibung in den agenda kacheln nur einzeilig damit es etwas
   schmaler ist") ist die Beschreibung auf EINE Zeile begrenzt (siehe .agenda-desc). */
.agenda-desc-row { display: flex; align-items: flex-start; gap: var(--space-3); margin-top: var(--space-2); min-height: 1.35em; box-sizing: border-box; }
/* Platzhalter fuer fehlenden Ort/fehlende Beschreibung (2026-09-15) -- grau und ohne Kursiv, damit
   sofort klar ist, dass da nichts steht. */
.agenda-desc-leer, .agenda-meta-leer { color: var(--text-dim); font-style: normal; opacity: 0.75; }
/* Punkt zwischen Datum/Uhrzeit und "laeuft gerade" -- wie der Punkt im Datum selbst. */
.agenda-kopf-punkt { color: var(--text-dim); opacity: 0.6; }
/* Anfuehrungszeichen deutlicher (Nutzerwunsch 2026-09-13) -- vorher ein fast unsichtbares Icon
   bei 55 % Deckkraft. */
.agenda-desc-row .mdi { flex-shrink: 0; font-size: var(--fs-lg); color: var(--accent-light); opacity: 1; margin-top: 0; }
/* max-height/line-height als Absicherung neben -webkit-line-clamp: Firefox begrenzt die Box in
   diesem Flex-Kontext (auto-Height-Spalte) teils nicht auf 2 Zeilen, wodurch Karten dort hoeher
   werden als in Chromium/WebKit -- mit fester max-height ist die Kartenhoehe browserunabhaengig
   gleich, auch wenn Firefox das Ellipsis-"..." selbst nicht anzeigt. */
.agenda-desc { flex: 1; min-width: 0; font-size: var(--fs-sm); font-style: italic; line-height: 1.35; max-height: 1.35em; color: var(--text); overflow: hidden; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; }
.agenda-desc p { margin: 0; }
/* Die Vorschau ist seit 2026-09-16 reiner Text (markdownAlsText() in app.js) -- die frueheren
   Sonderregeln fuer Ueberschriften, Listen und Code IN der Vorschau sind damit gegenstandslos:
   die Zeile sieht auf jeder Karte gleich aus, egal wie die Beschreibung gesetzt ist. */
.agenda-tags { display: flex; gap: var(--space-2); flex-wrap: wrap; }
/* Geburtstags-Widget etwas kompakter (2 statt 3 Eintraege, kuerzeres "BDay"-Streifenlabel) --
   angeglichen an die Zeilenhoehe des Releases-Widgets. */
/* .agenda-content-row entfaellt bei Geburtstagen jetzt komplett (siehe renderAgendaItems() in
   app.js), daher hier kein Padding-Override mehr dafuer noetig. */
#birthdays-list .agenda-header-bar { padding: var(--space-2) var(--space-4) var(--space-3); }
/* ---------- Baustein Leerzustand ----------
   Ein Aussehen fuer "hier ist noch nichts" in Listen, Widgets und Seitenleisten. Vorher hatte fast
   jede Liste ihre eigene Regel -- mal zentriert, mal linksbuendig, mit 6, 10 oder 20px Abstand --
   und drei Stellen (Chatraum-Liste, Game-Server, Passwort-Safe) nahmen dafuer sogar .settings-hint,
   den Hinweistext aus den Einstellungen. Neue Leerzustaende nehmen .empty-state. Die grossen
   Platzhalter im Hauptbereich (.wiki-empty-state) sind etwas anderes und bleiben eigen. */
.empty-state, .agenda-empty, .links-empty, .chat-empty, .activity-empty, .wiki-list-empty,
.wiki-sidebar-empty, .comments-empty, .date-group-empty {
  color: var(--text-dim); font-size: var(--fs-md); line-height: 1.5; text-align: center;
  margin: 0; padding: var(--space-8) var(--space-6);
  grid-column: 1 / -1;   /* in einem Kachel-Raster ueber alle Spalten, sonst sitzt er in der ersten */
}
/* .links-list ist ein 2-spaltiges Grid -- ohne explizite Spannweite landete der Leer-Hinweis als
   einziges Kind nur in der ersten Spalte und wirkte dadurch schmal/mittig verschoben statt ueber
   die volle Widget-Breite zu gehen. */
.links-list .links-empty { grid-column: 1 / -1; }

/* ---------- Tag-Chips ---------- */
.tag-chip {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-2xs); padding: var(--space-1) var(--space-4); border-radius: var(--radius-xl);
  background: var(--accent-tint); color: var(--text);
}
.attendee-chip { padding-left: var(--space-2); gap: var(--space-3); }
.attendee-chip-avatar { width: 16px; height: 16px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.attendee-chip-avatar-placeholder { font-size: var(--fs-lg); line-height: 1; color: var(--text-dim); }

/* ---------- Bongo Cat widget ---------- */
/* Der Inhalt des Bongo-Cat-Widgets steht auf einer eigenen Flaeche -- er ist keine Liste aus
   Karten, stand seit dem Wegfall der Widget-Flaeche also frei auf dem Seitengrund (2026-09-13
   gemeldet: "bongo cat hat auch keinen background mehr"). */
.bongocat-widget-body {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  padding: var(--space-5) var(--space-4) var(--space-4);
  background: var(--inhalt-kachel); border-radius: var(--radius);
}
.bongocat-row { display: flex; align-items: center; justify-content: center; gap: var(--space-2); width: 100%; }
.bongocat-bubble-col { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); margin-right: var(--space-5); min-width: 0; }
/* Nutzer-Feedback: "die katze auf dem desktop etwas kleiner machen und auch gerade" -- gleicher
   rotate()-Ausgleich wie beim mobilen Mini-Badge (.agenda-bongocat-mini-img), da die Katzengrafik
   (bongocat.png) selbst leicht schief gezeichnet/zugeschnitten ist. */
.bongocat-img { width: 82px; height: auto; flex-shrink: 0; transform: rotate(-13deg); }
.bongocat-bubble {
  position: relative;
  background: var(--white);
  color: #16161a;
  border: 2px solid #1c1c1e;
  /* Klein genug halten, dass links/rechts genug gerade Kante fuer den Sprechblasen-Pfeil
     uebrig bleibt -- bei zu grossem Radius faellt der Pfeil in die Rundung und wirkt
     dadurch nicht buendig an die Blase angeschlossen. */
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-5);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-sm);
  text-align: center;
  white-space: nowrap;
  box-shadow: var(--shadow-sm);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  max-width: 100%;
}
/* Nur noch EIN Dreieck ohne eigenen Rand (statt zwei verschachtelter fuer einen
   "Umriss") -- kein zweites Dreieck mehr, das exakt zum ersten passen muss, und
   grosszuegiger Ueberlapp in die Blase hinein, damit garantiert kein Spalt/Rand-
   Versatz an der Ansatzstelle sichtbar wird. */
.bongocat-bubble::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -4px;
  transform: translateY(-50%);
  width: 0;
  height: 0;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
  border-left: 6px solid var(--white);
}
.bongocat-bubble-empty { color: #8a8a90; font-weight: var(--fw-medium); font-style: italic; white-space: normal; }
.bongocat-bubble-code { font-family: "JetBrains Mono", monospace; letter-spacing: .03em; overflow-x: auto; cursor: pointer; }
/* Kopieren/Bearbeiten/Speichern/Abbrechen -- alle kleinen Icon-Buttons in der Sprechblase
   teilen sich diesen Stil, damit Bearbeiten direkt in der Blase stattfinden kann statt in
   einem Stift-Button oben in der Widget-Kopfzeile. */
.bongocat-bubble-icon-btn { border: none; background: transparent; color: #16161a; cursor: pointer; display: flex; padding: 0; flex-shrink: 0; opacity: .7; font-size: var(--fs-xs); }
.bongocat-bubble-icon-btn:hover { opacity: 1; }
.bongocat-bubble-input {
  width: 72px;
  min-width: 0;
  border: none;
  background: transparent;
  color: #16161a;
  font-family: "JetBrains Mono", monospace;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  letter-spacing: .03em;
  padding: 0;
  outline: none;
}
/* Nutzer-Feedback: "den text darunter auf jeden fall kleiner der ist nicht sooo wichtig" --
   die "von X · vor Y"-Zeile ist reine Nebeninfo, deutlich kleiner als der Code selbst. */
.bongocat-meta { color: var(--text-dim); font-size: var(--fs-2xs); text-align: center; }

/* ---------- Links widget ---------- */
.links-list { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; align-content: start; padding-right: var(--space-3); scrollbar-gutter: auto; overscroll-behavior: contain; }
.link-item {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: var(--inhalt-kachel);
  border: 1px solid transparent;
  text-decoration: none;
  color: var(--text);
  min-width: 0;
  transition: background var(--duration-fast) ease, border-color var(--duration-fast) ease;
}
.link-item:hover { background: var(--panel-hover); border-color: var(--link-accent, var(--accent)); }
.link-icon { font-size: var(--fs-xl); flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; }
.link-icon img { width: 18px; height: 18px; object-fit: contain; border-radius: var(--radius-xs); }
.link-title { flex: 1; min-width: 0; font-size: var(--fs-md); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------- Links-Tab (Kachel-Grid) ---------- */
/* Nutzer-Feedback: "mache bei links maximal 2 in eine reihe weil sich die kacheln verschieben
   wenn der name zu lang wird ... sollten immer gleich breit sein wenn es 2 in einer reihe sind"
   -- fest auf 2 gleich breite Grid-Spalten (repeat(2, 1fr)) statt vorher 3 an breiten Bildschirmen. */
.link-tile-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-5); }
@media (max-width: 560px) { .link-tile-grid { grid-template-columns: 1fr; } }
/* R61 Nutzer-Feedback: Beschreibung jetzt im selben Design wie bei Dateien (.files-row-desc-row)
   -- eigene volle Zeile unten mit kleinem Zitat-Icon statt daneben in der Titelzeile. Kachel
   deshalb jetzt zweizeilig als Column-Flex (.link-tile-main oben, .link-tile-desc-row unten)
   statt einer einzelnen Reihe. */
.link-tile {
  position: relative;
  display: flex; flex-direction: column; text-align: left;
  background: var(--inhalt-kachel); border: 1px solid transparent; border-radius: var(--radius);
  cursor: pointer; transition: background var(--duration-fast) ease, border-color var(--duration-fast) ease, transform var(--duration-fast) ease;
}
.link-tile:hover { background: var(--panel-hover); border-color: var(--link-accent, var(--accent)); transform: translateY(-1px); }
.link-tile-main { display: flex; align-items: center; gap: var(--space-6); padding: var(--space-6) var(--space-7); }
.link-tile-icon { font-size: var(--fs-3xl); flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; }
.link-tile-icon img { width: 28px; height: 28px; object-fit: contain; border-radius: var(--radius-xs); }
.link-tile-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.link-tile-title { font-weight: var(--fw-semibold); font-size: var(--fs-lg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.link-tile-domain { color: var(--text-dim); font-size: var(--fs-xs); }
/* R61 Nutzer-Feedback: eigene volle Zeile unten mit kleinem Zitat-Icon statt einer schmalen Pille
   in der Titelzeile -- IMMER gerendert (auch ohne Text), damit alle Link-Kacheln dieselbe Hoehe
   haben, unabhaengig davon ob eine Beschreibung vorhanden ist. Rechts etwas mehr Platz (44px),
   damit der Text beim Hover nicht unter die Bearbeiten-/Pin-Buttons oben rechts laeuft. */
.link-tile-desc-row { display: flex; align-items: flex-start; gap: var(--space-3); padding: 0 44px var(--space-5) var(--space-7); min-height: 20px; box-sizing: border-box; }
.link-tile-desc-row .mdi { flex-shrink: 0; font-size: var(--fs-xs); color: var(--text-dim); opacity: 0.55; margin-top: var(--space-1); }
.link-tile-desc {
  flex: 1; min-width: 0; color: var(--text-dim); font-size: var(--fs-sm); line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.link-tile-edit {
  position: absolute; top: 6px; right: 6px; opacity: 0; transition: opacity var(--duration-fast) ease;
  width: 22px; height: 22px; font-size: var(--fs-md);
}
.link-tile:hover .link-tile-edit { opacity: 1; }
.link-tile-pin {
  position: absolute; top: 6px; right: 30px; opacity: 0; transition: opacity var(--duration-fast) ease;
  width: 22px; height: 22px; font-size: var(--fs-md);
}
.link-tile:hover .link-tile-pin, .link-tile-pin.pinned { opacity: 1; }
.link-tile-pin.pinned { color: var(--highlight); }

/* ---------- Activity-Feed ---------- */
.activity-list { display: flex; flex-direction: column; gap: var(--space-4); flex: 1; min-height: 0; overflow-y: auto; padding-right: var(--space-3); scrollbar-gutter: auto; }
.activity-item { font-size: var(--fs-sm); color: var(--text-dim); display: flex; gap: var(--space-3); }
.activity-item b { color: var(--text); font-weight: var(--fw-semibold); }
.activity-time { margin-left: auto; white-space: nowrap; font-size: var(--fs-2xs); }

/* ---------- Jahresansicht: Unterreiter (Kalender / Releases / Geburtstage) ---------- */
/* Nutzer-Feedback: Abstand zum Inhalt darunter deutlich kleiner (war 18px). */
.year-subtabs {
  display: flex; gap: var(--space-4); margin-bottom: var(--space-2);
  position: sticky; top: var(--sticky-header-height, 0); z-index: var(--z-sticky); background: var(--bg); padding: var(--space-2) 0;
}
/* Nutzer-Feedback: gleicher Pillen-Stil wie die Medien-Scope-Filter (.filter-btn) statt der
   eckigeren .tab-btn-Basisoptik -- .tab-btn wird auch von der Haupt-Nav-Leiste oben verwendet,
   deshalb hier bewusst auf ".year-subtabs .tab-btn" gescoped statt die Basisklasse anzufassen.
   Betrifft Kalender (Kalender/Geburtstage), Compendium (Games/Filme-Serien/LAN-Party) und die
   Medien-Upload-Umschalter (gleiche Klasse, automatisch mit im gleichen Stil). */
.year-subtabs .tab-btn {
  background: var(--knopf); border: 1px solid var(--border); color: var(--text-dim);
  padding: var(--space-3) var(--space-6); border-radius: var(--radius-xl); font-size: var(--fs-md);
}
.year-subtabs .tab-btn.active { background: var(--accent); border-color: var(--accent); color: var(--text-on-color); }
.year-subtabs .tab-btn .filter-btn-check { display: none; margin-right: var(--space-3); font-size: 0.9em; }
.year-subtabs .tab-btn.active .filter-btn-check { display: inline-block; }
.media-upload-mode-toggle {
  position: static; background: transparent; padding: 0; margin-bottom: var(--space-7);
}
.media-upload-target-toggle {
  position: static; background: transparent; padding: 0; margin-bottom: var(--space-7);
}
/* R41: Event-Formular Spiel/Film/Serie -- Suche-zuerst-Resolver ersetzt den fruehreren R39/R40
   "Neu anlegen"/"Vorhandenes verknuepfen"-Umschalter komplett. */
#event-catalog-resolver { margin-bottom: var(--space-2); }
/* Nutzer-Feedback: "verknüpft mit spiel xy und das cover sollten auf selber höhe sein so wie die
   spalte 2 (datum etc.) auch" -- der Gap zum Suchfeld darueber sitzt jetzt als margin-bottom AUF
   dem Suchfeld-Label (nur relevant, solange es noch sichtbar ist), nicht mehr als margin-top auf
   der Status-Zeile selbst. Einmal aufgeloest wird das Suchfeld-Label ausgeblendet (siehe
   applyResolvedCatalog() in app.js) und die Status-Zeile ist das einzige sichtbare Kind von
   #event-catalog-resolver -- vorher zog ihr eigener margin-top sie dadurch ein Stueck tiefer als
   das Titelbild/Spalte-2 (die keinen vergleichbaren oberen Abstand haben). */
.event-catalog-resolver-status {
  display: flex; align-items: center; gap: var(--space-3); padding: var(--space-4) var(--space-6);
  border-radius: var(--radius-md); background: color-mix(in srgb, var(--warning) 12%, transparent); color: var(--warning); font-size: var(--fs-md);
}
#event-catalog-resolver-label { margin-bottom: var(--space-4); }
/* Nutzer-Feedback: "spiel film serie suchen in diesen events etwas hervorheben mit der
   akzentfarbe damit man weiß das man das eingeben MUSS" -- nur fuer den Pflicht-Fall (Spiel/Film/
   Serie-Session-Neuanlage, siehe applyCategoryFieldRules() in app.js), nicht fuer die optionale
   Verknuepfung bei Event/LAN-Party. */
#event-catalog-resolver-label.event-catalog-search-required #event-catalog-resolver-label-text {
  color: var(--accent); font-weight: var(--fw-bold);
}
#event-catalog-resolver-label.event-catalog-search-required .icon-search {
  border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent);
}
.event-catalog-resolver-status i { font-size: var(--fs-body); flex-shrink: 0; }
/* R48: "Nicht zugewiesen" (Katalog-Eintrag wurde geloescht) -- rot statt amber, damit es sich
   klar von den normalen "verknuepft"/"bereits vorhanden"-Zustaenden abhebt. */
.event-catalog-resolver-status.unassigned { background: color-mix(in srgb, var(--danger) 12%, transparent); color: var(--danger); }
.event-catalog-resolver-status .event-catalog-resolver-change-btn {
  margin-left: auto; background: none; border: none; color: inherit; text-decoration: underline;
  cursor: pointer; font-size: var(--fs-md); padding: 0; flex-shrink: 0;
}
/* "Kein Treffer? manuell anlegen" -- eigene Zeile am Ende der Ergebnisliste, optisch abgesetzt
   durch eine Trennlinie statt wie ein normaler Treffer auszusehen. */
.steam-result-manual { border-top: 1px solid var(--border); color: var(--text-dim); }
/* display:contents haelt die Kind-Elemente als direkte Grid-Items von .two-col-form -- ein
   <fieldset> selbst hat dadurch keine eigene Box mehr, "disabled" wirkt aber weiterhin nativ auf
   alle enthaltenen Formularelemente. Die Dimmung muss deshalb auf die Kinder gehen. */
fieldset.event-session-fields { display: contents; border: none; margin: 0; padding: 0; }
fieldset.event-session-fields:disabled > * { opacity: 0.4; pointer-events: none; }
.media-slider {
  position: relative; margin: 0 0 var(--space-8); border-radius: var(--radius); overflow: hidden;
  background: var(--panel-2); aspect-ratio: 21 / 6;
}
.media-slider-track { display: flex; height: 100%; transition: transform var(--duration-slow) ease; }
.media-slider-slide {
  position: relative; flex: 0 0 100%; height: 100%; cursor: pointer; background: var(--panel-2);
}
.media-slider-slide img { width: 100%; height: 100%; object-fit: cover; display: block; }
.media-slider-dots {
  position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%);
  display: flex; gap: var(--space-3);
}
.media-slider-dot { width: 7px; height: 7px; border-radius: 50%; background: rgba(255,255,255,0.4); }
.media-slider-dot.active { background: var(--white); }
@media (max-width: 720px) { .media-slider { aspect-ratio: 16 / 9; } }
.media-detail-back-btn { flex-shrink: 0; margin-right: var(--space-5); }
/* ---------- Alle Geburtstage als Fenster (2026-09-14) ----------
   Zwoelf Monatskarten, vier je Zeile (Nutzerwunsch: "immer 4 monate in einer row"); darunter je
   eine Zeile pro Geburtstag, die das Bearbeiten-Formular oeffnet. */
.geburtstage-modal { width: min(1200px, 94vw); height: min(820px, 88vh); max-width: none; }
.geburtstage-body { padding: var(--space-10) var(--space-12) var(--space-12); overflow-y: auto; }
.geburtstage-monate { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-8); }
@media (max-width: 1100px) { .geburtstage-monate { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px) { .geburtstage-monate { grid-template-columns: 1fr; } }
.geburtstage-monat { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.geburtstage-monat-kopf {
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-dim);
  padding: 0 var(--space-3) var(--space-3); border-bottom: 1px solid var(--border); margin-bottom: var(--space-3);
}
/* Der laufende Monat ist der einzige farbige Kopf -- ohne ihn sucht man im Jahr. */
.geburtstage-monat.ist-jetzt .geburtstage-monat-kopf { color: var(--accent-light); border-bottom-color: var(--accent-strong); }
.geburtstage-leer { color: var(--text-dim); padding: var(--space-2) var(--space-3); }
.geburtstage-zeile {
  display: flex; align-items: center; gap: var(--space-4); width: 100%; text-align: left;
  padding: var(--space-3); border: none; background: transparent; color: var(--text);
  font-family: inherit; font-size: var(--fs-md); border-radius: var(--radius-md); cursor: pointer;
  transition: background var(--duration-fast) ease;
}
.geburtstage-zeile:hover { background: var(--tint); }
.geburtstage-zeile.ist-heute { background: var(--accent-tint); font-weight: var(--fw-semibold); }
/* Zwei Zeilen nebeneinander: Symbol, darunter/daneben Datum und fetter Name, rechts das Alter
   mittig ueber beide Zeilen (2026-09-15). */
.geburtstage-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.geburtstage-tag { color: var(--text-dim); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.geburtstage-avatar { width: 26px; height: 26px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.geburtstage-avatar-leer { display: flex; align-items: center; justify-content: center; font-size: var(--fs-md); }
.geburtstage-name { font-weight: var(--fw-semibold); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Kurzes Aufblinken, wenn die Uebersicht aus einer Kachel heraus geoeffnet wurde. */
.geburtstage-zeile-blinkt { animation: geburtstagBlinken 0.5s ease-in-out 3; }
@keyframes geburtstagBlinken {
  0%, 100% { background: var(--tint); }
  50% { background: var(--accent-tint); box-shadow: 0 0 0 2px var(--accent); }
}
@media (prefers-reduced-motion: reduce) {
  .geburtstage-zeile-blinkt { animation: none; background: var(--accent-tint); box-shadow: 0 0 0 2px var(--accent); }
}
/* Das Alter als kleine Zahl am Ende -- "wird 30 Jahre alt" waere in der schmalen Spalte zu lang. */
.geburtstage-alter { color: var(--text-dim); font-size: var(--fs-sm); flex-shrink: 0; }

/* ---------- Kalender als Fenster (2026-09-13) ----------
   Der Kalender-Reiter ist entfallen; dasselbe Raster steht jetzt in einem Fenster, das das
   Kalender-Widget des Dashboards oeffnet (openCalendarModal() in app.js). Die Agenda-Spalte
   daneben ist mit dem Reiter weggefallen -- die Agenda steht im Menue links. */
.calendar-modal { width: min(1480px, 96vw); height: min(920px, 90vh); max-width: none; }
.calendar-modal .modal-header-bar { display: flex; align-items: center; gap: var(--space-8); }
.calendar-modal .modal-header-actions { margin-left: auto; display: flex; align-items: center; gap: var(--space-4); }
/* Der Kalender fuellt das Fenster: Wochentags-Kopf oben fest, das Raster darunter scrollt. */
.calendar-modal .year-calendar-col { flex: 1; min-height: 0; padding: 0 var(--space-10) var(--space-10); }
.year-calendar-col { min-width: 0; display: flex; flex-direction: column; }
.year-global-dow-header { flex-shrink: 0; }
.year-calendar-area { flex: 1; min-height: 0; height: auto; overflow-y: auto; }
/* Zonen-Hintergrund fuer die Kalender-Kacheln, analog zum Dashboard-Widgets-Bereich. */
.year-calendar-area { background: var(--panel); border-radius: var(--radius); padding: var(--space-7); min-width: 0; }
@media (max-width: 880px) {
  /* Auf dem Handy ist der Kalender eine volle Seite (2026-09-14: "der kalender sollte fullscreen
     sein und kein popup ... mit der menueleiste unten weiterhin im vordergrund sichtbar ... und
     damit auch ohne zurueck button") -- das Fenster fuellt alles ueber der Leiste, ohne Rand, ohne
     Rundung und ohne Schliessen-Kreuz. Verlassen wird der Kalender ueber die Leiste unten. */
  #calendar-modal.modal-overlay {
    padding: 0; align-items: stretch;
    /* Bis ganz nach unten, NICHT ueber der Leiste endend: der Spalt darueber liess den Kalender wie
       ein schwebendes Blatt aussehen (2026-09-16: "kalender ist noch etwas hervorgehoben und quasi
       fast ueber der menuebar unten. also man sieht das es ein popup ist"). Damit die Leiste
       trotzdem bedienbar bleibt, liegt der Kalender eine Stufe UNTER ihr -- sie schwebt darueber
       wie ueber jeder anderen Seite. Alles, was aus dem Kalender heraus aufgeht (Event-Ansicht,
       Formular), steht weiterhin auf --z-modal und damit ueber beidem. */
    bottom: 0;
    z-index: calc(var(--z-tab-bar) - 1);
    background: var(--bg); -webkit-backdrop-filter: none; backdrop-filter: none;
  }
  /* Platz fuer die schwebende Leiste, damit die letzte Woche nicht dahinter verschwindet. */
  .calendar-modal .year-calendar-col {
    padding-bottom: calc(var(--mobile-tab-bar-height) + var(--mobile-tab-bar-gap) * 2);
  }
  /* Das Jahr steht ganz links: die Kopfzeile bekommt 52px Innenabstand, sobald sie ein
     Schliessen-Kreuz ENTHAELT (.modal-header-bar:has(.modal-header-close)) -- hier ist das Kreuz
     zwar ausgeblendet, :has() greift aber trotzdem (2026-09-16: "mache die jahreszahl komplett
     linksbuendig"). */
  #calendar-modal .modal-header-bar { padding-left: var(--space-5); }
  .calendar-modal { width: 100%; height: 100%; max-height: none; border-radius: 0; box-shadow: none; }
  #calendar-modal .modal-header-close { display: none; }
  .year-calendar-area { background: none; padding: 0; }
  /* Die 20px Innenabstand der Spalte waren die "Balken" links und rechts (2026-09-14: "der mobile
     kalender hat immer noch links und rechts groessere balken das sollte weg"). */
  .calendar-modal .year-calendar-col { padding-inline: 0; }
  /* Der Monatsname klebte dadurch am linken Rand (2026-09-15: "kalender mobil hat die namen der
     monate noch zu weit links stehen da brauchen wir einen kleinen abstand") -- die Tageszahlen
     haben ihren Abstand aus dem Raster, die Ueberschrift braucht ihn hier. */
  .year-timeline-month-label { margin-left: var(--space-6); }
}

/* ---------- Event-Vorschau ---------- */
/* Nutzer-Feedback: "wir kombinieren die spalte des 1 und 4 ... spalte 2 ist jetzt die notiz ...
   der chat ist dann so breit wie jetzt spalte 3 und 4 zusammen" -- 3 statt vorher 4 Spalten:
   Bild+Funktionen+Beschreibung+Infos+Fotos (zusammengelegte alte Spalte 1+4) | Notiz (alte
   Spalte 2, etwas schmaler) | Chat (nimmt jetzt den Platz der alten Spalten 3+4 zusammen ein).
   Kein grid-column-Tausch mehr noetig (anders als bei der vorherigen 4-Spalten-Version) -- die
   DOM-Reihenfolge in index.html entspricht bereits der gewuenschten Spaltenreihenfolge. Kein
   align-items:start -- Standard-"stretch" laesst alle Spalten (v.a. den Chat) auf die Hoehe der
   Inhaltsspalten mitwachsen. #event-preview-modal .modal.modal-xl weiter unten vergroessert das
   Modal passend dazu. */
/* Nutzer-Feedback: "mache bitte die notiz area breiter und den chat etwas schmaler" (mehrfach
   nachjustiert) -- Notiz-Spalte zuletzt auf 420px verbreitert, Chat (1fr) schrumpft automatisch mit.
   Folgerunde ("jett ist es verschoben und die zahl (1) ist auch noch abgeschnitten. mache die
   spalte einfach etwas breiter und dafür vllt den chat minimal kleiner"): Medien-Spalte von 300px
   auf 330px verbreitert (Funktionen-Spalte daneben braucht mehr Platz fuer die zwei Kalender-Icons
   im Mehrtage-Modus) -- Chat (1fr) schrumpft dadurch automatisch um dieselben 30px mit. */
/* Nutzer-Feedback: "wenn sie nebeneinander so wie eben dann nicht hin passen machen wir jetzt das
   event vorschau fenster einfach etwas breiter und die 1 zeile auch einen ticken breiter" -- Medien-
   Spalte (traegt die Funktionen-/Kalender-Icon-Spalte neben dem Cover) von 330px auf 360px, Modal-
   Breite unten passend mitgezogen (siehe #event-preview-modal .modal.modal-xl). */
.event-preview-layout { display: grid; grid-template-columns: 360px 420px 1fr; gap: var(--space-10); }
@media (max-width: 720px) { .event-preview-layout { grid-template-columns: 1fr; } }
/* Braucht mehr Breite als die geteilte .modal-xl-Basis (1200px, auch vom Medien-Ordner-Popup
   genutzt) -- 3 echte Spalten sonst zu schmal. ID-scoped statt die Basisklasse zu aendern, wie
   schon bei #media-game-detail .modal.modal-xl nebenan. MUSS vor der @media(max-width:720px)-
   Vollbild-Regel weiter unten stehen (gleiche Selektor-Spezifitaet, dort gewinnt sonst nicht mehr
   die spaetere Quellreihenfolge -- bekannter Bug-Pattern in dieser Datei).
   Nutzer-Feedback: "wieder etwas schmaler aber dafür höher, nicht so lang gezogen, eher wie ein
   schönes fenster rechteckig" -- 1760px wirkte zu breit/flach. 1400px orientiert sich an der
   gleichen min-height-Groessenordnung wie #media-game-detail .modal (680px bei 1320px Breite,
   siehe dort) statt ein Sonderfall zu sein. min-height statt fixer height, damit kurze Events
   (wenig Beschreibung/Chat) nicht unnoetig hoch mit viel Leerraum wirken -- ab min-height wird
   NICHT mehr mitgewachsen (siehe .event-preview-layout unten), stattdessen scrollt jede Spalte
   fuer sich. */
#event-preview-modal .modal.modal-xl { max-width: 1430px; }
/* overflow-y:hidden ueberstimmt die geteilte .modal-with-header .modal-body-Regel (overflow-y:
   auto) -- hier soll NICHT der ganze Modal-Body als Einheit scrollen, sondern jede Spalte
   einzeln (siehe .event-preview-media-col/-notiz-col/-chat weiter unten). */
/* flex:0 0 <Groesse> statt nur height -- .modal-with-header ist selbst ein Column-Flex-Container,
   die geteilte Basisregel setzt dort "flex:1" (flex-basis:0%) auf .modal-body, was eine simple
   height-Angabe fuer die Hauptachsen-Groesse in Flexbox schlaegt (flex-basis gewinnt gegenueber
   height, ausser bei flex-basis:auto). Nur per explizitem flex-basis + grow:0/shrink:0 laesst
   sich diese feste Groesse tatsaechlich erzwingen. */
/* Nutzer-Feedback (Folgerunde): "jetzt ist das fenster etwas zu groß und der chat ist unten
   abgeschnitten auf meinem kleineren screen. beschränke die beschreibung in der höhe und mache
   dann daran die höhe des fensters fest" -- die vorherige 860px/90vh-Erhoehung ging auf kleineren
   Screens ueber den sichtbaren Viewport hinaus (die Chat-Spalte hat kein eigenes Scrollen fuer
   ihre eigene Hoehe, nur der Nachrichtenverlauf darin). Beschreibung/Fotos sind ohnehin schon auf
   280px begrenzt (siehe #event-preview-external-description/.media-widget-grid) -- die
   Modal-Hoehe orientiert sich wieder daran statt an der (variablen) Gesamtlaenge aller Abschnitte. */
/* Nutzer-Feedback: "mache das event view fenster ruhig etwas höher" -- moderate Erhoehung
   gegenueber der vorherigen 640px/76vh (die vorherige 860px/90vh-Version schnitt auf kleineren
   Bildschirmen den Chat unten ab, siehe Kommentar in der vorherigen Aenderung).
   Nutzer-Feedback (Folgerunde, nachdem die neue Event-Infos-Karte links dazukam): "mache das event
   vorschaufenster wieder höher" -- erneut moderat erhoeht (720px -> 800px), bleibt mit 86vh
   (statt der vorherigen 90vh) unterhalb der Schwelle, die beim 860px/90vh-Versuch auf kleineren
   Bildschirmen den Chat abschnitt. */
/* Nutzer-Feedback: "ein klein wenig höher das man mit der neuen medien übersicht nicht scrollen
   muss" -- die Medien-Ordner-Umschalter-Zeile + der "Alle Medien"-Link kamen im Laufe des Tages
   zur Medien-Spalte dazu und ragten dadurch knapp ueber die bisherige Hoehe hinaus. */
/* flex-shrink 1 und min-height 0 (2026-09): mit "0 0" war der Body fest 88vh hoch und passte samt
   Kopfzeile nicht mehr ins Modal (max-height 90vh) -- bei 900px Fensterhoehe ragte er 56px ueber die
   Unterkante hinaus, die Chat-Eingabe war halb abgeschnitten. Jetzt schrumpft er auf den Platz, der
   da ist; auf hohen Bildschirmen aendert sich nichts. */
#event-preview-modal .modal-body { flex: 0 1 min(840px, 88vh); min-height: 0; display: flex; overflow-y: hidden; }
#event-preview-modal .event-preview-layout { flex: 1; min-height: 0; }
/* Nutzer-Feedback: "mir gefällt es noch nicht wie das datum angezeigt wird ich finde es geht total
   unter - vorallem auf dem desktop" -- das Datum stand in zwei kleinen Kalender-Kacheln unter den
   Funktions-Knoepfen und hatte dort dasselbe Gewicht wie "Pin". Jetzt ein eigener Kopfstreifen ueber
   allen Spalten (renderEventPreviewHero() in event-preview.js). Spaltenrichtung statt der geerbten
   Zeilenrichtung, damit der Streifen oben steht und die Spalten den Rest der festen Hoehe nehmen; die
   Hoehe waechst um etwa die Streifenhoehe mit, sonst ginge sie komplett den Spalten ab. */
#event-preview-modal .modal-body { flex-basis: min(920px, 88vh); flex-direction: column; }
.event-preview-hero { flex-shrink: 0; padding: 0 var(--space-2) var(--space-10) var(--space-4); }
.event-preview-hero-date {
  margin: 0; font-size: var(--fs-4xl); font-weight: 800; line-height: 1.15; letter-spacing: -0.01em;
  color: var(--text); text-shadow: 0 2px 16px rgba(0,0,0,0.45);
  /* So breit wie das Datum selbst: die Monatsvorschau haengt am ganzen Element (renderEventPreviewHero()),
     sonst sprang sie schon ueber dem leeren Rest der Zeile auf */
  width: fit-content;
}
.event-preview-hero-sep { font-weight: var(--fw-regular); color: var(--text-dim); margin: 0 0.25em; }
.event-preview-hero-sub {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-8); margin-top: var(--space-4);
  font-size: var(--fs-lg); color: var(--text-dim);
}
.event-preview-hero-sub .mdi { font-size: var(--fs-body); vertical-align: -2px; margin-right: var(--space-2); }
.event-preview-hero-rel {
  padding: var(--space-1) var(--space-5); border-radius: var(--radius-full); font-size: var(--fs-sm); font-weight: var(--fw-bold); color: var(--text-on-color);
  /* Volle Akzentfarbe statt 60% Deckung: auf Papier stand die weisse Schrift sonst auf einer fast
     weissen Flaeche (Kontrast 1.03, gefunden mit TEST=hellmodus). */
  background: var(--accent);
}
.event-preview-hero-rel-past { background: var(--tint-hover); color: var(--text-dim); }
/* Nutzer-Feedback: "das titelbild ... quasi über das gesamte event geht mit übergang von links nach
   rechts und halt blurred allgemein damit es nicht stört" -- eigene Ebene hinter allem: z-index:-1
   innerhalb von isolation:isolate liegt ueber der Modal-Flaeche und unter jedem Inhalt, ohne dass
   Kopfzeile oder Body dafuer position/z-index brauchen. Die Maske laeuft nach rechts in die
   Modal-Flaeche aus, auf dem Handy nach unten (siehe @media weiter unten). */
#event-preview-modal .modal { isolation: isolate; }
/* Nutzer-Bug (2026-09-11): "die event übersicht am desktop ist ja abgerundet. aber wenn ein titelbild da
   ist ist es oben links eckig" -- das Bild (::before) liegt mit filter/mask auf einer eigenen
   Compositing-Ebene, die das Abrunden per overflow:hidden des Modals nicht in jedem Browser mitmacht.
   clip-path schneidet die Ebene selbst in Modalform zu (auf dem Handy randlos, siehe @media 720px). */
.event-preview-backdrop { display: none; position: absolute; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; border-radius: inherit; clip-path: inset(0 round var(--radius)); }
#event-preview-modal.event-preview-has-backdrop .event-preview-backdrop { display: block; }
/* Nutzer-Feedback (Folgerunde): "ich dachte es nur so, dass wir anstatt dem jetzigen titelbild den
   hintergrund als titelbild machen. daher bitte weniger blur und das bild an sich auch nicht so sehr
   "verzerren" oder zoomen damit man minimal noch erkennt was es ist" -- vorher fuellte das Bild per
   background-size:cover das ganze querformatige Modal, wurde dabei stark vergroessert und
   beschnitten, dazu 56px Unschaerfe. Jetzt steht es im Hochkant-Cover-Format (2:3, wie das Cover
   ueberall sonst) in voller Hoehe am linken Rand -- etwa so gross wie es ist, kaum weichgezeichnet,
   nur abgedunkelt -- und laeuft nach rechts in die Modal-Flaeche aus. Breite aus Hoehe per
   aspect-ratio (top+bottom gesetzt, width auto). */
.event-preview-backdrop::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; aspect-ratio: 2 / 3;
  background-image: var(--event-preview-bg-image, none); background-size: cover; background-position: center;
  /* 0.42 statt 0.5 (2026-09): bei hellen Titelbildern standen Uhrzeit/Ort darueber zu kontrastarm */
  filter: blur(3px) brightness(0.42) saturate(1.1);
  -webkit-mask-image: linear-gradient(to right, #000 40%, transparent 100%);
          mask-image: linear-gradient(to right, #000 40%, transparent 100%);
}
/* Alle Kacheln der Event-Ansicht teilen EINE Flaeche (2026-09, "manche sind durchsichtig manche
   nicht"): vorher gab es vier -- deckendes Grau (Funktionsknoepfe), 88% mit Unschaerfe (Teilnehmer,
   Fotos), 72% dunkel (Notiz, Chat) und auf dem Handy durchsichtig mit Unschaerfe (Spiel/Tags). Vorbild
   ist der durchscheinende Handy-Chat: dunkel, ohne Unschaerfe. Ohne Titelbild derselbe Ton deckend.
   Was IN einer Kachel liegt (Spiel-Pille, Medienordner, leere Fotoplaetze), bekommt eine leichte
   Toenung darueber statt einer eigenen deckenden Farbe. */
/* Die Kacheln der Event-Ansicht sind Inhaltskacheln -- sie haengen deshalb an der KACHELfarbe
   (2026-09-16 gefragt: "wie kann ich die farben der hintergruende einstellen in der event vorschau
   wenn kein hintergrundbild da ist?"). Vorher lagen sie auf --zone-bg bzw. --panel-hover und
   folgten damit gar keinem oder dem falschen Regler. */
#event-preview-modal { --event-kachel: var(--inhalt-kachel); --event-kachel-hover: var(--inhalt-kachel-hover); }
/* Ohne Titelbild lag --zone-bg (#0c1015) im Dunkelmodus praktisch auf der Modalflaeche --panel
   (#0b0e13) -- die Kacheln waren nicht mehr auszumachen (2026-09-14 gemeldet: "innerhalb von events
   zum ansehen die kein titelbild haben ist der background fast die gleiche farbe wie die elemente
   selbst"). Eine Stufe hoeher statt gleichauf; mit Titelbild bleibt es beim durchscheinenden Ton. */
/* Ohne Titelbild braucht es keine Sonderregel mehr je Modus: --inhalt-kachel liegt in beiden Modi
   schon eine Stufe ueber der Fensterflaeche. */
#event-preview-modal.event-preview-has-backdrop {
  /* Mit Titelbild durchscheinend, damit das Bild darunter bleibt -- aber in derselben Farbe. */
  --event-kachel: color-mix(in srgb, var(--inhalt-kachel) 72%, transparent);
  --event-kachel-hover: color-mix(in srgb, var(--inhalt-kachel-hover) 80%, transparent);
}
/* Deckende Flaechen wuerden das Bild verdecken -- Kopfzeile und Handy-Aktionszeile sind mit
   Titelbild durchsichtig. OHNE Titelbild ebenfalls: die Kopfzeile ist Teil des Fensters und soll
   keine eigene Farbe tragen (2026-09-16: "in einer event uebersicht ohne titelbild sollte die
   titelleiste keine eigene farbe mehr haben sondern genauso wie der rest sein"). */
#event-preview-modal .modal-header-bar,
#event-preview-modal .event-preview-mobile-actions-row { background: transparent; }
/* Titel, Ersteller, Datum und Uhrzeit/Ort stehen direkt auf dem Bild, das sie oft uebertoente
   (2026-09): volle Textfarbe, ein enger dunkler Schatten als Kontur plus ein weicher Hof, und oben
   ein dunkler Verlauf ueber dem Bild (::after), der hinter Kopfzeile und Datum liegt. */
#event-preview-modal.event-preview-has-backdrop .event-preview-hero-sub { color: var(--text); }
#event-preview-modal.event-preview-has-backdrop :is(#event-preview-title, .event-preview-creator, .event-preview-hero-date, .event-preview-hero-sub) {
  text-shadow: 0 1px 2px rgb(0 0 0 / 0.85), 0 2px 18px rgb(0 0 0 / 0.65);
}
.event-preview-backdrop::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 360px;
  background: linear-gradient(to bottom, rgb(0 0 0 / 0.5), transparent);
}
/* Spiel/Tags-Karte unter den Funktionsknoepfen (siehe layoutEventPreviewMobile()). Frueher unten
   buendig mit dem Hochkant-Cover daneben -- das ist seit 2026-09-11 entfallen, daher fester Abstand. */
/* Formular: dasselbe unscharfe Titelbild wie in der Ansicht, sobald eins gewaehlt ist (siehe
   syncEventFormBackdrop in initEventPreview()). Kopfzeile und Kategorie-Reiter werden dafuer
   durchsichtig; die Eingabefelder behalten ihren eigenen Grund und bleiben lesbar. */
#event-modal .modal { isolation: isolate; }
#event-modal.event-form-has-backdrop .event-preview-backdrop { display: block; }
#event-modal.event-form-has-backdrop .modal-header-bar,
#event-modal.event-form-has-backdrop .modal-category-tabs { background: transparent; }
/* Kleines Kopfzeilen-Titelbild -- nur auf Mobile sichtbar (siehe @media 720px weiter unten), wo es
   den grossen Cover-Bereich im Body ersetzt (Nutzer-Feedback: "das ist zu groß am handy"). Am
   Desktop bleibt der bestehende grosse Cover/Slider-Bereich unveraendert, dieses Element daher
   hier unconditional versteckt -- MUSS vor dem @media-Block stehen, sonst wuerde die dortige
   display:block-Regel bei gleicher Selektor-Spezifitaet durch Quellreihenfolge wieder verloren
   gehen (bereits mehrfach als Bug in dieser Datei aufgetreten). */
.event-preview-header-cover { display: none; width: 40px; height: 40px; border-radius: var(--radius-md); object-fit: cover; flex-shrink: 0; }
/* Nutzer-Feedback: "der chat button sollte sich oben in der menüleiste links neben dem cover bild
   befinden" -- gleiches Kreis-Button-Muster wie .modal-header-close (oben links) und
   .event-preview-header-cover (oben rechts), sitzt direkt links neben dem Cover-Thumbnail. Muss aus
   demselben Grund wie .event-preview-header-cover VOR dem @media-Block unconditional versteckt
   sein (bekanntes Bug-Pattern in dieser Datei). Oeffnet den Chat als eigene Vollbildseite, siehe
   setEventPreviewMobileTab() in app.js + .event-preview-mobile-tab-chat weiter unten. */
.event-preview-header-chat-btn { display: none; }
/* Mobiles Notiz-Zeile-Layout (siehe index.html) -- am Desktop bleibt die Zeile leer (der Notiz-
   Block wandert per layoutEventPreviewMobile() an seinen urspruenglichen Platz zurueck), muss aber
   aus demselben Grund wie .event-preview-header-cover VOR dem @media-Block unconditional versteckt
   sein (bekanntes Bug-Pattern in dieser Datei). */
.event-preview-mobile-notiz-row { display: none; }
/* Nutzer-Feedback (Folgerunde): "schiebe das datum nach links neben das titelbild rechts ...
   darunter aber Rechts neben das Titelbild den chat button in dann der verbleibenden breite" --
   Cover + eine schmale Seitenspalte (Kalender-Blatt, Spiel/Tags-Zeile, Chat-Button gestapelt)
   nebeneinander, ersetzt das bisherige Cover-neben-Notiz-Umfliessen. Gleicher Grund fuer das
   unconditional display:none wie oben. */
.event-preview-mobile-cover-row { display: none; }
/* Nutzer-Feedback: "wenn man auf events tippt das in einer schönen optimierten übersicht" -- auf
   dem Handy ist Event-Tippen jetzt der Haupt-Anwendungsfall (Agenda ist der einzige Dashboard-
   Inhalt, siehe .dashboard-widgets-area @media 880px), daher volle-Bildschirm-Ansicht statt einer
   kleinen, von totem Rand umgebenen Modal-Karte -- wirkt wie eine native App-Detailseite statt
   einem Popup. Kopfzeile (Titel/Teilnehmen/Bearbeiten/Schliessen) bleibt dank .modal-with-header's
   bestehendem flex-column+overflow-Aufbau automatisch oben fixiert, waehrend der Body scrollt. */
/* Zeile fuer Dreifach-Toggle + Drei-Punkte-Menü unter der Kopfzeile -- nur auf Mobile sichtbar
   (siehe @media (max-width: 720px) direkt darunter), am Desktop lebt der Toggle stattdessen in der
   Kopfzeile selbst (per JS reparented, siehe layoutEventPreviewMobile() in app.js). MUSS vor dem
   @media-Block stehen, der sie auf "flex" setzt (gleiche Selektor-Spezifitaet, bekanntes
   Bug-Pattern in dieser Datei -- siehe Kommentar bei .event-preview-chat). */
.event-preview-mobile-actions-row { display: none; }
@media (max-width: 720px) {
  #event-preview-modal.modal-overlay { padding: 0; align-items: stretch; }
  /* Bugfix (Nutzer-Feedback: "in der event übersicht sind die fotos abgeschnitten") -- die
     Desktop-Fixhoehe von #event-preview-modal .modal-body (min(640px,76vh) + overflow-y:hidden,
     noetig fuer die 3 unabhaengig scrollenden Spalten, siehe dort) blieb bisher auch auf Mobile
     bestehen, obwohl dort alle Spalten zu EINER gestapelten Seite werden (grid-template-columns:1fr,
     siehe .event-preview-layout oben). Inhalte unterhalb der festen 640px-Grenze (z.B. die Fotos-
     Sektion weit unten in der Medien-Spalte) wurden dadurch abgeschnitten statt normal mitzuscrollen.
     Stellt die geteilte .modal-with-header .modal-body-Basisregel (flex:1, overflow-y:auto) wieder
     her -- exakt das im Kommentar weiter oben ("waehrend der Body scrollt") beschriebene Verhalten. */
  /* Nutzer-Feedback: "unter dem bild widget wenn es ganz unten ankommt etwas freien platz machen
     damit es nicht unten im bildschirm verschwindet" -- 16px wirkte am unteren Rand (v.a. mit der
     Medien-Sektion als letztem Element, siehe order:4 oben) zu knapp, deutlich grosszuegiger. */
  #event-preview-modal .modal-body { flex: 1; overflow-y: auto; padding-bottom: calc(40px + env(safe-area-inset-bottom, 0px)); }
  /* Kopfstreifen und Hintergrundbild auf dem Handy (Basisregeln siehe .event-preview-hero und
     .event-preview-backdrop oben). Der Body scrollt hier als Ganzes, die Spalten duerfen also nicht
     wie am Desktop auf die Resthoehe schrumpfen -- ausser im Chat, der die volle Hoehe fuellen muss.
     Das Bild laeuft von oben nach unten aus ("mobil von oben nach unten blurred und unten quasi mit
     übergang zur hintergrund farbe") und bleibt beim Scrollen stehen, weil es am Modal haengt. */
  #event-preview-modal .event-preview-layout { flex: none; }
  #event-preview-modal.event-preview-mobile-tab-chat .event-preview-layout { flex: 1; min-height: 0; }
  #event-preview-modal.event-preview-mobile-tab-chat .event-preview-hero { display: none; }
  .event-preview-hero { padding: 0 var(--space-1) var(--space-7); }
  .event-preview-hero-date { font-size: var(--fs-3xl); }
  .event-preview-hero-sub { font-size: var(--fs-md); gap: var(--space-2) var(--space-6); }
  /* Hochkant-Bild in voller Breite oben, Hoehe aus der Breite (2:3) -- laeuft nach unten aus. */
  .event-preview-backdrop::before {
    right: 0; bottom: auto; width: 100%;
    -webkit-mask-image: linear-gradient(to bottom, #000 45%, transparent 100%);
            mask-image: linear-gradient(to bottom, #000 45%, transparent 100%);
  }
  #event-preview-modal .modal.modal-xl { max-width: none; width: 100%; height: 100%; max-height: none; border-radius: 0; animation: iosSheetIn var(--duration-slow) var(--ease-sheet) both; }
  /* Nutzer-Feedback: "die event übersicht in fullscreen mit X button oben rechts. und die buttons
     allgemein in der titelleiste schöner anpassen an mobil das man aber alles noch sieht ...
     nimm mobil das cover bild auch bitte oben rechts mit in die titelleiste klein" -- Titelzeile
     (Titel/Datum/Tags) bleibt oben allein stehen, die Aktionsleiste (Teilnehmen/Anpinnen/Teilen/
     Bearbeiten/Cover) rutscht als eigene, horizontal scrollbare Zeile darunter, das Schliessen-X
     wird als klar erkennbarer runder Button oben rechts rausgeloest -- bleibt unabhaengig davon,
     wie lang Titel/Aktionsleiste werden, immer an derselben Stelle erreichbar. */
  /* Nutzer-Feedback (naechste Runde): "bild des events bitte dann rechts nach oben wenn der
     zurück pfeil links dann ist. die weiteren buttons sollten unter die verknüpfung in der
     leiste oben ... bearbeiten sollte ein kleiner button sein" -- Kopfzeile reserviert jetzt auf
     BEIDEN Seiten Platz (links Zurueck-Pfeil, rechts Titelbild), Titel-/Tags-Zeile und
     Aktionsleiste bleiben wie bisher als zwei gestapelte volle Zeilen dazwischen. */
  /* Mobil-Feedback: "mache das mit dem mittleren down button wenn man mobil etwas öffnet weg
     zurück zu einem button links oben für zurück und mache die seiten die sich mobil öffnen
     wieder fullscreen. so dass auch die zurück wischen geste läuft wieder" -- der zwischenzeitlich
     eingefuehrte .mobile-sheet-handle (mittiger Chevron-Down-Griff) ist wieder weg, stattdessen
     reserviert die Kopfzeile wieder BEIDE Seiten (links Zurueck-Pfeil ueber .modal-header-close,
     rechts Titelbild). */
  /* Nutzer-Feedback (Folgerunde): "bitte den titel des events nicht durch den chat button
     abschneiden" -- der bisherige absolut positionierte Chat-Button in der Kopfzeile brauchte
     rechts reservierten Platz (52px); jetzt lebt er als Volle-Breite-Button im Content-Fluss (siehe
     .event-preview-mobile-chat-btn-slot weiter unten), die Kopfzeile braucht rechts also nur noch
     normales Padding statt einer festen Reservierung. */
  #event-preview-modal .modal-header-bar {
    flex-wrap: wrap; position: relative; padding: var(--space-7) var(--space-8) var(--space-6) 52px; gap: var(--space-4);
  }
  #event-preview-modal .event-preview-title-wrap { flex: 1 1 100%; min-width: 0; }
  #event-preview-modal .modal-header-actions {
    flex: 1 1 100%; flex-wrap: nowrap; overflow-x: auto; gap: var(--space-4); padding-bottom: var(--space-1);
  }
  /* Der Zurueck-Pfeil selbst (Position/Groesse/Farbe) kommt jetzt von der geteilten
     .modal-header-close-Regel weiter oben (gilt app-weit fuer alle Header-Bar-Modals) -- hier
     nur noch die auf dieses Modal zugeschnittene Kopfzeilen-Breite/Umbruch. */
  /* Aktions-Buttons kompakter (weniger Innenabstand/Schrift) statt der Desktop-Groesse, damit
     Teilnehmen+Anpinnen+Teilen+Bearbeiten nebeneinander in die Leiste passen, ohne abgeschnitten
     zu wirken. */
  #event-preview-modal .modal-header-actions .text-btn,
  #event-preview-modal .modal-header-actions .primary-btn { padding: var(--space-3) var(--space-5); font-size: var(--fs-md); white-space: nowrap; flex-shrink: 0; }
  #event-preview-modal .modal-header-actions .icon-btn { flex-shrink: 0; }
  /* Der Interessiert/Ich-nehme-teil/Mit-Vorbehalt-Dreifach-Toggle ist als Pill-Segmentgruppe schon
     kompakt genug -- auf Mobile nur etwas engeres Padding statt Desktop-Groesse. */
  #event-preview-modal .event-preview-attendance-toggle { flex-shrink: 0; }
  #event-preview-modal .event-preview-attendance-btn { padding: var(--space-3) var(--space-4); font-size: var(--fs-xs); }
  /* Nutzer-Feedback (Folgerunde): "mache das cover in normaler größe dort hin machen und daneben
     die notiz machen. chat icon dann nach rechts schieben" -- das Cover zieht jetzt in normaler
     (nur etwas verkleinerter) Groesse in den Body um (siehe .event-preview-mobile-cover-row weiter
     unten), die kleine Kreis-Vorschau in der Kopfzeile entfaellt dadurch ersatzlos. */
  #event-preview-modal .event-preview-header-cover { display: none; }
  /* Mobil-Feedback: "der edit button ist irgendwie in der zweiten zeile. ich glaube der ist nicht
     versteckt" -- #event-preview-edit-btn war auf Mobil nie ausgeblendet, obwohl er dort laengst
     ueber den Proxy-Eintrag im Drei-Punkte-Menue erreichbar ist (showEventPreviewMobileMoreMenu()
     in app.js) -- blieb dadurch als einziger sichtbarer Rest von .modal-header-actions (neben dem
     Schliessen-X) in einer eigenen, ansonsten leeren Zeile stehen. !important noetig: JS setzt dort
     eine INLINE style.display je nach can_edit-Recht (gleiches Muster wie #settings-btn). */
  #event-preview-edit-btn { display: none !important; }
  /* Nutzer-Feedback (Folgerunde): "schiebe das datum nach links neben das titelbild rechts, DARUNTER
     der name des games films serie DANEBEN (so wie jetzt auch die tags) DARUNTER aber Rechts neben
     das Titelbild den chat button in dann der verbleibenden breite rechts neben dem titelbild. Unter
     das titelbild die notiz" -- echte zweispaltige Zeile: Cover links (feste Breite), rechts daneben
     eine schmale Spalte (.event-preview-mobile-cover-side-col), die Kalender-Blatt, Spiel/Tags-Karte
     UND Chat-Button gestapelt enthaelt. Weil die Spalte schmaler als die volle Zeilenbreite ist,
     landet das Kalender-Blatt automatisch direkt neben dem Cover (kein "justify-content:space-between"
     mehr noetig, das es zuvor an den rechten Rand der GESAMTEN Zeile gedrueckt hat). */
  /* Nutzer-Feedback (Folgerunde): "jetzt noch so schieben das der chat button auf der selben höhe
     abschließt wie das titelbild" -- align-items:stretch (statt flex-start) streckt die Seitenspalte
     auf die volle Cover-Hoehe, margin-top:auto auf dem Chat-Button (letztes Kind der Spalte, siehe
     weiter unten) schiebt ihn dadurch exakt an den unteren Rand dieser gestreckten Spalte -- also
     buendig mit der Cover-Unterkante. */
  .event-preview-mobile-cover-row { display: flex; align-items: stretch; gap: var(--space-6); margin-top: var(--space-1); margin-bottom: var(--space-8); }
  .event-preview-mobile-cover-side-col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-4); }
  /* Die Spiel/Tags-Karte lebt jetzt IN der Seitenspalte statt als eigene Zeile ueber die volle
     Breite -- kein order:-1 mehr noetig (das war fuer die Reihenfolge unter den Geschwistern der
     AEUSSEREN .event-preview-media-col gedacht; hier drinnen bestimmt einfach die tatsaechliche
     DOM-Reihenfolge, in die layoutEventPreviewMobile() (app.js) Kalender-Blatt/Karte/Chat-Button
     einfuegt). Kein eigener Hintergrund/Innenabstand mehr, fuegt sich dadurch nahtlos in die Spalte. */
  .event-preview-mobile-cover-side-col :is(#event-preview-link-section, #event-preview-infos-section, #event-preview-attendees-section) { background: none; padding: 0; margin: 0; }
  /* Nutzer-Feedback (2026-09-11): "tags sollten mobil auch ... sie haben noch keinen background - wie am
     desktop" -- die innere Karte behaelt Flaeche und Innenabstand wie am Desktop, ist aber nur so breit wie
     ihr Inhalt. Folgerunde: "mobil: mache das bitte linksbündig im moment ist es rechts" -- die frueheren
     Rechtsbuendig-Regeln (Stack, Pille, Tags, Ueberschrift) sind entfallen; Verknuepfung und Tags stehen als
     eigene Abschnitte links untereinander wie alle anderen Abschnitte. */
  .event-preview-mobile-cover-side-col .event-preview-infos-card { width: fit-content; max-width: 100%; }
  /* Randlos auf dem Handy: das Titelbild darf bis in die Bildschirmecken reichen (siehe Desktop-clip-path). */
  .event-preview-backdrop { clip-path: none; }
  /* Die Kachel Teilnehmer/Verknuepfung/Tags (.event-preview-infos-karte, Regeln weiter unten) steht auf dem Handy in
     der Seitenspalte -- dort ohne eigenen Abstand nach oben, die Spalte hat ihr gap. */
  .event-preview-infos-karte { margin-top: 0; }
  /* Nutzer-Feedback (2026-09-11): "wenn es keine beschreibung gibt zeige mir mobil das feld auch gar nicht an" --
     sonst stand nur der Platzhalter "Beschreibung einfügen ..." da; ergaenzen geht auf dem Handy ueber Bearbeiten. */
  .event-preview-mobile-notiz-row:has(.event-preview-notiz-placeholder) { display: none; }
  /* Nutzer-Feedback (Folgerunde): "darunter (neben cover) das chat icon deutlich subtiler" (gedeckte
     Pillen-Optik statt kraeftiger Fuellung) -- Folgerunde: "den chat button auf die gesamte breite
     ziehen in der zeile wo er ist" -- jetzt volle Breite der Seitenspalte statt einer schmalen Pille,
     behaelt aber die gedeckte (nicht kraeftig gefuellte) Farboptik. margin-top:auto schiebt ihn ans
     untere Ende der (durch align-items:stretch am Elternteil) gestreckten Spalte, siehe Kommentar an
     .event-preview-mobile-cover-row oben. Lebt als drittes Kind der Seitenspalte
     (layoutEventPreviewMobile() in app.js verschiebt das SELBE Kopfzeilen-Element hierher -- kein
     Duplikat, keine zweite unread-badge-ID noetig). */
  /* Nutzer-Feedback (Folgerunde): "den button auch in dem gradient machen wie die kalender icons" -- trug danach den
     Akzent-Verlauf. Kehrtwende 2026-09-11: "nicht mehr in der farbe wie das hub machen sondern etwas weniger
     auffällig" -- jetzt die ruhige Kachel-Flaeche wie Teilnehmer/Tags darunter, nur das Symbol in der Akzentfarbe.
     Er steht seitdem oben in der Seitenspalte (layoutEventPreviewMobile()), margin-top:auto (unten festhalten) ist
     damit entfallen. */
  .event-preview-mobile-cover-side-col .event-preview-header-chat-btn {
    display: flex; align-items: center; justify-content: center; gap: var(--space-3);
    /* relative statt static (2026-09): die unsichtbare 44px-Tippflaeche (::after, siehe pointer:coarse
       weiter oben) misst sich am naechsten positionierten Vorfahren -- mit static war das die ganze
       Event-Ansicht, und jeder Tipp irgendwo darin oeffnete den Chat. */
    position: relative; width: 100%; height: auto;
    padding: var(--space-4) var(--space-6); border-radius: var(--radius);
    background: var(--event-kachel); border: none; color: var(--text); font-size: var(--fs-sm); font-weight: var(--fw-semibold);
    cursor: pointer;
  }
  .event-preview-mobile-cover-side-col .event-preview-header-chat-btn .mdi { color: var(--accent); }
  .event-preview-mobile-cover-side-col .event-preview-header-chat-btn:active { background: var(--event-kachel-hover); }
  .event-preview-mobile-cover-side-col .event-preview-header-chat-btn-label { font-size: inherit; font-weight: inherit; }
  .event-preview-mobile-cover-side-col .unread-badge { position: static; margin: 0 0 0 var(--space-2); }
  /* Nutzer-Feedback (Folgerunde): "dann darunter also unter das bild die notiz" -- die Notiz ist
     jetzt ein regulaerer vollbreiter Block (kein Umfliessen mehr noetig, das Cover sitzt nicht mehr
     in dieser Zeile). */
  /* Nutzer-Feedback (weitere Folgerunde): "mache das weiterlesen der notiz bitte weg und mache es
     mobil einfach so lang wie es ist. man kann ja scrollen das passt." -- kein max-height/overflow-
     Clamping mehr, die Notiz wird komplett dargestellt und die Seite scrollt einfach mit. */
  .event-preview-mobile-notiz-row { display: block; }
  /* display:block statt der geerbten Desktop-Regel (flex; flex-direction:column, siehe Basisregel
     weiter unten in dieser Datei). */
  /* Eigene Karte wie Teilnehmer und Fotos darunter: ohne Flaeche ging "Notiz einfügen ..." auf dem
     Handy im Hintergrund unter (2026-09). */
  /* Seit 2026-09-11 traegt die Huelle unter dem Kopf die Flaeche ("mobil solte die überschrift
     'beschreibung' dort auch herausgelöst sein und drüber stehen genau wie bei den anderen sachen") --
     der Block selbst ist durchsichtig, "BESCHREIBUNG" steht darueber wie Teilnehmer und Medien. */
  .event-preview-mobile-notiz-row .event-preview-notiz-block { display: block; min-width: 0; background: none; padding: 0; }
  .event-preview-mobile-notiz-row .event-preview-notiz-header { padding: 0 0 var(--space-4); }
  .event-preview-mobile-notiz-row .event-preview-notiz-karte {
    display: block; background: var(--event-kachel); border-radius: var(--radius); padding: var(--space-5) var(--space-6);
  }
  .event-preview-mobile-notiz-row .event-preview-notiz-placeholder { opacity: 1; }
  .event-preview-mobile-notiz-row .event-preview-notiz-block .event-preview-description {
    flex: none; overflow: visible;
  }
  /* Nutzer-Feedback (Folgerunde): "die neue info box mit spiel und tags sollte unter der teilnehmer
     box sein" -- .event-preview-media-col ist ein Column-Flex-Container (siehe dortige Basisregel),
     die Reihenfolge laesst sich daher rein per CSS-order umsortieren, ohne die Desktop-Reihenfolge
     im Markup anzutasten. Alle sichtbaren Geschwister brauchen einen expliziten Wert (Elemente ohne
     "order" gelten als 0 und wuerden sonst vor jedem positiven Wert einsortiert). */
  /* Folgerunde: "die datum kacheln müssen noch mobil angezeigt werden ... daneben würde ich das
     spiel und tag ding schieben quasi nebeneinander" -- die Info-Karte (jetzt inkl. Kalender-Icon,
     siehe .event-preview-infos-section Regel weiter unten) rutscht dafuer vor die Teilnehmer-Box.
     Notiz-Block bekommt selbst keinen "order"-Wert (bleibt beim Default 0 -- er wird per JS bereits
     an die richtige Stelle im DOM verschoben, siehe layoutEventPreviewMobile()). Spaetere Runde
     ("zeige mir mobil das hier über der notiz an") schiebt die Info-Karte per order:-1 sogar noch
     VOR den Notiz-Block -- sie ist also nicht mehr die kleinste Zahl hier, sondern die Notiz. */
  /* Teilnehmer: frueher hier order:2 fuer die Handy-Spalte -- seit 2026-09-11 stehen sie in der Kachel
     Teilnehmer/Verknuepfung/Tags (.event-preview-infos-karte) und muessen dort OBEN stehen ("bitte mache teilnehmer
     in dem bereich ganz nach oben"); die alte Regel schob sie in der Flex-Kachel ans Ende. */
  #event-preview-linked-section { order: 3; }
  #event-preview-media-section { order: 4; }
  /* Nutzer-Feedback (Folgerunde, mit Screenshot): "mache mir das mobil dann gleich so das das
     nicht einen background hat sondern in zwei 'kacheln' unterteilt ist" -- statt der einen
     gemeinsamen .event-preview-section-Kartenflaeche um Kalender-Icon(e) UND Spiel/Tags-Zeile
     bekommen beide jetzt ihre eigene Kachel (background/radius/padding einzeln), die Sektion
     selbst wird nur noch zum reinen Flex-Container ohne eigene Flaeche. Der ID-Selektor
     "#event-preview-infos-section" ist spezifischer als die Klassenregel ".event-preview-section"
     und gewinnt daher auch ohne !important. */
  /* Nutzer-Feedback (Folgerunde): "kannst du bitte noch den background bei den kacheln des datums
     weg nehmen und auch bei den tags / spielnamen. dann bitte die tag spielnamen leiste oben
     ausrichten (selbe höhe wie die kacheln anfagen.)" -- gleiche Kachel-Hintergruende wie oben
     eingefuehrt entfallen wieder (deckt sich jetzt mit der Desktop-Optik, die ihre Hintergruende
     ebenfalls entfernt hat, siehe #event-preview-infos-section @media min-width:721px weiter
     unten). Hintergrund/Reihenfolge der Spiel/Tags-Karte selbst siehe jetzt
     .event-preview-mobile-cover-side-col #event-preview-infos-section weiter oben (lebt seit der
     Folgerunde in der Seitenspalte neben dem Cover statt als eigene volle Zeile). */
  /* Nutzer-Feedback: "interesse teilname vorbehalt buttons bitte nicht in die titelleiste damit
     die schmal bleibt, quasi eins drunter" -- eigene, volle Zeile direkt unter der Kopfzeile (siehe
     index.html), Dreifach-Toggle wird per JS hierher verschoben (layoutEventPreviewMobile()) und
     nimmt den verfuegbaren Platz per flex:1 auf jedem Segment ein, das Drei-Punkte-Menü bleibt
     kompakt daneben. */
  .event-preview-mobile-actions-row {
    display: flex; align-items: center; gap: var(--space-4); padding: 0 var(--space-7) var(--space-6); background: var(--panel-2);
  }
  .event-preview-mobile-actions-row .event-preview-attendance-toggle { flex: 1; }
  .event-preview-mobile-actions-row .event-preview-attendance-btn { flex: 1; text-align: center; }
  #event-preview-mobile-more-btn { flex-shrink: 0; }
  /* Nutzer-Feedback: "den direkt bearbeiten knopf der notizen bitte mobil ausblenden" -- die
     Notiz bleibt weiterhin ueber das volle Bearbeiten-Formular (Drei-Punkte-Menue) editierbar,
     nur der schnelle Inline-Stift-Button direkt an der Notiz-Ueberschrift entfaellt auf Mobile. */
  #event-preview-notiz-edit-btn { display: none; }
}
/* Mobil-Feedback: "mache bitte bei den events aus dem chat und der event ansicht unten wo der chat
   geöffnet werden kann zwei tabs zum hin und her schalten dann muss man nur eine unterseite
   öffnen. mache generell da wo es chats gibt das zwei tabs system" -- generischer Segmented-Tab-
   Umschalter, verwendet ueberall dort, wo Mobile ein "Details/Info neben Chat"-Layout in zwei Tabs
   statt permanent gestapelter Bloecke aufteilt (Event-Vorschau: Details/Chat, Rooms: Chat/Info).
   Ersetzt bei der Event-Vorschau das vorherige "Chat öffnen"-Sheet (eigener Bottom-Sheet-Screen
   ueber der Vorschau). Nur auf Mobile sichtbar, am Desktop stehen die jeweiligen Spalten ohnehin
   immer nebeneinander. */
/* Bugfix: "mobile-tab-bar" kollidierte mit der bereits bestehenden app-weiten unteren
   Navigationsleiste (siehe .mobile-tab-bar weiter unten in dieser Datei, position:fixed +
   Pillen-Optik) -- gleicher Klassenname, spaetere Regel gewann bei der Kaskade und riss diesen
   Tab-Umschalter faelschlich aus dem normalen Fluss (position:fixed), wodurch weder die Fuellfarbe
   noch die Position stimmten und das schwebende Element vermutlich auch Touch-Scroll-Gesten
   darunter blockierte. Eigener, eindeutiger Klassenname behebt beides. */
.mobile-pane-tabs { display: none; }
@media (max-width: 720px) {
  /* Mittig und ohne eigene Flaeche (2026-09-14: "das chat und info button ist zwar gut aber nicht
     mittig und hat einen komischen background") -- die beiden Knoepfe stehen als Segment-Paar in
     der Mitte statt als zwei ueber die ganze Breite gedehnte Flaechen auf einem eigenen Balken. */
  .mobile-pane-tabs {
    display: flex; justify-content: center; gap: var(--space-2); padding: var(--space-3) var(--space-7) var(--space-5);
    background: none;
  }
  .mobile-pane-tab-btn {
    flex: 0 1 140px; display: flex; align-items: center; justify-content: center; gap: var(--space-3);
    padding: var(--space-3) var(--space-6); border-radius: var(--radius-full); border: none; background: var(--knopf);
    color: var(--text-dim); font-size: var(--fs-md); font-weight: var(--fw-semibold); cursor: pointer;
  }
  .mobile-pane-tab-btn.active { background: var(--accent); color: var(--text-on-color); }
  .mobile-pane-tab-btn .unread-badge { position: static; margin-left: var(--space-1); }
}
/* Nutzer-Feedback: "event ansehen / bearbeiten / erstellen muss auch fullscreen werden und
   angepasst" -- gleiches Muster wie #event-preview-modal direkt darueber (auch #event-modal ist
   bereits .modal-with-header, Kopfzeile bleibt also automatisch fixiert). ":not(.catalog-quick-
   add)" laesst das kleine Spiel/Film/Serie-Such-Popup (siehe #event-modal.catalog-quick-add
   weiter unten) bewusst unangetastet -- das ist ein eigener, absichtlich kompakter Modus, kein
   "richtiges" Event erstellen/bearbeiten. */
@media (max-width: 720px) {
  #event-modal.modal-overlay:not(.catalog-quick-add) { padding: 0; align-items: stretch; }
  #event-modal:not(.catalog-quick-add) .modal.modal-wide { max-width: none; width: 100%; height: 100%; max-height: none; border-radius: 0; }
}
/* Nutzer-Feedback: "die screenshot folder gerne fullscreen anzeigen und die ganzen buttons
   ausblenden auser hochladen und den titel in der leiste oben" -- gleiches Fullscreen-Muster wie
   oben; die Mehrfachauswahl-Buttons bleiben ausgeblendet (Hochladen (#media-detail-add-btn) und
   der Zurueck-Pfeil bleiben als einzige Aktionen). !important noetig, da #media-detail-bulk-
   delete-btn sein inline style.display per JS umschaltet, was eine normale CSS-Regel ohne
   !important sonst schlagen wuerde. */
@media (max-width: 720px) {
  #media-game-detail.modal-overlay { padding: 0; align-items: stretch; }
  /* Auch mobil ohne das iOS-Sheet (2026-09-15) -- dieselbe ruhige Einblendung wie ueberall. */
  #media-game-detail .modal.modal-xl { max-width: none; width: 100%; height: 100%; max-height: none; border-radius: 0; animation: fadeInUp var(--duration-base) ease both; }
  /* Nutzer-Feedback: "die bar [Titel/Buttons des Medien-Ordners] ist im vergleich zur sammlung
     oben angeheftet und soll auch nach unten [verschwinden koennen]" -- die geteilte .modal-with-
     header-Basisregel haelt die Kopfzeile bewusst FEST oben (nur .modal-body scrollt darunter,
     siehe dortiger Kommentar "Speichern/Schließen immer sichtbar"). Sammlung ist dagegen keine
     Modal-Kopfzeile, sondern normaler Seiteninhalt, der beim Scrollen ganz normal mitwandert. Auf
     Mobile soll sich der Medien-Ordner genauso verhalten: das ganze Popup (statt nur .modal-body)
     wird zum Scroll-Container, die Kopfzeile ist dadurch nur noch das erste Element im normalen
     Textfluss und scrollt beim Runterscrollen mit aus dem Bild, statt fixiert zu bleiben. */
  #media-game-detail .modal.modal-with-header { max-height: none; overflow-y: auto; }
  #media-game-detail .modal-body { flex: none; overflow-y: visible; }
  /* Nutzer-Feedback (Folgerunde): Download/Titelbild anpassen/Teilen/Event öffnen sind jetzt im
     #media-detail-options-btn-Dropdown zusammengefasst statt eigener Buttons -- der bleibt hier
     bewusst sichtbar (der Download-Button war es vorher auch schon), nur select-btn/bulk-delete-btn
     (Mehrfachauswahl) bleiben auf Mobile ausgeblendet. select-btn steckt inzwischen im
     .media-detail-header-stack -- display:none auf dem Button selbst reicht weiterhin, der Stack
     zeigt dann einfach nur noch die Funktionen-Zeile. */
  #media-detail-bulk-delete-btn,
  #media-detail-select-btn { display: none !important; }
  /* Nutzer-Feedback: "die titelleiste ist noch verschoben ... das medien hochladen kann auch nur
     ein kleiner plus button sein" -- Ursache war die generische ".modal-header-bar{flex-wrap:wrap}"
     Mobile-Regel (siehe dort, greift bei Platzmangel app-weit), die bei "+ Medien hochladen" als
     letztem verbleibenden Aktions-Button auf eine eigene Zeile umbrach, Zurueck-Pfeil/Cover/Titel
     dadurch wie eine eigene Zeile darueber wirkten. Mit nur noch einem kleinen Icon-Button (wie
     #media-add-btn auf der Medien-Hauptseite) passt die ganze Kopfzeile wieder auf eine Zeile,
     nowrap + Titel-flex:1 erzwingt das explizit (Titel ellipsiert statt umzubrechen). */
  #media-game-detail .modal-header-bar.media-detail-header { flex-wrap: nowrap; }
  #media-game-detail .wiki-header-titles { flex: 1; }
  #media-detail-add-btn {
    width: 34px; height: 34px; padding: 0; border-radius: var(--radius-md);
    display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  }
  #media-detail-add-btn .media-detail-add-label { display: none; }
  /* Gleiches Kollaps-Muster wie #media-detail-add-btn direkt darueber -- sonst wird die
     Kopfzeile mit ausgeschriebenem "Funktionen"-Label auf Mobile zu eng. select-btn ist ohnehin
     ausgeblendet (siehe oben), der Stack zeigt auf Mobile daher nur noch den Funktionen-Button. */
  .media-detail-header-stack { width: 34px; }
  .media-detail-stack-btn { width: 34px; height: 34px; padding: 0; }
  .media-detail-stack-btn span { display: none; }
}
.slider-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  background: var(--on-image-bg); border: none; color: var(--text-on-color);
  width: 32px; height: 32px; border-radius: 50%; cursor: pointer; font-size: var(--fs-xl);
}
.slider-prev { left: 8px; }
.slider-next { right: 8px; }
/* Ein kleiner gruener Haken vor der Beschriftung zeigt, dass eine Option an ist (2026-09-15:
   "mache an jedes setting (button) der aktiv ist einen gruenen kleinen haken vorne dran. wirklich
   nicht zu gross sondern subtil") -- die Faerbung des Knopfs bleibt wie sie war. */
.event-form-options .pin-toggle-btn:is(.pinned, .recurring-active, .countdown-active, .private-active, .folder-active)::before {
  content: "\F012C"; font-family: "Material Design Icons"; font-size: var(--fs-md);
  color: var(--success); line-height: 1; margin-right: calc(var(--space-2) * -1);
}
.pin-toggle-btn.pinned { background: color-mix(in srgb, var(--highlight) 25%, transparent); border-color: var(--highlight); color: var(--highlight); }
.pin-toggle-btn.recurring-active { background: color-mix(in srgb, var(--success) 25%, transparent); border-color: var(--success); color: var(--success); }
/* Countdown und "Privat" (2026-09-14) -- eingeschaltet in der Akzent- bzw. Warnfarbe, gleiche
   Machart wie die beiden Zustaende darueber. */
.pin-toggle-btn.countdown-active { background: var(--accent-tint); border-color: var(--accent-strong); color: var(--accent-light); }
.pin-toggle-btn.private-active { background: color-mix(in srgb, var(--highlight) 22%, transparent); border-color: var(--highlight); color: var(--highlight); }
.field-label { display: flex; align-items: center; gap: var(--space-3); font-weight: var(--fw-bold); color: var(--text); }
.field-label .mdi { font-size: var(--fs-lg); color: var(--text-dim); }
/* Nutzer-Feedback: "Heute Button in Datum leiste integrieren (wie chat emoji button)" -- gleiches
   Overlay-Muster wie .chat-input-emoji-btn/.chat-input-wrap (absolut positioniert im Eingabefeld
   statt als eigener Button daneben). Nutzer-Feedback (Folgerunde): "kannst du den heute button
   ändern auf text 'Heute'" -- zeigt jetzt wieder den Text statt nur eines Kalender-Icons, dafuer
   mit eigenem (dezentem) Hintergrund statt komplett transparent, damit er als Button lesbar bleibt. */
.date-with-today { position: relative; }
.date-with-today input { width: 100%; padding-right: 58px; box-sizing: border-box; }
/* Zwei Knoepfe im Feld (2026-09-14: "neben dem heute button noch ein release button") -- sie
   stehen zusammen in einer kleinen Leiste rechts im Feld, das Feld macht dafuer mehr Platz frei,
   sobald der Release-Knopf sichtbar ist. */
.date-feld-knoepfe { position: absolute; right: 4px; top: 50%; transform: translateY(-50%); display: flex; align-items: center; gap: var(--space-2); }
.date-feld-knoepfe .date-today-btn { position: static; transform: none; }
.date-feld-knoepfe .date-today-btn:active { transform: scale(0.92); }
.date-with-today.hat-release input { padding-right: 124px; }
/* Auf dem Handy stehen "Release" und "Heute" untereinander (2026-09-15: "mache den release und
   heute button am handy uebereinander der schneidet alles ab") -- nebeneinander frassen sie 124px
   der 191px breiten Zelle, das Datum selbst war abgeschnitten. Das Feld waechst dafuer in die
   Hoehe, die Knoepfe bleiben rechts darin. */
@media (max-width: 880px) {
  /* Die Hoehe braucht die ID: ".modal :is(input, select)" setzt min-height (gleiche Kaskade, aber
     hoehere Spezifitaet als eine reine Klassenregel). */
  #event-modal .date-with-today.hat-release input { padding-right: 74px; min-height: 58px; }
  .date-with-today.hat-release .date-feld-knoepfe { flex-direction: column; gap: var(--space-1); align-items: stretch; right: var(--space-2); }
  .date-with-today.hat-release .date-feld-knoepfe .date-today-btn { width: 100%; height: 22px; }
}
/* "Release" sieht aus wie "Heute" daneben (2026-09-15: "der release button sollte nicht gelb sein
   sondern so wie der heute button") -- vorher trug er die volle Akzentfarbe, die in einer
   Organisation mit gelbem Akzent deutlich heraus stach. Er erbt damit .date-today-btn. */
.date-today-btn {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  background: var(--knopf); border: 1px solid var(--border); color: var(--text-dim); cursor: pointer;
  height: 24px; display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-xs); font-weight: var(--fw-semibold); border-radius: var(--radius-sm); padding: 0 var(--space-4);
}
.date-today-btn:hover { background: var(--panel-hover); color: var(--text); }
.date-today-btn:active { transform: translateY(-50%) scale(0.92); }
.event-preview-meta { color: var(--text-dim); font-size: var(--fs-md); margin-top: var(--space-3); display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--space-2) var(--space-8); }
.event-preview-meta a { color: var(--link-accent, var(--accent)); }
.event-preview-meta-item { white-space: nowrap; }
.event-preview-meta-item .mdi { font-size: var(--fs-lg); vertical-align: -1px; }
/* R38 Phase 7: .game-preview-tagline/.game-preview-link-item entfernt -- gehoerten exklusiv zum
   jetzt entfernten #game-preview-modal/#movie-preview-modal (Steam-/TMDB-Kurzbeschreibung, externer
   Link). Das vereinheitlichte Event-Vorschau-Popup nutzt stattdessen #event-preview-external-section. */
/* Nutzer-Feedback: "bitte blende in der event übersicht am desktop auch die fetched beschreibung
   aus. die brauchen wir hier nicht" -- war bisher nur auf Mobile ausgeblendet (siehe @media
   max-width:720px weiter oben, "die gefetchten beschreibungen lassen wir in der mobilansicht ganz
   weg"), gilt jetzt ueberall. !important weiterhin noetig, da openEventPreview() (app.js) den
   Abschnitt per Inline-style.display="" einblendet, sobald externe Beschreibung vorhanden ist. */
#event-preview-external-section { display: none !important; }
.event-preview-description-row { display: flex; align-items: flex-start; gap: var(--space-4); margin-top: var(--space-7); }
.event-preview-description-row .mdi { flex-shrink: 0; font-size: var(--fs-xl); margin-top: var(--space-1); color: var(--text-dim); }
/* Dieselbe Groesse wie in der Agenda-Karte (2026-09-15: "mache die groesse des beschreibungstextes
   allgemein immer gleich gross ... ich mag es wie es von der groesse ist wenn es zweizeilig ist"). */
.event-preview-description { flex: 1; min-width: 0; color: var(--text-dim); font-size: var(--fs-sm); line-height: 1.4; overflow-wrap: anywhere; }
/* Markdown-Rendering wrapt Text in <p>, das per Browser-Default oben Rand hat -- neben dem
   flex-start-ausgerichteten Icon liess das den Text sichtbar unter statt neben dem Icon
   beginnen. Nur erstes/letztes Kind zuruecksetzen, damit mehrere Absaetze weiterhin Abstand
   zueinander behalten. */
.event-preview-description > *:first-child { margin-top: 0; }
.event-preview-description > *:last-child { margin-bottom: 0; }
.event-preview-description a, .boards-notes-content a, #wiki-editor-mount a, .agenda-desc a {
  color: var(--link-accent, var(--accent));
}
/* Karten-Hintergrund fuer die einzelnen Vorschau-Abschnitte (Funktionen, Beschreibung, Teilnehmer,
   Gefundene Links, Verknuepft, Fotos, Notiz) -- vorher standen sie unterlegt-frei direkt auf der
   Modal-Flaeche, dadurch wirkte die Vorschau "zu leer"/unstrukturiert (Feedback). var(--panel-2)
   hebt sie sichtbar vom var(--zone-bg)-Spaltenhintergrund ab (siehe .event-preview-media-col/
   -notiz-col weiter unten) -- gleiches "Bubble auf Zone"-Prinzip wie die Chat-Nachrichten-Bubbles
   auf var(--zone-bg) im Chat daneben. */
.event-preview-section {
  margin-top: var(--space-7); background: var(--event-kachel, var(--panel-2)); border-radius: var(--radius); padding: var(--space-5) var(--space-6);
}
/* Gleiche Text-/Icon-Groesse wie die Meta-Zeile (Datum/Ort) darueber -- vorher deutlich
   kleiner (0.78rem ohne eigene Icon-Regel), wirkte im Vergleich zum Rest der Vorschau
   unausgewogen. */
.event-preview-section-label { margin: 0 0 var(--space-3); font-size: var(--fs-md); font-weight: normal; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.03em; }
/* Nutzer-Feedback: "umschaltbaren button daneben auf dem steht ich nehme teil / mit vorbehalt" --
   gleiches Segmented-Toggle-Grundmuster wie .wiki-mode-toggle (Edit/Read), nur zwei inhaltliche
   Optionen statt Modi. Kein aktiver Zustand = beide Segmente unausgewaehlt (weder attending noch
   tentative gesetzt), ein erneuter Klick auf das bereits aktive Segment loescht den Status wieder
   (siehe wireEventPreviewAttendanceToggle() in app.js). */
.event-preview-attendance-toggle {
  display: flex; align-items: center; gap: var(--space-1); background: var(--tint);
  border-radius: var(--radius-full); padding: var(--space-1); flex-shrink: 0;
}
.event-preview-attendance-btn {
  border: none; background: transparent; color: var(--text-dim); font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  padding: var(--space-3) var(--space-6); border-radius: var(--radius-full); cursor: pointer; transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
  white-space: nowrap;
}
.event-preview-attendance-btn:hover { color: var(--text); }
.event-preview-attendance-btn.active[data-status="interested"] { background: var(--accent); color: var(--text-on-color); }
.event-preview-attendance-btn.active[data-status="attending"] { background: var(--success); color: var(--on-success); }
.event-preview-attendance-btn.active[data-status="tentative"] { background: var(--warning); color: var(--on-warning); }
.event-preview-section-label .mdi { font-size: var(--fs-lg); vertical-align: -1px; }
/* Nutzer-Feedback: "mache die anderen überschriften auch weg (idgb und teilnehmer) -- mache aber
   bei teilnehmer eine gesamtzahl oben rechts einfach hin" -- ersetzt die vorherige "Teilnehmer
   (n)"-Ueberschrift durch ein reines Zahlen-Badge in der Kartenecke, position:relative auf der
   Section noetig als Bezugsrahmen fuer das absolut positionierte Badge.
   Nutzer-Feedback (Folgerunde): "kannst du bitte noch die zahl der gesamt teilnehmer von oben
   rechts nach oben links und weiter in die ecke schieben, das es vllt sogar etwas über die kachel
   an der ecke hinaus steht" -- jetzt oben links mit negativem Versatz, haengt dadurch bewusst
   etwas ueber Karten-Rand/-Ecke hinaus (gleiches Ueberhang-Muster wie .icon-btn-badge an anderer
   Stelle der App, nur oben-links statt oben-rechts gespiegelt). */
/* Seit 2026-09-11 an der inneren Karte (Ueberschrift steht ueber ihr), damit der Kreis an deren Ecke haengt. */
.event-preview-attendees-card { position: relative; }
.event-preview-attendees-count-badge {
  position: absolute; top: -6px; left: -6px;
  min-width: 20px; height: 20px; padding: 0 var(--space-3);
  border-radius: var(--radius-full); background: var(--accent); color: var(--text-on-color);
  font-size: var(--fs-xs); font-weight: var(--fw-bold); line-height: 20px; text-align: center;
  border: 1.5px solid var(--panel);
}
.event-preview-attendees { display: flex; flex-direction: column; gap: var(--space-5); }
/* Nutzer-Feedback: "das interessiert widget sollte einfach untergliedert sein in teilnehmer
   vorbehalt und interessiert" -- eine kleine Untergruppen-Ueberschrift pro Zusage-Art statt einer
   flachen, undifferenzierten Pill-Liste. */
.event-preview-attendee-group-label { font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: var(--space-2); margin-left: var(--space-5); }
.event-preview-attendees-row { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-left: var(--space-5); }
/* Nutzer-Feedback: "mache auf der linken seite eine extra rubrik event infos mit Datum uhrzeit,
   spiel/film/serie und tags" -- Datum/Uhrzeit, verknuepfte Spiel/Film/Serie-Pille und Tags standen
   vorher kompakt nebeneinander in der Kopfzeile (.event-preview-header-row), stehen jetzt als
   eigene Karte. flex+gap statt einzelner Margins, da .linked-media-pill je nach Event verknuepft/
   unverknuepft ein-/ausgeblendet wird (display:none) und ein reiner margin-bottom dann eine
   sichtbare Luecke haette hinterlassen. Kein eigener Ueberschrift-Header mehr (Nutzer-Feedback
   Folgerunde: "mache die überschrift event infos weg"), daher auch keine .event-preview-section-
   label hier -- die Karte ist selbsterklaerend durch ihren Inhalt (Kalender-/Pin-Icons). */
:is(#event-preview-link-section, #event-preview-infos-section) .event-preview-infos-card { display: flex; flex-direction: column; gap: var(--space-4); }
/* Nutzer-Feedback (mehrere Kehrtwenden): stand zuerst als eigene Karte unter dem Cover, wanderte
   dann in die schmale Funktionen-Spalte neben Pin/Share, jetzt zuletzt: "die tags und das spiel
   film serie relase ding wandern in einen eigenen bereich unter die medien" -- eigener Abschnitt
   NACH der Medien-Sektion (siehe index.html), kein Umzug in die Funktionen-Spalte mehr noetig,
   daher auch keine Spalten-spezifischen Ueberschreibungen mehr (volle Spaltenbreite verfuegbar,
   die Basisregeln fuer .event-preview-infos-media-row/.linked-media-pill reichen jetzt aus).
   "es sollte nur der background auf dem event übersicht an sich entfernt sein nicht von den
   items" -- die Sektion selbst bleibt ohne Karten-Hintergrund (ID-Selektor gewinnt gegen die
   .event-preview-section-Basisregel), Tags/Pille haben aber wieder ihren eigenen Hintergrund
   (siehe .tag-chip-Inline-Style in app.js sowie .linked-media-pill weiter unten).
   Nutzer-Feedback (weitere Kehrtwende): "mach wieder einen gemeinsamen background zusätzlich für
   die release und tag area" -- die Sektion bekommt jetzt wieder die geerbte .event-preview-section-
   Kartenflaeche ZUSAETZLICH zu den weiterhin eigenen Item-Hintergruenden von Tags/Pille, statt
   background:none. */
:is(#event-preview-link-section, #event-preview-infos-section) .event-preview-infos-card { padding: var(--space-5) var(--space-6); }
/* Ueberschriften ueber Optionen, Teilnehmer und Tags (Nutzer-Feedback 2026-09-11: "mache in der event
   übersicht noch die überschriften über die buttons, teilnehmer und tags") -- dieselben Werte wie die
   Koepfe BESCHREIBUNG/CHAT/MEDIEN. Die Spiel/Tags-Karte hatte frueher bewusst keine ("mache die
   überschrift event infos weg"), die Teilnehmer-Karte auch nicht ("mache die überschrift teilnehmer weg");
   beides ist mit diesem Wunsch ueberholt. */
.event-preview-section-label.event-preview-kopf {
  margin: 0 0 var(--space-4); font-size: var(--fs-2xs); font-weight: var(--fw-semibold); color: var(--text-dim);
  letter-spacing: 0.04em; text-transform: uppercase;
}
/* Huelle + Karte (Folgerunde 2026-09-11: Ueberschriften "herausgelöst von den kacheln also darüber stehend
   wie bei datum usw. in der event bearbeiten maske. bspw. ist teilnehmer noch in der kachel selbst") --
   Teilnehmer, Verknuepfung & Tags und Medien: das Element mit der ID ist die Huelle (Abstand zum vorigen
   Abschnitt, wird ein-/ausgeblendet und auf dem Handy umgehaengt), die Karte liegt darin ohne eigenen
   oberen Abstand. Beschreibung und Chat sind ganze Spalten mit Kopfleiste, keine Kacheln -- deren Koepfe
   bleiben in der Spalte. */
.event-preview-block { margin-top: var(--space-7); }
.event-preview-block > .event-preview-section, .event-media-tile-card { margin-top: 0; }
/* Die Ueberschriften der linken Spalte stehen ohne Karte direkt auf dem Titelbild-Hintergrund -- gleicher
   Schatten wie Datum und Titel dort, sonst verschwand die graue Schrift auf hellen Bildern. */
#event-preview-modal.event-preview-has-backdrop .event-preview-media-col :is(.event-preview-kopf, .event-media-tile-head .event-preview-section-label, .event-preview-mobile-notiz-row .event-preview-notiz-header .event-preview-section-label) {
  color: var(--text); text-shadow: 0 1px 2px rgb(0 0 0 / 0.85), 0 2px 18px rgb(0 0 0 / 0.65);
}
.linked-media-pill-icon { font-size: var(--fs-md); flex-shrink: 0; }
/* Nutzer-Feedback (Folgerunde): "mache die tags neben das spiel in die gleiche zeile" -- Pille und
   Tags waren als eigene Flex-Kinder von #event-preview-infos-section jeweils eine eigene Zeile,
   jetzt gemeinsam in einer Reihe. flex-wrap noetig, da beide zusammen bei langen Tag-Listen breiter
   als die Spalte werden koennten. */
.event-preview-infos-media-row { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
/* Nutzer-Feedback (Folgerunde): "den titel des spiels nach oben rechts und darunter release machen
   und darunter die tags" -- Stack-Wrapper um Pille/Release/Tags (siehe index.html), damit sie mobil
   als Spalte NEBEN dem Kalender-Icon stehen koennen (siehe @media max-width:720px weiter unten).
   Am Desktop bleibt display:contents -- die Kinder verhalten sich weiterhin wie direkte Flex-Items
   dieser Zeile, unveraendertes Desktop-Layout (Pille links, Tags rechts daneben in einer Reihe). */
.event-preview-linked-media-stack { display: contents; }
/* Nutzer-Feedback (Folgerunde): "event übersicht desktop: bitte füge hier auch das release hinzu.
   am besten unter dem titel des films serie. auch hier kombinieren" -- das Release-Datum war in
   einer frueheren Runde am Desktop bewusst entfernt worden (separate Zeile neben der Pille wirkte
   redundant); jetzt als zweite Zeile INNERHALB der Pille (.linked-media-pill-release, siehe weiter
   unten) wieder da, auf Desktop UND Mobil identisch -- keine eigene @media-Regel mehr noetig. */
/* Medien-Spalte (alte Spalte 1+4 zusammengelegt): festes Cover-Bild oben, darunter Funktionen/
   Beschreibung/Teilnehmer/Gefundene-Links/Verknuepft/Fotos gestapelt. */
.event-preview-media-col { min-width: 0; display: flex; flex-direction: column; }
/* Nutzer-Feedback: "das titelbild wirklich hochkant machen ... dann können wir rechts daneben die
   funktions buttons machen untereinander" -- Cover und Funktionen-Liste stehen jetzt nebeneinander
   in einer Zeile (Cover schmal/hochkant links, Funktionen rechts daneben) statt Cover volle Breite
   mit Funktionen darunter. */
/* align-items:stretch streckt .event-preview-funktionen auf die volle Cover-Hoehe. Daran hing frueher
   das Kalender-Icon am Cover-Unterrand; seit das Datum gross im Kopfstreifen steht
   (.event-preview-hero), ist stretch nur noch die harmlose Voreinstellung. */
.event-preview-cover-funktionen-row { display: flex; gap: var(--space-6); align-items: stretch; }
/* Nutzer-Feedback: "die bearbeiten anpinnen etc buttons sollten in einem drei punkte menü neben
   dem interesse etc button sein" -- die Funktionen-Liste (Bearbeiten/Anpinnen/Teilen/Hochladen)
   ist auf Mobile jetzt AUSSCHLIESSLICH ueber #event-preview-mobile-more-btn erreichbar (proxy-
   klickt dieselben, hier nur versteckten Buttons, siehe showEventPreviewMobileMoreMenu() in
   app.js) -- die ganze Zeile (nicht nur die Buttons) wird ausgeblendet, sonst bliebe eine leere,
   Platz fressende Zeile stehen (das Cover direkt daneben ist ja bereits oben ausgeblendet).
   MUSS nach der unconditionalen Basisregel direkt darueber stehen (gleiche Selektor-Spezifitaet,
   sonst gewinnt bei <=720px durch spaetere Quellreihenfolge wieder deren "display:flex" --
   bekanntes Bug-Pattern in dieser Datei, siehe gleichlautender Kommentar bei .event-preview-chat). */
@media (max-width: 720px) {
  .event-preview-cover-funktionen-row { display: none; }
}
/* Das Hochkant-Cover (.event-preview-cover) samt Platzhalter ist seit 2026-09-11 entfallen
   (Nutzer-Feedback: "die kachel titelbild hinzufügen ... bitte mache nur den kleinen button"). */
/* Nutzer-Feedback: "mache bei den funktionen die überschrift und den hintergrund weg und schiebe
   sie nach oben" -- kein .event-preview-section mehr auf diesem Element (siehe index.html), daher
   hier bewusst KEIN background/padding mehr; nur noch Flex-Groesse + Ausrichtung neben dem Cover. */
.event-preview-funktionen { flex: 1; min-width: 0; margin-top: 0; display: flex; flex-direction: column; }
/* Optionen-Kachel (Nutzer-Feedback 2026-09-11: "kannst du von optionen bis tags ... einen durchgehenden background
   als kachel machen wie bei chat usw. dann ist das nicht so unruhig"). Folgerunde: "trenne dafür aber optionen auf
   dem desktop raus und mache es als eigenen background" -- seitdem nur noch die Knoepfe; Teilnehmer, Verknuepfung
   und Tags stehen in der eigenen Kachel darunter (.event-preview-infos-karte). */
.event-preview-funktionen-karte { background: var(--event-kachel); border-radius: var(--radius); padding: var(--space-6); }
/* Teilnehmer, Verknuepfung und Tags in einer Kachel, Desktop wie Handy (siehe index.html): die Abschnitte verlieren
   ihre eigenen Karten, die Ueberschriften bleiben als Zwischentitel. Ohne sichtbaren Abschnitt (alle inline
   display:none) verschwindet die Kachel. */
.event-preview-infos-karte {
  display: flex; flex-direction: column; gap: var(--space-6); margin-top: var(--space-7);
  background: var(--event-kachel); border-radius: var(--radius); padding: var(--space-6);
}
.event-preview-infos-karte:not(:has(> .event-preview-block:not([style*="none"]))) { display: none; }
.event-preview-infos-karte > .event-preview-block { margin: 0; }
#event-preview-modal .event-preview-infos-karte :is(.event-preview-infos-card, .event-preview-attendees-card) { background: none; padding: 0; }
/* Gesamtzahl der Teilnehmer: ohne eigene Karte haengt der Kreis rechts neben der Ueberschrift statt an der Ecke. */
.event-preview-infos-karte .event-preview-attendees-count-badge { top: -25px; left: auto; right: 0; border-color: transparent; }
/* Nutzer-Feedback: "die funktionsbuttons ... bitte linksbündig" + "rechts daneben die funktions
   buttons machen untereinander" -- zurueck zu einspaltig gestapelt (schmalere Spalte neben dem
   Cover haette fuer 2 Spalten ohnehin keinen Platz mehr) und linksbuendiger statt zentrierter Text. */
.event-preview-funktionen-list { display: flex; flex-direction: column; gap: var(--space-2); }
/* Nutzer-Feedback: "den anpinnen und link teilen button gerne nebeneinander machen" -- Pin/Share
   sind aktuell die einzigen Eintraege in dieser Liste, daher direkt auf der Basisklasse per
   Modifier statt eines generischen 2-Spalten-Grids (das bei kuenftig mehr Eintraegen wieder
   umgestellt werden muesste). justify-content:center + flex:1 auf den Buttons (siehe unten)
   lassen beide gleich breit nebeneinander stehen statt linksbuendig gestapelt. */
.event-preview-funktionen-list-row { flex-direction: row; gap: var(--space-3); }
/* Nutzer-Feedback (weitere Folgerunde): "die buttons pin und share mit backgrounds machen" --
   bisher transparent (nur Hover zeigte einen Hintergrund, siehe .event-preview-funktion-btn
   Basisregel), jetzt permanent derselbe var(--panel-2)-Ton wie die anderen Buttons in dieser
   Spalte (Medienordner etc.), damit sie als eigenstaendige Buttons erkennbar sind statt wie reiner
   Text zu wirken. */
.event-preview-funktionen-list-row .event-preview-funktion-btn {
  flex: 1; justify-content: center; text-align: center;
  background: var(--knopf); border-color: transparent;
}
.event-preview-funktionen-list-row .event-preview-funktion-btn:hover { background: var(--panel); }
/* Nutzer-Feedback (Kehrtwende): "wir machen alle buttons im stil von pin und share ... die pin,
   share, album öffnen und medien hochladen und medienordner on/off buttons kommen in ein 3x2
   raster unter das titelbild" -- ersetzt die vorherige reine Pin/Share-Zeile: Album öffnen/Medien
   hochladen/Medienordner sind aus der Medien-Sektion hierher gewandert, alle 5 Elemente teilen sich
   jetzt dasselbe 3x2-Raster (gleiche .event-preview-funktion-btn-Optik). Das 6. Feld bleibt leer. */
.event-preview-funktionen-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-3); }
/* Nutzer-Feedback: "mache die 4 buttons oben gleich groß" -- width:auto liess jeden Button nur so
   breit wie seinen eigenen Text werden (Pin/Share dadurch sichtbar schmaler als Album/Hochladen),
   obwohl die Grid-Spalten selbst schon gleich breit waren. width:100% streckt jeden Button jetzt
   auf seine volle Grid-Zelle, dadurch wirklich gleich groß. */
.event-preview-funktionen-grid .event-preview-funktion-btn {
  width: 100%; justify-content: center; text-align: center;
  /* Seit die Knoepfe in der Kachel liegen (2026-09-11, "die buttons brauchen dann natürlich eine etwas andere
     farbe damit man sie sieht"): helle Toenung ueber der Kachel wie Album/Hochladen bei den Fotos, statt der
     Kachelfarbe selbst, in der sie verschwunden waeren. */
  background: var(--tint); border-color: transparent;
}
.event-preview-funktionen-grid .event-preview-funktion-btn:hover { background: var(--tint-hover); }
/* Oben stehen Pin, Share, Titelbild und Countdown -- alle vier gleich breit in zwei Reihen
   (2026-09-14: "mache den titelbild button genauso breit wie die anderen und schiebe countdown
   rechts daneben"). Der Titelbild-Knopf nahm bis dahin die ganze zweite Zeile; sein langer Text
   kuerzt jetzt mit Ellipse statt die Zelle zu sprengen. */
#event-preview-cover-btn span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* "Titelbild ansehen" (2026-09, vorher "Ansehen" im Raster oben). Nutzer-Feedback (2026-09-11): "das
   titelbild anzeigen button ganz nach unten links von der maske" -- per margin-top:auto an den
   Spaltenboden. Dort lagen frueher die schwebenden Rundknoepfe (Anleitung/Changelog, fest bei left:16px,
   38px breit) ueber seiner Kante; solange die Ansicht bis an den Fensterrand reicht (Rand
   max(20px, (100vw - 1430px) / 2), Innenabstand 28px), rueckt er darum genau so weit ein, dass er 8px
   rechts neben den Rundknoepfen beginnt. Auf dem Handy steht er im Drei-Punkte-Menue
   (showEventPreviewMobileMoreMenu() klickt diesen Knopf stellvertretend). */
.event-preview-cover-view-row {
  margin-top: auto; padding-top: var(--space-7); display: flex;
  padding-left: max(0px, calc(34px - max(20px, (100vw - 1430px) / 2)));
}
.event-preview-cover-view-btn { background: var(--event-kachel, var(--panel-2)); border-color: transparent; font-size: var(--fs-sm); }
.event-preview-cover-view-btn:hover { background: var(--event-kachel-hover, var(--panel)); }
@media (max-width: 720px) { .event-preview-cover-view-row { display: none; } }
/* Nutzer-Feedback: "medienordner sollte generell 'medienordner' da stehen haben und quasi dann ein
   großer button sein der zusammen mit dem an aus einen background hat. es sollte auch nicht mehr
   das folder icon klickbar sein" -- kein eigenes Icon/kein eigener Button mehr fuer das Label
   (reiner Text), Label + An/Aus-Segmente teilen sich EINEN Hintergrund und wirken dadurch wie ein
   einzelner, in sich geschlossener Button in derselben Optik wie die anderen 4 Rasterfelder. */
/* Nutzer-Feedback (weitere Kehrtwende): "den medienordner on off button machen wir unter die 6
   bilder schmal mittig auf die gesamte breite" -- lebt jetzt als eigene Zeile unter dem Foto-
   Raster (siehe #event-preview-media-section in index.html) statt als Rasterfeld im Funktionen-
   Grid, dadurch width:fit-content + margin:auto statt gestreckt ueber eine Grid-Zelle. */
.event-preview-media-folder-group {
  display: flex; align-items: center; justify-content: center; gap: var(--space-4); min-width: 0;
  width: fit-content; margin: var(--space-4) auto 0;
  background: var(--tint); border-radius: var(--radius); padding: var(--space-3) var(--space-7);
}
.event-preview-media-folder-group:hover { background: var(--tint-hover); }
/* Event-Ansicht (Nutzer-Feedback 2026-09-11: "den medienordner on off background noch dunkler machen wie die
   anderen backgrounds damit man es besser lesen kann") -- im Medien-Kopf steht der Umschalter frei, auf dem
   Titelbild schien die helle Toenung durch. Jetzt dieselbe Flaeche wie die Kacheln. */
#event-preview-modal .event-preview-media-folder-group { background: var(--event-kachel); }
#event-preview-modal .event-preview-media-folder-group:hover { background: var(--event-kachel-hover); }
.event-preview-media-folder-group-label {
  font-size: var(--fs-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
/* Nutzer-Feedback (2026-09-11): "die buttons für hochladen und album runter zu der album übersicht"
   -- zwei gleich breite Knoepfe ueber den Foto-Kacheln, im selben getoenten Ton wie Kacheln und
   Medienordner-Umschalter derselben Karte (var(--tint)), damit alles als ein Block liest. */
.event-preview-media-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); margin-bottom: var(--space-4); }
.event-preview-media-actions .event-preview-funktion-btn { justify-content: center; text-align: center; background: var(--tint); border-color: transparent; }
.event-preview-media-actions .event-preview-funktion-btn:hover { background: var(--tint-hover); }
/* Kopf der Medien-Kachel (2026-09-11: "mache den medienordner on off button dann außerhalb der
   bilderansicht neben die überschrift 'medien' rechts") -- wie die Koepfe von Beschreibung und Chat:
   Ueberschrift links, Bedienelement rechts. Der Umschalter sass vorher zentriert unter den Fotos. */
.event-media-tile-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); margin-bottom: var(--space-4); min-height: var(--control-h-sm); }
/* Gleiche Werte wie die Koepfe von Beschreibung und Chat daneben (.event-preview-notiz-header/
   -chat-header): mit der Basis von .event-preview-section-label war "MEDIEN" groesser und duenner. */
.event-media-tile-head .event-preview-section-label {
  margin: 0; font-size: var(--fs-2xs); font-weight: var(--fw-semibold); color: var(--text-dim);
  letter-spacing: 0.04em; text-transform: uppercase;
}
.event-media-tile-head .event-preview-media-folder-group { margin: 0; padding: var(--space-1) var(--space-1) var(--space-1) var(--space-5); }
/* Nutzer-Feedback: "der medien ordner button sollte neben dran ein on off button quasi attachted
   haben der aussieht wie das ich nehme teil und nicht teil. den kann man dann switchen. der on off
   teil ändert seine farbe. der button an sich aber nicht mehr" -- gleiches Segmented-Toggle-Muster
   wie .event-preview-attendance-toggle, nur 2 statt 3 Segmente. Nur diese Segmente faerben sich
   noch je nach Zustand, das Label daneben bleibt immer neutral (siehe
   renderEventPreviewMediaSection() in app.js). */
.event-preview-media-folder-onoff {
  display: flex; align-items: center; gap: var(--space-1); background: var(--tint);
  border-radius: var(--radius-full); padding: var(--space-1); flex-shrink: 0;
}
.event-preview-media-folder-onoff-seg {
  border: none; background: transparent; color: var(--text-dim); font-size: var(--fs-xs); font-weight: var(--fw-bold);
  padding: var(--space-2) var(--space-4); border-radius: var(--radius-full); cursor: pointer; white-space: nowrap;
  transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.event-preview-media-folder-onoff-seg:hover { color: var(--text); }
.event-preview-media-folder-onoff-seg.active[data-seg="on"] { background: var(--success); color: var(--on-success); }
.event-preview-media-folder-onoff-seg.active[data-seg="off"] { background: var(--warning); color: var(--on-warning); }
/* Nutzer-Feedback: "medien hochladen ist mit textumbruch ... verkleinern das es so ist wie die
   anderen buttons" -- die Funktionen-Spalte ist seit dem groesseren Cover schmaler, "Medien
   hochladen" ist das laengste Label und brach dadurch als einziges auf 2 Zeilen um. Kleinere
   Schrift/Padding + nowrap+ellipsis als Sicherheitsnetz statt Umbruch. */
.event-preview-funktion-btn {
  width: 100%; justify-content: flex-start; text-align: left; background: transparent; border-color: transparent;
  padding: var(--space-3) var(--space-3); font-size: var(--fs-sm);
  /* bewusst kompakter als der Baustein-Nebenknopf (--control-h): sechs davon stehen im Raster.
     Schrift seit 2026-09 --fs-sm statt --fs-xs -- 11.5px war neben dem Rest der Ansicht zu klein. */
  min-height: 0; line-height: normal;
}
.event-preview-funktion-btn span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.event-preview-funktion-btn:hover { background: var(--panel); border-color: var(--border); color: var(--text); }
/* Nutzer-Feedback: "ob ein medienordner aktiv oder nicht ist sollte als kleines icon neben medien
   hochladen zusehen sein" -- eigenes, kleines Status-Icon getrennt vom Haupt-Icon des Buttons,
   damit der Zustand auch ohne Hover auf einen Blick erkennbar ist (aktiv: gruen/Akzentfarbe voll,
   inaktiv: dezent umrandet). */
.event-preview-media-folder-status-icon { margin-left: auto; font-size: var(--fs-md); flex-shrink: 0; color: var(--text-dim); }
.event-preview-media-folder-status-icon.active { color: var(--success); }
/* Nutzer-Feedback: "mache die beschreibung zwei zeilen lang mit einem weiterlesen button ... damit
   die ganzen bilder auch zu sehen sind" -- standardmaessig auf 2 Zeilen geclampt (spart der darunter
   stehenden Fotos-Kachel Platz), "Weiterlesen" schaltet auf die vorherige 280px-Scrollbox um (siehe
   .event-preview-description-expanded weiter unten + wireEventPreviewDescriptionToggle() in app.js). */
.event-preview-description-clamped {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; max-height: none;
}
.event-preview-description-expanded { display: block; max-height: 280px; overflow-y: auto; overscroll-behavior: contain; }
/* Nutzer-Feedback (Folgerunde): "der weiterlesen button bei der fetched beschreibung sollte in
   mittiger kleiner pfeil nach unten sein" (danach: "mache den pfeil ... einfach eine kleine linie
   mit einem leichten pfeil das es noch dünner wird") -- kein .icon-btn-Rahmen/-Hintergrund mehr,
   stattdessen eine duenne Trennlinie ueber einem kleinen, dezenten Pfeil, beides in var(--text-dim)
   statt vollem Text/Icon-Kontrast. display:flex (Column) + margin:auto zentriert das Ganze
   horizontal in der vollen Spaltenbreite, Pfeil dreht beim Aufklappen um 180° statt den Text zu
   wechseln. */
.event-preview-description-toggle-btn {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  width: auto; height: auto; margin: var(--space-4) auto 0; padding: var(--space-1) var(--space-7);
  background: none; border: none; color: var(--text-dim); cursor: pointer;
}
.event-preview-description-toggle-line { width: 20px; height: 1px; background: var(--border); }
.event-preview-description-toggle-btn .mdi { font-size: var(--fs-sm); line-height: 1; transition: transform var(--duration-fast) ease; }
.event-preview-description-toggle-btn:hover { color: var(--text); }
.event-preview-description-toggle-btn:hover .event-preview-description-toggle-line { background: var(--text-dim); }
.event-preview-description-toggle-btn-expanded .mdi { transform: rotate(180deg); }
/* Notiz-Spalte (alte Spalte 2) -- eigene, schmalere Spalte nur fuer die Event-eigene Notiz (die
   externe/gefetchte Beschreibung steht jetzt in der Medien-Spalte, siehe .event-preview-media-col
   oben). Auf Mobile ist sie Teil des Details-Tabs (siehe .event-preview-mobile-tabs), am Desktop
   bleibt sie nur fuer die Notiz-Karte reserviert. */
.event-preview-notiz-col { min-width: 0; display: flex; flex-direction: column; }
/* Nutzer-Feedback: "wenn dort nichts ist mache trotzdem einen notiz block auf die gesamte höhe"
   -- flex:1 statt der Standard-margin-top-Kartenoptik, damit der Block auch ohne Chat-Vorschau
   darunter (Desktop) die komplette Spaltenhoehe ausfuellt. */
.event-preview-notiz-block { margin-top: 0; flex: 1; display: flex; flex-direction: column; min-height: 0; }
/* Huelle unter dem Kopf (2026-09-11, fuer den Kopf ueber der Kachel auf dem Handy): auf dem Desktop ohne
   eigene Box, damit Beschreibung/Werkzeugleiste/Feld weiter direkte Flex-Kinder des Blocks sind (flex:1). */
.event-preview-notiz-karte { display: contents; }
/* Gleiche Huelle im Chat (Suche, Nachrichten, Eingabe) fuer den Kopf ueber der Karte am Desktop (siehe
   @media(min-width:721px) bei .event-preview-notiz-col). Auf dem Handy ohne eigene Box -- der Vollbild-Chat
   bleibt unveraendert. */
.event-preview-chat-karte { display: contents; }
/* Horizontales Padding kommt von .event-preview-notiz-col (siehe @media weiter unten). Eigenes
   padding-top:8px (statt vom Spalten-Rand geerbtem 0) haelt die Titelzeile exakt auf gleicher Hoehe
   wie .event-preview-chat-header -- beide haben jetzt dieselbe padding-top-Verantwortung an
   derselben Stelle (Header selbst, nicht Spalte). */
.event-preview-notiz-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-4) 0 var(--space-4);
}
.event-preview-notiz-header .event-preview-section-label {
  margin: 0; font-size: var(--fs-2xs); font-weight: var(--fw-semibold); color: var(--text-dim);
  letter-spacing: 0.04em; text-transform: uppercase;
}
.event-preview-notiz-header-actions { display: flex; align-items: center; gap: var(--space-1); }
.event-preview-notiz-block .event-preview-description { flex: 1; overflow-y: auto; overscroll-behavior: contain; }
.event-preview-notiz-placeholder { font-style: italic; color: var(--text-dim); opacity: 0.75; cursor: pointer; }
.event-preview-notiz-textarea {
  flex: 1; width: 100%; min-height: 120px; resize: vertical; background: var(--panel-2);
  border: 1px solid var(--border); color: var(--text); border-radius: var(--radius-md); padding: var(--space-4) var(--space-5);
  font-size: var(--fs-md); font-family: inherit; line-height: 1.4;
}
.event-preview-notiz-edit-actions { display: flex; gap: var(--space-4); margin-top: var(--space-4); }
.event-preview-notiz-edit-actions .primary-btn, .event-preview-notiz-edit-actions .text-btn { padding: var(--space-3) var(--space-6); font-size: var(--fs-sm); }
/* Nutzer-Feedback: "das fenster sollte sich nicht erweitern wenn etwas zu lang wird, sondern
   wenn die größe des fensters erreicht ist sollte man die spalten scrollen können" -- jede der
   drei Spalten bekommt ihr eigenes overflow-y:auto statt dass #event-preview-modal .modal-body
   als Ganzes scrollt (siehe dortige overflow-y:hidden-Regel). min-height:0 ist noetig, weil
   Grid-/Flex-Items sonst nie unter ihre content-Groesse schrumpfen (Browser-Default
   min-height:auto) und dadurch nie einen eigenen Scrollbalken bekommen wuerden. Nur am Desktop
   relevant -- ab 720px ist #event-preview-modal ohnehin fullscreen mit normalem Seiten-Scroll. */
@media (min-width: 721px) {
  #event-preview-modal .event-preview-media-col,
  #event-preview-modal .event-preview-notiz-col {
    min-height: 0; overflow-y: auto;
    /* Nutzer-Feedback: "der hintergrund scrollt wenn man im event view scrollt und am ende
       angekommen ist" -- ohne overscroll-behavior reicht der Browser eine Scroll-Geste am oberen/
       unteren Rand dieser Spalte an den naechsten scrollbaren Vorfahren weiter (hier: die
       Dashboard-Seite HINTER dem Modal, da .modal-overlay selbst nicht scrollt). "contain" stoppt
       diese Weiterleitung an der Spaltengrenze. */
    overscroll-behavior: contain;
  }
  /* Bugfix (Nutzer-Feedback: "die komplette teilnehmerzahl wird auf mobil richtig angezeigt aber
     auf desktop noch abgeschnitten links") -- ein Element mit overflow-y ungleich "visible" laesst
     den Browser automatisch auch overflow-x auf "auto" umschalten (CSS-Spec-Kopplung, laesst sich
     nicht per explizitem overflow-x:visible umgehen, siehe overflow-y:auto oben). Die Teilnehmer-
     Zahl haengt bewusst 6px nach links/oben ueber die Kartenecke hinaus (siehe .event-preview-
     attendees-count-badge) -- ohne Platz dafuer wird genau dieser Ueberhang an der Spalten-Kante
     abgeschnitten. 8px Innenabstand links schafft den noetigen Puffer, ohne dass sich die Spalte
     optisch merklich verschiebt (auf Mobile nicht noetig, dort ist .event-preview-media-col nicht
     einzeln scrollbar, siehe Basisregel oben "nur am Desktop relevant"). */
  #event-preview-modal .event-preview-media-col { padding-left: var(--space-4); }
  /* Nutzer-Feedback (vorherige Runde): "die spalten auf die gleiche höhe" -- gleicher
     var(--zone-bg)-Ton wie die einzelnen .event-preview-section-Karten darin, die verschmelzen
     dadurch optisch nahtlos mit dem Spalten-Hintergrund statt als "Box in der Box" aufzufallen. */
  /* Nutzer-Feedback: "notiz ist irgendwie eingerückt links und rechts das ist auch komisch" +
     Folgerunde zur Titelzeilen-Ausrichtung -- kein padding-top mehr hier (verschob die
     .event-preview-notiz-header bisher 10px unter den Spaltenrand, waehrend .event-preview-chat-
     header dank fehlendem Spalten-Padding buendig am Spaltenrand sitzt, siehe .event-preview-chat-
     header). Stattdessen traegt die Titelleiste selbst jetzt 8px eigenes padding-top, exakt wie
     .event-preview-chat-header -- beide Titelzeilen liegen dadurch auf derselben Hoehe. */
  /* Nutzer-Feedback (2026-09-11): "evetn übersicht desktop: löse auch hier die überschriften beschreibung und
     chat heraus" -- wie Teilnehmer/Medien links: die Spalte selbst ist durchsichtig, die Kopfzeile (mit
     Stift bzw. Glocke/Pin/Umfragen/Suche) steht darueber, buendig mit der Karte, und die Flaeche traegt
     die innere Karte (.event-preview-notiz-karte bzw. .event-preview-chat-karte). Vorher trug die Spalte
     die Flaeche und die Kopfzeile sass mit 14px Seiteneinzug darin. */
  #event-preview-modal .event-preview-notiz-col { background: none; padding: 0; }
  #event-preview-modal .event-preview-chat { background: none; }
  #event-preview-modal :is(.event-preview-notiz-header, .event-preview-chat-header) { padding: 0 0 var(--space-4); }
  /* Nutzer-Feedback (2026-09-11): "die kachel optionen [ist] weiter oben als beschreibung und chat. mache es gleich"
     -- deren Kopfzeilen tragen Knoepfe (Stift, Glocke, Pin, ...) und sind dadurch so hoch wie ein Icon-Knopf, die
     Ueberschrift steht mittig darin. Der Optionen-Kopf war nur die Textzeile: Ueberschrift 7px, Kachel 14px zu hoch
     (TEST=eventkacheln misst es). Gleiche Mindesthoehe wie der Medien-Kopf (.event-media-tile-head). */
  #event-preview-modal .event-preview-funktionen > .event-preview-kopf { display: flex; align-items: center; min-height: var(--control-h-sm); }
  #event-preview-modal .event-preview-notiz-karte,
  #event-preview-modal .event-preview-chat-karte {
    display: flex; flex-direction: column; flex: 1; min-height: 0;
    background: var(--event-kachel, var(--zone-bg)); border-radius: var(--radius);
  }
  #event-preview-modal .event-preview-notiz-karte { padding: var(--space-5) var(--space-6); }
  /* Die Koepfe stehen jetzt frei auf dem Titelbild-Hintergrund -- gleicher Schatten wie links. */
  #event-preview-modal.event-preview-has-backdrop :is(.event-preview-notiz-header, .event-preview-chat-header) .event-preview-section-label {
    color: var(--text); text-shadow: 0 1px 2px rgb(0 0 0 / 0.85), 0 2px 18px rgb(0 0 0 / 0.65);
  }
  /* Historischer Rahmen-/Grid-Override fuer diese Sektion (aus der Zeit, als die Pille exakt unter
     der 150px breiten Cover-Spalte stehen sollte) ist veraltet und entfernt -- #event-preview-
     infos-section lebt seit "die tags und das spiel film serie relase ding wandern in einen
     eigenen bereich unter die medien" (siehe Basisregel weiter oben) als eigener, voll breiter
     Abschnitt unter der Medien-Sektion, nicht mehr neben dem Cover. Der 150px/1fr-Grid-Override
     passte dadurch nicht mehr zur tatsaechlichen Breite und quetschte Pille/Tags faelschlich in
     eine schmale erste Spalte -- die Basisregeln (Flex, volle Breite) reichen jetzt aus. */
}
/* R61: "Gefundene Links" -- Karten aus der Beschreibung ausgelagert (attachLinkEmbeds() mit
   targetContainer), damit der Beschreibungstext selbst nicht durch grosse Karten unterbrochen
   wird. Gleiche .link-embed-card-Optik, jetzt einspaltig (schmale Seitenspalte statt volle Breite).
   Nutzer-Feedback (Folgerunde): "mache daraus einen button neben dem notiz-edit-button, falls es
   links gibt, sonst kein button" -- lebt jetzt in einem eigenen Klick-Popover
   (#event-preview-linkfound-popover, .create-menu/.pinned-panel-popover-Basisoptik) statt fest im
   Spaltenfluss zu stehen; #event-preview-linkfound-btn wird nur eingeblendet, wenn attachLinkEmbeds()
   mindestens eine Karte gefunden hat (siehe toggleBtn-Parameter in app.js). */
.event-preview-linkfound-popover { width: 320px; }
.event-preview-linkfound { display: grid; grid-template-columns: 1fr; gap: var(--space-4); padding: var(--space-3); }
/* R61 Nutzer-Feedback: Verknuepfte Notizen und Dateien in EINER Box statt zwei nebeneinander --
   beide Widgets (renderWikiLinkWidget()/renderFileLinkWidget()) rendern weiterhin unabhaengig in
   ihren eigenen <span>, teilen sich hier nur Ueberschrift/Hintergrund und fliessen als ein
   gemeinsamer Chip-Strom zusammen. */
.event-preview-linked-combined { display: flex; flex-wrap: wrap; gap: var(--space-3); }
/* Google-Fotos-Alben werden als weitere "Slides" im vereinheitlichten Compendium-Mediaslider
   gefuehrt (media_type "album", siehe renderCompendiumMediaSlider() in app.js) -- eigene
   Kartenoptik statt Vorschaubild, Klick oeffnet den externen Link statt der Lightbox. */
.media-slider-slide-album {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-5);
  height: 100%; color: var(--link-accent, var(--accent)); font-size: var(--fs-lg); cursor: pointer;
}
.media-slider-slide-album .mdi { font-size: var(--fs-display); }
.event-preview-chat {
  display: flex; flex-direction: column;
  /* var(--zone-bg) statt var(--panel)+Rahmen: die Medien-Spalte daneben ist selbst unterlegt-frei
     (transparent, zeigt die Modal-Flaeche var(--panel)) -- ohne eigenen, abgesetzten Hintergrund
     wuerde die Chat-Spalte optisch mit dem Modal verschmelzen. var(--zone-bg) ist dunkler als
     var(--panel) und heller als gar nichts, die Chat-Bubbles (var(--panel-2)) bleiben darauf
     weiterhin gut sichtbar. */
  background: var(--event-kachel, var(--zone-bg)); border: none; border-radius: var(--radius);
  height: 100%; min-height: 320px;
}
.event-preview-chat .chat-messages { flex: 1; max-height: none; overscroll-behavior: contain; }
.event-preview-chat .chat-form { border-top: none; }
/* Nur auf Mobile im Chat-Vollbild-Modus sichtbar (siehe @media (max-width: 720px) weiter unten) --
   muss aus demselben Grund wie .event-preview-header-cover/.event-preview-header-chat-btn VOR dem
   @media-Block unconditional versteckt sein (bekanntes Bug-Pattern in dieser Datei, gleiche
   Selektor-Spezifitaet wuerde die dortige display:flex-Regel sonst durch Quellreihenfolge wieder
   verlieren). */
.event-preview-chat-back-btn { display: none; }
/* Mobile: Details und Chat sind wieder zwei eigene Vollbildseiten statt Tabs (Nutzer-Feedback:
   "mache aus dem chat und detail tab wieder jeweils eine seite ... wenn man drauf klickt kommt man
   auf den chat der wieder mit einem zurück button oben links ausgestattet ist und fullscreen ist")
   -- Umschalten weiterhin ueber dieselbe CSS-Klasse + setEventPreviewMobileTab() in app.js (kein
   Routing-Wechsel, bleibt derselbe Modal-Screen), aber jetzt OHNE sichtbare Tab-Leiste: der Chat
   wird per Klick auf #event-preview-header-chat-btn (Kopfzeile, siehe oben) geoeffnet und per
   #event-preview-chat-back-btn (siehe .event-preview-chat-back-btn weiter unten) wieder verlassen.
   Per Default ist Details aktiv, die Chat-Spalte bleibt dann unsichtbar. MUSS nach der
   unconditional .event-preview-chat-Basisregel oben stehen (gleiche Selektor-Spezifitaet, sonst
   gewinnt bei <=720px durch spaetere Quellreihenfolge wieder deren "display:flex" -- bekannter
   Bug-Pattern in dieser Datei). */
@media (max-width: 720px) {
  .event-preview-chat { display: none; }
  #event-preview-modal.event-preview-mobile-tab-chat .event-preview-media-col,
  #event-preview-modal.event-preview-mobile-tab-chat .event-preview-notiz-col { display: none; }
  /* Chat-Modus aktiv: die komplette Kopfzeile (Titel/Cover/Teilnahme-Toggle) sowie die Aktions-
     zeile darunter (Drei-Punkte-Menue) verschwinden -- der Chat ist jetzt eine eigene Vollbild-
     seite mit nur noch dem eigenen Zurueck-Pfeil (#event-preview-chat-back-btn) oben links, genau
     wie bei den anderen Vollbild-Unterseiten der App (Rooms/Medien-Ordner). */
  #event-preview-modal.event-preview-mobile-tab-chat .modal-header-bar,
  #event-preview-modal.event-preview-mobile-tab-chat .event-preview-mobile-actions-row { display: none; }
  /* Chat-Tab aktiv: der Chat ist jetzt normaler In-Flow-Inhalt (kein position:fixed-Sheet mehr),
     nimmt die volle verbleibende Hoehe des Modal-Bodys ein. position:relative als Bezugsrahmen
     fuer den absolut positionierten Zurueck-Pfeil. */
  #event-preview-modal.event-preview-mobile-tab-chat .event-preview-chat {
    display: flex; flex: 1; min-height: 0; position: relative;
  }
  /* Nutzer-Feedback (Folgerunde): "mache mobil im chat den titel 'chat' und die leiste ruhig auf
     die selbe zeile wie den zurück button" -- vorher schwebte der Zurueck-Pfeil ueber einer per
     padding-top freigehaltenen LEEREN Zeile, die eigentliche Kopfzeile (Titel+Icons) stand als
     eigene Zeile DARUNTER. Jetzt teilen sich beide dieselbe Zeile: kein eigenes padding-top am
     Chat-Container mehr, stattdessen bekommt .event-preview-chat-header selbst genug padding-left,
     um Platz fuer den ueber ihr liegenden (weiterhin absolut positionierten, da kein echtes
     Flex-Geschwister der Kopfzeile im Markup) Zurueck-Pfeil zu lassen -- 34px Breite + 10px
     Abstand vom Rand + 10px Luft zum Titel. */
  #event-preview-modal.event-preview-mobile-tab-chat .event-preview-chat-header {
    padding-left: 54px;
  }
  #event-preview-modal.event-preview-mobile-tab-chat .event-preview-chat-back-btn {
    display: flex; align-items: center; justify-content: center;
    position: absolute; top: 10px; left: 10px; z-index: 2;
    width: 34px; height: 34px; border-radius: 50%; background: var(--knopf);
    border: none; color: var(--text); font-size: var(--fs-2xl); cursor: pointer;
  }
  /* Nutzer-Feedback (Folgerunde): "es sollte wenn der chat im event übersicht mobil auch fullscreen
     sein. derzeit sind da noch leisten an der seite und der chat hat eine andere hintnergrundfarbe"
     -- die geteilte .modal-with-header .modal-body-Regel setzt padding:20px (bzw. 16px auf sehr
     schmalen Screens) auf ALLEN Seiten; vorher wurde hier nur padding-bottom ueberschrieben, wodurch
     links/rechts/oben weiterhin ein sichtbarer Rand (die "Leisten") stehenblieb -- durch die Luecke
     schimmerte dort die Modal-Flaeche (var(--panel)) statt der Chat-Flaeche (var(--zone-bg)) durch,
     das war die "andere Hintergrundfarbe". padding komplett auf 0, damit der Chat wirklich
     randlos/fullscreen ist. */
  #event-preview-modal.event-preview-mobile-tab-chat .modal-body { padding: 0; }
  /* Abgerundete Ecken ergeben bei einer randlosen Vollbild-Flaeche keinen Sinn mehr (an den 4 Ecken
     wuerde sonst wieder die Modal-Flaeche dahinter durchscheinen -- derselbe Effekt wie oben, nur
     an den Ecken statt am Rand). */
  #event-preview-modal.event-preview-mobile-tab-chat .event-preview-chat { border-radius: 0; }
  /* Nutzer-Feedback (Folgerunde): "mache mir bitte die zeile im chat mit den optionen wie chat
     abonnieren wieder sichtbar" -- die vorherige "display:none"-Regel (Begruendung damals: Titel
     "Chat" ist neben dem gleichnamigen Tab-Button redundant) hatte versehentlich auch die
     tatsaechlich noetigen Funktions-Icons (Abonnieren/Angepinnt/Umfragen/Suche) mit ausgeblendet --
     die Zeile ist jetzt wieder sichtbar. */
  /* Nutzer-Feedback: "im chat sollte die nachricht schreiben zeile etwas weiter hoch damit sie auf
     dem handy nicht abgeschnitten wird unten" (Folgerunde: "nachricht schreiben muss wenn die
     tastatur offen ist noch etwas weiter hoch" -- Abstand nochmal vergroessert) -- Sicherheitsabstand
     fuer die Eingabezeile (Home-Indicator-Geste auf iPhones ohne Home-Button + Luft zur Tastatur,
     die per wireKeyboardSafeSheet() in app.js die sichtbare Modal-Hoehe zwar schon korrekt an den
     tatsaechlich sichtbaren Bereich anpasst, die Eingabezeile sass dort aber noch zu knapp am
     unteren Rand). */
  .event-preview-chat .chat-form {
    padding-bottom: calc(var(--space-16) + env(safe-area-inset-bottom, 0px));
  }
}
/* Kompakte Zwischenstufe fuer schmalere Desktop-Fenster, in denen 3 echte Spalten zu gequetscht
   wirken wuerden -- Medien-Spalte links (spannt beide Zeilen), Notiz daneben, Chat darunter ueber
   die volle Breite. Grenze seit 2026-09 bei 1300px (Laptop-Stufe, vorher 1050px); dreispaltig
   bliebe dem Chat bei 900-1000px nur 40-140px (Grundregel: 360px + 420px + Rest).
   Linke Spalte 360px wie in der dreispaltigen Anordnung: seit Titelbild und Funktionsknoepfe
   nebeneinander stehen, schnitten die frueheren 260px die Knoepfe ab ("Pin", "Titelb...") -- das
   war zwischen 721 und 1050px schon vor dem Zusammenlegen der Breakpoints so. */
@media (min-width: 721px) and (max-width: 1300px) {
  .event-preview-layout { grid-template-columns: 360px 1fr; }
  .event-preview-media-col { grid-column: 1; grid-row: 1 / 3; }
  .event-preview-notiz-col { grid-column: 2; grid-row: 1; }
  .event-preview-chat { grid-column: 2; grid-row: 2; margin-top: var(--space-10); min-height: 260px; }
}

/* ---------- Onboarding ---------- */
.onboarding-intro { color: var(--text-dim); font-size: var(--fs-lg); margin-top: -8px; }
.page-subtitle { color: var(--text-dim); font-size: var(--fs-md); margin: 0 0 var(--space-7); }

/* ---------- Settings-Hinweistext ---------- */
.settings-hint { color: var(--text-dim); font-size: var(--fs-sm); margin: 0 0 var(--space-6); line-height: 1.5; }
/* Wiederverwendbarer Info-Hinweis mit leichtem Hintergrund + Icon (statt reinem gedimmten Text
   wie .settings-hint) -- fuer Erklaerungen, die staerker auffallen sollen, z.B. die
   Chatroom-Inaktiv-Loeschung. */
.info-hint-box {
  display: flex; align-items: flex-start; gap: var(--space-4); background: var(--inhalt-kachel);
  border-radius: var(--radius-md); padding: var(--space-4) var(--space-5); margin: 0 0 var(--space-6); color: var(--text-dim);
  font-size: var(--fs-sm); line-height: 1.5;
}
.info-hint-box .mdi { color: var(--accent); flex-shrink: 0; margin-top: 1px; font-size: var(--fs-body); }

/* ---------- Year calendar: durchlaufende Wochen-Timeline (Teamup-Stil) ---------- */
/* R68 Nutzer-Feedback: "mache die mo di mi do fr sa so leiste sticky und nicht jedes mal wieder
   eine zeile und die monate am desktop sollten schon genauso ausehen wie mobil / also mobil und
   desktop sollten gleich sein" -- EIN einzelner, wirklich global angehefteter Wochentags-Kopf
   ersetzt jetzt ueberall (Mobile UND Desktop) die vorher pro Monat wiederholte, fett hinterlegte
   .year-timeline-dow-header (JS baut sie seitdem gar nicht mehr, siehe renderYearGrid()). Nur der
   sticky-top-Offset unterscheidet sich je Breakpoint, weil .year-calendar-area am Desktop weiterhin
   ihr eigener Scroll-Container ist (top:0 reicht dort), am Handy dagegen die ganze Seite scrollt
   (dort steht er seit dem Umzug ins Fenster fest ueber der scrollenden Box). "nicht so hervorgehoben... einfach nur fett dann eine linie drunter
   und mit etwas abstand" -- kein Panel-Hintergrund mehr, nur fette Schrift + duenne Trennlinie. */
.year-timeline-dow-header { display: none; }
/* R68 Nutzer-Feedback: "die leiste oben sollte ausserhalb des kalenders selbst sein quasi drüber
   damit der background nicht so komisch aussieht im dekstop mode" -- .year-calendar-area hat am
   Desktop einen eigenen Panel-Hintergrund (--zone-bg) + Padding, der Kopf sass vorher DARIN mit
   eigenem --bg-Hintergrund, was wie ein falsch eingefaerbtes Rechteck im Panel wirkte. Jetzt ist er
   ein Geschwister-Element DAVOR (siehe .year-calendar-col in index.html), am Desktop dadurch ganz
   ohne Sticky-Positionierung noetig (steht bereits fest ueber der scrollenden Box, siehe
   .year-calendar-area{overflow-y:auto}). Horizontales Padding gleicht das Padding von
   .year-calendar-area aus (14px Desktop/0 Mobile), sonst waeren die 7er-Gruppen nicht mehr exakt
   ueber den Tagesspalten im Grid darunter ausgerichtet. Die Aussen-Gruppe spiegelt exakt dieselben
   grid-template-columns/Breakpoints wie .year-timeline darunter (3 Monatsspalten Desktop, 1
   Mobile) -- nur so richten sich die 7er-Wochentagsgruppen je Gruppe korrekt ueber ihrer
   jeweiligen Monatsspalte aus. */
.year-global-dow-header {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 var(--space-10);
  background: var(--bg);
  /* Rechtes Padding zusaetzlich um die tatsaechliche Breite der Scrollbar von .year-calendar-area
     erweitert (--calendar-scrollbar-width, per JS live nachgehalten, siehe
     syncCalendarScrollbarWidth() in app.js) -- der Kopf sitzt jetzt AUSSERHALB dieser scrollenden
     Box und verliert dadurch nicht automatisch dieselbe Breite wie #year-grid darin, was sonst zu
     einem mit der Zeit (je weiter rechts man in den 3 Monatsspalten schaut) immer staerker
     werdenden horizontalen Versatz fuehrte. */
  padding: var(--space-3) calc(var(--space-7) + var(--calendar-scrollbar-width, 0px)) var(--space-4) var(--space-7);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--border);
}
@media (max-width: 880px) {
  .year-global-dow-header { grid-template-columns: 1fr; }
  .year-global-dow-header-group:not(:first-child) { display: none; }
}
/* R68 Nutzer-Feedback: "donnerstag ist perfekt zentriert und darum links rechts nicht mehr" --
   die Gruppe rahmte ihre 7 Spalten bisher ohne das 10px-Innenpadding UND ohne den 4px-Spalten-
   Abstand, den .year-timeline-month-body/.year-week-daynums/.year-week-lanes fuer die tatsaechlichen
   Tages-Zellen darunter verwenden (padding:0 10px 6px bzw. gap:4px) -- dadurch war die Kopf-Gruppe
   insgesamt breiter als das Tagesraster, symmetrisch um die Mitte (Donnerstag) verzerrt. Padding +
   Gap hier 1:1 uebernommen, damit beide Raster exakt dieselbe Spalten-Geometrie haben. */
.year-global-dow-header-group { display: grid; grid-template-columns: repeat(7, 1fr); gap: 0 var(--space-2); padding: 0 var(--space-5); }
.year-global-dow-header-group span {
  text-align: center; font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--text-dim);
  letter-spacing: 0.02em; text-transform: uppercase;
}
/* Am Handy faellt das Padding von .year-calendar-area auf 0 -- der Kopf zieht mit. Sticky braucht
   er seit dem Umzug ins Fenster nicht mehr: dort scrollt das Raster in seiner eigenen Box, der Kopf
   steht als Geschwister darueber fest. */
@media (max-width: 880px) {
  .year-global-dow-header { padding-left: 0; padding-right: 0; }
}
/* "nur halt eine spalte mobil und 3 spalten dekstop" -- Desktop zeigt immer 3 Monate nebeneinander
   (unabhaengig vom Agenda-Einklapp-Zustand, die fruehere 2/3-Unterscheidung dafuer ist entfallen),
   Mobile bleibt einspaltig. */
.year-timeline { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-2) var(--space-10); align-items: start; }
@media (max-width: 880px) { .year-timeline { grid-template-columns: 1fr; } }
/* Jeder Monat ist jetzt eine eigene, einheitlich gestaltete "Kachel" -- wie ein normales Widget
   (gleicher Hintergrund/Radius wie .widget), keine Farbabstufung zwischen Monaten mehr (das sah
   zu aehnlich aus wie die opacity-gedimmten vergangenen Tage und war verwirrend). Die
   Unterscheidung zwischen Monaten kommt jetzt aus dem eigentstaendigen Kachel-Hintergrund +
   Abstand, nicht mehr aus Farbtoenen. */
.year-timeline-month-card {
  background: var(--panel); border-radius: var(--radius);
  padding: 0 var(--space-5) var(--space-3); overflow: hidden;
}
/* R67 Nutzer-Feedback: "quasi auch die monatstage in den kalender integrieren und nicht einzelne
   quadrate pro monat machen" (iOS-Kalender als Referenz) -- der Kachel-Hintergrund/-Rahmen ist
   komplett entfallen, Monate laufen jetzt durchgehend ineinander statt als einzelne umrandete
   Boxen. Nur die Monats-Ueberschrift (.year-timeline-month-label, siehe unten) bleibt als kleine
   Pille sichtbar -- sie sitzt als eigenes Element AUSSERHALB dieses Wrappers, ist von der
   Entfernung des Box-Looks hier also unberuehrt. */
.year-timeline-month-card-split { background: none; padding: 0; overflow: visible; }
.year-timeline-month-card-split .year-timeline-month-body {
  padding: 0 var(--space-5) var(--space-3); overflow: visible;
}
/* Monats-Ueberschrift sitzt als erstes Element IN der Kachel, mittig ausgerichtet, im selben
   "Bar"-Stil wie die Wochentags-Kopfzeile darueber (eigener Hintergrund, abgerundet) -- keine
   sticky-Positionierung mehr: die Kombination aus position:sticky + dem overflow:hidden der
   Monats-Kachel (fuers abgerundete Eck-Clipping noetig) fuehrte dazu, dass die Ueberschrift beim
   Scrollen sichtbar angeschnitten wurde. Ohne Sticky ist sie stattdessen ein normaler Block ganz
   oben in jeder Kachel -- robust, kein Clipping mehr, sieht dafuer optisch genauso aus wie die
   Mo/Di/Mi-Leiste gewuenscht. */
/* R68 Nutzer-Feedback: "hat nach wie vor Überschriften und riesige Monats-Titel... die monate am
   desktop sollten schon genauso ausehen wie mobil" -- die fruehere zentrierte Pille mit eigenem
   Hintergrund ist ueberall (Mobile UND Desktop) durch ein kleines, schlichtes, linksbuendiges Label
   ohne Hintergrund ersetzt, naeher am iOS-Vorbild. */
.year-timeline-month-label {
  background: none; width: auto; text-align: left; color: var(--text-dim);
  font-size: var(--fs-md); font-weight: var(--fw-semibold); letter-spacing: 0.01em;
  margin: var(--space-3) 0 var(--space-2) var(--space-1); padding: 0;
}
/* R68 Nutzer-Feedback: "mache gerne die vertikalen striche im kalender weg" -- die duennen
   Spalten-Trennlinien zwischen den 7 Wochentagen (vorher als Hintergrund-Layer simuliert, siehe
   Kommentar-Historie) sind komplett entfernt, nur die horizontale Zeilentrennung bleibt. */
.year-week-block {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-2) 0; border-bottom: 1px solid var(--border);
  position: relative;
}
.year-timeline-month-card .year-week-block:last-child { border-bottom: none; }
/* Hover-Hervorhebung fuer die GESAMTE Tagesspalte (siehe mousemove-Listener in buildYearWeekRow())
   statt nur die kleine Datums-Zahl -- ein wiederverwendetes, per JS positioniertes Element hinter
   dem eigentlichen Inhalt (z-index unter daynums/lanes), damit es Klicks nicht blockiert. */
.year-week-hover-highlight {
  position: absolute; top: 0; bottom: 0;
  background: var(--panel-2); border-radius: var(--radius-sm);
  pointer-events: none; z-index: 0; display: none;
}
.year-week-daynums { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--space-2); position: relative; z-index: 1; }
.year-week-daynum {
  position: relative;
  font-size: var(--fs-xs); color: var(--text-dim); text-align: center;
  padding: var(--space-1) 0; border-radius: var(--radius-xs); cursor: pointer;
}
.year-week-daynum.month-start { font-weight: var(--fw-bold); color: var(--text); }
/* Tage mit mindestens einem Termin/Release/Geburtstag sind fett -- leichtere Orientierung beim
   ueberfliegen einer Woche, ohne jeden Balken einzeln lesen zu muessen. */
.year-week-daynum.has-events { font-weight: var(--fw-bold); color: var(--text); }
/* R68 Nutzer-Feedback: "hebe im kalender gerne den tag der aktuell ist hervor indem du ihn nicht
   umkreist sondern die zahl des tages in der theme farbe hinterlegst und fett machst. hinterlege
   es mit dem gradient das die event tags auf der seite in der agenda auch haben" -- ersetzt den
   fruehreren Rahmen um die ganze Tagesspalte (.year-week-today-outline, entfallen) durch eine
   ausgefuellte Datums-Zahl, gleicher Gradient wie damals der Farbstreifen der Agenda-Karten
   (.agenda-category-stripe, 2026-09-13 durch das Trapez-Faehnchen .agenda-typ-leiste ersetzt). */
/* Nutzer-Feedback (mehrere Folgerunden, Kehrtwende): nach diversen Zwischenstaenden (gefuellte
   ganze Tagesspalte, dann nur noch Rahmen um die ganze Spalte, dazwischen eckige Datums-Zahl) kam
   das Feedback "der aktuelle tag wie er hervorgehoben wird sieht kacke aus. hebe mir den aktuellen
   tag einfach ganz subtil hervor und mache nur die zahl oben mit dem gradient background nicht die
   ganze zeile dann kann es auch wieder etwas runder werden" -- zurueck zu ausschliesslich der
   Datums-Zahl (keine Hervorhebung der ganzen Tagesspalte mehr, siehe .year-week-today-highlight
   weiter oben in der Historie, komplett entfernt), dafuer deutlich subtilerer Gradient (naeher an
   var(--panel-2) statt am vollen Akzent-Ton) und wieder abgerundete Ecken statt eckig. */
/* Nutzer-Feedback (Folgerunde): "bitte im normalen kalender den aktuellen tag etwas
   offensichtlicher machen" -- staerkerer Akzent-Anteil im Gradient (28/40% -> 55/75%) plus weisser
   statt gedimmter Text fuer besseren Kontrast, dazu ein duenner Ring als zusaetzliche Kante.
   Nutzer-Feedback (weitere Folgerunde): "hinterlege den aktuellen tag im kalender ganz leicht" --
   wieder zurueckgedreht: kein Gradient/Ring mehr, nur noch eine einzelne, sehr helle Akzent-
   Einfaerbung (20% statt vorher 55-75%) als schlichter Hintergrund. Bleibt weiterhin nur an der
   Datums-Zahl (keine ganze Zeile/Spalte), siehe Historie oben. */
.year-week-daynum.today {
  font-weight: var(--fw-bold); color: var(--text); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 20%, var(--panel-2));
}
/* Vergangene Tage sind gedimmt -- opacity statt Farbaenderung, wirkt unabhaengig davon, ob der
   Tag zusaetzlich fett (has-events) oder ein Monatsanfang ist. */
.year-week-daynum.past { opacity: 0.45; }
/* Nutzer-Feedback (Kehrtwende): "entferne die geburtstags umrandung. mache stattdessen das
   kuchen icon oben rechts in die tittelleiste (zahl) des jeweiligen tages" -- ersetzt die
   vorherige gestrichelte Umrandung der ganzen Tagesspalte (.year-week-birthday-outline, entfallen)
   durch ein kleines Icon direkt an der Datums-Zahl. */
/* Nutzer-Feedback (Folgerunde): "das birthday icon braucht oben und rechts ein wenig padding es
   ist zu weit in der ecke" -- Versatz nach aussen verkleinert, damit das Icon nicht bis in die
   aeusserste Ecke der Zelle reicht. */
.year-week-daynum-birthday-icon {
  position: absolute; top: -1px; right: 0; font-size: var(--fs-2xs); line-height: 1;
  color: color-mix(in srgb, var(--birthday-icon-color, #ec4899) 85%, white);
  pointer-events: none; z-index: 2;
}
.year-week-lanes {
  display: grid; grid-template-columns: repeat(7, 1fr); grid-auto-rows: 20px; gap: var(--space-2) var(--space-2);
  position: relative; z-index: 1;
  /* Platz fuer mindestens 3 Termin-Zeilen reservieren (3 * 20px + 2 * 3px Gap) -- IMMER, nicht nur
     bei Wochen mit Terminen: jede Kachel soll gleich gross sein (echtes Kalenderraster), egal ob
     Inhalt drin ist oder nicht. Vorher kollabierten leere Wochen komplett (.empty{display:none}),
     das ist auf Nutzerwunsch entfernt. */
  min-height: 69px;
}
.year-week-bar.past { opacity: 0.5; }
/* Vertikal zentriert per Flexbox statt ueber exakt zur Zeilenhoehe passende line-height -- bei
   sehr niedrigen Zeilen (Mini-Kalender-Widget, 13px) sorgte das dafuer, dass der Text (Icon-Glyphe
   hat eine andere Baseline/Zeilenhoehe als reiner Text) leicht unten abgeschnitten bzw. nicht
   sauber mittig wirkte. Der eigentliche Text-Inhalt steckt dafuer in .year-week-bar-label, da
   Ellipsis/Ueberlauf auf einem Flex-Container selbst unzuverlaessig ist. */
.year-week-bar {
  display: flex; align-items: center;
  border-radius: var(--radius-xs); padding: 0 var(--space-2); font-size: var(--fs-xs);
  color: var(--text-on-color);
  /* renderYearGrid() setzt die Kategoriefarbe als --balken-farbe (nicht direkt als background),
     damit der helle Modus sie abdunkeln kann -- weisse Schrift auf hellem Cyan oder Gelb ist auf
     Papier sonst nicht lesbar (gemessen mit TEST=hellmodus). Gleiche 82%-Regel wie die Faehnchen. */
  /* In beiden Modi um 82% abgedunkelt -- dieselbe Regel wie die Kategorie-Faehnchen: weisse Schrift
     auf hellem Cyan (Termin) oder Gelb (Feiertag) war sonst weder auf Papier noch im Dunkeln
     lesbar (2.4:1, gemessen mit TEST=hellmodus). */
  background: color-mix(in srgb, var(--balken-farbe, #888) 82%, black);
  /* Etwas Abstand zu den Spalten-Trennlinien links/rechts, damit die Leisten nicht am Rand
     kleben -- nur auf der jeweils "offenen" Seite, nicht dort, wo ein Balken ueber die Wochengrenze
     hinaus weiterlaeuft (siehe .continues-left/-right), sonst entstuende genau an der Schnittkante
     eine unschoene Luecke. */
  margin: 0 var(--space-1);
  /* Regression aus der Flex-Zentrierung oben: der Balken ist ein Grid-Item (in .year-week-lanes),
     Grid-Items haben standardmaessig min-width:auto und schrumpfen dadurch nie unter die
     Content-Mindestbreite ihres Textes -- bei langen Titeln ragte der Balken dadurch sichtbar in
     die naechste Tagesspalte, obwohl grid-column korrekt nur einen Tag umfasste. overflow:hidden
     setzt die automatische Mindestbreite auf 0 (Grid-Spezifikation) und erzwingt so das Clipping
     auf die zugewiesene Spaltenbreite -- min-width:0 zusaetzlich als explizite Absicherung. */
  overflow: hidden; min-width: 0;
}
/* Weich auslaufen statt "...": bei rund 57px pro Tag frass die Ellipse ein ganzes Zeichen, ein
   Termin hiess dann "Ki..." -- jetzt bleibt "Kinoab" lesbar, und der volle Titel steht als Tooltip am
   Balken (bar.title in app.js). Die Ausblendung liegt auf der Beschriftung, nicht auf dem Balken: dort
   lief jeder Termin am rechten Ende aus, auch einer, dessen Titel passt (2026-09). Die Beschriftung
   fuellt den Balken (flex:1), die Maske erfasst ihre letzten 8px -- die erreicht nur ein Titel, der
   wirklich zu lang ist. Das Mini-Widget hat eigene Regeln (zentriert, clip). */
.year-week-bar-label {
  overflow: hidden; text-overflow: clip; white-space: nowrap; min-width: 0; flex: 1;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 8px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 8px), transparent);
}
.year-week-bar.continues-left { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: 0; }
.year-week-bar.continues-right { border-top-right-radius: 0; border-bottom-right-radius: 0; margin-right: 0; }
.year-week-bar.is-game { cursor: default; opacity: 0.9; }

/* ---------- Day detail popover ---------- */
/* ---------- Erstellen-Auswahlmenü ---------- */
/* Nutzer-Feedback: "das mobile menü mit den 3 punkten kann im dropdown deutlich weniger breit
   sein das ist unnötig. und dann aber trotzdem bei den drei punkten öffnen" -- eigene, schmalere
   min-width statt der geteilten .create-menu-Basisbreite (200px), Ankerposition/Oeffnungslogik
   bleibt unveraendert (positionPopover() in app.js). Zwei Klassen sind spezifischer als die
   einfache .create-menu-Regel darueber und gewinnen daher unabhaengig von der Quellreihenfolge. */
.create-menu.event-preview-mobile-more-menu { min-width: 170px; }
.create-menu {
  position: fixed;
  /* Gleiche Glasflaeche wie Fenster und Kacheln (2026-09-15), in der dichteren Fensterstufe. */
  background: var(--flaeche-fuellung-fenster);
  -webkit-backdrop-filter: var(--flaeche-unschaerfe);
          backdrop-filter: var(--flaeche-unschaerfe);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-3);
  min-width: 200px;
  max-height: 60vh;
  overflow-y: auto;
  z-index: var(--z-menu);
  box-shadow: var(--shadow-lg);
  animation: fadeInUp var(--duration-fast) ease both;
}
/* Nutzer-Feedback (Kehrtwende, aktuelle Runde): "mache die mobile popups ... nicht auf die gesamte
   breite. das ist fast überall so und muss fixed werden" -- eine fruehere Feedback-Runde wollte
   genau das Gegenteil (Vollbreiten-Dropdowns, siehe Kommentar-Historie), das wird hier bewusst
   zurueckgenommen: .create-menu bleibt auf JEDER Breite kompakt (min-width:200px, Groesse ueber
   opts.width/positionPopover() gesteuert) statt sich per left/right:10px!important ueber fast die
   ganze Bildschirmbreite zu strecken. */
@media (max-width: 720px) {
  .agenda-filter-popover, .pinned-panel-popover, .past-events-popover {
    width: min(300px, calc(100vw - 32px)) !important;
    font-size: var(--fs-md);
  }
  /* Dunkler Hintergrund hinter diesen drei Popovern (showMobilePopoverBackdrop() in app.js) --
     eigenes Element statt eines ::before-Pseudo-Elements, da es klickbar sein muss (schliesst das
     Popover, gleiches Muster wie ein Modal-Overlay). */
  .agenda-popover-mobile-backdrop {
    position: fixed; inset: 0; background: var(--scrim); z-index: calc(var(--z-popover) + 5);
    animation: overlayFade var(--duration-fast) ease both;
  }
}
.create-menu-item {
  display: flex; align-items: center; gap: var(--space-5); width: 100%;
  background: none; border: none; color: var(--text);
  padding: var(--space-5) var(--space-5); border-radius: var(--radius-sm); cursor: pointer;
  font-size: var(--fs-lg); text-align: left; font-family: inherit;
}
.create-menu-item:hover { background: var(--panel-hover); }
.create-menu-item.active { background: var(--aktiv-kachel); color: var(--accent); }
/* Discord-artiges Kontextmenue fuer Chat-/Kommentar-Nachrichten (Rechtsklick oder "..."-Button
   der Hover-Toolbar, siehe showChatContextMenu() in app.js) -- teilt sich die .create-menu-Basis-
   optik mit allen anderen Klick-Popovern der App, "Loeschen" bekommt eine eigene rote Variante. */
.create-menu-item-danger { color: var(--danger); }
.create-menu-item-danger:hover { background: color-mix(in srgb, var(--danger) 15%, transparent); }
.chat-context-menu { min-width: 210px; }

.agenda-filter-popover { min-width: 220px; }
.agenda-filter-checkbox-row {
  display: flex; align-items: center; gap: var(--space-5); width: 100%;
  padding: var(--space-5) var(--space-5); border-radius: var(--radius-sm); cursor: pointer;
  font-size: var(--fs-lg); color: var(--text);
}
.agenda-filter-checkbox-row:hover { background: var(--panel-hover); }
.agenda-filter-checkbox-row input[type="checkbox"] { flex-shrink: 0; accent-color: var(--accent); width: 15px; height: 15px; }

.pinned-panel-popover { width: 300px; max-height: 400px; overflow-y: auto; }
.pinned-panel-popover .agenda-list { max-height: none; }
/* Phase 2 Nutzer-Feedback: "Angepinnte Nachrichten" -- gleiches Popover-Grundmuster wie das
   Angeheftet-Panel (showPinnedMessagesPanel() in app.js), Zeilen aber als Avatar+Text-Snippet
   statt reiner Namens-Pillen, da hier der Nachrichteninhalt der Sinn ist. */
.pinned-message-item {
  display: flex; align-items: center; gap: var(--space-4); width: 100%; padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm);
  cursor: pointer; text-align: left; background: none; border: none; font-family: inherit;
}
.pinned-message-item:hover { background: var(--panel-hover); }
.pinned-message-avatar { width: 26px; height: 26px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.pinned-message-body { display: flex; flex-direction: column; min-width: 0; gap: 1px; }
.pinned-message-author { font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--text-dim); }
.pinned-message-text { font-size: var(--fs-md); color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Nutzer-Feedback: "mach die angehefteten wieder kleiner und nicht als fullscreen und den
   schließen button so wie bei den profile settings" -- zurueck zum normalen, am Anker verankerten
   Popover (kein eigener Media-Query-Fullscreen-Block mehr), der Schliessen-Button sitzt jetzt wie
   .modal-close (siehe dort) absolut oben rechts statt inline in der Titelzeile. */
.pinned-panel-popover-close {
  position: absolute; top: 8px; right: 8px;
  background: none; border: none; color: var(--text-dim);
  font-size: var(--fs-xl); cursor: pointer; padding: var(--space-1);
}
/* R60 Nutzer-Feedback: "Vergangene Events"-Popup breiter als die uebrigen .pinned-panel-popover-
   Nutzungen (Angeheftet-Panel) -- positionPopover()s width-Option beeinflusst nur die
   Links-Positionierung, nicht die tatsaechliche Breite (die kommt von hier). */
.past-events-popover { width: 380px; }
/* Das gemeinsame Fenster (angeheftet + vergangen) braucht Platz fuer zwei Spalten -- die
   width-Option von positionPopover() steuert nur die Position, die Breite kommt von hier. */
.agenda-archiv-popover { width: min(680px, calc(100vw - 32px)); max-height: min(70vh, 620px); }
.widgets-menu { max-width: 260px; }
.widgets-menu-title { font-size: var(--fs-xs); color: var(--text-dim); padding: var(--space-2) var(--space-5) var(--space-4); text-transform: uppercase; letter-spacing: 0.03em; }
/* Muss NACH der .widgets-menu-title-Basisregel stehen (gleiche Klassen-Spezifitaet, spaetere
   Quellposition gewinnt sonst -- dasselbe wiederkehrende Muster wie beim .status-ticker-Fix an
   anderer Stelle in dieser Datei), sonst gewinnt deren padding-right:10px zurueck und der
   absolut positionierte Schliessen-Button ueberlappt den Titeltext. */
.pinned-panel-popover-title { padding-right: var(--space-12); }
/* R60 Nutzer-Feedback: "(letzte 14 Tage)" soll als eigene zweite Zeile unter dem Titel stehen
   statt in derselben Zeile mitzulaufen (wurde bei schmalerer Popover-Breite arg gedrungen). */
.past-events-panel-subtitle { display: block; font-size: var(--fs-2xs); font-weight: var(--fw-regular); letter-spacing: normal; margin-top: var(--space-1); opacity: 0.75; }
.widgets-menu-item {
  display: flex; align-items: center; gap: var(--space-5); width: 100%;
  padding: var(--space-4) var(--space-5); border-radius: var(--radius-sm); cursor: pointer; font-size: var(--fs-lg);
}
.widgets-menu-item:hover { background: var(--panel-hover); }
.widgets-menu-item input[type="checkbox"] { flex-shrink: 0; }
.widgets-menu-reset-btn {
  display: flex; align-items: center; gap: var(--space-4); width: 100%; margin-top: var(--space-3);
  padding: var(--space-4) var(--space-5); border-radius: var(--radius-sm); cursor: pointer; font-size: var(--fs-md);
  background: transparent; border: 1px solid var(--border); border-top: 1px solid var(--border);
  color: var(--text-dim);
}
.widgets-menu-reset-btn:hover { background: var(--panel-hover); color: var(--text); border-color: var(--accent); }

.day-hover-preview {
  position: fixed;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  max-width: 240px;
  z-index: var(--z-sticky);
  box-shadow: var(--shadow-popover);
  animation: fadeInUp var(--duration-fast) ease both;
}
.day-hover-preview-item {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-xs);
  border-left: 3px solid var(--accent);
  font-size: var(--fs-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
.day-hover-preview-item:hover { background: var(--panel-hover); }
.day-hover-preview-item + .day-hover-preview-item { margin-top: var(--space-1); }
/* Gliederung des Hover-Popups: "An diesem Tag" fuer eintaegige Termine, je mehrtaegigem
   Termin eine eigene "Zeitraum x-y"-Ueberschrift direkt darueber -- ersetzt die vorherige,
   pro Event wiederholte "An diesem Tag"/"Zeitraum"-Unterzeile. */
.day-hover-preview-heading {
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-dim);
  margin: var(--space-4) var(--space-2) var(--space-2);
}
.day-hover-preview-heading:first-child { margin-top: 0; }
.day-hover-preview .add-event-link {
  color: var(--accent);
  cursor: pointer;
  font-size: var(--fs-sm);
  margin-top: var(--space-3);
  display: inline-block;
}

/* Nutzer-Feedback (Folgerunde): "mache den popup kalender ... nicht so hoch sondern irgendwie mehr
   viereckig" -- breiter (260px -> 300px) und die Terminzeilen (.year-week-lanes) auf eine einzelne,
   flache Zeile statt der vorher fuer 3 Termine reservierten Mindesthoehe reduziert -- in diesem
   kleinen Hover-Kontext reicht ein Hinweis auf "hier ist was", nicht die volle Balken-Liste, das
   ergibt zusammen mit der Breite ein deutlich quadratischeres Format statt der vorherigen, sehr
   hochkant wirkenden Saeule. */
.calendar-icon-hover-preview {
  position: fixed;
  width: 300px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  z-index: var(--z-hover-preview);
  box-shadow: var(--shadow-popover);
  animation: fadeInUp var(--duration-fast) ease both;
}
.calendar-icon-hover-preview .year-week-lanes { grid-auto-rows: 14px; gap: var(--space-1) var(--space-2); min-height: 16px; }
.calendar-icon-hover-preview .year-week-bar { min-width: 0; font-size: var(--fs-2xs); padding: 0 var(--space-2); }
.calendar-icon-hover-preview .year-week-bar-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Nutzer-Feedback: "bitte den hover kalender nicht anklickbar machen" -- die Basisregel oben setzt
   cursor:pointer unbedingt (fuer den grossen, echten klickbaren Kalender), hier explizit
   zurueckgesetzt, da buildYearWeekRow() in diesem Kontext mit interactive:false aufgerufen wird
   (kein Klick-Handler mehr, siehe app.js) und der Cursor das sonst faelschlich weiter anzeigen wuerde. */
.calendar-icon-hover-preview .year-week-daynum { font-size: var(--fs-2xs); cursor: default; }
/* Nutzer-Feedback: "hebe mir den tag des termins hervor über den ich hovere. z.b. mache so einen
   effekt wie bei den benachrichtigungen wo der jeweilige tag so shimmert" -- gleicher pulsierender
   Ring wie .unread-badge (siehe @keyframes unread-pulse), hier als Umrandung um die Tageszahl statt
   als kleiner Punkt. */
/* Nutzer-Feedback (Folgerunde): "die animation des tages schöner machen das sieht kacke aus und
   bitte den gesamten tag umranden" -- vorher sprang der Puls zwischen einem color-mix()-Wert und
   dem Schluesselwort "transparent", zwei technisch unterschiedlichen Werttypen, die der Browser
   nicht sauber ineinander animieren konnte (wirkte wie ein hartes Blinken statt eines weichen
   Pulses). Beide Keyframes nutzen jetzt dieselbe color-mix()-Funktion (nur der Alpha-Anteil
   aendert sich: 45% -> 0%), dadurch echte, weiche Interpolation.
   Nutzer-Feedback (weitere Folgerunde): "mache wenn ich über den kalender hover nicht das der tag
   pulsiert sondern das event im kalender" -- die Hervorhebung sitzt jetzt auf dem Balken (.year-
   week-bar) DIESES Events statt auf der ganzen Tageszahl (die ja auch fuer andere, an diesem Tag
   liegende Termine steht), siehe showCalendarIconHoverPreview() in app.js. outline statt border,
   da der Balken bereits eine eigene Kategorie-Hintergrundfarbe traegt -- ein border wuerde deren
   Box zusaetzlich verschieben/verkleinern, outline legt sich nur aussen drueber. */
.calendar-icon-hover-preview .calendar-hover-target-bar {
  outline: 1.5px solid var(--accent);
  outline-offset: 1px;
  animation: calendar-hover-day-pulse 1.8s ease-in-out infinite;
}
/* Nutzer-Feedback (weitere Folgerunde): "bitte nach aussen pulsieren und nicht nach innen" -- der
   Puls startete bisher bei spread:0, also GENAU auf der Kante des statischen outline-Rings
   (1.5px + 1px offset) und war dort am undurchsichtigsten -- dieser hell-und-nah-am-Ring-Moment
   dominierte optisch, waehrend die tatsaechlich nach aussen wachsende Phase gleichzeitig
   ausblasste, wirkte dadurch wie ein Puls INS Bild hinein statt heraus. Start-spread jetzt knapp
   ausserhalb des statischen Rings (statt darauf), Ziel-spread deutlich weiter aussen -- die
   sichtbar hellste Phase liegt dadurch klar ausserhalb des Rings und wandert erkennbar nach aussen. */
@keyframes calendar-hover-day-pulse {
  0%, 100% { box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent); }
  50% { box-shadow: 0 0 0 8px color-mix(in srgb, var(--accent) 0%, transparent); }
}
.calendar-icon-hover-preview-label {
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-dim);
  margin: 0 var(--space-2) var(--space-2);
}

/* ---------- Games ---------- */
.games-toolbar, .wiki-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--space-8);
  flex-wrap: wrap;
  gap: var(--space-5);
}
.wiki-toolbar-actions { display: flex; align-items: center; gap: var(--space-3); }
/* Nutzer-Feedback: "mache alle überschriften im gleichen stil (kleiner etc.) wie auf den
   widgets" -- gleiche Werte wie .widget h3 (siehe dort), damit Notizen/Rooms/Tools/Sammlung-
   Spaltenueberschriften optisch zu den Dashboard-Widget-Ueberschriften passen. */
.wiki-toolbar h3,
.sammlung-column-heading-row h3,
.dashboard-widgets-heading.boards-sidebar-heading-row h3 {
  margin: 0; font-size: var(--fs-2xs); font-weight: var(--fw-semibold); color: var(--text-dim); letter-spacing: 0.04em; text-transform: uppercase;
}
.filter-group { display: flex; gap: var(--space-3); }
/* ---------- Baustein Pille (Filter, Umschalter) ----------
   Gewaehlt = Akzent-Toenung plus Haekchen (.filter-btn-check), nicht volle Akzentfarbe -- siehe
   .tab-btn.active; das Haekchen sagt es auch ohne Farbe. */
.filter-btn {
  background: var(--knopf);
  border: 1px solid var(--border);
  color: var(--text-dim);
  min-height: var(--control-h-sm);
  padding: var(--space-1) var(--space-7);
  border-radius: var(--radius-full);
  cursor: pointer;
  font-size: var(--fs-md);
  display: inline-flex; align-items: center; justify-content: center;
  transition: background var(--duration-fast) ease, border-color var(--duration-fast) ease, color var(--duration-fast) ease;
}
.filter-btn:hover { background: var(--knopf-hover); color: var(--text); }
.filter-btn.active { background: var(--accent-tint); color: var(--accent-light); border-color: var(--accent-strong); }
.filter-btn-check { display: none; margin-right: var(--space-3); font-size: 0.9em; }
.filter-btn.active .filter-btn-check { display: inline-block; }

/* ---------- Datumsgruppierte Listenansicht (Releases/Geburtstage/Kategorien) ---------- */
.date-grouped-list { display: flex; flex-direction: column; gap: var(--space-2); max-width: 720px; }
.date-group-header {
  font-size: var(--fs-md); font-weight: var(--fw-bold); color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.03em;
  margin: var(--space-12) 0 var(--space-4); padding-bottom: var(--space-3); border-bottom: 1px solid var(--border);
}
.date-group-header:first-child { margin-top: 0; }
.date-group-item {
  display: flex; align-items: center; gap: var(--space-6);
  padding: var(--space-5) var(--space-6); border-radius: var(--radius-md); background: var(--panel);
  border: 1px solid var(--border); cursor: pointer;
  transition: background var(--duration-fast) ease, transform var(--duration-fast) ease;
}
.date-group-item:hover { background: var(--panel-hover); transform: translateX(2px); }
.date-group-item img { width: 42px; height: 42px; border-radius: var(--radius-md); object-fit: cover; flex-shrink: 0; }
.date-group-icon { font-size: var(--fs-2xl); flex-shrink: 0; width: 42px; height: 42px; display: flex; align-items: center; justify-content: center; background: var(--inhalt-kachel); border-radius: var(--radius-md); }
.date-group-info { flex: 1; min-width: 0; }
.date-group-title { font-weight: var(--fw-semibold); font-size: var(--fs-lg); }
.date-group-subtitle { color: var(--text-dim); font-size: var(--fs-sm); margin-top: var(--space-1); }

/* ---------- Kachel-Grid (Releases / Geburtstage) ---------- */
.tile-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--space-8); }
/* Feste Hoehe (--fixed-content-height, wie ueberall sonst) fuer die eigenstaendigen
   Kachel-Raster-Seiten (Medien, Releases/Geburtstage im Kalender-Tab, Links) -- NICHT fuer
   .boards-media-strip (kleiner Screenshot-Streifen INNERHALB der Compendium-Spiel-Detailansicht,
   die soll ihre natuerliche Inhaltshoehe behalten). Gezielt per ID statt ueber die geteilte
   .tile-grid-Klasse, um genau diesen einen Sonderfall auszunehmen.
   align-content:start ist hier entscheidend -- ohne das strecken sich die Grid-ZEILEN (Standard
   ist align-content:stretch, sobald der Container hoeher als der Inhalt ist) ueber die gesamte
   feste Hoehe, wodurch jede einzelne Kachel riesig wirkte und unten viel Leerraum hatte, statt
   dass die Kacheln ihre natuerliche Groesse behalten und der Rest einfach als scrollbarer
   Leerraum UNTER dem Raster liegt. */
/* Mobil-Feedback: "mach das feeling auf mobile noch ausgereifter" -- overscroll-behavior:contain
   wie bereits bei Chat/Kalender/Notiz-Boxen, damit ein Ueberziehen am oberen/unteren Rand dieser
   Haupt-Scrollflaechen (Sammlung, Medien, Links) nicht auf die dahinterliegende Seite durchschlaegt
   bzw. den nativen Browser-Rubber-Band/Pull-to-Refresh ausloest. */
#games-list, #birthdays-tile-grid, #media-detail-grid, #links-tab-grid {
  height: var(--fixed-content-height); overflow-y: auto; align-content: start; overscroll-behavior: contain;
}
/* R49: Nutzer-Feedback -- die erste Zeile sass beim Hover (.link-tile:hover translateY(-1px),
   siehe oben) exakt am oberen Rand des eigenen overflow-y:auto-Containers und wurde dadurch vom
   Container selbst 1px oben abgeschnitten. Kleiner Puffer reicht, ohne Hover-Transform oder
   Scrollverhalten anzufassen. */
#links-tab-grid { padding-top: var(--space-2); }
/* Baustein Suchfeld: dieselbe Pille wie .wiki-search-input (Notizen, Dateien, Sammlung) -- vorher
   hier ohne Rahmen und 36px hoch, dort mit Rahmen und 33px. */
.media-search-input {
  width: 100%; max-width: 360px; min-height: var(--control-h); padding: var(--space-2) var(--space-7); margin-bottom: var(--space-8); border-radius: var(--radius-full);
  /* Ohne Rahmen (2026-09-16: "alle suchleisten haben einen rand mache den weg") -- die Flaeche
     allein grenzt das Feld genug ab; beim Fokus kommt der Ring dazu (Regel weiter unten). */
  border: none; background: var(--panel); color: var(--text); font-size: var(--fs-md); box-sizing: border-box;
  transition: border-color var(--duration-fast) ease, box-shadow var(--duration-fast) ease;
}
/* Im Toolbar-Zeile mit dem "+ Hinzufügen"-Button teilt sich das Suchfeld die Zeile statt sie
   allein zu beanspruchen -- beide Elemente landen so auf gleicher Höhe. max-width:none, sonst
   verhindert das geerbte max-width:360px, dass flex:1 das Feld tatsaechlich bis zum Button
   streckt. */
.games-toolbar .media-search-input { flex: 1; margin-bottom: 0; max-width: none; }
/* R63 Nutzer-Feedback: "mache die suchleiste bei medien mittig wie bei sammlung" -- Button+Suche
   sind weiterhin eine zusammenhaengende Gruppe (der Plus-Button bleibt links neben der Suche
   noetig, da es -- anders als bei Sammlung mit ihren Spalten-eigenen Add-Buttons -- keinen
   anderen Ort fuer den Medien-Upload-Einstieg gibt), aber die ganze Gruppe steht jetzt zentriert
   in der Zeile statt linksbuendig, gleiche Suchleisten-Breite wie .sammlung-search-input. */
.games-toolbar.media-search-toolbar { display: flex; align-items: center; justify-content: flex-start; gap: var(--space-5); }
/* Seit 2026-09-11 stehen die Filter-Chips gleich neben der Ueberschrift, die Suche rueckt nach rechts. */
.media-search-toolbar .sammlung-heading { margin-right: var(--space-4); }
.media-search-toolbar .media-search-input { width: 100%; max-width: 320px; margin-bottom: 0; margin-left: auto; }
/* Hauptaktion des Seitenkopfs: normaler Knopf in Baustein-Hoehe statt des runden 38px-Knopfs */
.media-search-toolbar #media-add-btn { flex-shrink: 0; width: var(--control-h); height: var(--control-h); padding: 0; }
@media (max-width: 720px) {
  /* Nutzer-Feedback: "die suchleiste bei medien ist etwas zu lang und steht rechts über" -- ohne
     explizites min-width:0 kann ein <input> als Flex-Kind (flex:1 kommt von der geteilten
     .games-toolbar .media-search-input-Regel) trotzdem nicht unter seine intrinsische
     Mindestbreite schrumpfen und den "+"-Button daneben aus der Zeile drängen. */
  .media-search-toolbar .media-search-input { max-width: none; min-width: 0; }
}
/* Ordner-Uebersicht als Alben (2026-09-11, vom Nutzer aus drei Entwuerfen gewaehlt: "V1 mit Chips und Jahren") --
   ersetzt die Zweispalten-Seite mit hochkanten Einzelbild-Kacheln (4 Spalten) und der Foto-Wand rechts. Jahres-
   Ueberschrift im vereinbarten Ueberschriften-Stil (wie BESCHREIBUNG/CHAT der Event-Ansicht), darunter so viele
   Alben nebeneinander, wie mit mindestens 190px passen (Desktop fuenf bis sechs), auf dem Handy zwei. */
.media-search-toolbar .media-filter-chips { display: flex; gap: var(--space-3); flex-wrap: wrap; }
/* .filter-btn bringt ein eigenes display mit, das das hidden-Attribut sonst aushebelt -- ein Chip fuer eine Art ohne
   Ordner (renderMediaFolders()) stand dadurch trotzdem da. */
.media-search-toolbar .media-filter-chips[hidden], .media-filter-chips [hidden] { display: none; }
.media-ordner { display: flex; flex-direction: column; }
/* Ohne diese Zeile zeigt der Medien-Reiter am Desktop BEIDES: die Kacheln und den Hinweis daneben.
   Grund: ein "display" in einer Autorenregel schlaegt das display:none, das der Browser fuer
   [hidden] mitbringt -- baueMedienSpalte() setzt .hidden zwar korrekt, sichtbar blieb es trotzdem. */
.media-ordner[hidden], .media-ordner-liste[hidden] { display: none; }
/* Icon und Text klebten im Hinweis aneinander -- der leere Zustand setzt sie sonst ohne Abstand. */
.media-leer .mdi { margin-right: var(--space-3); }
.media-jahr {
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold); color: var(--text-dim); text-transform: uppercase;
  letter-spacing: 0.04em; margin: var(--space-12) 0 var(--space-5);
}
.media-alben { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: var(--space-10) var(--space-8); }
.media-album { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; cursor: pointer; border-radius: var(--radius-md); }
.media-album:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* 6:5, damit das grosse Bild (zwei Drittel der Breite, volle Hoehe) genau 4:5 hat wie der Zuschnitt im Titelbild-
   Fenster (#media-cover-picker) -- ein dort eingestelltes Ordner-Titelbild erscheint hier unveraendert. Das grosse
   Bild steckt in einer eigenen Box, damit ein Zoom (transform: scale) nicht ueber die kleinen Bilder ragt. */
.media-album-collage {
  aspect-ratio: 6 / 5; border-radius: var(--radius-md); overflow: hidden; background: var(--inhalt-kachel);
  display: grid; grid-template-columns: 2fr 1fr; grid-template-rows: 1fr 1fr; gap: 2px; margin-bottom: var(--space-3);
  outline: 2px solid transparent; outline-offset: 2px; transition: outline-color var(--duration-fast) ease;
}
.media-album-collage img { width: 100%; height: 100%; min-height: 0; object-fit: cover; display: block; }
.media-album-gross { grid-row: 1 / 3; overflow: hidden; min-height: 0; }
.media-album-gross img { transform-origin: center; }
.media-album-collage-eins { grid-template-columns: 1fr; grid-template-rows: 1fr; place-items: center; color: var(--text-dim); font-size: var(--fs-3xl); }
.media-album-collage-eins .media-album-gross { grid-row: auto; width: 100%; height: 100%; }
.media-album:hover .media-album-collage { outline-color: var(--accent); }
.media-album-titel { font-weight: var(--fw-medium); font-size: var(--fs-sm); color: var(--text); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.media-album-meta { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-sm); color: var(--text-dim); min-width: 0; }
.media-album-meta span { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.media-album-meta .mdi { font-size: var(--fs-md); flex-shrink: 0; }
@media (max-width: 720px) {
  .games-toolbar.media-search-toolbar { flex-wrap: wrap; }
  /* Die Art-Chips braucht es auf dem Handy nicht (2026-09-15: "den filter in medien alle events
     lan partys brauche ich nicht entferne ihn mobil") -- die Suche daneben reicht. */
  .media-search-toolbar .media-filter-chips,
  .app-context > .media-filter-chips { display: none !important; }
  .media-alben { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-8) var(--space-6); }
}
/* R61 Nutzer-Feedback: dezenter "Ambilight"-Lichtschein hinter jeder Kachel (seit 2026-09-11 nur noch bei den
   Kacheln der Sammlung, die das Medien-Kachel-Design uebernommen haben -- der Medien-Tab zeigt Alben) -- eigener, nicht
   geclippter Wrapper (die eigentliche Kachel braucht overflow:hidden fuer ihren Blur-Hintergrund,
   der Glow soll aber ueber den Kachelrand hinausbluten). Wrapper traegt jetzt role/tabindex
   (siehe buildMediaTile() in app.js), nicht mehr die innere .tile-card. */
.media-game-tile-wrap { position: relative; cursor: pointer; border-radius: 10px; }
.media-game-tile-wrap:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.media-game-tile-glow {
  position: absolute; inset: -14px; z-index: -1; border-radius: var(--radius-xl);
  background-image: var(--tile-bg-image, none); background-size: cover; background-position: center;
  filter: blur(22px) saturate(1.5) brightness(0.85); opacity: 0.5;
  transition: opacity var(--duration-base) ease;
}
.media-game-tile-wrap:hover .media-game-tile-glow { opacity: 0.75; }
/* Nutzer-Feedback (Nachtrag): "der leuchtende hintergrund ... ist noch so abgeschnitten zwischen
   den ordnern" -- 12px Spaltenabstand reicht trotzdem nicht: der -14px-Inset des Glows allein ragt
   schon weiter in die Luecke hinein, als die Luecke ueberhaupt breit ist, wird also von der
   blickdichten Nachbarkachel scharfkantig abgeschnitten statt weich auszulaufen. Verkleinert den
   Glow selbst passend zur mobilen Luecke, statt die Luecke (und damit die Kachelbreite) weiter
   aufzublasen. Muss NACH der Basisregel oben stehen, sonst verliert die Media-Query bei gleicher
   Selektor-Spezifitaet gegen die spaeter im Stylesheet stehende Basisregel. */
@media (max-width: 560px) {
  .media-game-tile-glow { inset: -5px; filter: blur(9px) saturate(1.5) brightness(0.85); }
}
/* .tile-card.media-game-tile (statt nur .media-game-tile): braucht hoehere Spezifitaet als die
   allgemeine .tile-card-Basisregel weiter unten. Deren flex-direction:column passt jetzt genau --
   Bild oben, Titelleiste als eigene Zeile darunter (siehe naechster Kommentar), kein Override mehr
   noetig. R61 Nutzer-Feedback: kein Rahmen mehr (explizit border:none, statt sich auf den geerbten
   transparenten 1px-Rahmen der .tile-card-Basisregel zu verlassen). */
.tile-card.media-game-tile {
  position: relative; overflow: hidden; border: none; background: var(--inhalt-kachel);
}
/* Nutzer-Feedback (Kehrtwende, mehrere Runden): "ich finde das alles nicht so schön ... der
   hintergrund der ordner ist das bild ... unten ist einfach ein kleiner grauer sehr simpler balken
   mit titel" -- dann weiter: "mache die überschrift immer einzeilig und die kacheln viereckig und
   unter das viereck die überschrift das es bissl länger ist als ein viereck". Struktur komplett
   umgebaut: das Bild ist jetzt ein eigenes quadratisches Element (.media-game-tile-img-wrap,
   aspect-ratio 1/1) statt die ganze Kachel zu fuellen, die Titelleiste (.media-game-tile-header)
   steht als eigene Zeile IM NORMALEN FLUSS darunter (nicht mehr absolut ueberlagert) -- macht die
   Gesamtkachel dadurch von selbst "ein bisschen laenger als ein Viereck", ganz ohne Overlay/Blur/
   Gradient. Siehe buildMediaTile() in app.js fuer das neue Markup. */
/* Nutzer-Feedback (Folgerunde): "mache die gerne etwas höher von der maske" -- statt exakt
   quadratisch (1/1) jetzt etwas hochkantiger (4/5), gleiches Seitenverhaeltnis wie der Zuschnitt
   im Titelbild-Auswahl-Popup (#media-cover-picker), damit dort und in der Kachel exakt dasselbe
   zu sehen ist. */
.media-game-tile-img-wrap {
  position: relative; aspect-ratio: 4 / 5; overflow: hidden; flex-shrink: 0;
}
/* Nutzer-Feedback (Folgerunde): "die titelbilder sollten ... so hin geschoben werden können das es
   auch im ordner übersicht so erscheint und passt" -- statt eines background-image-Divs jetzt ein
   echtes <img> mit object-position/transform:scale, damit ein per Titelbild-Auswahl-Popup
   (#media-cover-picker-modal) gesetztes Foto samt Position/Zoom 1:1 genauso in der Kachel landet
   wie im Auswahl-Popup selbst (siehe setupCoverDrag()/buildMediaTile() in app.js). Fehlt eine
   explizite Position/Zoom (kein Titelbild gesetzt), liefern die CSS-Defaults unten dasselbe
   "einfach mittig, ungezoomt" Verhalten wie vorher der background-image-Div. */
.media-game-tile-bg {
  position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: var(--tile-bg-position, center); transform: scale(var(--tile-bg-zoom, 1));
  transform-origin: center;
}
.media-game-tile-header {
  display: flex; align-items: center; gap: var(--space-4); min-width: 0;
  padding: var(--space-4) var(--space-5); background: var(--panel-2);
}
/* Nutzer-Feedback: "mache die überschrift immer einzeilig" -- statt des bisherigen zweizeiligen
   Umbruchs jetzt einzeilig mit Ellipsis-Abschneidung, passend zur eigenen (nicht mehr ueber das
   Foto gelegten) Titelzeile -- kein Text-Schatten/weisse Zwangsfarbe mehr noetig, normale
   Kartentext-Optik wie ueberall sonst in der App. */
.media-game-tile-title {
  color: var(--text); font-weight: var(--fw-semibold); font-size: var(--fs-lg);
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis; min-width: 0;
}
/* R50: Nutzer-Feedback -- welcher Ordner-Typ (Spiel/Film/Serie/LAN-Party/Event/Allgemein) das
   ist, war der Kachel selbst nicht anzusehen (nur am aktiven Filter-Chip erkennbar). Kleines
   Label oben rechts auf dem Bildbereich (jetzt relativ zu .media-game-tile-img-wrap statt der
   ganzen Kachel, seit die Titelleiste nicht mehr darueber liegt). */
.media-game-tile-type-badge {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  background: color-mix(in srgb, var(--panel-2) 88%, transparent);
  color: var(--text-on-color); font-size: var(--fs-2xs); font-weight: var(--fw-semibold);
  padding: var(--space-1) var(--space-4); border-radius: var(--radius-xl); pointer-events: none;
}
.tile-card {
  position: relative; display: flex; flex-direction: column;
  border-radius: var(--radius); overflow: hidden; scrollbar-gutter: auto; cursor: pointer;
  background: var(--inhalt-kachel); border: 1px solid transparent;
  transition: transform var(--duration-fast) ease, box-shadow var(--duration-fast) ease;
}
.tile-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
.tile-card-image-wrap { position: relative; width: 100%; flex-shrink: 0; aspect-ratio: 3 / 4; background: var(--panel-2); overflow: hidden; }
.tile-card.tile-square .tile-card-image-wrap { aspect-ratio: 4 / 3; }
.tile-card.tile-wide .tile-card-image-wrap { aspect-ratio: 16 / 9; }
.tile-card-image-wrap video { width: 100%; height: 100%; object-fit: cover; display: block; }
.tile-card-image-wrap .tile-video-badge {
  position: absolute; top: 8px; right: 8px; background: var(--on-image-bg); color: var(--text-on-color);
  border-radius: 50%; width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
}
.tile-card-image-wrap img { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; }
.tile-card-image-fallback { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; font-size: var(--fs-display); color: var(--text-dim); }
.tile-card-badges {
  position: absolute; bottom: 8px; left: 8px; display: flex; gap: var(--space-2); z-index: 1;
}
.tile-badge {
  display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--fs-2xs); font-weight: var(--fw-semibold);
  background: var(--on-image-bg); color: var(--text-on-color); padding: var(--space-1) var(--space-3); border-radius: var(--radius-xl);
}
.tile-badge .mdi { font-size: var(--fs-sm); }
.tile-badge.tile-badge-attending { color: var(--success-text); }
.tile-card-pin-btn {
  position: absolute; top: 8px; left: 8px; z-index: 1;
  background: var(--on-image-bg); color: var(--text-on-color); border: none; border-radius: 50%;
  width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.tile-card-pin-btn:hover { background: var(--on-image-bg-hover); }
.tile-card-pin-btn.pinned { color: var(--highlight); }
/* Nicht angepinnt: nur bei Hover ueber die Kachel sichtbar (wie der Loeschen-Button) --
   angepinnt bleibt dauerhaft sichtbar/gold, damit der Status auch ohne Hover ablesbar ist. */
.tile-card-pin-btn:not(.pinned) { opacity: 0; transition: opacity var(--duration-fast) ease, background var(--duration-fast) ease; }
.tile-card-image-wrap:hover .tile-card-pin-btn:not(.pinned) { opacity: 1; }
.tile-card-delete-btn {
  position: absolute; top: 8px; right: 8px; z-index: 1;
  background: var(--on-image-bg); color: var(--text-on-color); border: none; border-radius: 50%;
  width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; cursor: pointer;
  opacity: 0; transition: opacity var(--duration-fast) ease, background var(--duration-fast) ease;
}
.tile-card-image-wrap:hover .tile-card-delete-btn { opacity: 1; }
.tile-card-delete-btn:hover { background: var(--danger-hover); }
.tile-select-mode .tile-card-image-wrap { cursor: pointer; }
.tile-card-select-check {
  /* R43: Nutzer-Feedback -- Haekchen sass zu dicht am oberen Bildrand, wirkte dadurch vom
     abgerundeten Kartenrahmen angeschnitten. Etwas weiter nach unten, bleibt aber oben rechts auf
     Hoehe des Bildes (wie Pin-/Loeschen-Button). */
  position: absolute; top: 14px; right: 8px; z-index: 1;
  width: 24px; height: 24px; border-radius: 50%; border: 2px solid rgba(255,255,255,0.8);
  background: var(--on-image-bg); display: flex; align-items: center; justify-content: center; color: transparent;
}
.tile-card-select-check.checked { background: var(--accent); border-color: var(--accent); color: var(--text-on-color); }
.tile-card.tile-selected { outline: 2px solid var(--accent); outline-offset: -2px; }
#media-detail-select-btn.active { background: var(--accent); color: var(--text-on-color); }
.wiki-tree-edit-toggle-btn.active { background: var(--accent); color: var(--text-on-color); }
/* R43: "N loeschen"-Button setzte die Zahl per Textknoten davor -- als Flex-Item kollabierte der
   Leerraum zwischen Textknoten und <span>, sodass Zahl und Label ohne sichtbaren Abstand
   aneinanderklebten ("0delete"). Expliziter gap statt auf den Leerraum im Textknoten verlassen. */
#media-detail-bulk-delete-btn { display: inline-flex; align-items: center; gap: var(--space-2); }
.tile-card-body { padding: var(--space-5) var(--space-6); display: flex; flex-direction: column; gap: var(--space-2); }
.tile-card-title { font-weight: var(--fw-semibold); font-size: var(--fs-lg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tile-card-subtitle { color: var(--text-dim); font-size: var(--fs-sm); }
/* ---------- Screenshot-/Video-Ordner: normales Kachel-Grid ---------- */
/* R60 Nutzer-Feedback: das vorherige CSS-Mehrspalten-("Masonry")-Layout scrollte in Kombination
   mit dem hoehenbegrenzten Popup-.modal-body NICHT normal nach unten, sondern erzeugte bei
   Ueberlauf neue Spalten NACH RECHTS (CSS-Mehrspalten-Layout braucht eine begrenzte Hoehe, um
   ueberhaupt mehrspaltig zu werden -- genau das hat das Popup jetzt). "Schoen grid-maessig" statt
   Masonry gewuenscht -- normales .tile-grid wie ueberall sonst in der App (#media-game-tiles
   etc.), eigene Zellengroesse statt der Basis-200px, da das Popup (#media-game-detail) jetzt
   ausserhalb von #tab-screenshots liegt (dessen 300px-Override greift hier nicht mehr). */
#media-detail-videos-grid, #media-detail-photos-grid { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--space-10); }
/* R61 Nutzer-Feedback: Fotos innerhalb eines Ordners jetzt "wie bei Google Fotos" -- jedes Bild
   behaelt sein eigenes Seitenverhaeltnis statt in eine feste Box zugeschnitten zu werden. BEWUSST
   kein CSS-Mehrspalten-Layout (column-count) -- das hat sich innerhalb dieses hoehenbegrenzten,
   scrollenden .modal-body schon einmal als Bug erwiesen (Ueberlauf erzeugte neue Spalten NACH
   RECHTS statt normal nach unten zu scrollen, siehe R60-Kommentar direkt darueber). Stattdessen
   ein echtes CSS-Grid mit feinem 1px-Zeilenraster; row-gap:0 + margin-bottom auf der Kachel statt
   gap, damit der per JS gesetzte grid-row-end:span N (siehe renderTileGrid() in app.js) exakt der
   Bildhoehe entspricht, ohne dass der Grid-eigene Row-Gap die Rechnung verfaelscht. */
#media-detail-photos-grid {
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  grid-auto-rows: 1px; row-gap: 0; column-gap: var(--space-10);
}
#media-detail-photos-grid .tile-card { margin-bottom: var(--space-10); }
#media-detail-photos-grid .tile-card-image-wrap { aspect-ratio: auto; height: auto; }
#media-detail-photos-grid .tile-card-image-wrap img { height: auto; object-fit: initial; }
/* Nutzer-Feedback: "versuche ein bisschen das an dem ios fotos zu orientieren und die fotos dann
   so anzuordnen. gerade sieht man nur ein foto pro zeile" -- Ursache: das direkt darueber stehende
   Desktop-"Google Fotos"-Masonry-Raster nutzt minmax(200px,1fr), was auf Handybreite abzueglich
   .modal-body-Padding nur fuer eine Spalte reicht. Auf Mobile stattdessen ein dichtes quadratisches
   Raster wie in der iOS-Fotos-App (mehrere Spalten, minimaler Abstand, zugeschnittene Kacheln statt
   eigenem Seitenverhaeltnis). MUSS nach dem Desktop-Block hier stehen (nicht z.B. im
   #media-game-detail-720px-Mobile-Block weiter oben in der Datei) -- bei gleicher Spezifitaet
   gewinnt bei CSS immer die spaeter im Quelltext stehende Regel, unabhaengig von der Media-Query
   (bereits einmal an dieser Stelle als Bug reproduziert). grid-row-end braucht !important:
   renderTileGrid() setzt dafuer beim Masonry-Modus (opts.masonry, siehe app.js) eine INLINE
   Style-Angabe pro Kachel, die eine normale Regel sonst nicht schlagen wuerde. */
@media (max-width: 720px) {
  #media-detail-videos-grid, #media-detail-photos-grid {
    grid-template-columns: repeat(3, 1fr); gap: var(--space-2);
  }
  #media-detail-photos-grid { grid-auto-rows: auto; row-gap: var(--space-2); column-gap: var(--space-2); }
  #media-detail-photos-grid .tile-card { margin-bottom: 0; grid-row-end: auto !important; }
  #media-detail-photos-grid .tile-card-image-wrap { aspect-ratio: 1 / 1; height: auto; }
  #media-detail-photos-grid .tile-card-image-wrap img { height: 100%; object-fit: cover; }
}
/* R60 Nutzer-Feedback: kein Screenshots/YouTube-Umschalter mehr -- stattdessen Ueberschriften
   ueber den beiden Abschnitten (nur sichtbar, wenn tatsaechlich beide Arten vorhanden sind, siehe
   loadMediaDetailGrid()). */
.media-detail-section-heading {
  display: flex; align-items: center; gap: var(--space-4); margin: 0 0 var(--space-6);
  font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim);
}
#media-detail-videos-section { margin-bottom: var(--space-12); }
/* ---------- Bildraster in der Event-Vorschau ---------- */
/* Das Dashboard-Widget "Neueste Screenshots/Videos" benutzt diese Klasse seit 2026-09-14 NICHT
   mehr (es fuellt seine Kachel jetzt wie ein Album, siehe .bilder-album weiter unten) -- hier bleibt
   nur noch das feste Raster der Event-Vorschau. */
.media-widget-grid {
  display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: var(--space-3);
  height: 280px;
}
/* Nutzer-Feedback (weitere Kehrtwende): "die medien werden um 3 fotos quasi ein 3x2 raster
   ergänzt und zeigen einfach nur bilder" -- Hochladen/Album öffnen/Medienordner sind ins neue
   Funktionen-Raster gewandert (siehe .event-preview-funktionen-grid), diese Sektion zeigt jetzt
   nur noch reine Bild-/Platzhalter-Kacheln, 6 statt 3 (3 Spalten x 2 Zeilen). Nur in der Event-
   Vorschau, das Dashboard-Widget (gleiche .media-widget-grid-Klasse) behaelt sein eigenes
   2x2-Raster unveraendert. */
/* Seit 2026-09-11 ueber die Klasse der gemeinsamen Medien-Kachel (eventMediaTileHtml()) statt der ID der
   Ansicht -- dieselbe Kachel steht im Formular. */
.event-media-tile-grid {
  grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(2, 1fr); height: 150px;
}
/* Nutzer-Feedback: "die placeholder brauchen auch einen background der sich minimal abhebt" -- die
   app-weite .media-widget-grid-cell(-empty)-Basisregel nutzt denselben var(--panel-2)-Ton wie
   #event-preview-media-section selbst (.event-preview-section), dadurch verschwamm der Platzhalter
   bisher komplett mit der Karte dahinter. Nur hier (nicht global -- betrifft sonst z.B. das
   Dashboard-Widget) einen Ton heller. */
.event-media-tile-grid .media-widget-grid-cell,
.event-media-tile-grid .media-widget-grid-cell-empty { background: var(--tint); }
/* Bis drei Bilder eine Reihe, ohne Bilder nur "Noch keine Bilder im Album" mittig und etwas kleiner
   (2026-09-11, statt sechs Platzhalter-Kacheln, siehe renderEventMediaTileGrid()). */
.event-media-tile-grid.event-media-tile-grid-einzeilig { grid-template-rows: 1fr; height: 72px; }
.event-media-tile-grid.event-media-tile-grid-leer { display: flex; align-items: center; justify-content: center; height: auto; padding-block: var(--space-6); }
.event-media-tile-leer { font-size: var(--fs-sm); color: var(--text-dim); text-align: center; }
/* Ausgegrautes "Album" (keine Bilder): .text-btn:disabled blendet ab, der Hover der Kachel-Knoepfe soll nicht aufleuchten. */
.event-preview-media-actions .event-preview-funktion-btn:disabled { pointer-events: none; }
.media-widget-grid-cell {
  position: relative; border-radius: var(--radius-md); overflow: hidden; background: var(--inhalt-kachel); cursor: pointer;
}
.media-widget-grid-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Nutzer-Feedback: "es sollten immer placeholders ... angezeigt werden falls keine fotos da
   sind" -- leere Kachel statt img, gleiche Optik wie .event-photo-widget-cell-empty im Formular. */
/* Nutzer-Feedback: "bei den medien unten die foto placeholder mit einem dezenten background
   ausstatten" -- .media-widget-grid-cell hat zwar bereits var(--panel-2), explizit hier nochmal
   gesetzt (statt sich auf die Vererbung von der Basisklasse zu verlassen), damit die leere Kachel
   auch als eigenstaendiger, klar sichtbarer Platzhalter erkennbar ist. */
.media-widget-grid-cell-empty {
  display: flex; align-items: center; justify-content: center; cursor: default;
  color: var(--text-dim); font-size: var(--fs-2xl); background: var(--panel-2);
}
/* ---------- Dashboard-Widget "Neueste Bilder": Album ---------- */
/* Nutzerwunsch 2026-09-14: "ein schoenes grid ... wo bilder verschiedenster groesse angezeigt
   werden und nicht alles 4eckig" -- Spalten, Zeilenhoehe und die Spans der einzelnen Bilder setzt
   passeBilderAlbumAn() in app.js aus der tatsaechlichen Kachelgroesse und den Bildmassen; hier
   steht nur, was davon unabhaengig ist. Nicht scrollen, sondern zuschneiden -- ein Album zeigt so
   viel, wie die Flaeche hergibt. */
#media-widget-list { overflow: hidden; padding-right: 0; }
.bilder-album {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
  grid-auto-rows: 100px;
  grid-auto-flow: row dense;
  gap: var(--space-2);
  align-content: start;
}
.bilder-album-zelle {
  position: relative; min-height: 0; border-radius: var(--radius-md); overflow: hidden;
  background: var(--inhalt-kachel); cursor: pointer;
}
.bilder-album-zelle img { width: 100%; height: 100%; object-fit: cover; display: block; transition: scale var(--duration-base) ease; }
.bilder-album-zelle:hover img { scale: 1.06; }
/* Was nicht mehr vollstaendig in die Flaeche passt, wird unsichtbar -- aber bleibt im Raster
   stehen (visibility statt display), sonst ruecken die uebrigen nach und der Zuschnitt pendelt. */
.bilder-album-zelle-aus { visibility: hidden; }
/* "+N" auf der letzten sichtbaren Kachel, sonst wirkt das Album vollstaendig, obwohl es das nicht ist. */
.bilder-album-mehr {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  background: var(--on-image-bg); color: var(--text-on-color);
  font-size: var(--fs-lg); font-weight: var(--fw-bold);
}
.bilder-album-zelle .tile-video-badge {
  position: absolute; top: 6px; right: 6px; background: var(--on-image-bg); color: var(--text-on-color);
  border-radius: 50%; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center;
}

.media-widget-grid-cell .tile-video-badge {
  position: absolute; top: 6px; right: 6px; background: var(--on-image-bg); color: var(--text-on-color);
  border-radius: 50%; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center;
}
/* Nutzer-Feedback: "zeige rechts neben dem alle medien button eine zahl in klammern wie viele
   fotos das sind in lila am besten fett" -- eigenes <span>, damit nur die Zahl selbst (nicht der
   ganze Button/Text) lila+fett ist. Gleicher Violett-Ton wie CATEGORY_COLORS.lan_party in app.js,
   einzige "Lila" bereits im bestehenden Farbschema. */
.event-preview-media-count { color: #a855f7; font-weight: var(--fw-bold); }
/* "Alle Medien" ohne Medien-Ordner -- kleines Kachel-Popover statt direkt in die Lightbox zu
   springen (siehe showEventPhotosOverviewPopover() in app.js). */
.event-photos-overview-popover { width: 260px; max-height: 320px; overflow-y: auto; }
.event-photos-overview-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-3); }
.event-photos-overview-cell { aspect-ratio: 1; }
/* .event-preview-media-folder-btn-filled wird nur noch vom Bearbeiten-Formular genutzt (siehe
   #event-form-media-folder-toggle-btn/#event-form-media-upload-btn in index.html) -- die
   Event-Vorschau hat ihr eigenes, neutrales .event-preview-funktion-btn-Styling (siehe oben). */
.event-preview-media-folder-btn-filled { background: var(--knopf); border-color: transparent; }
.event-preview-media-folder-btn-filled:hover { background: var(--panel); border-color: transparent; }
/* Nutzer-Feedback: "mache die fotos kleiner und in einer 3x3 reihe. wir machen das medien ding
   zweispaltig, linke spalte sind 'funktionen'" -- der Umschalter steht jetzt alleine (nicht mehr
   neben einem gleich breiten "Medien hochladen"-Button) in der schmalen Funktionen-Spalte, daher
   kein eigenes flex:1/justify-content:center mehr -- erbt stattdessen die normale linksbuendige
   volle-Breite-Optik von .event-preview-funktion-btn (siehe weiter oben). Gilt auch weiterhin fuer
   den Bearbeiten-Formular-Umschalter (#event-form-media-folder-toggle-btn), der denselben Stil
   nutzt. */
/* Nutzer-Feedback (weitere Folgerunde): "der medienordner button ist rechtsbündig. mache ihn
   bitte so, dass wenn aktiv er grün hinterlegt ist wie der teilnahme button und gelb wenn
   deaktiviert (nicht mehr rot)" -- width:auto statt der geerbten width:100% (.event-preview-
   funktion-btn) macht daraus einen kompakten, am linken Rand verankerten Pill-Button statt eines
   ueber die volle Zeilenbreite gestreckten Elements. Farbe kommt jetzt als echter Hintergrund
   (wie .event-preview-attendance-btn.active[data-status="attending"]/"tentative"), nicht mehr nur
   als Textfarbe. */
.event-preview-media-folder-toggle-btn {
  width: auto; display: inline-flex; gap: var(--space-2); white-space: nowrap;
  border-radius: var(--radius-full); padding: var(--space-2) var(--space-5); transition: none;
}
/* Nutzer-Feedback: "mache das medienordner icon als einen ordner und grün wenn es einen ordner
   gibt und als einen durchgestrichenen ordner oder so und gelb wenn es keinen ordner gibt" --
   Icon-Glyphe selbst wechselt in app.js (mdi-folder <-> mdi-folder-off).
   Folgerunde: "der medienordner button sollte einen haken vorne haben wenn er aktiv ist und ein x
   wenn nicht" -- zusaetzliches Status-Icon (.event-preview-media-folder-status-icon).
   Weitere Folgerunde: voller Hintergrund statt nur Textfarbe (siehe oben), Icon folgt per
   color:inherit derselben dunklen Kontrastfarbe wie der Text statt einer eigenen, davon
   unabhaengigen Gruen/Rot-Faerbung (wirkte sonst inkonsistent gegen den neuen farbigen
   Hintergrund). */
/* Seit 2026-09 getoent statt voll gefuellt und mit derselben Ecke wie "Medien hochladen" daneben: die
   knallgruene Pille neben einem ruhigen Knopf wirkte wie ein Fremdkoerper. Gruen/gelb bleibt als
   Zustandsfarbe erhalten, jetzt als Schrift auf leichter Toenung. */
.event-preview-media-folder-toggle-btn.folder-active { background: color-mix(in srgb, var(--success) 16%, transparent); color: var(--success); border-color: transparent; }
.event-preview-media-folder-toggle-btn.folder-inactive { background: color-mix(in srgb, var(--warning) 16%, transparent); color: var(--warning); border-color: transparent; }
.event-preview-media-folder-toggle-btn.folder-active:hover { background: color-mix(in srgb, var(--success) 26%, transparent); color: var(--success); }
.event-preview-media-folder-toggle-btn.folder-inactive:hover { background: color-mix(in srgb, var(--warning) 26%, transparent); color: var(--warning); }
.event-form-linked-info.event-folder-toggle-block #event-form-media-folder-toggle-btn { border-radius: var(--radius-md); padding: var(--space-2) var(--space-7); }
/* margin-left:0 hebt das auto der aelteren Grundregel weiter oben auf -- es schob das Symbol im
   gestreckten Knopf nach rechts, links blieb eine leere Flaeche stehen. */
.event-preview-media-folder-status-icon { flex-shrink: 0; color: inherit; margin-left: 0; }
/* R49 P3.3: YouTube-Kacheln im Compendium-Detail-Panel -- gleiches Kachel-Aussehen wie das
   Dashboard-Widget, aber etwas flacher, da sie nur einer von mehreren gestapelten Abschnitten im
   Info-Panel sind statt dessen einziger Inhalt. */
.compendium-youtube-grid { height: 180px; }
.compendium-youtube-more-link {
  display: block; margin-top: var(--space-4); font-size: var(--fs-sm); color: var(--accent); text-align: center;
}
.compendium-youtube-more-link:hover { text-decoration: underline; }
/* ---------- Wiki (zweispaltig) ---------- */
/* Feste Hoehe (--fixed-content-height, wie ueberall sonst) statt vorher unbegrenzter, mit der
   ganzen Seite mitwachsender Hoehe -- Sidebar und Hauptbereich scrollen jetzt jeweils intern
   (wie beim Compendium-Tab), statt die gesamte Seite in die Laenge zu ziehen. */
/* Nutzer-Feedback: "bitte bei notes und tools die länge der seite der bildschirmseite anpassen wie
   auf den anderen seiten auch" -- gleiche echte viewport-relative Hoehe wie beim Kalender-Tab
   (.year-agenda-col, siehe dortiger Kommentar), statt der sonst bewusst statischen
   --fixed-content-height, die auf kuerzeren Bildschirmen zu Seiten-Ueberlauf fuehrte. 80px =
   main{padding:20px ... 60px}, damit die Box exakt im sichtbaren Bereich endet. */
.wiki-layout { display: grid; grid-template-columns: 300px 1fr; gap: var(--space-10); align-items: start; height: var(--inhalt-hoehe); }
@media (max-width: 880px) { .wiki-layout { grid-template-columns: 1fr; height: auto; } }
/* Zonen-Hintergrund + Trennlinie, analog zum Dashboard-Agenda/Widgets-Bereich. Kein
   position:sticky mehr noetig -- die Sidebar hat jetzt selbst eine feste Hoehe (100% der fixen
   Layout-Hoehe) mit eigenem internen Scroll statt mit der Seite mitzuscrollen. */
.wiki-sidebar {
  height: 100%; overflow-y: auto;
  background: var(--panel);
  border-radius: var(--radius); padding: var(--space-7);
}
@media (max-width: 880px) { .wiki-sidebar { border-right: none; height: auto; } }
.wiki-search-input {
  width: 100%; min-height: var(--control-h); padding: var(--space-2) var(--space-7); margin-bottom: var(--space-5); border-radius: var(--radius-full);
  border: none; background: var(--panel); color: var(--text); font-size: var(--fs-md); box-sizing: border-box;
  transition: border-color var(--duration-fast) ease, box-shadow var(--duration-fast) ease;
}
.wiki-list { list-style: none; padding: 0 3px 0 0; margin: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.wiki-list-item {
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
  transition: background var(--duration-fast) ease, border-color var(--duration-fast) ease;
}

.wiki-list-item.active { background: var(--aktiv-kachel); border-color: var(--accent); color: var(--aktiv-text); font-weight: var(--fw-semibold); }
/* R58 Nutzer-Feedback: hebt Notizen, in denen gerade live jemand ist, direkt in der Liste
   hervor -- Umrandung + kleine farbige Punkte (Cursor-Farbe der jeweiligen Person, siehe
   userColor()) statt eines separaten Ticker-Eintrags in der Menueleiste. */
.wiki-list-item.wiki-list-item-live { border-color: var(--accent); }
.wiki-list-presence-dots { display: flex; gap: var(--space-2); flex-shrink: 0; }
.wiki-list-presence-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; box-shadow: 0 0 0 1.5px var(--panel-2); }
.wiki-list-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* R60 Nutzer-Feedback: die Umbenennen-/Loeschen-Buttons erschienen bisher bei JEDEM Hover ueber
   eine Zeile -- nervt beim normalen Durchklicken. Jetzt standardmaessig unsichtbar UND
   unklickbar, erscheinen nur noch, wenn der neue Bearbeiten-Umschalter aktiv ist (.edit-mode auf
   #wiki-list/#files-folder-tree, siehe wiki-edit-toggle-btn/files-edit-toggle-btn). */
.wiki-list-item .icon-btn { opacity: 0; pointer-events: none; transition: opacity var(--duration-fast) ease; }
.wiki-list.edit-mode .wiki-list-item .icon-btn { opacity: 1; pointer-events: auto; }
body.no-edit .wiki-list-item .wiki-edit { display: none; }
.wiki-main {
  background: var(--panel); border: none; border-radius: var(--radius);
  /* R58 Bugfix: .wiki-main scrollte bisher als Ganzes, mit einer sticky Titelleiste ONEND darin --
     ein zweites sticky Element (die Editor-Werkzeugleiste) liess sich darin nicht sauber
     stapeln (JS-berechneter top-Versatz war fragil, driftete bei Layout-Aenderungen). Jetzt
     dasselbe Baukasten-Muster wie .boards-main/#boards-content (siehe dort): der ganze Bereich
     scrollt NICHT mehr selbst, Titelleiste + Werkzeugleiste sind normale, nie scrollende
     Flex-Kinder, nur die eigentlichen Inhalte (Editor-Text, Info-Sidebar) scrollen intern.
     Kein position:sticky mehr noetig. */
  height: 100%; display: flex; flex-direction: column; overflow: hidden;
}
@media (max-width: 880px) { .wiki-main { height: auto; overflow: visible; } }
#wiki-page-view { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.wiki-empty-state { color: var(--text-dim); text-align: center; padding: 60px var(--space-10); font-size: var(--fs-body); }
/* Icon ueber dem Text statt eines Emojis davor (Schritt 4 der Design-Ueberarbeitung) */
.wiki-empty-state .mdi { display: block; font-size: var(--fs-3xl); margin-bottom: var(--space-4); opacity: 0.6; }
/* Nutzerwunsch 2026-09-14: "mache die titelleiste bei notes wie bei den alben nicht dunkel. mache
   das auch so bei rooms" -- die Kopfzeile traegt keine eigene Flaeche mehr, sie sitzt auf der
   Flaeche der Ansicht (im Album ist das seit 19.95 schon so). Betrifft ueber dieselbe Klasse auch
   die Sammlungs-Fenster, wo die dunklere .modal-header-bar-Flaeche denselben Bruch erzeugte. */
/* Zwei Klassen im Selektor, damit die spaetere .modal-header-bar-Regel (--panel-2) nicht wieder
   gewinnt -- die Kopfzeilen tragen beide Klassen. */
.modal-header-bar.wiki-header-bar {
  border-radius: var(--radius) var(--radius) 0 0; align-items: flex-start;
  background: none; flex-shrink: 0;
}
.wiki-header-titles { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.wiki-header-titles h2 { white-space: normal; font-size: var(--fs-2xl); font-weight: var(--fw-bold); display: flex; align-items: center; gap: var(--space-4); }
/* Nutzer-Feedback: "zeige in der titelleiste ... was gerade geschaut wird" -- ersetzt die
   Spiel-Verknuepfung fuer Watch-Together-Raeume, siehe renderChatRoomWatchNowPlaying() in app.js. */
.chatroom-watch-nowplaying {
  display: flex; align-items: center; gap: var(--space-3); font-size: var(--fs-md); color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chatroom-watch-nowplaying .mdi { color: var(--youtube); flex-shrink: 0; }
/* R60 Nutzer-Feedback: Byline und Ordner-Pfad teilen sich jetzt eine Zeile statt zwei -- die
   Titelleiste wird dadurch insgesamt eine Zeile niedriger. */
.wiki-header-meta-row { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.wiki-page-byline { color: var(--text-dim); font-size: var(--fs-sm); display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.wiki-byline-sep { opacity: 0.5; }
.wiki-page-path { color: var(--text-dim); font-size: var(--fs-sm); display: flex; align-items: center; gap: var(--space-3); }
/* R60 Nutzer-Feedback: Loeschen-Button ist jetzt immer sichtbar (statt nur im Lesemodus) und
   sitzt oben rechts in der Sidebar statt in der Titelleiste, analog zu
   .chatroom-info-panel-top. */
/* Umschalter (Edit/Read) links, Löschen rechts -- beide seit 2026-09-15 im Abschnitt "Funktionen"
   der rechten Spalte. */
.wiki-sidebar-top-actions { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.wiki-sidebar-delete-btn:hover { background: var(--danger); color: var(--text-on-color); }
.wiki-page-icon-display { display: inline-flex; border-radius: var(--radius-sm); padding: var(--space-1); }
.wiki-page-icon-display.clickable { cursor: pointer; }
.wiki-page-icon-display.clickable:hover { background: var(--panel-hover); }
/* Wie in Rooms: der Inhalt fuellt die Flaeche der Ansicht ohne Einrueckung -- und liegt darin auf
   einer eigenen, dunkleren Flaeche, genau wie die Chat-Spalte im Raum (2026-09-14: "es fehlen immer
   noch die black hintergruende wie bei rooms in den medienordnern und in den notes"). */
.wiki-body { padding: 0; flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* R57: einzelner, immer gemounteter Tiptap/Yjs-Editor ersetzt das fruehere Lese-/Schreib-
   Pane-Paar -- dezent aufgehellter, abgerundeter "Papier"-Hintergrund (Nutzer-Wunsch) statt
   nahtlos im Panel-Hintergrund zu verschwinden, Innenabstand haelt Text vom Rand fern.
   R58 Nutzer-Feedback: der "Papier"-Hintergrund sitzt jetzt auf der aeusseren .wiki-editor-card
   (umschliesst auch die Werkzeugleiste), .wiki-editor-mount selbst traegt nur noch
   Innenabstand/Textformatierung und ist jetzt der eigentliche interne Scroll-Container
   (wie .boards-info-panel) statt .wiki-main als Ganzes. */
.wiki-editor-card {
  display: flex; flex-direction: column; flex: 1; min-height: 0;
  /* Dieselbe dunkle Flaeche wie .chatroom-chat-col im Raum (2026-09-14) -- buendig mit der Ansicht,
     nicht eingerueckt. */
  background: var(--panel); border-radius: var(--radius); overflow: hidden;
}
/* R60 Nutzer-Feedback: Trennlinie unter der Werkzeugleiste raus, Notiz-Inhalt ruckt dadurch
   naeher an die Werkzeugleiste (weniger Leerraum obendrueber). */
.wiki-editor-mount { flex: 1; overflow-y: auto; padding: var(--space-5) var(--space-7) var(--space-8); line-height: 1.6; overflow-wrap: anywhere; overscroll-behavior: contain; }
/* Werkzeugleiste (Formatierung links, Lesen/Bearbeiten-Umschalter rechts) -- normales, nie
   scrollendes Flex-Kind ganz oben in .wiki-editor-card (siehe Kommentar dort), kein
   position:sticky/JS-Offset mehr noetig. */
/* Die Werkzeugleiste gehoert IN die dunkle Notizflaeche (2026-09-14: "die area der notes (die
   dunkle) ist zu weit unten mache das read und edit innerhalb der dunklen area") -- mit eigener,
   hellerer Flaeche sah sie aus wie ein Streifen darueber, und die dunkle Flaeche begann erst
   darunter. */
.wiki-editor-topbar {
  flex-shrink: 0;
  display: flex; align-items: center; gap: var(--space-5); flex-wrap: wrap;
  padding: var(--space-3) var(--space-5);
  background: none; border-radius: var(--radius) var(--radius) 0 0;
  /* Die Werkzeugleiste bleibt im Lesemodus stehen (nur unsichtbar, siehe .wiki-toolbar-aus) --
     die Hoehe dieser Zeile aendert sich beim Umschalten dadurch nicht. */
}
.wiki-editor-toolbar { display: flex; align-items: center; gap: var(--space-1); flex-wrap: wrap; }
/* Im Lesemodus bleibt die Werkzeugleiste sichtbar, aber ausgegraut (2026-09-15: "blende die
   bearbeiten leiste in den notizen immer ein aber solange nicht auf edit steht graue die buttons
   aus") -- so springt beim Umschalten auch nichts. */
.wiki-editor-toolbar.wiki-toolbar-aus { opacity: 0.4; pointer-events: none; }
.wiki-editor-toolbar.wiki-toolbar-aus .wiki-tb-btn { color: var(--text-dim); }
.wiki-tb-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: var(--radius-sm); border: none; background: transparent;
  color: var(--text-dim); cursor: pointer; font-size: var(--fs-lg);
  transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.wiki-tb-btn:hover { background: var(--panel); color: var(--text); }
.wiki-tb-btn.active { background: var(--accent-tint); color: var(--accent); }
.wiki-tb-sep { width: 1px; height: 18px; background: var(--border); margin: 0 var(--space-2); flex-shrink: 0; }
/* Nutzer-Feedback: Bilder in Notizen -- ohne diese Regel rendert ein eingefuegtes Bild in voller
   Originalgroesse und kann die Notiz-Spalte sprengen. mark[data-highlight] kommt von der neuen
   Highlight-Extension (siehe notes-editor.js), a[href] vom neuen Link-Mark. */
.wiki-editor-mount img { max-width: 100%; border-radius: var(--radius-sm); }
.wiki-editor-mount mark { background: color-mix(in srgb, var(--highlight) 35%, transparent); color: inherit; border-radius: var(--radius-xs); padding: 0 1px; }
.wiki-editor-mount a { color: var(--accent); text-decoration: underline; }
.event-preview-notiz-toolbar { margin-bottom: var(--space-3); padding-bottom: var(--space-3); border-bottom: 1px solid var(--border); }
/* Nutzer-Feedback: "Push-Abo" pro Chat -- Glocke faerbt sich in der Akzentfarbe, sobald der
   Nutzer fuer diesen Chat angemeldet ist (gleiche Optik wie .pin-toggle-btn.pinned). */
#event-preview-chat-subscribe-btn.chat-subscribe-active,
#chatroom-chat-subscribe-btn.chat-subscribe-active { color: var(--accent); }
/* Nutzer-Feedback: "die globale suche sollte nicht auf der rechten seite sondern mittig als
   popup sein" -- aus der rechts angedockten Seitenleiste wurde ein zentriertes Fenster. Die
   Klassennamen bleiben (search-sidebar*), damit agenda.js/Harness-Tests unveraendert greifen;
   nur die Positionierung ist neu. Gleicher z-index-Layer/Backdrop-Ton wie Modals. */
.search-sidebar-backdrop { position: fixed; inset: 0; background: var(--scrim); z-index: var(--z-modal); animation: overlayFade var(--duration-fast) ease both; }
.search-sidebar {
  position: fixed; top: 12vh; left: 50%; translate: -50% 0;
  width: min(620px, calc(100vw - var(--space-12) * 2)); max-height: min(64vh, 560px);
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
  z-index: calc(var(--z-modal) + 1);
  display: flex; flex-direction: column; overflow: hidden; box-shadow: var(--shadow-lg);
  /* Gleiche Einblendung wie .modal -- das Fenster soll sich wie die uebrigen Popups anfuehlen.
     translate/scale statt transform, weil translate: -50% 0 die Zentrierung haelt. */
  animation: suchePop var(--duration-fast) ease both;
}
@keyframes suchePop {
  from { opacity: 0; scale: 0.96; }
  to { opacity: 1; scale: 1; }
}
.search-sidebar-header { display: flex; align-items: center; gap: var(--space-4); padding: var(--space-7); border-bottom: 1px solid var(--border); }
.search-sidebar-input-row {
  flex: 1; display: flex; align-items: center; gap: var(--space-4); background: var(--panel-2);
  border: 1px solid var(--border); border-radius: var(--radius-md); padding: var(--space-4) var(--space-5); color: var(--text-dim);
}
.search-sidebar-input { flex: 1; background: none; border: none; color: var(--text); font-size: var(--fs-lg); font-family: inherit; outline: none; }
.search-sidebar-filters { display: flex; gap: var(--space-3); flex-wrap: wrap; padding: var(--space-5) var(--space-7); border-bottom: 1px solid var(--border); }
.search-sidebar-filter-chip {
  background: var(--panel-2); border: 1px solid var(--border); color: var(--text-dim);
  border-radius: var(--radius-xl); padding: var(--space-2) var(--space-6); font-size: var(--fs-sm); cursor: pointer; transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.search-sidebar-filter-chip:hover { background: var(--panel); color: var(--text); }
.search-sidebar-filter-chip.active { background: var(--accent); border-color: var(--accent); color: var(--text-on-color); }
.search-sidebar-results { flex: 1; overflow-y: auto; padding: var(--space-5) var(--space-7); }
.search-sidebar-empty { color: var(--text-dim); font-size: var(--fs-md); text-align: center; margin-top: var(--space-12); }
.search-sidebar-group-label { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim); margin: var(--space-7) 0 var(--space-3); }
.search-sidebar-group:first-child .search-sidebar-group-label { margin-top: 0; }
.search-sidebar-result {
  display: flex; align-items: center; gap: var(--space-4); width: 100%; text-align: left;
  background: none; border: none; color: var(--text); padding: var(--space-4) var(--space-3); border-radius: var(--radius-sm);
  cursor: pointer; font-size: var(--fs-md); transition: background var(--duration-fast) ease;
}
.search-sidebar-result:hover { background: var(--panel-hover); }
.search-sidebar-result i { color: var(--text-dim); flex-shrink: 0; }
.search-sidebar-result span { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search-sidebar-result-tag { flex: 0 0 auto; font-size: var(--fs-2xs); color: var(--text-dim); background: var(--panel-2); border-radius: var(--radius-md); padding: 1px var(--space-3); }
@media (max-width: 720px) {
  /* Nutzer-Feedback: "die suche ist noch etwas zu breit wenn man sie öffnet ... man muss die
     seite dann kleiner machen" -- 100vw zaehlt auf manchen Mobilbrowsern breiter als die
     tatsaechlich sichtbare Viewport-Breite (schliesst z.B. die Scrollbar-Breite mit ein, obwohl
     Mobile gar keine sichtbare Scrollbar hat), wodurch das fixed-positionierte Panel ueber den
     rechten Rand hinausragte und die ganze Seite horizontal scrollbar machte. 100% bezieht sich
     stattdessen auf den umgebenden (bereits korrekten) Viewport-Container.
     */
  /* Mobil sitzt das Fenster weiter oben und laesst die Tab-Leiste frei. */
  .search-sidebar {
    top: var(--space-10); width: calc(100% - var(--space-8) * 2);
    max-height: calc(100vh - var(--space-10) - var(--mobile-tab-bar-height, 64px) - var(--space-10));
  }
}
/* Segmentierter Edit/Read-Umschalter -- ersetzt das vorherige Stift-Icon in der Titelleiste,
   sitzt jetzt oben rechts direkt im Notizfeld (Nutzer-Wunsch, siehe Screenshot-Vorlage).
   R58 Bugfix: margin-left:auto statt justify-content:space-between auf .wiki-editor-topbar --
   mit space-between UND ausgeblendeter Werkzeugleiste (Lesemodus, nur noch dieses eine
   Flex-Kind uebrig) landete der Umschalter links statt rechts, sprang beim Moduswechsel
   sichtbar hin und her. margin-left:auto schiebt ihn IMMER ganz nach rechts, unabhaengig
   davon, ob die Werkzeugleiste gerade sichtbar ist. */
.wiki-mode-toggle {
  display: flex; align-items: center; gap: var(--space-1); background: var(--tint);
  border-radius: var(--radius-full); padding: var(--space-1); flex-shrink: 0;
}
.wiki-mode-btn {
  border: none; background: transparent; color: var(--text-dim); font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  padding: var(--space-3) var(--space-7); border-radius: var(--radius-full); cursor: pointer; transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.wiki-mode-btn:hover { color: var(--text); }
.wiki-mode-btn.active { background: var(--text); color: var(--bg); }
.wiki-editor-mount .ProseMirror { outline: none; }
.wiki-editor-mount .ProseMirror p.is-editor-empty:first-child::before {
  content: attr(data-placeholder); float: left; color: var(--text-dim); pointer-events: none; height: 0;
}
/* R60 Nutzer-Feedback: Tabellen (Tiptap Table/TableRow/TableHeader/TableCell) -- ohne eigene CSS
   haben Tabellenzellen in contenteditable-Inhalten keinerlei sichtbare Grenzen. Angelehnt an
   Tiptaps eigenes Beispiel-Stylesheet, an die App-Farbvariablen angepasst. */
.wiki-editor-mount table { border-collapse: collapse; table-layout: fixed; width: 100%; margin: 0.75em 0; overflow: hidden; }
.wiki-editor-mount table td, .wiki-editor-mount table th {
  border: 1px solid var(--border); padding: var(--space-3) var(--space-5); vertical-align: top; box-sizing: border-box;
  position: relative; min-width: 60px;
}
.wiki-editor-mount table th { background: var(--panel-2); font-weight: var(--fw-semibold); text-align: left; }
.wiki-editor-mount table .selectedCell { background: var(--accent-tint); }
.wiki-editor-mount table .column-resize-handle {
  position: absolute; right: -2px; top: 0; bottom: 0; width: 4px; background: var(--accent);
  pointer-events: none;
}
.wiki-editor-mount table p { margin: 0; }
/* Checklisten (Tiptap TaskList/TaskItem) -- Checkbox + Text nebeneinander statt Listenpunkt. */
.wiki-editor-mount ul[data-type="taskList"] { list-style: none; padding-left: 0; margin: 0.5em 0; }
.wiki-editor-mount ul[data-type="taskList"] li { display: flex; align-items: flex-start; gap: var(--space-4); }
.wiki-editor-mount ul[data-type="taskList"] li > label { margin-top: var(--space-2); user-select: none; }
.wiki-editor-mount ul[data-type="taskList"] li > div { flex: 1; min-width: 0; }
/* Nutzer-Feedback (Screenshot): Haken sitzt "verschoben" -- Ursache war der normale
   Browser-Absatz-Rand (16px) auf dem <p> im Task-Item, der den Text unter die Checkbox
   wegdrueckte, waehrend das Label selbst nur 3px Versatz hat. Wie schon bei Tabellenzellen
   (.wiki-editor-mount table p) auf 0 gesetzt, damit Checkbox und erste Textzeile buendig sind. */
.wiki-editor-mount ul[data-type="taskList"] li > div p { margin: 0; }
/* Live-Cursor/-Markierungen anderer, gerade in derselben Notiz aktiver Nutzer (Tiptap
   CollaborationCursor) -- Farbe kommt per inline style von userColor(), siehe notes-editor.js. */
.wiki-editor-mount .collaboration-cursor__caret {
  position: relative; margin-left: -1px; margin-right: -1px;
  border-left: 1.5px solid; border-right: 1.5px solid; word-break: normal; pointer-events: none;
}
.wiki-editor-mount .collaboration-cursor__label {
  position: absolute; top: -1.4em; left: -1px; font-size: var(--fs-2xs); font-weight: var(--fw-semibold); line-height: 1;
  white-space: nowrap; border-radius: var(--radius-xs) var(--radius-xs) var(--radius-xs) 0; padding: var(--space-1) var(--space-3); color: var(--text-on-color); user-select: none;
}
/* R60 Nutzer-Feedback: "live anzeigen wenn jemand einen Text markiert" -- y-prosemirror wrappt
   eine fremde Textmarkierung bereits zuverlaessig in .ProseMirror-yjs-selection (bestaetigt per
   Live-Test mit zwei Tabs), setzt dabei aber in der hier geladenen Versionskombination (siehe
   Versions-Pinning-Kommentar oben in notes-editor.js) KEINE Hintergrundfarbe per Inline-Style --
   die Markierung war dadurch technisch da, aber komplett unsichtbar. Fallback-Hintergrund hier,
   inline gesetzte Farben (falls die Bibliothek sie doch mal liefert) gewinnen automatisch. */
.wiki-editor-mount .ProseMirror-yjs-selection { background-color: var(--accent-strong); border-radius: var(--radius-xs); }
/* R57 Phase 4: rechte Info-Spalte im Notiz-Detail (.boards-detail-row/.boards-info-panel/
   .boards-subtab-panel sind das wiederverwendete Chatroom-Detail-Panel-Baukasten).
   R58 Nutzer-Feedback "deutlich schoener wie bei Chatrooms": uebernimmt jetzt dieselbe Karten-
   Optik pro Abschnitt (eigener var(--panel-2)-Hintergrund, abgerundet) wie
   #chatroom-content .boards-subtab-panel weiter unten in dieser Datei, statt reiner
   Textbloecke ohne visuelle Trennung. */
/* Seit 2026-09-15 teilt sich die Album-Ansicht diese Optik ("mache die medienordner ansicht genauso
   wie bspw. die notes ... lasse es so aehnlich bei alben aussehen"). */
#wiki-page-view .boards-info-panel,
#media-game-detail .boards-info-panel { display: flex; flex-direction: column; gap: var(--space-5); }
#wiki-page-view .boards-subtab-panel,
#media-game-detail .boards-subtab-panel {
  background: var(--panel-2); border-radius: var(--radius); padding: var(--space-6) var(--space-7); margin: 0;
}
#wiki-page-view .boards-subtab-panel h4,
#media-game-detail .boards-subtab-panel h4 {
  margin: 0 0 var(--space-4); display: flex; align-items: center; gap: var(--space-3);
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim);
}
/* Farbiger Punkt vor dem Namen einer live anwesenden Person -- Farbe kommt per inline style aus
   derselben userColor()-Formel wie der Tiptap-Cursor (notes-editor.js), damit Person <-> Farbe
   ueberall in dieser Notiz konsistent ist. */
.wiki-presence-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.wiki-sidebar-meta { display: flex; flex-direction: column; gap: var(--space-4); font-size: var(--fs-sm); color: var(--text-dim); }
.wiki-sidebar-meta i { margin-right: var(--space-3); color: var(--text-dim); }
/* R58: Versionshistorie ist jetzt ein voll-breiter Button statt einer dauerhaft sichtbaren
   Liste -- oeffnet ein Popover (siehe showWikiRevisionsPanel()). */
.wiki-sidebar-history-btn {
  display: flex; align-items: center; gap: var(--space-4); width: 100%;
  background: none; border: none; color: var(--text); font-size: var(--fs-md); font-weight: var(--fw-semibold);
  padding: var(--space-1) 0; cursor: pointer; text-align: left;
}
.wiki-sidebar-history-btn i { color: var(--text-dim); font-size: var(--fs-body); }
.wiki-sidebar-history-btn:hover { color: var(--accent); }
.wiki-sidebar-history-btn:hover i { color: var(--accent); }
.wiki-revisions-popover { width: 280px; max-height: 400px; overflow-y: auto; }
.wiki-sidebar-revisions { display: flex; flex-direction: column; gap: var(--space-4); }
.wiki-revision-row { font-size: var(--fs-sm); }
.wiki-revision-row-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.wiki-revision-when { color: var(--text); }
.wiki-revision-author { color: var(--text-dim); }
.wiki-revision-excerpt { color: var(--text-dim); margin-top: var(--space-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wiki-revision-revert-btn { font-size: var(--fs-2xs); padding: var(--space-1) var(--space-4); }
/* Markdown-Codeblöcke (Wiki-Vorschau, Event-/Spiel-Beschreibung, Agenda) -- highlight.js liefert Farbe+Hintergrund via .hljs.
   R38 Phase 7: #game-preview-description aus diesen Selektorlisten entfernt (gehoerte zum jetzt
   entfernten #game-preview-modal) -- #event-preview-description deckt Spiele/Filme/Serien/
   LAN-Partys seit der Event-Verschmelzung bereits mit ab. R57: #wiki-read-view -> #wiki-editor-mount. */
.editor-preview pre, .agenda-desc pre, #event-preview-description pre, #boards-notes pre, #wiki-editor-mount pre {
  background: transparent; padding: 0; margin: 0.6em 0; overflow-x: auto;
}
.editor-preview pre code.hljs, .agenda-desc pre code.hljs, #event-preview-description pre code.hljs, #boards-notes pre code.hljs, #wiki-editor-mount pre code {
  border-radius: var(--radius-md); padding: var(--space-6) var(--space-7); background: var(--panel); display: block;
}
.editor-preview code, .agenda-desc code, #event-preview-description code, #boards-notes code, #wiki-editor-mount code {
  font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; font-size: 0.85em;
}
.editor-preview :not(pre) > code, .agenda-desc :not(pre) > code, #event-preview-description :not(pre) > code, #boards-notes :not(pre) > code, #wiki-editor-mount :not(pre) > code {
  background: var(--panel); padding: 0.15em 0.4em; border-radius: var(--radius-xs);
}
.wiki-folder-row {
  display: flex; align-items: center; gap: var(--space-3); padding: var(--space-4) var(--space-3); border-radius: var(--radius-md);
  cursor: pointer; font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--text-dim);
  transition: background var(--duration-fast) ease; user-select: none;
}
/* R60 Nutzer-Feedback: Notizen-Ordner sollen groesser wirken -- .wiki-folder-row gehoert
   ausschliesslich den Notizen (Dateien nutzt fuer Ordnerzeilen .wiki-list-item), daher ohne
   weitere Scope-Klasse ueberschreibbar. */
#wiki-list .wiki-folder-row { padding: var(--space-6) var(--space-5); font-size: var(--fs-lg); gap: var(--space-4); }
#wiki-list .wiki-folder-row .mdi-folder-outline { font-size: var(--fs-xl); }
.wiki-folder-row:hover { background: var(--panel-hover); }
.wiki-folder-row.drag-over, .wiki-list-item.drag-over { background: var(--accent-tint); outline: 1px dashed var(--accent); }
.wiki-folder-toggle { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; cursor: pointer; flex-shrink: 0; }
.wiki-folder-row .mdi-chevron-right { transition: transform var(--duration-fast) ease; font-size: var(--fs-body); }
.wiki-folder-row.expanded .mdi-chevron-right { transform: rotate(90deg); }
/* R60 Nutzer-Feedback: statt der duennen Einzugslinie (.wiki-tree-indent-*::before weiter unten)
   bekommt ein Notizen-Wurzelordner jetzt einen eigenen Karten-Hintergrund, gleicher Look wie
   .boards-subtab-panel in der rechten Info-Spalte -- man sieht auf einen Blick, was drin liegt.
   Nur Wurzel-Ordner (Tiefe 0, siehe wiki-folder-wrap-Klasse in renderFolder()), Unterordner
   bleiben ein einfacher Einzug ohne verschachtelte Karte. */
#wiki-list .wiki-folder-wrap {
  background: var(--panel-2); border-radius: var(--radius); padding: var(--space-3) var(--space-3) var(--space-4); margin: var(--space-1) 0;
}
#wiki-list .wiki-folder-wrap .wiki-folder-row:hover { background: var(--tint); }
.wiki-divider-row {
  display: flex; align-items: center; gap: var(--space-4); padding: var(--space-3) var(--space-2);
  cursor: grab;
}
.wiki-divider-line { flex: 1; height: 1px; background: var(--border); }
.wiki-divider-label { font-size: var(--fs-2xs); color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap; }
/* R60 Nutzer-Feedback: Umbenennen-/Loeschen-Buttons nur noch im Bearbeiten-Modus sichtbar,
   siehe gleiche Begruendung/Umschalter-Mechanik bei ".wiki-list-item .icon-btn" weiter oben. */
.wiki-divider-row .wiki-folder-actions, .wiki-folder-row .wiki-folder-actions {
  display: flex; gap: var(--space-1); opacity: 0; pointer-events: none; transition: opacity var(--duration-fast) ease;
}
.wiki-list.edit-mode .wiki-divider-row .wiki-folder-actions, .wiki-list.edit-mode .wiki-folder-row .wiki-folder-actions {
  opacity: 1; pointer-events: auto;
}
.wiki-divider-row .wiki-folder-actions .icon-btn, .wiki-folder-row .wiki-folder-actions .icon-btn { width: 22px; height: 22px; font-size: var(--fs-md); }
.wiki-folder-row .wiki-folder-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wiki-folder-children { display: flex; flex-direction: column; gap: var(--space-1); }
.wiki-folder-children.collapsed { display: none; }
.wiki-tree-indent-1, .wiki-tree-indent-2 { position: relative; }
.wiki-tree-indent-1 { padding-left: var(--space-12); }
.wiki-tree-indent-2 { padding-left: 46px; }
/* R60 Nutzer-Feedback: Notizen in Ordnern sollen etwas deutlicher eingerueckt sein -- nur fuer
   Notizen (Dateien behaelt die kompaktere Einrueckung). */
#wiki-list .wiki-tree-indent-1 { padding-left: 38px; }
#wiki-list .wiki-tree-indent-2 { padding-left: 58px; }
.wiki-tree-indent-1::before, .wiki-tree-indent-2::before {
  content: ""; position: absolute; top: 0; bottom: 0; width: 1px; background: var(--border);
}
.wiki-tree-indent-1::before { left: 12px; }
.wiki-tree-indent-2::before { left: 32px; }
/* Notizen ersetzen die Linie durch den Karten-Hintergrund oben -- Dateien behaelt sie. */
#wiki-list .wiki-tree-indent-1::before, #wiki-list .wiki-tree-indent-2::before { display: none; }
.wiki-list-item[draggable="true"] { cursor: grab; }
/* R60 Rework: einheitliche Ziehen-Optik fuer alle vom neuen Drag&Drop-Motor erfassten Zeilen
   (Seiten/Ordner/Trenner in Notizen UND Dateien), siehe bindTreeDraggable() in app.js. */
.tree-draggable { cursor: grab; }
.tree-draggable.dragging { opacity: 0.4; }
/* Einfuege-Indikator beim Umsortieren (davor/danach) -- reine Randlinie statt Layout-Verschiebung,
   damit whrend des Ziehens nichts hin- und herspringt. */
.tree-drop-before { box-shadow: inset 0 2px 0 0 var(--accent); }
.tree-drop-after { box-shadow: inset 0 -2px 0 0 var(--accent); }
body.no-edit .wiki-folder-row .wiki-folder-actions { display: none; }
.wiki-edit-title-input {
  /* Ohne Innenabstand und mit der Zeilenhoehe des angezeigten Titels -- sonst ist die Kopfzeile im
     Bearbeiten-Modus zwei Pixel hoeher und die Notiz darunter ruckt (2026-09-15). */
  flex: 1; min-width: 0; font: inherit; background: transparent; border: none;
  color: var(--text); padding: 0; line-height: inherit; height: auto;
}
.wiki-edit-title-input:focus { outline: none; border-bottom: 1px solid var(--accent); }

/* ---------- Buttons ---------- */
/* Baustein Hauptknopf. Rahmen in Flaechenfarbe statt keinem, damit er genau so hoch ist wie der
   gerahmte .text-btn neben ihm. Hoehe, Rundung und Schrift teilen sich alle drei Knopfarten. */
.primary-btn {
  background: var(--accent);
  border: 1px solid var(--accent);
  color: var(--text-on-color);
  min-height: var(--control-h);
  padding: var(--space-2) var(--space-7);
  border-radius: var(--radius-md);
  cursor: pointer;
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  line-height: 1.2;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-3);
  transition: background var(--duration-fast) ease, border-color var(--duration-fast) ease;
}
.primary-btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--accent) 85%, white);
  border-color: color-mix(in srgb, var(--accent) 85%, white);
}
/* Nutzer-Feedback: "in der sammlung ist der speichern und löschen button nicht gleich hoch" --
   .danger-btn hatte weder das gleiche font-size wie .primary-btn (erbte dadurch die groessere
   Umgebungsschrift) noch glich sein 1px-Rahmen (den .primary-btn gar nicht hat) durch reduziertes
   vertikales Padding aus -- beides zusammen liess ihn sichtbar hoeher wirken. */
.danger-btn {
  background: transparent;
  border: 1px solid var(--danger);
  color: var(--danger);
  min-height: var(--control-h);
  padding: var(--space-2) var(--space-7);
  border-radius: var(--radius-md);
  cursor: pointer;
  font-size: var(--fs-md);
  font-weight: var(--fw-medium);
  line-height: 1.2;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-3);
  transition: background var(--duration-fast) ease;
}
.danger-btn:hover:not(:disabled) { background: color-mix(in srgb, var(--danger) 12%, transparent); }
/* R55: gefuellte Variante fuer seltene, bewusst "gefaehrlich wirkende" Aktionen (z.B. der
   Admin-Passwort-Bypass-Button beim Chatroom-Beitritt) -- .danger-btn (Outline) wirkt dafuer zu
   unauffaellig. */
.danger-btn-solid {
  background: var(--danger); border: 1px solid var(--danger); color: var(--text-on-color);
  min-height: var(--control-h); padding: var(--space-2) var(--space-7); border-radius: var(--radius-md); cursor: pointer;
  font-size: var(--fs-md); font-weight: var(--fw-medium); line-height: 1.2;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-3);
}
.danger-btn-solid:hover { filter: brightness(1.1); }
.chatroom-admin-override-row { margin-top: var(--space-8); padding-top: var(--space-7); border-top: 1px dashed var(--border); }
.chatroom-admin-override-row .settings-hint { margin: 0 0 var(--space-4); }

/* Generisches Bestaetigungs-Popup (R36), ersetzt Browser-confirm() -- kompakter als die
   normalen Formular-Modals, da nur Text + zwei Buttons drinstehen. */
.confirm-dialog { max-width: 340px; }
.confirm-dialog-message { font-size: var(--fs-lg); line-height: 1.4; margin-bottom: var(--space-10); white-space: pre-line; }
.confirm-dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-5); }
/* Tipp-den-Namen-Sperre (R38, z.B. Organisation loeschen) -- nur sichtbar, wenn showConfirmDialog()
   mit requireTypedText aufgerufen wurde, siehe app.js. */
.confirm-dialog-typed-row { margin: -8px 0 var(--space-8); }
.confirm-dialog-typed-input {
  width: 100%; padding: var(--space-4) var(--space-5); border-radius: var(--radius-md); border: 1px solid var(--border);
  background: transparent; color: inherit; font-size: var(--fs-lg); box-sizing: border-box;
}

/* ---------- Modal ---------- */
.modal-overlay {
  position: fixed; inset: 0;
  background: var(--scrim);
  display: flex; align-items: center; justify-content: center;
  z-index: var(--z-modal);
  padding: var(--space-10);
  animation: overlayFade var(--duration-fast) ease both;
  /* Nutzer-Feedback: "der hintergrund scrollt wenn man im event view scrollt und am ende
     angekommen ist" -- Scroll-Geste an der Grenze einer inneren scrollbaren Box (Spalten/
     Beschreibung/Chat) reicht der Browser sonst an den naechsten scrollbaren Vorfahren weiter,
     hier die Dashboard-Seite HINTER dem (selbst nicht scrollenden) Overlay. Blanket-Fix fuer alle
     Modals statt nur Event-Vorschau, gleiches Problem koennte ueberall auftreten. */
  overscroll-behavior: contain;
}
/* Weichgezeichneter Hintergrund hinter Dialogen: zeigt "dahinter liegt die Seite, gerade nicht
   bedienbar" -- genau der Zweck, fuer den Blur gedacht ist (nicht als Deko). Leicht, damit man die
   Seite noch erkennt. */
.modal-overlay { -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
/* Bestaetigungs-Dialog (z.B. "Link neu erzeugen?") wird oft AUS einem bereits offenen Modal heraus
   ausgeloest (Settings/Org-Edit/...) -- alle .modal-overlay teilen sich z-index:200, bei gleicher
   Zahl entscheidet dann die DOM-Reihenfolge, und #confirm-dialog-modal steht im HTML VOR diesen
   Modals, landete also dahinter statt davor (Nutzer-Feedback: "die Meldung kommt hinter den
   bereits offenen Fenstern"). Eigener, hoeherer z-index macht das unabhaengig von der DOM-Position
   -- Bestaetigungs-Dialoge sollen ohnehin immer ueber allem anderen stehen. */
/* Das Event-Formular wird aus anderen Fenstern heraus geoeffnet (Geburtstags-Uebersicht,
   Event-Ansicht) -- bei gleichem z-index gewinnt sonst das zuletzt im Markup stehende Fenster und
   das Formular liegt dahinter (2026-09-14 gemeldet). Knapp ueber .modal-overlay, aber unter dem
   Bestaetigungs-Dialog, der wiederum aus dem Formular heraus aufgeht. */
#event-modal { z-index: calc(var(--z-modal) + 5); }
/* Aus dem Formular heraus gehen weitere Fenster auf (Verknuepfen mit Spiel/Film/Serie, Titelbild-
   Auswahl, Album) -- die muessen ueber dem Formular liegen, sonst verschwinden sie dahinter
   (2026-09-14 gemeldet: "wenn ich film verknuepfe wird das hinter der aktuellen maske angezeigt"). */
#event-link-modal, #event-album-modal, #event-type-modal { z-index: calc(var(--z-modal) + 6); }
#confirm-dialog-modal { z-index: var(--z-over-modal); }
/* Das geoeffnete Album ist kein Dialog, sondern eine ANSICHT, die offen bleibt, waehrend man
   weiterarbeitet -- es steht im Markup aber hinter allen Dialogen und gewann damit bei gleichem
   z-index die DOM-Reihenfolge (2026-09-16 gemeldet: "wenn medien geoeffnet ist oeffnen sich die
   settings hinter dem album"). Eine Stufe tiefer: jeder echte Dialog liegt darueber, alles, was aus
   dem Album heraus aufgeht (Bildansicht, Bestaetigung), ebenfalls. */
#media-game-detail.modal-overlay { z-index: calc(var(--z-modal) - 1); }
.modal {
  /* Dieselbe Glasflaeche wie die Kacheln und Leisten (2026-09-15: "allgemein brauchen wir ueberall
     den selben glass effekt bei solchen sachen") -- der Grund darunter ist ohnehin schon
     weichgezeichnet (.modal-overlay), das Fenster liegt also auf einer ruhigen Flaeche. */
  background: var(--flaeche-fuellung-fenster);
  -webkit-backdrop-filter: var(--flaeche-unschaerfe);
          backdrop-filter: var(--flaeche-unschaerfe);
  border: none;
  /* Ersetzt den Rahmen zur Abgrenzung vom halbtransparenten Overlay -- Dashboard-inspiriert
     (Trennung per Hintergrund/Schatten statt Linie), gleiches Schatten-Muster wie die
     randlosen Popover (.icon-dropdown-popover). */
  box-shadow: var(--shadow-lg);
  border-radius: var(--radius);
  padding: var(--space-12);
  width: 100%; max-width: 460px;
  max-height: 90vh;
  overflow-y: auto;
  overflow-x: hidden;
  position: relative;
  animation: modalPop var(--duration-base) ease both;
}
.modal-wide { max-width: 920px; }
/* Nutzer-Feedback: die Compendium-"+"-Buttons (Spiel/Film/Serie) sollen nur ein kleines Such-
   Fenster oeffnen statt des kompletten Event-Formulars, da bei einer Katalog-Neuanlage ohnehin nur
   die Suchzeile bedienbar ist (Rest gesperrt) -- siehe .catalog-quick-add-Toggle in openEventModal()
   (app.js). Alles ausser Titelzeile/Schliessen-Button und dem Suchresolver wird ausgeblendet. */
/* min-height: das Modal enthaelt in diesem Zustand nur die Suchzeile und schrumpft dadurch auf
   ca. 180px Hoehe -- die Trefferliste darunter (position:absolute, bis zu 260px hoch, siehe
   .steam-search-panel-dropdown) ragt dann ueber den unteren Modal-Rand hinaus und wird dort vom
   "overflow:hidden" auf .modal-with-header abgeschnitten (Nutzer-Feedback: "ich sehe nicht was in
   der Suche herauskommt"). Reserviert von vornherein genug Platz fuer Suchzeile + volle
   Trefferliste, damit sie komplett innerhalb des Modals bleibt. */
#event-modal.catalog-quick-add .modal { max-width: 480px; min-height: 420px; }
#event-modal.catalog-quick-add .modal-category-tabs,
#event-modal.catalog-quick-add .event-cover-links-group,
#event-modal.catalog-quick-add fieldset.event-session-fields,
#event-modal.catalog-quick-add #event-form-options,
#event-modal.catalog-quick-add .modal-header-actions .primary-btn,
#event-modal.catalog-quick-add #event-delete-btn {
  display: none !important;
}
#event-modal.catalog-quick-add #event-catalog-resolver { margin-bottom: 0; }
/* R60 Nutzer-Feedback: Medien-Ordner-Detailansicht kommt jetzt als grosses Popup -- breiter als
   .modal-wide, da hier ein Foto-Kachelraster statt Formularfelder angezeigt wird. */
.modal-xl { max-width: 1200px; }
/* R61 Nutzer-Feedback: Standardhoehe statt sich rein am (bei wenigen Fotos duennen) Inhalt zu
   orientieren -- min() haelt das auf kleinen Fenstern trotzdem unterhalb des Viewports (bestehendes
   max-height:90vh auf .modal-with-header greift ohnehin als Obergrenze fuer grosse Ordner). */
#media-game-detail .modal { min-height: min(680px, 82vh); }
/* R61 Nutzer-Feedback: Ordner-Popup eine Spalte breiter -- gezielt nur hier statt am geteilten
   .modal-xl (das nutzt auch #event-preview-modal, das soll unveraendert bleiben).
   Nutzer-Feedback (Folgerunde): "mache den medien ordner auf dem desktop ruhig etwas breiter". */
#media-game-detail .modal.modal-xl { max-width: 1520px; }
/* Schliessen-x als Icon-Knopf mit Hover-Flaeche wie .icon-btn, nur ohne Rahmen -- vorher ein
   nacktes Zeichen ohne erkennbare Klickflaeche. */
.modal-close {
  position: absolute; top: 12px; right: 14px;
  width: var(--control-h-sm); height: var(--control-h-sm); padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; border-radius: var(--radius-sm); color: var(--text-dim);
  font-size: var(--fs-xl); line-height: 1; cursor: pointer;
  transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.modal-close:hover { background: var(--tint-hover); color: var(--text); }
/* Zurueck-Button fuers mobile iOS-Settings-Stil-Drilldown im Profil-Modal (siehe
   @media(max-width:720px) weiter unten) -- Positionierung schon hier als Basis-Regel, sichtbar
   wird er ausschliesslich ueber die dortige .mobile-settings-detail-Klasse. */
#profile-mobile-back-btn { display: none; position: absolute; top: 12px; left: 14px; }
/* Mobil-Feedback: "wieder mit x oben schließbar" -- analog zu #profile-mobile-back-btn oben
   rechts positioniert, nur auf Mobil sichtbar (siehe @media(max-width:720px) weiter unten). */
.profile-mobile-close-btn { display: none; position: absolute; top: 12px; right: 14px; z-index: 1; }
.modal h2 { margin-top: 0; }
.modal form:not(.two-col-form) { display: flex; flex-direction: column; gap: var(--space-6); }
.modal label { display: flex; flex-direction: column; gap: var(--space-2); font-size: var(--fs-md); color: var(--text-dim); }
/* ---------- Baustein Feld ----------
   Textfelder und Auswahllisten sind so hoch wie die Knoepfe (--control-h), damit ein Feld mit
   Knopf daneben buendig ist. Die Mindesthoehe gilt bewusst nicht fuer Checkboxen, Farbwaehler
   und Co. -- ".modal input" trifft die sonst mit. Fokus: Akzent-Rahmen plus Toenungsring, damit
   man auch ohne Cursor sieht, wo man tippt. */
.modal input, .modal textarea, .modal select {
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-2) var(--space-5);
  border-radius: var(--radius-sm);
  font-size: var(--fs-lg);
  font-family: inherit;
  transition: border-color var(--duration-fast) ease, box-shadow var(--duration-fast) ease;
}
.modal :is(input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]), select) {
  min-height: var(--control-h);
}
.modal :is(input, textarea, select):focus,
.media-search-input:focus, .wiki-search-input:focus {
  outline: none; box-shadow: 0 0 0 3px var(--accent-soft);
}
.modal textarea { resize: vertical; min-height: 70px; }
/* Nutzer-Feedback: "mache bei der notiz bearbeiten und wenn man ein event bearbeitet bei dem
   notiz feld die gleiche werkzeugleiste wie wenn man die notiz einfach so bearbeitet" -- das
   Beschreibungsfeld im Event-Bearbeiten-Formular war bisher der einzige Markdown-Editor mit dem
   vollen EasyMDE-Funktionsumfang (Tabellen, Bilder, Vorschau); ersetzt durch dieselbe schlanke
   Textarea+Werkzeugleiste wie die Notiz-Direktbearbeitung, daher hier grosszuegigere Mindesthoehe
   als der generische Formular-Standard oben. */
#event-description { min-height: 220px; }
.form-actions { display: flex; gap: var(--space-5); margin-top: var(--space-3); }
/* Upload-Fortschritt (Medien-Hochladen-Maske) -- Bilder werden vorher clientseitig auf
   max. 1920px Kantenlaenge verkleinert (resizeImageForUpload() in app.js), der Balken hier
   zeigt trotzdem den echten XHR-Upload-Fortschritt der (kleineren) Datei(en). */
.media-upload-progress-row { display: flex; align-items: center; gap: var(--space-4); margin-top: var(--space-2); }
.media-upload-progress-track { flex: 1; height: 6px; border-radius: var(--radius-xs); background: var(--panel-2); overflow: hidden; }
.media-upload-progress-bar { height: 100%; width: 0%; background: var(--accent); border-radius: var(--radius-xs); transition: width var(--duration-fast) ease; }
.media-upload-progress-label { font-size: var(--fs-xs); color: var(--text-dim); min-width: 32px; text-align: right; }
@media (max-width: 880px) {
  /* 16px verhindert das automatische Reinzoomen von iOS Safari beim Fokussieren von Eingabefeldern. */
  .modal input, .modal textarea, .modal select, .login-card input, .chat-form input[type="text"] { font-size: var(--fs-body); }
}
/* Die Regel oben kannte nur Dialogfelder, Login und Chat -- die Suchfelder (13.6px) liessen iOS beim
   Antippen weiter hineinzoomen. Auf Touch-Geraeten jetzt JEDES Feld mindestens 16px. Die hohe
   Spezifitaet von :is() (die des laengsten Arguments) ist Absicht: sie schlaegt die Klassenregeln
   der einzelnen Felder. */
@media (pointer: coarse) {
  :is(input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]):not([type=hidden]), select, textarea) {
    font-size: max(16px, 1em);
  }
}

/* ---------- Modal mit fixer Kopfzeile (Speichern/Schließen immer sichtbar) ---------- */
.modal-with-header {
  padding: 0;
  display: flex;
  flex-direction: column;
  max-height: 90vh;
  overflow: hidden;
}
/* var(--panel-2) statt var(--panel) + Rahmen: grenzt sich jetzt per Hintergrund vom
   .modal-Koerper darunter ab (Dashboard-inspiriert), statt per Linie. */
.modal-header-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  padding: var(--space-7) var(--space-10);
  background: var(--panel-2);
  border-bottom: none;
  flex-shrink: 0;
}
.modal-header-bar h2 { margin: 0; font-size: var(--fs-body); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.event-preview-title-wrap { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
/* Nutzer-Feedback (Folgerunde): "mache auf der linken seite eine extra rubrik event infos mit
   Datum uhrzeit, spiel/film/serie und tags" -- Datum/Uhrzeit + verknuepfte Spiel/Film/Serie-Pille
   + Tags sind aus der Kopfzeile in eine eigene Karte umgezogen (siehe #event-preview-infos-section
   in index.html/style.css), .event-preview-title-wrap enthaelt jetzt nur noch den Titel. Die
   groessere Desktop-Schrift bleibt trotzdem sinnvoll (die Kopfzeile ist jetzt schlanker/hat mehr
   Platz fuer den Titel allein). */
@media (min-width: 721px) {
  #event-preview-modal #event-preview-title { font-size: var(--fs-2xl); }
}
/* Pill unter der Event-Vorschau-Ueberschrift fuer eine verknuepfte Spiel-/Film-/Serie-Karte
   (R35) -- oeffnet per Hover showLinkedMediaPopup() (Desktop) bzw. per Klick (Mobile, kein Hover
   verfuegbar), siehe openEventPreview() in app.js. Kein cursor:pointer mehr -- die Pille selbst
   ist kein Button/Klickziel mehr, der eigentliche Klick ("In der Sammlung ansehen") lebt im
   Popup selbst.
   Nutzer-Feedback: "das link icon dazu sollte mittig von beiden schrift zeilen sein" -- die Pille
   ist jetzt selbst der Zeilen-Flex (Icon + Textblock), align-items:center zentriert das Icon
   dadurch ueber die volle Hoehe BEIDER Textzeilen statt nur der ersten (siehe .linked-media-pill-
   text weiter unten fuer den Zwei-Zeilen-Block). */
/* Nutzer-Feedback (Kehrtwende): "die tags und die game film serien release datum anzeige sollten
   schon einen background haben. es sollte nur der background auf dem event übersicht an sich
   entfernt sein nicht von den items" -- gefuellter Pillen-Hintergrund wieder da (wie urspruenglich),
   nur die AEUSSERE #event-preview-infos-section bleibt ohne eigenen Karten-Hintergrund. */
.linked-media-pill {
  display: inline-flex; align-items: center; gap: var(--space-3); align-self: flex-start;
  font-size: var(--fs-xs); padding: var(--space-2) var(--space-6); border-radius: var(--radius-full);
  background: var(--panel-2); color: var(--text); border: none;
  transition: background var(--duration-fast) ease;
}
.linked-media-pill:hover { background: var(--panel); }
/* In der Event-Ansicht liegt die Pille auf einer Kachel oder direkt auf dem Titelbild: Toenung statt
   deckendem Grau, wie alles andere, was dort in einer Kachel liegt (siehe --event-kachel). */
#event-preview-modal .linked-media-pill { background: var(--tint); }
#event-preview-modal .linked-media-pill:hover { background: var(--tint-hover); }
/* Nutzer-Feedback (2026-09-11): "können wir in der event übersicht die verknüpfung auch noch viereckig und nicht rund
   machen und auf die gesamte breite so wie die anderen 'buttons' nur halt kein button sondern verhalten so wie zuvor
   auch" -- Ecken und volle Breite wie Pin/Share/Titelbild darueber; Inhalt (Icon, Titel, Release) und Verhalten
   (Hover-Popup am Desktop, Tippen auf dem Handy) bleiben. */
#event-preview-modal .linked-media-pill { border-radius: var(--radius-md); align-self: stretch; width: 100%; box-sizing: border-box; }
#event-preview-modal #event-preview-link-section .event-preview-infos-card { width: auto; }
.event-preview-creator { font-size: var(--fs-sm); color: var(--text-dim); display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
/* Art des Events (2026-09-11: "man soll ... auch sehen was es ist spiel film serie event etc.") -- stand
   zuerst als rundes Etikett vor "Erstellt von". Folgerunde: "mittig von der gesamten event maske oben und
   machen dann oben einen kleinen balken über die gesamte breite quasi wie eine top linie in der gleichen
   farbe ... und dann mittig das flag ... nicht mehr rund sondern wie ein viereck aber schräg nach unten
   zusammen" -- Farblinie am oberen Modalrand, daran mittig ein Faehnchen, dessen Seiten nach unten
   zusammenlaufen (clip-path-Trapez). Eigene Zeile VOR der Kopfzeile statt darin: so haengt das Faehnchen
   nie ueber einem langen Titel oder dem Handy-Zurueckpfeil. Ansicht und Formular teilen die Leiste
   (setzeTypFlagge() in event-preview.js). Leicht abgedunkelte Kategoriefarbe, damit die weisse Schrift
   auch auf hellen Farben (Feiertag-Gelb) lesbar bleibt. */
.event-typ-leiste {
  --typ-flaeche: color-mix(in srgb, var(--typ-farbe) 82%, black);
  position: relative; z-index: 1; flex-shrink: 0; height: 0; border-top: 3px solid var(--typ-flaeche);
  /* Nutzer-Feedback 2026-09-11 in zwei Runden: erst "zwischen dem neuen balken oben und dem event titel ein
     breiter balken in background farbe. der sollte bitte weg" (die Leiste war eine eigene durchsichtige Zeile,
     darunter schien die Fensterflaeche als Streifen durch), dann "wenn es kein titelbild gibt ist der balken
     oben wo auch die überschrift ist zu breit" -- die Leiste ist jetzt nur noch die 3px-Linie; das Faehnchen
     haengt absolut darunter ueber dem oberen Innenabstand der Kopfzeile, die so schmal bleibt wie vorher. */
}
.event-typ-leiste[hidden] { display: none; }
.event-typ-flagge {
  position: absolute; top: 0; left: 50%; transform: translateX(-50%); white-space: nowrap;
  padding: 0 var(--space-8) 2px; background: var(--typ-flaeche); color: #fff; line-height: 1.5;
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold); letter-spacing: 0.06em; text-transform: uppercase;
  clip-path: polygon(0 0, 100% 0, calc(100% - 9px) 100%, 9px 100%);
}
/* Handy: der Titel beginnt dort gleich neben dem Zurueckpfeil und reicht bei laengeren Namen bis unter die
   Mitte -- Kopfzeile und Pfeil ruecken 10px tiefer, damit das Faehnchen nichts verdeckt (am Desktop steht der
   Titel links, die Mitte bleibt frei). */
@media (max-width: 720px) {
  :is(#event-preview-modal, #event-modal) .event-typ-leiste:not([hidden]) + .modal-header-bar { padding-top: 24px; }
  :is(#event-preview-modal, #event-modal) .event-typ-leiste:not([hidden]) + .modal-header-bar .modal-header-close { top: 20px; }
}
/* Auswahl-Fenster vor dem Anlegen (showCreateMenu() in app.js, 2026-09-11): grosse Kacheln statt Liste,
   links ein Streifen und das Symbol in der Kategoriefarbe wie die Agenda-Karten. */
#event-type-modal .modal { max-width: 540px; width: 92vw; }
.event-type-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); }
/* Ohne den frueheren Farbstreifen links (2026-09-15: "wir haben diese seitlichen farben nicht
   mehr") -- die Art zeigt jetzt das farbige Symbol, und bei "Event" sagen kleine Punkte, welche
   Arten daraus werden koennen. */
.event-type-option {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); text-align: left; min-width: 0;
  padding: var(--space-6); border: none; border-radius: var(--radius);
  background: var(--panel-2); color: var(--text); cursor: pointer; font-family: inherit;
  transition: background var(--duration-fast) ease;
}
.event-type-option-kopf { display: flex; align-items: center; gap: var(--space-4); width: 100%; }
.event-type-farben { display: inline-flex; align-items: center; gap: var(--space-2); margin-left: auto; }
.event-type-farbpunkt { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.event-type-option:hover { background: var(--panel-hover); }
.event-type-option .mdi { font-size: var(--fs-2xl); color: var(--typ-farbe); }
.event-type-option-label { font-weight: var(--fw-semibold); font-size: var(--fs-body); }
.event-type-option-text { font-size: var(--fs-sm); color: var(--text-dim); line-height: 1.35; }
@media (max-width: 380px) { .event-type-grid { grid-template-columns: 1fr; } }
.event-preview-creator:empty { display: none; }
#event-preview-modal.event-preview-has-backdrop .event-preview-creator { color: color-mix(in srgb, var(--text) 85%, transparent); }
.linked-media-pill-text { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 1px; min-width: 0; }
.linked-media-pill-main { display: block; }
.linked-media-pill-release { font-size: var(--fs-2xs); color: var(--text-dim); font-weight: var(--fw-medium); }
/* R60 Nutzer-Feedback: aufgewertete Kopfzeile statt schlicht transparent -- unscharfer
   Hintergrund aus dem Cover-/Titelbild des Spiels/Films/Events (falls vorhanden, per
   --media-detail-bg-image von den open*Detail()-Funktionen gesetzt), sonst einfach ein
   kraeftigerer Panel-Ton als Fallback. Gleiches ::before-Blur-Muster wie beim Compendium-Slider
   (siehe .compendium-media-slider .media-slider-slide weiter unten). */
.modal-header-bar.media-detail-header {
  position: relative; overflow: hidden;
  background: var(--panel-2); border-bottom: none; padding: var(--space-7) var(--space-10); justify-content: flex-start;
}
/* Der unscharfe, stark abgedunkelte Titelbild-Grund hinter dem Albumnamen ist 2026-09-14 gefallen
   ("die background farbe der medienfolder ist noch falsch zu dunkel") -- die Kopfzeile traegt jetzt
   wie alle anderen Titelleisten einfach die Flaeche der Ansicht. Die Variable
   --media-detail-bg-image setzt setMediaDetailHeaderVisual() weiterhin; sie wird nur nicht mehr
   gezeichnet, damit ein spaeterer Wunsch nach einem (helleren) Grund nichts neu verdrahten muss. */
.modal-header-bar.media-detail-header::before { content: none; }
.modal-header-bar.media-detail-header > * { position: relative; z-index: 1; }
.modal-header-bar.media-detail-header h2 { font-size: var(--fs-2xl); font-weight: var(--fw-semibold); color: var(--text); text-align: left; }
.modal-header-bar.media-detail-header .modal-header-actions { margin-left: auto; align-items: stretch; }
.media-detail-cover-avatar {
  width: 42px; height: 42px; border-radius: 10px; overflow: hidden; flex-shrink: 0;
  background: var(--panel); display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-2xl); box-shadow: 0 0 0 2px rgba(255,255,255,0.12);
}
/* Nutzer-Feedback: "mache bitte die ordneroptionnen zu 'funktionen' und schreibe den markieren
   button auch als text. mache sie dann untereinander und gehauso hoch wie den medien
   hinizufügen button" -- Auswählen/Funktionen sind jetzt ein zweizeiliger Stack statt zweier
   eigenstaendiger Buttons. align-items:stretch auf .modal-header-actions (siehe oben) sorgt
   dafuer, dass der Stack automatisch exakt so hoch wird wie sein groesster Geschwister-Button
   (#media-detail-add-btn, .primary-btn) -- kein hartkodierter Hoehenwert noetig. */
/* Nutzerwunsch 2026-09-14: "passe auch die anpassen und funktionen buttons an das sie zu den
   anderen buttons passen nebeneinander" -- aus dem zweizeiligen Stapel mit eigener Optik werden
   zwei normale Knoepfe in einer Reihe, in derselben Hoehe (--control-h) wie "Medien hochladen"
   daneben. */
/* Album-Ansicht seit 2026-09-15 zweispaltig wie die Notiz-Ansicht: Raster links, Leiste rechts.
   Die Knoepfe stehen darin untereinander und ueber die volle Breite der Leiste. */
/* Das Album geht auf wie eine Kachel auf dem Dashboard (2026-09-15: "entferne die animation wie
   sich ein medienordner oeffnet. es sollte so wie auf dem dashboard aussehen") -- dieselbe
   Einblendung wie .widget/.tab-content statt des Modal-Aufpoppens. */
#media-game-detail .modal { animation: fadeInUp var(--duration-base) ease both; }
/* Der Albumtitel war als einziger dieser drei Ansichten doppelt so gross wie die Ueberschrift von
   Raeumen und Notizen (fs-4xl/800 gegen fs-2xl/bold). Seit 2026-09-15 ("mache den titel von alben
   etwas kleiner und mache auch so: rooms, notes") traegt er dieselbe Ueberschrift wie die beiden --
   die Regel steht in .wiki-header-titles h2, hier bleibt nur der ruhigere Zeilenfall. */
#media-detail-title { font-size: var(--fs-body); font-weight: var(--fw-bold); line-height: 1.15; letter-spacing: -0.01em; }
.media-detail-row { align-items: stretch; height: 100%; min-height: 0; }
.media-detail-row > .boards-detail-left-col { min-width: 0; overflow-y: auto; }
/* Nur der Ordner selbst scrollt, nicht die Leiste daneben (2026-09-15: "im medien ordner sollte
   nur der ordner selbst scrollen nicht die rechte leiste mit") -- bisher scrollte der ganze
   Modal-Body als Einheit. Auf schmalen Breiten bleibt es beim Scrollen des ganzen Popups (siehe
   den Mobile-Block weiter oben), deshalb die Mindestbreite. */
@media (min-width: 901px) {
  /* Als Flex-Spalte, damit die Reihe den Rest genau ausfuellt: mit "height: 100%" plus dem
     margin-top von .boards-detail-row stand sie 10px ueber und wurde unten angeschnitten. */
  #media-game-detail .modal-body { overflow-y: hidden; display: flex; flex-direction: column; }
  #media-game-detail .media-detail-row { height: auto; }
  /* Unten stand der hellere Grund unter der Ordnerflaeche hervor (2026-09-15: "bei alben ist es
     auch noch so, dass unten der teil noch von dem helleren background uebersteht") -- die Flaeche
     reicht jetzt bis an den Rand des Fensters. */
  #media-game-detail .modal-body { padding-bottom: 0; }
  /* Gleich viel Luft rechts wie in Raeumen und Notizen, nur einen Tick weiter aussen
     ("im medienordner koennte es einen ticken weiter rechts sein"). */
  #media-game-detail .modal-body { padding-right: var(--space-7); }
}
.media-detail-funktionen { display: flex; flex-direction: column; gap: var(--space-3); }
.media-detail-funktionen > button { width: 100%; justify-content: center; }
/* Am Desktop stehen alle weiteren Funktionen ausgeschrieben untereinander statt hinter einem
   "Weitere"-Knopf (2026-09-16: "bitte alle funktionen eines albums am desktop anzeigen und keinen
   weitere button"). Der Container ist ein eigener Flex-Stapel, damit .media-detail-funktionen > button
   (width:100%) darueber hinweg weiter nur fuer die festen Knoepfe gilt. */
.media-detail-weitere { display: flex; flex-direction: column; gap: var(--space-3); }
.media-detail-weitere > button { width: 100%; justify-content: center; }
/* Mit Element-Selektor, weil .media-detail-stack-btn weiter unten in derselben Datei
   display:inline-flex setzt -- bei gleicher Spezifitaet gewinnt die spaetere Regel. */
button.media-detail-nur-mobil { display: none; }
@media (max-width: 900px) {
  /* Gestapelt ist fuer fuenf ausgeschriebene Knoepfe kein Platz -- dort bleibt das Dropdown. */
  .media-detail-weitere { display: none; }
  button.media-detail-nur-mobil { display: inline-flex; }
  /* Gestapelt gehoeren die Funktionen nach oben -- unter dem Raster waeren sie nach hundert Fotos
     nicht mehr zu finden. */
  .media-detail-row { flex-direction: column; }
  .media-detail-row > .media-detail-side { order: -1; flex: none !important; width: 100%; }
  .media-detail-funktionen { flex-direction: row; flex-wrap: wrap; }
  .media-detail-funktionen > button { width: auto; flex: 1 1 140px; }
}
.media-detail-header-stack { display: flex; align-items: center; gap: var(--space-3); }
.media-detail-stack-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-3);
  height: var(--control-h); background: var(--tint); border: none; color: var(--text);
  border-radius: var(--radius-full); cursor: pointer; padding: 0 var(--space-6);
  font-family: inherit; font-size: var(--fs-sm); white-space: nowrap;
}
.media-detail-stack-btn:hover { background: var(--tint-hover); }
/* Am Desktop fuehrt der Weg zurueck ueber die Albenliste daneben -- der Pfeil war dort nur ein
   zweiter, ueberfluessiger Weg (2026-09-14: "mache auf dem desktop den zurueck button weg"). */
@media (min-width: 881px) {
  body.hat-kontextspalte #media-detail-back-btn { display: none; }
}
.media-detail-stack-btn.active { background: var(--accent); color: var(--text-on-color); border-color: var(--accent); }
.media-detail-stack-btn i { font-size: var(--fs-md); }
.media-detail-cover-avatar img { width: 100%; height: 100%; object-fit: cover; }
.media-detail-cover-avatar:empty { display: none; }
.modal-header-actions { display: flex; align-items: center; gap: var(--space-4); flex-shrink: 0; }
.modal-header-close { font-size: var(--fs-xl); }
/* Nutzer-Feedback: "wir machen das jetzt alles wie eine schöne App" -- Zurueck-Pfeil (mdi-arrow-
   left, per JS in .modal-header-close injiziert) statt "×" auf Mobile, angelehnt an das bereits
   bestehende #media-detail-back-btn-Vorbild. Am Desktop bleibt exakt das bisherige "×" (Icon hier
   unsichtbar), erst die @media-Regel weiter unten tauscht sichtbar/unsichtbar und positioniert
   den Button in die obere linke Ecke. */
.modal-header-close-icon { display: none; }
@media (max-width: 720px) {
  .modal-header-close-x { display: none; }
  .modal-header-close-icon { display: inline-block; }
  /* :has() statt einer Ausschlussliste -- trifft automatisch genau die Header-Bar-Modals, die
     tatsaechlich einen .modal-header-close-Button enthalten (8 von 9 .modal-with-header-Modals;
     #media-game-detail nutzt stattdessen sein eigenes #media-detail-back-btn und bleibt dadurch
     automatisch unberuehrt, ohne es explizit ausschliessen zu muessen). */
  /* Nutzer-Feedback (Event bearbeiten, Mobil): "den zurück pfeil bündig mit der überschrift, der
     ist grad weiter oben" -- Ursache: die Basisregel (.modal-header-bar { align-items:center })
     zentriert h2 und .modal-header-actions vertikal zueinander; sobald die Aktionen-Zeile mobil auf
     mehrere Zeilen umbricht (z.B. Speichern+Löschen+Anheften/Wiederholen bei event-modal), wird die
     Zeile hoeher und die zentrierte Ueberschrift rutscht dadurch nach unten -- der absolut
     positionierte Zurueck-Pfeil (fester top:10px) bleibt aber oben stehen und wandert so optisch
     "davon". align-items:flex-start bindet die Ueberschrift stattdessen an den oberen Rand, exakt
     wie der Pfeil, unabhaengig davon wie hoch die Aktionen-Zeile wird. */
  .modal-header-bar:has(.modal-header-close) { padding-left: 52px; position: relative; align-items: flex-start; }
  .modal-header-close {
    position: absolute; top: 10px; left: 10px; right: auto; z-index: 2;
    width: 34px; height: 34px; border-radius: 50%; background: var(--panel-2);
    display: flex; align-items: center; justify-content: center; font-size: var(--fs-2xl); flex-shrink: 0;
  }
}
.modal-with-header .modal-body { overflow-y: auto; overflow-x: hidden; padding: var(--space-10); flex: 1; }
.modal-with-header .modal-close { display: none; }
@media (max-width: 560px) {
  .modal-overlay { padding: var(--space-5); }
  /* Nutzer-Feedback: "die screenshot folder sollten wirklich fullscreen sein. die leiste oben ist
     derzeit noch kleiner" -- Ursache: .modal-with-header setzt weiter oben padding:0 (die
     Kopfzeile soll randlos ueber die volle Breite gehen), diese Regel hier stand aber SPAETER im
     Stylesheet und ueberschrieb das bei gleicher Spezifitaet einfach wieder mit 16px, wodurch
     Kopfzeile UND Body bei jedem .modal-with-header-Popup (nicht nur Medien-Ordner) auf schmalen
     Screens sichtbar eingerueckt wirkten statt randlos. :not() haelt .modal-with-header-Popups
     bewusst aussen vor -- deren Kopfzeile bleibt padding:0 (randlos), nur der Body bekommt sein
     eigenes 16px ueber die naechste Regel darunter. */
  .modal:not(.modal-with-header) { padding: var(--space-8); }
  .modal-header-bar { padding: var(--space-6) var(--space-8); }
  .modal-with-header .modal-body { padding: var(--space-8); }
}

/* ---------- Kategorie-Tab-Leiste im Modal-Header ---------- */
.modal-category-tabs {
  display: flex; gap: var(--space-3); padding: var(--space-5) var(--space-10); overflow-x: auto;
  background: var(--panel-2); border-bottom: none; flex-shrink: 0;
  scrollbar-width: thin;
}
.category-tab {
  display: inline-flex; align-items: center; gap: var(--space-3); white-space: nowrap; flex-shrink: 0;
  min-height: var(--control-h-sm);   /* Masse der Pille, siehe .filter-btn; aktiv bleibt die Kategoriefarbe */
  padding: var(--space-1) var(--space-7); border-radius: var(--radius-full); cursor: pointer; font-size: var(--fs-md);
  background: var(--panel-2); border: 1px solid var(--border); color: var(--text-dim);
  transition: background var(--duration-fast) ease, border-color var(--duration-fast) ease, color var(--duration-fast) ease;
}
.category-tab:hover { background: var(--panel-hover); }
.category-tab.active { color: var(--text-on-color); border-color: var(--tab-color, var(--accent)); background: var(--tab-color, var(--accent)); }
/* R47: Kategorie eines bestehenden Eintrags ist nicht mehr umschaltbar -- restliche Tabs bleiben
   sichtbar (Uebersicht), aber erkennbar deaktiviert statt einfach interaktionslos gleich auszusehen. */
.category-tab-locked { opacity: 0.4; cursor: not-allowed; }
.category-tab-locked:hover { background: var(--panel-hover); }

/* ---------- Zwei-spaltiges Formular (Event/Spiel) ---------- */
.two-col-form { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-6) var(--space-10); align-items: start; }
@media (max-width: 560px) { .two-col-form { grid-template-columns: 1fr; } }
.form-col { display: flex; flex-direction: column; gap: var(--space-6); }
/* min-width:0 -- Grid-Items sind per Spec standardmaessig "min-width: auto", wodurch ein breiter
   Inhalt (z.B. ein langes Checkbox-Label) statt zu umbrechen einfach die ganze Grid-Spur
   aufweitet und ueber den Rand hinauslaeuft, obwohl der Inhalt selbst (per .inline-check
   flex-wrap:wrap) eigentlich umbrechen koennte. Erst mit min-width:0 darf die Spur auf ihre
   eigentliche Breite schrumpfen. */
.form-full-width { grid-column: 1 / -1; min-width: 0; }
.form-row-3 { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-6) var(--space-10); }
@media (max-width: 560px) { .form-row-3 { grid-template-columns: 1fr; } }
/* Nutzer-Feedback: "füge zu der event uhrzeit für den start tag eine uhrzeit und für den endtag
   eine uhrzeit hinzu" -- die Datum/Enddatum/Uhrzeit-Zeile im Event-Formular hat jetzt ein viertes
   Feld (End-Uhrzeit), ID-Scope statt die geteilte .form-row-3-Basisklasse zu aendern (die koennte
   in Zukunft auch anderswo mit echten 3 Spalten gebraucht werden). */
#event-session-fields .form-row-3 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 560px) { #event-session-fields .form-row-3 { grid-template-columns: 1fr; } }
.form-title-row { display: flex; align-items: flex-end; gap: var(--space-5); }
.form-title-input { flex: 1; }
.form-title-row .icon-picker { flex-shrink: 0; }
/* Verknuepfungsfelder (Nutzer/Spiel-Film-Serie) im Event-Formular bleiben bedingt sichtbar, aber
   stehen ab R37 als optisch eigener, per Trennlinie abgesetzter Block am Formularende. */
.event-form-linked-block { border-top: 1px solid var(--border); padding-top: var(--space-6); margin-top: var(--space-2); display: grid; gap: var(--space-6); grid-template-columns: 1fr 1fr; }
/* Cover-Bild + Weitere-Fotos/Externe-Links bleiben technisch ausserhalb von <form id="event-form">
   (eigenes Submit-Handling), stehen aber seit R40 optisch ganz oben im Formular (Nutzer-Feedback:
   "Titelbild sollte wieder ganz oben sein") -- Trennlinie daher unten statt oben. */
.event-cover-links-group { border-bottom: 1px solid var(--border); margin-bottom: var(--space-8); padding-bottom: var(--space-8); display: flex; flex-direction: column; gap: var(--space-8); }

/* ---------- Event-Formular: Cover + Spalte 1 + Spalte 2 (Layout-Neugestaltung) ---------- */
/* Cover-Spalte (Titelbild + Cover-bezogene Buttons) lebt jetzt innerhalb von <form>, aber
   ausserhalb des sperrbaren <fieldset>, damit sie immer bedienbar bleibt (siehe HTML-Kommentar
   bei #event-form-cover-col). */
.event-form-cover-col { display: flex; flex-direction: column; gap: var(--space-6); min-width: 0; }
/* Spalte 1 (Spiel-Suche/Verknuepfung, Titelbild-uebernehmen, Ordner-Toggle, Foto-Widget) --
   ebenfalls ausserhalb des Fieldsets, damit die Suche waehrend einer gesperrten Spiel/Film/Serie-
   Session nutzbar bleibt. */
.event-form-col1 { display: flex; flex-direction: column; gap: var(--space-5); min-width: 0; }
/* Spalte 2 (Datum/Enddatum/Ort/Tags/Teilnehmer) lebt INNERHALB des Fieldsets (native
   disabled-Sperre bleibt dadurch erhalten) -- durch display:contents auf dem Fieldset (siehe
   .event-session-fields weiter unten) wird sie trotzdem zum direkten Geschwister-Grid-Item von
   Cover-Spalte/Spalte 1 im selben #event-form-Grid. */
.event-form-col2 { display: flex; flex-direction: column; gap: var(--space-6); min-width: 0; }
/* Nutzer-Feedback: "mache das event bearbeiten fenster breiter das es rechts nicht abgeschnitten
   wird" -- nur auf Desktop (Mobil nutzt bereits eine eigene max-width:none/width:100%-Regel
   fuer #event-modal, siehe @media(max-width:720px) weiter oben in dieser Datei). */
@media (min-width: 721px) {
  /* Zwischenzeitlich 1160px fuer den Knopf "Titelbild übernehmen" neben der Verknuepfung -- der ist seit
     2026-09-11 eine Auswahl am Titelbild-Knopf, die Verknuepfungs-Spalte braucht den Platz nicht mehr. */
  #event-modal:not(.catalog-quick-add) .modal.modal-wide { max-width: 1080px; }
}
/* Desktop-Raster des Formulars -- benannte Flaechen. Verlauf (alles 2026-09-11): erste Runde Titelbild-
   Kachel raus ("datum uhrzeit etc. da rein schieben"); zweite Runde "titelbild hochladen / ändern links
   oben ... daneben mit spiel etc verknüpfen ... spalte 2 rechts: teilnehmer, darunter medien"; dritte
   Runde "schiebe beim bearbeiten den event titel nach oben über titelbild etc.", "anheften und wiederholen
   neben die datumsauswahl mit überschrift 'optionen'", "schiebe tags zudem unter teilnehmer".
   Links: Titelbild (so breit wie seine Knoepfe) | Verknuepfung, darunter Datum | Optionen (so breit wie
   ihre Knoepfe), darunter Ort und die Beschreibung; rechts Teilnehmer, Tags, Medien. Die Beschreibung
   steht links neben der hohen Medien-Kachel statt ueber die volle Breite darunter -- sonst klaffte unter
   dem Ort eine Luecke so hoch wie die Kachel. Tags/Teilnehmer stecken im Markup in einer Zeile
   (#event-form-col3, auf dem Handy nebeneinander), die hier per display:contents aufgeloest wird. Leere
   Flaechen (Termin/Geburtstag ohne Titelbild, Verknuepfung, Medien) fallen einfach weg. Nur Desktop --
   das Handy stapelt per order (siehe @media(max-width:720px) weiter unten). */
#event-form.two-col-form { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 721px) {
  #event-form.two-col-form {
    /* Die Verknuepfung steht seit 2026-09-15 UNTER dem Titelbild statt daneben ("film spiel serie
       verknuepft button in event bearbeiten ist abgeschnitten wenn verknuepft mache die
       verknuepfung ruhig unter den titelbild ueberschrift reiter") -- daneben teilte sie sich die
       Zeile mit Optionen und Teilnehmern und wurde bei mittleren Fensterbreiten so schmal, dass
       "Spiel verknüpft" auf zwei Zeilen brach. Die erste Spalte bekommt dafuer eine Mindestbreite. */
    grid-template-columns: minmax(190px, max-content) minmax(0, 1fr) max-content minmax(320px, 0.9fr);
    grid-template-areas:
      "titel        titel    titel    titel"
      "trenn        trenn    trenn    trenn"
      "titelbild    optionen optionen teilnehmer"
      "verknuepfung optionen optionen teilnehmer"
      "datum        datum    datum    tags"
      "ort          ort      ort      ."
      "beschr       beschr   beschr   ."
      "extra        extra    extra    extra";
  }
  #event-form .form-title-row { grid-area: titel; }
  #event-form-title-divider { grid-area: trenn; }
  #event-form-cover-col { grid-area: titelbild; }
  #event-form-col1 { grid-area: verknuepfung; }
  #event-form-col2 { grid-area: datum; }
  #event-form-options { grid-area: optionen; }
  /* Nutzer-Feedback (2026-09-11): "dafür schieben wir den reiter optionen mit den zwei buttons am desktop rechts
     daneben nebeneinander die beiden buttons" (neben die Verknuepfung, siehe Raster oben) und "mache das datum von
     bis feld deutlich kleiner" -- der Von/Bis-Block bekommt eine feste Hoechstbreite statt der ganzen Zeile. */
  /* Zwei gleich breite Spalten statt max-content: Anheften/Wiederholen sind dadurch gleich gross,
     und die Rhythmus-Auswahl darunter sitzt genau unter ihnen, ohne das Raster zu verschieben
     (2026-09-13, Nutzerwunsch: "die beiden knoepfe genauso gross und drunter"). */
  /* Feste Breite statt max-width (2026-09-14 gemeldet: "der button sollte nicht breiter werden wenn
     aktiv") -- sonst wuchs der ganze Block, sobald die Rhythmus-Auswahl dazukam, und beide Knoepfe
     darueber wurden mit breiter. */
  #event-form-options {
    display: grid; width: 320px; max-width: 100%;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    /* Alle Optionen sind gleich grosse Knoepfe, der Medienordner steht unter "Wiederholen"
       (2026-09-15: "mache bei event erstellen den medienordner button genauso gross und unter
       wiederholen"). Der Rhythmus braucht keine eigene Zelle mehr -- er klappt als Fenster ueber
       dem Knopf auf (siehe .event-form-wiederholung). */
    grid-template-areas:
      "label     label"
      "anheften  wiederholen"
      "countdown ordner"
      "privat    .";
    column-gap: var(--space-3); row-gap: var(--space-3); align-content: start;
  }
  #event-form-options .field-label { grid-area: label; }
  #event-pin-header-btn { grid-area: anheften; }
  #event-countdown-header-btn { grid-area: countdown; }
  #event-private-header-btn { grid-area: privat; }

  #event-recurring-anker { grid-area: wiederholen; }
  #event-form-options .event-form-folder-toggle { grid-area: ordner; }
  #event-form-col2 { max-width: 27rem; }
  #event-location-label { grid-area: ort; }
  #event-form-col3 { display: contents; }
  #event-attendees-field { grid-area: teilnehmer; }
  #event-tags-field { grid-area: tags; }
  #event-description-label { grid-area: beschr; }
  #event-form .event-form-linked-block { grid-area: extra; }
  /* Sammlungs-Schnellanlage (480px, ohne Fieldset, siehe .catalog-quick-add): Titelbild-Knopf und Suche
     untereinander ueber die volle Breite -- mit dem Raster oben bliebe der Rest leer (TEST=katalogschnell,
     gegen den Vorzustand gemessen). */
  #event-modal.catalog-quick-add #event-form.two-col-form { grid-template-columns: minmax(0, 1fr); grid-template-areas: "titelbild" "verknuepfung"; }
}
/* Ueberschriften "Titelbild", "Verknüpfung", "Medien" (2026-09-11: "titelbild solte auch eine überschrift
   haben genauso wie verknüpfungen auch", "medien (als überschrift)"). */
.event-form-cover-group { display: flex; flex-direction: column; gap: var(--space-4); }
#event-catalog-resolver > .event-form-group-heading { margin-bottom: var(--space-4); }
/* "Mit Spiel/Film/Serie verknüpfen" eckig wie die Titelbild-Knoepfe (gleiche Klassen, siehe index.html);
   eingeschaltet in der Akzent-Toenung, die es als Pille (.filter-btn.active) hatte. */
.event-catalog-optional-toggle-btn.active { background: var(--accent-tint); color: var(--accent-light); border-color: var(--accent-strong); }
.event-catalog-optional-toggle-btn:not(.active):hover { background: var(--panel-hover); border-color: transparent; color: var(--text); }
/* Ueberschriften im Formular einheitlich. Nutzer-Feedback (2026-09-11): "passe überall die überschriften
   in einem gleichen stil an. gerade sind mache sachen fett manche nicht und manche grau. mache die
   überschriften einfach wie zuvor bei event titel oder 'chat' auf der event übersicht" -- dieselben Werte
   wie die Koepfe BESCHREIBUNG/CHAT der Ansicht (.event-preview-notiz-header/-chat-header) und der Kopf der
   Medien-Kachel, ohne Symbol davor. .field-label bleibt ausserhalb des Event-Formulars unveraendert. */
#event-modal .field-label, #event-title-label-text {
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold); color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.04em;
}
#event-modal .field-label .mdi { display: none; }
/* Die Trennlinie unter dem Titel (seit 2026-09-11 oben) nur als Linie -- den Abstand gibt das Raster. */
#event-form-title-divider { margin: 0; padding: 0; }
/* "Optionen" (Anheften/Wiederholen, 2026-09-11) neben dem Datum, Knoepfe untereinander wie die Titelbild-
   Knoepfe; eingeschaltet in den Farben von .pin-toggle-btn.pinned/.recurring-active. .text-btn:hover
   faerbt sonst rot (fuer destruktive Knoepfe gedacht). */
.event-form-options { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.event-form-options .pin-toggle-btn { justify-content: flex-start; }
.event-form-options .pin-toggle-btn:not(.pinned):not(.recurring-active):not(.folder-active):hover { background: var(--panel-hover); border-color: transparent; color: var(--text); }
/* Medienordner unter Anheften/Wiederholen, "so breit wie beide buttons" (2026-09-11) -- ueberspannt beide
   Spalten des Optionen-Rasters (Desktop max-content, Handy 1fr); eingeschaltet in der Akzent-Toenung wie
   der Verknuepfen-Knopf. */
.event-form-options .event-form-folder-toggle { grid-column: 1 / -1; }
.pin-toggle-btn.folder-active { background: var(--accent-tint); border-color: var(--accent-strong); color: var(--accent-light); }
/* Album-Fenster (openEventAlbumModal(), 2026-09-11: "das album als popup öffnen und nicht als so kleinen
   hover") -- alle Fotos als Kacheln, ein Klick oeffnet die Lightbox. */
#event-album-modal .modal { max-width: 900px; width: 92vw; }
.event-album-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--space-4); }
.event-album-cell { aspect-ratio: 1; padding: 0; border: none; }
/* Verknuepfen-Popup (syncEventLinkPopup()): Platz fuer die Trefferliste unter dem Suchfeld, die als
   Dropdown (position:absolute, bis 260px hoch) sonst am Fensterrand abgeschnitten wuerde -- wie bei der
   Sammlungs-Schnellanlage (.catalog-quick-add). */
#event-link-modal .modal { max-width: 520px; width: 92vw; min-height: 420px; }
/* Infos zum gewaehlten bzw. verknuepften Eintrag im Verknuepfen-Fenster (2026-09-11: "mit den infos des spieles
   release beschreibung usw das ich verknüpfen will + weitere events") und die Knopfleiste darunter. */
.event-link-preview { display: flex; flex-direction: column; gap: var(--space-6); }
.event-link-preview-kopf { display: flex; gap: var(--space-6); align-items: flex-start; }
.event-link-preview-cover { width: 88px; aspect-ratio: 2 / 3; object-fit: cover; border-radius: var(--radius-md); flex-shrink: 0; background: var(--panel-2); }
.event-link-preview-kopf-text { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.event-link-preview-kopf-text .event-preview-kopf { margin: 0; }
.event-link-preview-title { margin: 0; font-size: var(--fs-lg); line-height: 1.25; overflow-wrap: anywhere; }
.event-link-preview-hinweis { font-size: var(--fs-sm); color: var(--text-dim); }
.event-link-preview-hinweis:empty { display: none; }
.event-link-preview-desc { font-size: var(--fs-sm); color: var(--text-dim); line-height: 1.5; white-space: pre-line; max-height: 14em; overflow-y: auto; }
/* Ohne Release und ohne weitere Events (neuer Eintrag ohne Datum) keine leere Karte. */
.event-form-linked-info:not(:has(> .event-form-linked-info-block:not([style*="none"]))) { display: none; }
.event-link-modal-footer {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--space-4); flex-wrap: wrap;
  padding: var(--space-6) var(--space-10); background: var(--panel-2); flex-shrink: 0;
}
#event-link-unlink-btn { margin-right: auto; }
/* Nutzer-Feedback (2026-09-11): "mobil ist in der spiel verknüpft zurück button noch etwas verschoben" -- die
   app-weite Handy-Regel setzt den Zurueckpfeil fest auf top:10px (abgestimmt auf mehrzeilige Kopfzeilen); die
   einzeilige, kleine Ueberschrift dieses Fensters stand dadurch 5px hoeher als der Pfeil. Hier mittig in der Zeile. */
@media (max-width: 720px) {
  #event-link-modal .modal-header-close { top: 50%; transform: translateY(-50%); }
  /* "Verknüpfung lösen" und "Andere suchen" passten bei 360px mit 20px Seitenrand nicht in eine Zeile. */
  .event-link-modal-footer { padding: var(--space-6) var(--space-8); }
  .event-link-modal-footer .text-btn { padding-left: var(--space-4); padding-right: var(--space-4); }
}
/* Verknuepft zeigt der Knopf Art und Titel (syncEventLinkButton() in app.js) -- lange Titel werden gekuerzt. */
.event-catalog-optional-toggle-btn { max-width: 100%; min-width: 0; }
.event-catalog-optional-toggle-btn .event-link-btn-lang { min-width: 0; max-width: 22em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Nutzer-Feedback (2026-09-11): "mache auf dem desktop das 'mit spiel film serie verknüpfen' button genauso wie mobil
   nur spiel film serie steht drin" -- ueberall die Kurzbeschriftung ("Spiel/Film/Serie" bzw. "Spiel verknüpft"),
   der Titel steht im Tooltip und im Verknuepfen-Fenster. Dafuer passt am Desktop "Optionen" daneben. */
#event-catalog-optional-toggle .event-link-btn-lang { display: none; }
#event-catalog-optional-toggle .event-link-btn-kurz { display: inline; }
/* Titelbild als Knopf statt Kachel (2026-09-11, siehe index.html): die Kachel bleibt unsichtbar im DOM. */
#event-title-image-picker { display: none; }
.event-form-cover-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.event-form-cover-btn { background: var(--knopf); border-color: transparent; gap: var(--space-3); font-size: var(--fs-sm); }
/* .text-btn:hover faerbt rot (fuer destruktive Knoepfe gedacht) -- passt zu "Entfernen", nicht zum Hochladen. */
/* Hover in --panel-hover wie beim Heute-Knopf: --panel ist die Fensterfarbe selbst, der Knopf verschwand
   beim Ueberfahren (auf dem Handy-Screenshot sah "Wiederholen" dadurch wie ein blosser Text aus). */
#event-title-image-btn:hover { background: var(--panel-hover); border-color: transparent; color: var(--text); }
/* Termin/Geburtstag haben kein Titelbild. Auf dem Handy bleibt die Cover-Spalte trotzdem sichtbar, weil
   dort Anheften/Wiederholen darin stehen -- also die Titelbild-Knoepfe gezielt ausblenden. */
#event-modal.event-header-no-cover .event-form-cover-group { display: none; }
/* Die geerbte #event-session-fields .form-row-3-Regel (4 gleich breite Spalten, fuer die
   frueher volle Formularbreite gedacht) passt nicht mehr -- Spalte 2 ist dafuer zu schmal, Datum/
   Enddatum stehen jetzt als zwei eigene 2-spaltige Zeilen. Hoehere Spezifitaet (ID + 3 Klassen)
   gewinnt automatisch gegen die 1-Klassen-Basisregel, kein !important noetig. */
/* Von/Bis-Block (2026-09-11, "start und enddatum in von bis ändern und datum als überschrift machen"):
   Ueberschrift, dann je Zeile Beschriftung | Datum | Uhrzeit. Die Uhrzeit hat eine feste Breite und
   margin-left:auto -- steht in der Bis-Zeile kein Enddatum (Termin), bleibt die End-Uhrzeit trotzdem
   genau unter der Start-Uhrzeit. Frueher zwei Raster-Zeilen mit je eigener Beschriftung; deren
   Gleichverteilungs-Regel ("datum und enddatum block sind nicht gleich groß") ist damit entfallen.
   Nutzer-Feedback (Mobil): "datum und uhrzeit sollten in eine zeile passen" -- gilt weiter, die Zeile
   bricht auch auf dem Handy nicht um. */
.event-form-date-block { display: flex; flex-direction: column; gap: var(--space-4); }
.event-form-date-heading { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); }
/* "Uhrzeit" steht genau ueber der Uhrzeit-Spalte (gleiche feste Breite, beide am rechten Rand). */
.event-form-date-heading .event-form-date-zeit-kopf { flex: 0 0 8.5rem; }
/* Nutzer-Feedback (2026-09-11): "umrande das von feld in der eingestellten farbe und die anderen nicht dann
   erkennt man das es nicht optional ist" -- nur das Pflicht-Datum traegt die Akzentfarbe. */
#event-date { border-color: var(--accent); }
.event-form-date-row { display: flex; align-items: center; gap: var(--space-4); }
.event-form-date-row-label { flex: 0 0 2.2em; color: var(--text-dim); font-size: var(--fs-sm); }
.event-form-date-row .date-with-today, .event-form-date-end { flex: 1; min-width: 0; }
.event-form-date-time { flex: 0 0 8.5rem; min-width: 0; margin-left: auto; }
.event-form-date-row input { width: 100%; box-sizing: border-box; }
/* Nur iOS-Safari (Nutzer-Feedback 2026-09-11: "datum von bis ist auf der rechten seite auf meinem iphone
   abgeschnitten ... warum sieht es auf meinem iphone etwas anders aus als wenn ich am pc den browser kleiner
   ziehe?") -- iOS zeichnet Datums-/Uhrzeitfelder als System-Steuerelement mit eigener Mindestbreite und
   zentriertem Wert; in der festen 8.5rem-Spalte ragte das Feld dadurch rechts aus dem Fenster. Chromium (auch
   mit Touch-Emulation, TEST=eventvonbishandy) zeigt das nicht -- deshalb gezielt per -webkit-touch-callout,
   das nur iOS-WebKit kennt: ohne System-Optik schrumpft das Feld auf seine Spalte, der Wert steht links wie
   in den anderen Feldern. min-height haelt ein leeres Feld (iOS zeigt dann keinen Platzhalter) auf Hoehe. */
@supports (-webkit-touch-callout: none) {
  .event-form-date-row input { -webkit-appearance: none; appearance: none; min-width: 0; display: block; }
  .event-form-date-row input::-webkit-date-and-time-value { text-align: left; min-height: 1.4em; }
  /* Folgerunde: "am handy steht im feld uhrzeit kein placeholder drin" -- ohne System-Optik zeigt iOS ein leeres
     Uhrzeitfeld ganz leer. Platzhalter als Pseudo-Element der umgebenden Beschriftung, solange das Feld leer ist
     (.leer, syncEventTimePlaceholders() in app.js); Klicks gehen durch aufs Feld. */
  .event-form-date-time { position: relative; }
  .event-form-date-time.leer::after {
    content: "--:--"; position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
    color: var(--text-dim); pointer-events: none;
  }
}
.event-form-tags-attendees-row { display: flex; gap: var(--space-6); }
.event-form-tags-attendees-row .form-col { flex: 1; min-width: 0; }
/* Nutzer-Feedback: "grau und kleiner" fuer das "(optional)"-Suffix in Feldlabels (Uhrzeit/
   Enddatum/End-Uhrzeit) -- bisher gleich gross/hell wie der Rest des Labels. */
.field-label-optional { font-weight: var(--fw-regular); color: var(--text-dim); font-size: 0.82em; }
/* Nutzer-Feedback: "infos zum game ... unter die titelbild übernehmen button schieben und alles
   so im stil von weitere events machen" -- steht jetzt in Spalte 1 (siehe index.html), nicht mehr
   als eigene volle Zeile zwischen Spalte 2 und Titel. */
/* Nutzer-Feedback (Folgerunde): "release, in besitz und weitere events sollten einen hintergrund
   gemeinsam haben und in etwa gleich aussehen" -- ein gemeinsamer Karten-Hintergrund fuer den
   ganzen Block statt (wie zuvor) gar keinem, "In Besitz" ist komplett entfallen. */
.event-form-linked-info {
  display: flex; flex-direction: column; gap: var(--space-4);
  background: var(--panel-2); border-radius: var(--radius-md); padding: var(--space-5) var(--space-6);
}
.event-form-linked-info-block { display: flex; flex-direction: column; gap: var(--space-3); }
.event-form-linked-info-label { font-size: var(--fs-xs); color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.03em; }
/* Zeilen innerhalb der gemeinsamen Karte brauchen keinen eigenen Hintergrund mehr (sonst wirkt es
   wie zwei verschachtelte Karten) -- nur noch die Karte selbst ist hinterlegt. */
.event-form-linked-info .boards-linked-event-row { background: transparent; padding: var(--space-1) 0; }
/* Nutzer-Feedback: "release datum neben den text release" -- Label + Wert stehen jetzt in einer
   Zeile statt Label ueber Wert (siehe index.html). Anders als die "Weitere Events"-Zeilen ist die
   Release-Zeile nicht klickbar -- cursor/hover der wiederverwendeten .boards-linked-event-row-Basis
   wieder neutralisiert. */
.event-form-linked-info-inline-label { font-size: var(--fs-xs); color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.03em; }
.event-form-linked-info-release-row { cursor: default; }
.event-form-linked-info-release-row:hover { background: transparent; }

/* ---------- Foto/Ordner-Widget (4er-Kachel, ersetzt "Weitere Fotos"/"Medien hochladen") ---------- */
/* Nutzer-Feedback: "die bilder etwas kleiner darstellen und unter das cover schieben ... aus den 4
   bildern 4 bilder nebeneinander machen dann wird es nicht so hoch" -- am Desktop jetzt eine Reihe
   zu 4 (statt 2x2), dadurch deutlich flacher; Mobil behaelt die bisherige 2x2-Kachel-Groesse neben
   dem Titelbild (siehe @media(max-width:720px) weiter unten). */
.event-photo-widget-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: 1fr; gap: var(--space-2);
  max-width: 180px; aspect-ratio: 4 / 1;
}
.event-photo-widget-cell { position: relative; border-radius: var(--radius-md); overflow: hidden; background: var(--panel-2); }
.event-photo-widget-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.event-photo-widget-cell-empty { display: flex; align-items: center; justify-content: center; color: var(--text-dim); font-size: var(--fs-2xl); }
.event-photo-widget-cell-delete {
  position: absolute; top: 3px; right: 3px; z-index: 2; width: 20px; height: 20px;
  border-radius: 50%; border: none; background: var(--on-image-bg); color: var(--text-on-color);
  display: flex; align-items: center; justify-content: center; font-size: var(--fs-2xs); cursor: pointer; padding: 0;
}
.event-photo-widget-cell-add {
  border-radius: var(--radius-md); border: 1px dashed var(--border); background: var(--panel-2); color: var(--text-dim);
  display: flex; align-items: center; justify-content: center; font-size: var(--fs-2xl); cursor: pointer;
}
.event-photo-widget-cell-add:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.event-photo-widget-cell-add:disabled { opacity: 0.5; cursor: not-allowed; }
.event-photo-widget-grid.disabled .event-photo-widget-cell-empty { opacity: 0.5; }
/* Nutzer-Feedback: "unter die 4 foto kacheln verschieben und nur daneben schreiben
   'Medienordner erstellen'" -- Toggle + Kurzlabel jetzt in einer Zeile statt Toggle + eigene
   Beschreibungszeile darunter. */
/* Nutzer-Feedback: "medienordner erstellen bitte rechtsbündig" -> "sorry ich meinte links" -- die
   Zeile ist linksbuendig. (Das fruehere margin-left:auto von .wiki-mode-toggle, das sie nach rechts
   zog, gibt es seit 2026-09-15 nicht mehr -- der Umschalter sitzt jetzt in der rechten Spalte.) */
.event-folder-toggle-block { display: flex; align-items: center; gap: var(--space-4); margin-top: var(--space-2); }
/* Nutzer-Feedback (Folgerunde): "schiebe die medien ordner zeile über die release etc. infos mit
   eigenem background ... das die beiden buttons nebeneinander sind (medienordner + medien
   hochladen)" -- teilt sich per .event-form-linked-info denselben Karten-Hintergrund wie die
   Release/Weitere-Events-Karte darunter (siehe .event-form-linked-info Basisregel), hier aber als
   Reihe statt Spalte (die Basisregel setzt flex-direction:column, hier ueberschrieben) und ohne
   deren margin-top von .event-folder-toggle-block. Beide Buttons gleich breit nebeneinander. */
.event-form-linked-info.event-folder-toggle-block { flex-direction: row; margin-top: 0; }
.event-form-linked-info.event-folder-toggle-block #event-form-media-folder-toggle-btn,
.event-form-linked-info.event-folder-toggle-block #event-form-media-upload-btn { flex: 1; justify-content: center; }
/* Nutzer-Feedback: "neben medienordner erstellen sollte ein kleines 'i' sein für die information
   was das ist." -- native title-Tooltip, gleiches Muster wie die uebrigen Icon-only-Buttons in
   diesem Formular (z.B. Bild-aendern/-entfernen), die ebenfalls nur per title-Attribut erklaeren. */
.event-form-info-icon { font-size: var(--fs-lg); color: var(--text-dim); cursor: help; }
.event-form-info-icon:hover { color: var(--text); }
/* Nutzer-Feedback: "die 'i' symbole soltlena uch ein hover popup erhalten" -- echtes Popover statt
   der bisherigen nativen title-Tooltips, per JS (attachInfoTooltip in app.js) dynamisch positioniert
   und an body angehaengt, damit es nicht vom Modal-overflow abgeschnitten wird. */
.info-tooltip-popover {
  position: fixed;
  z-index: var(--z-toast);
  max-width: 260px;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  background: var(--panel);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-md);
  font-size: var(--fs-sm);
  color: var(--text-dim);
  line-height: 1.4;
  /* Muss Hover-Events empfangen, damit die Maus vom Icon auf das Popover wandern kann (siehe
     scheduleHide()/cancelHide() in attachInfoTooltip(), app.js), ohne es sofort zu schliessen. */
  pointer-events: auto;
  /* Erlaubt Absaetze/Zeilenumbrueche im data-tooltip-Text (siehe attachInfoTooltip() in app.js) --
     ohne dies wuerden \n-Zeichen im Fliesstext ignoriert und lange Tooltips wie der Bongo-Cat-Tipp
     zu einem unuebersichtlichen Textblock verschmelzen. */
  white-space: pre-line;
}
/* Optionales data-tooltip-html (siehe attachInfoTooltip() in app.js) fuer Tooltips mit
   eingebetteten Screenshots wie beim Bongo-Cat-Steam-Einladungslink-Tipp. */
.info-tooltip-popover p { margin: 0 0 var(--space-4); }
.info-tooltip-popover p:last-child { margin-bottom: 0; }
.info-tooltip-popover .bongocat-tip-step img {
  display: block;
  max-width: 100%;
  margin-top: var(--space-2);
  border-radius: var(--radius-xs);
  border: 1px solid var(--tint-hover);
}
/* Nutzer-Feedback: Termin/Geburtstag haben kein Titelbild und keine Spiel/Film/Serie-Verknuepfung
   -- Cover-Spalte und Spalte 1 sind dadurch komplett leer, siehe applyCategoryFieldRules() (setzt
   diese Klasse auf #event-modal). Beide Spalten ausblenden und das Grid auf eine Spalte
   zurueckfallen lassen, statt eine leere Luecke links neben Spalte 2 stehen zu lassen. */
/* Das Raster dafuer steht seit 2026-09-11 im @media(min-width:721px)-Block bei #event-form.two-col-form. */
#event-modal.event-header-no-cover .event-form-cover-col,
#event-modal.event-header-no-cover .event-form-col1 { display: none; }
/* Auswahl Hochladen/Uebernehmen am Titelbild-Knopf (showTitleImageSourceMenu(), 2026-09-11) -- ersetzt den frueheren
   Knopf "Titelbild übernehmen"; lange Titel der Verknuepfung werden gekuerzt. */
.create-menu.event-title-image-source-menu { min-width: 200px; max-width: min(300px, calc(100vw - 32px)); }
.event-title-image-source-menu .create-menu-item span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Reiter "Bilder" in "Organisation bearbeiten" (loadOrgImagesOverview() in settings.js, 2026-09-11): Tabelle je
   Bereich, Liste der Alben mit ZIP-Download, Aufraeumen. Das Fenster ist ohnehin nur am Desktop erreichbar. */
.org-images-tabelle { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); margin: var(--space-4) 0 var(--space-8); }
.org-images-tabelle th {
  text-align: left; padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border);
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold); color: var(--text-dim); letter-spacing: 0.04em; text-transform: uppercase;
}
.org-images-tabelle td { padding: var(--space-4); border-bottom: 1px solid var(--border); }
.org-images-tabelle :is(th, td):not(:first-child) { text-align: right; white-space: nowrap; }
.org-images-tabelle .org-images-offen { color: var(--warning); font-weight: var(--fw-semibold); }
.org-images-kopf {
  margin: 0 0 var(--space-4); font-size: var(--fs-2xs); font-weight: var(--fw-semibold); color: var(--text-dim);
  letter-spacing: 0.04em; text-transform: uppercase;
}
.org-images-albums { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-8); max-height: 320px; overflow-y: auto; }
.org-images-album { display: flex; align-items: center; gap: var(--space-5); padding: var(--space-4) var(--space-5); background: var(--inhalt-kachel); border-radius: var(--radius-md); }
.org-images-album-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.org-images-album-titel { font-weight: var(--fw-medium); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.org-images-aufraeumen { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-5); }

/* ---------- Titelbild-Auswahl ---------- */
.title-image-picker {
  position: relative;
  margin-bottom: var(--space-8);
  border-radius: 10px;
  overflow: hidden;
  background: var(--panel-2);
  border: 1px dashed var(--border);
  cursor: pointer;
  min-height: 160px;
  display: flex; align-items: center; justify-content: center;
}
.title-image-picker img { width: 100%; max-height: 260px; object-fit: cover; display: block; }
/* Nutzer-Feedback: "passe das vorschaubild auf die richtige größe an (cover format)" -- das
   Titelbild ist jetzt IMMER im selben Hochkant-Cover-Format (2:3) wie die schreibgeschuetzte
   Event-Vorschau (siehe .event-preview-cover), statt vorher zwischen breit (16:9) und hochkant
   je nach Kategorie/Verknuepfung zu wechseln (der bisherige Toggle in applyCategoryFieldRules()
   in app.js ist entfallen). Der Klassenname war frueher "-16-9", ist jetzt auf die neue
   Standardform umbenannt -- kein JS referenziert den Klassennamen direkt (nur ueber die ID
   #event-title-image-picker), die Umbenennung ist daher gefahrlos. */
.event-form-cover-col .title-image-picker-portrait { margin: 0; flex-shrink: 0; }
.event-refetch-cover-btn {
  width: 100%; box-sizing: border-box; justify-content: center; gap: var(--space-3); font-size: var(--fs-sm);
  padding: var(--space-3) var(--space-5); margin-bottom: var(--space-2);
}
.event-refetch-cover-btn:hover { background: var(--panel-hover); border-color: var(--accent); color: var(--accent); }
.event-refetch-cover-btn.is-loading i { animation: spin 0.8s linear infinite; }
/* Gleiches Seitenverhaeltnis wie .event-preview-cover -- Nutzer-Feedback: "titelbild etwas
   größer machen, dass die 4 fotos und das titelbild unten an der gleichen linie abschließen". */
.title-image-picker-portrait { aspect-ratio: 2 / 3; max-width: 180px; margin: 0 auto var(--space-8); }
.title-image-picker-portrait img { width: 100%; height: 100%; max-height: none; object-fit: cover; }
.steam-link-row { display: flex; gap: var(--space-4); align-items: flex-start; }
/* align-self:center (nicht global align-items, siehe Kommentar bei #event-catalog-kind-select
   weiter unten) -- sonst bleibt der Wrap durch align-items:stretch hoeher als sein Input, wodurch
   Input und Dropdown zwar gleich hoch, aber nicht auf gleicher Hoehe zentriert sind. */
.steam-search-inline-wrap { position: relative; flex: 3 1 0; min-width: 0; align-self: center; }
.steam-search-inline-wrap input { width: 100%; box-sizing: border-box; }
/* R48: Event/LAN-Party-Resolver -- Zieltyp-Dropdown links neben dem Suchfeld. */
.steam-search-inline-wrap-row { display: flex; gap: var(--space-4); align-items: stretch; }
/* R49: Nutzer-Feedback -- das Dropdown wurde durch align-items:stretch auf die Hoehe des
   Such-Wraps gestreckt (dessen .icon-search hat ein margin-bottom fuer das Ergebnis-Panel
   darunter, das faelschlich mitgestreckt wurde). align-self:center statt globalem align-items,
   damit .steam-search-panel-dropdown (position:absolute relativ zu .steam-search-inline-wrap)
   unangetastet bleibt. */
/* Nutzer-Feedback: trotz align-self:center blieb ein sichtbarer Hoehenunterschied zum Suchfeld
   daneben -- <select> und <input> rendern bei identischem Padding/Font-Size browserbedingt nicht
   exakt gleich hoch. Explizite, gemeinsame Hoehe fuer beide erzwingt echte Deckungsgleichheit.
   Ausserdem: .icon-search hat ein margin-bottom:8px (Abstand zu einer normal-flow-Ergebnisliste
   bei anderen Verwendungen dieser Klasse) -- das blaeht hier den .steam-search-inline-wrap auf
   44px auf (36px Input + 8px Margin), obwohl das Ergebnis-Panel hier position:absolute ist und
   den Abstand gar nicht braucht. Dadurch war der Wrap trotz align-self:center genauso hoch wie
   die Zeile, waehrend das Select echt zentriert wurde -- beide sassen dadurch auf unterschiedlicher
   Hoehe. margin-bottom:0 nur fuer dieses Suchfeld behebt das, ohne .icon-search global anzufassen. */
#event-catalog-kind-select { flex: 0 0 110px; align-self: center; height: 36px; box-sizing: border-box; }
#event-catalog-search-input { height: 36px; box-sizing: border-box; margin-bottom: 0; }
.steam-search-panel-dropdown {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 20;
  margin-top: 0;
}
.form-hint { display: block; color: var(--text-dim); font-size: var(--fs-sm); margin-top: var(--space-2); font-weight: var(--fw-regular); }
.steam-desc-field label { display: block; margin-bottom: var(--space-2); }
.steam-desc-readonly {
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: var(--space-4) var(--space-5); font-size: var(--fs-md); line-height: 1.5; color: var(--text-dim);
  max-height: 140px; overflow-y: auto;
}
.steam-search-panel {
  margin-top: var(--space-3); padding: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--panel-2); max-height: 260px; overflow-y: auto;
}
.steam-search-panel .icon-search { width: 100%; margin-bottom: var(--space-3); box-sizing: border-box; }
.steam-result { display: flex; gap: var(--space-4); align-items: center; padding: var(--space-3); border-radius: var(--radius-sm); cursor: pointer; }
.steam-result:hover { background: var(--panel-hover); }
.steam-result img { width: 40px; height: 24px; object-fit: cover; border-radius: var(--radius-xs); }
.steam-result span { font-size: var(--fs-md); }
.steam-result-badges { display: flex; gap: var(--space-2); flex-shrink: 0; color: var(--text-dim); }
.steam-result-badges i { font-size: var(--fs-body); }
/* Nutzer-Feedback (Mobil, Event erstellen): "titelbild hinzufügen text noch zentriert" -- bei
   schmalem Titelbild (Mobil-Portraitformat) bricht der Text auf zwei Zeilen um; align-items:center
   zentriert nur den Text-Block als Ganzes innerhalb des Flex-Containers, die beiden umgebrochenen
   Zeilen selbst blieben ohne text-align darin linksbuendig zueinander. */
.title-image-empty { color: var(--text-dim); font-size: var(--fs-lg); display: flex; flex-direction: column; align-items: center; gap: var(--space-4); text-align: center; }
.title-image-picker input[type="file"] {
  position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%;
}
.title-image-picker.has-image input[type="file"] { pointer-events: none; }
.title-image-picker.has-image img { cursor: grab; touch-action: none; }
.title-image-picker.has-image img:active { cursor: grabbing; }
/* Nutzer-Feedback: Zoom/Verschieben-Anpassung beim Event-Titelbild komplett entfernt (Zoom-Slider/
   Drag-Hinweis aus dem HTML entfernt, setupCoverDrag() nicht mehr fuer den Event-Picker verdrahtet,
   siehe app.js) -- Grab-Cursor waere hier irrefuehrend, da Ziehen nichts mehr bewirkt. Bewusst nur
   fuer #event-title-image-picker gescoped, .title-image-picker ist mit dem weiterhin per Drag&Zoom
   anpassbaren Profilbanner geteilt (#profile-banner-picker). */
#event-title-image-picker.has-image img { cursor: default; touch-action: auto; }
.title-image-change-btn {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  background: var(--on-image-bg); color: var(--text-on-color); border: none; pointer-events: auto;
}
.title-image-change-btn:hover { background: var(--on-image-bg-hover); color: var(--text-on-color); }
.title-image-remove-btn {
  position: absolute; top: 8px; right: 42px; z-index: 2;
  background: var(--on-image-bg); color: var(--text-on-color); border: none; pointer-events: auto;
}
.title-image-remove-btn:hover { background: var(--on-image-bg-hover); color: var(--text-on-color); }
.title-image-drag-hint {
  position: absolute; bottom: 8px; left: 8px; z-index: 2;
  background: var(--on-image-bg); color: var(--text-on-color); font-size: var(--fs-2xs); padding: var(--space-2) var(--space-4); border-radius: var(--radius-sm);
  pointer-events: none; opacity: 0; transition: opacity var(--duration-fast) ease;
}
.title-image-picker.has-image:hover .title-image-drag-hint { opacity: 1; }

/* Nutzer-Feedback: "die titelbilder sollten in den ordner optionen festgelegt werden können und
   dort auch so hin geschoben werden können" -- Titelbild-Auswahl fuer Medien-Ordner-Kacheln
   (#media-cover-picker-modal): kein Upload wie bei .title-image-picker sonst ueblich, sondern ein
   Foto aus dem bereits vorhandenen Ordnerinhalt anklicken (Streifen unten), danach im Vorschaubild
   oben wie gewohnt per setupCoverDrag()/Zoom-Slider anpassen.
   Nutzer-Feedback (Folgerunde): "hoch und runter ziehen ist buggy" -- Ursache: die geteilte
   .title-image-picker-Basisregel hat weder feste Groesse noch festes Seitenverhaeltnis (nur
   min-height:160px + Flex-Zentrierung), waehrend das <img> selbst nur width:100%+max-height:260px
   ohne feste Hoehe hatte. Je nach Foto-Seitenverhaeltnis konnte die tatsaechlich gerenderte
   Bildhoehe dadurch von picker.getBoundingClientRect() (die Basis fuer setupCoverDrag()s Prozent-
   Rechnung) abweichen -- Letterboxing/Leerraum oben/unten liess vertikales Ziehen an der falschen
   Stelle landen. Festes Seitenverhaeltnis (4/5, identisch zur Kachel selbst, siehe
   .media-game-tile-img-wrap) + width/height:100% mit object-fit:cover auf dem Bild sorgt dafuer,
   dass Picker-Box und gerenderte Bildflaeche exakt uebereinstimmen -- gleiches Prinzip wie beim
   schon immer zuverlaessigen Profilbanner-Picker (#profile-banner-picker), der ebenfalls eine feste
   Groesse hat. Nebeneffekt: die Vorschau zeigt jetzt exakt denselben Ausschnitt wie spaeter die
   Kachel. */
#media-cover-picker {
  aspect-ratio: 4 / 5; max-width: 220px; margin: 0 auto; min-height: 0;
}
#media-cover-picker img { width: 100%; height: 100%; max-height: none; object-fit: cover; }
/* Nutzer-Feedback: "die zoom leiste unten kann ruhig unter dem bild sein und mittig" -- normaler
   Regler im Fluss unter der Bildbox statt eines Overlays darauf (siehe Markup in index.html). */
.media-cover-picker-zoom {
  display: block; width: 100%; max-width: 220px; margin: var(--space-5) auto var(--space-7);
}
.media-cover-picker-thumbs {
  display: flex; gap: var(--space-4); overflow-x: auto; padding-bottom: var(--space-2); margin-bottom: var(--space-6);
}
.media-cover-picker-thumb {
  flex-shrink: 0; width: 64px; height: 64px; border-radius: var(--radius-md); overflow: hidden;
  border: 2px solid transparent; cursor: pointer; background: var(--panel-2);
}
.media-cover-picker-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.media-cover-picker-thumb.selected { border-color: var(--accent); }
.media-cover-picker-empty-hint { color: var(--text-dim); font-size: var(--fs-md); }

.media-upload-game-row { display: flex; gap: var(--space-4); align-items: stretch; }
.media-upload-game-row select { flex: 1; }
.media-upload-game-row .icon-btn { width: 38px; height: auto; }
#media-upload-modal .modal { max-width: 540px; }
/* Nutzer-Feedback: "Mach die mobile medien hochladen maske gerne fullscreen wie die anderen." */
@media (max-width: 720px) {
  #media-upload-modal.modal-overlay { padding: 0; align-items: stretch; }
  #media-upload-modal .modal { max-width: none; width: 100%; height: 100%; max-height: none; border-radius: 0; animation: iosSheetIn var(--duration-slow) var(--ease-sheet) both; }
}
/* Sichtbares Fehler-Feedback fuer Pflichtfelder im Medien-Upload-Formular (Phase 6) -- wird
   per JS nur nach einem fehlgeschlagenen Submit-Versuch gesetzt, nicht dauerhaft ueber :invalid,
   damit ein leeres Auswahlfeld beim Oeffnen der Maske nicht sofort rot erscheint. */
.field-invalid { border-color: var(--danger) !important; box-shadow: 0 0 0 1px var(--danger); }
.file-drop-zone {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-3); padding: var(--space-10); border-radius: 10px; background: var(--panel-2); border: 1px dashed var(--border);
  color: var(--text-dim); font-size: var(--fs-lg); cursor: pointer; text-align: center;
}
.file-drop-zone i { font-size: var(--fs-2xl); }
.file-drop-zone input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; }
.file-drop-zone.has-files { border-style: solid; border-color: var(--accent); color: var(--text); }

.media-tile-datetime {
  position: absolute; bottom: 8px; left: 8px; z-index: 2;
  background: var(--on-image-bg); color: var(--text-on-color); font-size: var(--fs-2xs); padding: var(--space-2) var(--space-4); border-radius: var(--radius-sm);
  pointer-events: none; opacity: 0; transition: opacity var(--duration-fast) ease;
}
.tile-card:hover .media-tile-datetime { opacity: 1; }
.title-image-zoom {
  display: none; position: absolute; bottom: 8px; left: 70px; right: 8px; z-index: 2;
  width: auto;
}
.title-image-picker.has-image .title-image-zoom { display: block; }

/* ---------- EasyMDE (Markdown-Editor) im Dark-Theme ---------- */
.EasyMDEContainer .editor-toolbar {
  background: var(--panel-2); border: 1px solid var(--border); border-bottom: none;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0; opacity: 1;
}
.EasyMDEContainer .editor-toolbar button,
.EasyMDEContainer .editor-toolbar a {
  color: var(--text) !important;
  -webkit-text-fill-color: var(--text) !important;
  opacity: 1 !important;
}
.EasyMDEContainer .editor-toolbar button::before,
.EasyMDEContainer .editor-toolbar a::before {
  color: var(--text) !important;
  -webkit-text-fill-color: var(--text) !important;
}
.EasyMDEContainer .editor-toolbar button:hover,
.EasyMDEContainer .editor-toolbar a:hover,
.EasyMDEContainer .editor-toolbar button.active,
.EasyMDEContainer .editor-toolbar a.active {
  background: var(--panel-hover); border-color: var(--border);
}
.EasyMDEContainer .editor-toolbar button.disabled,
.EasyMDEContainer .editor-toolbar a.disabled {
  color: var(--text-dim) !important;
  -webkit-text-fill-color: var(--text-dim) !important;
}
.EasyMDEContainer .editor-toolbar i.separator { border-left: 1px solid var(--border); }
.EasyMDEContainer .CodeMirror {
  background: var(--panel-2); border: 1px solid var(--border); border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  color: var(--text); min-height: 420px;
}
.EasyMDEContainer .CodeMirror-cursor { border-left-color: var(--text); }
.EasyMDEContainer .cm-s-easymde .cm-header { color: var(--accent-light); }
.EasyMDEContainer .cm-s-easymde .cm-link, .EasyMDEContainer .cm-s-easymde .cm-url { color: var(--accent); }
.EasyMDEContainer .editor-preview, .EasyMDEContainer .editor-preview-side {
  background: var(--panel); color: var(--text);
}
.EasyMDEContainer .editor-statusbar { color: var(--text-dim); }

/* ---------- Icon picker (MDI-Suche) ---------- */
.icon-picker { display: flex; }
.icon-dropdown-btn {
  display: flex; align-items: center; gap: var(--space-4); padding: var(--space-3) var(--space-5); border-radius: var(--radius-sm);
  background: var(--knopf); border: 1px solid var(--border); color: var(--text-dim);
  cursor: pointer; font-size: var(--fs-md); font-family: inherit; width: 100%; text-align: left;
}
.icon-dropdown-btn:hover { background: var(--panel-hover); }
.icon-dropdown-btn.has-selection { color: var(--text); }
.icon-dropdown-preview {
  font-size: var(--fs-xl); width: 24px; height: 24px; display: flex; align-items: center;
  justify-content: center; flex-shrink: 0; color: var(--text-dim);
}
.icon-dropdown-btn.has-selection .icon-dropdown-preview { color: var(--accent); }
.icon-dropdown-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.icon-dropdown-caret { color: var(--text-dim); flex-shrink: 0; }

/* ---------- Icon-Dropdown-Popover (Discord-Stil, immer sichtbares Grid + Suche) ---------- */
.icon-dropdown-popover {
  position: fixed;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-5);
  width: 280px;
  z-index: var(--z-menu);
  box-shadow: var(--shadow-lg);
  animation: fadeInUp var(--duration-fast) ease both;
}
.icon-search {
  width: 100%; background: var(--panel-2); border: 1px solid var(--border); color: var(--text);
  padding: var(--space-4) var(--space-5); border-radius: var(--radius-sm); font-size: var(--fs-md); font-family: inherit; margin-bottom: var(--space-4);
}
/* R43: bisher kein eigener :focus-Stil -- fiel auf den unpassenden blauen Browser-Standardrahmen
   zurueck (am auffaelligsten beim GIF-Suchfeld im Chat-Picker, gleiche Klasse wie Steam/TMDB/
   Notiz-Suchfelder). */
.icon-search:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-tint); }
.icon-dropdown-clear {
  display: flex; align-items: center; gap: var(--space-3); width: 100%; padding: var(--space-4) var(--space-4); margin-bottom: var(--space-3);
  background: none; border: 1px solid var(--border); border-radius: var(--radius-sm); color: var(--text-dim);
  cursor: pointer; font-size: var(--fs-sm); font-family: inherit;
}
.icon-dropdown-clear:hover { background: var(--panel-hover); }
.icon-results { display: flex; flex-wrap: wrap; gap: var(--space-3); max-height: 220px; overflow-y: auto; }
.icon-option {
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--panel-2);
  cursor: pointer;
  font-size: var(--fs-xl);
  flex-shrink: 0;
  transition: background var(--duration-fast) ease, border-color var(--duration-fast) ease;
}
.icon-option:hover { background: var(--panel-hover); }
.icon-option.selected { border-color: var(--accent); background: var(--accent-tint); color: var(--accent); }
.icon-set-tabs { display: flex; gap: var(--space-3); margin-bottom: var(--space-3); }
.icon-set-tab {
  flex: 1; padding: var(--space-3); border-radius: var(--radius-sm); border: 1px solid var(--border); background: none;
  color: var(--text-dim); cursor: pointer; font-size: var(--fs-sm); font-family: inherit;
}
.icon-set-tab.active { background: var(--accent); color: var(--text-on-color); border-color: var(--accent); }

/* ---------- Mini-Kalender-Popover fuer Event-Datumsfelder (R37) ---------- */
.date-picker-popover { width: 240px; padding: var(--space-4); }
.date-picker-popover-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-3); }
.date-picker-popover-label { font-size: var(--fs-md); font-weight: var(--fw-semibold); }
.date-picker-popover-dow { display: grid; grid-template-columns: repeat(7, 1fr); text-align: center; margin-bottom: var(--space-1); }
.date-picker-popover-dow span { font-size: var(--fs-2xs); color: var(--text-dim); }
.date-picker-popover-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--space-1); }
.date-picker-popover-day {
  aspect-ratio: 1; display: flex; align-items: center; justify-content: center;
  border: none; border-radius: var(--radius-sm); background: none; color: var(--text);
  cursor: pointer; font-size: var(--fs-sm); font-family: inherit;
}
.date-picker-popover-day:hover { background: var(--panel-hover); }
.date-picker-popover-day.today { border: 1px solid var(--accent); }
.date-picker-popover-day.selected { background: var(--accent); color: var(--text-on-color); }

/* ---------- GIF-Picker (Giphy) ---------- */
.gif-results { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); max-height: 226px; overflow-y: auto; }
.gif-option { width: 100%; height: 80px; object-fit: cover; border-radius: var(--radius-sm); cursor: pointer; background: var(--panel-2); }
.gif-option:hover { outline: 2px solid var(--accent); }

/* ---------- Lightbox ---------- */
.lightbox-overlay { background: var(--scrim-strong); z-index: var(--z-overlay); }
.lightbox-content { position: relative; max-width: 90vw; max-height: 90vh; display: flex; align-items: center; justify-content: center; }
.lightbox-content img, .lightbox-content video { max-width: 90vw; max-height: 90vh; border-radius: var(--radius-md); object-fit: contain; }
.lightbox-content iframe { width: min(90vw, 960px); height: min(85vh, 780px); border-radius: var(--radius-md); border: none; }
/* max-height allein wuerde bei knapper Fensterhoehe die feste width ueberstimmen und die 16:9-
   Ratio brechen (Breite bliebe stehen, nur die Hoehe wird gekappt) -- width nimmt deshalb das Min.
   aus vw-, px- und einer aus 85vh zurueckgerechneten vh-Grenze (85vh * 16/9), so bleibt die Ratio
   in jeder Fenstergroesse exakt 16:9 statt nur bei ausreichend hohem Viewport. */
.lightbox-content iframe.lightbox-iframe-video { width: min(90vw, 1280px, 151.1vh); height: auto; aspect-ratio: 16 / 9; }
.lightbox-close {
  position: absolute; top: 16px; right: 16px; font-size: var(--fs-3xl);
  background: var(--on-image-bg); color: var(--text-on-color); border-radius: 50%; width: 40px; height: 40px;
}
/* Download-Button links neben dem Schliessen-Button (gleiche runde Optik, siehe .lightbox-close). */
.lightbox-download {
  position: absolute; top: 16px; right: 64px; font-size: var(--fs-xl);
  background: var(--on-image-bg); color: var(--text-on-color); border-radius: 50%; width: 40px; height: 40px;
}
/* R52: Nutzer-Feedback -- Pfeile waren bisher relativ zum kompletten Viewport positioniert
   (left/right:16px vom Bildschirmrand), nicht relativ zum tatsaechlich angezeigten Bild. Bei
   schmaleren Bildern (z.B. Hochformat-Poster) klafften dadurch grosse, "unzentrierte" Luecken
   zwischen Pfeil und Bild. Jetzt Kind von .lightbox-content (position:relative) -- die Pfeile
   hugieren immer den tatsaechlichen Bildrand. Ausserdem deutlich subtiler/transparenter statt
   der vorherigen soliden schwarzen Kreise, wird erst bei Hover praesenter.
   position:absolute nimmt sie aus dem Flex-Fluss von .lightbox-content raus, verschiebt also
   nicht das Bild selbst. */
.lightbox-prev, .lightbox-next {
  position: absolute; top: 50%; transform: translateY(-50%);
  background: var(--on-image-bg); color: rgba(255,255,255,0.7); width: 44px; height: 44px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: var(--fs-3xl); cursor: pointer; border: none;
  transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.lightbox-prev:hover, .lightbox-next:hover { background: var(--on-image-bg-hover); color: var(--text-on-color); }
.lightbox-prev { left: 12px; }
.lightbox-next { right: 12px; }

/* ---------- Foto-Streifen (Thumbnail-Switcher) ---------- */
/* ---------- Eigener Farbwähler ---------- */
.color-picker-popover { width: 220px; }
.color-swatch-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--space-3); margin-bottom: var(--space-5); }
.color-swatch {
  width: 28px; height: 28px; border-radius: 50%; cursor: pointer; border: 2px solid transparent;
}
.color-swatch:hover { transform: scale(1.1); }
.color-swatch.selected { border-color: var(--text); }
.color-picker-hex-row { display: flex; gap: var(--space-3); align-items: center; }
.color-picker-hex-row input[type="text"] {
  flex: 1; background: var(--panel-2); border: 1px solid var(--border); color: var(--text);
  padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm); font-size: var(--fs-md); font-family: inherit;
}
/* R43: Farbrad -- ergaenzt die Presets/den Hex-Wert um eine frei waehlbare Farbe. */
.color-wheel-wrap { display: flex; justify-content: center; margin-bottom: var(--space-5); }
/* Helligkeitsregler rechts neben dem Farbrad (2026-09-16) -- ohne ihn liess sich nur die mittlere
   Helligkeit einer Farbe waehlen. */
.color-wheel-row { display: flex; align-items: center; gap: var(--space-5); }
.color-picker-hex-row { display: flex; align-items: center; gap: var(--space-3); }
.color-picker-hex-row .color-picker-hex-input { flex: 1; min-width: 0; }
.color-picker-hex-ok { flex-shrink: 0; }
.input-fehler { border-color: var(--danger) !important; }
.color-lightness {
  appearance: none; -webkit-appearance: none; writing-mode: vertical-lr; direction: rtl;
  width: 18px; height: 150px; border-radius: var(--radius-full); cursor: pointer;
  background: var(--regler-verlauf, linear-gradient(to top, #000, #888, #fff));
  border: 1px solid var(--border);
}
.color-lightness::-webkit-slider-thumb {
  -webkit-appearance: none; width: 22px; height: 10px; border-radius: var(--radius-xs);
  background: var(--panel); border: 2px solid var(--text); cursor: grab;
}
.color-lightness::-moz-range-thumb {
  width: 22px; height: 10px; border-radius: var(--radius-xs);
  background: var(--panel); border: 2px solid var(--text);
}
.color-wheel-canvas { border-radius: 50%; cursor: crosshair; touch-action: none; }
.color-dot-btn {
  width: 28px; height: 28px; border-radius: 50%; border: 2px solid var(--border); cursor: pointer; padding: 0;
}

/* ---------- Emoji-Picker ---------- */
.emoji-picker-popover { width: 280px; }
/* Bugfix (Nutzer-Feedback: "wenn ich auf reaktionen in chats klicke sind die emojis rechts
   abgeschnitten") -- "repeat(8, 1fr)" allein sizet Grid-Spuren mit minmax(auto,1fr): die Spalten
   duerfen nicht unter ihre max-content-Breite schrumpfen, wodurch das Grid bei 8 Emoji-Buttons
   breiter wird als der 280px-Popover und die ueberschuessigen rechten Spalten vom overflow-x:hidden
   stillschweigend abgeschnitten wurden. minmax(0, 1fr) erzwingt echtes Schrumpfen auf die
   verfuegbare Breite. */
.emoji-results { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: var(--space-1); max-height: 260px; overflow-y: auto; overflow-x: hidden; }
.emoji-option {
  background: none; border: none; font-size: var(--fs-2xl); padding: var(--space-2); cursor: pointer; border-radius: var(--radius-sm);
}
.emoji-option:hover { background: var(--panel-hover); }

/* ---------- Kombinierter Emoji/GIF-Picker (WhatsApp-Stil, Emoji-Tab zuerst) ---------- */
.emoji-gif-picker-popover { width: 300px; }
.emoji-gif-tabs { display: flex; gap: var(--space-2); margin-bottom: var(--space-4); border-bottom: 1px solid var(--border); }
.emoji-gif-tab {
  background: none; border: none; color: var(--text-dim); padding: var(--space-3) var(--space-5); cursor: pointer;
  font-size: var(--fs-md); font-weight: var(--fw-semibold); border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.emoji-gif-tab:hover { color: var(--text); }
.emoji-gif-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.emoji-gif-tab-content .icon-search { margin-bottom: var(--space-3); }
/* Feste Hoehe, unabhaengig vom aktiven Tab (Emoji-Grid vs. GIF-Suchfeld+Grid) -- sonst
   "springt" der Popover beim Tab-Wechsel, da beide Inhalte unterschiedlich hoch waeren. */
.emoji-gif-tab-content { height: 260px; overflow: hidden; }

/* ---------- Wiki-Verlinkung (subtiler Picker in Games/Medien/Event-Vorschau) ---------- */
.wikilink-widget { display: inline-flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
/* Nutzer-Feedback: "blende die notiz verküpfen auf mobile auch aus. ich habe den reiter notizen
   ja auch nicht" -- Notizen (Wiki) ist auf Mobile ueberhaupt nicht erreichbar (fehlt bewusst in
   #mobile-tab-bar), daher ist die Verlinkungs-Chip-Reihe (Compendium/Medien/Event-Vorschau/...)
   dort nur totes UI ohne Funktion. Eine einzige Regel deckt alle Einsatzorte gleichzeitig ab, da
   sie sich alle dieselbe .wikilink-widget-Klasse teilen (siehe renderWikiLinkWidget()). */
@media (max-width: 720px) {
  .wikilink-widget { display: none; }
}
.wikilink-chip { display: inline-flex; align-items: center; gap: var(--space-1); }
.wikilink-btn {
  color: var(--text-dim); font-size: var(--fs-sm); display: inline-flex; align-items: center; gap: var(--space-2);
  width: auto; padding: var(--space-2) var(--space-4); border-radius: var(--radius-xl); background: transparent; border: 1px dashed var(--border);
}
.wikilink-btn:hover { color: var(--text); border-color: var(--text-dim); }
.wikilink-btn-linked { border-style: solid; background: var(--knopf); }
.wikilink-btn-label { max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wikilink-remove-btn { width: 20px; height: 20px; font-size: var(--fs-2xs); color: var(--text-dim); }
.wikilink-remove-btn:hover { color: var(--danger); }
.wikilink-panel {
  position: fixed; z-index: var(--z-menu); width: 240px; box-shadow: var(--shadow-lg);
  animation: fadeInUp var(--duration-fast) ease both;
}
.wikilink-results { max-height: 240px; overflow-y: auto; }
.wikilink-existing-list { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
.wikilink-existing-label { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim); }
.wikilink-existing-chip {
  display: flex; align-items: center; gap: var(--space-3); background: var(--panel-2); border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-4); font-size: var(--fs-sm);
}
.wikilink-existing-chip .wikilink-existing-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wikilink-existing-remove { width: 20px; height: 20px; font-size: var(--fs-2xs); color: var(--text-dim); flex-shrink: 0; }
.wikilink-existing-remove:hover { color: var(--danger); }
.wikilink-type-badge {
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim);
  background: var(--panel-2); border-radius: var(--radius-md); padding: 1px var(--space-3); margin-left: var(--space-2);
}
/* R60 Nutzer-Feedback: sitzt jetzt in der rechten Sidebar unter "Verknuepfungen" (der Add-Button
   dazu steckt in .chatroom-section-heading-row daneben) statt oben in der Titelleiste -- dort ist
   die Spalte schmal genug, dass die Chips umbrechen sollen statt horizontal zu scrollen. */
.wiki-page-links { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.wiki-page-links .wikilink-btn-label { max-width: 90px; }

/* ---------- Boards ---------- */
/* Achtung: .boards-layout/.boards-sidebar/.boards-game-list sind KEIN Compendium-Exklusiv-Muster
   -- der Rooms-Tab (#tab-chatrooms) verwendet exakt dieselben Klassen fuer sein eigenes
   Raum-Listen-Layout (siehe index.html). Breiten-/Kachel-Aenderungen fuer Compendium muessen daher
   ueber #tab-boards skopiert werden statt die Basisregeln hier global zu aendern. */
.boards-layout { display: grid; grid-template-columns: 300px 1fr; gap: var(--space-8); height: var(--fixed-content-height); }
/* R62: #tab-boards nutzt .boards-layout/.boards-sidebar/.boards-main seit dem 3-Spalten-Umbau
   nicht mehr (siehe .sammlung-columns weiter unten) -- der vormalige #tab-boards-Breiten-Override
   ist deshalb entfallen, die Basisregel bleibt fuer Rooms unveraendert bestehen. */
/* R60 Nutzer-Feedback: Chatrooms-Tab soll sich an die tatsaechliche Browserhoehe anpassen statt
   an die feste --fixed-content-height (1010px, gedacht fuer die durchschnittliche Compendium-
   Fensterhoehe) -- auf kleinen Fenstern ragte der Tab unnoetig ueber den sichtbaren Bereich
   hinaus, auf grossen blieb Platz ungenutzt. Offset = sticky Topbar + Status-Ticker + main-
   Padding-top davor (193px, live vermessen -- R61: der frühere Seiten-Untertitel, der hier mit
   reinzählte, ist inzwischen ueberall entfernt) + das normale main-Padding-bottom (60px) danach --
   ergibt eine Seite ohne eigenes Scrollen, der Chat-Bereich selbst scrollt weiterhin intern
   (#chatroom-messages). */
#tab-chatrooms .boards-layout { height: var(--inhalt-hoehe); min-height: 420px; }
/* Zonen-Hintergrund + Trennlinie, analog zum Dashboard-Agenda/Widgets-Bereich. */
.boards-sidebar {
  display: flex; flex-direction: column; gap: var(--space-5); overflow-y: auto;
  background: var(--panel);
  border-radius: var(--radius); padding: var(--space-7);
}
/* +/Filter stehen jetzt neben der Ueberschrift statt in einer eigenen Zeile unter der
   Suchleiste -- .dashboard-widgets-heading ist normalerweise nur ein Block (margin-bottom),
   dieser Modifier macht daraus eine rechtsbuendige Kopfzeile analog zu anderen
   Widget-Ueberschriften mit Aktions-Button. */
.boards-sidebar-heading-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-5); }
.boards-sidebar-controls { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
/* padding-right statt Beschnitt: die Zeilen ruecken beim Ueberfahren 2px nach rechts (siehe
   .chatroom-row:hover). Ohne Platz dafuer ragen sie aus der Liste -- der Browser blendet dann eine
   Querlaufleiste ein und macht den Inhalt schmaler (2026-09-16), mit overflow-x: clip wurde
   stattdessen die rechte Kante der Karte abgeschnitten ("die hover animation schneidet das objekt
   rechts ab"). Die drei Pixel Platz loesen beides. */
.boards-game-list { display: flex; flex-direction: column; gap: var(--space-3); padding-right: 3px; }
/* R62: die drei Compendium-Listen (nicht #chatroom-list, das teilt sich nur die Basis-Klasse
   .boards-game-list oben) sind jetzt Kachel-Raster in eigenen Spalten -- alle drei einheitlich
   3 pro Zeile (Nutzer-Feedback: "mache die lan party auch 3 einträge nebeneinander das alles
   schön gleich ist"), per zusaetzlicher Klasse statt ID-Selektor ausgeloest, damit sich
   Spaltenbreite responsive anpassen laesst. */
.sammlung-tile-grid-3 { display: grid; gap: var(--space-6); align-content: start; grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* R62: neues 3-Spalten-Grundgeruest (Spiele | Filme&Serien | LAN-Party nebeneinander) --
   ersetzt das vorherige Reiter+Sidebar+Detail-Panel-Layout. Jede Spalte traegt nur einen eigenen
   Hintergrund (kein Rahmen, Nutzer-Feedback), globale Suche zentriert oben durchsucht alle drei
   gleichzeitig (siehe #sammlung-search-input-Listener in app.js). */
/* ---------- Seitenkopf ----------
   Titel links, Suche und Aktionen rechts -- fuer Tools, Sammlung und Medien. Bis 2026-09 stand der
   Titel hier zentriert ueber einem zentrierten Suchfeld, Medien hatte gar keinen Titel und Suche
   plus Knopf schwebten mittig in der linken Spalte. */
/* Gleiche Kopfzeile auf allen Seiten (2026-09-14): Dashboard, Tools, Sammlung und Medien
   beginnen dadurch auf derselben Hoehe, statt je nach Inhalt dieser Zeile 30-50px auseinander
   zu liegen. */
.sammlung-toolbar, .dashboard-toolbar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-6);
  /* Zeile plus Abstand ergeben zusammen genau einen Rasterschritt -- dadurch beginnt der Inhalt
     darunter wieder auf einer Rasterlinie, so wie auf den Seiten ganz ohne diese Zeile. */
  min-height: var(--control-h); margin-bottom: calc(var(--gitter-schritt) - var(--control-h));
}
/* Der Name der Seite steht seit 2026-09-13 gross in der Kopfzeile ("Tools //") -- die Ueberschrift
   auf der Seite selbst ist damit eine Wiederholung und traegt jetzt denselben zurueckhaltenden
   Stil wie die Widget-Ueberschriften (2026-09-14: "sollten ... kleinere ueberschriften haben"). */
.sammlung-heading {
  margin: 0; font-size: var(--fs-2xs); font-weight: var(--fw-semibold); line-height: 1.2;
  color: var(--text-dim); letter-spacing: 0.04em; text-transform: uppercase;
}
.sammlung-search-input { width: 100%; max-width: 320px; margin-bottom: 0; }
.sammlung-columns { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-8); align-items: start; }
.sammlung-column { background: var(--panel); border-radius: var(--radius); padding: var(--space-7); min-width: 0; }
.sammlung-column-heading-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-5); margin-bottom: var(--space-6); }
.sammlung-column-heading-row h3 { margin: 0; }
@media (max-width: 1300px) { .sammlung-tile-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* Unter 880px ist die Sammlung einspaltig (siehe .sammlung-columns) -- die zwei Kacheln der Regel
   darueber (gedacht fuer die schmalen Spalten des dreispaltigen Layouts) fuellten dann die ganze
   Breite und wurden riesig (bei 700px je 318x477px). Drei nebeneinander; unter 560px wieder zwei. */
@media (max-width: 880px) { .sammlung-tile-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 880px) { .sammlung-columns { grid-template-columns: 1fr; } }
@media (max-width: 560px) { .sammlung-tile-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Nutzer-Feedback: "die link liste ist ewig lang im vergleich zu den anderen sachen, mache alle
   gleich lang" -- die Links-Spalte im Tools-Tab wuchs mit der Anzahl der Links unbegrenzt weiter,
   waehrend Wunschliste/Platzhalter kurz blieben (align-items:start liess jede Spalte auf ihre
   eigene Inhaltshoehe wachsen). Gleiche feste, bildschirmangepasste Hoehe wie ueberall sonst in
   der App (--fixed-content-height, siehe .dashboard-widgets-area/.year-calendar-area) fuer alle
   drei Tools-Spalten, mit eigenem Scroll pro Spalte statt einer beliebig langen Seite. */
#tab-links .sammlung-column { height: calc(100vh - var(--sticky-header-height, 78px) - 80px); overflow-y: auto; }
/* Nutzer-Feedback: "lasse mich bei tools innerhalb jeder 'spalte' scrollen, also nicht die spalte
   links und meist besessene gleichzeitig sondern einzeln" -- eine geteilte Spalte (zwei gestapelte
   Panels, siehe .tools-column-split) darf nicht mehr selbst scrollen, sonst wandern beim Scrollen
   BEIDE Panels gemeinsam. Stattdessen bekommt jedes Panel seinen EIGENEN Scrollbereich (Ueberschrift
   bleibt dabei fix stehen, nur der Inhalt darunter scrollt) -- .tools-placeholder-panel selbst
   bleibt unveraendert flex:1 (teilt die feste Spaltenhoehe gleichmaessig auf beide Panels auf). */
#tab-links .sammlung-column.tools-column-split { overflow-y: hidden; }
#tab-links .tools-placeholder-panel > *:not(.sammlung-column-heading-row) { flex: 1; min-height: 0; overflow-y: auto; }
@media (max-width: 880px) {
  #tab-links .sammlung-column { height: auto; overflow-y: visible; }
  #tab-links .sammlung-column.tools-column-split { overflow-y: visible; }
  #tab-links .tools-placeholder-panel > *:not(.sammlung-column-heading-row) { overflow-y: visible; }
}

/* Tools-Tab (vormals "Links"): gleiches .sammlung-columns-Geruest wie die Sammlung, siehe
   #tab-links in index.html. Rechte Spalte ist zusaetzlich vertikal in zwei gleich grosse
   Platzhalter-Panels geteilt (Passwort-Safe + ein weiteres, noch unbenanntes Tool). */
/* Nutzer-Feedback: "die links von den meistgespielten und den passwort safe vom game server
   haben jetzt einen durchgehenden background, das soll nicht so sein" -- eine Saeule mit zwei
   gestapelten Panels darf nicht den Hintergrund des Elternblocks (.sammlung-column) erben, sonst
   verschmelzen beide Panels optisch zu einer einzigen Flaeche. Der Elternblock bleibt daher hier
   transparent, jedes Panel traegt seinen EIGENEN einzelnen Hintergrund (kein Nesting, siehe
   .tools-placeholder-panel unten) -- so bleibt es bei "nur ein background pro Panel", nur eben
   sichtbar getrennt statt durchgehend. */
.tools-column-split { display: flex; flex-direction: column; gap: var(--space-8); background: none; padding: 0; }
.tools-placeholder-panel { flex: 1; display: flex; flex-direction: column; background: var(--zone-bg); border-radius: var(--radius); padding: var(--space-7); min-height: 120px; }
.tools-placeholder-panel .settings-hint { margin: auto 0 0; opacity: 0.6; }
/* Nutzer-Feedback: "mache das unter password safe zu einer liste meist besessener spiele wie die
   wunschliste ... mit icons der user die das game haben und mit eigenem hover" -- gleiche
   .wishlist-item-Kachel wie die Wunschliste-Spalte, nur mit einer Avatar-Reihe statt der
   Herz-Anzahl. Der Hover kommt "for free" mit renderAvatarHtml() (data-avatar-*-Attribute werden
   von der bereits bestehenden, global delegierten showAvatarHoverCard()-Logik aufgefangen). */
/* Nutzer-Feedback: "sollte eine zahl erscheinen und beim hover sollten die personen angezeigt
   werden" -- Avatar-Stapel durch eine reine Besitzer-Zahl ersetzt, Namen/Avatare leben jetzt nur
   noch im Hover-Popover (showMostOwnedHoverPanel() in app.js). */
.most-owned-count {
  display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; cursor: default;
  font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--text-dim);
  background: var(--tint); border-radius: var(--radius-xl); padding: var(--space-2) var(--space-5);
}
.most-owned-count:hover { color: var(--text); background: var(--tint-hover); }

/* Pelican-Panel-Serverliste (Tools-Tab, unter Passwort-Safe) -- siehe loadGameServers()/
   renderGameServers() in app.js. */
.game-servers-grid { display: flex; flex-direction: column; gap: var(--space-4); }
.game-server-item { background: var(--tint-subtle); border-radius: var(--radius); padding: var(--space-4) var(--space-5); }
.game-server-item-top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.game-server-name { font-size: var(--fs-md); font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.game-server-state { font-size: var(--fs-2xs); font-weight: var(--fw-semibold); padding: var(--space-1) var(--space-4); border-radius: var(--radius-xl); flex-shrink: 0; }
.game-server-state-online { background: color-mix(in srgb, var(--success) 18%, transparent); color: var(--success-text); }
.game-server-state-starting, .game-server-state-stopping { background: color-mix(in srgb, var(--highlight) 18%, transparent); color: var(--highlight); }
.game-server-state-offline { background: var(--tint-hover); color: var(--text-dim); }
.game-server-stats { display: flex; gap: var(--space-6); margin-top: var(--space-3); }
.game-server-stat { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-xs); color: var(--text-dim); }
.game-server-bar { margin-top: var(--space-3); height: 4px; border-radius: var(--radius-xs); background: var(--tint-hover); overflow: hidden; }
.game-server-bar-fill { height: 100%; background: var(--accent); border-radius: var(--radius-xs); transition: width var(--duration-slow) ease; }

/* Passwort-Safe (Tools-Tab) -- Nutzer-Feedback: "die liste sollte recht schmal bleiben ... username
   passwort. token" -- Zeilen zeigen Titel + optionale Server-Verknuepfung, Details
   (Benutzername/Passwort/Token) leben im Bearbeiten-Popup statt in der Liste selbst. */
/* Nutzer-Feedback: "mache das entsperren des safes etwas schöner mit einem dunklen eingabefeld
   und das ganze etwas mittiger oben. mit entsperr button innerhalb des eingabefelds wie beim chat
   eingeben" -- zentriert statt linksbuendig, mit etwas Abstand nach oben; das Eingabefeld nutzt
   bewusst dieselben .chat-input-wrap/.chat-form-input-Klassen wie die Chat-Eingabezeile (siehe
   #chatroom-comment-text), damit der Entsperr-Button optisch identisch im Feld sitzt. */
.password-safe-locked { display: flex; flex-direction: column; gap: var(--space-6); align-items: center; padding-top: var(--space-10); }
.password-safe-unlock-input-wrap { width: 100%; max-width: 260px; }
.password-safe-unlock-input-wrap input[type="password"] {
  width: 100%; background: var(--panel-2); border: 1px solid var(--border); color: var(--text);
  padding: var(--space-5) 34px var(--space-5) var(--space-6); border-radius: var(--radius-md); font-size: var(--fs-md); font-family: inherit;
}
.password-safe-passkey-btn { margin: 0 auto; }
/* Nutzer-Feedback: "die beschreibung wenn kein passwort gesetzt ist schöner haben" -- Karten-Hinweis
   mit Icon statt reinem gedimmten Fliesstext. */
.password-safe-empty-hint {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-4); text-align: center;
  padding: var(--space-12) var(--space-8); color: var(--text-dim); background: var(--tint-subtle); border-radius: var(--radius);
  width: 100%;
}
.password-safe-empty-hint i { font-size: var(--fs-4xl); opacity: 0.55; }
.password-safe-empty-hint p { margin: 0; font-size: var(--fs-sm); line-height: 1.45; max-width: 230px; }
.password-safe-list { display: flex; flex-direction: column; gap: var(--space-4); }
/* Wie jede andere Inhaltskachel, nicht als blosse Toenung (2026-09-16: "ein passworteintrag in
   tools hat die farbe irgendwie nicht uebernommen") -- eine Toenung zeigt die Flaeche darunter und
   folgt damit keiner gewaehlten Kachelfarbe. */
.password-safe-item { background: var(--inhalt-kachel); border-radius: var(--radius); padding: var(--space-4) var(--space-5); }
.password-safe-item-top { display: flex; align-items: center; gap: var(--space-4); }
.password-safe-item-title { font-size: var(--fs-md); font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0; }
.password-safe-item-server { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-2xs); color: var(--text-dim); flex-shrink: 0; }
.password-safe-item-actions { display: flex; gap: var(--space-1); flex-shrink: 0; }
.password-safe-item-actions .icon-btn { width: 24px; height: 24px; font-size: var(--fs-md); }
/* Nutzer-Feedback: "kannst du username passwort und token nebeneinander schreiben? und beim
   drauf klicken wird kopiert dann spart man sich die buttons ... wir kürzen auch benutzername mit
   U: und passwort mit P: ab ... die schrift darf ruhig etwas kleiner sein" -- ein Chip pro Feld,
   nebeneinander per flex-wrap, ganzer Chip klickbar statt separatem Kopier-Button. */
.password-safe-item-cred-line { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-3); }
/* Nachtrag-Feedback: "die überschriften U: P: nicht hinterlegt mit einem background, stattdessen
   nur die passwörter hinterlegen. und dann klickbar wie zuvor wo man den 'button' also den
   background mit dem passwort darin anklicken konnte und nicht mit so einem unterstreichen" --
   Label bleibt reiner Text ohne eigenen Hintergrund, nur der Wert ist eine anklickbare Pille
   (Hintergrund + Hover-Aufhellung wie ein Button, kein Unterstreichen mehr). */
.password-safe-cred { display: inline-flex; align-items: baseline; gap: var(--space-2); font-size: var(--fs-2xs); font-family: "SF Mono", "Consolas", monospace; max-width: 100%; }
.password-safe-cred-label { color: var(--text-dim); flex-shrink: 0; }
.password-safe-cred-value {
  color: var(--text-dim); cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  background: var(--tint-subtle); border-radius: var(--radius-xs); padding: var(--space-1) var(--space-3);
}
.password-safe-cred-value:hover { color: var(--text); background: var(--tint-hover); }
.password-safe-reveal-row { display: flex; gap: var(--space-3); align-items: center; }
.password-safe-reveal-row input { flex: 1; min-width: 0; }

/* Wunschliste: kompakte Kachelliste, absichtlich schmaler/einfacher als die grossen
   Sammlung-Kacheln (kein Klick-Popup, nur Titel+Herzanzahl -- die eigentliche Verwaltung
   passiert weiterhin drueben in der Sammlung). */
.wishlist-grid { display: flex; flex-direction: column; gap: var(--space-4); }
/* Eigene Flaeche statt einer 4-%-Toenung (2026-09-13 gemeldet: "am meisten zum spielen gewuenscht
   hat keinen background die kacheln") -- seit die Widgets selbst keine Flaeche mehr tragen, war die
   Toenung gegen den Seitengrund praktisch unsichtbar. */
.wishlist-item { display: flex; align-items: center; gap: var(--space-5); background: var(--inhalt-kachel); border-radius: var(--radius); padding: var(--space-4) var(--space-5); cursor: pointer; }
.wishlist-item:hover { background: var(--panel-hover); }
.wishlist-item-cover, .wishlist-item-cover-empty { width: 36px; height: 48px; border-radius: var(--radius-xs); object-fit: cover; flex-shrink: 0; }
.wishlist-item-cover-empty { display: flex; align-items: center; justify-content: center; background: var(--zone-bg); font-size: var(--fs-xl); opacity: 0.6; }
.wishlist-item-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
.wishlist-item-hearts { display: flex; align-items: center; gap: var(--space-2); color: var(--heart); font-weight: var(--fw-semibold); flex-shrink: 0; }
/* Nutzer-Feedback: "in der wuschliste neben jedem eintrag ein clear button ... falls man das spiel
   schon gespielt hat kann man alle likes clearen (nur mit dem entsprechenden recht dazu)" -- nur
   sichtbar mit can_delete_all (siehe renderWishlist() in app.js), da es ALLE Herzen entfernt, nicht
   nur das eigene. */
.wishlist-item-clear-btn {
  flex-shrink: 0; background: none; border: none; color: var(--text-dim); cursor: pointer;
  padding: var(--space-2); border-radius: var(--radius-sm); display: flex; align-items: center; opacity: 0.6;
}
.wishlist-item-clear-btn:hover { opacity: 1; color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent); }
/* Nutzer-Feedback: "einen kleinen link zur sammlung bei der wunschliste als mini button oben rechts" --
   gleiche Zeile wie die Spalten-Ueberschrift, ganz rechts. */
.wishlist-sammlung-link {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-xs); color: var(--text-dim); text-decoration: none; padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-full); background: var(--tint); cursor: pointer; border: none;
}
.wishlist-sammlung-link:hover { background: var(--accent); color: var(--text-on-color); }
/* Nutzer-Feedback: "mache den bearbeiten button einfach als kleinen stift rechts neben 'zur
   sammlung'" -- beide Buttons teilen sich jetzt einen rechtsbuendigen Wrapper statt eigener Zeile. */
.wishlist-heading-actions { margin-left: auto; display: flex; align-items: center; gap: var(--space-3); }
#wishlist-edit-toggle-btn.active { background: var(--accent); color: var(--text-on-color); border-color: var(--accent); }

/* Herz-Button auf Sammlung-Spiele-Kacheln (nur "games", siehe renderCompendiumCategoryList()) --
   sitzt als Kind von .compendium-tile-badges-left oben links auf dem Cover (siehe dort), daher
   selbst nicht mehr absolut positioniert. */
.compendium-tile-heart-btn {
  display: flex; align-items: center; gap: var(--space-2); pointer-events: auto;
  background: var(--on-image-bg); backdrop-filter: blur(4px); color: var(--text-on-color);
  border: none; border-radius: var(--radius-full); padding: var(--space-2) var(--space-4); font-size: var(--fs-sm); cursor: pointer;
}
.compendium-tile-heart-btn:hover { background: var(--on-image-bg-hover); }
.compendium-tile-heart-btn.hearted { color: var(--heart); }
.compendium-tile-heart-count { font-weight: var(--fw-semibold); }
/* Nutzer-Feedback: .boards-game-row/-header/-title/-content/-cover-wrap/-cover-empty/-meta/
   -players (die fruehere einspaltige Karten-Zeile) sind seit der Umstellung auf hochkante
   Cover-Kacheln (.compendium-tile-*, siehe unten) nirgends mehr referenziert und deshalb entfernt.
   Die Meta-Zeilen-Klassen (-activity/-date/-next-event) bleiben dagegen bestehen -- die nutzt die
   neue Kachel unveraendert weiter (nur innerhalb von .compendium-tile-meta statt .boards-game-row-meta). */
.boards-game-row-activity, .boards-game-row-date, .boards-game-row-next-event { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-2xs); color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.boards-game-row-activity .mdi, .boards-game-row-date .mdi, .boards-game-row-next-event .mdi { font-size: var(--fs-md); }
/* R62 Nutzer-Feedback: "schreibe in den kacheln den 'nächstes event xy' in gelb" -- immer gelb
   statt der vorherigen bedingten Faerbung nach Dringlichkeit (agenda-header-date-soon/-today),
   und zeigt jetzt zusaetzlich den Event-Titel statt nur des Datums (siehe renderCompendiumCategoryList()). */
/* R62 Nutzer-Feedback: "mache den gelben text oben links in das cover und deutlich kleiner" --
   kleines Badge oben links auf dem Cover (Gegenstueck zu .compendium-tile-badges oben rechts),
   statt einer eigenen Zeile in der unteren Info-Leiste. */
/* Nutzer-Feedback: "den like button bitte zu den anderen anzeigen oben links ... tun" -- Herz und
   "Naechstes Event"-Badge stehen jetzt zusammen in einer Spalte oben links auf dem Cover (Gegenstueck
   zur Spalte .compendium-tile-badges oben rechts), statt getrennt oben links/unten links. Container
   traegt die Positionierung, die Kinder selbst sind nicht mehr einzeln absolut positioniert. */
.compendium-tile-badges-left { position: absolute; top: 8px; left: 8px; z-index: 2; max-width: 62%; display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); }
.compendium-tile-next-event {
  display: inline-flex; align-items: center; gap: var(--space-2); max-width: 100%; background: var(--on-image-bg); color: var(--highlight);
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold); padding: var(--space-1) var(--space-4); border-radius: var(--radius-xl); pointer-events: none;
}
.compendium-tile-next-event .mdi { font-size: var(--fs-xs); flex-shrink: 0; }
/* Nutzer-Feedback: "events die in der kachel ... angezeigt werden sollten wenn sie zu lang sind mit
   ... hinten angezeigt werden" -- text-overflow:ellipsis auf dem Flex-Container selbst (vorherige
   Regel) griff nicht, weil Flex-Kinder (Icon+Text) per Default nicht unter ihre Inhaltsbreite
   schrumpfen -- eigener Span mit min-width:0 (erlaubt das Schrumpfen) traegt jetzt die Ellipse. */
.compendium-tile-next-event-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* LAN-Party-"Findet statt"-Zeile (dateLine bei subtabKey==="lanparty") -- gleiche Farbcodierung
   wie .boards-game-row-next-event oben, da beide dieselbe dueInfoFor()-Klasse tragen koennen. */
.boards-game-row-date.agenda-header-date-soon { color: var(--highlight); font-weight: var(--fw-semibold); }
.boards-game-row-date.agenda-header-date-today { color: var(--danger); font-weight: var(--fw-bold); }
.boards-sort-controls { display: flex; gap: var(--space-3); margin: var(--space-4) 0; }
/* Nutzer-Feedback: natuerliche Breite statt flex:1-Streckung, damit die Buttons genauso aussehen
   wie die Medien-Scope-Filter (.filter-btn im .media-scope-filter, keine Ueberschreibung dort). */
.boards-sort-controls .filter-btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); }
.boards-sort-controls .mdi { font-size: var(--fs-lg); }
/* Nutzer-Feedback: Compendium-Kacheln jetzt hochkant wie ein Boxart-/DVD-Cover, im gleichen
   Kachel-Stil wie der Medien-Tab (.media-game-tile-*, siehe buildMediaTile() in app.js) --
   Wrapper + Kachel als eigene Ebenen, exakt dasselbe Muster, nur mit einer festen 2:3-statt
   16:9-Aspect-Ratio (echte Steam-/TMDB-/IGDB-Cover sind selbst 2:3) und OHNE geblurrten
   Hintergrund-Layer -- das Cover bleibt hier scharf, nur hinter dem Info-Panel unten liegt ein
   eigener backdrop-filter:blur-Streifen (siehe .compendium-tile-info), damit der Text lesbar
   bleibt ohne das ganze Cover zu verunschaerfen. */
.compendium-tile-wrap { position: relative; cursor: pointer; border-radius: 10px; }
.compendium-tile-wrap:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.compendium-tile-wrap.active .tile-card.compendium-tile { outline: 2px solid var(--accent); outline-offset: 0; }
/* Das Leuchten hinter den Kacheln (eine unscharfe Kopie des Covers) ist 2026-09-14 auf
   Nutzerwunsch gefallen ("entferne das leuchten in der sammlung") -- der Wrapper bleibt, er traegt
   Fokusrahmen und Aktiv-Markierung. */
.tile-card.compendium-tile {
  position: relative; aspect-ratio: 2 / 3; overflow: hidden; border: none;
  display: block; background: var(--inhalt-kachel);
}
.compendium-tile-cover { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.compendium-tile-cover-empty { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: var(--fs-display); color: var(--text-dim); background: var(--panel-2); }
/* Pin/Mitspieler-Anzahl/Teilnahme-Haekchen -- gleiche obere-rechte-Ecke wie das Typ-Badge im
   Medien-Tab (.media-game-tile-type-badge), hier aber mehrere kleine Icon-Pillen statt einer. */
.compendium-tile-badges { position: absolute; top: 8px; right: 8px; z-index: 2; display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-2); }
.compendium-tile-badge-icon {
  display: inline-flex; align-items: center; gap: var(--space-2); background: var(--on-image-bg); color: var(--text-on-color);
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold); padding: var(--space-1) var(--space-4); border-radius: var(--radius-xl); pointer-events: none;
}
.compendium-tile-badge-attending { background: var(--accent); }
/* Nutzer-Feedback: "mache die blurry bars in den sammlungen von den covers auch in dieser
   simplen farbe" -- gleiche Kehrtwende wie zuvor bei den Medien-Ordner-Kacheln (siehe
   .media-game-tile-header): das bisherige Frosted-Glass-Blur (@supports backdrop-filter) samt
   Farbverlauf-Fallback fliegt raus, ersetzt durch den schlichten, flachen Grauton (var(--panel-2)),
   voll deckend statt halbtransparent -- keine Blur-Berechnung mehr noetig. */
.compendium-tile-info {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  padding: var(--space-4) var(--space-5) var(--space-5); background: var(--inhalt-kachel);
}
/* Nutzer-Feedback: "titel die nicht in die kachel passen (nicht zweizeilig werden) mit … kürzen"
   -- einzeilig mit Ellipsis statt des vorherigen 2-Zeilen-Clamps. */
.compendium-tile-title {
  color: var(--text-on-color); font-weight: var(--fw-bold); font-size: var(--fs-lg); line-height: 1.25; text-shadow: 0 1px 4px rgba(0,0,0,0.6);
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis; margin-bottom: var(--space-2);
}
.compendium-tile-meta { display: flex; flex-direction: column; gap: var(--space-1); }
.compendium-tile-meta .boards-game-row-activity,
.compendium-tile-meta .boards-game-row-date { color: var(--text); }
/* R62: der vormalige #boards-game-list/#movies-list/#lanparty-list/#tab-boards .boards-layout-
   Breakpoint hier ist mit dem 3-Spalten-Umbau entfallen (siehe .sammlung-columns-Breakpoints). */
.boards-main { display: flex; flex-direction: column; min-width: 0; border: none; border-radius: var(--radius); overflow: hidden; background: var(--panel); }
/* R38 Phase 5 (Movies-Cutoff-Fix, Hauptursache): #movies-content/#lanparty-content bekommen
   ihr "display:flex" nur inline per JS gesetzt (selectCompendiumItem()), OHNE eine eigene
   flex-direction-Regel -- Default ist "row", wodurch Header-Leiste und .boards-detail-row
   nebeneinander statt untereinander liefen (Ursache des vom Nutzer beschriebenen "abgeschnitten/
   durcheinander"). #boards-content hatte diese Regel bereits; jetzt fuer alle drei Compendium-
   Panels vereinheitlicht statt weiterhin nur fuer Games gesetzt. */
/* R50 Nachtrag: #chatroom-content fehlte hier -- ohne "height:100%; min-height:0" hatte der
   Chatroom-Detailbereich keine begrenzte Hoehe, wodurch #chatroom-messages trotz "flex:1" nichts
   zum Hineinflexen hatte und stattdessen mit jeder neuen Nachricht die ganze Seite laenger wurde,
   statt intern zu scrollen (Nutzer-Feedback: "beginnen unten, nicht immer laenger werden"). */
#chatroom-content { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.boards-release-date { color: var(--text-dim); font-size: var(--fs-md); }
/* R42: Datum (samt Release-/Termin-Icon) steht jetzt rechtsbuendig in derselben Zeile wie die
   Ueberschrift statt in einer eigenen Zeile darunter -- spart 1-2 Zeilen Header-Hoehe. Mitspieler/
   Interessiert-Chips und die "Verknuepfte Notizen"-Verlinkung zogen dafuer ins neue Info-Panel um
   (siehe .boards-info-panel), .boards-header-meta (frueherer gemeinsamer Container fuer alle drei)
   entfaellt komplett. */
/* Nutzer-Feedback: Titelleiste deutlich schmaler -- Titel/Datum/Beitreten-Button jetzt EINE Zeile
   statt zwei (Attend-Button stand vorher in einer eigenen .boards-header-attend-row darunter).
   align-items:center statt :baseline, da jetzt auch ein Button (kein reiner Text) in der Zeile
   sitzt. h2 truncated mit Ellipsis statt zu umbrechen, damit die Zeile bei langen Titeln nicht
   trotzdem zweizeilig wird. */
.boards-header-bar-compact { align-items: center; }
.boards-header-title-row { display: flex; align-items: center; gap: var(--space-6); }
.boards-header-title-row h2 { margin: 0; min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.boards-header-title-row .boards-release-date { flex-shrink: 0; display: inline-flex; align-items: center; gap: var(--space-2); }
.boards-players-inline { display: flex; flex-wrap: wrap; gap: var(--space-2); }
/* Nutzer-Feedback: "die rechte spalte wo in besitz steht rechtsbündig machen" -- die 30%-Spalte
   neben der Beschreibung (siehe .boards-desc-col-30) bekommt Ueberschrift+Chips rechtsbuendig,
   damit sie sich sichtbar von der linksbuendigen Beschreibung absetzt statt an deren Rand zu
   kleben. */
.boards-desc-col-30 .attendees-heading { justify-content: flex-end; }
.boards-desc-col-30 .boards-players-inline { justify-content: flex-end; }
.boards-players-inline .tag-chip { font-size: var(--fs-xs); padding: var(--space-1) var(--space-4); cursor: default; }
.boards-player-chip-self { border: none; cursor: pointer; background: var(--accent); color: var(--text-on-color); }
.boards-player-chip-self:hover { filter: brightness(1.15); }
.boards-player-chip-add { background: transparent; border: 1px dashed var(--border); color: var(--text-dim); cursor: pointer; }
.boards-player-chip-add:hover { border-color: var(--accent); color: var(--accent); }
.boards-subtab-panel { padding: var(--space-5) var(--space-7); }
.boards-notes-content { margin-top: var(--space-5); font-size: var(--fs-md); line-height: 1.5; }
.compendium-meta-badges { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-2); max-width: 100%; }
.compendium-meta-badge { background: var(--tint); border: 1px solid var(--border); border-radius: var(--radius-xl); padding: var(--space-2) var(--space-5); font-size: var(--fs-xs); color: var(--text-dim); max-width: 100%; }
.compendium-meta-badge strong { color: var(--text); font-weight: var(--fw-semibold); }
.boards-subtab-panel h4 { margin: var(--space-5) 0 var(--space-3); font-size: var(--fs-sm); color: var(--text-dim); }
.attendees-heading { display: flex; align-items: center; gap: var(--space-3); }
.boards-subtab-panel h4:first-child { margin-top: 0; }
/* R42: Kopfzeile mit "Vergangene anzeigen"-Button neben "Geplante Events" (statt einer
   gemeinsamen "Geplante & vergangene Events"-Liste, siehe renderCompendiumLinkedEvents()). */
.boards-linked-events-heading-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.boards-linked-events-heading-row h4 { margin: var(--space-5) 0 var(--space-3); }
.boards-linked-events-heading-row:first-child h4 { margin-top: 0; }
.boards-linked-events-heading-row .icon-btn { font-size: var(--fs-lg); }
.boards-past-events-popup { max-height: 320px; overflow-y: auto; display: flex; flex-direction: column; gap: var(--space-2); }
/* R64 Nutzer-Feedback: "rechts neben geplante events noch eine spalte vergangene events im selben
   stil machen" -- ersetzt die vorherige Kopfzeile mit Icon-Button (boards-linked-events-heading-row,
   bleibt fuer LAN-Party unveraendert bestehen) durch zwei gleichrangige Spalten mit je eigener
   Ueberschrift, siehe boards-linked-events-section in index.html (nur Spiel/Film/Serie). */
.boards-linked-events-cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-8); }
.boards-linked-events-col h4 { margin: var(--space-5) 0 var(--space-3); }
.boards-linked-events-col:first-child h4 { margin-top: 0; }
/* "bei beiden event ansichten nicht mehr als 5 events anzeigen und dann mit einem 'mehr anzeigen'
   popup fortfahren" -- je Spalte auf 5 Eintraege gedeckelt (renderLinkedEventsCapped() in app.js),
   der Button erscheint nur, wenn tatsaechlich mehr als 5 vorhanden sind. */
.boards-linked-events-more-btn {
  display: block; width: 100%; margin-top: var(--space-3); padding: var(--space-3) var(--space-4); font-size: var(--fs-sm);
  color: var(--text-dim); background: transparent; border: 1px dashed var(--border); border-radius: var(--radius-sm);
  cursor: pointer; transition: border-color var(--duration-fast) ease, color var(--duration-fast) ease;
}
.boards-linked-events-more-btn:hover { border-color: var(--accent); color: var(--accent); }
@media (max-width: 560px) { .boards-linked-events-cols { grid-template-columns: 1fr; } }
/* R39: "Geplante & vergangene Events"-Liste im Compendium-Detail (Reverse-Lookup ueber
   linked_event_id) -- gleiches Zeilen-Muster wie andere kompakte Listen (z.B. date-group-item). */
.boards-linked-events-list { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-3); }
.boards-linked-event-row { display: flex; align-items: center; gap: var(--space-4); padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm); background: var(--inhalt-kachel); border: 1px solid transparent; cursor: pointer; font-size: var(--fs-md); transition: background var(--duration-fast) ease; }
.boards-linked-event-row:hover { background: var(--panel-hover); }
.boards-linked-event-date { color: var(--text-dim); white-space: nowrap; font-size: var(--fs-sm); }
.boards-linked-event-date.past { opacity: 0.6; }
/* Nutzer-Feedback: bevorstehende (<= 5 Tage) bzw. heutige Events umrandet wie ueberall sonst
   gelb/rot (agenda-header-date-soon/-today-Farbwerte, siehe dueInfoFor() in app.js). */
.boards-linked-event-row.due-soon { border-color: var(--highlight); }
.boards-linked-event-row.due-today { border-color: var(--danger); }
.boards-linked-event-title { display: flex; align-items: center; gap: var(--space-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Filmstreifen jetzt UNTER dem grossen Slider-Bild statt daneben (vorher .boards-detail-right) --
   der Medien-Ordner-Button ist in die Kopfzeile umgezogen (siehe .modal-header-actions), daher
   kein Abstand mehr zu einem Button hier noetig. */
.compendium-media-thumbs { display: flex; flex-wrap: wrap; justify-content: flex-start; gap: var(--space-3); margin: 0 0 var(--space-6); }
.compendium-media-thumb { position: relative; width: 52px; height: 52px; border-radius: var(--radius-sm); overflow: hidden; cursor: pointer; background: var(--panel-2); flex-shrink: 0; transition: box-shadow var(--duration-fast) ease; }
/* R54: Nutzer-Feedback -- im Event-Formular (Spiel/Film/Serie) liess sich ein bereits
   hochgeladenes Bild nicht mehr loeschen, anders als bei normalen Events ("Weitere Fotos").
   Nur dort per JS angehaengt (refreshEventFormMediaThumbs()), nicht bei den Compendium-
   Slider-Thumbnails, wo ein Klick auf das Bild stattdessen den Slider umschaltet.
   Nutzer-Feedback: der volle rote Kreis-Badge wirkte zu gross/aufdringlich -- jetzt gleicher
   dezenter, transparenter Icon-Stil wie .chat-message-delete-btn (kein Hintergrund, nur das
   Icon selbst, erst bei Hover rot). */
.compendium-media-thumb-delete {
  position: absolute; top: -4px; right: -4px; width: 16px; height: 16px; border-radius: var(--radius-xs);
  background: transparent; color: var(--text-dim); border: none; cursor: pointer;
  font-size: var(--fs-2xs); line-height: 1; display: flex; align-items: center; justify-content: center;
  z-index: 1; transition: color var(--duration-fast) ease;
}
.compendium-media-thumb-delete:hover { color: var(--danger); }
.compendium-media-thumb img { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; }
/* Hebt hervor, welches Thumbnail gerade im Slider oben gezeigt wird ("wie in einer Diashow") --
   wird von renderCompendiumMediaThumbs()/renderCompendiumMediaSlider() (app.js) synchron gehalten.
   Bewusst ein INSET box-shadow statt outline (vorher outline+outline-offset): outline sitzt AUSSERHALB
   der Box und folgt border-radius browserabhaengig ungenau, was den umrandeten Thumb bei "overflow:
   hidden"-Eltern optisch leicht schief/verschoben wirken liess (Nutzer-Feedback: "Bilder zu weit
   links, zumindest wenn der Rahmen um das Bild ist"). box-shadow inset bleibt innerhalb der Box und
   folgt dem border-radius exakt. */
.compendium-media-thumb.active { box-shadow: inset 0 0 0 2px var(--accent); }
/* R54: Nutzer-Feedback -- Add-Kachel im Event-Formular-Thumbnail-Streifen (siehe
   refreshEventFormMediaThumbs() in app.js), damit "weitere Medien hochladen" ueber die kleinen
   Vorschaubildchen selbst laeuft statt ueber einen separaten Button darueber. */
.compendium-media-thumb-add {
  border: 1px dashed var(--border); background: var(--panel-2); color: var(--text-dim);
  display: flex; align-items: center; justify-content: center; font-size: var(--fs-xl);
  transition: border-color var(--duration-fast) ease, color var(--duration-fast) ease;
}
.compendium-media-thumb-add:hover { border-color: var(--accent); color: var(--accent); }
/* Etwas niedriger als das frühere 16:9 (weniger Scroll-Platzbedarf), siehe Nutzer-Feedback. */
.media-slider.compendium-media-slider { aspect-ratio: 2 / 1; margin: 0 0 var(--space-5); }
/* R51: Nutzer-Feedback -- Slider/Filmstreifen sollen im Compendium-Detail auf die gleiche
   Einrueckung wie der Notizen-Text darunter (.boards-subtab-panel padding: 10px 14px) gebracht
   werden, statt randlos die volle Spaltenbreite auszufuellen. Muss NACH der .media-slider.
   compendium-media-slider-Regel oben stehen, da deren margin-Shorthand sonst (bei gleicher
   Selektor-Spezifitaet) die hier gesetzten margin-left/right-Langformen wieder ueberschreiben wuerde. */
.boards-detail-topleft .compendium-media-slider,
.boards-detail-topleft .compendium-media-thumbs,
.boards-detail-topleft .compendium-media-empty-btn { margin-left: var(--space-7); margin-right: var(--space-7); }
/* R38 Phase 5 hatte hier bewusst object-fit:contain gesetzt, um Hochformat-Poster nicht
   anzuschneiden -- erzeugte aber bei normalen Breitbild-Screenshots unerwuenschte Raender
   (Letterboxing). Nutzer-Feedback: Bild soll den Rahmen immer ausfuellen -- zurueck auf "cover"
   (matcht damit wieder .media-slider-slide img oben, keine Compendium-spezifische Ausnahme mehr). */
.compendium-media-slider .media-slider-slide img { object-fit: cover; }
/* R50: Nutzer-Feedback -- das R42-Innenabstand-"Schwimmen" wirkte inzwischen eher wie ein
   unerwuenschter Rahmen um das Bild; Bild soll wieder die volle Breite/Hoehe des Sliders
   ausfuellen, kein Innenabstand mehr. Ausserdem: der Track rendert seit R50 nur noch die aktuell
   aktive Kachel (kein CSS-transform-basiertes Sliden mehr, siehe renderCompendiumMediaSlider() in
   app.js) -- braucht daher kein Flex-Layout mehr, ein einzelnes Block-Kind genuegt.
   .compendium-media-thumbs bleibt bewusst ohne eigenen Innenabstand, damit der Filmstreifen
   buendig mit dem jetzt randlosen Slider abschliesst. */
.compendium-media-slider .media-slider-track { display: block; }
.compendium-media-slider .media-slider-slide { width: 100%; border-radius: var(--radius-sm); overflow: hidden; }
/* "Weitere öffnen"-Kachel am Ende der Masonry (siehe opts.limit/onMore in
   renderCompendiumMediaThumbs()) -- oeffnet den vollen Medien-Ordner als Popup ueber der Vorschau
   (openEventMediaFolderPopup() in app.js), statt hier endlos weiterzuscrollen. */
.compendium-media-thumb-more {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-2);
  width: 100%; padding: var(--space-10) var(--space-4); margin: 0 0 var(--space-4); break-inside: avoid;
  border: none; border-radius: var(--radius-sm); cursor: pointer;
  background: var(--panel-2); color: var(--text-dim); font-family: inherit; font-size: var(--fs-sm);
  transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.compendium-media-thumb-more:hover { background: var(--panel); color: var(--text); }
.compendium-media-thumb-more .mdi { font-size: var(--fs-2xl); }
.boards-layout-single { grid-template-columns: 300px; }
.boards-chat-heading { margin: 0; padding: var(--space-5) var(--space-7) var(--space-2); font-size: var(--fs-md); color: var(--text-dim); display: flex; align-items: center; gap: var(--space-3); flex-shrink: 0; }

/* R42/R49: zwei unabhaengige Flex-Spalten (frueher ein 2x2-Grid: Slider|Info oben, Notizen|Chat
   unten). Nutzer-Feedback R49: bei einem Eintrag ohne Medien sollten Notizen direkt unter den
   "Medien hinzufuegen"-Button hochrutschen -- im gemeinsamen Grid bestimmte aber die Zeilenhoehe
   (das Maximum aus Slider-Spalte UND Info-Panel-Spalte) auch die Position von Notizen, selbst wenn
   das Info-Panel viel laenger war als der schmale Ersatz-Button. Mit zwei eigenstaendigen Flex-
   Spalten (.boards-detail-left-col: Slider/Button + Notizen, .boards-detail-right-col: Info-Panel +
   Chat) hat jede Spalte ihre eigene interne Hoehen-Aufteilung, Notizen ruecken dadurch immer direkt
   unter den Slider/Button, unabhaengig davon wie lang das Info-Panel rechts ist. */
.boards-detail-row {
  display: flex; flex: 1; min-height: 0; gap: var(--space-7);
  /* R43: Nutzer-Feedback -- Slider/Info-Box klebten direkt am Header/Attend-Badge darueber ohne
     jeglichen Abstand. */
  margin-top: var(--space-5);
}
.boards-detail-left-col { display: flex; flex-direction: column; flex: 2; min-width: 0; gap: var(--space-6); min-height: 0; }
.boards-detail-right-col { display: flex; flex-direction: column; flex: 1; min-width: 0; gap: var(--space-6); min-height: 0; }
/* Raeume und Notizen liessen ihre rechte Spalte bis an die Kante der Ansicht laufen, der
   Medienordner hatte dort Luft -- jetzt ueberall gleich (2026-09-15: "mache die rechte spalte in
   watch together raeumen, chatraeumen und in notizen genauso mit einem abstand rechts wie bei den
   medienordnern ... damit die inhalte ueberall gleich aussehen"). */
@media (min-width: 881px) {
  #chatroom-content > .boards-detail-row, #wiki-page-view .boards-detail-row { padding-right: var(--space-12); }
}
.boards-detail-topleft { min-width: 0; }
/* R62 Nutzer-Feedback: "kompakt im kleinen stil einer event vorschau" -- die drei Compendium-
   Detail-Popups (boards/movies/lanparty-detail-modal) wiederverwenden dieselbe .boards-detail-row-
   Struktur wie zuvor das rechte Seiten-Panel (und wie Rooms/Notizen, siehe deren eigene
   .boards-detail-row-Nutzung -- daher hier bewusst scoped statt die Basisregel zu aendern), aber
   deutlich schmaler und immer einspaltig gestapelt statt der vorherigen Zwei-Spalten-Aufteilung. */
/* Nutzer-Feedback: "mache das popup breiter. die cover sind 'gequetscht' kleiner" -- seit der
   70/30-Beschreibung/In-Besitz-Spalte (siehe boards-cover-desc-col-split) ist im vorherigen
   640px-Popup spuerbar weniger Platz je Spalte uebrig als vorher, das Cover wirkte dadurch
   gequetscht. Popup deutlich breiter, Cover-Spalte entsprechend mitgewachsen. */
.compendium-popup { max-width: 820px; }
.compendium-popup .modal-body { padding: var(--space-8) var(--space-10); }
.compendium-popup .boards-detail-row { flex-direction: column; margin-top: 0; }
.compendium-popup .boards-detail-left-col,
.compendium-popup .boards-detail-right-col { flex: none; }
.compendium-popup .boards-cover-desc-row { margin: 0 0 var(--space-6); }
.compendium-popup .boards-cover-portrait { width: 170px; }
.compendium-popup .boards-detail-topleft .compendium-media-slider,
.compendium-popup .boards-detail-topleft .compendium-media-thumbs,
.compendium-popup .boards-detail-topleft .compendium-media-empty-btn { margin-left: 0; margin-right: 0; }
.compendium-popup .boards-detail-bottomleft { padding-right: 0; overflow-y: visible; }
.compendium-popup .boards-info-panel { max-height: none; overflow-y: visible; }
/* Nutzer-Feedback: Cover jetzt hochkant (wie ein Boxart-/DVD-Cover) neben der Spiel-/Film-/
   Serien-Beschreibung -- die Beschreibung stand vorher ganz rechts im Info-Panel, hier ist durch
   das schmalere Cover Platz dafuer frei geworden (mehr Raum bleibt dadurch im rechten Info-Panel
   fuer "Geplante Events"). */
.boards-cover-desc-row { display: flex; gap: var(--space-7); margin: 0 var(--space-7) var(--space-6); }
.boards-cover-portrait {
  flex-shrink: 0; width: 170px; aspect-ratio: 2 / 3; border-radius: var(--radius); overflow: hidden;
  background: var(--panel-2); display: flex; align-items: center; justify-content: center;
}
.boards-cover-portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }
.boards-cover-portrait-empty { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; font-size: var(--fs-display); color: var(--text-dim); }
.boards-cover-desc-col { flex: 1; min-width: 0; overflow-y: auto; }
.boards-cover-desc-col .boards-subtab-panel { padding: 0; }
.boards-cover-desc-col .boards-subtab-panel h4 { margin-top: 0; }
/* R64 Nutzer-Feedback: "in besitz oben rechts in einer 70/30 spalte anzgeigen ... 70 Beschreibung /
   30 In besitz ... darunter kommt verknüpfte dateien" -- Beschreibung (links, 70%) und In Besitz +
   Verknuepfte Dateien (rechts, 30%, uebereinander gestapelt) stehen jetzt nebeneinander statt
   vertikal weiter unten im Info-Panel (siehe boards-detail-modal/movies-detail-modal in index.html). */
.boards-cover-desc-col-split { display: grid; grid-template-columns: 7fr 3fr; gap: var(--space-7); align-items: start; }
.boards-desc-col-70, .boards-desc-col-30 { min-width: 0; }
.boards-desc-col-30 { display: flex; flex-direction: column; gap: var(--space-5); }
@media (max-width: 560px) {
  .boards-cover-desc-row { flex-direction: column; }
  .boards-cover-portrait { width: 140px; }
  .boards-cover-desc-col-split { grid-template-columns: 1fr; }
}
/* R43: ersetzt den Slider, solange ein Eintrag keine Medien hat -- schmal (nicht hoch, siehe
   Nutzer-Feedback) statt der vorherigen festen Slider-Aspect-Ratio, damit die Zeile darunter
   (Notizen) entsprechend weiter nach oben rutscht.
   R44: Inhalt linksbuendig statt zentriert -- Nutzer-Feedback, dass Icon/Text auf der linken
   Seite buendig mit der "Notizen"-Ueberschrift darunter beginnen sollen (beide haben 14px
   Padding, siehe .boards-subtab-panel), statt in der Buttonmitte zu schweben. */
.compendium-media-empty-btn {
  box-sizing: border-box; display: flex; align-items: center; justify-content: flex-start;
  gap: var(--space-4); padding: var(--space-5) var(--space-7); margin: 0 0 var(--space-5); font-size: var(--fs-md); color: var(--text-dim);
  background: var(--knopf); border: 1px dashed var(--border); border-radius: var(--radius);
  cursor: pointer; transition: border-color var(--duration-fast) ease, color var(--duration-fast) ease;
}
.compendium-media-empty-btn:hover { border-color: var(--accent); color: var(--accent); }
/* R49: "Bearbeiten"-Button neben "Notizen", oeffnet ein Notizen-only-Popup statt des ganzen
   Event-Formulars. */
.boards-notes-heading-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.boards-notes-heading-row h4 { margin: 0; }
/* R43: Nutzer-Feedback -- Info-/Chat-Box sollen wie ein Dashboard-Widget aussehen (kein Rahmen,
   eigener var(--panel)-Hintergrund, abgerundet), statt des vorherigen var(--zone-bg)/randlosen
   Looks, der eher wie ein reiner Container statt einer eigenstaendigen Karte wirkte. */
.boards-info-panel {
  min-width: 0; overflow-y: auto;
  background: var(--panel); border-radius: var(--radius); padding: var(--space-5) var(--space-6);
}
.boards-detail-bottomleft { min-width: 0; min-height: 0; overflow-y: auto; padding-right: var(--space-7); overscroll-behavior: contain; }
/* .boards-info-panel traegt bereits eigenes horizontales Padding -- innen liegende
   .boards-subtab-panel-Bloecke (Notizen-Muster wiederverwendet) sollen das nicht verdoppeln. */
.boards-info-panel .boards-subtab-panel { padding: var(--space-2) 0; }
.boards-detail-right {
  min-width: 0; display: flex; flex-direction: column; min-height: 0; flex: 1;
  background: var(--panel); border-radius: var(--radius); border: none;
}
@media (max-width: 880px) {
  .boards-detail-row { flex-direction: column; }
  .boards-detail-left-col, .boards-detail-right-col { flex: none; }
  .boards-info-panel { max-height: 320px; }
  /* R38 Phase 5: .boards-layout hatte (anders als das strukturell identische .wiki-layout,
     siehe dortiges @media (max-width: 880px)) bisher GAR keinen Mobile-Breakpoint -- die feste
     300px-Sidebar-Spalte blieb bei schmalen Breiten neben der Detailansicht stehen und quetschte
     sie fast vollstaendig aus dem sichtbaren Bereich. Betrifft seit dem R62-3-Spalten-Umbau nur
     noch Rooms (.sammlung-columns hat eigene Breakpoints weiter oben). */
  .boards-layout { grid-template-columns: 1fr; height: auto; }
  .boards-sidebar { border-right: none; height: auto; }
  /* Analog zu .wiki-main (siehe dortiges @media (max-width: 880px)) -- "height: auto" allein
     reicht nicht, das darueberliegende .boards-main hat weiterhin "overflow: hidden" aus der
     Basis-Regel oben, wodurch .boards-detail-row (flex:1; min-height:0) bei fehlender fester
     Elternhoehe auf einen kleinen Implicit-Grid-Wert kollabierte und Notizen/Diskussion am
     unteren Rand abgeschnitten wurden -- live bei schmaler Breite an Movies verifiziert (gleiche
     Klasse wie Games/LAN-Party, daher kein Movies-spezifischer Fix). */
  .boards-main { height: auto; overflow: visible; }
  /* .modal-header-actions ist app-weit "flex-shrink: 0" (siehe .modal-header-actions weiter
     oben) -- bei Titel + mehreren Aktions-Buttons (Beitreten/Anheften/Teilen/Bearbeiten/Schliessen)
     wird das auf schmalen Breiten oft zusammen breiter als das Modal, wodurch Buttons rechts
     abgeschnitten werden bzw. ganz aus dem sichtbaren Bereich rutschen (live an "Ich bin
     interessiert!" bei Games/Movies/LAN-Party UND am kompletten Bearbeiten/Schliessen-Cluster der
     Event-Vorschau verifiziert -- dort war sogar der Schliessen-Button nicht mehr erreichbar).
     Ehemals nur fuer die drei Compendium-Detail-Header gescoped, jetzt bewusst auf die geteilte
     Basisklasse ausgeweitet: flex-wrap greift nur ein, wenn der Inhalt tatsaechlich nicht passt,
     aendert also an Modals, die schon auf eine Zeile passen, gar nichts. */
  .modal-header-bar { flex-wrap: wrap; }
  /* flex-wrap allein reicht nicht: .modal-header-actions ist app-weit "flex-shrink: 0" (siehe
     .modal-header-actions weiter oben), dadurch bleibt der Container selbst ungebremst auf seiner
     Inhalts-Breite (alle Buttons in einer Zeile) -- eigene Kinder wrappen erst, wenn der Container
     tatsaechlich schmaler als sein Inhalt ist. max-width:100% deckelt ihn auf die verfuegbare
     Zeilenbreite, erst dadurch greift flex-wrap auf den Buttons selbst. */
  .modal-header-actions { flex-wrap: wrap; justify-content: flex-end; flex-shrink: 1; max-width: 100%; }
}

/* ---------- Dateien ---------- */
/* R44: Drag&Drop-Upload -- Highlight beim Ueberziehen echter OS-Dateien ueber den Inhaltsbereich
   des aktuell offenen Ordners (siehe openFilesUploadModalWithFiles() in app.js). */
#files-content.drag-over { outline: 2px dashed var(--accent); outline-offset: -4px; background: var(--accent-soft); border-radius: var(--radius); }
.files-list { display: flex; flex-direction: column; gap: var(--space-1); }
/* R61 Phase E: zweizeilig -- .files-row-main (Icon/Name/Groesse/Buttons) oben, die volle
   .files-row-desc-row darunter (siehe dort), statt vorher einer einzelnen Flex-Zeile. */
.files-row { display: flex; flex-direction: column; border-radius: var(--radius-md); background: var(--inhalt-kachel); border: 1px solid transparent; cursor: default; }
.files-row:hover { border-color: var(--accent); }
.files-row-main { display: flex; align-items: center; gap: var(--space-4); padding: var(--space-4) var(--space-6); }
.files-row .mdi { font-size: var(--fs-xl); color: var(--text-dim); flex-shrink: 0; }
.files-row .files-row-name-wrap { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.files-row .files-row-name-line { display: flex; align-items: center; gap: var(--space-4); min-width: 0; }
.files-row .files-row-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-shrink: 0; max-width: 60%; }
/* R60 Nutzer-Feedback: die ganze Zeile oeffnet jetzt die Vorschau, nicht mehr nur der
   Dateiname -- Cursor/Hover-Hinweis deshalb auf die ganze Zeile statt nur den Namen. */
.files-row-clickable { cursor: pointer; }
.files-row-clickable:hover .files-row-name-previewable { color: var(--accent); text-decoration: underline; }
/* R61 Phase E Nutzer-Feedback: eigene volle Zeile unten mit kleinem Zitat-Icon statt einer
   schmalen Pille in der Namensspalte -- IMMER gerendert (auch ohne Text), damit alle
   Dateizeilen dieselbe Hoehe haben, unabhaengig davon ob eine Beschreibung vorhanden ist. */
.files-row-desc-row { display: flex; align-items: flex-start; gap: var(--space-3); padding: 0 var(--space-6) var(--space-4) 34px; min-height: 24px; box-sizing: border-box; }
.files-row-desc-row .mdi { flex-shrink: 0; font-size: var(--fs-xs); color: var(--text-dim); opacity: 0.55; margin-top: 1px; }
.files-row-desc {
  flex: 1; min-width: 0; color: var(--text-dim); font-size: var(--fs-2xs); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* R43: zeigt an, ob/mit welchem Spiel/Film/Serie/LAN-Party-Eintrag eine Datei verknuepft ist
   (siehe /api/filelinks/reverse in renderFilesContent()) -- vorher war eine Verknuepfung nach
   dem Setzen nirgends in der Dateiliste selbst sichtbar. */
.files-row .files-row-links { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-1); }
.files-row-link-chip {
  display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--fs-2xs); color: var(--text-dim);
  background: var(--panel); border-radius: var(--radius-xs); padding: 1px var(--space-3); white-space: nowrap;
}
.files-row-link-chip .mdi { font-size: var(--fs-md); color: var(--text-dim); }
.files-row .files-row-size { font-size: var(--fs-xs); color: var(--text-dim); flex-shrink: 0; }
.files-row .icon-btn { background: none; border: none; color: var(--text-dim); cursor: pointer; padding: var(--space-2); flex-shrink: 0; }
.files-row .files-row-delete:hover { color: var(--danger); }
.files-row .files-row-download:hover, .files-row .files-row-desc-btn:hover { color: var(--accent); }

/* ---------- Comments ---------- */
.comment-item { background: var(--inhalt-kachel); border-radius: var(--radius-md); padding: var(--space-4) var(--space-5); }
.comment-item .comment-head { display: flex; justify-content: space-between; gap: var(--space-4); font-size: var(--fs-xs); color: var(--text-dim); margin-bottom: var(--space-2); }
.comment-item .comment-author { font-weight: var(--fw-semibold); color: var(--text); }
.comment-item .comment-text { font-size: var(--fs-lg); white-space: pre-wrap; word-break: break-word; }
.comment-delete { background: none; border: none; color: var(--text-dim); cursor: pointer; font-size: var(--fs-xs); }
.comment-delete:hover { color: var(--danger); }

.event-links-tabs { display: flex; gap: var(--space-4); margin-bottom: var(--space-5); }
.event-links-add-row { display: flex; gap: var(--space-3); margin-bottom: var(--space-4); }
.event-links-add-row input { flex: 1; min-width: 0; }
.event-links-list { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-5); }
.event-link-row {
  display: flex; align-items: center; gap: var(--space-4); padding: var(--space-3) var(--space-5); border-radius: var(--radius-sm);
  background: var(--inhalt-kachel); border: 1px solid var(--border); font-size: var(--fs-md);
}
.event-link-row-url { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--link-accent, var(--accent)); }
.event-link-row .icon-btn { flex-shrink: 0; }

/* ---------- Login ---------- */
.login-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-10);
}
.login-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-16);
  width: 100%;
  max-width: 360px;
  display: flex;
  flex-direction: column;
  gap: var(--space-7);
}
.login-card h1 { margin: 0; text-align: center; font-size: var(--fs-2xl); }
.login-sub { margin: 0 0 var(--space-3); text-align: center; color: var(--text-dim); font-size: var(--fs-lg); }
/* Neutraler statt des .text-btn-Standard-Rot (das ist fuer Loeschen/Verlassen-artige Aktionen
   gedacht, s.o.) -- Passkey-Anmeldung ist eine gleichwertige Alternative zum Passwort-Login. */
.login-passkey-btn { justify-content: center; width: 100%; }
.login-passkey-btn:hover { background: var(--accent); border-color: var(--accent); color: var(--text-on-color); }
.login-card label { display: flex; flex-direction: column; gap: var(--space-2); font-size: var(--fs-md); color: var(--text-dim); }
.login-card input {
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-5) var(--space-5);
  border-radius: var(--radius-sm);
  font-size: var(--fs-lg);
  font-family: inherit;
}
.login-error { color: var(--danger); font-size: var(--fs-md); }

/* ---------- Settings (Discord-Stil: Tabs links) ---------- */
/* Feste statt inhaltsabhaengiger Hoehe -- vorher wuchs/schrumpfte das ganze Fenster beim
   Tab-Wechsel mit dem jeweiligen Panel-Inhalt (z.B. viel kuerzer bei "Tags" als bei "Log"),
   was staendiges Springen der Fenstergroesse verursachte. Jetzt bleibt das Fenster immer auf
   voller Groesse, nur der Panel-Bereich selbst scrollt bei Bedarf. */
/* Nutzer-Feedback: "mache das settings fenster deutlich größer damit wir mehr sehen" (Rechte-
   Rework mit Gruppen-Tabellen pro Organisation) -- deutlich vergroessert gegenueber der alten
   960px/56vh-Groesse, damit die neuen Mitglieder×Organisationen×Gruppen-Tabellen genug Platz haben. */
.settings-modal-inner { max-width: 1400px; height: 78vh; min-height: 420px; display: flex; flex-direction: column; overflow: hidden; }
.settings-modal-inner > h2 { flex-shrink: 0; }
/* Nutzer-Feedback: Organisation-bearbeiten wird aus den Haupt-Einstellungen heraus geoeffnet --
   minimal kleiner als das Haupt-Einstellungen-Fenster, damit optisch erkennbar bleibt, dass es
   ein untergeordnetes Fenster ist. ID-scoped, teilt sich .settings-modal-inner sonst mit
   #settings-modal (identische Groesse gewuenscht). */
#org-edit-modal .settings-modal-inner { max-width: 1320px; height: 74vh; min-height: 400px; }
.settings-layout { display: flex; align-items: stretch; gap: var(--space-12); flex: 1; min-height: 0; }
.settings-tabs {
  display: flex; flex-direction: column; gap: var(--space-2); flex: 0 0 200px;
  border-right: 1px solid var(--border); padding-right: var(--space-8);
}
.settings-tab-btn {
  background: transparent; border: none; color: var(--text-dim);
  padding: var(--space-5) var(--space-6); border-radius: var(--radius-sm); cursor: pointer; font-size: var(--fs-lg);
  text-align: left; width: 100%; transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.settings-tab-btn:hover { background: var(--panel-hover); color: var(--text); }
.settings-tab-btn.active { background: var(--aktiv-kachel); color: var(--text); }  /* Auswahl, siehe .tab-btn.active */
/* Nur im Profil-Modal-Markup vorhanden (siehe index.html), auf Desktop ausgeblendet -- wird erst
   auf Mobile als Listen-Chevron sichtbar, s.u. */
.settings-tab-chevron { display: none; color: var(--text-dim); font-size: var(--fs-xl); }
.settings-panels { flex: 1; min-width: 0; overflow-y: auto; }
.settings-panel { display: none; animation: fadeInUp var(--duration-base) ease both; }
.settings-panel.active { display: block; }
/* R42: Changelog-Tab in den Settings -- gleiche .changelog-group/.changelog-version-Bausteine
   wie im Popover (renderChangelogGrouped()), hier ohne feste max-height (Settings-Modal hat
   bereits eine eigene, groessere Scrollflaeche). */
.changelog-settings-list { max-width: 640px; }
@media (max-width: 720px) {
  .settings-layout { flex-direction: column; }
  .settings-tabs { flex-direction: row; flex-wrap: wrap; border-right: none; border-bottom: 1px solid var(--border); padding-right: 0; padding-bottom: var(--space-5); width: 100%; flex-basis: auto; flex-shrink: 0; }
  .settings-tab-btn { width: auto; }
  /* Nutzer-Feedback: "die settings ... sollten full screen sein" -- ID-scoped statt an der
     geteilten .settings-modal-inner-Klasse (die auch #settings-modal/#org-edit-modal nutzen,
     beide auf Mobile ohnehin nicht erreichbar, aber ein globaler Fullscreen waere trotzdem
     unpassend fuer diese admin-lastigen Fenster). Gleiches Muster wie beim Event-Vorschau-
     Vollbild-Fix (siehe #event-preview-modal weiter oben). */
  /* Mobil-Feedback (aktuelle Runde): "nicht mehr die menüleiste mobil unten abschneiden sondern
     da drüber gehen" -- Kehrtwende gegenueber der fruesheren Entscheidung (Kommentar-Historie
     siehe Git), die die Tab-Bar bewusst freihielt. Jetzt wieder ein echter Vollbild-Overlay
     (inset:0 aus der .modal-overlay-Basisregel greift wieder ungekuerzt), die Seite legt sich wie
     jedes andere Vollbild-Modal ueber die schwebende Tab-Bar (z-index 310 vs. 160 der Bar). */
  #profile-modal.modal-overlay {
    padding: 0; align-items: stretch;
  }
  #profile-modal .modal.settings-modal-inner {
    max-width: none; width: 100%; height: 100%; min-height: 0; max-height: none; border-radius: 0;
    animation: iosSheetIn var(--duration-slow) var(--ease-sheet) both;
  }

  /* Nutzer-Feedback: "überarbeite die gesamten settings auf eine schöne mobile ansicht evtl.
     wie ios settings mit vor zurück buttons und quasi einstellungs seiten als liste wo man dann
     drauf kommt" -- NUR das Profil-Modal (das einzige Settings-Fenster, das auf Mobile
     ueberhaupt erreichbar ist, s.o. -- #settings-modal/#org-edit-modal bleiben unveraendert bei
     der normalen Tabzeile weiter oben). Startzustand ist die Liste (#profile-modal OHNE
     .mobile-settings-detail); ein Tab-Klick schaltet per JS (siehe .settings-tab-btn-Click-
     Handler in app.js) auf .mobile-settings-detail um -- das jeweilige Panel wird dabei ganz
     normal ueber die schon bestehende .settings-panel.active-Logik sichtbar, hier wird nur
     zwischen Tab-Liste und Panel-Bereich umgeschaltet. */
  #profile-modal.mobile-settings-detail #profile-mobile-back-btn { display: flex; }
  .profile-mobile-close-btn { display: flex; }
  /* Nutzer-Feedback: "der zurück button ist noch über der überschrift links" -- der Zurueck-
     Pfeil ist absolut positioniert (siehe Basis-Regel weiter oben) und wurde dadurch vom
     Titel-h2 einfach ueberlagert statt daneben zu stehen, da h2 wie ueberall sonst bei der
     normalen Modal-Padding-Kante (24px) beginnt. Nur im Detail-Zustand noetig -- die Liste zeigt
     ja keinen Zurueck-Button und der Titel soll dort ganz normal links buendig bleiben. */
  #profile-modal.mobile-settings-detail #profile-modal-title { padding-left: 34px; }
  #profile-modal:not(.mobile-settings-detail) .settings-panels { display: none; }
  #profile-modal.mobile-settings-detail .settings-tabs { display: none; }
  #profile-modal .settings-tabs {
    flex-direction: column; flex-wrap: nowrap; gap: 0; border-bottom: none; padding-bottom: 0;
  }
  #profile-modal .settings-tab-btn {
    display: flex; align-items: center; justify-content: space-between; width: 100%;
    padding: var(--space-7) var(--space-3); border-radius: 0; border-bottom: 1px solid var(--border); font-size: var(--fs-lg);
  }
  #profile-modal .settings-tab-btn.active { background: transparent; color: var(--text); }
  #profile-modal .settings-tab-chevron { display: inline-block; }
}
.settings-add-toggle-btn { margin-bottom: var(--space-7); }
.settings-add-card {
  background: var(--inhalt-kachel); border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-7) var(--space-8); margin-bottom: var(--space-8);
}
.settings-add-card h3 { margin: 0 0 var(--space-6); font-size: var(--fs-lg); }
/* Nutzer-Feedback: "Sichtbare Tabs" huebscher als nackte Checkbox-Zeilen -- Kachel-Buttons mit
   Icon (gleiches Icon wie der jeweilige Nav-Tab), gefuellter Akzentfarbe im aktiven Zustand
   (gleiche Konvention wie .filter-btn.active ueberall sonst in der App, z.B. Chatroom-Typ-Wahl). */
.visible-tabs-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-5); margin-top: var(--space-5); }
.visible-tab-btn {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: var(--space-4);
  padding: var(--space-10) var(--space-6); border-radius: var(--radius); border: 1px solid var(--border);
  background: var(--knopf); color: var(--text-dim); cursor: pointer; font-family: inherit;
  transition: border-color var(--duration-fast) ease, background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.visible-tab-btn:hover { border-color: var(--accent); color: var(--text); }
.visible-tab-icon { font-size: var(--fs-4xl); }
.visible-tab-label { font-size: var(--fs-md); font-weight: var(--fw-semibold); }
.visible-tab-check { position: absolute; top: 8px; right: 8px; font-size: var(--fs-xl); opacity: 0; transition: opacity var(--duration-fast) ease; }
.visible-tab-btn.active { background: var(--accent); border-color: var(--accent); color: var(--text-on-color); }
.visible-tab-btn.active .visible-tab-check { opacity: 1; }

.settings-org-section { padding: var(--space-6) 0; border-top: 1px solid var(--border); }
.settings-org-section:first-child { border-top: none; padding-top: var(--space-2); }
.settings-org-section h4 { margin: 0 0 var(--space-5); font-size: var(--fs-sm); font-weight: var(--fw-bold); color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.03em; }
/* Zweite Ueberschrift INNERHALB eines Abschnitts (die helle Farbpalette unter der dunklen) --
   braucht Luft nach oben, sonst klebt sie am Knopf der Palette darueber. */
.settings-org-section h4.settings-org-subheading { margin-top: var(--space-10); }
/* Die Knoepfe unter der Farbpalette in einer Zeile, umbrechend auf schmalen Fenstern. */
.settings-farb-knoepfe { display: flex; flex-wrap: wrap; gap: var(--space-3); }
/* Farbbereich in Gruppen (2026-09-16): Ueberschrift, eine Zeile Erklaerung, darunter das Raster.
   Der Bereich selbst ist KEIN Raster mehr -- sonst landen die Gruppen nebeneinander in Spalten. */
.settings-farb-bereich { display: flex; flex-direction: column; }
.settings-farb-gruppe + .settings-farb-gruppe { margin-top: var(--space-8); }
.settings-farb-gruppe-titel {
  margin: 0 0 var(--space-1); font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--text);
  text-transform: none; letter-spacing: 0;
}
.settings-farb-gruppe-text { margin: 0 0 var(--space-4); }
/* Name und Erklaerung stehen untereinander -- die Karte wird dadurch zweizeilig. */
.settings-category-card-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.settings-category-card-hinweis { font-size: var(--fs-2xs); color: var(--text-dim); line-height: 1.3; }
/* Der Name darf umbrechen -- "Textfarbe gedaempft" wurde sonst mit Ellipse abgeschnitten. */
.settings-category-card-text .settings-category-card-name { white-space: normal; overflow: visible; text-overflow: clip; }
/* Discord-Tab: kleine nummerierte Anleitung zum Webhook-Einrichten (siehe org-edit-discord-Panel). */
.settings-discord-howto { margin: 0; padding-left: var(--space-10); display: flex; flex-direction: column; gap: var(--space-3); font-size: var(--fs-md); color: var(--text-dim); }
/* Nutzer-Feedback: die drei Benachrichtigungs-Checkboxen sahen nackt/unschoen aus -- gleiche
   Filter-Pillen-Optik wie ueberall sonst (.filter-btn/.filter-btn-check), aber als volle Zeilen
   statt kompakter Chips, damit die laengeren Labels gut lesbar bleiben. */
/* .push-notify-toggle-btn (Profil > Benachrichtigungen) teilt sich dieselbe Optik -- bewusst eine
   eigene Klasse statt .discord-notify-toggle-btn direkt zu benutzen, da fuer diese schon ein
   generischer "einfach nur .active umschalten"-Click-Handler existiert (siehe weiter unten); ein
   zweiter, eigener Handler fuer den Push-Speichervorgang wuerde bei gleicher Klasse zusammen mit
   dem generischen Handler die Klasse zweimal umschalten (= sichtbar keine Aenderung). */
.discord-notify-toggles { display: flex; flex-direction: column; gap: var(--space-4); }
.discord-notify-toggle-btn, .push-notify-toggle-btn {
  display: flex; align-items: center; gap: var(--space-4); width: 100%; box-sizing: border-box;
  justify-content: flex-start; text-align: left; padding: var(--space-5) var(--space-7);
}
.discord-notify-toggle-btn .mdi:not(.filter-btn-check), .push-notify-toggle-btn .mdi:not(.filter-btn-check) { font-size: var(--fs-body); flex-shrink: 0; color: var(--text-dim); }
.discord-notify-toggle-btn.active .mdi:not(.filter-btn-check), .push-notify-toggle-btn.active .mdi:not(.filter-btn-check) { color: var(--text-on-color); }
.settings-add-row { display: flex; gap: var(--space-6); flex-wrap: wrap; align-items: flex-end; }
.settings-field { display: flex; flex-direction: column; gap: var(--space-2); font-size: var(--fs-xs); color: var(--text-dim); flex: 1; min-width: 140px; }
.settings-field-color { flex: 0 0 auto; min-width: 0; }
.settings-add-row input[type="text"], .settings-add-row input[type="password"] {
  background: var(--panel); border: 1px solid var(--border); color: var(--text);
  padding: var(--space-4) var(--space-5); border-radius: var(--radius-sm); font-size: var(--fs-lg); font-family: inherit;
}
.settings-add-row input[type="color"] { width: 40px; height: 36px; padding: var(--space-1); border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--panel); }
.org-upload-row { display: flex; align-items: center; gap: var(--space-4); }
.org-sound-status { font-size: var(--fs-md); color: var(--text-dim); }
.org-upload-preview { width: 28px; height: 28px; border-radius: var(--radius-sm); object-fit: cover; border: 1px solid var(--border); }
.org-logo-img { width: 1.2em; height: 1.2em; border-radius: var(--radius-xs); object-fit: cover; vertical-align: middle; }
/* flex-wrap:wrap (statt Browser-Default nowrap): laengere Checkbox-Labels (z.B. "Keinen Medien-
   Ordner anzeigen (Medien bleiben im Event selbst erhalten)") liefen auf schmalen Breiten ueber den
   Rand hinaus -- der Text-Flex-Item wollte trotz white-space:normal nicht unter seine einzeilige
   Breite schrumpfen (klassisches Flexbox-min-width:auto-Verhalten), nur ein Zeilenumbruch der
   gesamten Zeile behebt das. Rein defensiv, greift nur bei tatsaechlichem Platzmangel. */
.inline-check { display: flex; align-items: center; gap: var(--space-3); font-size: var(--fs-md); color: var(--text-dim); flex-wrap: wrap; }
.inline-check input { width: auto; }

/* R50: Invite-Links (Settings > Benutzer) */
.invite-link-list { display: flex; flex-direction: column; gap: var(--space-4); margin-top: var(--space-6); }
.invite-link-row { display: flex; align-items: center; gap: var(--space-4); background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-md); padding: var(--space-4) var(--space-5); }
.invite-link-row-info { display: flex; flex-direction: column; gap: var(--space-1); min-width: 120px; font-size: var(--fs-md); }
.invite-link-row-info .settings-hint { margin: 0; }
.invite-link-url { flex: 1; background: var(--panel-2); border: 1px solid var(--border); color: var(--text-dim); padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm); font-size: var(--fs-sm); font-family: monospace; min-width: 0; }

/* R50: Onboarding-Anleitung (Diaschau) */
.tutorial-modal-inner { max-width: 560px; }
.tutorial-modal-inner h2 { margin-top: 0; }
#tutorial-slide-body p { line-height: 1.5; }
.tutorial-hint { color: var(--text-dim); font-size: var(--fs-md); }
.tutorial-area-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-5); margin-top: var(--space-7); }
.tutorial-area-chip { background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-md); padding: var(--space-5) var(--space-6); font-size: var(--fs-md); display: flex; flex-direction: column; gap: var(--space-1); }
.tutorial-area-chip span { color: var(--text-dim); font-size: var(--fs-sm); }
.tutorial-area-chip .mdi { font-size: var(--fs-xl); color: var(--accent-light); line-height: 1; }
.tutorial-example-tile { background: var(--inhalt-kachel); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; margin-top: var(--space-7); }
.tutorial-example-tile-header { background: var(--panel-hover); padding: var(--space-4) var(--space-6); font-weight: var(--fw-bold); font-size: var(--fs-lg); }
.tutorial-example-tile-body { display: flex; align-items: center; gap: var(--space-6); padding: var(--space-5) var(--space-6); }
.tutorial-example-tile-thumb { width: 44px; height: 44px; border-radius: var(--radius-md); background: var(--panel); display: flex; align-items: center; justify-content: center; font-size: var(--fs-2xl); flex-shrink: 0; }
.tutorial-example-tile-info { font-size: var(--fs-md); line-height: 1.4; }
.tutorial-example-tile-meta { color: var(--text-dim); font-size: var(--fs-sm); }
.tutorial-nav { display: flex; align-items: center; justify-content: space-between; margin-top: var(--space-10); }
.tutorial-dots { display: flex; gap: var(--space-3); }
.tutorial-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--border); }
.tutorial-dot.active { background: var(--accent); }
.tutorial-nav-buttons { display: flex; gap: var(--space-5); }
/* Nutzer-Feedback: "bitte 'mit spiel verknüpfen' wieder hin schreiben und nicht ja nein draus
   machen. sondern einfach ein button in diesem stil aber nicht ja nein" -- einzelner
   .filter-btn-Toggle statt Ja/Nein-Segmenten, .event-folder-toggle-block liefert Flex/Abstand
   zum Info-Icon bereits. */
#event-catalog-optional-block { margin-bottom: var(--space-4); }
.event-catalog-optional-toggle-btn { display: inline-flex; align-items: center; gap: var(--space-3); }

.table-scroll { overflow-x: auto; }
.settings-table { width: 100%; border-collapse: collapse; font-size: var(--fs-md); }
.settings-table th, .settings-table td { padding: var(--space-4) var(--space-5); text-align: left; border-bottom: 1px solid var(--border); white-space: nowrap; }
.settings-table th { color: var(--text-dim); font-weight: var(--fw-semibold); font-size: var(--fs-sm); }
.settings-table input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }
.settings-table th.checkbox-col, .settings-table td.checkbox-cell { text-align: center; }
/* Die Organisationen-Spalte traegt eine unbegrenzte Zahl von Mitgliedschafts-Chips (settings-chip-badge)
   -- mit white-space:nowrap sprengt das die Tabellenbreite und drueckt die Aktions-Spalte (Rechte
   bearbeiten) ausserhalb des sichtbaren Modals, sodass nur noch der .table-scroll-Scrollbalken sie
   erreichbar macht. Spalte darf umbrechen, Aktions-Spalte bekommt dafuer eine reservierte Breite. */
.settings-table th.settings-col-orgs, .settings-table td.settings-col-orgs { white-space: normal; }
.settings-table th.settings-col-action, .settings-table td.settings-col-action { white-space: nowrap; width: 160px; }
.org-row-actions { display: flex; align-items: center; gap: var(--space-3); white-space: nowrap; }
.settings-table-perms td, .settings-table-perms th { white-space: normal; }
.perm-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--space-2) var(--space-8); min-width: 260px; }
.perm-grid-item { display: flex; align-items: center; gap: var(--space-3); font-size: var(--fs-sm); cursor: pointer; }
.perm-grid-item input { width: 14px; height: 14px; cursor: pointer; flex-shrink: 0; }
/* R44: Nutzer-Feedback -- die Checkbox-Liste war eine unstrukturierte Wand ohne erkennbaren
   Zusammenhang zwischen den Rechten. Gruppen-Titel + optionaler Hinweistext (z.B. dass
   Org-Admin die Haekchen darunter ueberfluessig macht) schaffen Kontext pro Abschnitt. */
.perm-group { margin-bottom: var(--space-5); }
.perm-group:last-of-type { margin-bottom: 0; }
.perm-group-title { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-dim); margin-bottom: var(--space-2); }
.perm-group-hint { font-size: var(--fs-xs); color: var(--text-dim); margin-top: var(--space-2); line-height: 1.35; }

/* R61 Rechte-Rework: Mehrfachauswahl-Liste der Rechtegruppen einer Organisation im Rechte-Popup
   (ersetzt die vorherige Einzel-Flag-Checkbox-Matrix, siehe buildGroupAssignmentSection() in app.js). */
.perm-group-checklist { display: flex; flex-direction: column; gap: var(--space-3); }
.perm-group-checkbox-item { font-size: var(--fs-md); }
.perm-group-color-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: var(--space-3); vertical-align: middle; }

/* "Rechte bearbeiten"-Popup (R32, verbreitert + gruppiert in R44) -- ersetzt die frueher pro
   Nutzer/Organisation direkt in der Tabelle sichtbare Checkbox-Matrix, die bei vielen Nutzern
   schnell unuebersichtlich wurde. R44: von 340px auf 460px verbreitert (Nutzer-Feedback: Popup
   durfte ruhig breiter sein) + dreispaltiges perm-grid statt zweispaltig, damit die laengeren,
   klareren Kurz-Labels nicht umbrechen. */
.user-rights-popover { width: 460px; max-width: calc(100vw - 24px); max-height: min(640px, calc(100vh - 40px)); overflow-y: auto; padding: var(--space-5); }
/* Zoom-Korrektur fuer grosse Bildschirme, gleiches Prinzip wie #tab-chatrooms .boards-layout. */
@media (min-width: 2560px) and (max-width: 3839px) {
  .user-rights-popover { max-height: min(640px, calc((100vh - 40px) / 1.18)); }
}
@media (min-width: 3840px) {
  .user-rights-popover { max-height: min(640px, calc((100vh - 40px) / 1.45)); }
}
.user-rights-org-block { padding: var(--space-4) var(--space-5); border-radius: var(--radius-md); background: var(--inhalt-kachel); margin-bottom: var(--space-4); }
.user-rights-org-name { font-weight: var(--fw-semibold); font-size: var(--fs-md); margin-bottom: var(--space-4); }
.user-rights-org-actions { display: flex; align-items: center; gap: var(--space-4); margin-top: var(--space-4); }
.user-rights-add-org-row { padding: var(--space-2) var(--space-1) var(--space-1); }
.user-rights-add-org-row select { flex: 1; min-width: 0; }
/* R44: eigener Block ganz oben im globalen Rechte-Popup fuer User.is_global_admin -- unabhaengig
   von den Organisations-Abschnitten darunter, da global admin userweit statt pro Organisation gilt. */
.user-rights-global-admin-block { background: var(--accent-soft); }
.user-rights-global-admin-toggle { font-weight: var(--fw-semibold); }

.chip-list { display: flex; flex-wrap: wrap; gap: var(--space-4); }
.settings-chip {
  display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-5); border-radius: var(--radius-xl);
  background: var(--panel-2); border: 1px solid var(--border); font-size: var(--fs-md);
}
.settings-chip .chip-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.settings-chip .chip-dot.color-dot-btn { border: none; padding: 0; cursor: pointer; }
.settings-chip button { background: none; border: none; color: var(--text-dim); cursor: pointer; font-size: var(--fs-lg); padding: 0; line-height: 1; }
.settings-chip button:hover { color: var(--danger); }
.settings-chip-badge { font-size: var(--fs-2xs); color: var(--text-dim); background: var(--panel); padding: 1px var(--space-3); border-radius: 10px; }
/* R61 Rechte-Rework: zeigt in der globalen Benutzer-Tabelle zusaetzlich die Gruppennamen an, die
   ein Nutzer in dieser Organisation hat (Nutzer-Feedback: "sehen welcher user in welcher orga ist
   und welche gruppen er dort hat"). */
.settings-chip-badge-groups { color: var(--text); opacity: 0.75; }
.settings-chip .inline-check { font-size: var(--fs-xs); }

.settings-category-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--space-5); }
/* R48: Settings > "Nicht zugewiesen" -- eine Zeile pro verwaister Session, gleiches Karten-Muster
   wie .settings-category-card. */
.unassigned-row {
  display: flex; align-items: center; gap: var(--space-5); padding: var(--space-5) var(--space-7); border-radius: 10px;
  background: var(--inhalt-kachel); border: 1px solid var(--border); margin-bottom: var(--space-4); flex-wrap: wrap;
}
.unassigned-row-icon { font-size: var(--fs-xl); flex-shrink: 0; }
.unassigned-row-title { font-weight: var(--fw-semibold); }
.unassigned-row-cat { font-size: var(--fs-sm); color: var(--text-dim); padding: var(--space-1) var(--space-4); border-radius: var(--radius-full); background: var(--panel); }
.unassigned-row-date { font-size: var(--fs-sm); color: var(--text-dim); }
.unassigned-row-assign-btn { margin-left: auto; }
.unassigned-row-picker { margin-left: auto; display: flex; align-items: center; gap: var(--space-3); }
.unassigned-row-select { max-width: 220px; }
/* Verwaiste Medien-Ordner (siehe loadSettingsOrphanedMedia()) -- kleines Vorschaubild statt Icon,
   wo vorhanden. */
.unassigned-row-thumb { width: 32px; height: 32px; border-radius: var(--radius-sm); object-fit: cover; flex-shrink: 0; }
.settings-subheading { margin: var(--space-10) 0 var(--space-4); font-size: var(--fs-lg); }
.settings-log-list { max-height: 60vh; overflow-y: auto; }
.settings-category-card {
  display: flex; align-items: center; gap: var(--space-5); padding: var(--space-6) var(--space-7); border-radius: 10px;
  background: var(--inhalt-kachel); border: 1px solid var(--border);
}
/* Nur bei den Theme-Karten (mit Farbwaehler): die ganze Karte ist klickbar statt nur der
   winzige Farbpunkt darin, der als alleinige Klickflaeche viel zu klein/schwer zu treffen war.
   Tag-Karten (buildTagCard) teilen sich die Basisklasse, haben aber keinen Karten-Klick. */
.settings-category-card-clickable { cursor: pointer; transition: border-color var(--duration-fast) ease; }
.settings-category-card-clickable:hover { border-color: var(--accent); }
.settings-category-card .color-dot-btn {
  width: 18px; height: 18px; border-radius: 50%; flex-shrink: 0; border: 1px solid var(--border);
  padding: 0; cursor: pointer;
}
.settings-category-card-icon { font-size: var(--fs-xl); flex-shrink: 0; }
.settings-category-card-name { font-size: var(--fs-lg); font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.settings-category-card .chip-dot { width: 14px; height: 14px; }
.settings-category-card-delete { margin-left: auto; flex-shrink: 0; }

.org-member-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); font-size: var(--fs-md); padding: var(--space-2) 0; }
.org-member-row .om-name { flex: 1 0 100%; font-weight: var(--fw-semibold); }

/* ---------- Chip-Picker (Tags / Teilnehmer im Formular) ---------- */
.chip-picker { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.chip-option {
  padding: var(--space-3) var(--space-6); border-radius: var(--radius-xl); border: 1px solid var(--border);
  background: var(--panel-2); color: var(--text-dim); cursor: pointer; font-size: var(--fs-sm);
  transition: background var(--duration-fast) ease, color var(--duration-fast) ease, border-color var(--duration-fast) ease;
}
.chip-option.selected { background: var(--accent); color: var(--text-on-color); border-color: var(--accent); }
.chip-option .chip-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: var(--space-3); vertical-align: middle; }
.chip-picker-empty { color: var(--text-dim); font-size: var(--fs-sm); }
.chip-create-btn {
  width: 26px; height: 26px; border-radius: 50%; border: 1px dashed var(--border);
  background: transparent; color: var(--text-dim); cursor: pointer; display: inline-flex;
  align-items: center; justify-content: center; font-size: var(--fs-lg); padding: 0;
}
.chip-create-btn:hover { border-color: var(--accent); color: var(--accent); }
.chip-create-popover { padding: var(--space-5); width: 220px; }
.chip-create-popover input[type="text"] {
  width: 100%; padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm); border: 1px solid var(--border);
  background: var(--panel-2); color: var(--text); font-size: var(--fs-md); margin-bottom: var(--space-4); box-sizing: border-box;
}
.chip-create-popover .color-swatch-grid { margin-bottom: var(--space-4); }
.chip-create-popover .primary-btn { width: 100%; padding: var(--space-3); font-size: var(--fs-sm); }

/* ---------- iOS-artige Bottom-Tab-Bar (Mobile) ---------- */
/* Nutzer-Feedback: "lass mich generell auf mobil gerne auch nur die agenda und den kalender sehen
   ... mache mir das mit einer Menüleiste unten wie bei normalen Apple Apps. agenda, kalender und
   settings." -- ersetzt fuer diese drei Kernbereiche das bisherige Hamburger-Menue als primaeren
   Einstieg; alle uebrigen Tabs (Compendium/Medien/Notes/Dateien/Rooms/Links) bleiben ueber den
   Hamburger weiterhin erreichbar, nur nicht mehr so prominent. */
.mobile-tab-bar {
  display: none;
  position: fixed; z-index: var(--z-tab-bar);
  /* Nutzer-Feedback: "die mobile bar unten bitte floating und nicht bis zu den rändern und
     weiter oben. wie ein langer button als hintergrund." -- statt randlos ueber die volle Breite
     (left:0/right:0/bottom:0) jetzt ein schwebender, komplett umrandeter, voll abgerundeter
     Balken mit Abstand nach allen Seiten. */
  left: var(--mobile-tab-bar-gap); right: var(--mobile-tab-bar-gap);
  bottom: calc(var(--mobile-tab-bar-gap) + env(safe-area-inset-bottom, 0px));
  border-radius: var(--radius-full);
  overflow: hidden;
  /* Ohne Rahmen (2026-09-16: "bitte die menueleiste unten mobil nicht umranden") -- die Leiste hebt
     sich ueber Flaeche und Schatten genug ab, die 1px-Kante las sich wie ein aufgeklebter Button. */
  background: var(--panel); border: none;
  box-shadow: var(--shadow-lg);
  /* Erzwingt einen eigenen Compositing-Layer -- ohne das kann position:fixed in iOS Safari beim
     Scrollen kurz "hinterherhinken"/ruckeln, statt wirklich am Bildschirmrand zu kleben. */
  transform: translateZ(0);
  -webkit-transform: translateZ(0);
}
/* Basis-.tab-btn ist ein Pillen-Chip (Hintergrund var(--panel-2), horizontales Padding) -- hier
   komplett zurueckgesetzt und stattdessen wie ein echter App-Tab (Icon oben, Beschriftung darunter,
   volle gleichmaessige Breite) gebaut. Hoehere Spezifitaet (.mobile-tab-bar .tab-btn) statt einer
   neuen Klasse, damit die geteilte data-tab-Klick-Verkabelung aus app.js (die generisch auf
   ".tab-btn[data-tab]" matcht) unveraendert mitgreift. */
.mobile-tab-bar .tab-btn {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-1); background: none; border: none; border-radius: 0; color: var(--text-dim);
  font-size: var(--fs-2xs); cursor: pointer; height: var(--mobile-tab-bar-height); padding: var(--space-3) var(--space-2);
  transform: none;
}
.mobile-tab-bar .tab-btn:hover:not(.active) { background: none; color: var(--text-dim); transform: none; }
/* Der aktive Reiter war nur eingefaerbt und ging zwischen fuenf gleich aussehenden Symbolen unter
   (2026-09-16: "bitte mobil den aktiven button besser hervorheben"). Jetzt liegt hinter seinem
   Symbol eine gefuellte Pille in der Akzentfarbe, die Beschriftung darunter wird fett und nimmt den
   helleren Akzentton -- dieselbe Machart wie der gewaehlte Reiter in der Menuespalte am Desktop. */
.mobile-tab-bar .tab-btn.active { background: none; color: var(--accent-light); font-weight: var(--fw-semibold); }
.mobile-tab-bar .tab-btn .nav-icon {
  display: flex; align-items: center; justify-content: center;
  width: 46px; height: 28px; border-radius: var(--radius-full);
  font-size: var(--fs-2xl); transform: none; color: inherit;
  transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.mobile-tab-bar .tab-btn.active .nav-icon {
  background: var(--accent); color: var(--text-on-color);
  box-shadow: 0 2px 8px color-mix(in srgb, var(--accent) 45%, transparent);
}
@media (max-width: 880px) {
  .mobile-tab-bar { display: flex; }
  /* Platz am Seitenende reservieren, damit die letzte Zeile Inhalt (z.B. die letzte Agenda-Karte)
     nicht hinter der schwebenden Tab-Bar verschwindet -- zweimal --mobile-tab-bar-gap, da die Bar
     jetzt sowohl unten (Position) als auch oben (Abstand zum Inhalt darueber) je einen Gap braucht. */
  body { padding-bottom: calc(var(--mobile-tab-bar-height) + var(--mobile-tab-bar-gap) * 2 + env(safe-area-inset-bottom, 0px)); }
}

/* ---------- PWA-Installations-/Push-Hinweisleiste (Mobile) ---------- */
/* Kleine, dismiss-bare Leiste unten am Bildschirmrand -- schlaegt "Zum Home-Bildschirm hinzufuegen"
   vor (solange noch nicht als App installiert), danach "Push aktivieren" (sobald installiert, aber
   noch kein Geraet abonniert). Nur auf Mobile-Breite (gleicher 720px-Grenzwert wie die uebrigen
   Mobile-Anpassungen dieser App); z-index 150 -- unter Modals (200), aber ueber normalem Inhalt.
   Siehe maybeShowPwaBanner() in app.js. */
.pwa-banner {
  /* Ueber der neuen Bottom-Tab-Bar statt darunter/verdeckt -- .pwa-banner ist ohnehin nur sichtbar,
     wenn die (bei <=880px aktive) Tab-Bar auch sichtbar ist (JS-Sichtbarkeitsgrenze 720px liegt
     darunter), daher unbedingt statt per eigener Media Query addiert. */
  position: fixed; left: 10px; right: 10px;
  bottom: calc(var(--mobile-tab-bar-height) + var(--mobile-tab-bar-gap) + 10px + env(safe-area-inset-bottom, 0px));
  z-index: var(--z-banner); display: flex; align-items: flex-start; gap: var(--space-6);
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-8) var(--space-10); box-shadow: var(--shadow-lg);
  animation: pwaBannerSlideUp var(--duration-base) ease both;
}
/* Nutzer-Feedback: "banner bitte deutlich groesser machen" -- Icon/Text/Buttons durchgehend
   aufgestockt (vorher wirkte die Leiste neben dem restlichen App-Text unauffaellig klein). */
.pwa-banner > .mdi { font-size: var(--fs-3xl); color: var(--accent); flex-shrink: 0; margin-top: var(--space-1); }
.pwa-banner-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.pwa-banner-text strong { font-size: var(--fs-body); }
.pwa-banner-text span { font-size: var(--fs-lg); color: var(--text-dim); line-height: 1.4; }
.pwa-banner-actions { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-4); flex-shrink: 0; }
.pwa-banner-actions .primary-btn { padding: var(--space-5) var(--space-8); font-size: var(--fs-lg); white-space: nowrap; }
.pwa-banner-actions .icon-btn { width: 32px; height: 32px; font-size: var(--fs-xl); }
@keyframes pwaBannerSlideUp { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }
/* Sicherheitsnetz: bleibt auch bei falsch ausgeloestem JS-Aufruf auf Desktop-Breite unsichtbar. */
@media (min-width: 721px) { .pwa-banner { display: none !important; } }

/* ---------- Toast-Hinweise (Round 11, R? Xbox-Achievement-Redesign) ---------- */
/* Nutzer-Feedback: "kannst du mir diese kleinen benachrichtigungen generell umbauen das man sie
   besser sieht? ich denke da an die alten xbox achievement unlocked animation, nur mit unserem
   logo im kreis" -- Kreis-Badge (Org-Logo/Icon) links + Text rechts statt reiner Text-Pille,
   Einflug mit Overshoot-Bounce, kurzer Lichtstreif ueber die Karte und ein farbiger Ring-Flash um
   den Kreis (gruen = Erfolg, rot = Fehler), genau wie beim Xbox-Achievement-Pop-up. */
.toast-container {
  position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%);
  z-index: var(--z-toast); display: flex; flex-direction: column; gap: var(--space-5); align-items: center;
  pointer-events: none;
}
.toast {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: var(--space-6);
  background: linear-gradient(155deg, var(--panel-2), var(--panel));
  border: 1px solid var(--border); color: var(--text);
  padding: var(--space-4) var(--space-10) var(--space-4) var(--space-4); border-radius: var(--radius-full); font-size: var(--fs-lg); font-weight: var(--fw-semibold);
  box-shadow: 0 10px 28px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.04) inset;
  opacity: 0; transform: translateY(16px) scale(0.9);
  --toast-glow: var(--accent, var(--success));
}
.toast-success { --toast-glow: var(--success); }
.toast-error { --toast-glow: var(--danger); border-color: color-mix(in srgb, var(--danger) 35%, transparent); }
.toast.show { animation: toastPopIn 0.5s var(--ease-spring) forwards; }
.toast.hide { animation: toastPopOut var(--duration-slow) ease forwards; }
@keyframes toastPopIn {
  0% { opacity: 0; transform: translateY(18px) scale(0.85); }
  55% { opacity: 1; transform: translateY(-3px) scale(1.03); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes toastPopOut {
  from { opacity: 1; transform: translateY(0) scale(1); }
  to { opacity: 0; transform: translateY(10px) scale(0.92); }
}
.toast::after {
  content: ""; position: absolute; top: 0; left: -60%; width: 35%; height: 100%;
  background: linear-gradient(115deg, transparent, rgba(255,255,255,0.22), transparent);
  transform: skewX(-20deg); pointer-events: none;
}
.toast.show::after { animation: toastShine 0.9s ease-out var(--duration-fast); }
@keyframes toastShine { from { left: -60%; } to { left: 130%; } }
.toast-badge {
  position: relative; flex-shrink: 0; width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  background: radial-gradient(circle at 32% 28%, rgba(255,255,255,0.16), transparent 65%), var(--panel-2);
  box-shadow: 0 0 0 2px rgba(255,255,255,0.08);
}
.toast-badge::before {
  content: ""; position: absolute; inset: -3px; border-radius: 50%;
  box-shadow: 0 0 0 2px var(--toast-glow), 0 0 14px 2px var(--toast-glow);
  opacity: 0;
}
.toast.show .toast-badge::before { animation: toastRingFlash 0.9s ease-out var(--duration-fast); }
@keyframes toastRingFlash {
  0% { opacity: 0; transform: scale(0.7); }
  35% { opacity: 1; transform: scale(1.12); }
  100% { opacity: 0; transform: scale(1); }
}
.toast-badge img, .toast-badge .org-logo-img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
.toast-badge i, .toast-badge .mdi { font-size: var(--fs-xl); color: var(--toast-glow); }
.toast-text { white-space: nowrap; }
@media (prefers-reduced-motion: reduce) {
  .toast { transition: opacity var(--duration-base) ease; }
  .toast.show, .toast.hide { animation: none; opacity: 1; transform: none; }
  .toast.hide { opacity: 0; }
  .toast::after, .toast.show::after { display: none; }
  .toast-badge::before, .toast.show .toast-badge::before { display: none; }
}
@media (max-width: 720px) {
  /* Ueber der Leiste unten statt darauf (2026-09-15: "die benachrichtigung ist ueber der
     menueleiste unten mache sie weiter hoch"). */
  .toast-container {
    left: 16px; right: 16px; transform: none; width: auto;
    bottom: calc(var(--mobile-tab-bar-height) + var(--mobile-tab-bar-gap) * 2 + var(--space-6));
  }
  .toast { width: 100%; max-width: none; box-sizing: border-box; justify-content: center; border-radius: var(--radius-xl); }
}

/* ---------- Chatrooms (R50) ---------- */
/* R50 Nachtrag: doppelt so grosse Kachel (Nutzer-Feedback) -- statt einer einzeiligen Zeile jetzt
   ein kleiner Steckbrief pro Raum mit Mitglieder-Avataren + Zeitangabe, analog zur Teilnehmerzeile
   in einer Event-Kachel. */
/* R50 Nachtrag: gleiche Zweiteilung wie eine Agenda-Kachel (.agenda-item: .agenda-header-bar +
   .agenda-content-row) statt eines einzelnen einfarbigen Blocks -- Ueberschrift bekommt einen
   eigenen, etwas dunkleren Kopfbalken, darunter der Inhaltsbereich (Karten-Grundfarbe) mit
   optionalem Spiel-Icon links + Infos rechts daneben (Nutzer-Feedback). */
.chatroom-row {
  position: relative;
  display: flex; flex-direction: column; border-radius: 10px;
  cursor: pointer; border: 1px solid transparent; margin-bottom: var(--space-3); background: var(--panel-2);
  overflow: hidden;
}
.chatroom-row:hover { border-color: var(--border); }
.chatroom-row.active { border-color: var(--accent); background: var(--aktiv-kachel); color: var(--aktiv-text); }
.chatroom-row.chatroom-row-inactive { opacity: 0.6; }
/* Der obere Bereich der Raumkachel ist durchscheinend statt deckend (2026-09-16: "sie sollte so
   sein wie bei den agenda kacheln blurred nicht dunkel undurchsichtig") -- dieselbe Mischung wie
   der Kopfbalken einer Agenda-Kachel mit Titelbild, damit ein Video-/Spielbild dahinter sichtbar
   bleibt. */
.chatroom-row-header-bar {
  display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-4) var(--space-6);
  /* Derselbe Ton wie die Faehnchenleiste darueber -- oben ein Block, unten der Inhalt (2026-09-16). */
  background: var(--inhalt-kachel-hover);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.chatroom-row-header-top { display: flex; align-items: center; gap: var(--space-4); }
.chatroom-row-lock { color: var(--text-dim); flex-shrink: 0; }
.chatroom-row-topic { flex: 1; min-width: 0; font-weight: var(--fw-semibold); font-size: var(--fs-lg); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chatroom-row-joined-badge {
  display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-2xs); font-weight: var(--fw-semibold);
  /* --success-text statt --success: als Schrift braucht Gruen auf Papier den dunkleren Ton. */
  color: var(--success-text); white-space: nowrap; flex-shrink: 0;
}
.chatroom-row-joined-badge .mdi { font-size: var(--fs-lg); }
/* R50 Nachtrag: Loeschen direkt auf der Kachel (fuer Admins/Ersteller), damit ein Raum auch ohne
   vorherigen Beitritt entfernt werden kann -- z.B. ein passwortgeschuetzter Fremd-Raum. */
.chatroom-row-delete-btn { width: 22px; height: 22px; flex-shrink: 0; }
.chatroom-row-delete-btn:hover { background: transparent; color: var(--danger); }
/* R50 Nachtrag: die Inaktiv/Loeschungs-Zeile wandert unter den Titel in die Kopfzeile (statt im
   Inhaltsbereich zu stehen) -- Gelb bleibt hier die Ausnahmefarbe fuer den drohenden Loesch-
   Countdown, siehe Bugfix-Kommentar weiter unten bei .chatroom-row-time. */
.chatroom-row-header-inactive { font-size: var(--fs-xs); color: var(--highlight); padding-left: var(--space-12); }
.chatroom-row-content-row { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-5) var(--space-6); }
.chatroom-row-top-line { display: flex; align-items: center; gap: var(--space-5); }
/* Groesser als der 36px-Standard-Thumb der Agenda -- passt zur doppelt so grossen Chatroom-
   Kachel, wiederverwendet aber dieselbe Bild/Icon-Platzhalter-Logik (.agenda-thumb). */
.chatroom-row-thumb.agenda-thumb { width: 44px; height: 44px; border-radius: var(--radius-md); font-size: var(--fs-2xl); flex-shrink: 0; }
/* Kein Spiel verknuepft -> kein Icon-Element im Markup -- .chatroom-row-info ruecht dadurch
   von selbst an die Stelle, an der sonst das Icon stuende (kein Flex-Gap ohne Icon-Sibling). */
.chatroom-row-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-2); }
/* R50 Nachtrag: Mitglieder-Avatare stehen jetzt in einer eigenen Zeile unter Bild/Spiel statt
   daneben (Nutzer-Feedback). */
.chatroom-row-avatars-line { display: flex; align-items: center; gap: var(--space-6); }
/* Die Avatare ueberlappen sich (margin-right: -8px), der letzte zieht den Text sonst an sich heran
   -- der Ausgleich hier haelt den Abstand zum "x Mitglieder" (2026-09-16). */
.chatroom-row-avatars { display: flex; align-items: center; margin-right: var(--space-4); }
.chatroom-row-avatar {
  width: 26px; height: 26px; border-radius: 50%; object-fit: cover;
  border: 2px solid var(--panel-2); margin-right: -8px;
}
.chatroom-row-avatar-placeholder {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--panel); font-size: var(--fs-lg); color: var(--text-dim);
}
.chatroom-row-meta { font-size: var(--fs-xs); color: var(--text-dim); }
/* R50 Nachtrag Bugfix: war vertauscht -- die neutrale "Erstellt vor X"-Zeitangabe eines aktiven
   Raums lief bisher in Gelb (Warnfarbe), waehrend die tatsaechlich dringliche Loeschungs-Countdown-
   Zeile eines inaktiven Raums gedimmt war. Gelb ist jetzt der Ausnahmefall (Loeschung droht) --
   dieser Fall wird seit dem Nachtrag oben ueber .chatroom-row-header-inactive gerendert, hier
   bleibt nur noch die neutrale "Erstellt vor X"-Variante fuer aktive Raeume. */
.chatroom-row-time { font-size: var(--fs-xs); color: var(--text-dim); }
.chatroom-row-game {
  display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--fs-xs); color: var(--text-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.chatroom-row-game .mdi { color: var(--accent); font-size: var(--fs-md); }

/* Status-Zeile im rechten Info-Panel: pulsierender Punkt statt Text-Emoji, siehe
   renderChatRoomDurationInfo(). Gruen+pulsierend = aktiv (Mitglieder drin), grau = inaktiv/leer.
   R50 Nachtrag Bugfix: die vorherige Version teilte sich die box-shadow-basierte .unread-badge-
   Animation (fuer einen ROTEN Punkt gedacht) -- auf dem gruenen Punkt wirkte der farblich
   unpassende rote Ring wie ein hektisches/zu schnelles Blinken. Jetzt ein eigener, farblich
   passender Ring per ::after (transform/opacity statt box-shadow -- guenstiger fuers Compositing,
   kein Repaint-Ruckeln, das ebenfalls als "komisches Blinken" wahrgenommen werden kann). */
.chatroom-status-row { display: flex; align-items: center; gap: var(--space-4); font-size: var(--fs-md); }
.chatroom-active-dot {
  position: relative; display: inline-block; width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0;
  background: var(--text-dim);
}
.chatroom-active-dot.chatroom-active-dot-active { background: var(--success); }
.chatroom-active-dot.chatroom-active-dot-active::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%; background: var(--success);
  animation: chatroom-dot-ping 1.8s cubic-bezier(0,0,0.2,1) infinite;
}
@keyframes chatroom-dot-ping {
  0% { transform: scale(1); opacity: 0.6; }
  75%, 100% { transform: scale(2.4); opacity: 0; }
}
.chatroom-lock-row { display: flex; align-items: center; gap: var(--space-3); margin-top: var(--space-2); font-size: var(--fs-sm); color: var(--text-dim); }
.chatroom-members-heading-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.chatroom-members-heading-row h4 { margin: 0; }

/* R50 Nachtrag: eigener "+"-Button oben rechts im Info-Panel statt neben der Mitglieder-
   Ueberschrift (Nutzer-Feedback) -- gilt fuer das ganze Panel, nicht nur den Mitglieder-Block. */
.chatroom-info-panel-top { display: flex; justify-content: flex-end; margin-bottom: var(--space-1); }
/* R50 Nachtrag: Karten-Optik pro Abschnitt (Status/Anfragen/Spiel/Mitglieder/Passwort) statt
   reiner Textbloecke -- gleiches Karten-Muster (var(--panel-2), abgerundet) wie an anderen Stellen
   der App (Chatroom-Kacheln, Chat-Nachrichten usw.), siehe Nutzer-Feedback "etwas schoener mit
   Backgrounds usw. wie der Rest der Seite". */
#chatroom-content .boards-info-panel { display: flex; flex-direction: column; gap: var(--space-5); }
#chatroom-content .boards-subtab-panel {
  background: var(--panel-2); border-radius: var(--radius); padding: var(--space-5) var(--space-6); margin: 0;
}
#chatroom-content .boards-subtab-panel h4 { margin: 0 0 var(--space-3); }
/* R50 Nachtrag: Spiel nachtraeglich verknuepfen/aendern -- Stift-Icon neben der "Spiel"-
   Ueberschrift oeffnet #chatroom-link-game-modal (Nutzer-Feedback). */
.chatroom-section-heading-row { display: flex; align-items: center; justify-content: space-between; }
.chatroom-section-heading-row h4 { margin: 0; }
.chatroom-section-heading-row .icon-btn { width: 22px; height: 22px; }
/* R50 Nachtrag: die Beitritts-Anfragen-Karte wird ohnehin nur eingeblendet, wenn tatsaechlich
   offene Anfragen vorliegen (siehe loadChatRoomJoinRequests()) -- ein oranger Rahmen zieht die
   Aufmerksamkeit dorthin, statt optisch wie jede andere Karte unterzugehen (Nutzer-Feedback). */
#chatroom-join-requests-section { border: 1.5px solid var(--warning); }
.chatroom-game-select-row { display: flex; gap: var(--space-3); align-items: center; }
.chatroom-game-select-row select { flex: 1; }
.chatroom-game-display { display: flex; align-items: center; gap: var(--space-4); }
.chatroom-game-display-icon { width: 28px; height: 28px; border-radius: var(--radius-sm); object-fit: cover; flex-shrink: 0; }
.chatroom-game-display-title { font-size: var(--fs-md); font-weight: var(--fw-semibold); }
/* R50 Nachtrag: Passwort-Anzeige mit Schluessel-Icon + Monospace-Chip statt reinem Fliesstext --
   liest sich wie ein Zugangscode statt wie normaler Satztext. */
.chatroom-password-display {
  display: inline-flex; align-items: center; gap: var(--space-3); background: var(--panel);
  border-radius: var(--radius-sm); padding: var(--space-3) var(--space-5); font-family: "Courier New", ui-monospace, monospace;
  font-size: var(--fs-lg); letter-spacing: 0.06em; color: var(--accent);
}
.chatroom-password-display .mdi { color: var(--text-dim); font-size: var(--fs-body); flex-shrink: 0; }

/* R50 Nachtrag: Chat links soll die volle verfuegbare Hoehe ausfuellen statt der festen
   320px-Deckelung aus der Basis-.chat-messages-Regel (gleiches Muster wie .event-preview-chat). */
/* Bugfix (Nutzer-Feedback: "auf Full-HD-Monitoren unten abgeschnitten"): ohne min-height:0 kann
   ein flex:1-Kind in einer Spalte nicht unter seine eigene Inhaltshoehe schrumpfen -- die feste
   Spaltenhoehe von #tab-chatrooms (calc(100vh - 253px)) reichte seit dem Watch-Together-Player
   (eigener fester Block ueber der Nachrichtenliste) auf kleineren Bildschirmen nicht mehr, das
   Formular am unteren Rand wurde dadurch von .boards-main's overflow:hidden abgeschnitten statt
   dass die Nachrichtenliste einfach kleiner wird. */
.chatroom-chat-col .chat-messages { flex: 1; min-height: 0; max-height: none; }
.chatroom-chat-col.chat-drop-active { outline: 2px dashed var(--accent); outline-offset: -4px; background: var(--panel-2); }
/* Nutzer-Feedback: "in watch together räumen only sollte der chat wenn er nicht mehr auf den
   screen passt klein nach rechts in die seitenleiste verschoben werden. die video control leiste
   sollte niemals verschwinden." -- #chatroom-chat-block (Ueberschrift+Suche+Verlauf+Formular als
   EINE Einheit) wird bei zu wenig Platz per JS in #chatroom-watch-chat-sidebar-slot umgehaengt
   (siehe layoutChatRoomWatchArea() in app.js). Normalzustand: identisches Flex-Verhalten wie vorher
   (die vier Kinder waren zuvor direkte Kinder von .chatroom-chat-col). */
.chatroom-chat-block { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.chatroom-watch-chat-sidebar-slot { display: flex; flex-direction: column; min-height: 0; }
.chatroom-watch-chat-sidebar-slot:empty { display: none; }
/* Die Mindesthoehe des Chats steht weiter unten bei .chatroom-watch-chat-sidebar-slot:not(:empty)
   -- hier stand frueher "min-height: 0" und hat sie (hoehere Spezifitaet) ausgehebelt, der Chat
   blieb dadurch klein (2026-09-16). */
#chatroom-content .boards-info-panel:has(.chatroom-watch-chat-sidebar-slot:not(:empty)) .chatroom-watch-chat-sidebar-slot {
  flex: 1;
}
/* Kompaktere Optik, wenn der Chat in der schmalen 250px-Seitenleiste sitzt statt in der breiten
   Hauptspalte -- kleinere Schrift/Abstaende, damit trotzdem noch etwas Verlauf sichtbar bleibt. */
.chatroom-chat-block-compact { font-size: var(--fs-sm); }
.chatroom-chat-block-compact .boards-chat-heading { font-size: var(--fs-md); padding: var(--space-3) 0 var(--space-2); }
.chatroom-chat-block-compact .boards-chat-heading .icon-btn { width: 26px; height: 26px; }
.chatroom-chat-block-compact .chat-messages { padding: var(--space-2) 0; }
.chatroom-chat-block-compact .chat-form { padding: var(--space-2) 0 0; }
/* R55: dieselbe Drop-Hervorhebung jetzt auch fuer die anderen Chat-/Kommentarbereiche
   (globaler Chat, Event-Kommentare, Compendium-Diskussion je Reiter). */
.event-preview-chat.chat-drop-active, .boards-detail-right.chat-drop-active {
  outline: 2px dashed var(--accent); outline-offset: -4px; background: var(--panel-2);
}

.chatroom-invite-list, .chatroom-join-requests-list {
  display: flex; flex-direction: column; gap: var(--space-3); max-height: 180px; overflow-y: auto;
}
.chatroom-invite-row, .chatroom-join-request-row {
  display: flex; align-items: center; gap: var(--space-4); padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm); background: var(--inhalt-kachel);
}
.chatroom-join-request-row-name { flex: 1; font-size: var(--fs-md); }
/* R50 Nachtrag: Annehmen/Ablehnen ausgeschrieben statt reiner Icon-Buttons (Nutzer-Feedback) --
   Text-Feld nimmt den restlichen Platz, die Buttons ruecken dadurch automatisch rechtsbuendig. */
.chatroom-invite-row-text { flex: 1; min-width: 0; }
.chatroom-invite-row-actions { display: flex; gap: var(--space-3); flex-shrink: 0; }
/* Personen-Auswahl beim Raum-Erstellen: gleiche An/Aus-Pillen wie der Medien-Scope-Filter
   (.filter-btn/.filter-btn-check) statt nackter Checkbox+Label-Zeilen -- passt zum Passwort-Toggle
   direkt darueber, der aus demselben Grund umgebaut wurde.
   R50 Nachtrag: eigener Karten-Hintergrund statt frei schwebender Chips (Nutzer-Feedback "etwas
   schoener gestalten"), Chips zeigen jetzt zusaetzlich den Avatar der Person. */
.chatroom-invite-picker {
  display: flex; flex-wrap: wrap; gap: var(--space-4); max-height: 200px; overflow-y: auto;
  padding: var(--space-5); background: var(--panel-2); border-radius: var(--radius);
}
.chatroom-invite-chip { display: inline-flex; align-items: center; padding: var(--space-3) var(--space-6) var(--space-3) var(--space-3); gap: var(--space-3); }
.chatroom-invite-chip-avatar { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.chatroom-invite-chip-avatar-placeholder { font-size: var(--fs-2xl); line-height: 1; color: inherit; flex-shrink: 0; }
.chatroom-password-toggle-btn { display: inline-flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-2); }
.chatroom-type-toggle { display: flex; gap: var(--space-4); margin-bottom: var(--space-7); }
.chatroom-type-toggle .filter-btn { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: var(--space-3); padding: var(--space-5) var(--space-5); }

/* Nutzer-Feedback: "füge die youtube link einfügen leiste ... etwas mit padding versehen" --
   horizontaler Abstand zu den Chatspalten-Kanten statt randlos anliegend. */
/* Dieselbe Form und Flaeche wie die Steuerleiste darueber (2026-09-15) -- beide sind Leisten unter
   dem Video und sahen vorher unterschiedlich aus. */
/* Die Leisten unter dem Video sind seitlich eingerueckt und tragen eine eigene, etwas hellere
   Flaeche -- dadurch wirken sie wie ein schwebender Block unter dem Bild (2026-09-16: "mache den
   leistenbereich unten schon in einer anderen background farbe aber etwas weiter links und rechts
   eingerueckt damit es besser aussieht als wuerde es so schweben"). */
.chatroom-watch-bar {
  display: flex; align-items: center; gap: var(--space-4); margin: 0 var(--space-7) var(--space-6);
  padding: var(--space-3) var(--space-5);
  background: var(--panel-2);
  border: none; border-radius: var(--radius-full);
}
.chatroom-watch-bar input { border-radius: var(--radius-full); }
.chatroom-watch-bar .text-btn { border-radius: var(--radius-full); }
.chatroom-watch-bar-icon { color: var(--youtube); font-size: var(--fs-2xl); flex-shrink: 0; }
/* Eingereihte Videos, von links nach rechts in Abspielreihenfolge (2026-09-16). Bewusst flach:
   eine schmale Leiste unter dem Video, kein zweiter grosser Block. */
.chatroom-watch-queue {
  display: flex; gap: var(--space-4); overflow-x: auto; padding: 0 var(--space-7) var(--space-5);
  scrollbar-width: thin;
}
.chatroom-watch-queue-eintrag {
  display: flex; align-items: center; gap: var(--space-4); flex-shrink: 0; max-width: 280px;
  padding: var(--space-3) var(--space-5) var(--space-3) var(--space-4);
  background: var(--inhalt-kachel); border-radius: var(--radius-md); cursor: grab;
  transition: background var(--duration-fast) ease, transform var(--duration-fast) ease,
    opacity var(--duration-fast) ease;
}
.chatroom-watch-queue-eintrag:hover { background: var(--panel-hover); }
/* Wie beim Verschieben einer Dashboard-Kachel: der gezogene Eintrag bleibt blass an seinem Platz,
   die anderen ruecken schon beim Ziehen zur Seite -- keine Linie am Ziel (2026-09-16). */
.chatroom-watch-queue-zieht { opacity: 0.4; transform: scale(0.97); cursor: grabbing; }
.chatroom-watch-queue-nummer {
  font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--text-dim);
  font-variant-numeric: tabular-nums; flex-shrink: 0;
}
.chatroom-watch-queue-bild { width: 80px; height: 45px; object-fit: cover; border-radius: var(--radius-sm); flex-shrink: 0; }
.chatroom-watch-queue-bild-leer {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--panel); color: var(--text-dim);
}
.chatroom-watch-queue-titel {
  font-size: var(--fs-sm); color: var(--text); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chatroom-watch-queue-weg { width: 22px; height: 22px; flex-shrink: 0; color: var(--text-dim); }
/* Watch-Together-Hinweise im Chat sind reiner Text ohne Avatar (2026-09-16) -- ohne Avatar bliebe
   sonst dessen Spalte leer. */
.chat-message-hinweis { grid-template-columns: 1fr; }
.chat-message-hinweis .chat-author { color: var(--text-dim); font-weight: var(--fw-medium); }
.chatroom-watch-queue-weg:hover { color: var(--danger); background: transparent; }
.chatroom-watch-bar input {
  flex: 1; background: var(--panel); border: 1px solid var(--border); color: var(--text);
  padding: var(--space-4) var(--space-5); border-radius: var(--radius-sm); font-size: var(--fs-md); font-family: inherit;
}
.chatroom-watch-bar input:focus { outline: none; border-color: var(--accent); }
.chatroom-watch-bar .text-btn { flex-shrink: 0; white-space: nowrap; }
/* Nutzer-Feedback: "bitte das video auf die gesamte chat breite anpassen" -- explizit NICHT nur
   auf Mobil, auch am Desktop soll das Video immer die volle Spaltenbreite ausnutzen ("ich meinte
   nicht nur das sich das yt video mobil anpassen soll sondern auch am desktop auf die gesamte
   spaltenbreite"). Frueher stand hier ein min(..., 89vh)-Deckel gegen zu hohe Videos auf sehr
   breiten/kurzen Fenstern -- bewusst entfernt, echtes 16:9 aus der Breite ist wichtiger als eine
   Hoehenbegrenzung; die Spalte scrollt ohnehin bei Bedarf. */
/* Volle Breite der Spalte: der fruehere 8px-Abzug liess links und rechts einen dunklen Streifen
   stehen (2026-09-16: "das watch together video ist nicht bis zum rand ausgefuellt"). */
.chatroom-watch-player-wrap { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; border-radius: var(--radius); overflow: hidden; margin: 0 auto var(--space-4); }
/* Nutzer-Feedback: "kannst du ein placeholder video bild einfügen wenn kein video läuft?" --
   gleiche Groesse/Position wie .chatroom-watch-player-wrap (16:9, randlos zur Bar darunter), damit
   der Raum nicht wie kaputt/leer wirkt, solange noch niemand einen Link gesetzt hat. Flach unten
   (border-radius nur oben), damit es sich nahtlos mit der darunter folgenden, gemergten Link-Leiste
   verbindet (.chatroom-watch-bar-merged, siehe updateChatRoomWatchUI() in app.js). */
.chatroom-watch-placeholder {
  width: 100%; aspect-ratio: 16 / 9; margin: 0 auto 0; background: var(--panel-2);
  border-radius: var(--radius) var(--radius) 0 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--space-4); color: var(--text-dim);
}
.chatroom-watch-placeholder .mdi { font-size: var(--fs-display); opacity: 0.5; }
.chatroom-watch-placeholder span { font-size: var(--fs-md); }
.chatroom-watch-player-wrap #chatroom-watch-player,
.chatroom-watch-player-wrap iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: none; }
/* YouTube waehlt die Aufloesung nach der Spielergroesse -- deshalb rendert das Iframe intern in
   1920x1080 und wird auf die tatsaechliche Breite heruntergerechnet (2026-09-16). Nur fuer
   YouTube: Twitch bringt seine eigene Leiste mit, die dabei mitskaliert wuerde. */
/* Nur am Desktop: auf dem Handy waere 1080p verschenkte Bandbreite (das Display zeigt ohnehin
   weniger), dort bleibt es bei der natuerlichen Groesse. */
@media (min-width: 881px) {
  .chatroom-watch-container[data-video-source="youtube"] .chatroom-watch-player-wrap iframe {
    /* !important, weil die YouTube-API dem Iframe eigene Inline-Groessen schreibt -- ohne das
       gewinnen die und der Player rendert wieder in Kastengroesse. */
    width: 1920px !important; height: 1080px !important; inset: 0 auto auto 0;
    transform: scale(var(--yt-skala, 1)); transform-origin: top left;
  }
}
.chatroom-watch-click-catcher { position: absolute; inset: 0; z-index: 2; cursor: pointer; }
/* Nutzer-Feedback: "wenn ich einen raum joine und bereits leute etwas anschauen das es bei mir
   synced wird mit einem schönen hinweis in der mitte, weil ich komme immer rein und das video
   startet bei 0 während die anderen woanders sind" -- deckt den Player waehrend
   warmUpChatRoomWatchPlayer() komplett zu (siehe app.js), damit der kurze autoplay-Fehlstart bei
   Position 0 nie sichtbar wird, bevor der echte Sprung zur synchronisierten Position passiert. */
.chatroom-watch-sync-overlay {
  position: absolute; inset: 0; z-index: 3; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--space-6);
  background: rgba(10, 10, 16, 0.78); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  color: var(--text-on-color); font-size: var(--fs-md); font-weight: var(--fw-semibold); text-align: center; padding: 0 var(--space-8);
}
.chatroom-watch-sync-spinner {
  width: 32px; height: 32px; border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, 0.25); border-top-color: var(--accent);
  animation: chatroom-watch-sync-spin 0.8s linear infinite;
}
@keyframes chatroom-watch-sync-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .chatroom-watch-sync-spinner { animation: none; }
}
/* Twitch bringt seine eigene Steuerleiste (Play/Pause/Seek/Lautstaerke/CC/Qualitaet) mit -- der
   Click-Catcher wuerde die blockieren, siehe applyWatchPlayerSourceUi() in app.js fuer das
   Ein-/Ausblenden je nach room.video_source. */
.chatroom-watch-container[data-video-source="twitch"] .chatroom-watch-click-catcher { display: none; }
/* Twitch bringt eine vollwertige eigene Steuerleiste (Play/Pause/Seek/Lautstaerke/Untertitel/
   Qualitaet) im Iframe selbst mit -- unsere Leiste waere nur eine funktionslose Dopplung. */
.chatroom-watch-container[data-video-source="twitch"] .chatroom-watch-controls { display: none; }
/* Nutzer-Feedback: eigene Steuerleiste AUSSERHALB des Videos statt YouTubes eingebauter Leiste
   (die per playerVars.controls=0 ausgeblendet wird). */
/* 2026-09-15 ("mache die controls noch schoener die unten sind. etwas mehr am design das wir
   gewaehlt haben"): dieselbe Glasflaeche wie Fenster und Kacheln, runde Form, und die Zeitleiste
   ist nicht mehr der nackte Browser-Regler, sondern eine duenne Spur mit Accent-Fuellung. */
.chatroom-watch-controls {
  display: flex; align-items: center; gap: var(--space-4); margin: var(--space-4) var(--space-7) var(--space-4);
  padding: var(--space-3) var(--space-5);
  background: var(--panel-2);
  /* Ohne Kontur (2026-09-16: "bitte die video controls nicht so komisch umranden") -- die Flaeche
     allein traegt die Leiste. */
  border: none; border-radius: var(--radius-full);
}
.chatroom-watch-controls .icon-btn { width: 32px; height: 32px; border-radius: 50%; }
.chatroom-watch-controls .icon-btn:hover { background: var(--knopf-hover); }
#chatroom-watch-playpause-btn {
  background: var(--accent); color: var(--text-on-color); width: 34px; height: 34px;
}
#chatroom-watch-playpause-btn:hover { background: var(--accent); filter: brightness(1.1); }
/* Das Qualitaets-Menue ist entfallen (2026-09-16) -- siehe Kommentar in index.html. */
.chatroom-watch-cc-btn-active, #chatroom-watch-cc-btn.active { color: var(--accent); }
.chatroom-watch-controls .icon-btn { flex-shrink: 0; }
.chatroom-watch-time { font-size: var(--fs-xs); color: var(--text-dim); font-variant-numeric: tabular-nums; flex-shrink: 0; min-width: 32px; text-align: center; }
/* Der gefuellte Teil links vom Griff kommt aus einem Verlauf: Chromium kennt nichts wie
   ::-moz-range-progress, und "accent-color" faerbt nur den Systemregler. Den Prozentwert setzt
   setzeWatchSeekFortschritt() in chatrooms.js. */
.chatroom-watch-seek-bar {
  flex: 1; cursor: pointer; appearance: none; -webkit-appearance: none; background: transparent;
  height: 16px; margin: 0;
}
.chatroom-watch-seek-bar::-webkit-slider-runnable-track {
  height: 4px; border-radius: var(--radius-full);
  background: linear-gradient(to right, var(--accent) var(--fortschritt, 0%), var(--panel-hover) var(--fortschritt, 0%));
}
.chatroom-watch-seek-bar::-webkit-slider-thumb {
  -webkit-appearance: none; width: 12px; height: 12px; margin-top: -4px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--panel); box-shadow: var(--shadow-sm);
  transition: transform var(--duration-fast) ease;
}
.chatroom-watch-seek-bar:hover::-webkit-slider-thumb { transform: scale(1.15); }
.chatroom-watch-seek-bar::-moz-range-track { height: 4px; border-radius: var(--radius-full); background: var(--panel-hover); }
.chatroom-watch-seek-bar::-moz-range-progress { height: 4px; border-radius: var(--radius-full); background: var(--accent); }
.chatroom-watch-seek-bar::-moz-range-thumb {
  width: 12px; height: 12px; border-radius: 50%; background: var(--accent); border: 2px solid var(--panel);
}

/* Nutzer-Feedback: Lautstaerke-Button oeffnet einen Slider statt nur stumm/laut umzuschalten --
   rein lokal (nie synchronisiert), deshalb kein Server-Roundtrip noetig. */
.chatroom-watch-volume-wrap { position: relative; flex-shrink: 0; }
.chatroom-watch-volume-popover {
  position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-5) var(--space-3); box-shadow: var(--shadow-md); z-index: 5;
}
.chatroom-watch-volume-slider {
  writing-mode: vertical-lr; direction: rtl; appearance: slider-vertical;
  width: 6px; height: 90px; accent-color: var(--accent); cursor: pointer;
}

/* Der Chat ist in Watch-Together-Raeumen ein eigener Abschnitt am Ende der rechten Spalte
   (2026-09-15: "mache in der rechten spalte die chat und info switches zu eigenen kategorien ohne
   switch ... dann in einem weiteren abschnitt den chat"). Er bekommt dort eine eigene Hoehe, statt
   die ganze Spalte auszufuellen -- sonst zoege sich deren Flaeche bis zum unteren Bildrand
   ("im watch together raum ist auch der background der rechten leiste bis ganz unten"). */
.chatroom-watch-chat-sidebar-slot:not(:empty) {
  /* Hoehe wie vor dem 16.09. -- entscheidend war etwas anderes: der Nachrichtenverlauf selbst
     wuchs nicht mit ("ich meinte das sich die flaeche vom chat erhoehen sollte"). .chat-messages
     hat app-weit "max-height: 320px"; die Ausnahme dafuer galt nur fuer den Chat in der
     Hauptspalte (.chatroom-chat-col), nicht fuer den in der Seitenleiste. */
  display: flex; flex-direction: column; flex: 1 1 auto; min-height: 420px; max-height: 72vh;
}
.chatroom-watch-chat-sidebar-slot .chat-messages { flex: 1; min-height: 0; max-height: none; }

/* Die dunkle Inhaltsflaeche von Raeumen, Notizen und Alben ist oben links eckig (2026-09-15:
   "das die linke obere ecke nicht abgerundet ist damit es besser aussieht") -- dort stoesst sie an
   die Ueberschrift und die Spalte links, und die Rundung liess sie davon abrutschen. Und ueberall
   derselbe, etwas groessere Innenabstand, damit der Inhalt in allen drei Ansichten gleich weit vom
   Rand beginnt ("die innenabstaende wo der inhalt beginnt etwas vergroessern und ueberall gleich
   machen"). */
#chatroom-content .chatroom-chat-col,
#wiki-page-view .wiki-editor-card,
#media-game-detail .boards-detail-left-col {
  border-top-left-radius: 0;
}
#media-game-detail .boards-detail-left-col { padding: var(--space-8); }
#chatroom-content .chatroom-chat-col :is(.boards-chat-heading, .chat-messages, .chat-form) {
  padding-inline: var(--space-8);
}
#wiki-page-view .wiki-editor-topbar { padding: var(--space-4) var(--space-8); }
#wiki-page-view .wiki-editor-mount { padding: var(--space-6) var(--space-8) var(--space-8); }
/* Die Werkzeugleiste steht mittig und etwas groesser ("bitte die leiste mit den werkzeugen in
   notizen zentrieren und etwas vergroessern"). */
#wiki-page-view .wiki-editor-toolbar { flex: 1; justify-content: center; gap: var(--space-2); }
#wiki-page-view .wiki-tb-btn { width: 32px; height: 32px; font-size: var(--fs-xl); }

/* Nutzer-Feedback: Video vergroessern (In-Page-"Theater"-Modus, kein OS-Vollbild) + echtes
   Vollbild ueber die Fullscreen-API (siehe #chatroom-watch-fullscreen-btn in app.js). */
/* Nutzer-Feedback: "die lightbox ... sollte die ganze restliche seite im background blurren --
   derzeit ist es nur komisches viereck um das video danach black und dann sieht man die seite
   noch" -- vorher hatte NUR der 40px-eingerueckte Kasten selbst einen eigenen (kleinen) Blur-
   Hintergrund, dahinter lag bloss eine halbtransparente schwarze Flaeche (0.75 Deckkraft), durch
   die man die eigentliche Seite noch durchschimmern sah. Jetzt traegt DIESER volle
   Viewport-Backdrop selbst das grossflaechige Blur-Bild, der Kasten daaruber ist transparent --
   dadurch wirkt es wie EIN durchgehender verschwommener Hintergrund von Bildschirmkante bis zum
   Video, statt zweier sichtbar getrennter Ebenen. */
/* Solange der Kino-Modus laeuft, traegt kein Kasten der Seite mehr den Glas-Effekt: ein
   backdrop-filter macht sein Element zum Bezugsrahmen fuer "position: fixed" darin -- das Video
   klebte dadurch im Raum-Kasten fest, statt den Bildschirm zu fuellen (2026-09-15). */
body.watch-kino .widget, body.watch-kino .boards-main, body.watch-kino .boards-sidebar,
body.watch-kino .app-nav, body.watch-kino .app-context {
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
/* Zweiter Teil desselben Problems: .tab-content ist "position: relative; z-index: 1" und damit ein
   eigener Stapel -- alles darin bleibt unter der Menuespalte (z-index 11), egal wie hoch der
   z-index des Videos ist. Im Kino-Modus wandert der Reiter deshalb selbst nach oben. */
body.watch-kino #tab-chatrooms { z-index: var(--z-theater); }

/* Der Hintergrund wird im Kino-Modus komplett weggeblendet (2026-09-15: "dunkle und blurre den
   hintergrund komplett ab") -- die Seite selbst kommt per backdrop-filter unscharf und dunkel
   dahinter, darueber liegt die eigene, ebenfalls weichgezeichnete Flaeche aus dem Videobild. */
.chatroom-watch-expand-backdrop {
  position: fixed; inset: 0; z-index: calc(var(--z-theater) - 10); background-color: rgba(0, 0, 0, 0.9);
  background-image: var(--watch-bg-image, none); background-size: cover; background-position: center;
  background-blend-mode: overlay; filter: blur(50px) brightness(0.35);
  -webkit-backdrop-filter: blur(24px) brightness(0.3); backdrop-filter: blur(24px) brightness(0.3);
}
/* Das Bild darf ueber die beiden Menuespalten links laufen ("mache das video im cinema mode auch
   ruhig ueber die menueleiste 2 links") und nutzt die Flaeche bis kurz vor den Bildschirmrand. */
.chatroom-watch-container.chatroom-watch-expanded {
  position: fixed; inset: 0; z-index: var(--z-theater);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-4);
  background: transparent; padding: var(--space-6); overflow: hidden;
  /* Was neben dem Video noch in die Hoehe muss: Steuerleiste, Abstand, Innenabstaende. Steht als
     Variable da, weil die Breite unten damit rechnet. */
  --kino-rand: 96px;
}
/* Das Seitenverhaeltnis darf NIE kippen (2026-09-15 gemeldet: "je nachdem wie ich den browser
   verschiebe wird das video abgeschnitten"). Der frueher genutzte Weg -- height: 100% plus
   aspect-ratio -- bricht genau dann, wenn die daraus errechnete Breite groesser als der Kasten
   ist: CSS rechnet die Hoehe dann NICHT zurueck (aspect-ratio greift nur, wenn die andere Seite
   "auto" ist), das Video lief seitlich aus dem overflow: hidden heraus. Jetzt gibt die Breite den
   Ton an und ist selbst gedeckelt: entweder volle Breite oder das, was die verfuegbare Hoehe im
   16:9 hergibt -- je nachdem, was kleiner ist. */
.chatroom-watch-container.chatroom-watch-expanded .chatroom-watch-player-wrap {
  position: relative; z-index: 1; flex: 0 0 auto;
  width: min(100%, calc((100vh - var(--kino-rand)) * 16 / 9));
  height: auto; max-height: calc(100vh - var(--kino-rand));
  aspect-ratio: 16 / 9; margin: 0; box-shadow: var(--shadow-lg);
}
.chatroom-watch-expand-close {
  position: absolute; top: var(--space-8); right: var(--space-8); z-index: 3;
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--on-image-bg); color: var(--text-on-color); font-size: var(--fs-xl);
}
.chatroom-watch-expand-close:hover { background: var(--on-image-bg-hover); color: var(--text-on-color); }
.chatroom-watch-container.chatroom-watch-expanded .chatroom-watch-controls {
  position: relative; z-index: 1; flex-shrink: 0; margin: 0;
  width: min(100%, calc((100vh - var(--kino-rand)) * 16 / 9));
}
/* Echtes Vollbild heisst hier auch echtes Vollbild: schwarzer Grund, kein weichgezeichnetes Bild
   drumherum und kein Innenabstand (2026-09-16: "fullscreen haette ich gerne einen echten fullscreen
   und nicht das video mit blurred aussenrum"). */
.chatroom-watch-container:fullscreen {
  background: #000; padding: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.chatroom-watch-container:fullscreen .chatroom-watch-player-wrap {
  /* Gleiche Rechnung wie im Kino-Modus: die Breite gibt den Ton an und ist von der Hoehe gedeckelt,
     damit 16:9 in jedem Bildschirmformat erhalten bleibt. */
  position: relative; z-index: 1; flex: 0 0 auto;
  width: min(100%, calc(100vh * 16 / 9)); height: auto; max-height: 100vh;
  aspect-ratio: 16 / 9; margin: 0; border-radius: 0;
}
/* Die Steuerleiste schwebt im Vollbild ueber dem unteren Bildrand statt darunter zu stehen. */
.chatroom-watch-container:fullscreen .chatroom-watch-controls {
  position: absolute; left: 50%; bottom: var(--space-8); transform: translateX(-50%);
  width: min(720px, calc(100% - var(--space-12) * 2)); margin: 0; z-index: 2;
}
.chatroom-watch-container:fullscreen .chatroom-watch-controls { position: relative; z-index: 1; }
/* R50 Nachtrag: groesserer, ausgeschriebener "Einladen"-Button statt reinem Icon-Button
   (Nutzer-Feedback) -- wiederverwendet .text-btn, etwas grosszuegiger dimensioniert. */
.chatroom-invite-more-btn { font-size: var(--fs-md); padding: var(--space-4) var(--space-7); }
.chatroom-join-request-actions { display: flex; gap: var(--space-2); }

/* ---------- Rooms: Bildschirmfreigabe (WebRTC Screen-Share) ---------- */
/* Nutzer-Feedback: "kannst du in die rooms eine screen share funktion hinzufügen wenn man einen
   raum eröffnet hat" -- eine Kachel pro aktivem Sharer (inkl. eigener Vorschau), typischerweise
   1-4 Stueck gleichzeitig -- flex-wrap statt eines schwereren Grid-Systems reicht dafuer. */
.screenshare-btn-active { background: rgba(220,38,38,0.18); border-color: var(--danger); color: var(--danger); }
.screenshare-grid { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-bottom: var(--space-5); }
.screenshare-tile {
  position: relative; flex: 1 1 260px; max-width: 420px; aspect-ratio: 16 / 9;
  background: #000; border-radius: var(--radius); overflow: hidden;
}
.screenshare-tile video { width: 100%; height: 100%; object-fit: contain; display: block; }
.screenshare-tile-label {
  position: absolute; left: 8px; bottom: 8px; padding: var(--space-1) var(--space-4); border-radius: var(--radius-full);
  background: var(--on-image-bg); color: var(--text-on-color); font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  max-width: calc(100% - 52px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.screenshare-tile-fullscreen-btn {
  position: absolute; right: 8px; bottom: 8px; background: var(--on-image-bg); border: none; color: var(--text-on-color);
}
.screenshare-tile-fullscreen-btn:hover { background: var(--on-image-bg-hover); }
.screenshare-tile:fullscreen { display: flex; align-items: center; justify-content: center; }
.screenshare-tile:fullscreen video { width: auto; height: auto; max-width: 100%; max-height: 100%; }

/* ---------- Settings: LiveKit-Statusanzeige (Integrationen-Tab) ---------- */
/* Nutzer-Feedback: "zeige mir ob alles verbunden ist und läuft" -- Ampel-Farbcodierung analog zu
   den bestehenden Gruen/Rot-Statusfarben im Projekt (#22c55e/--danger, siehe z.B. Online-Punkt,
   RSVP-Buttons). */
.livekit-status-badge {
  display: inline-block; padding: var(--space-3) var(--space-6); border-radius: var(--radius-full); font-size: var(--fs-md); font-weight: var(--fw-semibold);
  background: var(--panel-2); color: var(--text-dim); margin-bottom: var(--space-5);
}
.livekit-status-badge.livekit-status-ok { background: color-mix(in srgb, var(--success) 18%, transparent); color: var(--success); }
.livekit-status-badge.livekit-status-error { background: color-mix(in srgb, var(--danger) 15%, transparent); color: var(--danger); }
.livekit-var-list { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-2); }
.livekit-var-chip {
  font-size: var(--fs-xs); padding: var(--space-2) var(--space-5); border-radius: var(--radius-full); background: var(--panel-2);
  color: var(--text-dim); border: 1px solid var(--border);
}
.livekit-var-chip.livekit-var-set { color: var(--success); border-color: var(--success); }

.backup-restore-warning { color: var(--danger); display: flex; align-items: flex-start; gap: var(--space-3); }
.backup-import-progress-wrap { margin: var(--space-5) 0; }
.backup-import-progress-bar {
  height: 8px; border-radius: var(--radius-full); background: var(--panel-2); overflow: hidden;
}
.backup-import-progress-fill {
  height: 100%; width: 0%; background: var(--accent, var(--accent)); transition: width var(--duration-fast) ease;
}
/* R50 Nachtrag: statt eines kleinen Text-Links jetzt ein vollwertiger, breiter Sekundaer-Button
   (Nutzer-Feedback "größerer Button") -- optisch klar als Alternative zum "Beitreten"-Primaerbutton
   direkt darueber erkennbar (outline statt gefuellt), aber genauso gross/klickbar. */
#chatroom-password-form .form-actions .primary-btn { flex: 1; }
.chatroom-request-btn {
  display: flex; align-items: center; justify-content: center; gap: var(--space-3); flex: 1;
  padding: var(--space-5) var(--space-8); border-radius: var(--radius-md); font-size: var(--fs-md);
  background: transparent; border: 1.5px solid var(--accent); color: var(--accent); cursor: pointer;
  transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.chatroom-request-btn:hover { background: var(--accent); color: var(--text-on-color); }

/* R50 Nachtrag: das Klick-Popover am Ticker-Badge (.ticker-chatroom-badge/.chatroom-reminder-
   popover) entfaellt -- Chatraum-Zugehoerigkeit wird stattdessen direkt in der bereits
   bestehenden Avatar-Hover-Karte mitangezeigt (.avatar-hover-card-chatroom, siehe
   showAvatarHoverCard()) und Personen im selben Raum werden zusaetzlich visuell gruppiert
   (.ticker-chatroom-group) statt per zusaetzlichem Icon-Badge markiert -- ein Konzept statt zwei
   getrennter Popups fuers selbe Feature (Nutzer-Feedback). */
.ticker-chatroom-group {
  display: inline-flex; align-items: center; gap: var(--space-1); padding: var(--space-1) var(--space-4) var(--space-1) var(--space-2);
  border-radius: var(--radius-full); border: 1px solid var(--accent-strong); background: var(--accent-soft);
  animation: ticker-chatroom-group-pulse 2s ease-in-out infinite;
}
@keyframes ticker-chatroom-group-pulse {
  0%, 100% { border-color: var(--accent-strong); }
  50% { border-color: color-mix(in srgb, var(--accent) 95%, transparent); }
}
.avatar-hover-card-chatroom {
  margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--border);
}
.avatar-hover-card-chatroom-heading {
  display: flex; align-items: center; gap: var(--space-3); font-size: var(--fs-sm); font-weight: var(--fw-semibold); margin-bottom: var(--space-3);
}
.avatar-hover-card-chatroom-heading .mdi { color: var(--accent); }
.avatar-hover-card-chatroom-members { display: flex; flex-wrap: wrap; gap: var(--space-2); }
/* R59 Nutzer-Feedback: Text+Icon oben, darunter eine eigene Zeile mit zwei nebeneinander
   liegenden Buttons (Verlassen + Zurueck zum Raum, letzterer auffaelliger/gefuellt) statt eines
   einzelnen Text-Links neben dem Hinweistext. */
.chatroom-reminder-banner {
  position: fixed; left: 50%; transform: translateX(-50%); z-index: var(--z-overlay);
  display: flex; flex-direction: column; gap: var(--space-5); min-width: 360px; max-width: 90vw;
  background: var(--panel); border: 1.5px solid var(--highlight); border-radius: 10px;
  padding: var(--space-5) var(--space-8); font-size: var(--fs-md); box-shadow: var(--shadow-md);
}
.chatroom-reminder-top { display: flex; align-items: center; gap: var(--space-5); }
.chatroom-reminder-banner .mdi-forum-outline { color: var(--highlight); font-size: var(--fs-xl); }
.chatroom-reminder-text { flex: 1; display: flex; flex-direction: column; gap: var(--space-1); }
.chatroom-reminder-unread { color: var(--highlight); font-size: var(--fs-sm); font-weight: var(--fw-semibold); }
.chatroom-reminder-actions { display: flex; gap: var(--space-4); }
.chatroom-reminder-leave-btn, .chatroom-reminder-goto-btn {
  flex: 1; border: none; border-radius: var(--radius-md); padding: var(--space-4) var(--space-6); font-size: var(--fs-md); font-weight: var(--fw-semibold);
  cursor: pointer; transition: background var(--duration-fast) ease, color var(--duration-fast) ease;
}
.chatroom-reminder-leave-btn { background: var(--knopf); color: var(--text-dim); }
.chatroom-reminder-leave-btn:hover { background: var(--danger); color: var(--text-on-color); }
/* Auffaelliger als "Verlassen" -- gefuellt statt nur dezenter Hintergrund, das ist die
   Haupthandlung des Hinweises (Nutzer-Wunsch). */
.chatroom-reminder-goto-btn { background: var(--accent); color: var(--text-on-color); }
/* Mobil-Feedback (Event-Bearbeiten-Maske): Kategorie-Reihe beim Bearbeiten ausblenden, Titelbild
   verkleinern und die zugehoerigen Buttons (Cover-uebernehmen/Spiel-verknuepfen-Toggle/Fotos-
   hinzufuegen) rechts daneben anordnen. Bewusst als letzter Block der Datei platziert --
   .event-cover-photos-row hat weiter oben (R43) bereits eine @media(max-width:560px)-Regel auf
   flex-direction:column, die bei gleicher Spezifitaet sonst per spaeterer Quellreihenfolge gewinnen
   wuerde (bekanntes Cascade-Bug-Pattern dieser Datei). */
@media (max-width: 720px) {
  /* Nutzer-Feedback: "unter titelleiste (event, termin, spiel,...) kategorieleiste ausblenden
     mobil" -- vorher nur die gesperrte Variante (Spiel/Film/Serie-Session vor Aufloesung), jetzt
     grundsaetzlich alle Kategorie-Tabs auf Mobil. */
  #event-modal .modal-category-tabs { display: none; }
  /* Layout-Neugestaltung (Mobil): Grid faellt auf eine Spalte zurueck, die Reihenfolge weicht
     aber bewusst von der reinen Quellreihenfolge ab (siehe order-Regeln unten) -- Nutzer-Feedback:
     "reihenfolge danach: event titel, beschreibung, datum ... ort, tags und teilnehmer". */
  /* Zwei Spuren (2026-09-11: "mobil beim event bearbeiten das verknüpfen button neben den titelbild button
     machen. also rechts daneben da ist noch platz") -- Titelbild (so breit wie seine Knoepfe, die hier
     untereinander stehen) | Verknuepfung, alles andere ueber die volle Breite. Spalte 1 loest sich dafuer
     auf (display:contents): nur die Such-/Knopfzeile steht neben dem Titelbild, Status, "Titelbild
     übernehmen" und Release-Karte laufen darunter ueber die ganze Breite. Neben dem Titelbild nur die
     optionale Verknuepfung (Event/LAN-Party, .event-link-optional aus applyCategoryFieldRules()); die
     Pflicht-Suche bei Spiel/Film/Serie und die Sammlungs-Schnellanlage brauchen die Breite fuer die
     Trefferliste. Der Knopf zeigt hier nur "Verknüpfen" -- die Ueberschrift sagt schon, worum es geht. */
  /* Zeile Titelbild | Verknuepfung, Optionen darunter nebeneinander (2026-09-11, vom Nutzer so gewaehlt:
     "mache die optionen buttons neben verknüpfung und nebeneinander nicht mehr untereinander. mache entfernen
     bei titelbild auch daneben und kürzer mit entf. oder so. benenne verknüpfen button um in spiel film serie";
     alle drei Gruppen nebeneinander haetten ~555px gebraucht, das Handy hat ~370). Die Knoepfe zeigen hier
     ihr Kurzlabel ("Hochladen"/"Ändern", "Entf.", "Spiel/Film/Serie"), das Info-"i" neben der Verknuepfung
     entfaellt -- mit ihm passte die Zeile bei 402px nur auf den Pixel. Gemessen mit TEST=eventvonbis bei
     402/390/375/360px, mit Titelbild. Ist die Suche Pflicht, rueckt die Verknuepfung unter das Titelbild ueber die
     volle Breite. (Eine Statuszeile statt des Knopfs gibt es seit 2026-09-11 nicht mehr -- der Knopf zeigt die
     Verknuepfung selbst, er bleibt also auch verknuepft neben dem Titelbild.) */
  #event-form.two-col-form { grid-template-columns: max-content minmax(0, 1fr); column-gap: var(--space-5); }
  #event-form > *, #event-session-fields > *, #event-form-col1 > * { grid-column: 1 / -1; }
  #event-modal #event-form-col1 { display: contents; }
  #event-modal #event-form-cover-col { grid-column: 1; }
  #event-modal .event-form-cover-actions { flex-direction: row; flex-wrap: nowrap; }
  #event-modal :is(.event-link-btn-lang, .event-title-image-btn-lang, .event-title-image-entf-lang) { display: none; }
  #event-modal :is(.event-link-btn-kurz, .event-title-image-btn-kurz, .event-title-image-entf-kurz) { display: inline; }
  #event-modal #event-catalog-optional-block .event-form-info-icon { display: none; }
  #event-modal.event-header-no-cover #event-form-cover-col { display: none; }
  /* Reihenfolge seit 2026-09-11. Nutzer-Feedback: "schiebe beim bearbeiten des events in der mobilansicht
     den medien reiter ganz nach unten, den titel ganz nach oben und dann in der reihenfolge wie am
     desktop: datum ort tags teilnehmer, das das eben oben steht und nicht ganz unten. beschreibung kann da
     bleiben wo sie war" -- Titel, Titelbild, Verknuepfung, Datum, Optionen, Ort, Tags|Teilnehmer,
     Beschreibung, Nutzer-Verknuepfung (Geburtstag), Medien. Vorher ("reihenfolge danach: event titel,
     beschreibung, datum ... ort, tags und teilnehmer") stand das Datum erst unter der Beschreibung. */
  #event-modal .form-title-row { order: 1; }
  #event-modal #event-form-title-divider { order: 2; }
  #event-modal #event-form-cover-col { order: 3; }
  /* Optionen vor dem Datum ("mache mobil die optionen dann danach. also vor das datum"), seit der Folgerunde
     in der Zeile von Titelbild und Verknuepfung: Verknuepfen-Knopf und Optionen tragen dieselbe order wie
     die Zeile (4), damit das Raster sie dort einsortiert -- alles Weitere aus Spalte 1 (Status, "Titelbild
     übernehmen", Release-Karte, externe Beschreibung) folgt mit 5 darunter. */
  #event-modal #event-form-col1 > * { order: 5; }
  #event-modal.event-link-optional:not(.catalog-quick-add) #event-catalog-resolver { grid-column: 2; order: 4; }
  #event-modal #event-form-options { order: 5; }
  #event-modal #event-form-col2 { order: 6; }
  #event-modal #event-location-label { order: 7; }
  #event-modal #event-form-col3 { order: 8; }
  #event-modal #event-description-label { order: 9; }
  #event-modal .event-form-linked-block { order: 10; }
  /* Nutzer-Feedback: "4 bilder neben dem titelbild platzieren rechts". Cover-Spalte wird zur
     Flex-Zeile: Titelbild | Foto-Widget. Seit der Folgerunde ("2 spaltig mit links funktionen")
     lebt das Foto-Widget statisch in .event-form-media-cols (Desktop-Position, neben dem
     Medienordner-Umschalter) -- layoutEventEditFormMobile() (app.js) reparentet es hierher, sobald
     dieser Breakpoint greift, und wieder zurueck sobald nicht mehr. */
  #event-modal #event-form-cover-col {
    display: flex; flex-direction: row; align-items: flex-start; gap: var(--space-4);
  }
  #event-modal .title-image-picker-portrait { max-width: 100px; margin: 0; flex-shrink: 0; }
  /* Behaelt die bisherige 2x2-Kachel-Groesse bei (nur am Desktop wurde daraus eine Reihe zu 4,
     siehe Basisregel bei .event-photo-widget-grid weiter oben). */
  #event-modal #event-photo-widget-grid {
    grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(2, 1fr);
    max-width: 100px; aspect-ratio: 1; flex-shrink: 0;
  }
  /* Nutzer-Feedback (Folgerunde): "die foto und das foto hinzufügen icons etwas größer" -- nur
     mobil groesser (Desktop-Kacheln sind in der breiten 4x1-Reihe bereits ausreichend gross). */
  #event-modal #event-photo-widget-grid .event-photo-widget-cell-empty,
  #event-modal #event-photo-widget-grid .event-photo-widget-cell-add { font-size: var(--fs-3xl); }
  /* Anheften/Wiederholen stehen seit 2026-09-11 unter "Optionen" statt rechts neben dem Titelbild
     ("anheften wiederholen button ... neben dem titelbild rechts", "ausschreiben") -- auf dem Handy
     nebeneinander unter der Ueberschrift, ausgeschrieben wie bisher. */
  /* Anheften/Wiederholen nebeneinander ueber die volle Breite ("nebeneinander nicht mehr untereinander"),
     seit 2026-09-14 dazu Countdown neben Anheften und "Privat" rechts neben Wiederholen -- der
     Rhythmus steht darunter ueber beide Spalten, damit beim Einschalten nichts verrutscht
     ("dann darunter damit sich die sachen nicht verschieben die wiederholen aufpoppen laesst"). */
  /* Wie am Desktop: vier gleich grosse Knoepfe plus Medienordner, der Rhythmus klappt als Fenster
     ueber dem Knopf auf (2026-09-15) -- die frueher eigene Zeile "rhythmus" und der weite Abstand
     zum Medienordner-Knopf sind damit weg. */
  #event-modal #event-form-options {
    display: grid; width: auto; grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-areas:
      "label       label"
      "anheften    countdown"
      "wiederholen privat"
      "ordner      ordner";
  }
  #event-form-options .field-label { grid-area: label; grid-column: 1 / -1; }
  #event-modal #event-pin-header-btn { grid-area: anheften; }
  #event-modal #event-countdown-header-btn { grid-area: countdown; }
  #event-modal #event-recurring-anker { grid-area: wiederholen; }
  #event-modal #event-private-header-btn { grid-area: privat; }
  #event-modal #event-form-options .event-form-folder-toggle { grid-area: ordner; }
}
.chatroom-reminder-goto-btn:hover { filter: brightness(1.1); }
/* Nutzer-Feedback (#731): "kannst du aus beiden leisten (suchen und die control leiste) eine
   leiste machen? die suche quasi unter die control bar und dann einfach das unter das video?" --
   index.html hat die Link-Leiste jetzt hinter #chatroom-watch-container stehen (statt davor), hier
   nur noch die Nahtstelle optisch verschmelzen (kein Abstand, keine doppelte Rundung). Die Klasse
   .chatroom-watch-bar-merged wird von updateChatRoomWatchUI() (app.js) nur gesetzt, wenn der
   Video-Bereich tatsaechlich sichtbar ist -- ohne gesetztes Video bleibt die Leiste eigenstaendig
   mit normaler Rundum-Rundung. */
#chatroom-watch-controls { border-radius: var(--radius) var(--radius) 0 0; margin-bottom: 0; }
.chatroom-watch-bar.chatroom-watch-bar-merged {
  margin-top: -1px; border-radius: 0 0 var(--radius) var(--radius); border-top: none;
}
@media (max-width: 720px) {
  /* Mobil-Feedback: "passe noch die breite etc an das sieht sehr komisch aus auch die
     überschrift" -- Link-Leiste/Steuerleiste randlos ueber die volle Breite statt der
     Desktop-Seitenraender, Steuerleisten-Icons/Zeit kompakter damit die Zeile nicht umbricht. */
  .chatroom-watch-bar, #chatroom-watch-controls { margin-left: 0; margin-right: 0; }
  .chatroom-watch-controls { gap: var(--space-2); padding: var(--space-3) var(--space-3); flex-wrap: wrap; }
  .chatroom-watch-time { min-width: 26px; font-size: var(--fs-2xs); }
  #chatroom-chat-block .boards-chat-heading { font-size: var(--fs-lg); }
}
/* Mobil-Feedback: "rooms - gestalte das so, dass die räume angezeigt werden wie bei agenda. also
   die raum liste und wenn man einen raum öffnet sollte es wie der neue chat oder bilder ordner
   aufgehen" -- eigener Breakpoint (720px statt der von Sammlung mitgenutzten 900px-Stapel-Regel
   weiter oben, siehe .boards-layout dort), strikt auf #tab-chatrooms gescoped, um Sammlung
   (Spiele/Filme/LAN-Party teilen dieselben .boards-*-Klassen) nicht mit zu beeinflussen.
   Standardzustand zeigt nur die Liste (volle Breite); ein geoeffneter Raum bekommt die Klasse
   "mobile-room-open" auf .boards-main (siehe openChatRoom()/closeChatRoomMobile() in app.js).
   Mobil-Feedback (Folgerunde): "mache das mit dem mittleren down button wenn man mobil etwas
   öffnet weg zurück zu einem button links oben für zurück und mache die seiten die sich mobil
   öffnen wieder fullscreen. so dass auch die zurück wischen geste läuft wieder" -- echtes
   Vollbild (top:0, keine abgerundeten Ecken/Schatten) statt eines von top:6vh "einschwebenden"
   Bottom-Sheets, Zurueck-Pfeil oben links (#chatroom-mobile-back-btn) statt des vorherigen
   mittigen Chevron-Down-Griffs. */
/* Bugfix: die Basis-Regel "display:none" MUSS vor dem @media-Block stehen -- sonst gewinnt sie
   bei gleicher Selektor-Spezifitaet durch spaetere Quellreihenfolge und ueberstimmt das
   "display:flex" der Mobile-Regel, egal welche Viewport-Breite gerade aktiv ist (bekannter
   Bug-Pattern in dieser Datei, siehe aehnliche Kommentare bei .event-preview-chat weiter oben). */
#chatroom-mobile-back-btn { display: none; }
@media (max-width: 720px) {
  #tab-chatrooms .boards-layout { grid-template-columns: 1fr; height: auto; }
  #tab-chatrooms .boards-sidebar { border-right: none; height: auto; }
  #tab-chatrooms .boards-main { display: none; }
  #tab-chatrooms .boards-main.mobile-room-open {
    display: flex; flex-direction: column; position: fixed; inset: 0;
    z-index: var(--z-over-modal); background: var(--panel); border-radius: 0; box-shadow: none; overflow: hidden;
  }
  #tab-chatrooms .boards-main.mobile-room-open #chatroom-content {
    display: flex !important; flex-direction: column; flex: 1; min-height: 0; overflow-y: auto;
  }
  #tab-chatrooms .boards-main.mobile-room-open #chatroom-empty-state { display: none !important; }
  #tab-chatrooms .boards-main.mobile-room-open .boards-detail-row { flex: 1; min-height: 0; }
  #chatroom-content .modal-header-bar { padding-left: 52px; position: relative; }
  #chatroom-mobile-back-btn {
    display: flex; position: absolute; top: 10px; left: 10px; z-index: 2;
    width: 34px; height: 34px; border-radius: 50%; background: var(--knopf);
    align-items: center; justify-content: center; font-size: var(--fs-2xl); flex-shrink: 0;
  }
}
/* Mobil-Feedback: "mache generell da wo es chats gibt das zwei tabs system" -- Chat/Info sind auf
   Mobile jetzt zwei Tabs (siehe .mobile-pane-tabs oben + setChatRoomMobileTab() in app.js) statt
   permanent uebereinander gestapelter Bloecke (die generische .boards-detail-row{flex-direction:
   column}-Regel bei <=900px, siehe dort). Default ist der Chat-Tab aktiv, die Info-Spalte bleibt
   dann unsichtbar und der Chat bekommt die volle Hoehe statt sich mit dem Info-Panel zu teilen. */
/* Nur am Desktop stehen die Kopfzeilen-Knoepfe einzeln -- auf dem Handy liegen sie hinter den drei
   Punkten (siehe zeigeRaumKopfMenue() in chatrooms.js). */
#chatroom-mobile-invite-btn, #chatroom-mobile-more-btn, #chatroom-chat-more-btn { display: none; }
@media (max-width: 720px) {
  #chatroom-content.chatroom-mobile-tab-info .chatroom-chat-col { display: none; }
  #chatroom-content:not(.chatroom-mobile-tab-info) .boards-detail-right-col { display: none; }
  /* Der Info-Reiter scrollt fuer sich, sonst war er unten abgeschnitten (2026-09-16: "im info tab:
     der wird noch abgeschnitten nach unten"). !important, weil die Spalte im Markup ein
     "flex: 0 0 250px" als Inline-Stil traegt (ihre BREITE am Desktop) -- gestapelt wurde daraus
     eine feste HOEHE von 250px, und alles ab "Mitglieder" lag darunter. */
  #chatroom-content.chatroom-mobile-tab-info .boards-detail-right-col {
    flex: 1 1 auto !important; min-height: 0; overflow-y: auto;
  }
  #chatroom-content.chatroom-mobile-tab-info .boards-info-panel { max-height: none; overflow: visible; }

  /* Kopfzeile: Zurueck links, Titel mittig, Einladen und die drei Punkte rechts (2026-09-16). */
  #chatroom-mobile-invite-btn, #chatroom-mobile-more-btn, #chatroom-chat-more-btn { display: inline-flex; }
  #chatroom-content .modal-header-bar :is(#chatroom-screenshare-btn, #chatroom-rename-btn,
    #chatroom-delete-btn, #chatroom-leave-btn, #chatroom-invite-more-btn) { display: none !important; }
  #chatroom-content .modal-header-bar { padding-right: var(--space-4); gap: var(--space-3); }
  /* Der Titel steht links neben dem Zurueck-Pfeil, auf dessen Hoehe (2026-09-16: "mobil bitte die
     rooms ueberschriften linksbuendig machen wieder"). Die Knoepfe rechts bleiben absolut gesetzt. */
  #chatroom-content .modal-header-bar { justify-content: flex-start; }
  #chatroom-content .modal-header-bar .wiki-header-titles {
    flex: 1 1 auto; min-width: 0; align-items: flex-start; text-align: left;
    max-width: calc(100% - 150px);
  }
  #chatroom-content .modal-header-bar #chatroom-title {
    text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  #chatroom-content .modal-header-bar .modal-header-actions {
    position: absolute; right: var(--space-4); top: 50%; transform: translateY(-50%);
  }

  /* Die vier Chat-Funktionen liegen im Punkte-Menue. */
  #chatroom-chat-block .boards-chat-heading :is(#chatroom-chat-subscribe-btn,
    #chatroom-chat-pinned-btn, #chatroom-chat-polls-btn, #chatroom-chat-search-btn) { display: none; }
  #chatroom-chat-block .boards-chat-heading #chatroom-chat-more-btn { margin-left: auto; }

  /* Der Chat sieht aus wie der in der Event-Ansicht: durchscheinende Flaeche, die bis nach unten
     laeuft, und der Verlauf nutzt die ganze Hoehe (2026-09-16). */
  #tab-chatrooms .boards-main.mobile-room-open .chatroom-chat-col {
    background: var(--flaeche-fuellung);
    -webkit-backdrop-filter: var(--flaeche-unschaerfe); backdrop-filter: var(--flaeche-unschaerfe);
    flex: 1; min-height: 0;
  }
  #tab-chatrooms .boards-main.mobile-room-open #chatroom-chat-block { flex: 1; min-height: 0; }
  #tab-chatrooms .boards-main.mobile-room-open .chat-messages { flex: 1; min-height: 0; max-height: none; }
  #tab-chatrooms .boards-main.mobile-room-open #chatroom-content { overflow: hidden; }

  /* Watch Together: der erste Reiter zeigt das Video, der zweite den Verlauf darunter. */
  .chatroom-content-watch:not(.chatroom-mobile-tab-watchchat) #chatroom-chat-block { display: none; }
  .chatroom-content-watch.chatroom-mobile-tab-watchchat :is(#chatroom-watch-container,
    #chatroom-watch-bar, #chatroom-watch-queue, #chatroom-watch-placeholder) { display: none !important; }
  /* Der laufende Videotitel steht unter dem Raumnamen (2026-09-16: "bitte mache die anzeige des
     abgespielten videos am handy unter den titel") -- er wird gekuerzt, damit die Zeile haelt. */
  .chatroom-content-watch .modal-header-bar .wiki-header-titles {
    flex-direction: column; align-items: flex-start; gap: 0;
  }
  .chatroom-content-watch .modal-header-bar #chatroom-title { width: auto; max-width: 100%; }
  #chatroom-watch-nowplaying { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* Die Linkleiste bricht um, statt rechts hinauszulaufen: Eingabe oben, die beiden Knoepfe
     darunter. Das Kamera-Symbol davor entfaellt dort -- es haette eine eigene Zeile fuer sich. */
  #chatroom-watch-bar { flex-wrap: wrap; }
  #chatroom-watch-bar .chatroom-watch-bar-icon { display: none; }
  #chatroom-watch-url-input { flex: 1 1 100%; }
  #chatroom-watch-queue-add, #chatroom-watch-url-submit { flex: 1 1 0; justify-content: center; }
  /* Auf dem Handy gibt es nur Vollbild, keinen Kino-Modus (2026-09-16). */
  #chatroom-watch-expand-btn { display: none; }
}
/* Ist ein Raum offen, fuellt er den Bildschirm -- die Reiterleiste unten waere darin nur ein
   zweiter Ausgang neben dem Zurueck-Pfeil (2026-09-16). */
body.raum-offen #mobile-tab-bar { display: none; }

/* Schmale Handys (2026-09-11, Zeile Titelbild (Ändern | Entf.) | Verknuepfung "Spiel/Film/Serie"): mit vollen
   Knopfraendern und Symbolen passt sie bis 375px (TEST=eventvonbis: 73px Luft bei 375, 60px bei 402); darunter
   rechnerisch nicht mehr -- dort engerer Spurabstand und schmalere Knopfraender, die Symbole bleiben. Steht
   bewusst am Dateiende, damit die Werte die Handy-Regeln des Formulars (gleiche Spezifitaet) ueberschreiben.
   Die fruehere Stufe ohne Symbole (fuer die dreispurige Variante mit untereinander stehenden Optionen) ist
   mit der neuen Aufteilung entfallen. */
@media (max-width: 374px) {
  #event-form.two-col-form { column-gap: var(--space-3); }
  #event-modal :is(#event-title-image-btn, #event-title-image-remove-btn, #event-catalog-optional-toggle, #event-pin-header-btn, #event-recurring-header-btn) {
    padding-left: var(--space-4); padding-right: var(--space-4);
  }
}

/* ==================== Einheitliche Flächen (2026-09-14) ====================
   Nutzerwunsch: "mache zusätzlich die anderen seiten angepasst an das dashboard ... auch die area
   in der was angezeigt wird sollte ... gleich sein, hier sollten wir uns auch an der widget area
   orientieren, das alles gleich ist und das als default festlegen ... desweiteren fehlt überall
   dieser glass look. auch das man das grid sehen kann bspw. bei tools".

   Deshalb steht die Fläche genau EINMAL hier, am Ende der Datei: alles, was Inhalt trägt --
   Dashboard-Kachel, Notiz-/Raum-/Datei-Spalte, Tools-Panel, Sammlungsspalte, Kalenderfläche,
   Medien-Album -- bekommt dieselbe Milchglasfläche, denselben Radius und denselben Schatten. Die
   Regel steht bewusst hinter allen Bereichsabschnitten, damit sie deren frühere Einzelflächen
   (meist ein deckendes var(--panel)) überschreibt, statt dass jede Seite ihre eigene Optik
   behält. Wer eine Fläche ändern will, ändert --flaeche-* oben, nicht diese Liste. */
.widget,
.wiki-sidebar, .wiki-main,
.boards-sidebar, .boards-main,
.sammlung-column, .tools-placeholder-panel,
.year-calendar-area,
.media-album,
.app-nav, .app-context {
  background: var(--flaeche-fuellung);
  -webkit-backdrop-filter: var(--flaeche-unschaerfe);
          backdrop-filter: var(--flaeche-unschaerfe);
  box-shadow: var(--flaeche-schatten);
  border-radius: var(--radius);
}
/* Widgets und Medien-Alben stehen bewusst in derselben Liste wie Menuespalten und Fenster: sie
   sind FLAECHEN, keine Inhaltskacheln (2026-09-16 geklaert). Was auf ihnen liegt -- Events ohne
   Titelbild, Bongo Cat, Countdown-Zeilen -- traegt --inhalt-kachel und ist damit der einzige Ort,
   an dem die Kachelfarbe der Organisation wirkt. */
/* Die beiden festen Leisten behalten ihre stärkere Rundung -- sie sind Rahmen der Seite, keine
   Inhaltskacheln. */
.app-nav, .app-context { border-radius: var(--radius-xl); }
/* Eine Spalte, die nur gestapelte Panels hält, bleibt selbst leer -- sonst liegt Glas auf Glas
   (gleiche Begründung wie bei .tools-column-split weiter oben). */
.sammlung-column.tools-column-split {
  background: none; -webkit-backdrop-filter: none; backdrop-filter: none; box-shadow: none;
}
/* Das Album ist eine kleine Karte: Bildcollage plus Titel, mit etwas Luft ringsum. */
.media-album { padding: var(--space-4); }

/* ---- Seiten ohne zweite Menüspalte (Tools, Sammlung) ----
   Nutzerwunsch 2026-09-14: "bis auf bei den seiten die keine zweite linke leiste haben ... hier
   sollten wir uns auch an der widget area orientieren". Die beiden Seiten übernehmen deshalb die
   Maße der Kachelfläche des Dashboards: derselbe Abstand zwischen den Flächen (--kachel-abstand)
   und Panels, die so hoch sind wie eine zwei Einheiten hohe Kachel -- statt der früheren, an der
   Fensterhöhe hängenden Spaltenhöhe (100vh - Kopfzeile - 80px), die neben dem Dashboard fremd
   wirkte. Der Inhalt scrollt weiterhin im Panel. */
.sammlung-columns { gap: var(--kachel-abstand); }
#tab-links .sammlung-column { height: auto; overflow-y: visible; }
#tab-links .sammlung-column.tools-column-split { gap: var(--kachel-abstand); }
#tab-links .tools-placeholder-panel {
  flex: none;
  height: calc(var(--kachel-einheit) * 2 + var(--kachel-abstand));
  overflow: hidden;
}

/* Das Album am Desktop ist kein Overlay ueber der Seite, sondern DIE Inhaltsflaeche des
   Medien-Reiters (siehe body.hat-kontextspalte #media-game-detail weiter oben) -- es bekommt
   deshalb dieselbe Flaeche wie jede andere Inhaltsflaeche, statt als deckendes Modal aufzufallen. */
@media (min-width: 881px) {
  body.hat-kontextspalte #media-game-detail .modal {
    background: var(--flaeche-fuellung);
    -webkit-backdrop-filter: var(--flaeche-unschaerfe);
            backdrop-filter: var(--flaeche-unschaerfe);
    box-shadow: var(--flaeche-schatten);
  }
  body.hat-kontextspalte #media-game-detail .modal-header-bar.media-detail-header { background: none; }
}
/* Kleinere Ueberschrift im Album (2026-09-14: "fotoalben sollten kleinere ueberschriften haben") --
   der Name steht ohnehin schon in der Liste daneben. */
.modal-header-bar.media-detail-header h2 { font-size: var(--fs-lg); }

/* ---- Kopfzeile auf Seiten ohne zweite Menüspalte (Tools, Sammlung) ----
   Der Name des Hubs und „Tools //" stehen wie überall oben links (`.app-hub-titel` ist immer so
   breit wie beide Spalten zusammen) -- ohne zweite Spalte begann die Statusleiste aber schon
   hinter der Icon-Leiste und deckte beides zu (2026-09-14 gemeldet: "tools und sammlung hat nicht
   die // überschrift"). Die Leiste rückt deshalb genau um die fehlende Spalte ein und beginnt
   damit auf allen Seiten an derselben Stelle; der Inhalt darunter bleibt volle Breite. */
@media (min-width: 881px) {
  body.hat-navspalte:not(.hat-kontextspalte) .app-header-sticky {
    /* margin statt padding: die Leiste ist zwar durchsichtig, liegt aber (gleiche z-index-Stufe,
       spaeter im Dokument) UEBER dem Hub-Titel -- mit Innenabstand haette sie ihn weiterhin
       verdeckt, der Name war mitten im Wort abgeschnitten. Mit Aussenabstand beginnt der Kasten
       selbst erst hinter dem Titel. */
    margin-left: calc(var(--kontext-breite) + var(--schale-gap));
    width: auto;
  }
  /* Die kleine Überschrift auf der Seite selbst ist damit doppelt -- weg mit ihr (2026-09-14:
     "mache dafür diese kleinen überschriften oben links weg die die beiden seiten haben"). Die
     Zeile bleibt: sie trägt die Suche und hält den Inhalt auf der Rasterlinie. Auf dem Handy gibt
     es die Kopfzeile links nicht, dort ist die Überschrift der einzige Seitenname. */
  .sammlung-heading { display: none; }
}

/* ---- Handy: die Agenda ist der ganze Inhalt ----
   Dort gibt es keine zweite Spalte, die Agenda steht direkt auf der Seite. Eine eigene Flaeche
   (Milchglas + Schatten) wirkt da wie ein zweiter Hintergrund um die Karten herum (2026-09-14
   gemeldet: "auf dem handy hat die agenda jetzt auch einen komischen background der weg müsste ...
   es kann ruhig nur das grid zu sehen sein"). Und die leere Kopfzeile (die Begruessung ist unter
   720px ausgeblendet) hat sie 70px nach unten geschoben. */
/* Auf dem Handy laeuft das Muster deutlich weicher (2026-09-15: "mache den hintergrund pattern
   mobil deutlich mehr blurry") -- die Unschaerfe steckt in der SVG selbst (feGaussianBlur), denn
   ein CSS-filter auf dem Element wirkt VOR der Maske und liesse die Punkte scharf. */
@media (max-width: 880px) {
  body::before {
    -webkit-mask-image: url("/img/wellen-hinten-weich.svg"); mask-image: url("/img/wellen-hinten-weich.svg");
    opacity: calc(var(--wellen-deckkraft) * 1.15);
  }

}
@media (max-width: 880px) {
  .dashboard-col-agenda-fixed > .widget.widget-agenda-main,
  #tab-calendar .widget-agenda-main {
    background: none; box-shadow: none;
    -webkit-backdrop-filter: none; backdrop-filter: none;
    /* "overflow: hidden" (aus .widget) macht die Kachel selbst zum Scroll-Container -- eine sticky
       Kopfzeile darin klebt dann an DEREN Oberkante und wandert mit aus dem Bild. Auf dem Handy
       scrollt die Seite, nicht die Kachel, also hier sichtbar lassen (2026-09-15). */
    overflow: visible;
    /* Und keine Einblende-Animation: "animation: fadeInUp ... both" laesst die letzte Keyframe
       (transform: translateY(0)) DAUERHAFT stehen. Ein transformierter Vorfahre macht die sticky
       Kopfzeile darin zum Sonderfall -- sie wirkt beim Scrollen "locker" und laeuft dem Finger
       hinterher (2026-09-15 gemeldet). Die Seite selbst blendet weiterhin ein. */
    animation: none;
    /* Die Leiste soll auch UNGESCROLLT schon an der Kopfleiste kleben -- sonst springt sie beim
       ersten Scrollen um den oberen Abstand von main nach oben und beim Hochscrollen wieder zurueck
       (2026-09-15: "wirkt es als waere sie locker"). Deshalb diesen Abstand hier herausrechnen. */
    margin-top: calc(var(--space-7) * -1);
  }
  /* Rooms und Medien tragen ihre Kopfzeile wie die Agenda: oben festgeklebt, mit derselben Flaeche
     wie die Kopfleiste darueber (2026-09-15: "mache die titelleisten von rooms und medien im stil
     von der agenda. oben fest und mit dunklem background"). Dieselben drei Zutaten wie dort:
     sticky unter der Kopfleiste, eigene Flaeche, seitlich bis an den Rand. */
  #tab-chatrooms .boards-sidebar-heading-row,
  #tab-screenshots .media-search-toolbar {
    position: sticky; top: var(--sticky-header-height, 48px); z-index: var(--z-sticky);
    padding-top: var(--space-4); padding-bottom: var(--space-4);
    background: var(--panel); margin-bottom: 0;
  }
  #tab-chatrooms .boards-sidebar-heading-row::before,
  #tab-screenshots .media-search-toolbar::before {
    content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: var(--space-7);
    background: var(--panel);
  }
  /* Bis an den Bildschirmrand: die Medien-Leiste steht direkt in main (Innenabstand --space-6),
     die Rooms-Zeile dagegen in .boards-sidebar, die ueber die volle Breite laeuft und selbst
     --space-7 Innenabstand hat (2026-09-15: "hat rechts und links einen kleinen balken frei"). */
  #tab-screenshots .media-search-toolbar { margin-inline: calc(var(--space-6) * -1); padding-inline: var(--space-6); }
  /* Suche und "+" in derselben Groesse wie in der Raumliste, die Zeile dadurch schmaler
     (2026-09-16: "die suchleiste und der plus button sind zu gross mache sie wie bei rooms. und
     somit die zeile auch schmaler"). Steht hier und nicht im 720er-Block weiter oben, weil die
     Sticky-Regel direkt darunter sonst (spaetere Quellposition) das Polster zurueckholt. */
  #tab-screenshots .media-search-toolbar {
    padding-top: var(--space-3); padding-bottom: var(--space-3);
  }
  #tab-screenshots .media-search-input { min-height: var(--control-h-sm); padding-block: 0; font-size: var(--fs-sm); }
  /* Eckig wie die uebrigen Knoepfe der Kopfzeilen, nicht rund (2026-09-16: "der plus button in
     medien ist jetzt schon gut klein aber noch nicht 4-eckig"). */
  #tab-screenshots #media-add-btn {
    width: var(--control-h-sm); height: var(--control-h-sm); border-radius: var(--radius-md);
  }
  #tab-chatrooms .boards-sidebar-heading-row { margin-inline: calc(var(--space-7) * -1); padding-inline: var(--space-7); }
  /* Ohne die oberen Abstaende klebt die Zeile von Anfang an an der Kopfleiste, statt beim ersten
     Scrollen um diese Hoehe nach oben zu springen ("bewegt sich bisschen"): bei Medien ist es der
     Abstand von main, bei Rooms zusaetzlich der Innenabstand der Spalte. */
  #tab-chatrooms .boards-layout, #tab-screenshots .media-search-toolbar { margin-top: calc(var(--space-7) * -1); }
  #tab-chatrooms .boards-sidebar-heading-row { margin-top: calc(var(--space-7) * -1); }
  /* Derselbe Fallstrick wie bei der Agenda-Kachel: "overflow: auto" macht die Spalte zum
     Scroll-Container. Die sticky Kopfzeile klebt dann an DEREN Oberkante und wandert mit aus dem
     Bild, obwohl in Wahrheit die Seite scrollt. Auf dem Handy also sichtbar lassen (2026-09-15). */
  #tab-chatrooms .boards-sidebar { overflow: visible; }

  /* Die Kopfzeile der Agenda (Titel, Countdown, Bongo Cat, Knoepfe) bleibt beim Scrollen oben
     stehen (2026-09-15: "koennen wir mobil in der agenda die leiste oben sticky machen") -- direkt
     unter der App-Kopfzeile, die selbst sticky ist. Sie braucht dafuer einen eigenen, leicht
     weichgezeichneten Grund: die Karten wuerden sonst durch sie hindurchlaufen. Die negativen
     Seitenraender lassen ihn bis an den Bildschirmrand reichen. */
  .widget-agenda-main > .widget-header {
    position: sticky; top: var(--sticky-header-height, 48px); z-index: var(--z-sticky);
    margin-inline: calc(var(--space-6) * -1); padding-inline: var(--space-6);
    /* Dieselbe Flaeche wie die Kopfleiste darueber, damit beide als EINE Leiste lesen
       (2026-09-15: "die sticky bar oben an der menueleiste anheften dass da kein abstand ist") --
       durchscheinend sah es aus wie ein Spalt zwischen zwei Leisten. Der obere Innenabstand faellt
       weg, sonst klafft ueber dem Titel eine leere Zeile. */
    background: var(--panel);
    padding-top: var(--space-4); padding-bottom: var(--space-4);
  }
  /* Beim Scrollen soll zwischen Kopfleiste und Agenda-Leiste nichts durchlaufen: die Zeile beginnt
     buendig an der Kopfleiste (main bringt oben sonst seinen Abstand mit). */
  .widget-agenda-main > .widget-header::before {
    content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: var(--space-7);
    background: var(--panel);
  }
}
@media (max-width: 720px) {
  .dashboard-toolbar { min-height: 0; margin-bottom: 0; }
}

/* ---- Suche in der Seitenzeile (2026-09-14) ----
   "mache überall die suchleiste so wie bei sammlung": das Suchfeld der Seite steht oben in der
   Kopfzeile statt in der Menüspalte links. Es ist derselbe Knoten, nur woanders eingehängt
   (syncContextNav() in app.js) -- die Breite kommt deshalb von hier, nicht vom Feld selbst. */
.seiten-suche { display: flex; align-items: center; min-width: 0; }
.seiten-suche > input { width: 320px; max-width: 100%; margin: 0; }
/* Die Zeile gibt es nur am Desktop: auf dem Handy bleibt die Suche in der Liste (dort ist die
   Menüspalte ausgeblendet), die leere Zeile würde nur Platz kosten. */
@media (max-width: 880px) {
  #tab-wiki > .sammlung-toolbar, #tab-files > .sammlung-toolbar,
  #tab-chatrooms > .sammlung-toolbar, #tab-screenshots > .sammlung-toolbar { display: none; }
}
/* Notizen, Dateien und Räume haben durch die Kopfzeile einen Rasterschritt weniger Platz -- sonst
   stünde ihre Unterkante tiefer als auf allen anderen Seiten. */
@media (min-width: 881px) {
  /* Seiten MIT Suchzeile geben ihr einen Rasterschritt ab; Rooms hat keine und nimmt die volle
     Hoehe der Spalte daneben (2026-09-14). */
  .wiki-layout { height: calc(var(--inhalt-hoehe) - var(--gitter-schritt)); }
  #tab-chatrooms .boards-layout { height: var(--inhalt-hoehe); }
  body.hat-kontextspalte #media-game-detail .modal.modal-xl { height: calc(var(--inhalt-hoehe) - var(--gitter-schritt)); }
}

/* ---------- Countdown (2026-09-14) ----------
   Nutzerwunsch: "countdown zeigt die zeit in tage:stunden:minuten:sekunden an ... schön episch
   aussehend mit [] klammern oder so". Zahlen in Tabellenziffern (springen dadurch nicht), die
   Klammern in der Akzentfarbe, die Einheiten klein darunter. Gebaut von countdownHtml(),
   sekündlich gefüllt von aktualisiereCountdowns() (beides app.js). */
.countdown {
  display: inline-flex; align-items: stretch; gap: var(--space-2);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  /* Gelb (2026-09-14, Nutzerwunsch) -- dieselbe Farbe, die auch "angeheftet" und "bald" tragen. */
  color: var(--highlight);
}
/* Die Klammern sind keine Schriftzeichen mehr, sondern gezeichnete Kanten (2026-09-14: "mache die
   [] genauso gross wie den countdown selbst") -- als Glyphe waere ihre Hoehe an die Schriftgroesse
   gebunden und nie exakt so hoch wie der Block aus Zahl plus Einheit. */
.countdown-klammer {
  align-self: stretch; width: 7px; flex: none; font-size: 0;
  border: 2px solid currentColor; border-right: none; border-radius: 3px 0 0 3px;
}
.countdown-klammer:last-child { border: 2px solid currentColor; border-left: none; border-radius: 0 3px 3px 0; }
.countdown-feld { display: inline-flex; flex-direction: column; align-items: center; justify-content: center; line-height: 1; }
.countdown-feld b { font-size: var(--fs-xl); font-weight: var(--fw-bold); letter-spacing: 0.02em; }
.countdown-feld small { font-size: var(--fs-2xs); opacity: 0.75; text-transform: uppercase; letter-spacing: 0.06em; margin-top: 2px; }
.countdown-punkt { opacity: 0.6; font-size: var(--fs-lg); line-height: 1.1; align-self: center; }
.countdown-gross .countdown-feld b { font-size: var(--fs-2xl); }
/* Auf dem Handy steht der Countdown rechtsbuendig NEBEN dem Titel (2026-09-15, Nachtrag: "mache
   bitte die groesse des countdowns mobil kleiner damit sie rechtsbuendig neben den titel passt") --
   kleine Ziffern, keine Einheiten darunter, schmale Klammern. Der Titel darf dafuer schrumpfen:
   ohne Countdown nimmt er die ganze Zeile, sonst braeche der Countdown in eine eigene um. */
@media (max-width: 880px) {
  #event-preview-modal .modal-header-bar > .countdown {
    margin-left: auto; align-self: center; flex-shrink: 0; gap: 1px;
  }
  #event-preview-modal .modal-header-bar > .countdown .countdown-feld b { font-size: var(--fs-md); }
  #event-preview-modal .modal-header-bar > .countdown .countdown-feld small { display: none; }
  #event-preview-modal .modal-header-bar > .countdown .countdown-klammer { width: 4px; border-width: 1px; }
  #event-preview-modal .modal-header-bar:has(> .countdown) .event-preview-title-wrap { flex: 1 1 auto; }
}
/* Abgelaufen: die Zahlen stehen auf 0, die Farbe nimmt sich zurück. */
.countdown-vorbei { color: var(--text-dim); }
.countdown-vorbei .countdown-klammer { color: var(--text-dim); }
/* Im Kopfstreifen der Event-Ansicht steht der Countdown rechts neben dem Datum. */
.event-preview-hero-datumzeile { display: flex; align-items: flex-end; gap: var(--space-8); flex-wrap: wrap; }
/* ---- Countdown-Widget ---- */
/* Eine Zeile pro Countdown, gebaut wie die Wunschliste (2026-09-14: "vllt sollte es genauso
   aussehen wie bei 'am meisten zum spielen gewuenscht'") -- Titelbild links, Name daneben, der
   Countdown rechts. Flach genug, dass drei Zeilen in die Kachel passen. */
.countdown-widget-list { gap: var(--space-4); overflow: hidden; }
.countdown-eintrag {
  display: flex; align-items: center; gap: var(--space-5);
  width: 100%; padding: var(--space-3) var(--space-5); border: none; border-radius: var(--radius);
  background: var(--inhalt-kachel); color: var(--text); font-family: inherit; text-align: left; cursor: pointer;
}
.countdown-eintrag:hover { background: var(--kachel-hover); }
.countdown-eintrag-bild { width: 36px; height: 48px; border-radius: var(--radius-xs); object-fit: cover; flex-shrink: 0; }
.countdown-eintrag-bild-leer { display: flex; align-items: center; justify-content: center; background: var(--zone-bg); font-size: var(--fs-xl); opacity: 0.6; }
.countdown-eintrag-text { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 0; flex: 1; }
.countdown-eintrag-name { max-width: 100%; font-size: var(--fs-sm); font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.countdown-eintrag .countdown { flex-shrink: 0; }

/* ---- Handy: kein Raster hinter der Agenda ----
   2026-09-14 gemeldet: "entferne das background grid am handy und ersetze es durch die agenda
   background farbe damit ich alles besser lesen kann". Auf dem kleinen Schirm liegen die Karten
   direkt auf dem Grund (die Agenda hat dort keine eigene Fläche mehr, siehe weiter oben) -- das
   Raster lief dadurch mitten durch den Text. Statt des Rasters trägt der Inhaltsbereich dort
   dieselbe ruhige Fläche, auf der die Karten sonst liegen. */
@media (max-width: 880px) {
  /* Die ruhige Flaeche liegt auf dem BODY, nicht auf main::before (2026-09-14 gemeldet: "der
     hintergrund der agenda ist mobil oben und unten abgerundet ... und der hintergrund geht nicht
     komplett ueber die menueleiste mobil unten") -- als Pseudoelement von main endete sie am
     Inhalt: mit runden Ecken und ohne die feste Leiste unten zu erreichen. Als zusaetzliche
     Farbebene ueber dem Seitenverlauf deckt sie das ganze Fenster ab. */
  body { background-image: linear-gradient(var(--flaeche-fuellung), var(--flaeche-fuellung)), var(--bg-verlauf); }
  main::before { display: none; }
  /* Und dann traegt keine der grossen Flaechen mehr eine eigene (2026-09-14 gemeldet: "medien hat
     mit dem background auf dem handy das gleiche problem wie die agenda. rooms hat doppelte
     hintergruende und wenn die weg sind auch das abgerundete problem") -- auf dem Handy fuellt
     jede Ansicht ohnehin das ganze Fenster, eine zweite Flaeche darauf ist nur ein Kasten im
     Kasten. Die kleinen Karten darin (Nachrichten, Alben, Kacheln) behalten ihre. */
  .wiki-sidebar, .wiki-main, .boards-sidebar, .boards-main,
  #media-landing, #media-game-detail .modal, .year-calendar-area {
    background: none; box-shadow: none; border-radius: 0;
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }
}

/* ---------- Konfetti, wenn ein Countdown abgelaufen ist (2026-09-14) ----------
   Nutzerwunsch: "mache einmalig für jeden user wenn er rein kommt und ein countdown abgelaufen ist
   eine konfetti animation auf der ganzen seite ... zeige auch warum, also vllt mache alles blurry
   dann mittig countdown von xy und dann blurry und effekt wieder weg". Eine Schicht über allem:
   weichgezeichneter Grund, in der Mitte der Name, darüber die fallenden Schnipsel. Nach ein paar
   Sekunden (oder einem Klick) blendet alles wieder aus. Siehe zeigeCountdownFeier() in app.js. */
.konfetti-schicht {
  position: fixed; inset: 0; z-index: var(--z-over-modal); overflow: hidden; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--bg) 35%, transparent);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  animation: konfettiAuf var(--duration-slow) ease both;
}
.konfetti-schicht-aus { animation: konfettiZu var(--duration-slow) ease both; }
@keyframes konfettiAuf { from { opacity: 0; } to { opacity: 1; } }
@keyframes konfettiZu { from { opacity: 1; } to { opacity: 0; } }
.konfetti-text {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-4);
  text-align: center; padding: var(--space-10) var(--space-12); max-width: min(90vw, 640px);
  animation: konfettiTextAuf var(--duration-slow) var(--ease-sheet) both;
}
.konfetti-text-klein { font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--highlight); letter-spacing: 0.08em; text-transform: uppercase; }
.konfetti-text-gross { font-size: var(--fs-display); font-weight: var(--fw-bold); color: var(--text); line-height: 1.1; }
@keyframes konfettiTextAuf { from { opacity: 0; scale: 0.9; } to { opacity: 1; scale: 1; } }
.konfetti-teil {
  position: absolute; top: -40px; border-radius: 2px; pointer-events: none;
  animation-name: konfettiFall; animation-timing-function: linear; animation-iteration-count: 1; animation-fill-mode: both;
}
@keyframes konfettiFall {
  from { transform: translateY(0) rotate(0deg); opacity: 1; }
  to { transform: translateY(105vh) rotate(var(--drehung, 360deg)); opacity: 0.9; }
}
/* Zum abgelaufenen Countdown regnen Uhren statt Papierschnipsel (2026-09-15) -- dasselbe Fallen,
   nur ein Symbol statt einer Flaeche: keine Fuellung, keine feste Groesse (die kommt je Teil aus
   dem JS), Farbe wie der Countdown selbst. */
.konfetti-uhr {
  background: none; border-radius: 0; width: auto; height: auto;
  color: var(--highlight); line-height: 1; filter: drop-shadow(0 2px 6px rgb(0 0 0 / 0.35));
}
.konfetti-uhr .mdi { font-size: inherit; }
/* Wer Bewegung reduziert haben will, bekommt die Botschaft ohne Schneegestöber. */
@media (prefers-reduced-motion: reduce) {
  .konfetti-teil { display: none; }
}

/* Reiter im Hilfe-Fenster (Changelog / Anleitung, 2026-09-14) -- mittig in der Kopfzeile. */
.hilfe-reiter { display: flex; align-items: center; gap: var(--space-3); margin-left: auto; }
.hilfe-reiter + .modal-header-actions { margin-left: var(--space-6); }
#hilfe-pane-changelog { overflow-y: auto; }
#hilfe-pane-changelog .changelog-group { margin-bottom: var(--space-5); }

/* Der Rundknopf unten links zeigt den Changelog nur noch auf dem Handy -- am Desktop steckt beides
   im Hilfe-Knopf der Leiste (2026-09-14, siehe oeffneHilfeFenster() in app.js). */

/* Auf dem Handy steckt Hell/Dunkel im Profilmenue (2026-09-14) -- das eigene Symbol in der
   Kopfleiste entfaellt dort. */
@media (max-width: 880px) {
  #theme-toggle-btn { display: none; }
}

/* Kopfzeile der Sammlung: Suche links, daneben der Knopf "Wishlist + Owned" (2026-09-14 -- die
   Ueberschrift ist am Desktop ausgeblendet, ohne margin-left:auto beginnt die Zeile also links). */
.sammlung-toolbar-rechts { display: flex; align-items: center; gap: var(--space-5); min-width: 0; }
/* Ein langer Knopf, nicht zweizeilig. */
.wunschliste-btn { white-space: nowrap; flex-shrink: 0; }
.wunschliste-btn span { white-space: nowrap; }
/* Feste 320px statt width:100% -- in dieser schrumpf-nach-Inhalt-Zeile loest 100% zirkulaer auf
   und faellt auf die Textbreite zurueck; das Feld war dadurch schmaler als die Suchfelder der
   anderen Seiten (alle 320px). flex-shrink bleibt erlaubt, damit es auf schmalen Fenstern weicht. */
.sammlung-toolbar-rechts .sammlung-search-input { margin: 0; width: 320px; flex: 0 1 320px; }
/* Im Fenster stehen beide Listen untereinander, jede in ihrer Kachel wie zuvor im Tools-Reiter. */
.wunschliste-modal-body { display: flex; flex-direction: column; gap: var(--space-8); }
.wunschliste-modal-body .tools-placeholder-panel { flex: none; }
@media (max-width: 720px) {
  .sammlung-toolbar-rechts { flex-wrap: wrap; gap: var(--space-3); }
  .sammlung-toolbar-rechts .sammlung-search-input { max-width: none; }
}

/* Die Anleitung im Hilfe-Fenster braucht ihr eigenes [hidden] -- .guide-body traegt display:flex,
   und ein display in einer Autorenregel schlaegt das display:none des hidden-Attributs (dieselbe
   Falle wie bei .media-ordner und .event-form-wiederholung). Ohne die Zeile stand die Anleitung
   unter dem Changelog (2026-09-14 gemeldet). */
#hilfe-pane-anleitung[hidden] { display: none; }

/* ---- Medien und Tools tragen dieselbe Flaeche wie Rooms (2026-09-14) ----
   Nutzerwunsch mit Screenshot: "medien und tools sollten den selben background bekommen wie rooms".
   In Rooms ist der Inhalt EINE Flaeche mit Titelzeile; Tools waren drei lose Karten nebeneinander
   und die Medien-Uebersicht stand ganz ohne Flaeche da. Die Kacheln darin verlieren dafuer ihre
   eigene -- sonst laegen zwei Flaechen uebereinander. */
@media (min-width: 881px) {
  /* Die Medien-Uebersicht steht auf einer Flaeche wie Rooms; Tools bleibt dagegen getrennt wie die
     Widgets und die Sammlung (2026-09-14, Kehrtwende nach einer Zwischenrunde: "tools muessen
     getrennt sein wie bei den widgets. oder sammlung"). */
  #media-landing {
    background: var(--flaeche-fuellung);
    -webkit-backdrop-filter: var(--flaeche-unschaerfe);
            backdrop-filter: var(--flaeche-unschaerfe);
    box-shadow: var(--flaeche-schatten);
    border-radius: var(--radius);
    padding: var(--space-7);
  }
}

/* ---- Countdown auf dem Handy ----
   Zentriert an seinem Platz (2026-09-14: "der countdown sollte mobil mittig angezeigt werden") und
   als winzige Zahlenreihe oben in der Agenda neben Bongo Cat. */
@media (max-width: 720px) {
  #event-preview-mobile-cover-side-col > .countdown { align-self: center; }
}
.agenda-countdown-mini { display: none; }
@media (max-width: 880px) {
  /* Immer nur ein Eintrag sichtbar, die anderen liegen gestapelt darunter und wechseln durch
     (2026-09-14) -- deshalb ein Raster mit einer Zelle statt einer Reihe. */
  .agenda-countdown-mini { display: grid; margin-right: var(--space-2); }
}
.agenda-countdown-mini-eintrag {
  /* Etwas Luft zwischen Name und Countdown (2026-09-14: "der countdown am handy sollte zwischen
     dem namen und dem countdown etwas mehr platz haben") -- vorher klebte beides aneinander. */
  grid-area: 1 / 1; display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  opacity: 0; pointer-events: none; transition: opacity var(--duration-base) ease;
}
.agenda-countdown-mini-eintrag.aktiv { opacity: 1; }
.agenda-countdown-mini-name {
  font-size: var(--fs-2xs); color: var(--text-dim); max-width: 110px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.agenda-countdown-mini .countdown-mini { gap: 1px; }
.agenda-countdown-mini .countdown-mini .countdown-feld b { font-size: var(--fs-2xs); }
.agenda-countdown-mini .countdown-mini .countdown-feld small { display: none; }
.agenda-countdown-mini .countdown-mini .countdown-punkt { font-size: var(--fs-2xs); }
.agenda-countdown-mini .countdown-mini .countdown-klammer { width: 4px; border-width: 1px; }

/* ---- Handy: Kalender, Medien und Raeume ueber die volle Breite ----
   2026-09-14 gemeldet: "mobil ist der kalender nicht auf voller breite nur hoehe. an der seite
   fehlt jeweils etwas da ist ein balken", "rooms ist mobil nicht auf voller breite", "kalender hat
   mobil oben noch abrundungen". Ursache war der Innenabstand der Overlays und die Rundung der
   Fenster. */
@media (max-width: 880px) {
  #calendar-modal .modal, #media-game-detail .modal { border-radius: 0; }
  #calendar-modal .modal-header-bar, #media-game-detail .modal-header-bar { border-radius: 0; }
  /* Die Zeile mit Datum und Knoepfen traegt dieselbe Flaeche wie die Titelleiste darueber und faellt
     etwas schmaler aus (2026-09-16: "mache die leiste oben wo das datum und die icons sind in der
     hintergrund farbe wie die titelleiste und etwas schmaler. damit es zu den anderen sachen
     passt") -- vorher hob sie sich als eigener, hoeherer Streifen ab. */
  #calendar-modal .modal-header-bar {
    background: var(--panel); padding-top: var(--space-3); padding-bottom: var(--space-3);
    min-height: 0;
  }
  #calendar-modal .modal-header-bar .icon-btn { width: var(--control-h-sm); height: var(--control-h-sm); }
  /* Das Jahr steht ganz rechts, direkt vor den Knoepfen (2026-09-16: "mache das jahr (2026) das man
     oben rechts sieht ganz rechtsbuendig"). */
  /* Das Jahr steht links, die Knoepfe bleiben rechts (2026-09-16: "mache das kalender jahr oben
     linksbuendig" -- Kehrtwende zur Runde davor). */
  #calendar-modal .year-nav { margin-left: 0; margin-right: auto; }
  /* Der Abonnieren-Knopf traegt auf dem Handy nur das Symbol -- ausgeschrieben passt die Zeile
     sonst nicht mehr neben Jahr und Filter. */
  #calendar-modal #year-subscribe-btn span { display: none; }
  #calendar-modal #year-subscribe-btn {
    padding: 0; width: var(--control-h-sm); height: var(--control-h-sm); min-height: 0;
    justify-content: center;
  }
  /* Beide Fenster enden ueber der Leiste unten, sonst deckt das Overlay sie ab und kein Reiter
     ist mehr erreichbar (2026-09-14 gemeldet). */
  #media-game-detail.modal-overlay {
    padding: 0; bottom: calc(var(--mobile-tab-bar-height) + var(--mobile-tab-bar-gap) * 2);
  }
  /* Raeume nutzen die volle Fensterbreite (2026-09-14: "rooms ist mobil nicht auf voller breite") --
     der seitliche Innenabstand von main wird dafuer wieder herausgerechnet. */
  #tab-chatrooms .boards-layout {
    width: auto; max-width: none; margin-inline: calc(var(--space-6) * -1);
  }
}

/* ---- Handy: was sich als Vollbild oeffnet, beginnt UNTER der Kopfzeile ----
   Die Kopfzeile der App liegt ueber diesen Flaechen. Begannen sie bei top:0, verschwand ihre
   eigene erste Zeile darunter -- im Raum der Zurueck-Pfeil (2026-09-14: "rooms sollte mobil einen
   zurueck button haben wenn man in einem raum ist" -- es gab ihn, man sah ihn nur nicht), im Album
   die Knoepfe "+" und "Funktionen" ("innerhalb von alben sind mobil die plus und 3 punkte buttons
   zu hoch"). --sticky-header-height haelt die tatsaechliche Hoehe live nach (siehe app.js). */
@media (max-width: 880px) {
  #media-game-detail.modal-overlay,
  #calendar-modal.modal-overlay { top: var(--sticky-header-height, 56px); }
}
@media (max-width: 720px) {
  #tab-chatrooms .boards-main.mobile-room-open {
    top: var(--sticky-header-height, 56px);
    /* Die Reiterleiste unten ist im offenen Raum ausgeblendet (body.raum-offen) -- der Raum reicht
       deshalb bis zum unteren Rand (2026-09-16). */
    bottom: 0;
  }
}

/* Die Raumkarten lagen mobil fast genau auf dem Ton der Seite (2026-09-14: "die rooms selbst haben
   den selben background farbe wie der background (mobil)") -- am Desktop liegen sie auf der
   Milchglasflaeche der Spalte, mobil gibt es die nicht. Deshalb hier eine Stufe heller plus Kante. */
/* Die Sonderfarben von 2026-09-14 sind entfallen (2026-09-16: "dort ist noch der dunkle balken in
   den rooms") -- die Kachel traegt seit dem Umbau ohnehin Flaeche und durchscheinenden Kopfbereich
   wie am Desktop, die zusaetzliche Stufe wirkte als Balken. */
@media (max-width: 880px) {
  #chatroom-list .chatroom-row { border-color: var(--border); }
}

/* Die Hauptknoepfe der Medien-Seite trugen als Einzige die volle Akzentfarbe -- in einer
   Organisation mit gelbem Akzent stach das heraus (2026-09-14: "der gelbe button bei medien an
   sich ist auch komisch ... das sollte schon aussehen wie die anderen"). Jetzt dieselbe ruhige
   Flaeche wie die uebrigen Icon-Knoepfe daneben. */
#media-add-btn.primary-btn,
#media-detail-add-btn.primary-btn {
  background: var(--knopf); color: var(--text); border: 1px solid var(--border);
}
#media-add-btn.primary-btn:hover,
#media-detail-add-btn.primary-btn:hover { background: var(--panel-hover); color: var(--text); }

/* Das Album traegt seine Bilder auf derselben dunklen Innenflaeche wie der Chat im Raum und die
   Notiz (2026-09-14: "es fehlen immer noch die black hintergruende wie bei rooms in den
   medienordnern"). Seit 2026-09-15 liegt sie NUR unter dem Raster, nicht unter der ganzen Ansicht
   ("er geht immer noch ueber den kompletten inhalt es sollte aber genau wie bei notes und rooms
   aussehen") -- die Leiste rechts steht wie bei den Notizen auf der aeusseren Flaeche. */
@media (min-width: 881px) {
  /* Links buendig mit der Kopfzeile, wie die Notiz-Karte und die Spalten der Sammlung
     (2026-09-15: "der hintergrund vom medien ordner sollte links keinen balken haben und wie die
     anderen in bspw sammlung ganz links anfangen") -- der Innenabstand des Fensters entfaellt auf
     dieser Seite, rechts und unten bleibt er. */
  body.hat-kontextspalte #media-game-detail .modal-body {
    /* Unten kein Abstand mehr: dort stand der hellere Grund unter der Ordnerflaeche hervor
       (2026-09-15: "unten der teil noch von dem helleren background uebersteht"). */
    margin: 0 var(--space-5) 0 0; padding-left: 0;
  }
  body.hat-kontextspalte #media-game-detail .boards-detail-left-col {
    /* Oben links eckig wie die Notiz-Karte und der Raum, und derselbe Innenabstand wie dort. */
    background: var(--panel); border-radius: 0 var(--radius) var(--radius) var(--radius);
    padding: var(--space-8);
  }
}

/* "Erstellt von ..." steht rechtsbuendig neben dem Titel statt darunter (2026-09-14). */
.modal-header-bar.wiki-header-bar .wiki-header-meta-row { margin-left: auto; align-self: center; flex-shrink: 0; }
@media (max-width: 720px) {
  .modal-header-bar.wiki-header-bar .wiki-header-meta-row { margin-left: 0; flex-wrap: wrap; }
}

/* Im hellen Modus lag die Agenda-Karte (--panel-2) fast genau auf dem Ton der Flaeche darunter
   (--panel zu 66% ueber dem Grund) -- die Karten verschwanden darin (2026-09-14 gemeldet: "im
   hellen modus haben die agenda einträge usw. den gleichen background wie manche backgrounds
   dahinter das sieht man dann nicht mehr"). Auf Papier ist die Karte deshalb HELLER als ihre
   Unterlage, nicht dunkler, dazu ein sehr weicher Schatten wie bei den anderen Karten. */
/* Die Farbe selbst steckt in --inhalt-kachel (beide Modi, siehe :root) -- hier bleibt nur
   der weiche Schatten, den die Karte auf Papier braucht, damit sie sich von ihrer Unterlage
   abhebt. Im dunklen Modus bestand dasselbe Problem andersherum (2026-09-14 gemeldet: "events ohne
   titelbild haben auch im dunkelmodus fast die gleiche farbe wie der background"); auch das loest
   jetzt der Ton, nicht mehr eine Sonderregel pro Modus. */
html[data-theme="hell"] .agenda-item { box-shadow: var(--shadow-sm); }

/* Zeile in der Admin-Liste "Private Events" (2026-09-14) -- gleiche Machart wie .unassigned-row,
   nur mit Schloss und Anzahl der Eingeladenen statt Zuweisen-Auswahl. */
.privates-event-leute {
  display: inline-flex; align-items: center; gap: var(--space-2); color: var(--text-dim);
  font-size: var(--fs-sm); flex-shrink: 0;
}

/* Griff zum Verschieben in den Baumlisten (Notizen, Dateien) -- nur im Bearbeiten-Modus sichtbar,
   und nur dort laesst sich ueberhaupt ziehen (siehe bindTreeDraggable() in app.js). */
.tree-drag-handle { display: none; flex-shrink: 0; color: var(--text-dim); cursor: grab; }
.tree-drag-handle .mdi { font-size: var(--fs-lg); line-height: 1; }
.wiki-list.edit-mode .tree-drag-handle { display: inline-flex; align-items: center; }
.wiki-list:not(.edit-mode) .tree-draggable { cursor: pointer; }
