/* Daria-Web — STRUKTUR-CSS
 *
 * Enthaelt ausschliesslich strukturelle CSS-Regeln:
 *   Layout (display, grid, flex), Sizing (width/height/dvh),
 *   Box-Model (margin/padding/gap/box-sizing), Positionierung
 *   (position/top/left/...), Show/Hide-Logik (visibility/pointer-events),
 *   Overflow/Scroll-Verhalten, Container-Queries, Mobile-Media-Queries.
 *
 * KEINE Farben, KEINE Borders, KEINE Schatten, KEINE Fonts.
 * Alles Visuelle lebt in /themes/<id>/theme.css und wird NACH dieser
 * Datei geladen — die Cascade ist bewusst so, dass Themes hier nichts
 * ueberschreiben muessen (disjunkte Property-Sets).
 *
 * Automatisch generiert durch tools/css_split.py beim Theming-Refactor
 * 2026-05-19 (Backlog-Item Theming A.1).
 */

/* ---------- Reset & Tokens ---------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }


/* Hellmodus-Defaults (Telegram-Light-Palette) */


/* Dark-Mode-Tokens — automatisch via OS, oder explizit per [data-theme="dark"] */



/* ---------- Login-Seite ---------- */
.login-body {
  min-height: 100%;
  display: grid;
  place-items: center;
  
  padding: 16px;
}
.login-card {
  width: 100%;
  max-width: 380px;
  
  
  
  padding: 28px 24px 20px;
  
}
.brand {  margin-bottom: 20px; }
.brand-logo {
  width: 56px; height: 56px;
  
  
  
   
  display: grid; place-items: center;
  margin: 0 auto 12px;
  
}
.brand-logo.small { width: 32px; height: 32px;  margin: 0;  }
/* Avatar im Header — wenn ein User-Profilbild vorhanden ist, ersetzt es das
   Default-D-Logo. img + Initiale-Fallback teilen sich denselben Slot. */
.brand-logo.avatar {
  
  overflow: hidden;
  padding: 0;
}
.brand-logo.avatar img {
  width: 100%; height: 100%;
  
  display: block;
}
/* Header-Avatar als Button: klickbar (öffnet Konto), zeigt beim Hover
   ein User-Icon-Overlay an. Cursor + relativer Container für das Overlay,
   sonst Default-Button-Reset (keine Border, kein Padding, transparent). */
.brand-logo-btn {
  position: relative;
   padding: 0; 
  
}



.brand-logo-fallback {
  display: grid; place-items: center;
  width: 100%; height: 100%; 
   
}
.brand-logo-overlay {
  position: absolute; inset: 0;
  display: grid; place-items: center;
   
  
   
  pointer-events: none;
}

/* Claude-Verbindung Status-Block im Konto-Tab. */
.claude-auth-status { margin-bottom: 12px; }
.claude-auth-status p { margin: 4px 0; }

/* Warnhinweis-Box im "Subscription wechseln"-Modal (Auth-Ausfall +
   Downgrade-Folgen). Theme-konform ueber --warning-*-Tokens, mit dezenten
   Fallbacks fuer Themes ohne diese Variablen. CSP-konform: keine Inline-
   Styles, alles laeuft ueber diese Klasse. */
.claude-switch-warn {
  margin: 4px 0 14px;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--warning-fg, #c98b00);
  background: var(--warning-bg, rgba(255, 193, 7, 0.18));
  color: var(--warning-fg, #c98b00);
  font-size: 0.9em;
  line-height: 1.45;
}
.claude-switch-warn ul { margin: 6px 0 0; padding-left: 18px; }
.claude-switch-warn li { margin: 3px 0; }

/* Admin-Bell im Header: relative Position für die Badge oben rechts. */
.icon-btn-bell { position: relative; }

/* Color-Scheme-Quick-Toggle im Header wurde am 2026-05-25 (Header-Cleanup,
   Wunsch Dennis) entfernt — die zugehoerigen Selektoren (.icon-btn-scheme,
   .scheme-icon, .scheme-icon-{auto,dark,light}) sind aus dem CSS raus,
   weil ihr Element nicht mehr existiert. Die Color-Scheme-Funktion lebt
   weiter im Konto-Tab im Dropdown #color-scheme-select. */

.bell-badge {
  position: absolute;
  top: 2px; right: 2px;
  min-width: 16px; height: 16px;
  padding: 0 4px;
  
  
  
   
   
  pointer-events: none;
}

/* Push-Pulse auf der Admin-Bell — wird gesetzt, wenn ein Backlog-Status-
   Change eingegangen ist, der noch nicht „gesehen" wurde (siehe app.js
   markAdminBellPulse). Effekt: gold-leuchtender Ring um den ganzen
   Bell-Button, plus dezentes Pulsieren des Icons selbst. Wird geclear't
   beim Bell-Klick oder beim Tab-Wechsel in den Admin-Tab. */

.icon-btn-bell-pulse::before {
  content: "";
  position: absolute;
  inset: -3px;
  
  
  
  
  pointer-events: none;
}



/* ---------- Toast / Snackbar (loest alert()/confirm() ab) ----------------
 * JS unter /toast.js — window.toast.{success,info,warn,error,confirm,undoable}.
 * Container wird lazy von toast.show() ans <body> gehaengt.
 *
 * Position:
 *   - Desktop (>= 600px): oben rechts, Stack nach unten.
 *   - Mobile  (< 600px):  unten zentriert, Stack nach oben — bessere
 *     Daumen-Reichweite, deckt keine wichtigen Header-Elemente ab.
 *
 * Severities greifen die bestehenden Theme-Tokens (--ok/--accent/--danger)
 * plus eine Warn-Farbe inline; Theme/Dark-Mode bleiben damit konsistent.
 */
.toast-container {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;  /* nur die Toasts selbst sind klickbar */
  max-width: min(420px, calc(100vw - 32px));
}
@media (max-width: 599px) {
  .toast-container {
    top: auto;
    right: 8px;
    left: 8px;
    bottom: 16px;
    flex-direction: column-reverse;  /* neueste Toasts unten zuerst */
    max-width: none;
  }
}
.toast {
  pointer-events: auto;
  
  
  
  
  
  padding: 10px 14px;
  
  display: flex;
  align-items: center;
  gap: 12px;
  
  
    
  
  
  
  word-wrap: break-word;
  overflow-wrap: anywhere;
}
/* Eintritt: leicht verschoben, dann reingleiten. */


/* Austritt: rausfaden + leicht verschoben. */


.toast-msg {
  flex: 1 1 auto;
  white-space: pre-line;  /* alert()-Strings mit \n bleiben lesbar */
}
.toast-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
}
.toast-btn {
  
  
  
  
  
  padding: 4px 10px;
  
  
  
}



.toast-counter {
  
  
  
  min-width: 28px;
  
}

/* Severities: nur die linke Akzentkante (und role/aria) unterscheidet sich,
 * Body bleibt theme-konform. Symbol-Praefix per ::before fuer schnelle
 * visuelle Differenzierung. */

.toast-success .toast-msg::before { content: "✓ ";   }

.toast-info    .toast-msg::before { content: "ℹ ";   }

.toast-warn    .toast-msg::before { content: "⚠ ";   }

.toast-error   .toast-msg::before { content: "✕ ";   }
/* Undoable: optisch als „passiert gerade" markieren. */

.toast-undoable .toast-msg::before { content: "↶ ";   }

/* Highlight-Pulse beim Sprung zum Backlog-Item (jumpToBacklogItem):
   Item kriegt fuer ~2 s einen gold-pulsierenden Glow, damit der User
   im langen Backlog sofort erkennt, welches Item gemeint war. */



/* Admin-Tab: Header-Row mit "Clear"-Button rechts neben H2. */
.admin-header-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.admin-header-row h2 { margin: 0; }

/* Admin-Messages-Liste: chat-artig, neueste zuerst, severity-Farben links. */
.admin-messages-list { display: flex; flex-direction: column; gap: 6px; max-height: 70vh; overflow-y: auto; }
.admin-msg {
  
  padding: 6px 10px;
  
  
}



.admin-msg-text {   word-break: break-word; }
.admin-msg-meta {  margin-top: 2px; }

/* =====================================================================
   Audit-Log-Viewer (2026-05-19)
   Filter-Zeile + dichte Tabelle. Bewusst kompakt, weil typische Logs
   schnell hunderte Eintraege haben.
   ===================================================================== */
.audit-filters {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end;
  margin: 12px 0;
}
.audit-filter {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 0.9em;
}
.audit-filter > span { color: var(--text-muted, #666); }
.audit-filter input,
.audit-filter select {
  min-width: 160px;
  padding: 6px 8px;
  font-size: 0.95em;
}
.audit-filter-actions { flex-direction: row; gap: 6px; align-items: center; }

.audit-table-wrap { overflow-x: auto; max-height: 70vh; overflow-y: auto; }
.audit-table {
  width: 100%; border-collapse: collapse; font-size: 0.9em;
}
.audit-table th,
.audit-table td {
  text-align: left; padding: 6px 8px; vertical-align: top;
  border-bottom: 1px solid var(--border, #e0e0e0);
}
.audit-table thead th {
  position: sticky; top: 0;
  background: var(--bg, #fff);
  font-weight: 600;
  z-index: 1;
}
.audit-table tbody tr { cursor: pointer; }
.audit-table tbody tr:hover { background: var(--hover-bg, rgba(0,0,0,0.04)); }
.audit-table .audit-col-time { white-space: nowrap; font-variant-numeric: tabular-nums; }
.audit-table .audit-col-type {
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 0.85em;
}
.audit-table .audit-col-user { font-weight: 500; }
.audit-table .audit-col-summary {
  max-width: 380px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--text-muted, #666);
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 0.85em;
}

.audit-detail-meta {
  display: grid; grid-template-columns: auto 1fr; gap: 4px 12px;
  margin-bottom: 10px;
}
.audit-detail-meta strong { color: var(--text-muted, #666); font-weight: 500; }

/* Schmales App-Fenster (WM.7, 2026-05-28): Filter untereinander, Tabelle
   horizontal scrollbar bleibt durch wrap. Container-Query gegen app-audit,
   damit die Regel auch in einem schmalen Desktop-Fenster greift, nicht nur
   bei schmalem Viewport. */
@container app-audit (max-width: 720px) {
  .audit-filter input,
  .audit-filter select { min-width: 0; width: 100%; }
  .audit-filter { flex: 1 1 calc(50% - 6px); }
  .audit-filter-actions { flex: 1 1 100%; justify-content: flex-end; }
}

/* Konto-Tab: Profilbild-Vorschau */
.account-row {
  display: flex; align-items: center; gap: 16px;
  flex-wrap: wrap;
}
.avatar-preview {
  width: 96px; height: 96px;
  
  overflow: hidden;
  
  
  flex-shrink: 0;
  position: relative;
}
.avatar-preview img {
  width: 100%; height: 100%;
  
  display: block;
}
.avatar-placeholder {
  width: 100%; height: 100%;
  display: grid; place-items: center;
   
  
}
/* Klickbares Avatar im Konto-Tab: bei Hover ein Kamera-Icon über das Bild
   blenden. Das Overlay ist standardmäßig unsichtbar und liegt absolut über
   dem <img>; Opacity-Übergang macht den Effekt sanft. */
.avatar-editable {
  
  padding: 0;
  
  
  
}

.avatar-overlay {
  position: absolute; inset: 0;
  
  
  display: grid; place-items: center;
  
  
}

/* Inline-Button im Fließtext (z. B. "Benutzer: dennis ✏️") — soll wie ein
   normaler klickbarer Name aussehen, nicht wie ein Button. Bei Hover blendet
   ein Edit-Icon dezent ein. */
.link-btn {
    padding: 2px 4px;
    
  display: inline-flex; align-items: center; gap: 4px;
  
}




/* ---------- Modal ---------- */
.modal-root {
  position: fixed; inset: 0; z-index: 1000;
  display: grid; place-items: center;
  
  padding: 16px;
  
}

.modal-card {
  
  
  
  
  max-width: 440px; width: 100%;
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  padding: 20px 22px;
  
}

.modal-card h3 { margin: 0 0 6px;  }
.modal-card-wide { max-width: 720px; }
/* Extra-breite Variante fuer Modals mit mehrspaltigen Tabellen (z.B.
   Backlog-Archiv). 720px (wide) ist fuer 6 Spalten + Zusammenfassung viel
   zu schmal — die Summary-Spalte wird auf 2-3 Zeichen pro Zeile gequetscht.
   min(1080px, 95vw) sorgt fuer Lesbarkeit auf Desktop UND laesst auf
   schmalen Fenstern noch ein bisschen Backdrop sichtbar. */
.modal-card-xwide { max-width: min(1080px, 95vw); }
/* Stark vergroesserte Variante fuer Detail-Modals mit langem Prosa-Inhalt
   (z. B. Backlog-Archiv-Item-Details: Beschreibung + ggf. mehrseitige
   Ergebnis-Antwort). xwide (1080px) ist fuer mehrzeilige Prosa noch zu
   schmal — auf grossen Bildschirmen verschenkt der Detail-Dialog Platz.
   xxwide nutzt fast die volle Viewport-Breite (max 1600px, 97vw), damit
   <pre>-Bloecke ohne Scrollbar lesbar sind. Wunsch Dennis 2026-05-18
   („Bitte stark vergroessern"). */
.modal-card-xxwide { max-width: min(1600px, 97vw); }
/* Archiv-Modal: nahezu volle Fensterbreite. Das Backlog-Archiv zeigt eine
   8-Spalten-Tabelle (Status/Titel/Von/Angelegt/Fertig/Dauer/Zusammenfassung/
   Aktionen) — selbst xwide (1080px) und xxwide (1600px) lassen die
   Zusammenfassungs-Spalte unnoetig schmal. min(2400px, 96vw) nutzt die
   ganze Fensterbreite und laesst nur einen schmalen Rand zum Backdrop,
   damit das Modal sichtbar „innerhalb der Seite" bleibt. Der px-Deckel
   greift erst auf sehr breiten Monitoren (> ~2500px).
   Wunsch Dennis 2026-05-21 („Backlog-Archiv die ganze Fensterbreite nutzen"). */
.modal-card-archive { max-width: min(2400px, 96vw); }
/* Detail-Modal-spezifische Pre-Bloecke: lange Zeilen sollen weich umbrochen
   werden, damit der Inhalt nicht horizontal aus dem Modal raushaengt; sehr
   lange Antworten bekommen einen vertikalen Scrollbereich, damit das Modal
   nicht laenger wird als der Viewport. */
.backlog-archive-details pre.backlog-log-pre {
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 60vh;
  overflow: auto;
  
  
  
  padding: 10px 12px;
  
  
  margin: 6px 0 14px;
}
.modal-card .hint { margin-bottom: 12px; }
.modal-actions {
  display: flex; justify-content: flex-end; gap: 8px;
  margin-top: 14px; align-items: center;
}
/* Top-Action-Bar in Modals (z. B. Archiv-Modal): Titel links, Buttons rechts.
   Bei schmalen Viewports brechen die Action-Buttons unter den Titel um. */
.modal-header-row {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 12px; flex-wrap: wrap; margin-bottom: 4px;
}
.modal-header-row h3 { margin: 0; }
.modal-header-actions {
  display: flex; gap: 8px; align-items: center; flex-shrink: 0;
}
.modal-header-actions .ghost { margin: 0; padding: 6px 12px;   }
.modal-actions .primary,
.modal-actions .ghost { margin: 0; padding: 8px 14px;   }
.modal-card label { display: block; margin: 10px 0 4px;   }
/* Checkbox + Beschriftung nebeneinander in einer Modal-Zeile (z. B. „Bei
   Abschluss melden" im Backlog-Edit-Modal). Eigene Klasse statt inline
   style="display:flex…": die CSP ist `style-src 'self'` ohne
   'unsafe-inline', inline-style-Attribute im innerHTML-String werden still
   verworfen (Item c30eca7a, 2026-07-25). Selektor bewusst mit .modal-card
   davor, damit er die generische `.modal-card label`-Regel oben schlaegt. */
.modal-card .modal-checkbox-row {
  display: flex;
  align-items: center;
  gap: .5em;
  margin-top: .5em;
}
.modal-card .modal-checkbox-row input[type="checkbox"] { width: auto; }
/* Claude-Login-Modal, Stufe 2: Zeile mit dem Login-Link. Der Link ist eine
   sehr lange OAuth-URL und muss umbrechen duerfen (frueher inline
   style="word-break: break-all", s. o. — CSP-blockiert). */
.claude-login-url-row { margin: 8px 0; }
.claude-login-url { word-break: break-all; }
.modal-card input[type="text"],
.modal-card input[type="password"] {
  width: 100%; padding: 10px 12px;
   
    
   box-sizing: border-box;
}


/* ---------- Sessions-Tab ---------- */
.sessions-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* Projekt-Filter-Dropdown (Wunsch Dennis 2026-05-21): Label + <select>
   nebeneinander, links in der Sessions-Toolbar. */
.sessions-project-filter {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.95em;
}
.sessions-project-filter > span { color: var(--text-muted, #666); }
.sessions-project-filter select { min-width: 200px; }
.inline-toggle { display: inline-flex; align-items: center; gap: 6px;    }
.sessions-list { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.session-item {


  padding: 10px 12px;

  /* Layout-Umbau (Wunsch Dennis 2026-05-19, Backlog-Item „Aktive Sessions
     labeln"): Drei Grid-Areas — links die Info-Spalte (Badges + ID + Meta),
     rechts oben die Action-Buttons, rechts UNTEN das Session-Name-Input.
     Damit liegt das Namens-Feld klar getrennt vom oberen Action-Row, ist
     aber visuell der Session-Box zugeordnet. Auf Mobil bricht die mobile-
     Regel weiter unten den Stack auf 1-spaltig auf, dann landen alle drei
     Areas untereinander — Info, Actions, Name. */
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "info actions"
    "info nameinput";
  gap: 10px;
  align-items: start;
}
.session-info     { grid-area: info; min-width: 0; }
.session-actions  { grid-area: actions; }
.session-name-row {
  grid-area: nameinput;
  display: flex;
  justify-content: flex-end;
  align-items: flex-end;
}


.session-id {
  
   
  word-break: break-all;
}
.session-badges { display: inline-flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-bottom: 4px; }
.badge {
   
  padding: 2px 7px; 
   
}





/* „Backlog task"-Badge — fuer Sessions, die der autonome Backlog-Worker
   gestartet hat. Bewusst tuerkis/teal (zwischen web/blau und active/gruen),
   damit Backlog visuell als eigene Klasse erkennbar ist und nicht mit
   einem User-Turn verwechselt wird. Etwas breiter als die Standard-Badges,
   damit „Backlog task" nicht zerquetscht wird. */

/* Geräte-Chip in der Sessions-Liste — "📱 Web (Mac)", "iOS", "macOS".
   Bewusst dezent (geringere Sättigung als web/bridge/cli), damit drei
   gleichzeitig verbundene Geräte die Liste nicht visuell dominieren. */

/* "📁 <workdir>" — Hinweis, dass eine Telegram-Session noch im Ordner eines
   anderen Projekts liegt. Bewusst dezent gelblich (zwischen bridge/orange und
   active/gruen), keine Uppercase-Transformation, damit der Workdir-Pfad
   lesbar bleibt. */
.badge.other-project {     max-width: 38ch; overflow: hidden; text-overflow: ellipsis; vertical-align: bottom; }
.session-devices { display: inline-flex; gap: 4px; flex-wrap: wrap; margin-left: 4px; }
.session-actions { display: flex; gap: 6px; flex-shrink: 0; }

/* Info-Bubble fuer "Daria wird gerade neu gestartet". Bewusst gelb/amber
   statt rot — der User soll wissen, dass es kein Fehler ist, sondern ein
   erwarteter Zustand (Container-Restart). Hintergrund-Tint passt zum
   bridge/orange-Badge, sodass das Pattern wiedererkennbar bleibt. */
.bubble.info-restart {
  
  
  /* Mittig + alle 4 Ecken rund + ohne Tail (Schweif).
     Wunsch Dennis 2026-05-18: System-Bubbles ("Daria wurde neu gestartet",
     greeting) sollen sich klar von Dialog-Bubbles unterscheiden — kein
     Sender-Anschluss, sondern wie ein zentrierter Hinweis-Banner. */
  align-self: center !important;
  
}
/* Tail (::before) unterdruecken — der `:not(.greeting)`-Selektor matcht
   info-restart sehr wohl, deshalb expliziter Override hier. */
.bubble.in.info-restart::before { content: none !important; }
.session-actions .ghost { padding: 6px 10px;  }
.brand h1 {  margin: 0 0 4px; }
.brand-sub { margin: 0;   }

.login-form { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.login-form label {   margin-top: 8px; }
.login-form input {
   
   
  padding: 12px 14px; 
  
}


.primary {
  margin-top: 14px;
    
  padding: 12px 16px; 
    
  
}



.error {   margin: 6px 0 0; }
.ok {   margin: 6px 0 0; }
/* Prominente Fehler-Box — wird im Username-Modal verwendet, damit eine
   Fehlermeldung garantiert auffällt (die kleine .error-Variante ging
   in vorherigen Iterationen offenbar an Dennis vorbei). */
.err-box {
  
  
  
  
  padding: 8px 12px;
  
  margin: 10px 0 0;
}
.hint {   margin: 0 0 8px; }
/* Inline-Hint unter einem Row-Label (z. B. unter "Endlos-Feed über /new
   hinweg") — kleiner, dezenter und in eigener Zeile, damit der Switch
   rechts ordentlich daneben sitzt. */
small.hint { display: block;  margin: 2px 0 0;  }

.login-footer { display: flex; justify-content: center; margin-top: 16px; }
.lang-toggle {
    
    padding: 6px 10px; 
}


/* ---------- App-Header & Tabs ---------- */
.app-header {
  position: sticky; top: 0; z-index: 10;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  
  
  
}
.header-left { display: flex; align-items: center; gap: 10px; position: relative; }


/* Projekt-Switcher (Header, neben Username). Inline-Button mit Separator,
   Label = basename des aktiven Workdir, Caret als Affordance. Klick oeffnet
   ein Popover unterhalb mit der Liste aller Projekte. */
.project-switcher {
  display: inline-flex; align-items: center; gap: 4px;
    
    padding: 4px 8px;
  
}

.project-switcher-sep {  margin-right: 2px; }


.project-switcher-popover {
  position: absolute; top: 100%; left: 0; margin-top: 6px;
  min-width: 240px; max-width: 420px; max-height: 70vh; overflow: auto;
   
   
  z-index: 50; padding: 4px;
}
.project-switcher-popover[hidden] { display: none; }
.project-switcher-item {
  display: grid; grid-template-columns: 1fr auto; align-items: baseline; column-gap: 8px;
  width: 100%; 
    padding: 8px 10px; 
   
}



.project-switcher-item-path {
  grid-column: 1 / -1;  
  
  word-break: break-all;
}



/* ---------- Projekte-Tab ---------- */
.projects-list {  margin: 0; padding: 0; }
.projects-item {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; 
}


.projects-item-main { flex: 1; min-width: 0; }
.projects-item-name { display: inline;  }
.projects-item-badge {
  display: inline-block; margin-left: 8px; padding: 1px 6px;
    
   vertical-align: middle;
}
/* „💬 Telegram-Chat ist hier"-Badge im Projekte-Tab. Selbe Box wie die
   Standard-„Standard"-Badge, aber im bridge/orange-Farbschema — passt zu
   den Sessions-Tab-Badges (.badge.bridge), damit die TG-Heimat optisch
   wiederzuerkennen ist. */

.projects-item-path {
  display: block; margin-top: 2px;  
   word-break: break-all;
}

.projects-item-actions { display: flex; gap: 6px; align-items: center; flex-shrink: 0; flex-wrap: wrap; }

.projects-new { display: flex; gap: 8px; }
.projects-new input {
  flex: 1;  
   
  padding: 10px 12px;  
}
.header-right { display: flex; align-items: center; gap: 6px; }
.icon-btn {
    
  width: 38px; height: 38px; 
  display: grid; place-items: center; 
  
}


.tabs {
  display: flex; gap: 4px; justify-content: center;
  overflow-x: auto; 
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
    
   padding: 8px 14px; 
   white-space: nowrap;
  
}



/* Mobile: Tabs ALS BAR OBEN, direkt unter Header (TEST 2026-05-17).
   Vorteil gegenüber bottom-bar: Tabs werden NIE von der iOS-Tastatur
   überdeckt — kein Spalt zwischen Eingabefeld und Tastatur möglich. */
/* Selektor-Liste (max-width:720px ODER html.is-mobile): die Tab-Bar-Top-Fixed-
   Layout greift auf JEDEM erkannten Mobile-Geraet, nicht nur bei sehr schmalem
   Viewport. Wichtig fuer Tablets (iPad portrait = 768px), wo @media 720 nicht
   passt, das Geraet aber via Touch+coarse-pointer als is-mobile gilt. */
@media (max-width: 720px) {
  .app-header { grid-template-columns: auto auto; }
  .tabs {
    position: fixed; top: 60px; left: 0; right: 0;
    padding: 6px 8px;
    z-index: 20;
    justify-content: space-around;
  }
  /* main braucht padding-top, damit Content nicht unter den fixed-Tabs steckt */
  main { padding-top: 60px; padding-bottom: 0; }
}
html.is-mobile .app-header { grid-template-columns: auto auto; }
html.is-mobile .tabs {
  position: fixed; top: 60px; left: 0; right: 0;
  padding: 6px 8px;
  z-index: 20;
  justify-content: space-around;
}
/* padding-top so dimensioniert, dass die fixed-Tab-Leiste nicht den Content
   ueberlagert: Header (~60px) + Tab-Bar-Hoehe (~50px) = ~110px. Auf
   bordered-Chat liegt der Wert anderswo (60px-Variante in der existierenden
   @media-Regel), aber fuer scrollende Tabs (Sessions, Memory etc.) ist 110
   die richtige Hoehe. */
html.is-mobile main { padding-top: 110px; padding-bottom: 0; }
/* Chat-Tab hat eigene Hoehen-Berechnung (Chat-Wrap = 100dvh - 60 - 60 + safe-
   area) — der Tab-Container braucht aber trotzdem padding-top, damit die
   erste Bubble nicht unter die fixed Tab-Bar rutscht. body.tab-chat
   ueberschreibt sonst das main-padding ueber #app-main wieder auf 0. */
html.is-mobile body.tab-chat main { padding-top: 60px; }
html.is-mobile body.tab-chat #app-main { padding-top: 60px; }

/* ---------- Tab-Panels ---------- */
#app-main { max-width: 880px; margin: 0 auto; padding: 16px; }
.tab-panel { display: block; }
.tab-panel[hidden] { display: none; }

/* ---------- WM.7: Container-Anker pro App (2026-05-28) ----------
   Die Built-in-Apps (Chat, Tasks, Backlog, Settings, Memory, Audit) sollen
   sowohl im klassischen Tab-Layout als auch im Desktop-WM in beliebigen
   Fensterbreiten korrekt layouten. Im WM ist das Panel in <daria-window>
   slot'ed und kennt seine eigene Breite, nicht die Viewport-Breite. Wir
   setzen daher auf jedem App-Wurzelelement einen named container, sodass
   die nachfolgenden @container-app-<id>-Regeln gegen die App-Wurzel-Breite
   aufloesen — im Tab-Modus ~= Viewport, im WM-Modus = Fensterbreite. */
#panel-chat     { container-type: inline-size; container-name: app-chat; }
#panel-backlog  { container-type: inline-size; container-name: app-backlog; }
#panel-settings { container-type: inline-size; container-name: app-settings; }
#panel-memory   { container-type: inline-size; container-name: app-memory; }
#audit-card     { container-type: inline-size; container-name: app-audit; }
/* Tasks ist ein Custom-Element <daria-app-tasks> mit id="panel-tasks"
   (Light-DOM, kein Shadow-Root). Anker hier auf #panel-tasks, damit der
   Container schon VOR dem Mount der inneren .tasks-shell aktiv ist;
   tasks.css setzt einen zweiten, redundanten Anker auf .tasks-shell. */
#panel-tasks    { container-type: inline-size; container-name: app-tasks; }

/* ---------- Einstellungs-Subnav (Menü-Umbau 2026-05-21, Item 9e3ef51f) ----------
   Leiste am oberen Rand des Einstellungs-Bereichs: schaltet zwischen den
   Bereichen Einstellungen / Sessions / Gedächtnis / Projekte / Admin um, die
   beim Menü-Umbau aus der oberen Tab-Leiste ins Zahnrad-Menü gewandert sind.
   Die Buttons darin tragen die Klasse `.tab` und erben dadurch das komplette
   Theme-Styling der oberen Tab-Leiste (Hover/Active/Farben je theme/web-
   extras.css) — hier wird also NUR der Container layoutet. Die Sichtbarkeit
   steuert das `hidden`-Attribut (selectTab() in app.js); das globale
   `[hidden]{display:none!important}` setzt sich gegen display:flex durch. */
.settings-subnav {
  display: flex;
  gap: 4px;
  margin-bottom: 16px;
  padding-bottom: 12px;
  /* Schmale Viewports: horizontal scrollen statt die Buttons umzubrechen —
     gleiches Verhalten wie die obere .tabs-Leiste. */
  overflow-x: auto;
  scrollbar-width: none;            /* Firefox: Scrollbalken ausblenden */
  border-bottom: 1px solid var(--border, rgba(127, 127, 127, 0.25));
}
.settings-subnav::-webkit-scrollbar { display: none; }

/* Header-Icon-Buttons im aktiven Zustand: das Zahnrad (#btn-settings-open),
   solange ein Einstellungs-Bereich offen ist, und das Status-Icon
   (#btn-status-open) im Status-Bereich. app.js toggelt die .active-Klasse.
   Die Icon-SVGs zeichnen mit stroke="currentColor", daher genügt es, die
   Schriftfarbe auf den Theme-Akzent zu setzen. */
.icon-btn.active { color: var(--accent, #3b82f6); }

/* Shutdown-Indikator als Eck-Badge am Zahnrad-Icon (Menü-Umbau 2026-05-21,
   Item 9e3ef51f). Der Indikator hing früher inline am Admin-Top-Tab; da
   Admin jetzt im Zahnrad-Menü steckt, sitzt der Badge am Zahnrad — analog
   zur Glocken-Badge (.bell-badge). #btn-settings-open braucht dafür
   position:relative als Bezugsrahmen. Sichtbarkeit über das hidden-Attribut
   (setShutdownPending() in app.js); das globale [hidden] greift. */
#btn-settings-open { position: relative; }
.settings-shutdown-indicator {
  position: absolute;
  top: 2px;
  right: 2px;
  font-size: 10px;
  line-height: 1;
  pointer-events: none;       /* Klicks gehen an den Zahnrad-Button durch */
  /* Dezenter Puls — „Achtung, etwas Ungewöhnliches" erkennbar beim
     Vorbeischauen, ohne aggressiv zu blinken. Nutzt die bestehende
     Keyframe-Animation shutdown-tab-pulse (weiter unten definiert). */
  animation: shutdown-tab-pulse 2.4s ease-in-out infinite;
}

.card {
  
  
  
  padding: 16px;
  margin-bottom: 14px;
  
}
.card h2 { margin: 0 0 12px;  }
.row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 0; gap: 12px; flex-wrap: wrap;
}


.ghost {
    
  padding: 8px 14px;  
   
}




/* Switch (Toggle) — reine CSS-Implementation, kein JS-Framework nötig */
.switch { position: relative; display: inline-block; width: 44px; height: 24px; }
.switch input {  width: 0; height: 0; }
.slider {
  position: absolute;  inset: 0;
   
  
}
.slider::before {
  content: ''; position: absolute;
  height: 18px; width: 18px; left: 3px; bottom: 3px;
   
  
}



select, .login-form input, .env-list input {
   
   
  padding: 8px 12px;  
  
}


/* Env-Editor */
.env-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.env-row {
  display: grid;
  grid-template-columns: minmax(140px, 1fr) 2fr auto;
  gap: 8px; align-items: center;
}

.env-row input { width: 100%; }

.env-actions { display: flex; justify-content: flex-end; gap: 8px; align-items: center; }
/* Zwei „Endlos-Feed leeren"-Buttons (Web + Telegram), nebeneinander mit
   etwas Abstand, wrap auf schmalen Browsern. */
.clear-endless-buttons { display: inline-flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
/* Im .env-Editor sollen "Neu laden" (.ghost) und "Speichern" (.primary)
   optisch gleich groß sein und exakt auf einer Höhe sitzen. `.primary` hat
   global ein margin-top für Login-Forms — das hier rauswerfen, sonst hängt
   "Speichern" tiefer. Padding/Font-Size matchen wir an die ghost-Variante an,
   damit die Höhe identisch ist; Farbe + Hintergrund (das "Design") bleiben. */
.env-actions .primary,
.env-actions .ghost {
  margin: 0;
  padding: 8px 14px;
  
  
  height: auto;
}

/* IP-Allowlist (Settings → Sicherheits-Card) */
.ipal-list-label {
  display: block;
  
  
  margin: 12px 0 6px;
}
.ipal-list-label code {
  
  padding: 1px 5px;
  
  
}
.ipal-list {
  
  padding: 0;
  margin: 0 0 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.ipal-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  
  
  
}
.ipal-list li code {
  
  
  
  flex: 1;
}
.ipal-list li.ipal-empty {
  
  
  justify-content: center;
}
.ipal-tag-you {
  
  
  
  padding: 2px 8px;
  
  
  
}
.ipal-remove {
  
  padding: 4px 10px;
}
.ipal-add-row {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
}
.ipal-add-row input {
  flex: 1;
  padding: 8px 10px;
  
  
  
  
  
  
}

.ipal-default-note {
  margin-top: 6px;


}

/* IP-Kind-Badge (kleine Pille hinter "Deine aktuelle IP: X.X.X.X").
   - 'loopback': dezent grau, weil informativ aber nicht alarmierend
   - 'private':  gelb (warning), weil RFC1918 i. d. R. ein Trusted-Proxy-Bug
   - 'bridge':   orange (danger), weil Docker-Bridge praktisch immer ein
                 Konfigurations-Bug ist und die Allowlist nutzlos macht
   - 'public':   wird ueber hidden ausgeblendet (kein Lärm im Normalfall) */
.ipal-kind-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 0.78em;
  font-weight: 500;
  line-height: 1.4;
  vertical-align: baseline;
  white-space: nowrap;
}
.ipal-kind-badge[data-kind="loopback"] {
  background: var(--accent-bg, rgba(80, 150, 200, 0.15));
  color: var(--accent-fg, currentColor);
}
.ipal-kind-badge[data-kind="private"] {
  background: var(--warning-bg, rgba(255, 193, 7, 0.18));
  color: var(--warning-fg, #c98b00);
}
.ipal-kind-badge[data-kind="bridge"] {
  background: var(--danger-bg, rgba(255, 132, 0, 0.22));
  color: var(--danger-fg, #c14b00);
  font-weight: 600;
}

/* IP-Allowlist-Status-Pill (Item 31d5d386, 2026-06-11): zeigt klar AN/AUS.
   - on:  dezent gruen (ok) — beruhigend, kein Alarm
   - off: prominent rot (danger) — ein deaktivierter Zustand MUSS auffallen,
          weil dann nur noch Passwort/MFA schuetzt. */
.ipal-status {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 0 6px;
  padding: 8px 12px;
  border-radius: 8px;
  font-size: 0.9em;
  font-weight: 500;
  line-height: 1.4;
  border: 1px solid transparent;
}
.ipal-status[data-state=""] { display: none; }
.ipal-status-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: 0 0 auto;
}
.ipal-status[data-state="on"] {
  background: var(--ok-bg, rgba(40, 167, 69, 0.14));
  color: var(--ok-fg, #1c7c33);
  border-color: var(--ok-fg, #1c7c33);
}
.ipal-status[data-state="on"] .ipal-status-dot {
  background: var(--ok-fg, #1c7c33);
}
.ipal-status[data-state="off"] {
  background: var(--danger-bg, rgba(220, 53, 69, 0.16));
  color: var(--danger-fg, #c0392b);
  border-color: var(--danger-fg, #c0392b);
  font-weight: 600;
}
.ipal-status[data-state="off"] .ipal-status-dot {
  background: var(--danger-fg, #c0392b);
}

/* Warnbanner (gelb fuer private, orange fuer bridge) — taucht zwischen
   Switch-Row und Listen-Wrap auf, sobald die erkannte IP in einem nicht-
   routbaren Bereich liegt. Theme-konform ueber --warning-*/--danger-*-Tokens
   mit dezenten Fallbacks fuer den seltenen Fall fehlender Theme-Vars. */
.ipal-warn-banner {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 10px 0 6px;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--warning-fg, #c98b00);
  background: var(--warning-bg, rgba(255, 193, 7, 0.18));
  color: var(--warning-fg, #c98b00);
  font-size: 0.9em;
  line-height: 1.4;
}
.ipal-warn-banner[data-kind="bridge"] {
  border-color: var(--danger-fg, #c14b00);
  background: var(--danger-bg, rgba(255, 132, 0, 0.22));
  color: var(--danger-fg, #c14b00);
}
.ipal-warn-banner .ipal-warn-icon {
  flex-shrink: 0;
  font-size: 1.1em;
  line-height: 1.2;
}
.ipal-warn-banner .ipal-warn-text {
  flex: 1 1 auto;
}
.ipal-warn-banner .ipal-warn-doc {
  flex-shrink: 0;
  font-weight: 500;
  text-decoration: underline;
  color: inherit;
}
.ipal-warn-banner .ipal-warn-doc:hover {
  text-decoration: none;
}

#card-ip-allowlist .env-actions { margin-top: 8px; }
#card-ip-allowlist .env-actions .primary,
#card-ip-allowlist .env-actions .ghost {
  margin: 0; padding: 8px 14px;   height: auto;
}

/* Status-Grid */
.status-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 12px;
}
.status-grid > div {
   
  padding: 10px 12px; display: flex; flex-direction: column; gap: 4px;
}

.status-grid strong {  word-break: break-word; }

/* App-Gesundheit (App-Governance P3-18): Selbstcheck-Status je App im
   Status-Tab. Ampel-Punkt + Name + Status/Detail. */
.apphealth-list { display: flex; flex-direction: column; gap: 2px; }
.apphealth-row {
  display: flex; align-items: baseline; gap: 9px;
  padding: 5px 4px; border-radius: 6px;
}
.apphealth-row:hover { background: rgba(128, 128, 128, 0.08); }
.apphealth-dot {
  width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto;
  position: relative; top: 1px;
}
.apphealth-name { font-weight: 600; flex: 0 0 auto; }
.apphealth-state { opacity: 0.75; font-size: 12px; word-break: break-word; }
.apphealth-empty { opacity: 0.6; font-size: 12px; padding: 4px; }

.codeblock {
   
  padding: 12px; 
  
   
  overflow-x: auto; max-height: 320px; overflow-y: auto;
  margin: 0;
}
.codeblock.logs { max-height: 420px; white-space: pre-wrap; word-break: break-word; }

/* QR + Recovery */
.qr-wrap {
   padding: 12px;
  display: inline-block; 
  margin: 10px 0;
}
.secret-line code {   padding: 2px 6px;  }
.recovery-list {
   padding: 0; margin: 8px 0;
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px;
  
  
}
.recovery-list li {
   padding: 6px 10px;  
}

/* ---------- Chat ---------- */
.chat-wrap {
  display: flex; flex-direction: column;
  height: calc(100vh - 140px);
  
  
  
  overflow: hidden;
  
  /* Damit der absolut positionierte Datum-Indicator (Telegram-Style)
     relativ zum Chat-Container schwebt, nicht zum Viewport. */
  position: relative;
}

/* Telegram-Style Datums-Indicator: schwebt zentriert am oberen Chat-Rand,
   zeigt für den obersten sichtbaren Bubble „Heute"/„Gestern"/Datum.
   Erscheint kurz beim Scrollen und fadet 1.2s nach Scroll-Ende wieder aus
   — exakt wie in Telegram-Desktop. JS toggelt `data-visible="true"`. */
.date-indicator {
  position: absolute;
  top: 10px;
  left: 50%;
  
  
  
  padding: 4px 12px;
  
  
  
  pointer-events: none;
  
  
  z-index: 20;
  white-space: nowrap;
  
  
  
}


/* ===== Usage-Badge oben rechts im Chat =====
   Schwebt absolut in der oberen rechten Ecke der chat-wrap. Zwei kleine
   Donut-Ringe (Kontextfenster, 5-Stunden-Plan-Limit). Hintergrund
   halbtransparent mit Blur, damit es Bubbles dahinter nicht verdeckt. */
.usage-badge {
  position: absolute;
  top: 12px; right: 12px;
  z-index: 5;
  display: inline-flex; gap: 6px; align-items: center;
  padding: 6px 8px;
  
  
  
  
  
  
  pointer-events: auto;
  user-select: none;
  
  
}
/* Watch-Mode: der Banner "Du liest die Telegram-Session mit" ist ein
   stehender Block am oberen Rand des Chat-Tabs (kein position:absolute,
   sondern Flow). Die absolut positionierten Donuts liegen ohne weiteres
   Zutun darueber und verdecken den "Zurueck"-Button. Body-Klasse wird
   vom JS gesetzt (setWatchBanner). 56px = Banner-Hoehe (10+10 Padding +
   ~18px Text + 1px Border + Reserve) plus ein bisschen Luft. */
body.watch-mode-active .usage-badge {
  top: 60px;
}
.usage-donut {
  width: 30px; height: 30px;
  
  
}


.usage-donut-text {
   
  
  
  pointer-events: none;
}
/* Ampel-Farben — von CSS-Klassen auf dem aeusseren <svg> gesteuert. */





/* Donut ist tappable / klickbar — Cursor + Fokus-Ring deutlich machen.
   Wir ueberschreiben damit den frueher gesetzten `cursor: help` auf
   .usage-donut (siehe oben). */


/* Popover unterhalb des Badge. Slide-In von oben, schwebt absolut. */
.usage-popover {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 180px;
  max-width: 280px;
  padding: 10px 12px;
  
  
  
  
  
  
  
  z-index: 6;
  pointer-events: auto;
  
}
.usage-popover[hidden] { display: none; }

/* ===== Scroll-down-Button im Chat (2026-06-21, Wunsch Dennis) =====
   Schwebt absolut unten rechts in der .chat-wrap, knapp ueber der
   Eingabeleiste. Optik bewusst identisch zum Kontextfenster-Donut oben
   rechts (.usage-badge): gefuellter, leicht transparenter Kreis mit
   Backdrop-Blur und dezentem Schatten, monochromes Icon. Anders als die
   themen-spezifische .usage-badge (deren Visuals in der jeweiligen
   web-extras.css liegen) definieren wir den Button hier in der immer
   geladenen style.css ueber die globalen Theme-Tokens — so ist er in
   JEDEM Theme korrekt gestylt, ohne pro Theme eine Regel nachzuziehen.
   Sichtbarkeit toggelt app.js ueber die Klasse .is-visible (Hysterese). */
.chat-scroll-down {
  position: absolute;
  right: 16px;
  /* knapp ueber der Eingabeleiste (~60px hoch) + Safe-Area auf Mobile */
  bottom: calc(76px + env(safe-area-inset-bottom, 0px));
  z-index: 6;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  padding: 0;
  border-radius: 50%;
  /* exakt die Donut-Bubble-Optik (siehe .usage-badge in web-extras.css) */
  background: var(--bubble-out-time-dark);
  border: 1px solid var(--overlay-soft);
  box-shadow: 0 1px 4px var(--overlay-soft);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--text);
  cursor: pointer;
  /* Start: ausgeblendet. Fade + Scale beim Ein-/Ausblenden; visibility wird
     verzoegert auf hidden geschaltet, damit der Button im versteckten
     Zustand weder fokussierbar noch klickbar ist. */
  opacity: 0;
  visibility: hidden;
  transform: scale(0.7) translateY(8px);
  transition: opacity 160ms ease, transform 160ms ease, visibility 0s linear 160ms;
}
.chat-scroll-down.is-visible {
  opacity: 1;
  visibility: visible;
  transform: scale(1) translateY(0);
  transition: opacity 160ms ease, transform 160ms ease, visibility 0s;
}
.chat-scroll-down:hover { filter: brightness(1.08); }
.chat-scroll-down:active { transform: scale(0.92) translateY(0); }
.chat-scroll-down:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.chat-scroll-down svg { display: block; }
/* Dark-Mode: dieselbe Verfeinerung wie .usage-badge (web-extras.css),
   damit der Button im dunklen Theme exakt wie der Donut wirkt. */
html[data-theme="dark"] .chat-scroll-down {
  background: var(--usage-badge-bg-dark);
  border-color: var(--voice-player-bg-dark);
}

/* ===== Topbar-Usage-Donuts (Backlog-Item 840681f8, 2026-05-25) =====
   Zwei kleine Donuts in der oberen Menuleiste (.header-right) — 5-Stunden-
   und 7-Tage-Plan-Fenster. Wiederverwendung der .usage-donut-Klassen aus
   dem Chat-Badge (Ampel-Farben + Theming aus jedem theme/web-extras.css).
   Hier nur Layout + Groessen-Override + Mobile-Fallback. */
.topbar-usage {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-right: 4px;
  pointer-events: auto;
  user-select: none;
}
.topbar-usage[hidden] { display: none; }
/* Kleiner als die Chat-Donuts (30px), passt zu den 20px-Topbar-Icons. */
.usage-donut.topbar-usage-donut {
  width: 24px;
  height: 24px;
}
/* Popover unterhalb des Topbar-Donuts. Eigene Positionierung statt
   Wiederverwendung der absoluten Werte aus .usage-popover, damit es
   am rechten Topbar-Rand hochkant erscheint und nicht aus dem Viewport
   rutscht. */
.usage-popover.topbar-usage-popover {
  top: calc(100% + 6px);
  right: 0;
  z-index: 25;
}
/* Mobile: weiter sichtbar, aber kompakter; Text minimal kleiner.
   720px ist der etablierte Mobile-Breakpoint im Projekt
   (siehe @media (max-width: 720px) im Chat-Layout). */
@media (max-width: 720px) {
  .usage-donut.topbar-usage-donut {
    width: 20px;
    height: 20px;
  }
  .usage-donut.topbar-usage-donut .usage-donut-text {
    font-size: 9px;
  }
  .topbar-usage { gap: 2px; margin-right: 2px; }
}
/* Sehr schmal (< 380px, z. B. iPhone SE im Hochformat): Donuts ganz
   ausblenden, weil sonst Glocke/Status/Zahnrad nicht mehr in eine
   Zeile passen. Werte sind weiterhin im Chat-Badge (Kontext + 5h)
   und im Status-Tab abrufbar. */
@media (max-width: 380px) {
  .topbar-usage { display: none; }
}

/* ===== Tresor-offen-Label (Backlog-Item 21a1d69c, 2026-07-05) =====
   Kleines gruenes Pill-Label ganz links in der Donut-Gruppe der Topbar.
   Sichtbar NUR wenn der Daria Tresor entsperrt ist (JS toggelt das
   hidden-Attribut anhand GET /api/vault/status). Klick oeffnet die
   Tresor-App. Das Gruen ist hier BEWUSST farbig (explizite Vorgabe
   Dennis) — die Monochrom-Icon-Regel gilt dafuer NICHT. Farbe kommt aus
   den bestehenden Theme-Erfolgs-Tokens (--ok-bg/--ok-fg), damit das Label
   in Light- UND Dark-Themes lesbar bleibt; die Literal-Fallbacks decken
   Themes ohne diese Tokens ab. Keine Inline-Styles (CSP style-src 'self'). */
.topbar-vault-status {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 22px;
  padding: 0 9px;
  margin-right: 4px;
  border: 1px solid var(--ok-fg, #1c7c33);
  border-radius: 999px;
  background: var(--ok-bg, rgba(40, 167, 69, 0.14));
  color: var(--ok-fg, #1c7c33);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  -webkit-appearance: none;
  appearance: none;
}
.topbar-vault-status[hidden] { display: none; }
.topbar-vault-status:hover,
.topbar-vault-status:focus-visible {
  /* Etwas kraeftiger beim Hover/Fokus; kein reines outline, damit die
     Pill-Form erhalten bleibt. */
  filter: brightness(1.06);
  outline: 2px solid var(--ok-fg, #1c7c33);
  outline-offset: 1px;
}
.topbar-vault-icon { flex: 0 0 auto; display: block; }
.topbar-vault-label { display: inline-block; }
/* Mobile: nur das Schloss-Icon, Text weg (Platz fuer Glocke/Status/Zahnrad),
   analog zur Donut-Verkleinerung. */
@media (max-width: 720px) {
  .topbar-vault-status { padding: 0 6px; gap: 0; }
  .topbar-vault-label { display: none; }
}

/* ===== Backlog-Status-Donut (Backlog-Item ba059f8b, 2026-06-06) =====
   Dritter Topbar-Donut links neben den Usage-Donuts. Anders als die
   einfarbigen Usage-Donuts (Fuellgrad ueber EINEN .usage-donut-fg-Ring)
   hat dieser Ring DREI gestackte Segment-Boegen (fertig/fehlerhaft/
   wartend), die als <circle>-Elemente via stroke-dasharray/-dashoffset
   gezeichnet werden. Geometrie + Linecap hier; die Farben kommen aus den
   bestehenden Ampel-Tokens (--usage-green/-red/-yellow), die PRO THEME aus
   der jeweiligen manifest.json injiziert werden → Light/Dark/Theme-tauglich
   ohne Edit in jeder theme/web-extras.css. Der BG-Ring wird ueber die
   geteilte .usage-donut-bg-Klasse mitgetinted (inkl. Dark-Mode-Override). */
.backlog-donut-seg {
  fill: none;
  stroke-width: 4;
  /* butt statt round: sonst ueberlappen benachbarte Segmente an den Enden
     und es entstehen falsche Farbkanten. */
  stroke-linecap: butt;
  /* Initial leer (0-Laenge), damit vor dem ersten Poll kein voller farbiger
     Ring aufblitzt. JS setzt die echte dasharray/offset pro Segment. */
  stroke-dasharray: 0 81.68;
  stroke-dashoffset: 0;
  /* Start oben (12 Uhr) im Uhrzeigersinn — identisch zu .usage-donut-fg. */
  transform: rotate(-90deg);
  transform-origin: 16px 16px;
  transition: stroke-dasharray 0.4s ease, stroke-dashoffset 0.4s ease, stroke 0.3s ease;
}
.backlog-donut-seg.seg-done    { stroke: var(--usage-green); }
.backlog-donut-seg.seg-failed  { stroke: var(--usage-red); }
.backlog-donut-seg.seg-waiting { stroke: var(--usage-yellow); }
/* Klickbare „Backlog oeffnen"-Zeile im Backlog-Popover (Touch-Navigation).
   Reset der Button-Defaults, damit sie wie eine Popover-Zeile aussieht,
   aber als Akzent-Link erkennbar bleibt. */
.usage-popover .backlog-popover-open {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding: 4px 0 0 0;
  border: none;
  border-top: 1px solid var(--overlay-soft-medium);
  background: none;
  text-align: left;
  cursor: pointer;
  color: var(--accent);
  font: inherit;
  font-variant-numeric: tabular-nums;
}
.usage-popover .backlog-popover-open:hover,
.usage-popover .backlog-popover-open:focus-visible {
  text-decoration: underline;
  outline: none;
}

.usage-popover .usage-popover-title {
  
  margin-bottom: 4px;
  
}



/* Dark-Mode: dunklerer Hintergrund, hellerer Text. */

@media (max-width: 720px) {
  /* dvh statt vh: berücksichtigt iOS-Browser-Chrome dynamisch.
     env(safe-area-inset-bottom): Home-Indicator beim iPhone (~34px). */
  .chat-wrap { height: calc(100dvh - 180px - env(safe-area-inset-bottom, 0px));
                  }
}
/* Chat-Tab: keine 880-px-Beschränkung, kein Außenrand. Header bleibt oben,
   darunter füllt das Chat-Fenster den gesamten Viewport — wie bei Telegram
   Web. Nur der Chat-Tab; Settings/Status/Konto bleiben in der Karten-Optik. */
body.tab-chat #app-main { max-width: none; padding: 0; }
body.tab-chat .chat-wrap {
  /* Default Header ≈ 58 px (padding 10+10 + Inhalt). Wir geben uns 60 px
     als Puffer, damit kein Scrollbar entsteht. Mobile hat zusätzlich die
     Bottom-Tabs unten — Höhe dort separat (~120 px Header + Tabs). */
  height: calc(100dvh - 60px);
    
}
@media (max-width: 720px) {
  /* Höhen-Berechnung: 100dvh - Header(60) - Tabs(60, oben) - Home-Indicator.
     Mit Tabs jetzt oben gibt es keinen Tastatur-Konflikt mehr — kein
     keyboard-open-Sonderfall nötig. dvh schrumpft bei Tastatur dank
     interactive-widget=resizes-content, chat-input klebt direkt drauf. */
  body.tab-chat .chat-wrap {
    height: calc(100dvh - 60px - 60px - env(safe-area-inset-bottom, 0px));
  }
}
/* is-mobile-Variante (Mobile-Detection per UA/Touch). Damit Tablets
   (iPad portrait = 768px) das gleiche Tab-Bar-Layout bekommen, obwohl
   @media (max-width:720px) bei ihnen nicht greift. */
html.is-mobile body.tab-chat .chat-wrap {
  height: calc(100dvh - 60px - 60px - env(safe-area-inset-bottom, 0px));
}
.chat-list {
  flex: 1; overflow-y: auto;
  padding: 14px; display: flex; flex-direction: column; gap: 10px;
  
  
               
            /* alte Edge/IE */
}
.chat-list::-webkit-scrollbar { display: none; } /* Chrome, Safari, neue Edge */
.bubble {
  max-width: 80%;
  padding: 8px 12px;
  
  word-wrap: break-word;
  white-space: pre-wrap;
  
  position: relative;
  /* `flex-shrink: 0` ist kritisch — chat-list ist `display: flex;
     flex-direction: column`. Ohne diese Regel würde der Browser die
     Bubble-Höhe in der Spalte komprimieren, sobald die Summe aller
     Bubbles die chat-list-Höhe überschreitet. Das hatte bei Bild-
     Bubbles zur Folge, dass `.bubble-media { overflow: hidden }` das
     Bild unten beschnitten hat — typischer „Bubble ist zu klein für
     das Bild"-Effekt. Mit shrink:0 behalten Bubbles ihre natürliche
     Höhe und chat-list scrollt regulär. */
  flex-shrink: 0;
}
.bubble.in {
  align-self: flex-start;
  
  
  
  
}
.bubble.out {
  align-self: flex-end;
  
  
}

/* Comic-/Telegram-Sprechblasen-Tail.
   - .bubble.in (Daria, links)  → Tail unten LINKS, zeigt nach links unten.
   - .bubble.out (Dennis, rechts) → Tail unten RECHTS, zeigt nach rechts unten.
   Form: 1:1 Telegram-Desktop-Tail (originaler SVG-Pfad, 9×17). Vertikale
   Anschlusskante an der Bubble-Seite, gebogene Außenkurve mit kleinem
   Bogen-Tip unten. Wir nutzen `clip-path: path()` mit pixelgenauen
   Koordinaten — die Pseudo-Element-Box ist exakt 9×17 px, der Pfad nutzt
   den vollen Box-Bereich. Color kommt via `background: var(--bubble-…)`,
   passt sich also automatisch an Light/Dark-Mode an.

   Wichtig: bei `.bubble.in` überlappen wir den Bubble-Border um 1 px
   (right: calc(100% - 1px)), damit kein sichtbarer Saum zwischen Tail und
   Bubble entsteht. Greeting-Bubbles bekommen bewusst keinen Tail. */
.bubble.in:not(.greeting)::before,
.bubble.out:not(.greeting)::before {
  content: '';
  position: absolute;
  bottom: 0;
  width: 12px;
  height: 20px;
  pointer-events: none;
}
.bubble.out:not(.greeting)::before {
  /* Tail an der rechten Bubble-Kante, ragt nach rechts heraus.
     Telegram-Originalpfad, hochskaliert auf 12×20 (×1.333 H, ×1.176 V).
     Vertikale Anschlusskante x=0 (y 0→20), Bogen-Tip, zwei Cubic-Beziers
     zurück nach (0,0). Box-Höhe 20px passt noch innerhalb der geraden
     rechten Bubble-Kante (Bubble-Border-Radius 14px lässt min. 16px
     Geraden bei einer einzeiligen Bubble). */
  left: 100%;
  
  
}
.bubble.in:not(.greeting)::before {
  /* Tail an der linken Bubble-Kante, ragt nach links heraus.
     Manuell gespiegelter Pfad (jedes x → 12-x, Arc-Sweep-Flag von
     1 auf 0 geflippt) — kein `transform: scaleX(-1)`-Trick mehr,
     der in Kombination mit `clip-path` bei manchen Renderern flackerte.
     Form ist 1:1 das horizontale Spiegelbild der .out-Form.

     Border-Kompensation: .bubble.in hat einen 1 px-Border, .out nicht.
     `position: absolute` rechnet relativ zur Padding-Box → ohne
     Korrektur säße der Tail-Boden 1 px ÜBER und 1 px RECHTS vom
     äußeren Bubble-Rand, was zu einer sichtbaren Stufe am Anschluss
     führte. Mit `bottom: -1px` und `right: calc(100% - 1px)`(*) sitzt
     der Tail bündig auf dem äußeren Bubble-Rand, genau wie bei .out.
     (*) Das negative 1px überlappt zusätzlich 1 px in den Bubble-
     Border, damit der Border-Streifen am Anschluss weg ist. */
  bottom: -1px;
  right: calc(100% - 1px);
  
  
}

/* Telegram-Zeitstempel unten rechts in jeder Konversations-Bubble.
   Realisiert via CSS-`::after`-Pseudo, das den Text aus dem
   `data-time`-Attribut auf der Bubble liest (`content: attr(data-time)`).
   Vorteil: das Attribut überlebt jedes spätere `innerHTML = …`
   (Streaming-Updates, Replay), während ein <span>-Kind beim ersten
   Update weg wäre. Wir reservieren via `padding-right`/`padding-bottom`
   auf der Bubble einen Eck-Platz, damit der Text nicht unter den
   Stempel rutscht. Tool- und Greeting-Bubbles haben kein data-time
   gesetzt — der Selektor matcht dort gar nicht. */
.bubble[data-time]::after {
  content: attr(data-time);
  position: absolute;
  /* Rechter Inset reservierter den Platz fuer .bubble-check (SVG, ~22px
     breit beim Double-Check). 28px = 22px SVG + 6px Inset. */
  right: 28px;
  bottom: 3px;
  
  
  
  pointer-events: none;
  user-select: none;
  white-space: nowrap;
}
/* Read-Receipt-Haekchen — Telegram-Stil mit ueberlappenden SVG-Glyphen.
   .bubble-check (Kind-Element von .bubble) bekommt im JS sein SVG
   gesetzt (single oder double); Position rechts-unten neben der Uhrzeit.
   Wird nur an eigene out-Bubbles angehaengt; im Live-Watch-Mode (fremde
   TG-User-Bubbles) wird der Span nicht gerendert (kein semantischer
   Read-Receipt-Status). */
.bubble-check {
  position: absolute;
  right: 6px;
  bottom: 3px;
  height: 11px;
  
  display: inline-flex;
  align-items: center;
  pointer-events: none;
  
}

.bubble-check svg { display: block; }
.bubble[data-time] {
  /* Eck-Platz fuer Uhrzeit + ueberlappende Doppel-Haekchen. 60px rechts
     reicht fuer "23:59" (~30px) + 22px SVG-Breite + 8px Inset. 14px unten
     reicht fuer die 11px-Schriftgroesse + 3px Inset. */
  padding-right: 60px;
  padding-bottom: 14px;
}
/* Unsichtbares Hit-Element exakt über dem ::after-Stempel — trägt das
   `title`-Attribut, sodass das volle Datum nur beim Hovern über die
   Uhrzeit erscheint, nicht über die ganze Bubble. JS hängt es an und
   ein MutationObserver re-attached es nach `innerHTML`-Updates. */
.bubble-time-hit {
  position: absolute;
  right: 0;
  bottom: 0;
  /* Deckt grob die Fläche des ::after-Stempels ab (rechts 8px + ~30px
     Text-Breite + ein bisschen Luft, unten 14px Padding-Reserve). */
  width: 46px;
  height: 18px;
  /* Über etwaigem Restcontent rechts-unten, aber unter der ::after-Layer
     (die ist `content`-getrieben und liegt visuell oben — das Hit-Element
     muss nicht sichtbar sein, nur hover-bar). */
  z-index: 1;
}

/* Custom Datum-Tooltip — ersetzt den nativen `title`-Tooltip, der erst
   nach ~500-700 ms erscheint. Wir zeigen unseren in <100 ms via JS.
   `position: fixed` damit er nicht vom Chat-`overflow` geclippt wird. */
.datetime-tooltip {
  position: fixed;
  
  
  padding: 4px 8px;
  
  
  
  pointer-events: none;
  z-index: 1000;
  
  
  white-space: nowrap;
  
  
}

/* Auf dem dunkelblauen Outgoing-Bubble-BG ist --text-dim zu dunkel —
   wir nehmen ein gedimmtes Weiß analog zu Telegram. */


.bubble.tool {
  align-self: flex-start;
  
  
  
  
  
  
  position: relative;
  overflow: visible;
}

/* ==========================================================================
   Interaktive Rückfrage-Karte im Chat (Item 9c6096b8)
   ==========================================================================
   Portiert aus dem mit Dennis abgestimmten Prototyp
   (docs/examples/rueckfrage-karte-prototyp-v2.html).

   THEMING: Es werden AUSSCHLIESSLICH echte Theme-Tokens benutzt (geprüft
   gegen alle sechs public/themes/<id>/manifest.json). Vier Farben, die der
   Prototyp verwendet, gibt es im Stack NICHT als Token — sie werden hier
   einmalig aus echten Tokens abgeleitet, statt sie zu erfinden oder hart zu
   kodieren (gleiches Muster wie weiter unten im File, vgl. --bg-alt-Fallback
   mit color-mix):
     --ask-row       (Prototyp: --row-bg)       aus --text
     --ask-row-hover (Prototyp: --row-bg-hover) aus --text
     --ask-media     (Prototyp: --media-bg)     aus --text
     --warn          ->  --toast-warn (existiert wirklich)
   Weil sie aus --text abgeleitet sind, kippen sie in JEDEM Theme automatisch
   mit Hell/Dunkel mit: heller Text auf dunklem Grund ergibt eine helle
   Aufhellung, dunkler Text auf hellem Grund eine dunkle Abtönung. */
.bubble.ask {
  --ask-row: color-mix(in srgb, var(--text, #808080) 6%, transparent);
  --ask-row-hover: color-mix(in srgb, var(--text, #808080) 11%, transparent);
  --ask-media: color-mix(in srgb, var(--text, #808080) 6%, transparent);
  max-width: min(580px, 94%);
  white-space: normal;
  padding: 10px 12px 12px;
}
.bubble.ask[data-time] { padding-right: 12px; padding-bottom: 24px; }
.bubble.ask[data-time]::after { right: 12px; bottom: 7px; }

.ask-head { display: flex; align-items: center; gap: 8px; }
.ask-icon { width: 16px; height: 16px; flex: none; color: var(--accent); }
.ask-title { font-weight: 600; font-size: 15px; flex: 1; }
.ask-collapse {
  width: 24px; height: 24px; flex: none; border: 0; background: transparent;
  cursor: pointer; color: var(--text-dim); border-radius: 6px;
  display: flex; align-items: center; justify-content: center;
}
.ask-collapse:hover { background: var(--ask-row-hover); color: var(--text); }
.ask-intro { color: var(--text-secondary); font-size: 14px; margin: 4px 0 10px; }

.ask-opts { display: flex; flex-direction: column; gap: 4px; }
.ask-opt {
  display: flex; gap: 9px; align-items: flex-start; padding: 8px 10px;
  border-radius: 10px; background: var(--ask-row); border: 1px solid transparent;
  cursor: pointer; transition: background .12s, border-color .12s;
}
.ask-opt:hover { background: var(--ask-row-hover); }
.ask-opt.checked { background: var(--accent-soft); border-color: var(--accent); }
.ask-opt input[type="checkbox"], .ask-opt input[type="radio"] {
  margin: 3px 0 0; accent-color: var(--accent); flex: none; width: 16px; height: 16px;
}
/* Nummernkreis — DER Vertrag zwischen Karte, Telegram-Klartext und Freitext
   („Zu 3: nein"). Deshalb immer sichtbar, auch in der beantworteten Karte. */
.ask-num {
  flex: none; width: 19px; height: 19px; margin-top: 1px; border-radius: 50%;
  background: var(--ask-row-hover); color: var(--text-dim);
  font-size: 11px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.ask-opt.checked .ask-num { background: var(--accent); color: var(--on-accent); }
.ask-opt-body { min-width: 0; flex: 1; }
.ask-opt-label { display: block; font-size: 14px; line-height: 1.35; }
.ask-opt-hint {
  display: block; font-size: 12.5px; color: var(--text-dim);
  margin-top: 2px; line-height: 1.35;
}
.ask-opt-tag {
  font-size: 11px; font-weight: 600; padding: 1px 6px; border-radius: 5px;
  margin-left: 6px; white-space: nowrap; background: var(--ask-row-hover);
  color: var(--text-dim); vertical-align: 1px;
}
.ask-opt-tag.rec { background: var(--accent-soft); color: var(--accent-strong); }
.ask-opt-other input[type="text"] {
  width: 100%; margin-top: 6px; padding: 6px 9px; background: var(--bg);
  color: var(--text); border: 1px solid var(--border); border-radius: 6px;
  font-size: 13.5px; font-family: inherit; outline: none;
}

.ask-fields { display: flex; flex-direction: column; gap: 12px; }
.ask-field-q { font-size: 14px; line-height: 1.4; margin-bottom: 5px; display: flex; gap: 8px; }
.ask-field-in { padding-left: 27px; }
.ask-field-why { font-size: 12.5px; color: var(--text-dim); margin: 0 0 5px 27px; }
.ask-field-in input[type="text"], .ask-field-in textarea {
  width: 100%; padding: 8px 10px; background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 6px; font-size: 14px;
  font-family: inherit; line-height: 1.4; outline: none; resize: vertical;
}
.ask-field-in textarea { min-height: 60px; }
.ask-field-in input:focus, .ask-field-in textarea:focus,
.ask-opt-other input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.ask-seg { display: flex; gap: 6px; flex-wrap: wrap; padding-left: 27px; }
.ask-seg button {
  padding: 6px 12px; border-radius: 8px; cursor: pointer;
  background: var(--ask-row); color: var(--text); border: 1px solid var(--border);
  font-size: 13.5px; font-family: inherit;
  transition: background .12s, border-color .12s;
}
.ask-seg button:hover { background: var(--accent-soft); border-color: var(--accent); }
.ask-seg button.selected {
  background: var(--accent); color: var(--on-accent); border-color: var(--accent);
}

/* Statuszeile: macht sichtbar, dass die Karte NICHT blockiert. */
.ask-status {
  display: flex; align-items: center; gap: 6px; margin-top: 10px;
  font-size: 12.5px; color: var(--text-dim);
}
.ask-status svg { flex: none; color: var(--text-dim); }

.ask-foot {
  display: flex; align-items: center; gap: 8px; margin-top: 8px;
  padding-top: 10px; border-top: 1px solid var(--bubble-in-border); flex-wrap: wrap;
}
.ask-count { font-size: 12.5px; color: var(--text-dim); margin-right: auto; }
.ask-btn {
  padding: 7px 14px; border-radius: 8px; cursor: pointer; font-size: 14px;
  font-family: inherit; font-weight: 600; border: 1px solid var(--border);
  background: var(--ask-row); color: var(--text);
  transition: background .12s, border-color .12s, transform .1s;
}
.ask-btn:hover { background: var(--ask-row-hover); border-color: var(--accent); }
.ask-btn:active { transform: scale(.97); }
.ask-btn.primary {
  background: var(--accent); color: var(--on-accent); border-color: var(--accent);
}
.ask-btn.primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
.ask-btn.quiet {
  border-color: transparent; background: transparent; color: var(--text-dim); font-weight: 400;
}
.ask-btn.quiet:hover {
  background: var(--ask-row-hover); color: var(--text); border-color: transparent;
}

/* Eingeklappt („Später") — die Karte bleibt OFFEN und beantwortbar, sie
   nimmt nur eine Zeile ein. */
.bubble.ask.folded .ask-intro, .bubble.ask.folded .ask-opts,
.bubble.ask.folded .ask-fields, .bubble.ask.folded .ask-status,
.bubble.ask.folded .ask-foot { display: none; }
.bubble.ask.folded { padding-bottom: 24px; }
.ask-folded-line { display: none; font-size: 12.5px; color: var(--text-dim); margin-top: 4px; }
.bubble.ask.folded .ask-folded-line { display: flex; align-items: center; gap: 8px; }
.ask-folded-line .ask-btn { padding: 3px 10px; font-size: 12.5px; font-weight: 400; }

/* Beantwortet — Auswahl bleibt lesbar, ist aber nicht mehr bedienbar;
   „Ändern" macht sie wieder auf. */
.bubble.ask.done .ask-opts, .bubble.ask.done .ask-fields { pointer-events: none; }
.bubble.ask.done .ask-opt:not(.checked) { opacity: .42; }
.bubble.ask.done .ask-opt:hover { background: var(--ask-row); }
.bubble.ask.done .ask-status, .bubble.ask.done .ask-count,
.bubble.ask.done .ask-actions, .bubble.ask.done .ask-collapse { display: none; }
.bubble.ask.done .ask-foot { border-top-color: transparent; }
.ask-done {
  display: none; align-items: center; gap: 6px; font-size: 12.5px;
  color: var(--ok); font-weight: 600; margin-right: auto;
}
.bubble.ask.done .ask-done { display: flex; }
.ask-reopen { display: none; }
.bubble.ask.done .ask-reopen { display: inline-block; }
.bubble.ask.done .ask-field-in input, .bubble.ask.done .ask-field-in textarea {
  background: transparent; border-color: transparent; padding-left: 0;
  color: var(--text-secondary);
}
.bubble.ask.done .ask-seg button:not(.selected) { display: none; }
/* Offene Karte, die noch NICHT beantwortet ist: „Ändern" bleibt verborgen,
   die Aktionen sind da (Standardfall, hier nur explizit dokumentiert). */
.bubble.ask:not(.done) .ask-done { display: none; }

/* Miniatur rechts in einer Option (Item 4eb19529) — sichtbare Varianten statt
   Beschreibungen. Feste Kachelgröße, damit die Optionen untereinander eine
   ruhige Kante behalten, egal wie das Bild proportioniert ist. */
.ask-opt-thumb {
  flex: none; width: 74px; height: 52px; border-radius: 7px; overflow: hidden;
  background: var(--ask-media); border: 1px solid var(--bubble-in-border);
  cursor: zoom-in;
}
.ask-opt-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* ==========================================================================
   Inline-Medien in JEDER Bubble + HTML-Vorschau (Item 4eb19529)
   ==========================================================================
   Portiert aus docs/examples/rueckfrage-karte-prototyp-v2.html, Abschnitt 3
   („Bild direkt in der Bubble, plus Anhang mit Öffnen/Download").

   THEMING wie beim Karten-Block oben: ausschließlich echte Theme-Tokens aus
   public/themes/<id>/manifest.json. Die drei Farben, die es im Stack nicht als
   Token gibt (--row-bg / --row-bg-hover / --media-bg im Prototyp), werden aus
   --text abgeleitet — dieselbe color-mix-Ableitung wie bei .bubble.ask, hier
   nur auf einen breiteren Geltungsbereich gehoben, weil Bild und Anhang in
   JEDER Bubble vorkommen dürfen (und die Vorschau sogar außerhalb).
   Keine hart kodierten Farben, keine style=-Attribute (CSP). */
.bubble, .htmlview-root {
  --ask-row: color-mix(in srgb, var(--text, #808080) 6%, transparent);
  --ask-row-hover: color-mix(in srgb, var(--text, #808080) 11%, transparent);
  --ask-media: color-mix(in srgb, var(--text, #808080) 6%, transparent);
}

/* --- Bild inline im Fließtext (Markdown ![alt](…?token=… "Unterschrift")) --- */
.md-image-enhanced img {
  border-radius: 10px;
  background: var(--ask-media);
  cursor: zoom-in;
}
.md-image-caption {
  display: block;
  font-size: 12.5px;
  line-height: 1.35;
  color: var(--text-dim);
  margin-top: 4px;
}

/* --- Anhang-Zeile INNERHALB der Bubble (Öffnen + Download) --- */
.bubble-att {
  display: flex; align-items: center; gap: 10px; margin: 8px 0 2px;
  padding: 8px 10px; border-radius: 10px;
  background: var(--ask-media); border: 1px solid var(--bubble-in-border);
}
.bubble-att-ico { width: 30px; height: 30px; flex: none; color: var(--accent); }
.bubble-att-body { min-width: 0; flex: 1; }
.bubble-att-name {
  display: block; font-size: 13.5px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bubble-att-meta { display: block; font-size: 12px; color: var(--text-dim); }
/* .ask-btn wird hier bewusst wiederverwendet (so schon im Prototyp): es ist
   der etablierte, durchgetestete Button-Stil des Chats — ein zweiter, optisch
   identischer Satz wäre nur Drift-Potenzial. */
.bubble-att .ask-btn { padding: 6px 12px; font-size: 13.5px; }
.icon-btn {
  width: 32px; height: 32px; flex: none;
  display: flex; align-items: center; justify-content: center;
  border-radius: 8px; cursor: pointer; background: transparent;
  color: var(--text); border: 1px solid var(--border);
  transition: background .12s, border-color .12s, color .12s;
}
.icon-btn:hover { background: var(--ask-row-hover); border-color: var(--accent); color: var(--accent); }

/* --- Sandboxed HTML-Vorschau ---------------------------------------------
   Eigenes Overlay-Fenster statt dariaWM: dariaWM.open() nimmt nur registrierte
   App-IDs und existiert nur in der Desktop-Shell — dieses Fenster soll aber
   auch in der Tab-Shell aufgehen. Layer-Konvention + z-index analog Lightbox. */
.htmlview-root {
  position: fixed; inset: 0; z-index: 9000;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  background: color-mix(in srgb, #000 62%, transparent);
}
.htmlview-window {
  display: flex; flex-direction: column;
  width: min(1100px, 100%); height: min(820px, 100%);
  border-radius: 12px; overflow: hidden;
  background: var(--bg-elev); border: 1px solid var(--border);
  box-shadow: 0 18px 48px color-mix(in srgb, #000 45%, transparent);
}
.htmlview-bar {
  display: flex; align-items: center; gap: 10px; flex: none;
  padding: 8px 10px;
  background: var(--bg-alt); border-bottom: 1px solid var(--border);
}
.htmlview-title {
  flex: 1; min-width: 0; font-size: 13.5px; font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* „isoliert"-Plakette: macht sichtbar, dass die Seite ohne Zugriff auf die
   Sitzung läuft. Kein Deko-Element — es ist die einzige Stelle, an der die
   Sandbox für den Nutzer überhaupt erkennbar ist. */
.htmlview-badge {
  flex: none; font-size: 11px; font-weight: 600; padding: 2px 8px;
  border-radius: 5px; background: var(--accent-soft); color: var(--accent-strong);
}
.htmlview-btn {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  padding: 5px 10px; border-radius: 8px; cursor: pointer;
  font-size: 13px; font-family: inherit; text-decoration: none;
  background: var(--ask-row); color: var(--text); border: 1px solid var(--border);
  transition: background .12s, border-color .12s, color .12s;
}
.htmlview-btn:hover { background: var(--ask-row-hover); border-color: var(--accent); color: var(--accent); }
.htmlview-close { padding: 5px 8px; }
/* Der iframe rendert FREMDES HTML (von Daria gebaut). Weißer Grund, damit eine
   Seite ohne eigenes Styling im Dark-Theme nicht schwarz auf schwarz steht —
   das ist Nutzinhalt, kein Chrome, und folgt daher NICHT dem Theme. */
.htmlview-frame { flex: 1; width: 100%; border: 0; background: #fff; }

/* Greeting-Bubble (Session-ID-Anzeige am Anfang einer Konversation und bei /new
   im Endlos-Feed) — visuell als Marker abgesetzt, dunkelgrün im Light- wie im
   Dark-Mode. Schriftfarbe ist immer weiß, damit der Kontrast in beiden Themes
   stimmt; sonst sähe der Text im Dark-Mode komplett unleserlich aus. */
.bubble.greeting {
  
  
  
  
  /* Mittig + alle 4 Ecken rund, ohne Tail. Greeting markiert eine neue
     Session als Trenner — kein "Sender" → kein Sprechblasen-Schweif.
     Wunsch Dennis 2026-05-18. */
  align-self: center;
  
}
/* Tail des greeting-bubbles unterdruecken (frueher: explizit gezeichnet
   mit eigenem clip-path-Pfad weiter unten). content:none gewinnt gegen
   den ::before-Selektor, weil dort kein !important steht. */
.bubble.greeting::before { content: none !important; }

/* Greeting-Bubble bekommt den gleichen Telegram-Tail wie .bubble.in
   (links unten), nur in den Grün-Tönen. 1:1 dieselbe Geometrie wie
   `.bubble.in:not(.greeting)::before` — gespiegelter Pfad, gleicher
   Border-Versatz (bottom:-1px, right:calc(100%-1px)).
   Hinweis: `.bubble.in:not(.greeting)::before` matcht hier nicht, also
   überschreiben wir nichts, sondern ergänzen einen eigenen Selektor.
   Color via Light/Dark-Media-Query, weil Greeting-Töne hardcoded sind. */
.bubble.greeting::before {
  content: '';
  position: absolute;
  bottom: -1px;
  right: calc(100% - 1px);
  width: 12px;
  height: 20px;
  pointer-events: none;
  
  
  
  
}


/* Disintegration: Bubble shrinkt + fadet, Partikel fliegen radial weg.
   Telegram-Style "vanish in particles" wenn der Schritt abgeschlossen ist.
   Wir hängen den Effekt an `.bubble.dissolving` (egal welcher Sub-Kind),
   damit auch typing- und Streaming-Bubbles beim Wegmachen denselben
   weichen Auflöse-Effekt bekommen. */
.bubble.dissolving {
  
  pointer-events: none;
  /* Partikel müssen über den Rand der Bubble hinausfliegen können.
     .bubble.tool hat das schon; .bubble.in/.out per Default nicht. */
  overflow: visible;
}

.tool-dot {
  position: absolute;
  width: 5px; height: 5px;
  
  
  pointer-events: none;
  
  
  
}

.bubble pre {
   padding: 8px;
  overflow-x: auto;
}

/* ===== Code-Block-Highlighting + Copy-Button =============================
   enhanceCodeBlocks() in app.js wrappt jeden <pre><code> in einen
   .code-block-wrap und haengt einen Copy-Button oben rechts dran.
   highlight.js (vendor/highlight.min.{js,css}) liefert die Farben
   via .hljs-* Klassen — Theme-Wechsel via prefers-color-scheme.
   ========================================================================= */
.code-block-wrap {
  position: relative;
  margin: 6px 0;
  /* Border um den Code-Block, damit er sich vom Bubble-Hintergrund abhebt
     (im Light-Mode hat hljs-light einen sehr hellen Hintergrund). */
  border-radius: 6px;
  overflow: hidden;
}
.code-block-wrap pre {
  margin: 0;
  /* Wir lassen padding den hljs-Default (1em fuer pre code.hljs) — siehe
     /vendor/highlight.min.css, Zeile 1. Damit kein doppeltes Padding. */
  padding: 0;
  overflow-x: auto;
  /* Font-Size leicht reduziert, damit lange Zeilen seltener brechen. */
  font-size: 12.5px;
  line-height: 1.45;
}
.code-block-wrap pre code,
.code-block-wrap pre code.hljs {
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  /* Whitespace: pre erhält die original Code-Einrueckung. Bubble haette
     sonst pre-wrap geerbt und Tabs/Leerzeichen zerstoert. */
  white-space: pre;
  /* hljs-default padding ist 1em (16px). Wir bumpen padding-top, damit
     Sprach-Badge (top:4px) + Copy-Button (top:4px) NICHT mit der ersten
     Code-Zeile kollidieren. 24px reicht fuer Badge-Hoehe (~12px) + 8px
     Luft. padding-right erhoeht, damit der Copy-Button die letzte Zeile
     nicht ueberlappt. */
  padding-top: 24px;
  padding-right: 48px;
}

/* Sprach-Badge (z. B. „js") in der linken oberen Ecke, dezent.
   Nur wenn data-lang gesetzt ist (renderMd hat ```lang erkannt). */
.code-block-wrap[data-lang]::before {
  content: attr(data-lang);
  position: absolute;
  top: 4px;
  left: 8px;
  font-size: 10px;
  font-family: var(--mono, ui-monospace, monospace);
  color: rgba(0, 0, 0, 0.45);
  text-transform: lowercase;
  letter-spacing: 0.5px;
  pointer-events: none;
  /* Subtil: nur bei Hover voll sichtbar, sonst halbtransparent — damit
     der Badge den Code nicht stoert. */
  opacity: 0.55;
  transition: opacity 120ms;
  z-index: 1;
}
.code-block-wrap:hover[data-lang]::before {
  opacity: 0.95;
}

/* Copy-Button — Hover-Only (auf Desktop). Auf Touch dauerhaft sichtbar,
   damit man ihn ohne Hover erreichen kann (siehe @media unten). */
.code-copy-btn {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 2;
  border: 0;
  background: rgba(255, 255, 255, 0.85);
  color: #333;
  padding: 3px 8px;
  border-radius: 4px;
  font-size: 12px;
  line-height: 1.4;
  cursor: pointer;
  opacity: 0;
  transition: opacity 120ms, background 120ms, transform 120ms;
  /* Verhindert Subpixel-Verschiebung beim Erscheinen. */
  transform: translateZ(0);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
  /* Kein focus-outline klauen — Tastatur-Nutzer sehen Fokus weiterhin. */
}
.code-block-wrap:hover .code-copy-btn,
.code-copy-btn:focus,
.code-copy-btn:focus-visible {
  opacity: 1;
}
.code-copy-btn:hover {
  background: #fff;
  transform: translateY(-1px);
}
.code-copy-btn.code-copy-btn-ok {
  opacity: 1;
  background: #5cd65c;
  color: #fff;
}

/* Touch-Geraete koennen nicht hovern → Button dauerhaft sichtbar.
   coarse pointer ≈ Finger; fine ≈ Maus/Trackpad. */
@media (hover: none) and (pointer: coarse) {
  .code-copy-btn { opacity: 0.7; }
}

/* Dark-Mode-Anpassungen — der Code-Block-Hintergrund kommt von hljs,
   wir passen nur Border, Badge und Copy-Button an. */
/* @theme dark - war frueher @media (prefers-color-scheme: dark);
   2026-05-19 umgestellt auf html[data-theme="dark"], siehe
   Backlog-Item "Dark/Light/Auto-Toggle". */
  html[data-theme="dark"] .code-block-wrap[data-lang]::before {
    color: rgba(255, 255, 255, 0.55);
  }
  html[data-theme="dark"] .code-copy-btn {
    background: rgba(40, 40, 40, 0.85);
    color: #eee;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
  }
  html[data-theme="dark"] .code-copy-btn:hover {
    background: #333;
  }
  html[data-theme="dark"] .code-copy-btn.code-copy-btn-ok {
    background: #2da34a;
    color: #fff;
  }


/* Code-Bloecke ausserhalb der Bubble (z. B. Backlog-Result-Preview,
   Memory-Datei-Vorschau) — gleiche Behandlung, aber die Bubble-Defaults
   greifen dort nicht. enhanceCodeBlocks() laeuft global per
   MutationObserver, also brauchen wir nur ein generisches Styling. */
.backlog-preview .code-block-wrap,
.memory-preview .code-block-wrap,
.memory-row .code-block-wrap {
  margin: 8px 0;
}


/* Markdown-Renderer (renderMd in app.js) produziert diese Elemente —
   wir geben ihnen kompakte, zur Bubble passende Defaults statt der
   Browser-Defaults (h1 wäre sonst riesig, Listen würden weit einrücken). */
.bubble :first-child { margin-top: 0; }
.bubble :last-child  { margin-bottom: 0; }
.bubble h1, .bubble h2, .bubble h3,
.bubble h4, .bubble h5, .bubble h6 {
  margin: 8px 0 4px;  
}




.bubble ul, .bubble ol { margin: 4px 0; padding-left: 22px; }
.bubble li { margin: 2px 0; }
.bubble blockquote {
  margin: 4px 0; padding: 2px 10px;
  
  
}





.bubble table {
  
  margin: 6px 0;
  
  max-width: 100%;
  display: block;
  overflow-x: auto;
  white-space: normal;  /* überschreibt pre-wrap der Bubble, damit Zellen nicht
                           durch jeden Zeilenumbruch sprengt werden */
}
.bubble th, .bubble td {
  
  padding: 4px 8px;
  
  vertical-align: top;
}


.bubble .meta {
    margin-top: 4px;
}
.typing { display: inline-flex; gap: 4px; }
.typing span {
  width: 6px; height: 6px; 
   
  
}




.chat-input-row {
  display: flex; align-items: flex-end; gap: 8px;
  padding: 10px; 
  
}
#chat-input {
  flex: 1;
   
   
  padding: 10px 14px; 
  resize: none; max-height: 200px; min-height: 40px;
    
}

.send-btn {
    
  width: 42px; height: 42px; 
  display: grid; place-items: center; 
  
  flex-shrink: 0;
}




/* Stop-Button: ersetzt den Send-Button waehrend ein Claude-Turn laeuft.
   Roter Kreis mit Quadrat (Stop-Glyph), gleicher Slot wie der Send-Button
   (42×42). Klick ruft /api/chat/abort. */
.stop-btn {
    
  width: 42px; height: 42px; 
  display: grid; place-items: center; 
  
  flex-shrink: 0;
}




/* Forms im Account-Tab */
form label { display: block;   margin: 10px 0 4px; }
form input[type="password"], form input[type="text"] {
  width: 100%; max-width: 360px;
   
   
  padding: 10px 12px;  
}


/* ---------- Mikrofon-Button ---------- */
.mic-btn {
    
  width: 42px; height: 42px; 
  display: grid; place-items: center; 
  flex-shrink: 0; 
}





/* Transkriptions-Bubble während Whisper läuft */


/* Audio-Player für TTS-Antworten */
audio.voice-answer {
  display: block;
  margin-top: 8px;
  width: 100%;
  height: 36px;
  
}
/* Custom Web-Audio-Player (Safari-kompatibel) */
.voice-player {
  display: flex; align-items: center; gap: 10px;
  margin-top: 8px;
  padding: 6px 12px;
  
  
  user-select: none;
}

.voice-play-btn {
    
  width: 28px; height: 28px; 
  display: grid; place-items: center; 
    flex-shrink: 0;
  
}


.voice-progress {
  flex: 1; height: 6px;
  
  
   position: relative;
  min-width: 80px;
}

.voice-progress-bar {
  height: 100%; width: 0;
  
  
  
}
.voice-time {
  
   
  min-width: 70px; 
}
.voice-speed-btn {
    
  
  padding: 2px 8px;  
   flex-shrink: 0;
  
  
  min-width: 36px;
}


/* Popover: Schnellauswahl aller Geschwindigkeiten oberhalb des Speed-Buttons */
.voice-player { position: relative; } /* nötig fürs absolut positionierte Popover */
.voice-speed-picker {
  position: absolute;
  display: flex; flex-direction: column; gap: 2px;
  padding: 4px;
  
  
  
  
  z-index: 5;
  min-width: 56px;
}
.voice-speed-opt {
   
   
  padding: 6px 10px;
  
   
  
  
  
}


.voice-error {
  margin-top: 8px;
  padding: 6px 10px;
  
  
  
  
}

/* ---------- Inline-Keyboard (Telegram-Style Buttons in Bubbles) ---------- */
.inline-kb { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.inline-kb-row { display: flex; gap: 6px; flex-wrap: wrap; }
.inline-kb-btn {
  flex: 1;
   
   
  padding: 8px 12px;  
  
   min-width: 80px;
}





/* ---------- Slash-Popover (Telegram-Slash-Menü-Äquivalent) ---------- */
.slash-menu {
  position: fixed;
  
  
  
  
  max-height: 280px;
  overflow-y: auto;
  /* Ueber dem Window-Manager-Mount (#wm-mount, z-index:50) UND der Topbar
     (.app-header, z-index:60). Sonst rendert das Slash-Popover im Desktop-/
     Fenster-Layout HINTER dem Chat-Fenster und ist unsichtbar, obwohl es
     korrekt geoeffnet wird (hidden=false). Bleibt unter Modals (1000) + Toast (9999). */
  z-index: 200;
  padding: 4px;
}
.slash-item {
  display: flex; align-items: baseline; gap: 10px;
  width: 100%; 
    
  padding: 8px 12px;  
   
}


.slash-cmd {
  
   
  min-width: 70px;
}


/* Menü-Button links neben dem Chat-Input (Telegram-Style "☰") */
.slash-menu-btn {
    
  width: 40px; height: 40px; 
  display: grid; place-items: center; 
  flex-shrink: 0; 
}


/* ===== Gedächtnis-Tab ===== */
.memory-info .memory-rules { margin: 8px 0 4px; padding-left: 20px; }
.memory-info .memory-rules li { margin: 4px 0;  }
.memory-info .memory-restart {
  margin-top: 8px; padding: 8px 12px; 
   
  
}
.memory-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.memory-row {
  display: flex; flex-direction: column; gap: 8px;
  padding: 10px 12px; 
  
  
}

.memory-row-main {
  display: flex; justify-content: space-between; gap: 12px;
  align-items: flex-start; flex-wrap: wrap;
}

.memory-row-path {
  
    margin-top: 2px;
  word-break: break-all;
}
.memory-row-meta {
   
  white-space: nowrap;
}
.memory-row-actions { display: flex; gap: 6px; justify-content: flex-end; }
.memory-field-label {
  display: block; margin: 12px 0 4px;
   
}
.memory-editor {
  width: 100%; min-height: 320px;
  
   
  padding: 10px 12px; 
   
  
  resize: vertical;
}

#mem-new-name, #mem-desc {
  width: 100%; padding: 8px 10px; 
  
  
}
.memory-type-select {
  width: 100%; padding: 8px 10px; 
  
   
  
}
.memory-type-when {
  margin: 6px 0 0;  
  
}
.memory-checkbox-row {
  display: flex; align-items: center; gap: 8px;
  margin-top: 12px;  
}
.memory-checkbox-row input { margin: 0; }

/* ===== Sessions: Name-Input =====
   Liegt seit 2026-05-19 in `.session-name-row` (bottom-right der Session-
   Box, siehe oben `.session-item`-Grid). Margins jetzt 0, weil die Row
   selbst (gap, justify-content) die Position bestimmt; der Input bekommt
   nur noch sein eigenes Width-Cap. */
.session-name-input {
  width: 100%; max-width: 280px;
  margin: 0;
  padding: 4px 8px;
}

/* „AKTIV"-Badge (Backlog-Item „Aktive Sessions labeln" — Wunsch Dennis
   2026-05-19): Sessions mit aktuell laufendem Claude-Spawn bekommen ein
   prominentes rotes Pill, das sich von Source-Badges (Web/Telegram/CLI) und
   den dezenten „aktiv"-Labels (current Web/Bridge) klar absetzt. Pulsierender
   Punkt vorne, damit der Live-Charakter sofort erkennbar ist. */
.badge.aktiv {
  background: #c8203a;
  color: #fff;
  font-weight: 700;
  letter-spacing: 0.4px;
  position: relative;
  padding-left: 18px;
}
.badge.aktiv::before {
  content: '';
  position: absolute;
  left: 7px;
  top: 50%;
  transform: translateY(-50%);
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 0 rgba(255,255,255,0.7);
  animation: badge-aktiv-pulse 1.6s ease-out infinite;
}
@keyframes badge-aktiv-pulse {
  0%   { box-shadow: 0 0 0 0   rgba(255,255,255,0.7); }
  70%  { box-shadow: 0 0 0 7px rgba(255,255,255,0);   }
  100% { box-shadow: 0 0 0 0   rgba(255,255,255,0);   }
}
/* Animation aus, wenn der User reduced-motion bevorzugt — der rote Punkt
   bleibt, blinkt aber nicht. */
@media (prefers-reduced-motion: reduce) {
  .badge.aktiv::before { animation: none; }
}




/* ===== Chat: Attach-Chip ===== */
.attach-chip-wrap {
  padding: 6px 14px 0;
  display: flex;
}
.attach-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 10px;
  
  
  
   
  max-width: 100%;
}
.attach-chip-name {
  
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 280px;
}

.attach-chip-remove {
    
    
  width: 20px; height: 20px;
  display: grid; place-items: center;
  
}


/* ===== Memory: Typen-Badges + Erklärungs-Legende ===== */
.memory-row-text { flex: 1; min-width: 0; }
.memory-row-desc {   margin: 2px 0; }
.memory-row-when {
    margin: 2px 0 4px;
  
}
.memory-type-badge {
  display: inline-block;
  padding: 1px 8px;
   
  
  vertical-align: middle;
  margin-left: 4px;
  
  
}




.memory-type-legend {
   padding: 0; margin: 8px 0 12px;
  display: flex; flex-direction: column; gap: 4px;
   
}
.memory-type-legend li {
  display: flex; gap: 8px; align-items: baseline;
}
.memory-type-legend .memory-type-badge { margin: 0; }

/* ===== Account: Lockout-Selects nebeneinander/wie inputs ===== */
#lockout-form select {
  display: block; margin-bottom: 0;
  padding: 8px 10px; 
}

/* Generische Inline-Reihe für Account-Forms (Login-Sperre, Passwort ändern):
   Felder + zugehöriger Submit-Button sitzen alle in einer Zeile, Labels
   stehen über dem jeweiligen Input. `align-items: flex-end` lässt den Button
   bündig zur Input-Unterkante stehen, auch wenn die Labels mehrzeilig sind.
   Auf schmalen Viewports wrappen die Felder; Buttons rutschen dann unter
   die zuletzt sichtbare Spalte. */
.inline-row {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: wrap;
}
.inline-field {
  display: flex; flex-direction: column;
  min-width: 160px;
}
.inline-field label {
  margin: 0 0 4px;
}
.inline-row input[type="password"],
.inline-row input[type="text"] {
  /* Override des form input-Defaults, der max-width: 360px setzt — in der
     Inline-Row soll das Input die Spaltenbreite voll ausnutzen. */
  max-width: none;
  margin: 0;
}
.inline-row button[type="submit"] {
  /* Keine Top-Margin, damit der Button bündig zur Input-Unterkante sitzt. */
  margin: 0;
}

/* ===== Chat: Media-Bubble (Telegram-Style Inline-Bild + Caption) ===== */
/* Bubble enthält ein Bild oder eine Dateikarte oben und – optional –
   eine Caption darunter, beides als gemeinsame Sprechblase. Damit der
   Bubble-Rahmen das Bild sauber abschneidet, schalten wir Padding und
   Overflow um (sonst guckt das Bild an den Ecken raus). Zusätzlich eine
   min-width/min-height, damit die Bubble auch während des Bild-Ladens
   bzw. bei sehr kleinen Bildern nie zur „dünnen Linie" zusammenfällt
   und Dennis weiß: hier ist eine Bubble, kein Fehler. */
/* Bild-Bubble: bewusst minimalistisch, kein Layout-Magic.
   - Padding 4px. `overflow: visible`, damit der Telegram-Tail
     (`.bubble.out::before` Pseudo-Element) nicht weggeclippt wird.
     Das Bild bleibt trotzdem innerhalb der Rundung, weil
     `.bubble-media` selbst `overflow: hidden` + `border-radius` hat.
   - JS setzt nach `img.onload` die Bubble-Breite per Inline-Style
     auf die tatsächlich gerenderte Bildbreite (siehe
     `addAttachmentBubble`), damit die Bubble exakt so breit ist wie
     das Bild — unabhängig von Browser-Layout-Quirks. */
.bubble.bubble-with-media {
  padding: 4px;
  overflow: visible;
}
.bubble.bubble-with-media[data-time] {
  padding-right: 4px;
  padding-bottom: 4px;
}
.bubble-media {
  display: block;
  margin: 0;
  
  overflow: hidden;
  
}

/* `font-size: 0` auf dem `<a>` killt die anonyme Inline-Linebox, die sonst
   ~24px Whitespace unter dem Bild erzeugt. Der Lade-Placeholder ist ein
   Sibling vom `<a>` und behält seinen eigenen `font-size`. */
.bubble-media a {
  display: block;
  
  
}
.bubble-media img {
  display: block;
  /* Ohne explizite Constraints aus dem CSS: JS misst `naturalWidth/Height`
     im `onload` und setzt die effektive Render-Größe als Inline-Style. */
  max-width: 100%;
  height: auto;
  
  
  /* Horizontal mittig in der Bubble. Sobald die Caption breiter ist als
     das Bild (das Bild wird ja auf max 40% chatW gecapped), bekommt das
     Bild durch die margin-auto-Logik gleichen Abstand links + rechts.
     Bei gleicher Breite (kleines Bild ohne Caption) sind die auto-Margins
     0 px → kein Layout-Sprung. Wunsch Dennis 2026-05-18. */
  margin-inline: auto;
}
/* Transparenz sichtbar machen (2026-06-05): Schachbrett-/Karo-Hintergrund
   HINTER dem Bild. Bei opaken Bildern (JPEG, PNG ohne Alpha) deckt das Bild
   das Karo vollständig ab — unsichtbar. Nur bei transparenten Bereichen
   (Alpha-PNG, transparentes WebP/GIF) scheint das Karo durch, statt das Bild
   auf einen undurchsichtigen Hintergrund flachzurechnen. Das Karo wird per
   CSS-Gradients gezeichnet (kein Asset, CSP-konform). Greift in Chat-Bubble
   UND Lightbox. */
.bubble-media img,
.lightbox-img {
  /* Helle Grundfarbe + zwei diagonale Gradient-Paare = klassisches 16px-Karo. */
  background-color: #ffffff;
  background-image:
    linear-gradient(45deg, #c9ccd1 25%, transparent 25%),
    linear-gradient(-45deg, #c9ccd1 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #c9ccd1 75%),
    linear-gradient(-45deg, transparent 75%, #c9ccd1 75%);
  background-size: 16px 16px;
  background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}
/* Lade-Placeholder, bis `img.onload` feuert. */
.bubble-media-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 132px;
  min-height: 72px;
  padding: 12px 16px;
  
  
  
}
/* Caption hat denselben linksbündigen Lese-Look wie ein Text-Bubble.
   Steht jetzt OBERHALB des Bildes (Telegram-Style), damit der Text auch
   sichtbar bleibt, wenn das Bild auf knappe Chat-Höhe geschrumpft wird. */
.bubble-caption {
  padding: 6px 10px 4px;
  white-space: pre-wrap;
  word-wrap: break-word;
}
/* Wenn die Caption oberhalb steht, braucht sie unten 6px Luft zum Bild
   und keinen Platz für den Zeitstempel (der sitzt rechts unten im Bubble,
   unter dem Bild). */
.bubble-caption.bubble-caption-top {
  padding: 4px 8px 6px;
}
/* Dateikarte (kein Bild): Telegram-Look mit Datei-Icon, Name, Größe.
   Hover-Highlight + Cursor zeigen Klickbarkeit (Download). */
.bubble-file {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px;
  
  
  
  
  
}


.bubble-file-icon {
  flex-shrink: 0;
  width: 40px; height: 40px;
  
  display: grid; place-items: center;
  
  
}
.bubble-file-meta {
  display: flex; flex-direction: column;
  min-width: 0;
}
.bubble-file-name {
   
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  max-width: 240px;
}


/* ===== Chat: Audio-Anhang (Custom-Player + Meta-Zeile) =====
   Player nutzt das gleiche Look-and-Feel wie `.voice-player` (TTS-
   Antworten), mit kleiner Anpassung: kein `margin-top`, weil der
   Player in der Bubble direkt oben sitzt, nicht unter einer Caption. */
.bubble-audio-player {
  margin-top: 0 !important;
  min-width: 280px;
}
.bubble-audio-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 4px;
  padding: 4px 6px;
  
  
  
}




/* ===== Chat: Drag&Drop-Overlay ===== */
.chat-dropzone {
  position: absolute;
  inset: 0;
  z-index: 30;
  display: grid;
  place-items: center;
  
  
  
  
  
  pointer-events: none;
  
}

.chat-dropzone-inner {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 24px 28px;
  
  
  
  
  
  
}

.chat-wrap.drag-over { /* Hook für zukünftige Effekte am Container selbst. */ }

/* ===== Chat: Composer-Preview-Strip (Multi-Upload, 2026-05-26) =====
   Liegt direkt über .chat-input-row und zeigt die aktuell ausgewählten
   Anhänge als horizontale Tile-Reihe. Bilder als Thumbnail, andere Files
   als Icon-Karte. Jeder Tile hat ein ✕-Button. Bei vielen Anhängen wird
   horizontal gescrollt (overflow-x). Mobile: Tiles + Schriftgrössen
   leicht kleiner. */
.composer-attachments {
  border-top: 1px solid var(--border-color, rgba(255,255,255,0.08));
  background: var(--composer-bg, transparent);
  padding: 8px 12px 6px;
}
.composer-attachments[hidden] { display: none; }
.composer-attachments .att-strip-header {
  font-size: 12px;
  color: var(--text-muted, #888);
  margin-bottom: 6px;
}
.composer-attachments .att-strip-tiles {
  display: flex;
  flex-direction: row;
  gap: 8px;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  padding-bottom: 2px;
}
.att-strip-tile {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  min-width: 0;
  flex: 0 0 auto;
  max-width: 240px;
  padding: 6px 28px 6px 6px;
  border: 1px solid var(--border-color, rgba(255,255,255,0.10));
  border-radius: 8px;
  background: var(--bubble-bg-out, rgba(255,255,255,0.04));
}
.att-strip-tile .att-strip-thumb {
  width: 40px; height: 40px;
  object-fit: cover;
  border-radius: 6px;
  flex: 0 0 40px;
  background: rgba(0,0,0,0.18);
}
.att-strip-tile .att-strip-icon {
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border-radius: 6px;
  flex: 0 0 40px;
  color: var(--text-muted, #888);
  background: rgba(255,255,255,0.04);
}
.att-strip-tile .att-strip-meta {
  display: flex; flex-direction: column;
  min-width: 0;
}
.att-strip-tile .att-strip-name {
  font-size: 13px;
  line-height: 1.2;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 160px;
}
.att-strip-tile .att-strip-size {
  font-size: 11px;
  line-height: 1.2;
  color: var(--text-muted, #888);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 160px;
}
.att-strip-tile .att-strip-remove {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 22px; height: 22px;
  display: grid; place-items: center;
  border: none;
  background: rgba(0,0,0,0.45);
  color: #fff;
  border-radius: 50%;
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
  padding: 0;
  opacity: 0.85;
}
.att-strip-tile .att-strip-remove:hover,
.att-strip-tile .att-strip-remove:focus-visible {
  opacity: 1;
  outline: 2px solid var(--accent, #4ea1ff);
  outline-offset: 1px;
}

html.is-mobile .att-strip-tile { max-width: 200px; }
html.is-mobile .att-strip-tile .att-strip-name,
html.is-mobile .att-strip-tile .att-strip-size { max-width: 130px; }

/* ===== Chat: Anhang-Preview-Modal (Telegram-Style) ===== */
.modal-card.modal-card-attachment { max-width: 520px; }
.att-preview-image {
  display: grid; place-items: center;
  
  
  padding: 6px;
  margin-bottom: 12px;
}
.att-preview-image img {
  display: block;
  max-width: 100%;
  max-height: 360px;
  width: auto; height: auto;
  
}
.att-preview-file {
  display: flex; align-items: center; gap: 12px;
  padding: 12px;
  
  
  
  margin-bottom: 12px;
}
.att-preview-file-icon {
  flex-shrink: 0;
  width: 48px; height: 48px;
  
  
  
  display: grid; place-items: center;
}
.att-preview-file-meta {
  display: flex; flex-direction: column; gap: 2px;
  min-width: 0;
}
.att-preview-file-name {
  
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.att-preview-label {
  display: block;
   
  margin: 4px 0;
}
.att-preview-caption {
  width: 100%;
  min-height: 44px;
  max-height: 160px;
  padding: 10px 12px;
  
  
  
  
  
  resize: vertical;
  
  box-sizing: border-box;
}


/* ===== Markdown-Image im Chat (Daria postet ein Bild via ![alt](url)) ===== */
.md-image {
  display: block;
  margin: 4px 0;
  
}
.md-image img {
  /* Gleiche Logik wie `.bubble-media img`: ganzes Bild zeigen, im Zweifel
     verkleinern, Aspekt erhalten. Klick öffnet Lightbox (siehe app.js). */
  max-width: 100%;
  max-height: 70vh;
  width: auto; height: auto;
  
  
  
}

/* ===== Image Lightbox (Click auf Inline-Bild im Chat) ===== */
/* Eigener Layer (NICHT der openModal-Layer), weil wir das Bild bis ans
   Viewport-Limit groß zeigen wollen — der Modal-Card-Container hätte da
   eine fixe max-width im Weg. ESC + Backdrop-Klick + X-Button schließen. */
.lightbox-root {
  position: fixed;
  inset: 0;
  z-index: 2000;
  
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 56px 16px 16px;
  
}

.lightbox-toolbar {
  position: absolute;
  top: 12px; right: 12px;
  display: flex; gap: 8px; align-items: center;
  z-index: 1;
}
.lightbox-btn {
  display: inline-flex; align-items: center; gap: 6px;
  
  
  
  
  padding: 8px 12px;
   
  
  
  
}

.lightbox-btn svg { display: block; }
/* Schließen-Button ist nur das Icon (kein Label) */
.lightbox-btn#lb-close { padding: 8px; }
.lightbox-stage {
  flex: 1;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  /* min-height: 0 — sonst sprengt die intrinsic height der img die
     parent-Größe und der Stage wird größer als der verfügbare Bereich. */
  min-height: 0;
  overflow: hidden;
}
.lightbox-img {
  display: block;
  /* Bild soll bis ans Viewport-Limit groß werden, aber nie clipped. */
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  
  
  
  
}

/* Utility */
[hidden] { display: none !important; }

/* C7 (2026-05-17): CSS-Klassen statt inline-style, damit die CSP
 * `style-src 'self'` (ohne 'unsafe-inline') greifen kann.
 * Tabellen-Zell-Alignment aus Markdown-Tabellen. */




/* Label links neben der „...tippt"-Animation (Dots stehen rechts vom
   Label, Wunsch Dennis 2026-05-18). */

/* Inline-Variante der Typing-Dots: rechts vom Bubble-Text (Tool-Calls,
   Streaming-Text), nicht als eigene Bubble. Vertikal mittig an der
   Text-Baseline, kleiner Abstand links zum Text. Dots-Animation kommt
   weiterhin aus `.typing span`-Selektor (oben). */
.typing-inline {
  display: inline-flex;
  vertical-align: middle;
  margin-left: 8px;
}

/* Status-Card: volle Session-UUIDs lesbar darstellen — monospace + sauberer
   Umbruch, damit die 36-Zeichen-UUID nicht den Card-Layout zerschießt. */
#st-web-session,
#st-session {
  
  
  word-break: break-all;
  
}

/* ---------- TLS-Cert-Karte im Status-Tab ----------
 * Vorher: inline-styles mit #fafafa-Background + #555-color → im Dark-Mode
 * weiss-auf-weiss unleserlich, weil der globale color: var(--text) (= weiss)
 * den nicht ueberall gesetzten Card-Text uebernimmt. Jetzt: theme-aware
 * via CSS-Variablen, plus dedizierte Status-Klassen fuer den Badge.
 */
.cert-card {
  
  
  
  
  padding: 12px 14px;
  margin-top: 8px;
  
}
.cert-card .cert-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  flex-wrap: wrap;
}
.cert-card .cert-info { flex: 1; min-width: 240px; }


.cert-card .cert-detail {
  
  
  margin-top: 4px;
  
}

.cert-card .cert-detail code {
  
  word-break: break-all;
  
  
  padding: 0 4px;
  
}
.cert-card .cert-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.cert-card .cert-badge {
  margin-left: 8px;
  
  
}




/* ---------- .env-Editor ----------
 * Sensible Keys (redacted) brauchen einen klaren Hinweis "leer = unveraendert",
 * sonst ueberschreibt ein arglos geklickter "Speichern" den Token mit dem
 * angezeigten Maskenwert (frueher "null").
 */
.env-row {
  display: grid;
  grid-template-columns: minmax(160px, 22%) 1fr auto;
  align-items: center;
  gap: 8px;
}
.env-row .env-key {
  
  
  
  word-break: break-all;
}

.env-row .env-actions { display: flex; gap: 4px; }
.env-row .env-redacted-hint {
  grid-column: 2 / 3;
  
  
  margin-top: -4px;
}
@media (max-width: 600px) {
  .env-row {
    grid-template-columns: 1fr;
  }
  .env-row .env-redacted-hint { grid-column: 1 / 2; }
}

/* ---------- Telegram-Setup-Card ----------
 * Dedizierte UI fuer TELEGRAM_BOT_TOKEN + USER_ID mit Inline-Anleitung
 * (@BotFather/@userinfobot). Schreibt ueber /api/env wie der generische
 * Editor, aber prominenter platziert. Theme-aware via CSS-Variablen.
 */
.tg-setup-howto {
  
  
  
  padding: 10px 14px;
  margin: 10px 0 16px;
}
.tg-setup-howto > summary {
  
  
  
  padding: 4px 0;
  
  position: relative;
  padding-left: 20px;
}
.tg-setup-howto > summary::-webkit-details-marker { display: none; }
.tg-setup-howto > summary::before {
  content: '▶';
  position: absolute;
  left: 0;
  
  top: 50%;
  
  
  
}


.tg-setup-steps {
  margin: 12px 0 8px;
  padding-left: 24px;
  
  
  
}
.tg-setup-steps li { margin-bottom: 6px; }

.tg-setup-howto code,
.tg-setup-row code {
  
  
  padding: 0 4px;
  
  
}
.tg-setup-row {
  margin: 14px 0;
}
.tg-setup-label {
  display: block;
  margin-bottom: 6px;
}
.tg-setup-label > span {
  display: block;
  
  
}
.tg-setup-label > .hint {
  display: block;
  margin-top: 2px;
  
  
  
}
.tg-setup-input {
  display: flex;
  gap: 6px;
  align-items: center;
}
.tg-setup-input input {
  flex: 1;
  min-width: 0;
}
.tg-setup-actions {
  display: flex;
  gap: 8px;
  margin-top: 14px;
  justify-content: flex-end;
  flex-wrap: wrap;
}

/* ---------- Modal-Dialog (generisch, derzeit nur Telegram-Setup) ----------
 * Backdrop liegt fixed ueber dem ganzen Viewport, der Dialog zentriert
 * mittig. Body-Klasse `modal-open` sperrt das Background-Scrolling, damit
 * der User nicht versehentlich den Chat hinter dem Modal scrollt.
 */
body.modal-open { overflow: hidden; }
.modal-backdrop {
  position: fixed;
  inset: 0;
  
  z-index: 1000;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 5vh 16px;
  overflow-y: auto;
  /* Sanftes Reinflashen — kein Fade, kein Slide, nur das Minimum. */
}
.modal-backdrop[hidden] { display: none; }
.modal-dialog {
  
  
  
  
  
  width: 100%;
  max-width: 640px;
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 10vh);
}
.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
  
}
.modal-header h2 {
  margin: 0;
  
  
  
}
.modal-close {
  
  
  
  
  
  width: 32px;
  height: 32px;
  
  
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-body {
  padding: 16px 18px;
  overflow-y: auto;
  flex: 1 1 auto;
}
.modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 18px;
  
  flex-wrap: wrap;
}
@media (max-width: 600px) {
  .modal-backdrop { padding: 0; }
  .modal-dialog {
    max-width: 100%;
    min-height: 100vh;
    max-height: 100vh;


  }
}

/* === Session-Viewer-Overlay (2026-05-22) ================================
   Eigenstaendiges, read-only Overlay-Fenster fuer den Verlauf einer
   beliebigen Session — siehe openSessionOverlay() in app.js. Es baut
   bewusst auf den bereits getheme-ten Modal-Klassen auf (.modal-backdrop/
   .modal-dialog/.modal-header/.modal-body/.modal-close), damit Farben und
   Hintergrund pro Theme „einfach passen". Hier nur die strukturellen
   Abweichungen: groesseres Fenster, die innere .chat-list fuellt den Body,
   ein Live-Badge im Header. */
.session-overlay-dialog {
  /* Breiter + fest hoch, damit der Chat-Verlauf viel Platz hat und die
     innere Liste scrollt statt das ganze Fenster. */
  max-width: min(1100px, 95vw);
  width: 100%;
  height: calc(100vh - 10vh);
  max-height: calc(100vh - 10vh);
}
.session-overlay-titlewrap {
  /* min-width:0 erlaubt dem Titel das Ellipsen-Clipping im Flex-Header. */
  min-width: 0;
}
.session-overlay-title {
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.session-overlay-subtitle {
  margin: 2px 0 0;
  font-size: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.session-overlay-headright {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
.session-overlay-badge {
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.session-overlay-badge[hidden] { display: none; }
.session-overlay-badge-live    { color: var(--accent, #2e8b57); }
.session-overlay-badge-running { color: var(--warn, #c98a00); }
.session-overlay-badge-done    { color: var(--text-dim, #888); }
/* Der Body soll NICHT selbst scrollen — die innere .chat-list uebernimmt
   das (flex:1 + overflow-y:auto). Sonst gaebe es zwei Scrollbalken. */
.session-overlay-body {
  padding: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}
.session-overlay-list {
  flex: 1 1 auto;
  min-height: 0;
}
/* Lade-/Leer-/Fehler-Hinweis mittig in der sonst leeren Liste. */
.session-overlay-empty {
  margin: auto;
  padding: 24px;
  text-align: center;
}
@media (max-width: 600px) {
  .session-overlay-dialog {
    max-width: 100%;
    width: 100%;
    height: 100vh;
    max-height: 100vh;
  }
}

/* Multi-Anhang-Bubble (TG-Album oder Web-Multi-Upload). Auto-Grid
   mit Variabel-Spaltenzahl je nach Anhang-Anzahl. Jedes Tile ist
   quadratisch (object-fit:cover crop'd), sodass das Layout auch bei
   ungerade vielen Bildern visuell ruhig bleibt. */
.bubble-media-grid {
  display: grid;
  gap: 3px;
  margin-top: 4px;
  
  overflow: hidden;
}
.bubble-media-grid[data-count="2"] { grid-template-columns: 1fr 1fr; }
.bubble-media-grid[data-count="3"] { grid-template-columns: 1fr 1fr 1fr; }
.bubble-media-grid[data-count="4"] { grid-template-columns: 1fr 1fr; }
.bubble-media-grid[data-count="5"], .bubble-media-grid[data-count="6"] { grid-template-columns: 1fr 1fr 1fr; }
.bubble-media-grid[data-count="7"], .bubble-media-grid[data-count="8"], .bubble-media-grid[data-count="9"] { grid-template-columns: 1fr 1fr 1fr; }
.bubble-media-tile {
  position: relative;
  aspect-ratio: 1 / 1;
  
  overflow: hidden;
  
}
.bubble-media-tile a { display: block; width: 100%; height: 100%; }
.bubble-media-tile img {
  width: 100%;
  height: 100%;
  
  display: block;
  
}
.bubble-media-multi { max-width: 480px; }

/* Telegram-Stil-Reaction-Pill: Emoji + kleines User-Initial-Avatar +
   Username, als kleine abgerundete Pille UNTEN-LINKS in der Bot-Bubble.
   Mehrere Pills nebeneinander, wenn mehrere Reactions auf dieselbe
   Bubble kommen. Hover hebt sie leicht hervor — als visuelles Echo,
   nicht als Aktion. */
.bubble-reactions {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 8px 0 0 -4px;
}
.reaction-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px 2px 4px;
  
  
  
  
  
  user-select: none;
}

.reaction-pill .reaction-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  
  
  
  
  
}
/* Avatar als echtes Profilbild — img-Variante, wenn vorhanden. Wenn
   das Bild fehlschlaegt, ersetzt das JS es durch ein <span> mit
   Initial-Buchstaben (s. applyReaction in app.js). */
.reaction-pill img.reaction-avatar-img {
  width: 16px;
  height: 16px;
  
  
  display: inline-block;
}


/* Multi-Upload-Preview (Phase 2). Grid mit allen Tiles + gemeinsamer
   Caption darunter. Tiles sind quadratisch (object-fit:cover); Layout
   skaliert via data-count analog .bubble-media-grid. */
.att-preview-multi-grid {
  display: grid;
  gap: 4px;
  margin-bottom: 8px;
  max-height: 380px;
  overflow-y: auto;
}
.att-preview-multi-grid[data-count="2"] { grid-template-columns: 1fr 1fr; }
.att-preview-multi-grid[data-count="3"], .att-preview-multi-grid[data-count="4"] { grid-template-columns: 1fr 1fr; }
.att-preview-multi-grid[data-count="5"], .att-preview-multi-grid[data-count="6"], .att-preview-multi-grid[data-count="7"], .att-preview-multi-grid[data-count="8"], .att-preview-multi-grid[data-count="9"], .att-preview-multi-grid[data-count="10"] {
  grid-template-columns: 1fr 1fr 1fr;
}
.att-preview-multi-tile {
  position: relative;
  aspect-ratio: 1 / 1;
  
  overflow: hidden;
  
}
.att-preview-multi-tile img {
  width: 100%; height: 100%;
   display: block;
}
.att-preview-multi-tile.att-preview-multi-file {
  display: flex; flex-direction: column; justify-content: center;
  align-items: center; padding: 8px; 
   
}
.att-preview-multi-meta {
    margin-bottom: 6px;
}

/* ===========================================================================
   Backlog (Admin-Tab) — Aufgabenliste für autonome Daria-Arbeit
   =========================================================================== */

/* Position: relative auf der Card, damit das Drop-Overlay (position:
   absolute, inset: 0) genau die Card-Flaeche bedeckt — nicht das gesamte
   Viewport. Eingefuehrt 2026-05-18 zusammen mit dem Drag-and-Drop-Feature. */
.backlog-card { position: relative; }

/* ============================================================================
   Backlog-Tab (eigene Seite) — 2026-05-21, Wunsch Dennis
   „Überarbeitung der Backlog Seite" (Backlog-Item 949cbd0c).

   Der Backlog ist seit dieser Iteration ein eigener Top-Level-Tab
   (#panel-backlog) statt einer Card im Admin-Tab. Layout:
     • Vollbild-Seite (.backlog-page) analog zum Chat-Tab — randlos,
       Höhe = Viewport minus Header.
     • Zwei fixe Kopfzeilen (.backlog-topbar): Zeile 1 = Aktionen +
       Kennzahlen, Zeile 2 = Shortcuts. Beide scrollen NICHT mit.
     • Darunter ein 40/60-Split (.backlog-split): links das
       Anlege-Formular (fix), rechts die Liste (vertikal scrollbar).
   Nur .backlog-pane-right scrollt — alles andere bleibt stehen.
   ============================================================================ */

/* Randlos + ohne 880px-Limit, exakt wie der Chat-Tab. */
body.tab-backlog #app-main { max-width: none; padding: 0; }

/* Tasks-Built-in-App + App-Launcher-Catalogue-Apps (Daily Planner etc.)
   bekommen — wie Chat und Backlog — die volle Container-Breite, statt
   in der 880-px-Karten-Optik gepinnt zu bleiben. body.tab-tasks und
   body.tab-app-launcher werden in app.js#selectTab() gesetzt (2026-05-30,
   Backlog-Item 2529448f: Regression aus 3f198776). Im Desktop-Modus
   greift diese Regel nicht, weil #app-main dort komplett ausgeblendet
   ist und die Apps in <daria-window>-Fenstern rendern. */
body.tab-tasks #app-main,
body.tab-app-launcher #app-main { max-width: none; padding: 0; }

/* Die Seite füllt den Viewport unter dem ~60px hohen, sticky Header.
   Flex-Column: zwei fixe Kopfzeilen, darunter der Split. overflow:hidden,
   damit die Seite selbst nie scrollt — das übernimmt nur die rechte Spalte. */
.backlog-page {
  display: flex;
  flex-direction: column;
  height: calc(100dvh - 60px);
  overflow: hidden;
}

/* Kopfzeilen: flex-shrink:0 → sie behalten ihre Höhe und scrollen nie mit. */
.backlog-topbar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px 16px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-alt, var(--bg-card));
}
/* Zeile 1: Aktionen links, Kennzahlen rechts — maximal auseinandergezogen. */
.backlog-topbar-1 { justify-content: space-between; flex-wrap: wrap; }
.backlog-topbar-actions,
.backlog-topbar-stats {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.backlog-topbar-stats { justify-content: flex-end; }
/* Kennzahlen-Zeile mittig auf gleicher Höhe ausrichten (2026-05-21, Wunsch
   Dennis „Große Backlog Seiten Anpassung"). Queue-ETA und Worker-Status
   tragen die Klasse `.hint`, die ein `margin: 0 0 8px` mitbringt — in der
   `align-items:center`-Flexbox zog dieses Bottom-Margin die beiden Elemente
   um 4 px nach OBEN, während die Kennzahl-Pillen (ohne `.hint`) auf der
   echten Mitte saßen. Hier alle direkten Kinder margin-frei stellen, dann
   zentriert die Flexbox sie alle exakt auf dieselbe Höhe. */
.backlog-topbar-stats > * { margin: 0; }

/* Kennzahl-Pillen (wartend / fertig / fehlgeschlagen / abgebrochen).
   Dezente Pill-Optik in der Hint-Größe; „fertig" grünlich,
   „fehlgeschlagen" gelblich (Warnung — Worker-Abbruch, Operator-
   Inspektion nötig), „abgebrochen" rötlich getönt. */
.backlog-stat {
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 0.85em;
  white-space: nowrap;
  cursor: help; /* title-Attribut erklärt die Zahl */
  background: var(--bg-elev, rgba(127, 127, 127, 0.12));
  color: var(--text-dim, var(--text));
}
.backlog-stat-done {
  background: var(--pill-completed-bg, rgba(40, 167, 69, 0.16));
  color: var(--pill-completed-fg, #1e7e34);
}
.backlog-stat-failed {
  background: var(--pill-warn-bg, rgba(255, 159, 10, 0.18));
  color: var(--pill-warn-fg, #b36b00);
}
.backlog-stat-cancelled {
  background: var(--pill-err-bg, rgba(220, 53, 69, 0.16));
  color: var(--pill-err-fg, #b3001b);
}
/* Klickbare Zähler-Pillen (2026-06-01, Wunsch Dennis "Backlog Tasks bei
   Klick auf die jeweiligen Anzeigen archivieren"): fertig/fehlgeschlagen/
   abgebrochen lassen sich anklicken, um die ganze Kategorie zu archivieren.
   cursor:pointer statt des sonst geltenden cursor:help signalisiert das, ein
   leichtes Anheben + Outline beim Hover gibt Klick-Feedback. */
.backlog-stat-clickable {
  cursor: pointer;
  user-select: none;
}
.backlog-stat-clickable:hover {
  filter: brightness(0.95);
  outline: 1px solid currentColor;
}
.backlog-stat-clickable:active {
  filter: brightness(0.9);
}

/* „+ Shortcut"-Button (neuen Shortcut anlegen). */
.backlog-shortcut-add { white-space: nowrap; }

/* 40/60-Split. flex:1 → nimmt die Resthöhe unter den Kopfzeilen.
   min-height:0 ist Pflicht: ohne das würden die scrollbaren Flex-Kinder
   ihre Inhaltshöhe erzwingen statt eine Scrollbar zu zeigen. */
.backlog-split {
  flex: 1;
  min-height: 0;
  display: flex;
}
.backlog-pane { box-sizing: border-box; }

/* Linke Spalte (40 %): Anlege-Formular. Scrollt NICHT mit der Liste.
   position:relative ist Bezugspunkt für das Drop-Overlay (#backlog-dropzone).
   overflow-y:auto ist nur ein Sicherheitsnetz — passt das Formular nicht in
   die Höhe (sehr kleiner Viewport), bekommt es eine eigene Scrollbar, statt
   gemeinsam mit der Liste zu scrollen. */
/* Die Breite ist seit dem Drag-Splitter (2026-06-06) nicht mehr fix:
   backlog-splitter.js setzt die CSS-Custom-Property --backlog-left-width
   am .backlog-split-Container (per CSSOM, nicht per inline-style → CSP-ok).
   Fallback 40 %, falls JS nicht lief oder kein persistierter Wert da ist.
   Den sichtbaren Trennstrich liefert jetzt der Splitter (::before), darum
   trägt die linke Spalte keinen border-right mehr (sonst Doppellinie). */
.backlog-pane-left {
  flex: 0 0 var(--backlog-left-width, 40%);
  max-width: var(--backlog-left-width, 40%);
  position: relative;
  overflow-y: auto;
  padding: 14px 16px;
}
/* Rechte Spalte: die Backlog-Liste, vertikal scrollbar. min-width:0 erlaubt
   das Schrumpfen unter die Inhaltsbreite (sonst sprengt langer Text den
   Flex-Anteil). Kein fester Flex-Anteil mehr — sie nimmt schlicht den Rest. */
.backlog-pane-right {
  flex: 1 1 auto;
  min-width: 0;
  overflow-y: auto;
  padding: 14px 16px;
}

/* ── Drag-Splitter zwischen den beiden Spalten (2026-06-06) ──────────────
   Schmale, greifbare vertikale Leiste. Monochrom (nur --border/--text-muted),
   col-resize-Cursor, dezent hervorgehoben bei Hover/Drag. touch-action:none
   ist Pflicht, damit Pointer-Events beim Touch-Drag nicht vom Scroll-Gesten-
   Handling des Browsers geschluckt werden. */
.backlog-splitter {
  flex: 0 0 9px;
  align-self: stretch;
  position: relative;
  cursor: col-resize;
  background: transparent;
  touch-action: none;
  z-index: 2;
}
/* Der eigentlich sichtbare Trennstrich — als Pseudo-Element zentriert in der
   9px-Greifzone, damit die Klickfläche breiter ist als die optische Linie. */
.backlog-splitter::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  transform: translateX(-50%);
  background: var(--border, rgba(127, 127, 127, 0.25));
  transition: width .12s ease, background .12s ease;
}
.backlog-splitter:hover::before,
.backlog-splitter.dragging::before {
  width: 3px;
  background: var(--text-muted, #888);
}
/* Tastatur-Fokus (der Splitter ist per tabindex fokussierbar). */
.backlog-splitter:focus-visible {
  outline: 2px solid var(--accent, #4ea1ff);
  outline-offset: -3px;
}
.backlog-pane-title { margin: 0 0 4px; }
/* Im Spalten-Layout sitzt das Formular direkt in der Spalte — die
   margin-bottom aus der alten Card-Optik ist überflüssig. */
.backlog-pane-left .backlog-form { margin-bottom: 0; }
/* Drag-over-Hook (Overlay-Sichtbarkeit steuert app.js per [hidden]). */
.backlog-pane-left.drag-over { /* placeholder */ }

/* --- Schmales App-Fenster (WM.7, 2026-05-28) -------------------------
   Kein Vollbild-Split: die Seite scrollt als Ganzes, die Panes stapeln
   vertikal, die Kopfzeilen werden zu normal mitscrollenden Blöcken.
   Container-Query gegen app-backlog: greift in jedem Fenster <900px,
   nicht nur bei schmalem Viewport. html.is-mobile-Regeln weiter unten
   bleiben fuer den Touch/UA-Pfad als Backup. */
@container app-backlog (max-width: 900px) {
  .backlog-page { height: auto; overflow: visible; }
  .backlog-split { flex-direction: column; }
  /* Im gestapelten Layout gibt es keine vertikale Grenze → Splitter weg. */
  .backlog-splitter { display: none; }
  .backlog-pane-left,
  .backlog-pane-right {
    flex: none;
    max-width: none;
    width: 100%;
    overflow: visible;
    border-right: none;
  }
  .backlog-pane-left { border-bottom: 1px solid var(--border); }
}
/* ≤720px non-mobile: dort wird die Tab-Leiste fixed (siehe @media 720px
   weiter oben). #app-main braucht dann oben Platz, sonst steckt die erste
   Kopfzeile unter den fixen Tabs. */
@media (max-width: 720px) {
  body.tab-backlog #app-main { padding-top: 110px; }
}
/* Mobilgeräte/Tablets: gleiche Stapel-Logik + Platz für die fixe Tab-Bar. */
html.is-mobile body.tab-backlog #app-main { padding-top: 110px; }
html.is-mobile .backlog-page { height: auto; overflow: visible; }
html.is-mobile .backlog-split { flex-direction: column; }
html.is-mobile .backlog-pane-left,
html.is-mobile .backlog-pane-right {
  flex: none;
  max-width: none;
  width: 100%;
  overflow: visible;
  border-right: none;
}
html.is-mobile .backlog-pane-left { border-bottom: 1px solid var(--border); }
/* Auch im Touch/Mobile-Stapellayout ist die vertikale Grenze sinnlos. */
html.is-mobile .backlog-splitter { display: none; }

/* --- Backlog im Desktop-Fenster (WM.7) -----------------------------------
   Bug (Item 640f5e3b, 2026-06-01): Beim Scrollen im linken „Neue Aufgaben
   anlegen“-Bereich scrollte die obere Button-/Kennzahlen-Zeile mit.
   Ursache: `.backlog-page` traegt fuer den Tab-Modus `height: calc(100dvh
   - 60px)` (volle VIEWPORT-Hoehe). Im Desktop-WM lebt die Seite aber in
   einem <daria-window>; das Panel ist dort `.wm-window-panel` mit
   `height:100%` + `overflow:auto`. Ist das Fenster kuerzer als der
   Viewport, wird die Seite (100dvh) groesser als der Fenster-Body → der
   ganze Panel-Slot scrollt, inklusive der eigentlich fixen Topbar. Der
   lange rechte Pane fing den Scroll vorher intern ab (overflow-y:auto),
   der kurze linke Pane nicht → Scroll blubberte zum Panel hoch → nur beim
   Scrollen links sichtbar.
   Fix: im breiten Split-Layout fuellt die Seite exakt die Fenster-Hoehe
   (height:100% von #panel-backlog, das selbst 100% des Window-Bodys ist).
   Dann passt die Seite genau in den Panel-Slot, der Slot scrollt nicht
   mehr, und nur die beiden Panes scrollen intern → Topbar bleibt fix.
   Nur >900px: der schmale Stapel-Modus (height:auto, scrollt als Ganzes,
   siehe @container-Block oben) bleibt absichtlich unberuehrt. */
@container app-backlog (min-width: 901px) {
  #panel-backlog.wm-window-panel .backlog-page { height: 100%; }
}

/* Drag-and-Drop-Overlay (2026-05-18, Wunsch Dennis). Optisch analog zur
   Chat-Dropzone, aber begrenzt auf die linke Spalte (#backlog-pane-left).
   Wird per app.js eingeblendet, sobald der User eine Datei darüber zieht;
   beim Drop landen die Files via /api/web/upload als Anhaenge im Create-Form.
   pointer-events: none — sonst wuerde der File-Picker-Klick durch das
   Overlay blockiert. Innen-Element bekommt pointer-events: auto. */
.backlog-dropzone {
  position: absolute;
  inset: 0;
  z-index: 30;
  display: grid;
  place-items: center;
  
  
  
  
  
  pointer-events: none;
  
}

.backlog-dropzone-inner {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 24px 28px;
  
  
  
  
  
  
  max-width: 80%;
  
}

/* Hook fuer zukuenftige Effekte am Card-Rand (z. B. dezenter Glow). */
.backlog-card.drag-over { /* placeholder */ }

/* Worker-Status-Pille in der Kennzahlen-Zeile. font-size an die Kennzahl-
   Pillen + die Queue-ETA (je 0.85em) angeglichen, damit die ganze Zeile
   einheitlich groß wirkt und sauber auf einer Höhe sitzt (2026-05-21). */
.backlog-worker-state {
  padding: 4px 10px;
  font-size: 0.85em;
  white-space: nowrap;
}

/* G.6a (2026-05-19): Throttle-Badge. Wird neben der Worker-Pille gerendert,
   zeigt eco/critical/paused mit Mode-spezifischem Farb-Akzent. mode=normal
   ist `hidden`-Attribut → CSS muss daran nichts tun. Farben halten sich an
   die Token-Themes aus theming-* (kein hartes Hex), damit das Badge im
   Dark- und Light-Mode lesbar bleibt — Fallback ist neutrales orange/rot. */
.backlog-throttle-badge {
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 0.85em;
  font-weight: 500;
  white-space: nowrap;
  cursor: help; /* Tooltip-Hint, da title-Attribut Hover-Info liefert */
}
.backlog-throttle-badge[data-throttle-mode="eco"] {
  background: var(--warning-bg, rgba(255, 193, 7, 0.18));
  color: var(--warning-fg, #c98b00);
}
.backlog-throttle-badge[data-throttle-mode="critical"] {
  background: var(--danger-bg, rgba(255, 132, 0, 0.22));
  color: var(--danger-fg, #c14b00);
}
.backlog-throttle-badge[data-throttle-mode="paused"] {
  background: var(--error-bg, rgba(220, 53, 69, 0.18));
  color: var(--error-fg, #b3001b);
  font-weight: 600;
}

/* ====================================================================
   H.4 (2026-05-20): Laufzeit-Schaetzer-UI.
   ==================================================================== */

/* Queue-ETA-Badge: neben Worker-Pille + Throttle-Badge im Admin-Tab.
   Bewusst dezent (Hint-Optik) — es ist eine Zusatz-Info, kein Alarm.
   Versteckt bei leerer Queue (hidden-Attribut, app.js#updateQueueEta). */
.backlog-queue-eta {
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 0.85em;
  white-space: nowrap;
  cursor: help; /* Tooltip mit Prioritaets-Aufschluesselung im title-Attr */
  background: var(--surface-2, rgba(127, 127, 127, 0.12));
}

/* Estimate-Zeile im Footer wartender Items ("⏱ Schätzung: ~12 min").
   Erbt die Hint-Farbe vom .backlog-item-footer; cursor:help signalisiert
   den Tooltip (P25-P75 + Verfahren + Kalibrierung). */
.backlog-estimate { cursor: help; }
/* Vergleichs-Zeile im Footer abgeschlossener Items. */
.backlog-estimate-cmp { white-space: nowrap; }
/* Drift-Hinweis (>±25 % Abweichung) — leicht hervorgehoben, damit der
   Operator sieht, dass die Heuristik diese Kategorie noch nachjustiert. */
.backlog-estimate-drift {
  cursor: help;
  color: var(--warning-fg, #c98b00);
}

/* Progress-Bar waehrend des Laufs (zwischen Beschreibung/Activity-Box und
   den Action-Buttons). data-state steuert die Fuellfarbe:
     normal   — laeuft im Rahmen der Schaetzung
     over     — elapsed > estimate × 1.5  (orange)
     way-over — elapsed > estimate × 2.5  (rot) */
.backlog-progress {
  margin: 8px 0 4px;
}
.backlog-progress-bar {
  height: 8px;
  border-radius: 999px;
  background: var(--surface-2, rgba(127, 127, 127, 0.18));
  overflow: hidden;
}
.backlog-progress-fill {
  height: 100%;
  border-radius: 999px;
  /* Default-Fuellfarbe: ruhiges Akzent-Gruen/Blau aus dem Theme. */
  background: var(--accent, #3b82f6);
  transition: width 0.4s ease;
}
.backlog-progress[data-state="over"] .backlog-progress-fill {
  background: var(--danger-fg, #e08600);
}
.backlog-progress[data-state="way-over"] .backlog-progress-fill {
  background: var(--error-fg, #d23048);
}
.backlog-progress-label {
  margin-top: 3px;
  font-size: 0.8em;
  color: var(--text-muted, #8a8a8a);
}
/* Hinweis-Text ("läuft länger als erwartet" / "deutlich über Schätzung")
   faerbt sich passend zum Bar-Zustand ein. */
.backlog-progress[data-state="over"] .backlog-progress-note {
  color: var(--danger-fg, #c14b00);
  font-weight: 500;
}
.backlog-progress[data-state="way-over"] .backlog-progress-note {
  color: var(--error-fg, #b3001b);
  font-weight: 600;
}
/* Item 3670443f (2026-06-19): eingefrorener Pause-Zustand. Die Fuellung wird
   gedimmt + bekommt ein dezentes diagonales Streifenmuster, damit auf einen
   Blick klar ist, dass der Fortschritt steht (nicht nur „langsam"). Reines
   CSS ueber das data-paused-Attribut — kein inline-Style, CSP-konform.
   transition aus, sonst wuerde die eingefrorene Breite beim Re-Render
   sichtbar „nachfahren". */
.backlog-progress[data-paused="1"] .backlog-progress-fill {
  opacity: 0.55;
  transition: none;
  background-image: repeating-linear-gradient(
    45deg,
    rgba(255, 255, 255, 0.25) 0,
    rgba(255, 255, 255, 0.25) 4px,
    rgba(0, 0, 0, 0) 4px,
    rgba(0, 0, 0, 0) 8px
  );
}
.backlog-progress[data-paused="1"] .backlog-progress-note {
  color: var(--text-muted, #8a8a8a);
  font-weight: 500;
}

/* BACKLOG.md-Editor (Modal): Textarea soll wie ein simpler Markdown-Editor
   wirken — monospace, voller Modal-Breite, dezente Borders, Theme-konform.
   resize:vertical, damit User bei langem Text die Hoehe selbst aufziehen
   kann ohne ins Modal-Scroll zu fallen. */
.backlog-notes-textarea {
  width: 100%;
  box-sizing: border-box;
  
  
  
  padding: 10px 12px;
  
  
  
  
  resize: vertical;
  min-height: 240px;
}
/* Pfad-Hinweis + Zeichenzaehler unter dem BACKLOG.md-Editor. Frueher inline
   style="…" im bodyHTML — von der CSP (`style-src 'self'`, kein
   'unsafe-inline') still verworfen (Item c30eca7a, 2026-07-25). */
.backlog-notes-path-hint {
  font-family: var(--font-mono, monospace);
  font-size: 12px;
}
.backlog-notes-size-hint { text-align: right; }


/* Paused-State (2026-05-19, Pause-Button): orange/gelblicher Hint statt
   Blau, sodass der User sofort sieht "da steht etwas still" — die Pulse-
   Animation entfaellt im Paused-Zustand (overrides die [data-busy="1"]-
   Regel oben durch hoehere Spezifitaet ueber den 2. Attribut-Selektor). */



/* Chat-Backlog-Indikator: Pille links neben dem Kontextfenster-Donut im
   .usage-badge (2026-05-26, Wunsch Dennis: vorher schwebte sie oben-links
   im Chat-Wrap, jetzt wandert sie in die Bubble links neben den Donut).
   Markup im #usage-badge (siehe app.html); Sichtbarkeit setzt
   updateChatBacklogIndicator(workerStatus) in app.js, abhaengig vom
   /api/backlog-Polling. Bei hidden faellt der Button komplett aus dem
   Flex-Layout — der Donut rueckt nicht, weil das Badge an `right: 12px`
   verankert ist und nur nach links waechst.

   Optik (Hintergrund + Pulse) lebt in theme/<theme>/web-extras.css.
   Layout-Reset hier:
     - kein position:absolute mehr (Flex-Kind),
     - kompaktes Padding + Whitespace-nowrap, damit die Pille schmal bleibt. */
.chat-backlog-indicator {
  user-select: none;
  padding: 4px 12px;
  white-space: nowrap;
}


/* Paused-State: analog zur Admin-Pille — orange Tint, keine Pulse-
   Animation, damit der User auf einen Blick „etwas steht still" liest. */


/* Pause/Play-Button im Backlog-Header (2026-05-19, Wunsch Dennis).
   Im "Resume"-Modus (also wenn der Worker pausiert ist) bekommt der Button
   einen leicht abgesetzten gruenlichen Tint, damit der User den "weiter"-
   Action visuell vom passiven Pausieren-Button unterscheidet. */



.backlog-header-actions {
  display: flex; align-items: center; gap: 10px;
  /* Auf schmalen Viewports darf die Action-Reihe umbrechen, sonst wuerde
     das neue KI-Modell-Dropdown (2026-05-20) den Header horizontal
     ueberlaufen lassen. */
  flex-wrap: wrap;
}

/* KI-Modell-Auswahl fuer den Backlog-Worker (2026-05-20, Wunsch Dennis
   "KI Modell Auswahl fuer den Backlog Worker"). Kompaktes Label+Select-
   Paar in der Backlog-Header-Actions-Reihe. Schriftgroesse an .hint
   angelehnt, damit das Steuerelement neben den .ghost-Buttons nicht zu
   wuchtig wirkt; das Label haelt Beschriftung und Dropdown zusammen. */
.backlog-worker-model-label {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.85rem;
  color: var(--text-muted, #666);
  white-space: nowrap;
}
.backlog-worker-model-select {
  font-size: 0.85rem;
  padding: 3px 6px;
}

/* Komplexitaets-Routing-Mapping (Worker-Token 3/4, 2026-05-21). Die Box sitzt
   in derselben Backlog-Header-Actions-Reihe wie das KI-Modell-Dropdown und ist
   nur sichtbar, wenn der Modus auf „Automatisch" steht (app.js toggelt das
   `hidden`-Attribut). Drei Mini-Dropdowns je Komplexitaets-Stufe; `flex-wrap`
   am Container faengt die Zusatzbreite auf schmalen Viewports ab. */
.backlog-routing-config {
  display: inline-flex; align-items: center; gap: 8px;
  flex-wrap: wrap;
  font-size: 0.85rem;
  color: var(--text-muted, #666);
}
.backlog-routing-config[hidden] { display: none; }
.backlog-routing-label { white-space: nowrap; }
/* Stufe = Beschriftung + Mini-Dropdown, zusammengehalten wie das
   Worker-Modell-Label. */
.backlog-routing-tier {
  display: inline-flex; align-items: center; gap: 4px;
  white-space: nowrap;
}

/* Archiv-Button unterhalb der Backlog-Liste (Wunsch Dennis 2026-05-18).
   Rechtsbuendig + kleiner Abstand zur letzten Item-Zeile, damit der Button
   klar als „nach den abgeschlossenen Aufgaben" gelesen wird statt mit der
   Liste zu verschmelzen. */
.backlog-archive-row {
  display: flex; justify-content: flex-end;
  margin-top: 10px;
}

/* Shortcut-Box (2026-05-21, Wunsch Dennis „Große Backlog Seiten Anpassung").
   Die Shortcut-Reihe war bis dahin eine eigene zweite Kopfzeile ganz oben;
   jetzt sitzt sie als eigene, optisch abgesetzte Box UNTER dem „Neue Aufgabe
   anlegen"-Formular in der linken Spalte. Border + Radius + dezenter
   Hintergrund grenzen sie klar als eigenständige Box ab. */
.backlog-shortcuts-box {
  margin-top: 16px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-card, var(--bg-elev, rgba(127, 127, 127, 0.06)));
}
.backlog-shortcuts-box-title {
  margin: 0 0 10px;
  font-size: 1rem;
  font-weight: 600;
}
/* Shortcut-Pillen + „+ Shortcut"-Button in einer gemeinsamen umbrechenden
   Reihe innerhalb der Box. */
.backlog-shortcuts-box-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* Shortcut-Buttons (Wunsch Dennis 2026-05-19, Item "Shortcut buttons").
   Jeder Shortcut ist ein Paar aus dem Haupt-Button (label) und dem
   Zahnrad-Button (edit). Die Paare wrappen auf schmalen Bildschirmen, der
   Zahnrad bleibt aber immer direkt neben seinem Label-Button (deshalb
   `.backlog-shortcut-pair`, kein flat flex). Border-Radius rechts/links
   innerhalb des Paares zugeschnitten, damit die zwei Buttons optisch wie
   ein verbundenes Steuerelement wirken — der User soll auf den ersten
   Blick sehen, dass Zahnrad zum linken Label gehoert. */
.backlog-shortcuts {
  display: flex; flex-wrap: wrap; gap: 8px;
}
/* In der Shortcut-Box sitzt die Pillen-Reihe bündig — kein Extra-Margin. */
.backlog-shortcuts[hidden] { display: none; }
.backlog-shortcut-pair {
  display: inline-flex; align-items: stretch;
}
.backlog-shortcut-btn {
   
  padding: 7px 14px;  
  
   
  
  
}


.backlog-shortcut-edit {
   
  padding: 7px 9px;  
  
   
  
  display: inline-flex; align-items: center; justify-content: center;
}

.backlog-shortcut-edit svg { width: 14px; height: 14px; }
/* Empty-State: kleiner Hinweis, wenn keine Shortcuts konfiguriert sind
   (z. B. nachdem der User alle geloescht hat). */
.backlog-shortcuts-empty {
   
  padding: 6px 0;
}

/* Shortcut-Edit-Modal: textarea + select brauchen explizit width:100% +
   einheitliches Padding, weil `.modal-card input[type="text"]` nur Inputs
   stylt — Textarea/Select wuerden sonst auf ihre Browser-Default-Breite
   (~20ch / Select-Content-Breite) zusammenfallen und das Modal-Layout
   wuerde unaufgeraeumt wirken. Border + Radius + Background passen wir an
   die Input-Optik an, damit es als einheitliches Form gelesen wird. */
.modal-card .shortcut-modal-input {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  
  
  
  
  
  
  
}
.modal-card textarea.shortcut-modal-input {
  resize: vertical;
  min-height: 120px;
  
  
  
}

/* Prio-Select soll nicht die volle Breite einnehmen — schmal halten, damit
   das Modal nicht visuell ein zweites textarea-Feld signalisiert. */
.modal-card .shortcut-modal-input-narrow { max-width: 220px; }

/* „Shortcut löschen"-Button im Edit-Modal (2026-05-21, Backlog-Item
   949cbd0c). Sitzt unter dem Prio-Select, optisch von den Eingabefeldern
   abgesetzt — die rote .ghost.danger-Optik kommt schon aus dem Basis-CSS,
   hier nur der Abstand nach oben. */
.modal-card .backlog-shortcut-delete-btn { margin-top: 14px; }

/* Backlog-Form: nutzt jetzt var(--bg-card) statt --bg-mute, damit es im
   Dark-Mode nicht weiss aufleuchtet. Border vom Theme. Wunsch Dennis
   2026-05-18 nach Sichtung im Dark-Mode-Screenshot. */
.backlog-form {
  display: flex; flex-direction: column; gap: 10px;
  margin-bottom: 16px;
  padding: 12px;
  
  
  
}
.backlog-field { display: flex; flex-direction: column; gap: 4px;  }
.backlog-field input[type="text"], .backlog-field textarea, .backlog-field select {
   padding: 6px 8px;
   
   
}
.backlog-field textarea { resize: vertical; min-height: 80px; }
.backlog-file-input {
   
  padding: 4px 0;
}
.backlog-attachments-preview {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-top: 6px;
}
.backlog-attachment-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 8px; 
   
  
  
}
.backlog-attachment-chip img {
  width: 28px; height: 28px;
   
}
.backlog-attachment-chip button.backlog-attachment-remove {

    padding: 0 2px;
}

/* Item 14adc54f (2026-06-13): Datei-Anhang-UI in der awaiting_user-Antwort-
   Box (openBacklogAnswerModal). CSP-konform: nur Klassen aus diesem
   externen Stylesheet, keine inline-styles. Theme-Tokens mit Fallback. */
.backlog-answer-droparea {
  position: relative;
}
/* Highlight beim Drueberziehen einer Datei. */
.backlog-answer-droparea.drag-over {
  outline: 2px dashed var(--accent, #4ea1ff);
  outline-offset: 3px;
  border-radius: 8px;
}
.backlog-answer-attach {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 8px;
}
/* Nativen File-Input visuell entfernen — Klick laeuft ueber das gelabelte
   Button-Element (label[for]). Nicht display:none, damit Fokus/Klick-Forward
   ueber das Label zuverlaessig funktioniert. */
.backlog-answer-file-input {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  border: 0;
}
.backlog-answer-attach-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  cursor: pointer;
  border: 1px solid var(--border, #3a3a3a);
  border-radius: 6px;
  color: var(--text, #e8e8e8);
  background: var(--bg-alt, rgba(127,127,127,0.06));
  font-size: 0.9em;
  line-height: 1;
}
.backlog-answer-attach-btn:hover {
  border-color: var(--accent, #4ea1ff);
}
.backlog-answer-attach-btn:focus-visible {
  outline: 2px solid var(--accent, #4ea1ff);
  outline-offset: 2px;
}
.backlog-answer-attach-btn svg {
  width: 16px; height: 16px;
  flex: 0 0 auto;
}
/* Drop-Hint-Overlay — nur sichtbar (hidden-Attribut weg) waehrend drag-over. */
.backlog-answer-drophint {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  text-align: center;
  padding: 8px;
  border-radius: 8px;
  background: var(--bg, rgba(20,20,20,0.88));
  opacity: 0.92;
  color: var(--accent, #4ea1ff);
  font-size: 0.95em;
  pointer-events: none;
  z-index: 5;
}
.backlog-form-row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.backlog-field-prio { min-width: 140px; }
.backlog-form-row button { margin-left: auto; }

.backlog-list { display: flex; flex-direction: column; gap: 10px; }

/* Items nutzen jetzt --bg statt #fff — dark-mode-konform. */
.backlog-item {
  
  
  
  padding: 10px 12px;
  
}






.backlog-item-header { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* Copy-ID-Button (Wunsch Dennis 2026-07-04): letzter Button in der Aktions-
   Reihe unten am Item. margin-left:auto schiebt ihn rechtsbuendig ans Zeilenende,
   rechts neben alle anderen Buttons. Icon + Label wie ein normaler Ghost-Button;
   SVG und Label tragen pointer-events:none, damit der Klick immer den <button>
   als Event-Target liefert (die Delegation prueft t.tagName === 'BUTTON'). */
.backlog-copy-id {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 6px;
}
.backlog-copy-id svg { pointer-events: none; display: block; flex: 0 0 auto; }
.backlog-copy-id .backlog-copy-id-label { pointer-events: none; }
/* Erfolgs-Feedback: fuer ~1 s das Icon durch ein Haekchen ersetzen. */
.backlog-copy-id.backlog-copy-id-ok svg { display: none; }
.backlog-copy-id.backlog-copy-id-ok::before {
  content: '✓';
  font-size: 14px; line-height: 1;
  pointer-events: none;
}

/* Item e003b40d (2026-07-05): render-seitige Serien-Buendelung des failed-
   Filters. Eine .backlog-series ersetzt >= 2 gleichartige failed-Karten durch
   eine zusammengeklappte Zeile mit Auf-/Zuklapp-Kopf. Erbt das failed-Styling
   (.backlog-item-failed), aber der Kopf ist kompakt und einzeilig, damit die
   Zeile auch in schmaler (Mobil-)Darstellung lesbar bleibt: Caret + Badge +
   Zaehler stehen fest, nur der Titel wird bei Platzmangel per Ellipsis gekappt. */
.backlog-series-header {
  display: flex; align-items: center; gap: 8px;
  width: 100%;
  padding: 4px 0;
  background: transparent; border: none;
  cursor: pointer;
  text-align: left;
  font: inherit; color: inherit;
}
/* Alle Kinder des Kopf-Buttons durchklicken lassen, damit die Klick-Delegation
   (t.tagName === 'BUTTON') immer den Button als Event-Target bekommt — analog
   zum Copy-ID-Button. */
.backlog-series-header > * { pointer-events: none; }
.backlog-series-caret {
  flex: 0 0 auto;
  font-size: 11px; opacity: 0.7;
  width: 1em; text-align: center;
}
.backlog-series-count {
  flex: 0 0 auto;
  font-weight: 600;
}
.backlog-series-title {
  flex: 1 1 auto;
  min-width: 0;                 /* erlaubt das Schrumpfen fuer text-overflow */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  opacity: 0.85;
}
/* Dezenter Archiv-Hinweis unter dem Kopf. */
.backlog-series-archived-hint {
  font-size: 12px; opacity: 0.7;
  margin: 2px 0 0 calc(1em + 8px);   /* leicht unter dem Caret eingerueckt */
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
.backlog-series-archive-link {
  background: none; border: none; padding: 0;
  font: inherit; cursor: pointer;
  color: var(--accent, #4a90d9);
  text-decoration: underline;
}
/* Mitglieder-Container: die aufgeklappten Einzel-Karten, leicht eingerueckt +
   optisch abgesetzt, damit die Zugehoerigkeit zur Serie erkennbar bleibt. */
.backlog-series-members {
  margin-top: 8px;
  padding-left: 10px;
  border-left: 2px solid var(--border, rgba(128,128,128,0.35));
  display: flex; flex-direction: column; gap: 8px;
}

.backlog-badge {
  display: inline-block; padding: 2px 8px;
    
  
}






.backlog-prio-tag {
   padding: 2px 6px;

}

/* Recurring-Tag (2026-05-19, Wunsch Dennis "Wiederkehrende Backlog-Tasks").
   Kleines Pill mit 🔁 + Schedule + naechster Lauf, sitzt im Header-Row
   zwischen Prio-Tag und Titel. Hat einen dezenten farbigen Hintergrund,
   damit Routine-Tasks visuell von One-Shot-Items unterscheidbar sind. */
.backlog-recurring-tag {
  display: inline-block;
  padding: 2px 8px;
  font-size: 12px;
  border-radius: 10px;
  background: var(--accent-bg, rgba(80, 150, 200, 0.15));
  color: var(--accent-fg, currentColor);
  white-space: nowrap;
  cursor: help;
}

/* Geplanter-Start-Badge (Feature 2026-07-20): 📅 + Startzeit. Nur sichtbar,
   solange scheduledStartAt in der Zukunft liegt (Render-Logik in app.js).
   Theme-agnostisch ueber echte Tokens (siehe Memory app-theme-token-names);
   dezent von der 🔁-Recurring-Pille abgesetzt (waermerer Akzent). */
.backlog-scheduled-tag {
  display: inline-block;
  padding: 2px 8px;
  font-size: 12px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--text-secondary, currentColor) 14%, transparent);
  color: var(--text-secondary, currentColor);
  white-space: nowrap;
  cursor: help;
}
/* Feld im Create-Form (datetime-local). Gleicher Grid-Platz wie das
   Recurring-Feld daneben. */
.backlog-field-scheduled-start { min-width: 200px; }
.backlog-scheduled-start {
  padding: 6px 8px;
  font-size: 13px;
}

/* Detailfeld im Create-Form (taucht nur auf, wenn Dropdown != "nie").
   Schmaler Single-Line-Input, gleicher visueller Stil wie das Prio-Dropdown
   daneben, damit beides sauber in einer Zeile bleibt. */
.backlog-field-recurring { min-width: 200px; }
.backlog-recurring-detail {
  min-width: 220px;
  padding: 6px 8px;
  align-self: flex-end;
  font-family: var(--font-mono, monospace);
  font-size: 13px;
}






.backlog-desc, .backlog-result-preview, .backlog-error {
  margin-top: 8px;

  white-space: pre-wrap; word-wrap: break-word;
}

/* Hinweis (2026-05-21, Wunsch Dennis „Große Backlog Seiten Anpassung"):
   Die aufklappbare Beschreibung (.backlog-desc-collapsible mit ~10-Zeilen-
   Clamp, Fade-Out und „mehr anzeigen"-Toggle) wurde komplett entfernt.
   Aufgabentexte stehen jetzt immer vollständig in der Karte — die
   zugehörigen Regeln (.backlog-desc-collapsible*, .backlog-desc-toggle-hint)
   sind hier ersatzlos gestrichen. */

/* Live-Activity-Box fuer laufende Backlog-Items: zeigt den aktuellen
   Arbeitsschritt (text / tool_use / thinking) der `claude -p`-stream-json.
   Aktualisiert per live-bus-Event `backlog_activity` ohne Re-Render.
   3-Zeilen-Clamp, blauer Akzent-Streifen — das ist die "blaue Box" aus
   Dennis' Aufgaben-Brief. Pulsierender Punkt links signalisiert "lebt
   gerade". Wunsch Dennis 2026-05-18. */
.backlog-activity {
  margin-top: 8px;
  padding: 6px 10px 6px 26px;
  position: relative;
  
  
  
  
  
  
  /* 3-Zeilen-Clamp mit Ellipsis — line-clamp ist die saubere Loesung,
     funktioniert in allen modernen Browsern. max-height ist Fallback,
     falls -webkit-line-clamp mal nicht greift. */
  display: -webkit-box;
  
  
  
  overflow: hidden;
  text-overflow: ellipsis;
  max-height: calc(1.4em * 3 + 12px);
  white-space: pre-wrap;
  word-break: break-word;
  
}
/* Animiertes "lebt"-Pulsiersignal am linken Rand. Reines CSS, kein JS-
   Timer noetig. */
.backlog-activity::before {
  content: "";
  position: absolute;
  left: 10px;
  top: 50%;
  width: 8px;
  height: 8px;
  margin-top: -4px;
  
  
  
  
}

/* Reduzierte Animation respektieren — OS-Einstellung schaltet den Puls
   auf einen statischen Punkt. */


/* ---------------------------------------------------------------------
   G.7 Heartbeat-Staleness-Badge (2026-05-19)
   ---------------------------------------------------------------------
   Wenn der Backlog-Spawn 60-s-Heartbeats aussetzt, rendert das Frontend
   ein farbcodiertes Badge unter der Activity-Box:
     - .backlog-heartbeat-warn  (> 120 s ohne Heartbeat): orange Border
     - .backlog-heartbeat-alert (> 300 s ohne Heartbeat): rote Border
   Optisch leicht zurueckhaltend (kein Toast, kein Modal), aber sichtbar
   genug, dass der Operator beim Scrollen ueber den Backlog-Tab auf
   einen Blick erkennt, welches Item ein potenzieller Zombie ist. */
.backlog-heartbeat {
  margin-top: 6px;
  padding: 4px 10px;
  border-radius: 6px;
  font-size: 0.85em;
  font-weight: 500;
  border: 1px solid;
  background: rgba(0, 0, 0, 0.02);
}
.backlog-heartbeat-warn {
  border-color: #d97706;  /* amber-600 */
  color: #b45309;          /* amber-700 — leicht dunkler fuer Kontrast */
  background: rgba(217, 119, 6, 0.08);
}
.backlog-heartbeat-alert {
  border-color: #dc2626;  /* red-600 */
  color: #b91c1c;          /* red-700 */
  background: rgba(220, 38, 38, 0.10);
}

.backlog-result-preview {
  
  
  padding: 6px 10px; 
  
  
  
}
.backlog-error {


  padding: 6px 10px;
}

/* G.8 (2026-05-20): Briefing-Lint-Hinweisblock. Beratend, nicht alarmierend
   — gedeckte amber-Toenung, damit es nicht mit dem roten .backlog-error
   konkurriert. Der Operator kann das Item trotz Lint-Issues starten. */
.backlog-lint {
  margin-top: 6px;
  padding: 6px 10px;
  border-radius: 6px;
  font-size: 0.85em;
  border: 1px solid #d97706;            /* amber-600 */
  background: rgba(217, 119, 6, 0.07);
}
.backlog-lint-head {
  font-weight: 600;
  color: #b45309;                        /* amber-700 */
  margin-bottom: 2px;
}
.backlog-lint-list {
  margin: 0;
  padding-left: 18px;
}
.backlog-lint-list li { margin: 1px 0; }
/* warning-Issues (toter Pfad / totes Memory) etwas kraeftiger als die
   reinen info-Hinweise (fehlendes Done-Kriterium). */
.backlog-lint-warning { color: #b45309; }
.backlog-lint-info    { color: var(--text-muted, #666); }

/* G.8: Diff-Ansicht des nach einem Fehlschlag weggesicherten Git-Stash
   (Modal-Body). Monospace + scrollbar, damit auch groessere Diffs lesbar
   bleiben, ohne das Modal zu sprengen. */
.backlog-git-diff-pre {
  margin: 0;
  max-height: 60vh;
  overflow: auto;
  padding: 8px 10px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.8em;
  line-height: 1.4;
  white-space: pre;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.04);
}

.backlog-item-footer { margin-top: 8px;  }
.backlog-item-actions {
  margin-top: 8px; display: flex; gap: 6px; flex-wrap: wrap;
}
.backlog-item-actions button { padding: 4px 10px;  }

/* „Live ansehen" / „Verlauf öffnen" — primaerer Aktion am laufenden
   bzw. abgeschlossenen Item; visuell hervorgehoben, damit Dennis sofort
   erkennt, dass er da reinklicken kann. */





/* Inline-Anhang-Thumbnails am abgeschlossenen Item, damit man sieht,
   was Daria zur Verfuegung hatte. */
.backlog-item-attachments {
  display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px;
}
.backlog-item-attachments img {
  width: 60px; height: 60px;
   
  
  
}
.backlog-item-attachment-doc {
  display: inline-flex; align-items: center;
  padding: 6px 10px; 
   
   
  
}

/* Archiv-Tabelle (Modal). */
.backlog-archive-table {
  width: 100%;  
}
.backlog-archive-table th, .backlog-archive-table td {
   padding: 8px 10px;
  
  vertical-align: top;
}


.backlog-archive-summary {

  /* Mit dem `modal-card-archive`-Modal (nahezu volle Fensterbreite) hat die
     Spalte sehr viel mehr Platz — Deckel daher von 560px auf 1000px erhoeht,
     damit die Zusammenfassung den neu gewonnenen Raum auch wirklich nutzt
     statt ihn an die schmalen Meta-Spalten zu verschenken. word-wrap
     schuetzt vor ueberlangen unzerteilbaren Strings (URLs, JSON ohne
     Whitespace). Wunsch Dennis 2026-05-21. */
  max-width: 1000px; word-wrap: break-word;
}
.backlog-archive-empty {
  padding: 24px;
}

/* „Moeglicherweise abgeloeste Fehlschlaege — Freigabe" (Item ac55ac24) im
   Archiv-Modal. Alle Masse hier als Klassen statt inline style="…" im
   innerHTML-String: die CSP ist `style-src 'self'` OHNE 'unsafe-inline',
   inline-style-Attribute wuerden still verworfen (Item c30eca7a,
   2026-07-25). */
.backlog-superseded-suggestions {
  margin: 0 0 1rem;
  padding: .75rem 1rem;
  border: 1px solid var(--border, #ddd);
  border-radius: .5rem;
  background: var(--surface-2, #faf7f2);
}
.backlog-superseded-title { margin: .1rem 0 .6rem; font-size: .95rem; }
.backlog-superseded-suggestion {
  display: flex;
  gap: .5rem;
  align-items: flex-start;
  padding: .4rem 0;
  border-top: 1px solid var(--border, #eee);
}
.backlog-superseded-main { flex: 1; min-width: 0; }
.backlog-superseded-meta { font-size: .82rem; opacity: .8; }
.backlog-superseded-actions { white-space: nowrap; }
.backlog-superseded-note { font-size: .78rem; opacity: .75; margin-top: .15rem; }

/* ===== Memory-Tab-Suche ================================================== */
/* Memory-Scope-Umschalter (Haupt ↔ Slot P (Plain)), 2026-07-23. Segmentierte
   Pill-Buttons; theme-agnostisch ueber echte Tokens + color-mix-Fallbacks. */
.memory-scope-switch {
  display: inline-flex; gap: 2px;
  padding: 3px;
  margin: 0 0 10px 0;
  border: 1px solid var(--border, #d0d0d0);
  border-radius: 999px;
  background: var(--bg-alt, color-mix(in srgb, var(--text, #000) 6%, transparent));
}
.memory-scope-btn {
  appearance: none; border: 0; cursor: pointer;
  padding: 5px 14px;
  border-radius: 999px;
  font-size: 13px; font-weight: 500;
  color: var(--text-secondary, var(--muted, #666));
  background: transparent;
}
.memory-scope-btn:hover { color: var(--text, inherit); }
.memory-scope-btn.is-active {
  background: var(--accent, #3b82f6);
  color: var(--on-accent, #fff);
}
.memory-scope-hint { margin: -4px 0 12px 0; }

.memory-search {
  display: flex; justify-content: flex-end;
  margin: 0 0 12px 0;
}
.memory-search-bar {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px;
  border: 1px solid var(--border, #d0d0d0);
  border-radius: 999px;
  background: var(--surface, #fff);
  width: 320px; max-width: 100%;
}
.memory-search-icon { color: var(--muted, #888); flex: 0 0 16px; }
#memory-search-input {
  flex: 1; min-width: 0;
  border: 0; outline: none;
  background: transparent;
  font: inherit; color: inherit;
  padding: 2px 0;
}
#memory-search-input::-webkit-search-cancel-button { display: none; }
.memory-search-clear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px;
  border: 0; padding: 0; cursor: pointer;
  background: transparent;
  color: var(--muted, #888);
  border-radius: 999px;
}
.memory-search-clear:hover { background: rgba(0,0,0,0.08); color: inherit; }

.memory-search-results .memory-search-summary {
  margin: 0 0 10px 0;
}
.memory-search-hits {
  display: flex; flex-direction: column; gap: 12px;
}
.memory-hit {
  border: 1px solid var(--border, #e0e0e0);
  border-radius: 10px;
  padding: 10px 12px;
  background: var(--surface, #fff);
}
.memory-hit-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-bottom: 4px;
}
.memory-hit-name {
  border: 0; background: transparent; padding: 0;
  font-weight: 600; font-size: 14px;
  cursor: pointer; text-align: left;
  color: var(--text, #111);
}
.memory-hit-name:hover { text-decoration: underline; }
.memory-hit-flag {
  font-size: 11px;
  padding: 2px 6px;
  border-radius: 999px;
  background: rgba(255, 159, 28, 0.18);
  color: #a35a00;
}
.memory-hit-desc {
  font-size: 12px;
  color: var(--muted, #555);
  margin-bottom: 6px;
}
.memory-hit-snippets {
  display: flex; flex-direction: column; gap: 4px;
  margin-top: 4px;
}
.memory-hit-snippet {
  display: flex; align-items: flex-start; gap: 10px;
  width: 100%;
  border: 0; padding: 6px 8px;
  background: transparent;
  border-radius: 6px;
  cursor: pointer;
  text-align: left;
  font: inherit; color: inherit;
}
.memory-hit-snippet:hover { background: rgba(0,0,0,0.05); }
.memory-hit-line {
  flex: 0 0 70px;
  font-size: 11px; font-family: var(--mono, ui-monospace, SFMono-Regular, monospace);
  color: var(--muted, #888);
  padding-top: 1px;
}
.memory-hit-text {
  flex: 1; min-width: 0;
  font-size: 13px; line-height: 1.4;
  font-family: var(--mono, ui-monospace, SFMono-Regular, monospace);
  white-space: pre-wrap; word-break: break-word;
}
mark.memory-hit-match {
  background: #ffe066;
  color: inherit;
  padding: 0 1px;
  border-radius: 2px;
}

/* @theme dark - war frueher @media (prefers-color-scheme: dark);
   2026-05-19 umgestellt auf html[data-theme="dark"], siehe
   Backlog-Item "Dark/Light/Auto-Toggle". */
  html[data-theme="dark"] .memory-search-bar { background: #2a2a2a; border-color: #444; }
  html[data-theme="dark"] .memory-hit { background: #2a2a2a; border-color: #444; }
  html[data-theme="dark"] .memory-hit-snippet:hover { background: rgba(255,255,255,0.08); }
  html[data-theme="dark"] mark.memory-hit-match { background: #ffd54a; color: #111; }
  html[data-theme="dark"] .memory-hit-flag { background: rgba(255,159,28,0.25); color: #ffb86b; }


/* =========================================================================
 * MOBILE-OPTIMIERUNGEN (Backlog-Item 7dddc224, 2026-05-19)
 *
 * Greifen NUR, wenn das Inline-Script im <head> ein mobiles Geraet erkannt
 * hat und `html.is-mobile` gesetzt ist. Ein Desktop-User, der das
 * Browserfenster schmal zieht, bekommt diese Regeln explizit NICHT — das
 * war Wunsch von Dennis ("nur aktiv sein, wenn ein mobiles Geraet erkannt
 * wurde").
 *
 * Hinweis zum Selektor: Ich greife auf `html.is-mobile` zu, nicht
 * `body.is-mobile`, weil der Head-Skript die Klasse SOFORT auf <html>
 * setzt — vor jedem Layout-Pass. `body.is-mobile` wird zusaetzlich von
 * adjustMobileChatHeight() in app.js gespiegelt, aber erst nach DOM-Ready.
 * Mit html.* greifen die Regeln ab dem ersten Paint.
 *
 * Inhalt:
 *   - Global: Touch-Targets, Tap-Highlight, iOS-Zoom-Avoidance
 *   - Header/Tabs: kompakter, dickere Buttons
 *   - Chat-Bubbles + Input: mehr Platz, groessere Tap-Areas
 *   - Listen/Karten: mehr Padding fuer leichte Treffer
 *   - Modals: Vollbild, Buttons groesser
 *   - Forms: Buttons full-width, Inputs hoeher
 *   - Tabellen (Admin/Backlog): horizontal scrollbar
 * ========================================================================= */

/* --- Global: Tap-Feedback + iOS-Zoom-Avoidance ---
   `-webkit-tap-highlight-color: transparent` entfernt das blaue Flash beim
   Tippen auf interaktive Elemente (Android Chrome, Safari iOS). Wir steuern
   Feedback selbst ueber :active-Styles. `touch-action: manipulation` schaltet
   das 300ms-Double-Tap-Zoom-Verhalten auf iOS aus → Buttons reagieren
   instant. */
html.is-mobile body {
  -webkit-tap-highlight-color: transparent;
  /* Safari iOS skaliert Inputs mit font-size < 16px automatisch beim Fokus
     hoch ("Zoom-to-Input") — kann man nur ueber font-size verhindern, NICHT
     ueber user-scalable=no (Accessibility-Fail). Wir setzen das auf alle
     Inputs/Textareas unten. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
html.is-mobile button,
html.is-mobile [role="button"],
html.is-mobile .tab,
html.is-mobile a {
  touch-action: manipulation;
}

/* iOS-Zoom-Avoidance: alle Eingabefelder ≥16px Font-Size. Wir setzen es
   AUSSCHLIESSLICH auf Mobile, sonst sieht der Desktop-Editor unnoetig fett
   aus. */
html.is-mobile input[type="text"],
html.is-mobile input[type="password"],
html.is-mobile input[type="email"],
html.is-mobile input[type="number"],
html.is-mobile input[type="search"],
html.is-mobile input[type="url"],
html.is-mobile input[type="tel"],
html.is-mobile textarea,
html.is-mobile select {
  font-size: 16px;
}

/* Projekt-Filter im Sessions-Tab auf Mobile: volle Breite, damit das
   <select> nicht ueber den schmalen Viewport hinauslaeuft. */
html.is-mobile .sessions-project-filter { width: 100%; }
html.is-mobile .sessions-project-filter select { flex: 1; min-width: 0; }

/* --- Header: kompakter Top-Bar, groessere Icon-Buttons ---
   Default 38x38 ist auf Touchscreens zu klein (Apple HIG: 44pt min, Material:
   48dp). Auf Mobile: 44x44 mit etwas mehr Tap-Area. */
html.is-mobile .app-header {
  padding: 8px 10px;
  gap: 8px;
}
html.is-mobile .icon-btn {
  width: 44px;
  height: 44px;
}
html.is-mobile .header-right {
  gap: 4px;
}
html.is-mobile .header-title {
  /* Auf sehr schmalen iPhones (375px) konkurriert der Header-Titel mit dem
     Projekt-Switcher. Verstecken — das Daria-Logo links macht den Brand klar. */
  display: none;
}
html.is-mobile .project-switcher {
  /* Bigger tap target + truncation, damit lange Projekt-Namen nicht den
     Header brechen. */
  padding: 6px 10px;
  max-width: 50vw;
  overflow: hidden;
}
html.is-mobile .project-switcher-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: inline-block;
  max-width: 100%;
  vertical-align: middle;
}
html.is-mobile .project-switcher-popover {
  /* Auf Mobile ueber die volle Breite + leichter unteren Schatten, damit der
     User nicht in einen winzigen 240px-Popover zielen muss. */
  position: fixed;
  left: 8px;
  right: 8px;
  top: 60px;
  min-width: 0;
  max-width: none;
  max-height: 70vh;
}
html.is-mobile .project-switcher-item {
  padding: 12px 12px;
}

/* --- Tabs: groessere Klick-Bereiche + Bottom-Padding fuer Home-Indicator ---
   Die existierende @media-720-Regel positioniert die Tab-Leiste fixed oben.
   Auf echten Mobile-Geraeten verstaerken wir das mit mehr Padding und einer
   sauberen Trennlinie. */
html.is-mobile .tab {
  padding: 10px 14px;
  min-height: 44px;
  /* Optisch deutliche Trennung Tab vs. Content: aktive Tabs werden via Theme
     hervorgehoben, hier nur Touch-Komfort. */
  display: inline-flex;
  align-items: center;
}
html.is-mobile .tabs {
  /* Horizontal scrollbar fuer den Fall dass kuenftig mehr Tabs dazukommen.
     Auf iPhones mit 5+ Tabs reichen 375px Breite nicht fuer space-around. */
  overflow-x: auto;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
}

/* --- Chat: mehr Platz, groessere Input-Area, Bubble-Width 88% ---
   Die existierenden @media-720-Regeln greifen Layout-seitig schon. Auf echten
   Mobile-Geraeten geben wir den Bubbles mehr Breite (80% → 88%) und vergroessern
   die Send-/Stop-Buttons. */
html.is-mobile .bubble {
  max-width: 88%;
}
html.is-mobile .chat-list {
  padding: 10px 8px;
  /* overscroll-behavior: contain → kein "Pull-to-Refresh", das ueber den
     ganzen Browser scrollt, wenn der User innerhalb des Chats nach unten
     ueber das letzte Bubble hinaus zieht. */
  overscroll-behavior: contain;
}
html.is-mobile .chat-input-row {
  padding: 8px 10px;
  /* env(safe-area-inset-bottom) → respektiert den iPhone-Home-Indicator. */
  padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
  gap: 6px;
}
html.is-mobile #chat-input {
  /* min-height 44px statt 40px fuer leichteren Tap, padding etwas groesser */
  min-height: 44px;
  padding: 12px 14px;
}
html.is-mobile .send-btn,
html.is-mobile .stop-btn {
  width: 48px;
  height: 48px;
}
html.is-mobile .mic-btn {
  /* Mikrofon-Button neben dem Send-Button — auf Mobile auch 48x48. */
  width: 48px;
  height: 48px;
}

/* --- Listen / Karten / Rows: mehr vertikales Padding ---
   .row ist die generische Settings-Zeile (Label + Wert/Button). Default 8px
   ist auf Touch zu eng. */
html.is-mobile .row {
  padding: 12px 0;
  gap: 10px;
}
html.is-mobile .card {
  padding: 14px;
  margin-bottom: 12px;
  /* border-radius bleibt vom Theme; nur Padding hier. */
}
html.is-mobile #app-main {
  padding: 12px;
}
html.is-mobile .projects-item {
  padding: 14px 12px;
}
html.is-mobile .projects-item-actions {
  /* Wenn Aktionen + Name nicht in eine Zeile passen, soll der Action-Block
     umbrechen und nicht den Namen abdraengen. */
  flex-wrap: wrap;
  gap: 8px;
}
html.is-mobile .projects-new {
  flex-direction: column;
  align-items: stretch;
}
html.is-mobile .projects-new input {
  width: 100%;
}

/* --- Buttons: Forms-Submit-Buttons full-width auf Mobile ---
   Forms auf Mobile mit halbbreiten Buttons sehen amateurhaft aus. Wir lassen
   .primary/.ghost in Form-Footer-Reihen voll-breit. */
html.is-mobile .modal-actions {
  flex-direction: column-reverse;
  /* Reverse: der Primaer-Button landet immer UNTEN, am Daumen — wie iOS
     Action-Sheets. Cancel oben. */
  gap: 8px;
  align-items: stretch;
}
html.is-mobile .modal-actions .primary,
html.is-mobile .modal-actions .ghost {
  width: 100%;
  padding: 14px 16px;
  min-height: 48px;
}
html.is-mobile .modal-footer {
  flex-wrap: wrap;
  padding: 12px;
  gap: 8px;
}

/* --- Modal: schon @media-600px-fullscreen, wir vertiefen den Look ---
   Volle Hoehe nutzen, kein Innenrand vom Backdrop. */
html.is-mobile .modal-backdrop {
  padding: 0;
}
html.is-mobile .modal-dialog,
html.is-mobile .modal-card {
  max-width: 100%;
  width: 100%;
  min-height: 100dvh;
  max-height: 100dvh;
  border-radius: 0;
  /* env(safe-area-inset-*) → der Modal-Inhalt knallt nicht in die Notch
     oder den Home-Indicator. */
  padding-top: calc(12px + env(safe-area-inset-top, 0px));
  padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
}
html.is-mobile .modal-header-row {
  /* Modal-Header sticky behalten beim Scroll → der User weiss immer noch,
     in welchem Modal er ist. */
  position: sticky;
  top: 0;
  background: inherit;
  z-index: 2;
  padding: 8px 4px;
}

/* --- Tabellen (Admin/Backlog): horizontal scrollbar statt Layout-Bruch ---
   Wir kennen Backlog-Tabellen, die auf Mobile zu breit sind. Statt jede
   einzeln zu Card-Stack umzubauen (riesige Aenderung), wrappen wir sie in
   einen horizontalen Scroll-Container — fingerfreundlich, zeigt alle
   Daten. */
html.is-mobile table {
  display: block;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
}

/* --- Date-Indicator: kleiner, weniger aufdringlich auf Mobile --- */
html.is-mobile .date-indicator {
  font-size: 12px;
  padding: 3px 10px;
}

/* --- Body-spiegel-Selektoren ---
   adjustMobileChatHeight() in app.js setzt zusaetzlich `body.is-mobile`. Fuer
   Code-Pfade, die `body.is-mobile` selektieren (z. B. die existierenden
   tab-chat-Regeln), bleiben die existierenden Regeln gueltig. Hier nur ein
   paar Eckpfeiler-Overrides fuer den Fall, dass body.is-mobile vor
   html.is-mobile kommt (race nach Page-Load). */
body.is-mobile #chat-input { min-height: 44px; padding: 12px 14px; }

/* --- iOS-Tastatur offen: Tabs ausblenden ---
   Wenn die iOS-Tastatur auf ist (body.keyboard-open), brauchen wir keine
   Tabs — sie wuerden eh nur Platz wegnehmen. Schon im alten Layout teils
   versucht, hier explizit fuer Mobile. */
html.is-mobile body.keyboard-open .tabs {
  display: none;
}
html.is-mobile body.keyboard-open main {
  padding-top: 60px;  /* Tabs weg → weniger Offset */
}

/* --- Toast-Container: schon @media-599px positioniert; mobile-spezifischer
   Touch-Komfort fuer den Action-Button. --- */
html.is-mobile .toast {
  padding: 12px 16px;
  font-size: 15px;
}
html.is-mobile .toast-actions button {
  min-height: 44px;
  padding: 0 14px;
}

/* ============================================================
 * Theming A.4 - Layout-Modi-System (2026-05-19)
 * ============================================================
 *
 * Jedes Theme deklariert in manifest.json `layout` als eines von:
 *   - "tabs"          (Default, Status quo)
 *   - "sidebar"       (200 px breite vertikale Tab-Bar links)
 *   - "compact-tabs"  (32 px hohe Tab-Bar oben, Icons statt Labels)
 *
 * themes.js setzt entsprechend body.layout-X und html.layout-X.
 * Die folgenden Regeln stehen BEWUSST am Ende der style.css, damit
 * sie tabs-spezifische Defaults aus dem Body der Datei ueberschreiben
 * koennen, ohne mit !important arbeiten zu muessen.
 *
 * Wichtig: HTML-Markup bleibt fuer alle Layouts identisch (struktur-
 * stabil). Wir verschieben nur via CSS - Reihenfolge in tabs/nav/
 * main bleibt im DOM gleich, fuer Screen-Reader und Tab-Order ist
 * das die einzig richtige Approach.
 *
 * Mobile-Override: auf schmalen Viewports (max-width:720px ODER
 * html.is-mobile) wird IMMER auf das klassische Top-Bar-Layout
 * zurueckgefallen - eine 200-px-Sidebar auf einem 402-px-iPhone
 * wuerde 50 % vom Screen wegnehmen. Die mobile Tab-Bar greift wie
 * im Default-Layout (siehe @media-Block weiter oben in dieser Datei).
 */

/* ---------- layout-tabs (Default) -----------------------------------
 * Status quo - alle bestehenden Regeln greifen unveraendert. Wir
 * setzen die Klasse trotzdem (damit man im DevTools sieht welches
 * Layout aktiv ist), brauchen aber keine eigenen Regeln.
 */


/* ---------- layout-sidebar ------------------------------------------
 * Tab-Bar wandert aus dem Header heraus an die linke Bildschirmkante.
 * Header bleibt oben (nur header-left + header-right, ohne tabs).
 * #app-main rueckt um die Sidebar-Breite nach rechts.
 */
body.layout-sidebar {
  /* Platz fuer die Sidebar reservieren. body-padding-left = sidebar-width.
     Funktioniert mit allen tab-panels, weil die alle in #app-main leben. */
  padding-left: 200px;
}

body.layout-sidebar .tabs {
  /* Aus dem Header-Flow rausziehen und als linke Spalte fixieren. */
  position: fixed;
  top: 0;
  left: 0;
  width: 200px;
  height: 100dvh;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  padding: 64px 8px 16px;
  gap: 4px;
  overflow-y: auto;
  overflow-x: hidden;
  z-index: 25;
  background: var(--surface-alt, var(--surface, transparent));
  border-right: 1px solid var(--border, rgba(127, 127, 127, 0.15));
  /* padding-top 64 schafft Platz unterhalb des sticky Headers, der ueber
     der Sidebar liegt (sticky top:0 + z-index:10 vs. sidebar z-index:25).
     Eigentlich liegt die Sidebar darueber, aber wir wollen visuell die
     ersten 60 px der Sidebar frei lassen, damit Header-Brandmarke und
     Sidebar-erster-Tab nicht uebereinanderkleben. */
}

body.layout-sidebar .tab {
  text-align: left;
  width: 100%;
  padding: 10px 14px;
  /* Tab in voller Breite und linksbuendig - Sidebar-konformes Layout. */
}

body.layout-sidebar .app-header {
  /* Tabs sind raus, also nur 2 Grid-Spalten: header-left | header-right */
  grid-template-columns: 1fr auto;
  /* Header weiter durchgehend von links bis rechts. body-padding-left
     schiebt ihn ohnehin schon nach rechts hinter die Sidebar. */
}

/* Mobile-Override fuer sidebar: faellt auf Top-Bar zurueck. */
@media (max-width: 720px) {
  body.layout-sidebar { padding-left: 0; }
  body.layout-sidebar .tabs {
    position: fixed;
    top: 60px;
    left: 0;
    right: 0;
    width: auto;
    height: auto;
    flex-direction: row;
    background: transparent;
    border-right: none;
    padding: 6px 8px;
    justify-content: space-around;
    overflow-y: hidden;
    overflow-x: auto;
  }
  body.layout-sidebar .tab {
    width: auto;
    text-align: center;
    padding: 8px 14px;
  }
  body.layout-sidebar .app-header { grid-template-columns: auto auto; }
}
html.is-mobile body.layout-sidebar { padding-left: 0; }
html.is-mobile body.layout-sidebar .tabs {
  position: fixed; top: 60px; left: 0; right: 0;
  width: auto; height: auto; flex-direction: row;
  background: transparent; border-right: none; padding: 6px 8px;
  justify-content: space-around;
  overflow-y: hidden; overflow-x: auto;
}
html.is-mobile body.layout-sidebar .tab {
  width: auto; text-align: center; padding: 8px 14px;
}
html.is-mobile body.layout-sidebar .app-header { grid-template-columns: auto auto; }


/* ---------- layout-compact-tabs ------------------------------------
 * Schmale Tab-Bar oben (32 px statt der heute ~50 px), Label per
 * font-size:0 versteckt, Icons via ::before-Pseudo (data-tab-spezifisch).
 * Verbraucht weniger vertikalen Platz - gut fuer dichte Workflows.
 */
body.layout-compact-tabs .tabs {
  min-height: 32px;
  padding: 0 8px;
  gap: 2px;
}

body.layout-compact-tabs .tab {
  /* Label per font-size:0 unsichtbar machen, ohne :empty zu brauchen
     (das wuerde i18n.js-replaced text breaken). */
  font-size: 0;
  padding: 4px 10px;
  min-height: 28px;
  min-width: 36px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

body.layout-compact-tabs .tab::before {
  /* Icon-Slot. Wir nutzen Unicode-Symbole statt Webfonts/SVGs, damit
     das Theme komplett ohne externe Assets auskommt (Briefing-Regel:
     "keine externen Deps"). data-tab-spezifische Icons setzen unten. */
  font-size: 16px;
  line-height: 1;
  display: inline-block;
  content: '\2022'; /* Bullet als Fallback */
}

/* Tab-Icons pro Tab. Emojis statt SVGs - keine Build-Pipeline, kein
   icon-Font noetig. Falls Dennis spaeter monochrome SVG-Icons will,
   hier ::before { content: url('/themes/<id>/icons/chat.svg'); }. */
body.layout-compact-tabs .tab[data-tab="chat"]::before     { content: '\1F4AC'; }  /* 💬 */
body.layout-compact-tabs .tab[data-tab="sessions"]::before { content: '\1F5C2'; }  /* 🗂 */
body.layout-compact-tabs .tab[data-tab="memory"]::before   { content: '\1F4D3'; }  /* 📓 */
body.layout-compact-tabs .tab[data-tab="projects"]::before { content: '\2318'; }   /* ⌘ */
body.layout-compact-tabs .tab[data-tab="admin"]::before    { content: '\2699'; }   /* ⚙ */
body.layout-compact-tabs .tab[data-tab="status"]::before   { content: '\24D8'; }   /* ⓘ */

/* Tab-Hover/Active zeigt das Label als Tooltip via title-attribute -
   ohne brauchen wir keine zusaetzliche Tooltip-Lib. Browser-native. */
body.layout-compact-tabs .tab { cursor: pointer; }

/* ============================================================
 * Theming A.5 (2026-05-19): Theme-Galerie im Konto-Tab
 * ============================================================
 * Kachel-Galerie aus Karten. Pro Theme: Preview-Bild oder Initial-
 * Platzhalter, Name, Description, optional aktiv-Badge. Hover-Effekt
 * triggert previewTheme; Klick aktiviert das Theme dauerhaft.
 *
 * Designziel: bewusst zurueckhaltend, damit die Galerie sich an jedes
 * Theme anpasst — keine fixen Farben, alles ueber var(--token).
 */
.theme-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 14px;
  margin-top: 4px;
}
.theme-gallery-loading {
  margin: 0;
}
.theme-card {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border, #e7e9eb);
  border-radius: 10px;
  background: var(--bg-elev, var(--bg-card, #fff));
  overflow: hidden;
  cursor: pointer;
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
  outline: none;
}
.theme-card:hover,
.theme-card:focus-visible {
  transform: translateY(-2px);
  box-shadow: var(--shadow-elev, 0 4px 16px rgba(16, 24, 40, .12));
  border-color: var(--accent, #2aabee);
}
.theme-card.is-active {
  border-color: var(--accent, #2aabee);
  box-shadow: 0 0 0 2px var(--accent-soft, rgba(42, 171, 238, .25)) inset;
}
.theme-card-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-alt, #f4f5f7);
  aspect-ratio: 16 / 9;
  width: 100%;
  overflow: hidden;
  color: var(--text-dim, #707579);
  font-size: 36px;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.theme-card-preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.theme-card-preview-placeholder {
  /* Subtiler Farbverlauf aus accent->soft als Fallback-Optik. Verlauf-
     Stops via var(); fallback rgb fuers Hosting im default-Theme. */
  background:
    linear-gradient(135deg, var(--accent-soft, #e3f2fd) 0%, var(--bg-alt, #f4f5f7) 100%);
  color: var(--accent-strong, #168acd);
}
.theme-card-badge {
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 2px 8px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--on-accent, #fff);
  background: var(--accent, #2aabee);
  border-radius: 999px;
  opacity: 0;
  transform: translateY(-2px);
  transition: opacity .15s ease, transform .15s ease;
  pointer-events: none;
}
.theme-card.is-active .theme-card-badge {
  opacity: 1;
  transform: translateY(0);
}
.theme-card-body {
  padding: 10px 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.theme-card-title {
  font-weight: 600;
  font-size: 14px;
  color: var(--text, #0f1419);
}
.theme-card-desc {
  font-size: 12px;
  color: var(--text-dim, #707579);
  line-height: 1.4;
  /* Max. 3 Zeilen — verhindert, dass eine Karte mit langer Description
     den gesamten Grid-Track in die Hoehe zieht. Modernes Truncating
     ueber line-clamp; ohne Support faellt das stillschweigend weg. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.theme-card-meta {
  font-size: 11px;
  color: var(--text-dim, #707579);
  margin-top: 2px;
  letter-spacing: 0.02em;
}
.theme-gallery-loading {
  color: var(--text-dim, #707579);
  font-style: italic;
}
/* Mobile-Variante: schmaler, weniger Spacing. Greift wenn unsere
   bestehende is-mobile-Klasse am <html> sitzt (mobile-detect.js). */
html.is-mobile .theme-gallery {
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 10px;
}
html.is-mobile .theme-card-preview { aspect-ratio: 4 / 3; font-size: 28px; }
html.is-mobile .theme-card-title { font-size: 13px; }
html.is-mobile .theme-card-desc { font-size: 11px; -webkit-line-clamp: 2; }

/* ============================================================
   Sprach-Umschalter — .lang-toggle
   ------------------------------------------------------------
   Der Konto-Tab nutzt seit 2026-05-21 ein <select>-Dropdown
   (#lang-select, gestylt wie jedes andere select via .inline-field),
   nicht mehr das fruehere DE/EN-Segmented-Control. Die zugehoerigen
   `.lang-toggle .lang-btn`-Regeln wurden dabei entfernt.
   `.lang-toggle` selbst bleibt: die Login-Seite (login.html) nutzt
   die Klasse weiterhin fuer ihren einzelnen Sprach-Umschalt-Button.
   Farben kommen vom Theme via CSS-Vars, damit Dark/Light/Pixar-
   Themes ohne Sonderfall passen.
   ============================================================ */
.lang-toggle {
  display: inline-flex;
  gap: 0;
  border: 1px solid var(--border, rgba(127,127,127,0.35));
  border-radius: 8px;
  overflow: hidden;
  margin-top: 4px;
}

/* ============================================================================
 * Mobile-Optimierung v2 (Backlog-Item 9b642383, 2026-05-19, Wunsch Dennis):
 *
 * Die erste Mobile-Iteration vom Morgen hat globale Touch-Targets, Modals und
 * Tabellen abgedeckt. Diese zweite Iteration adressiert die konkreten Pain-
 * Points aus dem Backlog-Item:
 *   1. Backlog-Form Inputs zu schmal       → full-width + 16px font-size
 *   2. Listen-Aktionsbuttons auf 320px     → flex-wrap-stretch
 *   3. Modal-Scroll auf iOS Safari         → overscroll-behavior + body-Inner-Scroll
 *   4. FAB "+ Neue Aufgabe"                → eigene Klasse, position: fixed
 *   5. Safe-Area-Insets links/rechts        → fuer Landscape-Notch-Geraete
 *   6. Sessions-Item-Layout auf 320px      → 1-Spalten-Stack
 *
 * Hinweis zum Drag-und-Drop-Reorder im Backlog (im Original-Item erwaehnt):
 * Backlog-Items haben aktuell GAR KEINEN Drag-Reorder — die Reihenfolge wird
 * vom Worker per Status+Priority sortiert (lib/backlog.js#pickNextPending).
 * Ein Long-Press-Touch-Fallback waere also kein „Fix", sondern ein neues
 * Feature. Das ist explizit aus dem Scope dieses low-Priority-Items genommen
 * (siehe CHANGELOG-Eintrag).
 * ========================================================================= */

/* --- Backlog-Form: Inputs voll-breit, Form-Row stacked, Submit unten --- */
html.is-mobile .backlog-form {
  /* Etwas mehr Innenabstand → Form-Felder atmen, Daumen-Tippen mit weniger
     Fehlgriff. */
  padding: 14px;
  gap: 12px;
}
html.is-mobile .backlog-field input[type="text"],
html.is-mobile .backlog-field textarea,
html.is-mobile .backlog-field select {
  /* font-size: 16px → iOS Safari zoomed sonst beim Fokus rein. Existiert
     bereits global im is-mobile-Block (s. o.), wir wiederholen es explizit
     fuer die Backlog-Form, damit es auch greift, wenn die globale Regel
     spaeter mal scoped wird. Padding 12px → 48px-hohe Touch-Targets. */
  font-size: 16px;
  padding: 12px;
  width: 100%;
  box-sizing: border-box;
}
html.is-mobile .backlog-field textarea {
  /* Textarea bewusst etwas hoeher, damit lange Prompts ohne Scroll
     eintippbar sind. */
  min-height: 120px;
}
html.is-mobile .backlog-form-row {
  /* Priorität + Wiederholungs-Detail + Submit nebeneinander funktioniert
     auf 320px nicht — vertikal stacken. Die Detail-Inputs sollen full
     width sein, der Submit-Button als letzter Block fuell-breit. */
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
}
html.is-mobile .backlog-field-prio,
html.is-mobile .backlog-field-recurring {
  /* min-width 140/200 → bei 320px-Screens kommen wir trotzdem klar, weil
     wir auf full-width gehen und die min-width nur fuer Desktop-Layout
     gedacht war. */
  min-width: 0;
  width: 100%;
}
html.is-mobile .backlog-recurring-detail {
  width: 100%;
  font-size: 16px;
  padding: 12px;
  box-sizing: border-box;
}
html.is-mobile .backlog-form-row button {
  /* margin-left: auto kollidiert mit align-items: stretch. Wir setzen
     explizit zurueck und machen den Submit-Button full-width + 48px
     touchfreundlich. */
  margin-left: 0;
  width: 100%;
  min-height: 48px;
  padding: 14px 16px;
}
html.is-mobile .backlog-file-input {
  /* iOS rendert <input type="file"> nativ — wir geben mehr Hoehe + Padding,
     damit der „Datei auswaehlen"-Button gut greifbar wird. */
  font-size: 16px;
  padding: 8px 0;
}

/* --- Backlog-Items: Aktion-Buttons auf 320px ---
   .backlog-item-actions ist schon flex-wrap → bricht um. Aber bei vielen
   Buttons (Antworten + Live + Verlauf + Bearbeiten + Abbrechen + Archiv)
   werden das 3-4 Reihen. Wir machen die Buttons groesser (48px Touch-
   Target) und gleich-breit auf Mobile, damit das Pattern visuell ruhig
   bleibt. */
html.is-mobile .backlog-item {
  /* Mehr Innenabstand → Inhalt atmet, Aktionen sind klar vom Header
     getrennt. */
  padding: 14px;
}
html.is-mobile .backlog-item-actions {
  gap: 8px;
  /* flex-wrap bleibt → Buttons brechen um, aber jeder ist mindestens
     48px hoch und nimmt mindestens 45% der Breite (2 pro Reihe). */
}
html.is-mobile .backlog-item-actions button {
  padding: 12px 14px;
  min-height: 44px;
  flex: 1 1 calc(50% - 4px);
  /* min-width 0 → wenn ein Button-Label sehr lang ist (z. B.
     „📜 Verlauf öffnen"), kann er trotzdem auf 50% Breite klemmen.
     Ohne das wuerde flex-basis nicht greifen. */
  min-width: 0;
}

/* --- Sessions-Liste: Grid-Layout aufbrechen auf 320px ---
   .session-item ist `grid-template-columns: 1fr auto` → ID + Aktionen
   nebeneinander. Auf 320px klemmt die ID, weil die Aktionen schon
   ~140px wegfressen. Wir gehen auf einspaltigen Stack: ID + Badges
   oben, Aktionen darunter, Name-Input ganz unten.
   Update 2026-05-19: zusammen mit dem neuen 3-Areas-Grid (info/actions/
   nameinput) MUESSEN wir die grid-template-areas hier ueberschreiben,
   sonst rendert der Browser zwei Spalten in einer One-Column-Area —
   resultiert in ueberlappenden Boxen. Ein-Spalten-Stack: info, actions,
   nameinput nacheinander. Das Name-Input wandert auf Mobil naturgemaess
   in die letzte Zeile (linksbuendig statt rechtsbuendig: die ganze
   Spalte gehoert ihm). */
html.is-mobile .session-item {
  grid-template-columns: 1fr;
  grid-template-areas:
    "info"
    "actions"
    "nameinput";
  gap: 12px;
  padding: 14px;
}
html.is-mobile .session-name-row { justify-content: flex-start; }
html.is-mobile .session-name-input { max-width: 100%; }
html.is-mobile .session-id {
  /* ID kann jetzt voll-breit auf der ersten Zeile stehen. */
  font-size: 14px;
}
html.is-mobile .session-item button,
html.is-mobile .session-item .session-actions button {
  /* Action-Buttons in der Sessions-Liste auch fingerfreundlich + nicht
     mehr in 80px-Spalten gequetscht. */
  min-height: 40px;
  padding: 8px 12px;
}

/* --- Modal-Scroll auf iOS Safari ---
   Die existierende is-mobile-Modal-Regel setzt min/max-height: 100dvh,
   aber wenn der Modal-Body laenger ist (z. B. langer Beschreibungstext
   im Edit-Modal), kommt iOS-Scroll-Bouncing dazwischen — das Page-
   Hintergrund-Scrolling triggert, statt dass der Modal-Body scrollt.
   `overscroll-behavior: contain` haelt den Scroll im Modal. Der Body
   bekommt zusaetzlich -webkit-overflow-scrolling: touch fuer das
   weiche iOS-Momentum-Scrolling. */
html.is-mobile .modal-card,
html.is-mobile .modal-dialog {
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
}
html.is-mobile .modal-body,
html.is-mobile .modal-card > *:not(.modal-header-row):not(.modal-actions):not(.modal-footer) {
  /* Inhalt scrollt, Header + Action-Footer bleiben sticky. */
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
html.is-mobile .modal-actions,
html.is-mobile .modal-footer {
  /* Sticky Footer am unteren Rand — Primary-Button bleibt am Daumen,
     auch wenn der Modal-Body lang ist. Safe-Area-Bottom als Padding
     extra, weil iOS sonst hinter dem Home-Indicator weiter scrollt. */
  position: sticky;
  bottom: 0;
  background: var(--bg, #fff);
  /* Bei dark-mode greift --bg → dunkler Hintergrund automatisch. */
  padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  z-index: 2;
  border-top: 1px solid var(--border, rgba(127,127,127,0.2));
}
/* @theme dark - war frueher @media (prefers-color-scheme: dark);
   2026-05-19 umgestellt auf html[data-theme="dark"], siehe
   Backlog-Item "Dark/Light/Auto-Toggle". */
  html[data-theme="dark"] html.is-mobile .modal-actions,
html[data-theme="dark"] html.is-mobile .modal-footer {
    background: var(--bg, #1a1a1a);
  }


/* --- Safe-Area-Insets links/rechts (Landscape-Notch) ---
   iPhone in Landscape: die Notch ist auf einer Seite, der Home-Indicator
   unten. env(safe-area-inset-left/right) muss am App-Header und an
   sticky-Elementen greifen, sonst rutscht Content unter die Notch. */
html.is-mobile .app-header {
  padding-left: max(12px, env(safe-area-inset-left, 0px));
  padding-right: max(12px, env(safe-area-inset-right, 0px));
}
html.is-mobile .tabs {
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
}
html.is-mobile main {
  padding-left: max(12px, env(safe-area-inset-left, 0px));
  padding-right: max(12px, env(safe-area-inset-right, 0px));
}
html.is-mobile .chat-input-row {
  /* Chat-Input bekommt schon Bottom-Safe-Area; links/rechts dazu, damit
     beim Landscape-Modus der Mikrofon-/Send-Button nicht hinter die
     Notch rutscht. */
  padding-left: max(8px, env(safe-area-inset-left, 0px));
  padding-right: max(8px, env(safe-area-inset-right, 0px));
}

/* --- Floating Action Button (FAB) "+ Neue Aufgabe" ---
   Erscheint nur auf is-mobile UND wenn der Admin-Tab (mit der Backlog-
   Card) aktiv ist (body.tab-admin). Klick scrollt das Backlog-Form in
   den Viewport und fokussiert das Titel-Input — Dennis tippt sofort los.
   Position bottom-right + safe-area-bottom + safe-area-right, damit auf
   iPhone-Landscape nicht unter Home-Indicator/Notch.
   z-index 10: ueber Card-Inhalten, aber UNTER Modals (modal-backdrop ist
   z-index 1000+) — das ist wichtig, weil wir den FAB beim Modal-Aufruf
   sonst verstecken muessten. */
.backlog-fab {
  display: none;
  /* default hidden — siehe is-mobile-Selektor unten. */
}
html.is-mobile body.tab-backlog .backlog-fab {
  display: inline-flex;
  position: fixed;
  right: calc(16px + env(safe-area-inset-right, 0px));
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  z-index: 10;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--primary, #2563eb);
  color: var(--primary-fg, #ffffff);
  border: none;
  align-items: center;
  justify-content: center;
  font-size: 28px;
  line-height: 1;
  font-weight: 400;
  /* Material-Schatten → der FAB sieht aus, als ob er ueber dem Content
     schwebt; auf Mobile ist das das erwartete Pattern. */
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25), 0 2px 4px rgba(0, 0, 0, 0.15);
  cursor: pointer;
  transition: transform 120ms ease, box-shadow 120ms ease;
  /* iOS-Bouncing/Tap-Verzoegerung aus: touch-action: manipulation
     verhindert das 300ms-Double-Tap-Zoom-Warten. */
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
html.is-mobile body.tab-backlog .backlog-fab:active {
  /* Klick-Feedback: kurz nach unten druecken + Schatten reduzieren. */
  transform: translateY(1px);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2), 0 1px 2px rgba(0, 0, 0, 0.1);
}
html.is-mobile body.tab-backlog .backlog-fab:focus-visible {
  outline: 3px solid var(--primary-fg, #fff);
  outline-offset: 2px;
}
/* Hide-Klasse, falls JS den FAB bei aktiven Modals temporaer ausblenden
   moechte (z. B. beim Antwort-Modal). Default: nicht gebraucht, weil
   Modals durch z-index ueberlagern. */
html.is-mobile body.tab-backlog .backlog-fab.hidden {
  display: none;
}

/* Marker fuer JS-Hooks: wenn auf is-mobile die Browser-Soft-Keyboard offen
   ist (body.keyboard-open kommt aus app.js#adjustMobileChatHeight), den FAB
   ausblenden — er saesse sonst ueber der Tastatur und ueberdeckt das letzte
   Form-Feld. */
html.is-mobile body.tab-backlog.keyboard-open .backlog-fab {
  display: none;
}

/* --- Hilfsklasse fuer kurzes Visual-Flash beim FAB-Klick (Scroll-Ziel) ---
   Wenn der FAB die Form in den Viewport scrollt, kurz pulsen → der User
   sieht, wohin der Klick gefuehrt hat. */
@keyframes backlog-fab-target-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(37, 99, 235, 0.5); }
  100% { box-shadow: 0 0 0 12px rgba(37, 99, 235, 0); }
}
html.is-mobile .backlog-form.fab-target-flash {
  animation: backlog-fab-target-pulse 700ms ease-out 1;
  border-radius: 6px;
}

/* =============================================================================
 * Shortcuts (Quick-Switcher + Cheat-Sheet) — 2026-05-19, Item a6241dd2.
 * Self-contained Stilblock; nutzt dieselben Theme-Variablen (--bg, --text,
 * --accent, --border) wie der Rest der App, damit Dark-/Light-Mode automatisch
 * mitgehen. z-index 2000 liegt knapp ueber den anderen Modals (1000), weil
 * Quick-Switcher auch ueber dem Memory-Editor-Modal sichtbar bleiben soll.
 * ============================================================================= */
.shortcuts-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 8vh 16px 16px;
  overflow-y: auto;
}
.shortcuts-modal-backdrop[hidden] { display: none; }

/* Modal-Card (Quick + Cheat teilen sich diese Basis-Klasse). --bg-card mit
   --bg-fallback, weil nicht alle Themes --bg-card definieren — wir wollen
   nicht in einem hellgrauen Backdrop transparent verschwinden. */
.shortcuts-modal {
  background: var(--bg-card, var(--bg, #fff));
  color: var(--text, #111);
  border: 1px solid var(--border, #d0d0d0);
  border-radius: 12px;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
  width: 100%;
  max-width: 640px;
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 16vh);
  overflow: hidden;
}

/* ---- Quick-Switcher ---- */
.shortcuts-quick-header {
  border-bottom: 1px solid var(--border, #e0e0e0);
  padding: 12px 14px;
}
.shortcuts-quick-input {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  border: 1px solid var(--border, #d0d0d0);
  border-radius: 8px;
  background: var(--bg, #fff);
  color: var(--text, #111);
  font-size: 16px;
  outline: none;
}
.shortcuts-quick-input:focus {
  border-color: var(--accent, #2563eb);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent, #2563eb) 25%, transparent);
}
.shortcuts-quick-list {
  list-style: none;
  margin: 0;
  padding: 6px 0;
  max-height: 56vh;
  overflow-y: auto;
}
.shortcuts-quick-item {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 8px 14px;
  cursor: pointer;
  border-left: 3px solid transparent;
}
.shortcuts-quick-item.is-active {
  background: var(--bg-hover, rgba(37, 99, 235, 0.10));
  border-left-color: var(--accent, #2563eb);
}
.shortcuts-quick-item:hover { background: var(--bg-hover, rgba(0, 0, 0, 0.04)); }
.shortcuts-quick-kind {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 10px;
  background: var(--bg-mute, #eef2f7);
  color: var(--text-muted, #555);
  min-width: 64px;
  text-align: center;
}
/* Pro Item-Kategorie eine eigene Akzentfarbe — hilft beim schnellen visuellen
   Filtern in einer langen Trefferliste. Wir bleiben dezent (10–15 % Alpha),
   damit's im Dark-Mode nicht schreit. */
.shortcuts-quick-kind-session { background: rgba(37, 99, 235, 0.18); color: #1d4ed8; }
.shortcuts-quick-kind-memory  { background: rgba(168, 85, 247, 0.18); color: #7e22ce; }
.shortcuts-quick-kind-project { background: rgba(34, 197, 94, 0.18); color: #15803d; }
.shortcuts-quick-kind-backlog { background: rgba(249, 115, 22, 0.18); color: #c2410c; }
/* K.13 — App-Eintraege (Built-in + Katalog) in der Befehlspalette, Teal. */
.shortcuts-quick-kind-app     { background: rgba(20, 184, 166, 0.18); color: #0f766e; }
/* Label-Zelle ist seit K.13 ein Flex-Container (App-Icon + Text). Das Icon
   ist optional (nur App-Eintraege tragen eines); fuer alle anderen Kinds
   enthaelt die Zelle nur den .shortcuts-quick-label-text-Span und sieht aus
   wie vorher. min-width:0 laesst den Text im Grid-1fr-Track schrumpfen, damit
   das Ellipsis am inneren Span greift. */
.shortcuts-quick-label {
  font-size: 14px;
  font-weight: 500;
  color: var(--text, #111);
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.shortcuts-quick-label-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* App-Icon: 18px-Quadrat. Emoji als zentrierter Glyph, Asset-Icon als
   contain-skaliertes Bild mit dezenter Rundung. flex:0 0 auto haelt es
   stabil, waehrend der Text daneben schrumpfen darf. */
.shortcuts-quick-icon {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  line-height: 1;
}
img.shortcuts-quick-icon {
  object-fit: contain;
  border-radius: 4px;
}
.shortcuts-quick-hint {
  font-size: 12px;
  color: var(--text-muted, #777);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 220px;
}
.shortcuts-quick-empty {
  padding: 16px 14px;
  color: var(--text-muted, #888);
  font-style: italic;
  text-align: center;
  list-style: none;
}
.shortcuts-quick-footer {
  border-top: 1px solid var(--border, #e0e0e0);
  padding: 8px 14px;
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 12px;
  color: var(--text-muted, #777);
}
.shortcuts-quick-footer kbd { margin-right: 4px; }

/* ---- Globales Spotlight (Backlog e57a0ba7, 2026-06-21) ---- */
/* Quellen-Auswahl-Toolbar: an/abwaehlbare Chips, server-first persistiert. */
.shortcuts-quick-sources {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border, #e0e0e0);
  max-height: 84px;
  overflow-y: auto;
}
.shortcuts-quick-source-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  font-size: 12px;
  line-height: 1.4;
  border-radius: 999px;
  border: 1px solid var(--border, #d6dbe2);
  background: transparent;
  color: var(--text-muted, #777);
  cursor: pointer;
  user-select: none;
}
.shortcuts-quick-source-chip:hover { background: var(--bg-hover, rgba(0, 0, 0, 0.04)); }
.shortcuts-quick-source-chip.is-on {
  background: rgba(37, 99, 235, 0.14);
  background: color-mix(in srgb, var(--accent, #2563eb) 14%, transparent);
  border-color: var(--accent, #2563eb);
  color: var(--accent, #2563eb);
}
.shortcuts-quick-source-chip.is-unavailable {
  opacity: 0.45;
  cursor: not-allowed;
  text-decoration: line-through;
}
.shortcuts-quick-source-chip.is-notready {
  /* Verfuegbar, aber Index noch nicht gebaut: gestrichelter Rahmen + gedimmt,
     bleibt aber anwaehlbar (kein line-through wie is-unavailable). */
  border-style: dashed;
  opacity: 0.7;
}
.shortcuts-quick-source-ico { display: inline-flex; }
.shortcuts-quick-source-ico svg { display: block; }

/* ---- Aktivierungs-Prompt (Item 732203cb, 2026-07-06) ----
   "Weitere Quellen aktivieren"-Hinweis, wenn nur backlog aktiv ist. Dezente
   Info-Zeile unter der Quellen-Toolbar; verschwindet bei Aktivierung/Wegklick. */
.shortcuts-quick-prompt {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border, #e0e0e0);
  background: color-mix(in srgb, var(--accent, #2563eb) 7%, transparent);
  font-size: 12px;
}
.shortcuts-quick-prompt[hidden] { display: none; }
.shortcuts-quick-prompt-body {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  flex: 1 1 auto;
  min-width: 0;
}
.shortcuts-quick-prompt-text { color: var(--text, #333); }
.shortcuts-quick-prompt-actions {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.shortcuts-quick-prompt-add,
.shortcuts-quick-prompt-all {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  font-size: 12px;
  line-height: 1.4;
  border-radius: 999px;
  border: 1px solid var(--accent, #2563eb);
  background: var(--accent, #2563eb);
  color: #fff;
  cursor: pointer;
  user-select: none;
}
.shortcuts-quick-prompt-add:hover,
.shortcuts-quick-prompt-all:hover { filter: brightness(1.08); }
.shortcuts-quick-prompt-all {
  background: transparent;
  color: var(--accent, #2563eb);
}
.shortcuts-quick-prompt-dismiss {
  flex: 0 0 auto;
  border: none;
  background: transparent;
  color: var(--text-muted, #777);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
}
.shortcuts-quick-prompt-dismiss:hover { color: var(--text, #333); }

/* Gruppen-Kopf je Quelle in der Trefferliste. */
.shortcuts-quick-group {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 8px 14px 3px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted, #888);
  list-style: none;
  cursor: default;
}
.shortcuts-quick-group-ico { display: inline-flex; opacity: 0.8; }
.shortcuts-quick-group-ico svg { display: block; }
.shortcuts-quick-group-name { flex: 1 1 auto; }
.shortcuts-quick-group-count {
  font-weight: 600;
  opacity: 0.7;
  font-variant-numeric: tabular-nums;
}
/* Inline-Snippet hinter dem Treffer-Titel (Mail/Messenger/Memory). */
.shortcuts-quick-snippet { color: var(--text-muted, #999); font-weight: 400; }
/* Fussnoten-Hinweis: ausgelassene/langsame Quellen bzw. "wird durchsucht". */
.shortcuts-quick-footnote {
  flex: 1 1 100%;
  font-size: 11px;
  color: var(--text-muted, #999);
  font-style: italic;
}

/* ---- Cheat-Sheet ---- */
.shortcuts-cheat-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 18px;
  border-bottom: 1px solid var(--border, #e0e0e0);
}
.shortcuts-cheat-header h2 {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: var(--text, #111);
}
.shortcuts-cheat-close {
  background: transparent;
  border: 0;
  color: var(--text-muted, #555);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 6px;
}
.shortcuts-cheat-close:hover { background: var(--bg-hover, rgba(0,0,0,0.06)); }
.shortcuts-cheat-list {
  list-style: none;
  margin: 0;
  padding: 8px 0;
  overflow-y: auto;
}
.shortcuts-cheat-list li {
  display: grid;
  grid-template-columns: 220px 1fr;
  align-items: center;
  gap: 16px;
  padding: 8px 18px;
}
.shortcuts-cheat-list li:nth-child(odd) {
  background: var(--bg-mute, rgba(0, 0, 0, 0.025));
}
.shortcuts-cheat-keys {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px;
}
.shortcuts-cheat-plus {
  font-size: 12px;
  color: var(--text-muted, #888);
  margin: 0 2px;
}
.shortcuts-cheat-label {
  font-size: 14px;
  color: var(--text, #111);
}
.shortcuts-cheat-footer {
  border-top: 1px solid var(--border, #e0e0e0);
  padding: 10px 18px;
  font-size: 12px;
  color: var(--text-muted, #777);
}
/* SC.3 (2026-06-21) — das Cheat-Sheet wird aus der Registry generiert. Neue
   Bausteine: Gruppen-Überschrift, „unbelegt"-Marker, Leerzustand, Footer-Texte. */
.shortcuts-cheat-footer p { margin: 0; }
.shortcuts-cheat-footer .shortcuts-cheat-note { margin-bottom: 6px; }
/* Gruppen-Überschrift (li) muss das 2-Spalten-Grid der normalen Zeilen
   überschreiben — sonst landet der Titel in der schmalen Tasten-Spalte. */
.shortcuts-cheat-list li.shortcuts-cheat-group {
  display: block;
  grid-template-columns: none;
  padding: 12px 18px 4px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted, #888);
  background: none;
}
/* Gruppen-Header nicht in die Zebra-Streifung der Zeilen einrechnen. */
.shortcuts-cheat-list li.shortcuts-cheat-group:nth-child(odd) { background: none; }
.shortcuts-cheat-list li.shortcuts-cheat-empty {
  display: block;
  grid-template-columns: none;
  padding: 18px;
  color: var(--text-muted, #888);
  background: none;
}
.shortcuts-cheat-unbound {
  font-size: 13px;
  color: var(--text-muted, #999);
}

/* Allgemeines kbd-Styling — falls die App noch keins definiert, geben wir ein
   minimales Look-and-Feel mit. Vorhandene .kbd-Klassen anderswo werden NICHT
   ueberschrieben, weil wir nur das Element `kbd` (Tag) stylen. */
.shortcuts-modal kbd {
  display: inline-block;
  padding: 2px 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  line-height: 1;
  color: var(--text, #111);
  background: var(--bg, #fff);
  border: 1px solid var(--border, #c8c8c8);
  border-radius: 4px;
  box-shadow: 0 1px 0 var(--border-strong, #b0b0b0);
  vertical-align: middle;
}

/* Mobile-Anpassung: auf engen Screens nimmt das Modal die volle Hoehe ein,
   damit der Quick-Switcher praktisch nutzbar ist (Tastatur waere sonst
   ueberdeckt). */
@media (max-width: 600px) {
  .shortcuts-modal-backdrop { padding: 0; }
  .shortcuts-modal {
    max-width: 100%;
    border-radius: 0;
    border: 0;
    height: 100vh;
    max-height: 100vh;
  }
  .shortcuts-cheat-list li { grid-template-columns: 1fr; gap: 4px; }
}

/* Dark-Mode-Feinjustierung — wir nutzen vorhandene Theme-Variablen, fuer
   einzelne Akzent-Pills setzen wir aber explizite Farben, damit die Lesbarkeit
   im Dark-Mode nicht abkippt. */
/* @theme dark - war frueher @media (prefers-color-scheme: dark);
   2026-05-19 umgestellt auf html[data-theme="dark"], siehe
   Backlog-Item "Dark/Light/Auto-Toggle". */
  html[data-theme="dark"] .shortcuts-quick-kind-session { background: rgba(96, 165, 250, 0.22); color: #93c5fd; }
  html[data-theme="dark"] .shortcuts-quick-kind-memory { background: rgba(192, 132, 252, 0.22); color: #d8b4fe; }
  html[data-theme="dark"] .shortcuts-quick-kind-project { background: rgba(74, 222, 128, 0.22); color: #86efac; }
  html[data-theme="dark"] .shortcuts-quick-kind-backlog { background: rgba(251, 146, 60, 0.22); color: #fdba74; }
  html[data-theme="dark"] .shortcuts-quick-kind-app { background: rgba(45, 212, 191, 0.22); color: #5eead4; }

html[data-theme="dark"] .shortcuts-quick-kind-session { background: rgba(96, 165, 250, 0.22); color: #93c5fd; }
html[data-theme="dark"] .shortcuts-quick-kind-memory  { background: rgba(192, 132, 252, 0.22); color: #d8b4fe; }
html[data-theme="dark"] .shortcuts-quick-kind-project { background: rgba(74, 222, 128, 0.22); color: #86efac; }
html[data-theme="dark"] .shortcuts-quick-kind-backlog { background: rgba(251, 146, 60, 0.22); color: #fdba74; }
html[data-theme="dark"] .shortcuts-quick-kind-app     { background: rgba(45, 212, 191, 0.22); color: #5eead4; }

/* ─── Markdown in nicht-Chat-Boxen ───────────────────────────────────────
   Wunsch Dennis 2026-05-19: alle Textboxen sollen Markdown anzeigen.
   .backlog-md wird auf alle Container (Backlog-Desc, Question, Preview,
   Admin-Msg-Text) gesetzt, in denen renderMd() jetzt HTML produziert.
   Wir geben den Markdown-Block-Elementen kompakte Defaults — die Boxen
   sind klein und sollen nicht durch Browser-Default-Margins explodieren.
   Variablen-frei, damit auch im Light- wie im Dark-Theme stimmig (Farben
   erbt der Container vom Parent). */
.backlog-md :first-child { margin-top: 0; }
.backlog-md :last-child  { margin-bottom: 0; }
.backlog-md h1, .backlog-md h2, .backlog-md h3,
.backlog-md h4, .backlog-md h5, .backlog-md h6 {
  margin: 6px 0 3px;
  font-size: 1em;
  font-weight: 600;
}
.backlog-md ul, .backlog-md ol {
  margin: 4px 0;
  padding-left: 22px;
}
.backlog-md li { margin: 2px 0; }
.backlog-md blockquote {
  margin: 4px 0;
  padding: 2px 10px;
  border-left: 3px solid currentColor;
  opacity: 0.85;
}
.backlog-md pre {
  margin: 6px 0;
  padding: 6px 10px;
  overflow-x: auto;
  font-size: 0.92em;
}
.backlog-md code {
  font-size: 0.92em;
  padding: 0 3px;
  border-radius: 3px;
}
.backlog-md table {
  border-collapse: collapse;
  margin: 6px 0;
}
.backlog-md th, .backlog-md td {
  border: 1px solid currentColor;
  padding: 2px 6px;
}
.backlog-md a { text-decoration: underline; }
.backlog-md img {
  max-width: 100%;
  height: auto;
}

/* ============================================================================
 * Phase J.4 (2026-05-19): Graceful Host-Shutdown — Card + Modal + Tab-Indikator
 * ============================================================================
 *
 * Visuelle Schwere: ROT, weil der Knopf destruktiv ist (Host geht aus).
 * Wir nutzen feste Rotwerte statt theming-Token, damit das auch im hellen
 * UND dunklen Theme klar als „Achtung" erkennbar bleibt — Token wie
 * --accent waeren je nach Theme blau/grau und wuerden die Warnwirkung
 * abschwaechen. Tab-Indikator ist bewusst klein + unaufdringlich (Layout
 * darf nicht springen, wenn er sichtbar wird).
 */

/* Trigger-Knopf im Idle-Zustand: roter, primaerer Button. Hover/Active
   etwas dunkler. Disabled (waehrend Request laeuft) auf 60 % Opacity. */
.shutdown-trigger-btn,
.shutdown-confirm-btn {
  background: #dc2626;
  color: #fff;
  border: 1px solid #b91c1c;
}
.shutdown-trigger-btn:hover,
.shutdown-confirm-btn:hover {
  background: #b91c1c;
  border-color: #991b1b;
}
.shutdown-trigger-btn:active,
.shutdown-confirm-btn:active {
  background: #991b1b;
}
.shutdown-trigger-btn[disabled],
.shutdown-confirm-btn[disabled] { opacity: 0.6; cursor: not-allowed; }

/* Pending-Banner: deutlich rot mit Icon links, Text rechts. Bleibt im
   normalen Card-Layout, damit es nicht ueber andere Cards floatet. */
.shutdown-banner {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid #b91c1c;
  background: #fef2f2;
  color: #7f1d1d;
  border-radius: 6px;
  margin-bottom: 10px;
}
html[data-theme="dark"] .shutdown-banner {
  background: rgba(220, 38, 38, 0.12);
  color: #fecaca;
  border-color: #ef4444;
}
.shutdown-banner-icon {
  font-size: 1.4em;
  line-height: 1;
  flex: 0 0 auto;
}
.shutdown-banner-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.shutdown-banner-body strong { font-size: 1.05em; }
.shutdown-banner-detail { font-size: 0.95em; }

.shutdown-pending-meta { margin: 4px 0 10px; }
.shutdown-pending-actions { display: flex; gap: 8px; }

/* Reason-Textarea im Modal: passt sich der Breite an, hat genug Hoehe
   fuer 2-3 Zeilen ohne Scrollbar. */
.shutdown-reason-input {
  width: 100%;
  min-height: 64px;
  padding: 8px 10px;
  border-radius: 6px;
  box-sizing: border-box;
  resize: vertical;
  font: inherit;
}
.shutdown-reason-label { display: block; margin: 6px 0 4px; font-weight: 600; }

.shutdown-step { margin: 6px 0; }
.shutdown-confirm-prompt { margin: 0 0 6px; font-size: 1.05em; }
.shutdown-confirm-current { margin: 0 0 8px; }

/* Keyframe für den dezenten Puls des Shutdown-Indikators. Genutzt von
   `.settings-shutdown-indicator` (Eck-Badge am Zahnrad — siehe weiter oben
   beim Abschnitt „Einstellungs-Subnav"). Pulsiert sanft, damit das
   „Achtung, etwas Ungewöhnliches" beim Vorbeischauen erkannt wird, ohne
   aggressiv hin- und herzublinken.
   Hinweis: Die frühere Klasse `.tab-shutdown-indicator` (Inline-Icon am
   Admin-Top-Tab) ist mit dem Menü-Umbau 2026-05-21 (Item 9e3ef51f)
   entfallen — der Indikator hängt jetzt als Badge am Zahnrad. */
@keyframes shutdown-tab-pulse {
  0%, 100% { opacity: 0.9; transform: scale(1); }
  50% { opacity: 0.5; transform: scale(0.92); }
}

/* ===== Accounts-Tab: E-Mail-Card (2026-05-21, Backlog "Accounts-Bereich") =====
   Das SMTP-Formular nutzt das bestehende .inline-row/.inline-field-Raster.
   email-/number-Inputs muessen — wie text/password (siehe .inline-row-Regel
   weiter oben) — die Default-max-width des generischen Form-Inputs ablegen,
   sonst bleiben sie in der Inline-Spalte zu schmal. */
.inline-row input[type="email"],
.inline-row input[type="number"] {
  max-width: none;
  margin: 0;
}
/* Zwischenueberschrift im Card-Body (SMTP-Block) — kleiner als die
   h2-Card-Ueberschrift, mit etwas Abstand nach oben zum vorigen Feld. */
.mail-subhead {
  margin: 18px 0 4px;
  font-size: 0.98rem;
}
/* Write-only-Geheimnis-Feld: Eingabe + „verwerfen"-Button (✕) nebeneinander
   in einer Zeile. Das Input nimmt die freie Breite, der Button bleibt klein. */
.secret-input-row {
  display: flex;
  gap: 6px;
  align-items: center;
}
.secret-input-row input {
  flex: 1 1 auto;
  max-width: none;
  margin: 0;
}

/* =========================================================================
   Bubble-Kontextmenue + Emoji-Picker + Antwort-Zitat-Leiste
   (Rechtsklick/Long-Press auf Chat-Bubbles — Wunsch Dennis 2026-05-22).

   Bewusst vollstaendig hier in style.css (nicht in den Theme-web-extras),
   mit Core-Tokens + Fallbacks — so sehen die Popover-Flaechen in allen
   Themes konsistent aus, ohne dass jede Theme-Datei angefasst werden muss.
   ========================================================================= */

/* --- Floating-Popover: Kontextmenue ------------------------------------- */
.msg-ctx-menu {
  position: fixed;
  z-index: 9000;
  min-width: 196px;
  max-width: 280px;
  padding: 6px;
  background: var(--surface, var(--bg-card, #ffffff));
  color: var(--text, #1a1a1a);
  border: 1px solid var(--border, rgba(0, 0, 0, .12));
  border-radius: 12px;
  box-shadow: var(--shadow-elev, 0 12px 32px rgba(16, 24, 40, .22));
  user-select: none;
  -webkit-user-select: none;
}

/* Emoji-Schnellwahl-Reihe oben im Menue (6 meistgenutzte + „+"). */
.msg-ctx-reactions {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px 2px 6px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border, rgba(0, 0, 0, .1));
}
.msg-ctx-emoji {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 38px;
  padding: 0;
  font-size: 21px;
  line-height: 1;
  background: transparent;
  border: 0;
  border-radius: 9px;
  cursor: pointer;
  transition: background-color .12s ease, transform .08s ease;
}
.msg-ctx-emoji:hover,
.msg-ctx-emoji:focus-visible {
  background: var(--bg-hover, var(--accent-soft, rgba(42, 171, 238, .16)));
  outline: none;
}
.msg-ctx-emoji:active { transform: scale(.88); }
.msg-ctx-emoji-more {
  font-size: 20px;
  font-weight: 600;
  color: var(--text-muted, #6b7280);
}

/* Aktions-Zeilen (Kopieren / Antworten). */
.msg-ctx-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  font-size: 14px;
  text-align: left;
  color: var(--text, #1a1a1a);
  background: transparent;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  transition: background-color .12s ease;
}
.msg-ctx-item:hover,
.msg-ctx-item:focus-visible {
  background: var(--bg-hover, var(--accent-soft, rgba(42, 171, 238, .16)));
  outline: none;
}
.msg-ctx-item-icon {
  display: inline-flex;
  width: 20px;
  justify-content: center;
  font-size: 15px;
}

/* --- Floating-Popover: voller Emoji-Picker ------------------------------ */
.emoji-picker {
  position: fixed;
  z-index: 9000;
  width: 328px;
  max-width: calc(100vw - 16px);
  max-height: 360px;
  overflow-y: auto;
  padding: 8px 10px 10px;
  background: var(--surface, var(--bg-card, #ffffff));
  color: var(--text, #1a1a1a);
  border: 1px solid var(--border, rgba(0, 0, 0, .12));
  border-radius: 12px;
  box-shadow: var(--shadow-elev, 0 12px 32px rgba(16, 24, 40, .22));
  user-select: none;
  -webkit-user-select: none;
}
.emoji-picker-section { margin-top: 6px; }
.emoji-picker-section:first-child { margin-top: 0; }
.emoji-picker-title {
  position: sticky;
  top: 0;
  padding: 4px 2px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--text-muted, #6b7280);
  background: var(--surface, var(--bg-card, #ffffff));
}
.emoji-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 1px;
}
.emoji-grid-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1 / 1;
  padding: 0;
  font-size: 21px;
  line-height: 1;
  background: transparent;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  transition: background-color .12s ease, transform .08s ease;
}
.emoji-grid-btn:hover,
.emoji-grid-btn:focus-visible {
  background: var(--bg-hover, var(--accent-soft, rgba(42, 171, 238, .16)));
  outline: none;
}
.emoji-grid-btn:active { transform: scale(.85); }

/* --- Antwort-Zitat-Leiste ueber dem Eingabefeld ------------------------- */
.chat-reply-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 6px;
  padding: 6px 8px;
  background: var(--bg-hover, var(--accent-soft, rgba(42, 171, 238, .12)));
  border-radius: 8px;
}
.chat-reply-bar[hidden] { display: none; }
.chat-reply-bar-mark {
  flex: 0 0 auto;
  align-self: stretch;
  width: 3px;
  min-height: 28px;
  border-radius: 2px;
  background: var(--accent, #2563eb);
}
.chat-reply-bar-body {
  flex: 1 1 auto;
  min-width: 0;
}
.chat-reply-bar-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--accent, #2563eb);
}
.chat-reply-bar-text {
  font-size: 13px;
  color: var(--text-muted, #6b7280);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chat-reply-bar-close {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  font-size: 14px;
  color: var(--text-muted, #6b7280);
  background: transparent;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  transition: background-color .12s ease;
}
.chat-reply-bar-close:hover,
.chat-reply-bar-close:focus-visible {
  background: var(--bg-hover, rgba(0, 0, 0, .08));
  outline: none;
}

/* Touch: kein natives Callout-Menue beim Long-Press auf Bubbles. */
.chat-list .bubble {
  -webkit-touch-callout: none;
}

@media (prefers-reduced-motion: reduce) {
  .msg-ctx-emoji,
  .emoji-grid-btn,
  .msg-ctx-item,
  .chat-reply-bar-close { transition: none; }
  .msg-ctx-emoji:active,
  .emoji-grid-btn:active { transform: none; }
}


/* ─── BL-N3 (2026-05-24): Notion-Sync-Split-Button-Dropdown ──────────────
   Das alte Einzel-Button-Konstrukt ist jetzt ein <details>-Dropdown mit
   drei Modi (upsert/insert/force). Die <summary> behaelt die .ghost-Optik
   (kommt aus dem Theme); das aufpoppende <ul> liegt absolut darunter. */
.backlog-notion-sync {
  position: relative;
  display: inline-block;
}
/* <summary> soll wie ein Button aussehen, nicht wie eine native
   Details-Zeile mit Marker links davor. */
.backlog-notion-sync > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.backlog-notion-sync > summary::-webkit-details-marker { display: none; }
.backlog-notion-sync > summary::marker { content: ''; }
.backlog-notion-sync[open] > summary > .backlog-notion-sync-caret {
  transform: rotate(180deg);
}
.backlog-notion-sync-caret {
  display: inline-block;
  transition: transform .15s ease;
  font-size: .85em;
  opacity: .7;
}
/* Aufklapp-Menue: absolut unter dem Summary, rechtsbuendig, oberhalb
   anderer Topbar-Elemente. */
.backlog-notion-sync-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 100;
  margin: 0;
  padding: 4px 0;
  list-style: none;
  min-width: 220px;
  background: var(--bg-elev, #fff);
  border: 1px solid var(--border-color, rgba(0, 0, 0, .15));
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, .18);
}
.backlog-notion-sync-menu li {
  margin: 0;
  padding: 0;
}
.backlog-notion-sync-item {
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0;
  padding: 8px 14px;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.backlog-notion-sync-item:hover,
.backlog-notion-sync-item:focus-visible {
  background: var(--bg-hover, rgba(0, 0, 0, .06));
  outline: none;
}
/* "Alles neu anlegen…" optisch als destruktive Aktion markieren — die
   Confirm-Modal kommt zusaetzlich aus dem JS-Handler. */
.backlog-notion-sync-item-danger {
  color: var(--danger-color, #b53e3e);
}
.backlog-notion-sync-item-danger:hover,
.backlog-notion-sync-item-danger:focus-visible {
  background: var(--bg-hover-danger, rgba(181, 62, 62, .1));
}
/* Wenn der Sync laeuft, signalisiert aria-disabled visuell + Cursor. */
.backlog-notion-sync > summary[aria-disabled="true"] {
  opacity: .55;
  pointer-events: none;
  cursor: progress;
}


/* ---------------------------------------------------------------------------
   Backlog-Throttle-Settings-Card (2026-05-26)
   Live-Konfiguration der Throttle-Schwellen im Admin-UI. Kleine Card mit
   Status-Box + Form. .invalid markiert ein Eingabefeld bei fehlgeschlagener
   client-seitiger Validation (eco<critical<pause, range 1..99) — der
   Speichern-Button bleibt zusaetzlich disabled. CSP-freundlich: keine
   Inline-Styles, alle Styles in dieser Datei.
   --------------------------------------------------------------------------- */
#card-backlog-throttle .throttle-status-box {
  padding: 8px 12px;
  margin-bottom: 12px;
  border: 1px solid var(--border, #ccc);
  border-radius: 6px;
  background: var(--bg-soft, rgba(0, 0, 0, 0.03));
}
#card-backlog-throttle .throttle-status-line {
  margin: 2px 0;
  font-variant-numeric: tabular-nums;
}
#card-backlog-throttle input.invalid {
  border-color: var(--error, #d9333f);
  outline-color: var(--error, #d9333f);
  background-color: rgba(217, 51, 63, 0.06);
}
#card-backlog-throttle .throttle-validation-msg {
  margin-top: 4px;
}

/* ===== Backup & Restore (2026-05-27) ===== */
#backup-card .backup-subblock {
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--border, rgba(127,127,127,0.18));
}
#backup-card .backup-subblock:first-of-type {
  border-top: none;
  margin-top: 8px;
  padding-top: 0;
}
#backup-card h3 {
  font-size: 1.05em;
  margin: 0 0 6px;
}
#backup-card h4 {
  font-size: 0.98em;
  margin: 8px 0 4px;
}
#backup-card h5 {
  font-size: 0.92em;
  margin: 6px 0 2px;
  font-weight: 600;
}
#backup-card .backup-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 8px;
}
#backup-card .backup-field > span {
  font-size: 0.88em;
  color: var(--text-muted, #666);
}
#backup-card .backup-field input[type="text"],
#backup-card .backup-field input[type="password"],
#backup-card .backup-field input[type="file"] {
  font-family: inherit;
  font-size: 0.95em;
  padding: 6px 8px;
  border: 1px solid var(--border, rgba(127,127,127,0.3));
  border-radius: 4px;
  background: var(--input-bg, transparent);
  color: inherit;
  width: 100%;
  box-sizing: border-box;
}
#backup-card .backup-progress {
  margin-top: 8px;
  padding: 8px;
  background: var(--bg-subtle, rgba(127,127,127,0.06));
  border-radius: 4px;
  font-size: 0.9em;
}
#backup-card .backup-result {
  margin-top: 8px;
  padding: 8px 10px;
  border: 1px solid var(--success, #2a8c3f);
  background: rgba(42, 140, 63, 0.06);
  border-radius: 4px;
}
#backup-card .backup-result p { margin: 4px 0; }
#backup-card .backup-result code.monospace { word-break: break-all; }
#backup-card .backup-preview {
  margin-top: 12px;
  padding: 10px;
  border: 1px solid var(--accent, #5a6cff);
  background: var(--bg-subtle, rgba(90, 108, 255, 0.04));
  border-radius: 4px;
}
#backup-card .backup-meta-list { margin: 4px 0 8px 18px; }
#backup-card .backup-diff-path {
  margin-top: 8px;
  padding: 6px 8px;
  background: var(--bg-very-subtle, rgba(127,127,127,0.04));
  border-radius: 4px;
}
#backup-card .backup-diff-section { margin-top: 4px; }
#backup-card .backup-diff-section ul {
  margin: 2px 0 2px 18px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.86em;
  max-height: 140px;
  overflow-y: auto;
}
#backup-card .backup-diff-section.added ul { color: var(--success, #2a8c3f); }
#backup-card .backup-diff-section.changed ul { color: var(--warn, #b08214); }
#backup-card .backup-diff-section.removed ul { color: var(--error, #d9333f); }
#backup-card .backup-confirm-block {
  margin-top: 10px;
  padding: 8px;
  background: rgba(217, 51, 63, 0.04);
  border-radius: 4px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
#backup-card .backup-confirm-block .toggle {
  display: flex;
  align-items: center;
  gap: 6px;
}
#backup-card .backup-safety-list {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
#backup-card .backup-safety-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 8px;
  border: 1px solid var(--border, rgba(127,127,127,0.18));
  border-radius: 4px;
  background: var(--bg-subtle, rgba(127,127,127,0.04));
}
#backup-card .backup-safety-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}
#backup-card .backup-safety-kind {
  display: inline-block;
  font-size: 0.78em;
  padding: 1px 6px;
  border-radius: 8px;
  background: var(--badge-bg, rgba(127,127,127,0.14));
  color: var(--text-muted, #666);
  width: fit-content;
}
#backup-card .backup-safety-name {
  font-weight: 500;
  font-size: 0.95em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#backup-card .backup-safety-time {
  font-size: 0.82em;
}
#backup-card .backup-safety-actions {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
}
#backup-card .backup-safety-actions button {
  font-size: 0.86em;
  padding: 3px 8px;
}
@media (max-width: 600px) {
  #backup-card .backup-safety-row { flex-direction: column; align-items: stretch; }
  #backup-card .backup-safety-actions { justify-content: flex-end; }
}

/* ---------- Letztes-Backup-Status-Panel (Item 1e4d16f4, 2026-07-06) ----------
   Ampel-Farben: gruen = ok, gelb = ok-aber-verdaechtig, rot = failed,
   grau = skipped/unbekannt. Als CSS-Variablen mit Fallback, damit Light-/
   Dark-Theme greifen und die Farben an einer Stelle liegen. */
#backup-status-card {
  --ampel-ok: var(--ok, #2e9e51);
  --ampel-warn: var(--warn, #d9a300);
  --ampel-fail: var(--danger, #d64545);
  --ampel-skip: var(--text-muted, #8a8a8a);
}
#backup-status-card .backup-status-main {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 10px;
}
#backup-status-card .ampel-lamp {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--ampel-skip);
  box-shadow: 0 0 0 3px rgba(127,127,127,0.12);
}
#backup-status-card .ampel-lamp.ampel-ok { background: var(--ampel-ok); box-shadow: 0 0 0 3px rgba(46,158,81,0.18); }
#backup-status-card .ampel-lamp.ampel-warn { background: var(--ampel-warn); box-shadow: 0 0 0 3px rgba(217,163,0,0.20); }
#backup-status-card .ampel-lamp.ampel-fail { background: var(--ampel-fail); box-shadow: 0 0 0 3px rgba(214,69,69,0.20); }
#backup-status-card .ampel-lamp.ampel-skip,
#backup-status-card .ampel-lamp.ampel-unknown { background: var(--ampel-skip); }
#backup-status-card .backup-status-result {
  font-weight: 600;
  font-size: 1.05em;
}
/* Diagnose-Block des letzten Backup-Fehlschlags (Item 20400256):
   Abbruchart + stderr-Auszug direkt in der Karte, damit der Grund ohne
   Host-Login lesbar ist. */
#backup-status-card .backup-status-diag {
  margin: 8px 0 4px;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--bg-alt);
  border-left: 3px solid var(--danger, #c0392b);
}
#backup-status-card .backup-status-diag-tail {
  margin: 4px 0 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  line-height: 1.4;
  color: var(--text-secondary);
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 160px;
  overflow: auto;
}

#backup-status-card .backup-status-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px 16px;
  margin: 12px 0 6px;
}
#backup-status-card .backup-status-facts > div {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
#backup-status-card .backup-status-facts dt {
  font-size: 0.78em;
  color: var(--text-muted, #777);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
#backup-status-card .backup-status-facts dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
}
#backup-status-card .backup-status-warn { color: var(--ampel-warn); font-weight: 600; }
#backup-status-card .backup-status-history-wrap { margin-top: 10px; }
#backup-status-card .ampel-history {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 5px;
}
#backup-status-card .ampel-dot {
  width: 15px;
  height: 15px;
  border-radius: 3px;
  background: var(--ampel-skip);
  cursor: help;
}
#backup-status-card .ampel-dot.ampel-ok { background: var(--ampel-ok); }
#backup-status-card .ampel-dot.ampel-warn { background: var(--ampel-warn); }
#backup-status-card .ampel-dot.ampel-fail { background: var(--ampel-fail); }
#backup-status-card .ampel-dot.ampel-skip,
#backup-status-card .ampel-dot.ampel-unknown { background: var(--ampel-skip); }
#backup-status-card #backup-status-lastgood { margin-top: 8px; }

/* ---------- Verbindungen & Sync — Health-Center-Karte (Item 6846b773, 2026-07-08)
   Aggregierte "ist alles gruen?"-Uebersicht im Admin-Tab. Gleiche Ampel-Semantik
   wie Backup-/Kalender-Ampel (gruen/gelb/rot/grau), eigenstaendig gescopt. */
#health-center-card {
  --ampel-ok: var(--ok, #2e9e51);
  --ampel-warn: var(--warn, #d9a300);
  --ampel-fail: var(--danger, #d64545);
  --ampel-skip: var(--text-muted, #8a8a8a);
}
#health-center-card .ampel-lamp {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--ampel-skip);
  box-shadow: 0 0 0 3px rgba(127, 127, 127, 0.12);
}
#health-center-card .ampel-lamp.ampel-ok { background: var(--ampel-ok); box-shadow: 0 0 0 3px rgba(46, 158, 81, 0.18); }
#health-center-card .ampel-lamp.ampel-warn { background: var(--ampel-warn); box-shadow: 0 0 0 3px rgba(217, 163, 0, 0.20); }
#health-center-card .ampel-lamp.ampel-fail { background: var(--ampel-fail); box-shadow: 0 0 0 3px rgba(214, 69, 69, 0.20); }
#health-center-card .ampel-lamp.ampel-skip { background: var(--ampel-skip); }

/* Gesamt-Ampel + Kopf-Satz. */
#health-center-card .hc-overall {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 10px 0 4px;
}
#health-center-card .hc-headline {
  font-weight: 600;
  font-size: 1.02em;
  line-height: 1.35;
}

/* Quell-Liste — je Registry-Quelle eine Zeile. */
#health-center-card .hc-sources {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
#health-center-card .hc-src {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 6px;
  border-radius: 8px;
  border-top: 1px solid var(--border, rgba(127, 127, 127, 0.16));
}
#health-center-card .hc-src:first-child { border-top: 0; }
/* Die kleine Zeilen-Ampel (statt der grossen 22px-Lampe). */
#health-center-card .hc-dot {
  width: 13px;
  height: 13px;
  box-shadow: none;
}
#health-center-card .hc-src-main {
  flex: 1 1 auto;
  min-width: 0;
}
#health-center-card .hc-src-top {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
#health-center-card .hc-src-label { font-weight: 600; }
#health-center-card .hc-src-time {
  font-size: 0.8em;
  color: var(--text-muted, #777);
  font-variant-numeric: tabular-nums;
}
#health-center-card .hc-src-detail {
  font-size: 0.88em;
  color: var(--text-muted, #777);
  overflow-wrap: anywhere;
}
#health-center-card .hc-action {
  flex: 0 0 auto;
  align-self: center;
  white-space: nowrap;
  font-size: 0.85em;
}
/* Image-Versionen je Tenant (Item d8d866a0): eigene Sub-Sektion unter den
   Quellen. Reine Anzeige — gleiche Zeilen-Optik (hc-src) wie die Quellen. */
#health-center-card .hc-sub-title {
  margin: 12px 0 2px;
  font-size: 0.82em;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-muted, #777);
}
#health-center-card .hc-ver { font-variant-numeric: tabular-nums; }
#health-center-card .hc-ver-warn { color: var(--ampel-warn, #d9a300); font-weight: 500; }

/* Kurzes Aufblitzen, wenn ein Deep-Link auf eine Karte scrollt. */
.hc-flash { animation: hcFlash 1.6s ease-out 1; }
@keyframes hcFlash {
  0% { box-shadow: 0 0 0 2px var(--accent, #4a7bd6); }
  100% { box-shadow: 0 0 0 2px rgba(74, 123, 214, 0); }
}

/* ---------- Sicherheit / Anmeldungen — Security-Timeline-Karte (Item e5df84d6,
   2026-07-09). Sichtbare Timeline der Sign-in-/Security-Events aus dem Mail-Index
   mit Inline-One-Click-Aktionen. Eigenstaendig gescopt. */
#security-timeline-card .st-range-lbl {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.9em;
  color: var(--text-muted, #777);
}
#security-timeline-card .st-range { max-width: 200px; }

/* Filter-Chip-Reihen (eventType + Provider). */
#security-timeline-card .st-chiprow {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0 0;
}
#security-timeline-card .st-chip {
  border: 1px solid var(--border, rgba(127, 127, 127, 0.28));
  background: transparent;
  color: var(--text, inherit);
  border-radius: 999px;
  padding: 3px 12px;
  font-size: 0.82em;
  cursor: pointer;
  line-height: 1.6;
}
#security-timeline-card .st-chip:hover { border-color: var(--accent, #4a7bd6); }
#security-timeline-card .st-chip-on {
  background: var(--accent, #4a7bd6);
  border-color: var(--accent, #4a7bd6);
  color: #fff;
}

/* Event-Liste. */
#security-timeline-card .st-list {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
#security-timeline-card .st-ev {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 6px;
  border-top: 1px solid var(--border, rgba(127, 127, 127, 0.16));
  flex-wrap: wrap;
}
#security-timeline-card .st-ev:first-child { border-top: 0; }
#security-timeline-card .st-trusted { opacity: 0.72; }

/* Typ-Badge (farbig je eventType — rein visuell, keine Ampel-Semantik). */
#security-timeline-card .st-badge {
  flex: 0 0 auto;
  align-self: center;
  font-size: 0.74em;
  font-weight: 600;
  letter-spacing: 0.01em;
  padding: 3px 8px;
  border-radius: 6px;
  white-space: nowrap;
  color: #fff;
  background: var(--text-muted, #8a8a8a);
}
#security-timeline-card .st-badge-sign_in { background: var(--ok, #2e9e51); }
#security-timeline-card .st-badge-new_device { background: var(--warn, #d9a300); }
#security-timeline-card .st-badge-app_password { background: #6a6ad6; }
#security-timeline-card .st-badge-security_alert { background: var(--danger, #d64545); }

#security-timeline-card .st-ev-main { flex: 1 1 260px; min-width: 0; }
#security-timeline-card .st-ev-top {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
#security-timeline-card .st-ev-provider { font-weight: 600; }
#security-timeline-card .st-ev-account {
  font-size: 0.85em;
  color: var(--text-muted, #777);
  overflow-wrap: anywhere;
}
#security-timeline-card .st-ev-time {
  font-size: 0.8em;
  color: var(--text-muted, #777);
  font-variant-numeric: tabular-nums;
}
#security-timeline-card .st-ev-meta {
  margin-top: 3px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  font-size: 0.85em;
  color: var(--text-muted, #777);
}
#security-timeline-card .st-meta-key { font-weight: 600; }
#security-timeline-card .st-meta-conf { font-variant-numeric: tabular-nums; }
#security-timeline-card .st-ev-subject {
  margin-top: 3px;
  font-size: 0.85em;
  color: var(--text-muted, #777);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

/* Inline-Aktionen je Zeile. */
#security-timeline-card .st-actions {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  align-self: center;
}
#security-timeline-card .st-actions button {
  font-size: 0.82em;
  white-space: nowrap;
}
#security-timeline-card .st-trust-secondary { opacity: 0.85; }
#security-timeline-card .st-trusted-badge {
  font-size: 0.82em;
  font-weight: 600;
  color: var(--ok, #2e9e51);
  white-space: nowrap;
}

/* ---------- Kalender-Sync-Gesundheits-Ampel (Item 88446fd2, 2026-07-07) -------
   Macht stille CalDAV-/Sync-Fehler in der Kalender-Integrations-Karte sichtbar.
   Nutzt dieselbe Ampel-Semantik wie das Backup-Panel (gruen/gelb/rot/grau), hier
   aber eigenstaendig gescopt, damit die Vars nicht am Backup-Card haengen. */
#calendar-sync-health {
  --ampel-ok: var(--ok, #2e9e51);
  --ampel-warn: var(--warn, #d9a300);
  --ampel-fail: var(--danger, #d64545);
  --ampel-skip: var(--text-muted, #8a8a8a);
  margin: 12px 0;
  padding: 12px 14px;
  border: 1px solid var(--border, #e0e0e0);
  border-radius: 10px;
  background: var(--surface-2, rgba(127,127,127,0.05));
}
#calendar-sync-health .sync-health-main {
  display: flex;
  align-items: center;
  gap: 12px;
}
#calendar-sync-health .ampel-lamp {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--ampel-skip);
  box-shadow: 0 0 0 3px rgba(127,127,127,0.12);
}
#calendar-sync-health .ampel-lamp.ampel-ok { background: var(--ampel-ok); box-shadow: 0 0 0 3px rgba(46,158,81,0.18); }
#calendar-sync-health .ampel-lamp.ampel-warn { background: var(--ampel-warn); box-shadow: 0 0 0 3px rgba(217,163,0,0.20); }
#calendar-sync-health .ampel-lamp.ampel-fail { background: var(--ampel-fail); box-shadow: 0 0 0 3px rgba(214,69,69,0.20); }
#calendar-sync-health .ampel-lamp.ampel-skip,
#calendar-sync-health .ampel-lamp.ampel-unknown { background: var(--ampel-skip); }
#calendar-sync-health .sync-health-headtext {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1;
}
#calendar-sync-health .sync-health-title { font-weight: 600; }
#calendar-sync-health .sync-health-sub {
  font-size: 0.85em;
  color: var(--text-muted, #777);
  font-variant-numeric: tabular-nums;
}
#calendar-sync-health .sync-health-action { flex: 0 0 auto; }
#calendar-sync-health .sync-health-cause {
  margin: 10px 0 0;
  padding: 8px 10px;
  border-radius: 8px;
  background: rgba(214,69,69,0.08);
  font-size: 0.9em;
}
#calendar-sync-health.health-warn .sync-health-cause { background: rgba(217,163,0,0.10); }
#calendar-sync-health .sync-health-cause .cause-tip { display: block; margin-top: 3px; color: var(--text-muted, #777); }
#calendar-sync-health .sync-health-history-wrap { margin-top: 10px; }
#calendar-sync-health .ampel-history {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 4px;
}
#calendar-sync-health .ampel-dot {
  width: 14px;
  height: 14px;
  border-radius: 3px;
  background: var(--ampel-skip);
  cursor: help;
}
#calendar-sync-health .ampel-dot.ampel-ok { background: var(--ampel-ok); }
#calendar-sync-health .ampel-dot.ampel-warn { background: var(--ampel-warn); }
#calendar-sync-health .ampel-dot.ampel-fail { background: var(--ampel-fail); }
#calendar-sync-health .ampel-dot.ampel-skip,
#calendar-sync-health .ampel-dot.ampel-unknown { background: var(--ampel-skip); }
#calendar-sync-health .sync-health-cals {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
#calendar-sync-health .sync-health-cals li {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.88em;
}
#calendar-sync-health .sync-health-cals .ampel-dot { cursor: default; }
#calendar-sync-health .sync-health-cals .cal-name { font-weight: 500; }
#calendar-sync-health .sync-health-cals .cal-reason { color: var(--text-muted, #777); }
#calendar-sync-health .sync-health-actionmsg { margin: 8px 0 0; font-size: 0.9em; }

/* ---------- App-Launcher-Tabs (Phase H, 2026-05-27, Item 33105c57) ----------
   Topbar-Tabs fuer gepinnte installierte Apps. Bauen optisch auf der
   generischen `.tab`-Regel auf (kompaktes padding 8/14, weisser Hintergrund)
   und ergaenzen nur das Icon+Label-Layout. Im layout-compact-tabs-Modus
   (icons-only Topbar) blenden wir das Label aus — das Icon trag das App-
   Branding. */
.tab.launcher-app-tab {
  display: inline-flex;
  align-items: center;
}
/* .launcher-app-tab-icon-Regeln entfernt am 2026-05-28 (Backlog-Item
   18a7315a, Wunsch Dennis): Topbar-Tabs fuer installierte Apps zeigen
   nur noch das Text-Label, ohne Icon-Span. Die zugehoerige compact-tabs-
   Label-Hide-Regel ist damit ebenfalls obsolet — im compact-Modus
   sollen die Tabs ihr Label behalten, weil es nichts gibt, das es
   ersetzen wuerde. */
.launcher-app-error {
  color: var(--daria-text-secondary, #888);
  font-size: 0.9em;
  padding: 1.5rem;
  text-align: center;
}

/* === Wallpaper (WM.9, 2026-05-28) =========================================
 *
 * Drei Schichten:
 *   1. CSS-Variable --daria-wallpaper-url legt das Hintergrundbild fest;
 *      wird live von app.js auf <html> gesetzt, sobald GET /api/wallpapers
 *      ein Setting liefert (FOUC-Schutz). Initialer Fallback ist die
 *      Theme-Hintergrundfarbe (--bg), damit kein nacktes Weiss kurz
 *      aufblitzt, wenn das Asset noch nicht geladen ist.
 *   2. Body bekommt das Wallpaper als ::before-Pseudo-Element NUR im
 *      Desktop-Modus (data-desktop-mode="1"). Im Tab-Modus bleibt der
 *      Theme-Hintergrund unveraendert — Wallpapers sind ein WM-Feature,
 *      und wir wollen die normalen Tabs nicht plotzlich mit Foto-
 *      Untergrund stoeren.
 *   3. Settings-UI: Grid + Tabs + aktueller Status.
 */

html {
  --daria-wallpaper-url: none;
  --daria-wallpaper-fit: cover;
}

html[data-desktop-mode="1"] body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background-image: var(--daria-wallpaper-url);
  background-size: var(--daria-wallpaper-fit);
  background-position: center;
  background-repeat: no-repeat;
  background-color: var(--bg, #1e293b);
  pointer-events: none;
}

/* === WM.12 (2026-05-28): Mount-Container fuer Desktop-Fenster ===========
 *
 * Im klassischen Modus traegt #wm-mount ein hidden-Attribut + display:none;
 * im Desktop-Modus zieht app.js das Attribut weg UND diese Regel setzt
 * display:block + fixed-Layer ueber der gesamten App-Flaeche.
 *
 * Warum 'fixed' und nicht 'absolute'? Das Mount-Element liegt im Markup
 * zwischen </header> und <main>. Bei 'absolute' wuerde es die Flow-Hoehe
 * dieses normalen Block-Kindes haben (null) und Fenster-Kinder muessten
 * relativ zu einem 0x0-Container positionieren. 'fixed' nagelt das ganze
 * Mount unabhaengig vom Body-Flow auf die Viewport-Region.
 *
 * pointer-events: none auf dem Container, damit Klicks DURCH den Mount
 * auf die klassische Topbar/Menueleiste durchgehen (Brand-Logo, Header-
 * Icons, Settings-Subnav). Fenster selbst setzen ihr eigenes
 * pointer-events: auto, sind also klickbar. WICHTIG: die Snap-Preview-
 * Outline (daria-snap-preview) traegt ebenfalls pointer-events: none —
 * sie nutzt den Mount als Positions-Kontext, soll aber das Drag-Pointer-
 * Capture nicht stoeren.
 *
 * inset: var(--menubar-h, 0) 0 0 0 — laesst Platz fuer die WM.8-
 * Menueleiste (28 px macOS / 40 px Windows). Falls die Bar nicht aktiv
 * ist, faellt der Default 0 ein und der Mount fuellt von oben.
 *
 * Im Desktop-Modus klemmt overflow:hidden auf <body> das Body-Scrollen
 * komplett ab — Apps scrollen innerhalb ihres Fensters (Container-
 * Queries aus WM.7). Das ist der "Desktop ist nicht scrollbar"-Punkt
 * aus dem Sammel-Brief. Im klassischen Modus (default) bleibt das
 * Browser-Scrollen erhalten.
 */
html[data-desktop-mode="1"] body {
  overflow: hidden;
}

#wm-mount {
  display: none;
}

/* === Desktop-Shell-Politur 2026-05-28 (Dennis-Review, Punkt 2) =========
 *
 * Der WM-Mount-Container muss SO layouten, dass Fenster physisch UNTER
 * der globalen Topbar (.app-header) und ZWISCHEN Topbar und ggf.
 * Bottom-Taskbar leben — nicht durch sie hindurch. Ansonsten verdeckt
 * ein bei y=0 platziertes Fenster die Topbar-Tabs/-Icons und gibt den
 * irreführenden Eindruck, die Topbar selbst sei ein Drag-Handle.
 *
 * Vier Modi:
 *   • Klassisch (kein Desktop-Mode): #wm-mount ist display:none.
 *   • Desktop + Custom-Shell: .app-header oben (default ~58 px),
 *     keine Menubar. → Mount-top = --app-header-h.
 *   • Desktop + Windows-Shell: .app-header oben + Taskbar unten (40 px).
 *     → Mount-top = --app-header-h, Mount-bottom = 40 px.
 *   • Desktop + macOS-Shell: Menüleiste oben (28 px), .app-header
 *     hidden (height: 0 in menubar.css). → Mount-top = 28 px.
 *
 * Die Custom-Property --wm-mount-top wird im body je Modus überschrieben
 * (Default 58 px, macOS 28 px). --wm-mount-bottom analog für die
 * Windows-Taskbar. --app-header-h ist die Topbar-Höhe; durch sticky
 * Layout und feste padding-Werte (10+10+ Inhalt) liegt sie zuverlässig
 * bei ~58 px. */
:root {
  --app-header-h: 58px;
  --wm-mount-top: var(--app-header-h);
  --wm-mount-bottom: 0px;
  /* Web-Desktop-Zoom (Item d7511bfa, 2026-07-06): globaler Skalierungs-
     Faktor (1 = 100 %, Default). Wird per CSSOM (documentElement.style.
     setProperty) aus dem app.html-Boot bzw. app.js#applyUiZoom gesetzt. */
  --ui-zoom: 1;
}
/* Der eigentliche Zoom: CSS `zoom` auf <html> skaliert den GESAMTEN Desktop
   (Menüleiste, Fenster, Schriften, Icons, Abstände) mit echtem Reflow — im
   Gegensatz zu transform:scale ohne Overflow-/Fixed-Positioning-Brüche.
   `zoom` skaliert getBoundingClientRect/clientX/-Y mit (visueller Raum), das
   Layout (--win-x etc.) bleibt aber im ungezoomten Raum; der Window-Manager
   und die Popover-Positionierer rechnen darüber via window.__dariaUiZoom
   zurück. Der :root-Default 1 lässt Login-Seite / Nicht-Zoom-Kontexte
   unberührt. */
html { zoom: var(--ui-zoom, 1); }
/* Zoom-Eingabefeld in der Kontoeinstellungen-Karte (#card-ui-zoom). */
.ui-zoom-controls {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.ui-zoom-controls #num-ui-zoom {
  width: 5em;
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.ui-zoom-controls .ui-zoom-unit {
  font-weight: 600;
  opacity: 0.7;
}
body[data-menubar-active="windows"] {
  --wm-mount-bottom: 40px;
}
body[data-menubar-active="macos"] {
  --wm-mount-top: 28px;
}
/* Hybrid-Shell (2026-06-06): obere macOS-Menüleiste UND untere Hybrid-Taskbar
   gleichzeitig — beide gleich hoch (gemeinsame Konstante --daria-menubar-h aus
   menubar.css, Fallback 28px). Der WM-Mount lässt oben + unten je diese Höhe
   frei, damit Fenster keine der beiden Bars überlappen. */
body[data-menubar-active="hybrid"] {
  --wm-mount-top: var(--daria-menubar-h, 28px);
  --wm-mount-bottom: var(--daria-menubar-h, 28px);
}

/* data-wm-mounted: gesetzt von app.js#activateWMShell, sobald der WM
   gemountet ist — AUCH beim Ad-hoc-Mount im klassischen Tab-Modus
   (Topbar-Launcher Dateien/Browser). Ohne dieses zweite Selektor-Bein
   blieb #wm-mount im Non-Desktop-Modus display:none und das frisch
   geoeffnete Fenster unsichtbar (Bug-Fix 2026-06-05). */
html[data-desktop-mode="1"] #wm-mount,
html[data-wm-mounted="1"] #wm-mount {
  display: block;
  position: fixed;
  inset: var(--wm-mount-top) 0 var(--wm-mount-bottom) 0;
  pointer-events: none;
  /* z-index: ueber dem Wallpaper (-1) und unter der Menueleiste (100).
     Ein eigener Stacking-Kontext, damit die individuellen --win-z-
     Werte der Fenster-Custom-Elements lokal bleiben. */
  z-index: 50;
}
/* Topbar im Desktop-Modus hat höhere z-Order als der WM-Mount, damit
   Fenster nicht visuell durch die Topbar laufen können. (Pointer-Hit-
   Testing wirkt durch das identische pointer-events:auto sowieso
   immer auf die Topbar; entscheidend ist der Visuelle Stack.) */
html[data-desktop-mode="1"] .app-header,
html[data-wm-mounted="1"] .app-header {
  z-index: 60;
}

/* Klassische Tab-Panels in #app-main werden im Desktop-Modus ausgeblendet,
   damit die Fenster die einzige sichtbare App-Flaeche sind. Die App-Header
   (Topbar mit Brand-Logo, Tabs, Zahnrad) bleibt, weil sie die Quelle fuer
   die Tab-Klicks ist, die das Window-Open ausloesen.

   Ausnahme: Klickt der User im Desktop-Modus auf einen Hilfs-Tab ohne
   App-Manifest (account, status, accounts, sessions, projects, apps),
   setzt selectTabOrOpenWindow() den Body-Marker `wm-classic-tab-active`
   und das #app-main wird wieder sichtbar — der User sieht dann das
   klassische Panel statt eines Fensters. Sobald wieder ein Mapped-Tab
   (Chat, Backlog, Settings, Memory, Audit, App-XYZ) geklickt wird,
   verschwindet der Marker und das Desktop-Layout (nur Fenster ueber
   Wallpaper) ist zurueck. */
html[data-desktop-mode="1"] #app-main {
  display: none;
}
html[data-desktop-mode="1"] body.wm-classic-tab-active #app-main {
  display: block;
}

/* === WM.12++ (Item 3c8dea5d, 2026-06-05): Content-Lade-/Fehler-Overlay ===
 *
 * Bug-Fix "sporadisch leeres App-Fenster". app.js#_wmAppendCustomElement
 * slottet bei externen/builtin Apps waehrend des Modul-Loads ein Status-
 * Overlay (.wm-content-status) in den <daria-window>-Body. Es fuellt die
 * Fensterhoehe und zentriert Spinner/Text bzw. Fehlertext + Retry-Button,
 * damit ein noch-nicht-geladenes oder fehlgeschlagenes Fenster sichtbar
 * "laedt" bzw. einen Fehler zeigt statt stumm leer zu bleiben.
 *
 * Die Elemente leben im Light-DOM des <daria-window>, also greift die
 * seiten-globale style.css (nicht das Shadow-Stylesheet). Alle Werte ueber
 * Klassen — CSP style-src 'self' verbietet inline style=. */
.wm-content-status {
  box-sizing: border-box;
  min-height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 24px;
  text-align: center;
  color: var(--text, #1d1d1f);
}
.wm-content-status-text {
  font-size: 14px;
  line-height: 1.4;
  opacity: 0.85;
  max-width: 32ch;
}
.wm-content-error .wm-content-status-text {
  color: var(--danger, #c42b1c);
  opacity: 1;
}
/* Spinner — reine CSS-Animation, kein Asset. */
.wm-content-spinner {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 3px solid var(--border, rgba(0,0,0,0.15));
  border-top-color: var(--accent, #0a84ff);
  animation: wm-content-spin 0.8s linear infinite;
}
@keyframes wm-content-spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .wm-content-spinner { animation-duration: 2s; }
}
.wm-content-retry-btn {
  appearance: none;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  padding: 7px 16px;
  border-radius: 8px;
  border: 1px solid var(--border, rgba(0,0,0,0.18));
  background: var(--accent, #0a84ff);
  color: #fff;
  transition: filter 0.15s;
}
.wm-content-retry-btn:hover { filter: brightness(1.08); }
.wm-content-retry-btn:active { filter: brightness(0.94); }

/* === WM.12-Followup (2026-05-28): System-Panels im <daria-window>-Slot ===
 *
 * app.js#_wmMountAppContent bewegt im Desktop-Modus die existierenden
 * Tab-Panels (chat/backlog/memory/settings/audit) AUS #app-main IN den
 * Body-Slot des dazugehoerigen <daria-window>-Elements. Damit der Inhalt
 * die volle Fenster-Hoehe nutzt — statt der frueheren Viewport-Annahme
 * (z. B. `.chat-wrap { height: calc(100vh - 140px) }`) — kappen wir die
 * Vollbild-Annahmen, sobald das Panel im Fenster sitzt. Marker dafuer ist
 * die Klasse `.wm-window-panel`, die _wmMountAppContent beim Verschieben
 * setzt und beim Zurueckwandern beim Close wieder entfernt.
 *
 * Wir lassen `[hidden]` weiter unten greifen — das Panel-Element selbst
 * ist nicht hidden, solange es im Fenster lebt. Nach dem Close-Pfad setzt
 * der Unmount-Handler hidden=true zurueck, damit das klassische
 * `selectTab()`-Verhalten unveraendert bleibt.
 *
 * Memory-Hinweis: KEIN `display:none` auf .wm-window-panel waehrend des
 * Mounts! Auch dann nicht "uebervorsichtig" mit `!important` ueberschreiben
 * — das daria-window's `.body { container-type: inline-size }` braucht ein
 * normales Layout-Kind, sonst wird die Container-Query der App tot.
 */
.wm-window-panel {
  height: 100%;
  width: 100%;
  display: block;
  /* Im Klassik-Modus gleiches Verhalten wie .tab-panel. Beim Restore
     ueberschreibt _wmUnmountAppContent hidden+entfernt diese Klasse. */
}
/* Chat-Wrap im Fenster: viewport-basierte Hoehen-Berechnung
   (`calc(100vh - 140px)`) deaktivieren — sonst rutscht das Eingabefeld
   aus dem 720-px-Fenster nach unten raus. */
daria-window .wm-window-panel .chat-wrap {
  height: 100%;
  max-height: 100%;
}
/* Backlog-Panel hat seine eigenen, fix positionierten Kopfzeilen — die
   sind absolut innerhalb von #panel-backlog. Damit die im Fenster
   funktionieren, braucht das Panel selbst relative Positionierung. */
daria-window .wm-window-panel {
  position: relative;
  overflow: auto;
}

/* === Desktop-Shell-Politur 2026-05-28 (Dennis-Review, Punkt 3) =========
 *
 * Innenkanten direkt unter der Titelleiste sollen NICHT abgerundet sein —
 * der Übergang Titelleiste → Content soll pixelgenau fluchten. Theme-CSS
 * (z. B. macos-light/web-extras.css) gibt `.chat-wrap` und vergleichbaren
 * App-Karten ein eigenes border-radius + border + box-shadow, das in der
 * Vollbild-Ansicht (.tab-chat) korrekt ist, im Fenster aber wie eine
 * doppelte Rundung aussieht (Pfeile im Dennis-Screenshot vom 2026-05-28).
 *
 * Wir nullen Border-Radius, Border und Shadow für die typischen App-
 * Karten-Container, sobald sie als .wm-window-panel im Fenster leben.
 * Die Aussenrundung des Fensters macht weiter :host { border-radius:
 * var(--win-radius) } in daria-window.js + overflow:hidden — sie bleibt
 * erhalten. */
daria-window .wm-window-panel,
daria-window .wm-window-panel > .chat-wrap,
daria-window .wm-window-panel > .panel-card,
daria-window .wm-window-panel > section.card {
  border-radius: 0 !important;
  border: 0 !important;
  box-shadow: none !important;
}

/* === Settings-Fenster Subnav (Item a22db3bb, 2026-05-29) =============
 *
 * Im Klassik-Modus sitzt #settings-subnav am oberen Rand von <main> mit
 * 16 px Bottom-Margin und einer Border darunter. Im Fenster mounten wir
 * sie als ersten Light-DOM-Kind eines <daria-window>; der Window-Body
 * (Shadow-DOM .body) ist `display:block; overflow:auto`. Damit die
 * Subnav beim Scrollen sichtbar bleibt UND nicht doppelt-Margin
 * produziert, ueberschreiben wir hier:
 *   - sticky: bleibt am Body-Top wenn ein Sub-Panel scrollt
 *   - kompaktes Padding, kein Margin
 *   - Hintergrund + Border-Bottom: sonst rutscht der Subnav-Text
 *     ueber den Panel-Content beim Scroll
 *
 * Der Marker `.wm-window-subnav` wird von app.js#_wmMountAppContent
 * gesetzt + beim Unmount wieder entfernt — die Klassik-Variante bleibt
 * unberuehrt. Memory: [[feedback-csp-inline-style-blocked]] — alles
 * via CSSOM/CSS-Klassen, kein inline-style.
 */
daria-window .wm-window-subnav {
  margin: 0 !important;
  padding: 8px 10px !important;
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg-card, rgba(255, 255, 255, 0.98));
  border-bottom: 1px solid var(--border, rgba(127, 127, 127, 0.25));
  border-radius: 0 !important;
}

/* Settings-Section --------------------------------------------------- */

.wallpaper-current {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0.5rem 0 1rem;
  padding: 0.75rem;
  background: var(--bg-alt, rgba(127,127,127,0.06));
  border-radius: 6px;
  min-height: 80px;
}

.wallpaper-current-thumb {
  width: 120px;
  height: 75px;
  background-size: cover;
  background-position: center;
  background-color: var(--bg, #f4f5f7);
  border-radius: 4px;
  flex-shrink: 0;
}

.wallpaper-current-meta {
  flex: 1;
  min-width: 0;
}

.wallpaper-current-meta strong {
  display: block;
  margin-bottom: 0.25rem;
}

.wallpaper-current-meta .hint {
  margin: 0;
  font-size: 0.85em;
}

.wallpaper-current-meta a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.wallpaper-tabs {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--bg-elev, rgba(127,127,127,0.18));
  margin-bottom: 0.75rem;
}

.wallpaper-tab {
  background: none;
  border: none;
  padding: 0.5rem 1rem;
  cursor: pointer;
  color: var(--text-muted, #888);
  border-bottom: 2px solid transparent;
  font-size: 0.95em;
}

.wallpaper-tab:hover { color: var(--text, inherit); }

.wallpaper-tab.is-active {
  color: var(--text, inherit);
  border-bottom-color: var(--accent, #2563eb);
}

.wallpaper-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
}

.wallpaper-legacy-toggle {
  margin-top: 14px;
}

.wallpaper-grid-legacy {
  margin-top: 12px;
}

.wallpaper-grid-legacy .wallpaper-thumb {
  opacity: 0.92;
}

.wallpaper-thumb {
  position: relative;
  aspect-ratio: 16 / 10;
  background-size: cover;
  background-position: center;
  background-color: var(--bg, #1e293b);
  border-radius: 6px;
  border: 2px solid transparent;
  cursor: pointer;
  overflow: hidden;
}

.wallpaper-thumb:hover {
  border-color: var(--accent-soft, rgba(37, 99, 235, 0.30));
}

.wallpaper-thumb.is-active {
  border-color: var(--accent, #2563eb);
  box-shadow: 0 0 0 1px var(--accent, #2563eb) inset;
}

.wallpaper-thumb-label {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 4px 8px;
  background: linear-gradient(to top, rgba(0,0,0,0.55), rgba(0,0,0,0));
  color: #fff;
  font-size: 0.8em;
  pointer-events: none;
}

.wallpaper-thumb-credit {
  position: absolute;
  top: 4px;
  right: 4px;
  padding: 2px 6px;
  background: rgba(0,0,0,0.45);
  color: #fff;
  font-size: 0.7em;
  border-radius: 3px;
  opacity: 0;
  transition: opacity 0.15s;
  pointer-events: auto;
  text-decoration: none;
}

.wallpaper-thumb:hover .wallpaper-thumb-credit { opacity: 1; }

.wallpaper-thumb-credit a {
  color: inherit;
  text-decoration: none;
}

.wallpaper-thumb-credit a:hover { text-decoration: underline; }

/* ── Wallpaper-Favoriten (Item aeeed022, 2026-07-24) ───────────────────── */

/* Stern-/Pin-Toggle oben links an jeder Vorschaukachel. */
.wallpaper-star {
  position: absolute;
  top: 4px;
  left: 4px;
  z-index: 2;
  width: 26px;
  height: 26px;
  padding: 0;
  line-height: 1;
  font-size: 15px;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s, background 0.15s, transform 0.1s;
}
.wallpaper-thumb:hover .wallpaper-star,
.wallpaper-star.is-pinned,
.wallpaper-star:focus-visible { opacity: 1; }
.wallpaper-star:hover { background: rgba(0, 0, 0, 0.7); transform: scale(1.08); }
.wallpaper-star.is-pinned { color: #ffd23f; }

/* Galerie-Container + Aufklapp-Toggle. */
.wallpaper-favorites {
  margin-top: 18px;
  border-top: 1px solid var(--border, rgba(148, 163, 184, 0.25));
  padding-top: 14px;
}
.wallpaper-fav-toggle { margin: 0; }
.wallpaper-fav-body { margin-top: 12px; }

/* ── Automatischer Wechsel / Rotation (Item 12e1117c, 2026-07-24) ── */
.wallpaper-rotation {
  margin-top: 14px;
  border-top: 1px solid var(--border, rgba(148, 163, 184, 0.25));
  padding-top: 14px;
}
.wallpaper-rot-toggle { margin: 0; }
.wallpaper-rot-body { margin-top: 12px; }
.wallpaper-rot-intro { margin: 0 0 12px; }
.wallpaper-rot-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 18px;
  align-items: flex-end;
}
.wallpaper-rot-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 150px;
}
.wallpaper-rot-flabel {
  font-size: 0.82em;
  color: var(--text-secondary, #64748b);
}
.wallpaper-rot-input {
  padding: 6px 8px;
  border: 1px solid var(--border, rgba(148, 163, 184, 0.35));
  border-radius: 8px;
  background: var(--bg-elev, var(--bg-alt, #fff));
  color: var(--text, inherit);
  font: inherit;
}
.wallpaper-rot-inline { display: inline-flex; gap: 8px; align-items: center; }
.wallpaper-rot-num { width: 84px; }
.wallpaper-rot-check {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 150px;
  font-size: 0.9em;
}
.wallpaper-rot-actions { margin-top: 14px; }
.wallpaper-rot-status { margin: 10px 0 0; }
.wallpaper-rot-status.is-warn { color: var(--warn, #b45309); }

/* Set-Chips-Leiste. */
.wallpaper-fav-sets {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-bottom: 10px;
}
.wallpaper-fav-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid var(--border, rgba(148, 163, 184, 0.3));
  background: var(--bg-alt, rgba(148, 163, 184, 0.12));
  color: var(--text, inherit);
  font-size: 0.85em;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.wallpaper-fav-chip:hover { border-color: var(--accent, #2563eb); }
.wallpaper-fav-chip.is-active {
  background: var(--accent, #2563eb);
  border-color: var(--accent, #2563eb);
  color: #fff;
}
.wallpaper-fav-chip-count {
  font-size: 0.85em;
  opacity: 0.7;
  background: rgba(0, 0, 0, 0.15);
  border-radius: 999px;
  padding: 0 6px;
}
.wallpaper-fav-chip.is-active .wallpaper-fav-chip-count { background: rgba(255, 255, 255, 0.25); opacity: 0.9; }
.wallpaper-fav-newset { font-weight: 600; }
.wallpaper-fav-chip.is-editing {
  padding: 3px 6px;
  cursor: default;
  background: var(--bg-elev, var(--bg-alt, rgba(148,163,184,0.14)));
}
.wallpaper-fav-rename-input {
  border: 1px solid var(--accent, #2563eb);
  border-radius: 6px;
  padding: 3px 8px;
  font-size: 0.85em;
  background: var(--bg, #fff);
  color: var(--text, inherit);
  width: 140px;
}

/* Set-Aktionsleiste (umbenennen/loeschen + Loesch-Bestaetigung). */
.wallpaper-fav-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  min-height: 4px;
  margin-bottom: 10px;
}
.wallpaper-fav-mini {
  padding: 3px 10px;
  font-size: 0.8em;
  border-radius: 6px;
  cursor: pointer;
}
.wallpaper-fav-mini.danger { color: #ef4444; border-color: rgba(239, 68, 68, 0.5); }
.wallpaper-fav-confirm { font-size: 0.85em; color: var(--text-secondary, var(--text, inherit)); }

/* Favoriten-Kachel: erbt .wallpaper-thumb, plus Entfernen-Knopf. */
.wallpaper-fav-remove {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 2;
  width: 24px;
  height: 24px;
  padding: 0;
  line-height: 1;
  font-size: 13px;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s, background 0.15s;
}
.wallpaper-fav-thumb:hover .wallpaper-fav-remove,
.wallpaper-fav-remove:focus-visible { opacity: 1; }
.wallpaper-fav-remove:hover { background: rgba(220, 38, 38, 0.85); }
.wallpaper-fav-empty {
  margin: 4px 0;
  padding: 14px;
  border: 1px dashed var(--border, rgba(148, 163, 184, 0.35));
  border-radius: 8px;
  text-align: center;
}

/* Ziel-Set-Menue beim Pinnen (nur bei >1 Set). */
.wallpaper-set-picker {
  min-width: 180px;
  max-width: 220px;
  background: var(--bg-elev, var(--bg-card, #1e293b));
  border: 1px solid var(--border, rgba(148, 163, 184, 0.35));
  border-radius: 10px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.35);
  padding: 6px;
}
.wallpaper-set-picker-title {
  font-size: 0.75em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-secondary, var(--text, inherit));
  opacity: 0.75;
  padding: 4px 8px 6px;
}
.wallpaper-set-picker-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 7px 10px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text, inherit);
  font-size: 0.88em;
  cursor: pointer;
}
.wallpaper-set-picker-item:hover { background: var(--accent-soft, rgba(37, 99, 235, 0.18)); }
.wallpaper-set-picker-item .hint { opacity: 0.6; }

.wallpaper-upload-label {
  display: inline-flex;
  align-items: center;
  padding: 0.4rem 0.9rem;
  background: var(--bg-elev, rgba(127,127,127,0.10));
  border-radius: 4px;
}

.wallpaper-unsplash-empty {
  padding: 1.5rem;
  background: var(--bg-alt, rgba(127,127,127,0.06));
  border-radius: 6px;
  text-align: center;
}

/* ---------- Desktop-Modus-Card-Lock (WM.11, 2026-05-28) ----------
   Auf iPhone/Mobile zeigt #desktop-mode-card seinen Toggle gegraut +
   einen Hinweis-Absatz. Das `.is-locked` wird in app.js#syncDesktopToggle
   gesetzt, wenn data-device-class auf 'iphone' oder 'mobile' steht
   (siehe canEnableDesktopMode). Reine Optik — die echte Sperre ist die
   `disabled`-Property am <input> und der Server-Reject im PATCH-Endpoint.
   --------------------------------------------------------------- */
#desktop-mode-card.is-locked .inline-row {
  opacity: 0.55;
  pointer-events: none;
}
#desktop-mode-card.is-locked #desktop-mode-locked-hint {
  color: var(--text-muted, #888);
  border-left: 3px solid var(--accent, #888);
  padding: 0.4rem 0.7rem;
  margin-top: 0.6rem;
  border-radius: 3px;
  background: var(--bg-alt, rgba(127,127,127,0.06));
}

/* =====================================================================
 * Browser-Extensions Admin-Card (M5b, 2026-05-28)
 * --------------------------------------------------------------------
 * Card lebt in #panel-admin und wird ueber app.js#initBrowserExtensions
 * gefuettert. Keine Inline-Styles — die Daria-CSP blockt
 * style-src 'unsafe-inline'. Layout-Aenderungen bitte hier eintragen.
 * ===================================================================== */

#browser-extensions-card .bext-subblock {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--c-border, #2a2a32);
}
#browser-extensions-card .bext-subblock:first-of-type {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

#browser-extensions-card h3 {
  font-size: 1.05rem;
  margin: 0 0 6px;
}

#browser-extensions-card .bext-field {
  display: block;
  margin: 8px 0 0;
}
#browser-extensions-card .bext-field > span {
  display: block;
  font-size: 0.85rem;
  color: var(--c-fg-dim, #aaa);
  margin-bottom: 2px;
}
#browser-extensions-card .bext-field input[type="text"],
#browser-extensions-card .bext-field input[type="file"] {
  width: 100%;
  max-width: 520px;
  box-sizing: border-box;
}
#browser-extensions-card .bext-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}
#browser-extensions-card .bext-button-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 10px;
  flex-wrap: wrap;
}

#browser-extensions-card .bext-list-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
#browser-extensions-card .bext-list-header h3 {
  margin: 0;
}
#browser-extensions-card .bext-list-actions {
  display: flex;
  gap: 8px;
}

#browser-extensions-card #bext-verify-summary {
  margin: 6px 0 0;
  min-height: 1em;
}
#browser-extensions-card #bext-verify-summary.bext-verify-ok {
  color: var(--c-ok, #5fb35f);
}
#browser-extensions-card #bext-verify-summary.bext-verify-bad {
  color: var(--c-err, #d05a5a);
}

#browser-extensions-card .bext-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 10px;
  font-size: 0.9rem;
}
#browser-extensions-card .bext-table th,
#browser-extensions-card .bext-table td {
  text-align: left;
  padding: 6px 8px;
  border-bottom: 1px solid var(--c-border, #2a2a32);
  vertical-align: top;
}
#browser-extensions-card .bext-table th {
  font-weight: 600;
  background: var(--c-bg-elev, rgba(255,255,255,0.04));
}
#browser-extensions-card .bext-table td.bext-id-cell code {
  font-size: 0.85em;
}
#browser-extensions-card .bext-perm-list {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}
#browser-extensions-card .bext-perm-list li {
  display: inline-block;
  padding: 2px 6px;
  font-size: 0.78em;
  border: 1px solid var(--c-border, #2a2a32);
  border-radius: 4px;
  background: var(--c-bg-elev, rgba(255,255,255,0.03));
}
#browser-extensions-card .bext-flag {
  display: inline-block;
  padding: 2px 6px;
  font-size: 0.78em;
  border-radius: 4px;
  background: var(--c-warn-bg, #4a3a10);
  color: var(--c-warn-fg, #ffd778);
  border: 1px solid var(--c-warn-fg, #ffd778);
  margin-right: 4px;
}
#browser-extensions-card .bext-flag.bext-flag-ok {
  background: transparent;
  color: var(--c-fg-dim, #aaa);
  border-color: var(--c-border, #2a2a32);
}
#browser-extensions-card .bext-row-failed td {
  background: var(--c-err-bg, rgba(208, 90, 90, 0.08));
}

/* ============================================================================
   Browser-Service-Telemetrie (M6f, 2026-05-29)
   ============================================================================ */
#browser-telemetry-card .btel-counts {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 12px 0 16px;
}
#browser-telemetry-card .btel-count {
  flex: 1 1 100px;
  min-width: 100px;
  padding: 10px 12px;
  border: 1px solid var(--c-border, #2a2a32);
  border-radius: 6px;
  background: var(--c-bg-soft, rgba(255, 255, 255, 0.02));
  display: flex;
  flex-direction: column;
  gap: 4px;
}
#browser-telemetry-card .btel-count-label {
  font-size: 12px;
  color: var(--c-fg-dim, #aaa);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
#browser-telemetry-card .btel-count-value {
  font-size: 24px;
  font-weight: 600;
  color: var(--c-fg, #e0e0e0);
  font-variant-numeric: tabular-nums;
}
#browser-telemetry-card .btel-table-wrap {
  overflow-x: auto;
}
#browser-telemetry-card .btel-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
#browser-telemetry-card .btel-table th,
#browser-telemetry-card .btel-table td {
  text-align: left;
  padding: 6px 10px;
  border-bottom: 1px solid var(--c-border, #2a2a32);
  vertical-align: middle;
}
#browser-telemetry-card .btel-table th {
  font-weight: 600;
  color: var(--c-fg-dim, #aaa);
}
#browser-telemetry-card .btel-table .btel-empty td {
  text-align: center;
  color: var(--c-fg-dim, #aaa);
  font-style: italic;
}
#browser-telemetry-card .btel-table .btel-num {
  font-variant-numeric: tabular-nums;
  text-align: right;
}
#browser-telemetry-card .btel-table .btel-mono {
  font-family: var(--mono-font, ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace);
  font-size: 12px;
  color: var(--c-fg-dim, #aaa);
}
#browser-telemetry-card .btel-status-running   { color: var(--win-ok, #5bd25b); }
#browser-telemetry-card .btel-status-suspended { color: var(--win-warn, #d9a93b); }
#browser-telemetry-card .btel-status-gc        { color: var(--c-fg-dim, #888); }
#browser-telemetry-card .btel-stats-err {
  color: var(--win-err, #e25b5b);
  font-style: italic;
}
#browser-telemetry-card .btel-footer {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
  flex-wrap: wrap;
}

/* ===== AKF.3 (2026-05-29) — Accounts: Google-OAuth + CalDAV ===== */

/* AKF.3 — Accounts: Google-OAuth + CalDAV-Karten.
   Wird an style.css drangehaengt. */

/* OAuth-Callback-Standalone-Seite */
.oauth-callback-body {
  background: var(--c-bg, #1a1a1a);
  color: var(--c-fg, #e0e0e0);
  font-family: var(--ui-font, system-ui, -apple-system, "Segoe UI", sans-serif);
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; margin: 0; padding: 16px;
}
.oauth-callback-card {
  background: var(--c-card, #232323);
  border: 1px solid var(--c-border, #333);
  border-radius: 8px;
  padding: 32px 28px; max-width: 480px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.oauth-callback-title { margin: 0 0 12px; font-size: 18px; }
.oauth-callback-message { margin: 0 0 8px; }
.oauth-callback-hint { color: var(--c-fg-dim, #888); font-size: 13px; }
.oauth-callback-link {
  display: inline-block; margin-top: 16px;
  color: var(--c-accent, #4aa3ff); text-decoration: none;
}
.oauth-callback-link:hover { text-decoration: underline; }
.oauth-callback-ok .oauth-callback-title { color: var(--win-ok, #5bd25b); }
.oauth-callback-error .oauth-callback-title { color: var(--win-err, #e25b5b); }

/* Integration-Karten im Accounts-Tab */
.integration-card .integration-status-row {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 8px 0 12px;
}
.integration-card .integration-pill {
  padding: 3px 10px; border-radius: 12px; font-size: 12px; font-weight: 500;
  background: var(--c-pill-bg, #2a2a2a); color: var(--c-fg-dim, #aaa);
  border: 1px solid var(--c-border, #333);
}
.integration-card .integration-pill.connected {
  background: rgba(91, 210, 91, 0.15); color: var(--win-ok, #5bd25b);
  border-color: rgba(91, 210, 91, 0.3);
}
.integration-card .integration-pill.error {
  background: rgba(226, 91, 91, 0.15); color: var(--win-err, #e25b5b);
  border-color: rgba(226, 91, 91, 0.3);
}
.integration-card .integration-pill.warning {
  background: rgba(217, 169, 59, 0.15); color: var(--win-warn, #d9a93b);
  border-color: rgba(217, 169, 59, 0.3);
}
.integration-card .integration-profile {
  display: flex; align-items: center; gap: 12px;
  padding: 10px; background: var(--c-bg-sub, #1a1a1a);
  border-radius: 6px; margin: 8px 0;
}
.integration-card .integration-profile-avatar {
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--c-bg-dim, #2a2a2a);
  object-fit: cover;
}
.integration-card .integration-profile-text { display: flex; flex-direction: column; gap: 2px; }
.integration-card .integration-profile-email { font-weight: 500; }
.integration-card .integration-profile-sub {
  font-size: 12px; color: var(--c-fg-dim, #888);
}
.integration-card .integration-scopes-list {
  display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 12px;
}
.integration-card .integration-scope {
  padding: 2px 8px; border-radius: 10px; font-size: 11px;
  background: var(--c-bg-sub, #1f1f1f); color: var(--c-fg-dim, #aaa);
  border: 1px solid var(--c-border, #333);
}
.integration-card .integration-calendars {
  display: flex; flex-direction: column; gap: 6px; margin: 10px 0;
}
.integration-card .integration-calendar-row {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; background: var(--c-bg-sub, #1a1a1a);
  border-radius: 6px;
}
.integration-card .integration-calendar-color {
  width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0;
  background: var(--c-fg-dim, #666);
  border: 1px solid var(--c-border, #333);
}
.integration-card .integration-calendar-name {
  font-weight: 500;
}
.integration-card .integration-calendar-url {
  font-size: 11px; color: var(--c-fg-dim, #888);
  word-break: break-all;
}
.integration-card .integration-setup-details {
  margin: 12px 0; padding: 10px 12px;
  background: var(--c-bg-sub, #1a1a1a);
  border: 1px solid var(--c-border, #333);
  border-radius: 6px;
}
.integration-card .integration-setup-details summary {
  cursor: pointer; user-select: none; font-weight: 500;
}
.integration-card .integration-import-hint {
  margin: 8px 0; padding: 8px 10px;
  background: rgba(74, 163, 255, 0.1);
  border: 1px solid rgba(74, 163, 255, 0.3);
  border-radius: 6px; font-size: 13px;
}

/* ============================================================
   Mobile-Navigationsmenü + Topbar-Umbau
   (2026-06-01, Backlog-Item 18531322 „Webinterface für Mobile umgestalten")
   ------------------------------------------------------------
   Auf Mobile ersetzt EIN Dropdown-Menü in der Topbar (#btn-mobile-nav)
   sowohl die separate .tabs-Leiste ALS AUCH den Projekt-Switcher. Das Menü
   enthält Webchat, Backlog und alle aktivierten (gepinnten) Apps. Die Topbar
   bleibt beim Scrollen oben (.app-header ist bereits position:sticky), nur
   der Inhalt darunter scrollt.

   Diese Sektion steht bewusst am DATEIENDE, damit sie im Cascade alle
   früheren mobile-Regeln (Tab-Bar-Positionierung/-Höhen, chat-wrap-calc,
   project-switcher) überschreibt — gleiche/höhere Spezifität + spätere
   Position gewinnen. Wo eine frühere Regel !important-frei nicht sicher zu
   schlagen war, steht hier explizit !important.
   ============================================================ */

/* Desktop-Default: Mobile-Menü unsichtbar, dort bleibt die klassische
   .tabs-Leiste die Navigation. */
.mobile-nav-menu-btn { display: none; }
.mobile-nav-menu-popover { display: none; }

/* Menüeintrag-Optik (gerätunabhängig, wird nur im Mobile-Popover sichtbar). */
.mobile-nav-menu-item {
  display: block; width: 100%;
  text-align: left;
  padding: 12px 14px;
  min-height: 44px;
  border: 0; background: transparent;
  font: inherit; color: inherit; cursor: pointer;
  border-radius: 6px;
}
.mobile-nav-menu-item:hover { background: var(--c-bg-sub, rgba(127,127,127,0.12)); }
.mobile-nav-menu-item.active { font-weight: 600; background: var(--c-bg-sub, rgba(127,127,127,0.12)); }

/* Gemeinsame Mobile-Regeln (UA/Touch-erkanntes Gerät via html.is-mobile) */
html.is-mobile .tabs { display: none !important; }
html.is-mobile .project-switcher,
html.is-mobile .project-switcher-popover { display: none !important; }
html.is-mobile .mobile-nav-menu-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  min-height: 44px;
  max-width: 60vw;
  cursor: pointer;
}
html.is-mobile .mobile-nav-menu-label {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
html.is-mobile .mobile-nav-menu-popover {
  position: fixed;
  left: 8px; right: 8px; top: 60px;
  max-height: 70vh; overflow: auto;
  z-index: 60; padding: 6px;
  display: block;
  background: var(--c-bg, #1a1a1a);
  border: 1px solid var(--c-border, #333);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.35);
}
html.is-mobile .mobile-nav-menu-popover[hidden] { display: none; }

/* Ohne fixe Tab-Bar braucht der Content keinen ~110px-Offset mehr: die
   sticky-Topbar (~60px) liegt in-flow, der Inhalt schließt direkt darunter
   an. (Vorher musste main padding-top die ABSOLUT positionierte Tab-Bar
   freihalten.) */
html.is-mobile main { padding-top: 0; }
html.is-mobile body.tab-chat main,
html.is-mobile body.tab-chat #app-main { padding-top: 0; }

/* Chat füllt jetzt 100dvh minus NUR die Topbar (vorher zusätzlich -60px für
   die Tab-Bar). */
html.is-mobile body.tab-chat .chat-wrap {
  height: calc(100dvh - 60px - env(safe-area-inset-bottom, 0px));
}

/* Viewport-basierter Fallback: schmaler Viewport ohne is-mobile-Erkennung
   (z. B. schmales Desktop-Browserfenster, responsive Devtools). */
@media (max-width: 720px) {
  .tabs { display: none !important; }
  .project-switcher,
  .project-switcher-popover { display: none !important; }
  .mobile-nav-menu-btn {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 8px 10px; min-height: 44px; max-width: 60vw; cursor: pointer;
  }
  .mobile-nav-menu-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .mobile-nav-menu-popover {
    position: fixed; left: 8px; right: 8px; top: 60px;
    max-height: 70vh; overflow: auto; z-index: 60; padding: 6px;
    display: block;
    background: var(--c-bg, #1a1a1a);
    border: 1px solid var(--c-border, #333);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.35);
  }
  .mobile-nav-menu-popover[hidden] { display: none; }
  main { padding-top: 0; }
  body.tab-chat main, body.tab-chat #app-main { padding-top: 0; }
  body.tab-chat .chat-wrap {
    height: calc(100dvh - 60px - env(safe-area-inset-bottom, 0px));
  }
}

/* ── K.12 (2026-06-05): Drop-Zone-Hervorhebung fuer den App-Daten-Bus
   (Drag&Drop zwischen App-Fenstern). Klassenbasiert + CSSOM-frei (CSP-
   konform: kein inline-style, keine <style>-Erzeugung). Die Klasse wird
   von _wmWireDndDropZone() in app.js am <daria-window>-Host getoggelt,
   waehrend ein akzeptierter Daria-DnD-Payload ueber dem Fenster schwebt. */
daria-window.daria-dnd-drop-active {
  outline: 2px solid var(--accent, #4ea1ff);
  outline-offset: -2px;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent, #4ea1ff) 28%, transparent);
}

/* Globaler Standard-Modell-Selektor in der Backlog-Topbar (2026-06-10).
   Schlankes Label+Select, optisch wie die .ghost-Buttons in der Aktionszeile. */
.backlog-default-model { display:inline-flex; align-items:center; gap:.4rem; font-size:.85rem; }
.backlog-default-model > span { color: var(--muted, #94a3b8); white-space:nowrap; }
.backlog-default-model select { font-size:.85rem; padding:.15rem .35rem; }

/* --- TR.7: systemweites Übersetzen-Kontextmenü --------------------------- */
/* public/translate/context-menu.js. Basis-Look erbt das Menü von
   .msg-ctx-menu (gemeinsame Klasse); hier nur die Abweichungen. Liegt mit
   z-index 9100 ÜBER Chat-Kontextmenü/Emoji-Picker (9000) und damit sicher
   über allen WM-Fenstern (#wm-mount). */
.tr-ctx-menu { z-index: 9100; }
.tr-ctx-menu.tr-ctx-wide { min-width: 240px; max-width: 360px; }

/* Kopfzeile des Ergebnis-Popovers: Richtung (z. B. "EN → DE") + Schließen. */
.tr-ctx-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 2px 6px 8px;
  margin-bottom: 4px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--text-muted, #6b7280);
  border-bottom: 1px solid var(--border, rgba(0, 0, 0, .1));
}
.tr-ctx-close {
  padding: 2px 7px;
  font-size: 14px;
  line-height: 1;
  color: var(--text-muted, #6b7280);
  background: transparent;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}
.tr-ctx-close:hover,
.tr-ctx-close:focus-visible {
  background: var(--bg-hover, var(--accent-soft, rgba(42, 171, 238, .16)));
  outline: none;
}

/* Übersetzungs-Ergebnis: selektier-/scrollbar (Menü selbst ist user-select:none). */
.tr-ctx-text {
  max-height: 240px;
  overflow: auto;
  padding: 6px;
  font-size: 14px;
  line-height: 1.45;
  white-space: pre-wrap;
  word-break: break-word;
  cursor: text;
  user-select: text;
  -webkit-user-select: text;
}

/* Status-Zeile (Spinner/Fehler). */
.tr-ctx-status {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 8px;
  font-size: 13px;
  color: var(--text-muted, #6b7280);
}
.tr-ctx-spinner {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  border: 2px solid var(--border, rgba(0, 0, 0, .15));
  border-top-color: var(--accent, #2aabee);
  border-radius: 50%;
  animation: trctx-spin .8s linear infinite;
}
@keyframes trctx-spin { to { transform: rotate(360deg); } }

/* Fußzeile unter dem Ergebnis (Kopieren; TR.9 ergänzt "Ersetzen"). */
.tr-ctx-foot {
  margin-top: 4px;
  padding-top: 6px;
  border-top: 1px solid var(--border, rgba(0, 0, 0, .1));
}

/* TR.10: Inline-Untermenü "Übersetzen nach …" (explizite Zielwahl).
   .msg-ctx-item ist flex — Caret (▸/▾ am Eltern-Eintrag) und ✓ (gemerkte
   Zielsprache) rücken per margin-left:auto an den rechten Rand; die
   Kind-Einträge sind eingerückt (leeres Icon + Extra-Padding). */
.tr-ctx-caret {
  margin-left: auto;
  font-size: 11px;
  color: var(--text-muted, #6b7280);
}
.tr-ctx-subitem { padding-left: 32px; }
.tr-ctx-check {
  margin-left: auto;
  font-size: 12px;
  color: var(--accent, #2aabee);
}

/* Chat-Verlauf: WM-Fenster (<daria-window>) vererben user-select:none in die
   reparenteten Panels — Bubble-Text muss fuer die Rechtsklick-Uebersetzung
   (und Kopieren generell) selektierbar sein (TR.7; gleiche Falle wie bei der
   Uebersetzer-App, siehe TR.4). */
#chat-list .bubble {
  user-select: text;
  -webkit-user-select: text;
}


/* ── Chat-Slot-Titelleiste + Badges (B-2b, Item c857a25f) ──────────────────
   Drei runde Slot-Badges (1·2·3) rechtsbuendig in der Fenster-Titelleiste der
   System-App „Chat", links neben dem Kamera-Icon der .extras. Der Badge-Bar-
   Knoten ist Light-DOM (per daria-window.setTitlebar in den named slot
   projiziert) → Dokument-CSS greift auf ihn und seine Kinder. */
.chat-titlebar {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-width: 0;
}
/* Fenstertitel HORIZONTAL MITTIG in der Titelleiste (Wunsch Dennis 2026-06-18).
   Der Chat-Titel soll optisch zur FENSTERMITTE zentriert sein — nicht zum
   freien Platz rechts der Ampel-Buttons. Trick: der Titel wird absolut
   positioniert. Sein Containing-Block ist die Shadow-DOM-`.titlebar` von
   <daria-window> (die ist `position: relative`), also die VOLLE Breite der
   Titelleiste über Ampeln (links) und Slot-/Kamera-Icons (rechts) hinweg.
   `left/right:0` + `margin-inline:auto` + `width:max-content` zentriert das
   Element exakt zur Fenstermitte, unabhängig von der Breite der seitlichen
   Bedienelemente. `max-width` hält den Titel klar von beiden Seiten weg und
   ellipsiert lange Titel, statt sie unter Ampeln/Badges laufen zu lassen.
   `pointer-events:none` lässt Klicks zu den darunterliegenden Drag-/Badge-
   Zonen durch. Scoped allein auf `.chat-titlebar-title` → betrifft nur das
   Chat-Fenster; andere Apps nutzen den Shadow-DOM-`.title` und bleiben
   unberührt. CSP-konform: reine CSS-Klasse, kein Inline-Style. */
.chat-titlebar-title {
  position: absolute;
  left: 0;
  right: 0;
  margin-inline: auto;
  width: max-content;
  max-width: calc(100% - 200px);
  text-align: center;
  pointer-events: none;
  font-weight: 600;
  font-size: 13px;
  color: var(--text, #1d1d1f);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chat-titlebar-badges {
  margin-left: auto;       /* schiebt die Badges an den rechten Rand (vor .extras) */
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  /* Über dem absolut zentrierten Titel, damit die Badges bei einem langen
     Titel klickbar/sichtbar oben liegen (der zentrierte Titel ist ohnehin
     pointer-events:none und per max-width vom Badge-Bereich entkoppelt). */
  position: relative;
  z-index: 2;
}
.chat-slot-badge {
  -webkit-appearance: none;
  appearance: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 1.5px solid var(--border, rgba(127, 127, 127, 0.45));
  background: transparent;
  color: var(--text, #1d1d1f);
  font-size: 11px;
  line-height: 1;
  font-weight: 600;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background 0.12s ease, border-color 0.12s ease, opacity 0.12s ease;
}
.chat-slot-badge:hover { border-color: var(--accent, #3b82f6); }
.chat-slot-badge:focus-visible { outline: 2px solid var(--accent, #3b82f6); outline-offset: 1px; }
/* gefuellt = fokussierter/aktiver Slot im Vordergrund */
.chat-slot-badge.is-focused {
  background: var(--accent, #3b82f6);
  border-color: var(--accent, #3b82f6);
  color: var(--on-accent, #ffffff);
}
/* umrandet = Slot offen (nicht vorne) ODER zu, aber Verlauf vorhanden */
.chat-slot-badge.is-active {
  background: transparent;
  border-color: var(--accent, #3b82f6);
  color: var(--accent, #3b82f6);
}
/* gedaempft = nie geoeffnet (kein Verlauf) */
.chat-slot-badge.is-empty {
  opacity: 0.45;
  border-color: var(--border, rgba(127, 127, 127, 0.45));
  color: var(--text, #1d1d1f);
}
/* (Fix A) „denkt" = in diesem Slot laeuft gerade ein Turn (auch im Hintergrund,
   seit Antworten nicht mehr in den sichtbaren Slot leaken). Sanftes Pulsieren
   des Rahmens, orthogonal zu is-focused/is-active/is-empty. */
.chat-slot-badge.is-thinking {
  animation: chat-slot-thinking 1.1s ease-in-out infinite;
}
@keyframes chat-slot-thinking {
  0%, 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
  50%      { box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.35); }
}
/* Governance P1-5: reiner Notify-Slot 'N'. Badge dezent abgesetzt (Buchstabe
   statt Zahl); der Feed erbt .chat-list. */
.chat-slot-badge-notify {
  font-weight: 700;
  letter-spacing: 0.02em;
}
/* Slot P (Plain/Personal/Private): monochromes Icon statt Ziffer. Das SVG erbt
   ueber currentColor die Badge-Farbzustaende (is-focused/is-active/is-empty). */
.chat-slot-badge-plain svg { display: block; }
/* Read-only Notify-Feed: erbt Layout von .chat-list (beide Klassen am Element).
   Keine Composer-Interaktion. */
.chat-notify-feed { }
/* Notify-Bubble: als reine Ausgabe erkennbar (linker Akzentstreifen), plus eine
   kleine Meta-Zeile (App · Kategorie · Uhrzeit). */
.bubble.bubble-notify {
  border-left: 3px solid var(--accent, #3b82f6);
}
.bubble-notify-meta {
  margin-top: 4px;
  font-size: 11px;
  opacity: 0.6;
  text-transform: none;
}
/* Slot-2/3-Platzhalter im B-2b-Zwischenzustand (echter Feed: B-2c) */
.chat-slot-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 100%;
  padding: 24px;
  text-align: center;
  color: var(--text-muted, #6b6b6b);
}
.chat-slot-placeholder-title {
  font-size: 18px;
  font-weight: 600;
  color: var(--text, #1d1d1f);
}
.chat-slot-placeholder-hint {
  font-size: 13px;
  max-width: 320px;
  line-height: 1.5;
}
/* B-2c: Statischer Feed-Snapshot eines Slot-Fensters, das gerade NICHT das
   Live-Panel #panel-chat hält. Zeigt den zuletzt aktiven Feed read-only, damit
   das Fenster nicht leer wirkt; sobald der Slot wieder fokussiert wird, ersetzt
   _setActiveChatSlot den Snapshot durch das echte, interaktive Panel. Die
   leichte Abdunklung signalisiert „pausiert/erhalten" gegenüber dem Live-Feed. */
.chat-slot-snapshot {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  overflow: hidden;
}
.chat-slot-snapshot-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 12px;
  opacity: 0.78;
  pointer-events: none; /* read-only: keine Klick-/Hover-Interaktion am Snapshot */
}

/* ── Multi-Account-Google + App-Zuordnung (Item 3d6f07e9, 2026-06-19) ──
   Stilistisch an die bestehenden .integration-card-Klassen angelehnt
   (gleiche Fallback-Farben, damit die neuen Blocks zur Google-Karte
   passen). */
.integration-card .google-accounts-list {
  display: flex; flex-direction: column; gap: 8px; margin: 6px 0 12px;
}
.integration-card .google-account-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 8px 10px; background: var(--c-bg-sub, #1a1a1a);
  border: 1px solid var(--c-border, #333); border-radius: 6px;
}
.integration-card .google-account-avatar {
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--c-bg-dim, #2a2a2a); object-fit: cover; flex-shrink: 0;
}
.integration-card .google-account-meta {
  display: flex; flex-direction: column; gap: 1px; flex: 1 1 160px; min-width: 0;
}
.integration-card .google-account-name { font-weight: 500; }
.integration-card .google-account-email {
  font-size: 12px; color: var(--c-fg-dim, #888);
  overflow: hidden; text-overflow: ellipsis;
}
.integration-card .google-account-badge {
  display: inline-block; padding: 1px 6px; border-radius: 8px; font-size: 10px;
  background: var(--c-bg-dim, #2a2a2a); color: var(--c-fg-dim, #aaa);
  border: 1px solid var(--c-border, #333);
}
.integration-card .google-account-actions { display: flex; gap: 6px; flex-shrink: 0; }
.integration-card .google-account-places {
  flex: 1 1 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-top: 6px;
}
.integration-card .google-account-places-input { flex: 1 1 200px; min-width: 0; }
.integration-card .google-account-places-hint { flex: 1 1 100%; }

.app-accounts-list { display: flex; flex-direction: column; gap: 14px; }
.app-account-block {
  padding: 10px 12px; background: var(--c-bg-sub, #1a1a1a);
  border: 1px solid var(--c-border, #333); border-radius: 6px;
}
.app-account-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.app-account-mode { font-size: 11px; }
.app-account-options { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.app-account-option {
  display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: 13px;
}
.app-account-option input { flex-shrink: 0; }

/* ── Kalender-Subsystem-Karte (Accounts, Backlog a47b1b2f, 2026-06-24) ──────
   Systemweite Kalender-Auswahl: pro Backend (Google/CalDAV) eine Sektion mit
   einer Liste aus Kalender-Zeilen, jede mit Sync-an/aus-Toggle rechts. Baut
   auf den vorhandenen .integration-calendar-*-Klassen auf; nur das Layout-
   Detail (Toggle nach rechts) + die Sektions-Ueberschrift kommen hinzu. */
.integration-card .calendar-backend-section { margin: 10px 0; }
.integration-card .calendar-backend-section > h3 {
  margin: 8px 0 4px; font-size: 12px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-dim, var(--c-fg-dim, #888));
}
.integration-card #calendar-backends-list .integration-calendar-row .switch {
  margin-left: auto; flex-shrink: 0;
}
.integration-card #calendar-backends-list .integration-calendar-name {
  word-break: break-word;
}


/* ── Phishing-Warn-Badge im Spotlight-Mailtreffer (Item 3250a1b2) ───────────
   Kleiner farbcodierter Chip vor dem Treffer-Titel: rot = hohes Risiko,
   gelb = verdaechtig. Klartext-Gruende im title-Tooltip. */
.shortcuts-quick-phish {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-right: 5px;
  padding: 0 4px;
  border-radius: 5px;
  font-size: .82em;
  line-height: 1.5;
  cursor: help;
  vertical-align: middle;
}
.shortcuts-quick-phish.is-high {
  background: rgba(220, 38, 38, .16);
  border: 1px solid rgba(220, 38, 38, .5);
}
.shortcuts-quick-phish.is-suspicious {
  background: rgba(202, 138, 4, .14);
  border: 1px solid rgba(202, 138, 4, .45);
}

/* ─── Zwischenschritt-Disclosure unter Antwort-Bubbles (Item 3b2fe53a, 2026-07-16) ───
   Eine Antwort-Bubble (.bubble.in) kann die Denk-Bloecke + Tool-Calls tragen,
   die zu ihr gefuehrt haben. Standardmaessig EINGEKLAPPT, per Pfeil ODER
   Kontextmenue aufklappbar. Kein inline-style/<style> — CSP-konform, Farben
   ueber Theme-Variablen (currentColor beim Chevron). */
.msg-steps {
  margin-top: 8px;
  padding-top: 6px;
  border-top: 1px solid var(--border, rgba(127, 127, 127, .28));
}
.msg-steps-summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: .82em;
  color: var(--text-muted, #8a8a8a);
  user-select: none;
}
.msg-steps-summary::-webkit-details-marker { display: none; }
.msg-steps-summary::marker { content: ''; }
.msg-steps-caret {
  display: inline-flex;
  align-items: center;
  transition: transform .15s ease;
  opacity: .8;
}
.msg-steps[open] > .msg-steps-summary .msg-steps-caret {
  transform: rotate(180deg);
}
.msg-steps-body {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-top: 6px;
}
/* Einzelne Schritte — Denk-Block (dezent) + gestrichelte Tool-Blase, analog
   zum Live-Stream. white-space: pre-wrap erhaelt Zeilenumbrueche im Reasoning. */
.msg-step {
  font-size: .82em;
  line-height: 1.4;
  padding: 5px 8px;
  border-radius: 8px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  color: var(--text-muted, #8a8a8a);
}
.msg-step-think {
  background: var(--bg-hover, rgba(127, 127, 127, .1));
}
.msg-step-tool {
  border: 1px dashed var(--border, rgba(127, 127, 127, .4));
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
}

/* ============================================================================
   Chromeloser Panel-Modus (`?chrome=none`, 2026-07-26, Item e70ebc9c)
   ============================================================================

   `/?tab=chat&chrome=none` bzw. `/?tab=backlog&chrome=none` zeigen GENAU EIN
   Panel formatfuellend: keine Topbar, keine Tab-Leiste, kein Dock, keine
   Taskleiste, kein Window-Manager. Anzeigeflaeche fuer die native macOS-App —
   die stellt ihr Fenster-Chrome selbst.

   Das Attribut `data-chrome="none"` setzt der Server ins <html> (server.js),
   also vor dem ersten Paint. Der Rest der Kette: app.js (Shell auf 'custom'
   herunterstufen), menubar.js + launch-dock.js (Boot-Guards), menubar.css
   (Bar + Body-Spacing). Vollstaendige Beschreibung inkl. der Pflicht, beide
   Darstellungen kuenftig mitzupflegen: docs/CHROMELESS-PANEL-MODE.md.

   Dieser Block steht bewusst am DATEIENDE: mehrere Regeln kontern
   gleich-spezifische Vorgaben von weiter oben (u. a. `body.tab-chat
   .chat-wrap` und die is-mobile-Varianten), die sonst per Source-Order
   gewinnen wuerden. Jede Regel haengt an `html[data-chrome="none"]` — ohne
   den Parameter ist der ganze Block wirkungslos, die regulaere Shell bleibt
   Byte-fuer-Byte beim alten Verhalten.
   ------------------------------------------------------------------------ */

/* Topbar samt Tab-Leiste, Zahnrad und Status-Icons. Sie ist `position:sticky`,
   belegt also Fluss-Hoehe — `display:none` statt `visibility`, sonst bliebe
   genau der tote Streifen oben stehen, den der Modus ausschliesst. */
html[data-chrome="none"] .app-header { display: none; }

/* Window-Manager-Buehne. app.js mountet sie chromelos gar nicht erst
   (activateWMShell kehrt frueh zurueck); die Regel ist die Absicherung fuer
   den Fall, dass ein Fremdpfad das hidden-Attribut doch entfernt. */
html[data-chrome="none"] #wm-mount { display: none !important; }

/* Body-Spacing der fixed Bars zuruecknehmen (Gegenstueck zu menubar.css). */
html[data-chrome="none"] body {
  padding-top: 0;
  padding-bottom: 0;
}

/* Die Buehne fuellt das Fenster: keine 880-px-Karte, kein Aussenrand. Chat und
   Backlog bringen das ueber ihre body-Klasse ohnehin mit — hier gilt es fuer
   JEDES Panel, damit der Modus auch dann randlos bleibt, wenn spaeter ein
   weiteres Panel freigegeben wird (CHROMELESS_PANELS in app.js). */
html[data-chrome="none"] #app-main {
  max-width: none;
  margin: 0;
  padding: 0;
}
/* Mobile-Varianten (`html.is-mobile …` und die 720-px-Media-Query) reservieren
   Platz fuer die dort oben fixierte Tab-Leiste. Die gibt es chromelos nicht. */
html[data-chrome="none"].is-mobile body.tab-chat #app-main,
html[data-chrome="none"].is-mobile body.tab-chat main,
html[data-chrome="none"].is-mobile body.tab-backlog #app-main { padding-top: 0; }

/* Slot-Kopfzeile des Chat-Panels (`.chat-titlebar-panel`, gebaut von
   app.js#installChromelessChatTitlebar). Im Desktop-Modus sitzt dieselbe
   Badge-Bar in der Titelleiste des <daria-window>; chromelos gibt es kein
   Fenster, also traegt das Panel sie selbst — sonst haette die macOS-App
   (Desktop-Fenster UND Seitenleisten-Shell) keine Slot-Auswahl.
   Sie ist bewusst schlank: auf dem Mac steht direkt darueber schon die native
   Fenster-Titelleiste. */
html[data-chrome="none"] .chat-titlebar-panel {
  /* Containing-Block fuer den absolut zentrierten `.chat-titlebar-title` — im
     Fenster uebernimmt das die Shadow-DOM-`.titlebar` von <daria-window>. */
  position: relative;
  box-sizing: border-box;
  /* Hoehe + Flaeche aus den ECHTEN Theme-Tokens der Fenster-Titelleiste
     (tokens.window.titlebar-h / .titlebar-bg im Manifest, dieselben, die
     daria-window.js verwendet) — nicht nativ erfinden, sonst weicht der
     Panel-Modus optisch vom Fenster ab. */
  height: var(--titlebar-h, 32px);
  padding: 0 12px;
  border-bottom: 1px solid var(--border, rgba(127, 127, 127, 0.25));
  background: var(--titlebar-bg, var(--bg-elev, transparent));
  flex: 0 0 auto;
}
/* Der Titel darf hier die volle Breite nutzen: es gibt keine Ampel-Knoepfe
   links, nur die Badges rechts (die per z-index ohnehin obenauf liegen). */
html[data-chrome="none"] .chat-titlebar-panel .chat-titlebar-title {
  max-width: calc(100% - 160px);
}

/* Hoehen: beide Panels rechnen regulaer `100dvh - 60px` fuer die Topbar. Ohne
   Topbar ist das exakt der tote Streifen unten, den Kriterium 1 verbietet.
   Das Chat-Panel traegt chromelos zusaetzlich die Slot-Kopfzeile — deren Hoehe
   muss hier abgezogen werden, sonst waechst das Panel ueber den Viewport
   hinaus und der Composer rutscht unten raus. */
html[data-chrome="none"] body.tab-chat .chat-wrap {
  height: calc(100dvh - var(--titlebar-h, 32px));
}
/* MP.9: Zeichnet der HOST die Slot-Auswahl selbst (macOS-App — echte
   Fenster-Titelleiste bzw. Knopf in der Seitenleiste), gibt es hier keine
   Kopfzeile und der Chat bekommt die volle Hoehe zurueck. Das Attribut setzt
   das documentStart-Skript der App, also VOR dem ersten Paint — sonst blitzte
   genau der 32-px-Streifen auf, den es zu vermeiden gilt. */
html[data-chrome="none"][data-slot-chrome="native"] body.tab-chat .chat-wrap {
  height: 100dvh;
}
/* overflow explizit mit: `html.is-mobile .backlog-page` setzt height:auto +
   overflow:visible. Die Hoehe kontern wir per Source-Order, das overflow muss
   aber ebenfalls zurueck auf den Desktop-Default — sonst scrollt chromelos die
   Seite selbst statt nur die rechte Spalte. */
html[data-chrome="none"] .backlog-page { height: 100dvh; overflow: hidden; }

/* ── Schreib-Puffer fuer Einstellungen: Fehler-Hinweis (Item 3cde22a0) ─────
   public/settings-writer.js sammelt Schalter-/Feld-Aenderungen und schickt
   sie gebuendelt. Ein 429 wird automatisch nachgereicht; erst wenn ALLE
   Versuche scheitern, erscheint dieser dezente Hinweis. Er rollt bewusst
   nichts zurueck, sondern bietet „Erneut speichern" an — der gepufferte
   Wunsch bleibt so lange erhalten.
   Klassen statt inline-`style`, weil style-src kein unsafe-inline erlaubt. */
.dsw-alerts {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 10000;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}
.dsw-alert {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(420px, calc(100vw - 32px));
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--border, rgba(128, 128, 128, 0.35));
  background: var(--bg-card, #1e1e1e);
  color: var(--text, inherit);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);
  font-size: 13px;
  line-height: 1.35;
}
.dsw-alert-msg { flex: 1 1 auto; }
.dsw-alert-retry {
  flex: 0 0 auto;
  padding: 4px 10px;
  border-radius: 7px;
  border: 1px solid var(--border, rgba(128, 128, 128, 0.35));
  background: var(--bg-alt, rgba(128, 128, 128, 0.14));
  color: inherit;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.dsw-alert-retry:hover:not(:disabled) { background: var(--bg-hover, rgba(128, 128, 128, 0.24)); }
.dsw-alert-retry:disabled { opacity: 0.6; cursor: default; }
.dsw-alert-close {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-secondary, inherit);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}
.dsw-alert-close:hover { background: var(--bg-hover, rgba(128, 128, 128, 0.2)); }
