/* ============ Operator-Console Palette ============
   Bewusst durchgaengig dunkel statt hell/dunkel je nach Systemeinstellung - die "Kontrollraum"-
   Optik (dunkler Grund, Kobalt-Akzent, Monospace fuer Technikdaten) IST die neue Design-
   Identitaet, kein optionaler Anstrich. Ein heller Zweig haette genau die pastellige "nur ein
   bisschen umgefaerbt"-Wirkung, die das ausdruecklich NICHT sein soll. Variablennamen bewusst
   unveraendert gegenueber dem vorigen Theme (nur Werte neu) - Mandanten-Branding (siehe
   layout.php, ueberschreibt --primary/--primary-hover live aus tenants.primary_color) und alle
   Templates, die diese Variablen per var(--xxx) referenzieren, funktionieren dadurch
   unveraendert weiter. */
:root {
    color-scheme: dark;
    --bg: #0a0d12;
    --surface: #10151c;
    --surface-alt: #161d26;
    --border: #232b36;
    --text: #e7ecf3;
    --text-muted: #8b96a6;
    --primary: #6e93ff;
    --primary-hover: #8ba6ff;
    --primary-contrast: #0a0d12;
    --accent-bg: rgba(110, 147, 255, 0.16);
    --danger: #f0555a;
    --danger-hover: #f2777b;
    --success: #34d399;
    --success-bg: rgba(52, 211, 153, 0.14);
    --danger-bg: rgba(240, 85, 90, 0.14);
    --warning-bg: rgba(245, 166, 35, 0.14);
    --warning-text: #f5a623;
    --radius: 9px;
    --radius-sm: 5px;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.4);
    --shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.6), 0 2px 8px rgba(0, 0, 0, 0.45);
    /* Fuer technische Nutzdaten (Hostnamen, IPs, Versionen, Log-/Audit-Zeilen) - eigenes,
       additives Token statt die Basisschrift zu ersetzen, siehe unten .mono/Log-/Tabellen-
       Regeln. Cascadia Code passt inhaltlich zum Windows-/Terminal-Kontext des Produkts. */
    --font-mono: "Cascadia Code", "Cascadia Mono", ui-monospace, "SFMono-Regular", Consolas, Menlo, monospace;
}
* { box-sizing: border-box; }
body {
    font-family: "Segoe UI Variable Text", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", Arial, sans-serif;
    margin: 0; background: var(--bg); color: var(--text);
    -webkit-font-smoothing: antialiased;
}
a { color: inherit; }
:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* ============ App-Gerüst: obere Befehlsleiste (Desktop + Handy) ============ */
/* Löst die vorige feste Seitenleiste ab - Navigation läuft jetzt horizontal über eine sticky
   Kopfleiste (.topbar), Admin-Unterseiten und Konto klappen als Dropdown-Panel auf (Desktop)
   bzw. landen gesammelt in einer linken Schublade (Handy, siehe .mobile-nav weiter unten). */
.app-shell { display: block; }

.topbar {
    position: sticky; top: 0; z-index: 40; display: flex; align-items: center; gap: 0.5rem;
    padding: 0.55rem 1rem; background: var(--surface); border-bottom: 1px solid var(--border);
}
.topbar-brand {
    display: flex; align-items: center; gap: 0.55rem; padding: 0.2rem 0.4rem 0.2rem 0;
    text-decoration: none; color: var(--text); font-weight: 700; font-size: 0.95rem; flex: 0 0 auto;
}
.topbar-brand img { height: 1.9rem; width: auto; display: block; border-radius: 4px; }
.topbar-nav { display: flex; align-items: center; gap: 0.15rem; flex: 1 1 auto; }
.nav-group-label {
    font-size: 0.7rem; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
    color: var(--text-muted); padding: 0.9rem 0.6rem 0.3rem;
}
.nav-item {
    display: flex; align-items: center; gap: 0.6rem; padding: 0.5rem 0.7rem; border-radius: var(--radius-sm);
    color: var(--text-muted); font-size: 0.87rem; font-weight: 560; text-decoration: none; cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease; border: none; background: none; font-family: inherit;
    white-space: nowrap;
}
.nav-item svg { flex: 0 0 auto; width: 16px; height: 16px; }
.nav-item:hover { background: var(--surface-alt); color: var(--text); }
.nav-item.active { background: var(--accent-bg); color: var(--primary); }
.nav-item.sub { font-size: 0.87rem; }

/* Dropdown-Panel (Admin-Unterseiten + Konto) - dieselbe Optik fuer beide, ueber ein
   .nav-dropdown[data-open] am gemeinsamen Wrapper gesteuert (siehe layout.php-Skript). */
.nav-dropdown { position: relative; flex: 0 0 auto; }
.dropdown-panel {
    position: absolute; top: 100%; margin-top: 0.4rem; min-width: 13rem; z-index: 41;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    box-shadow: var(--shadow-lg); padding: 0.4rem; display: none; flex-direction: column;
}
.nav-dropdown[data-open="true"] .dropdown-panel { display: flex; }
.nav-dropdown.align-left .dropdown-panel { left: 0; }
.nav-dropdown.align-right .dropdown-panel { right: 0; }
.dropdown-panel .nav-item { width: 100%; box-sizing: border-box; }

/* Suche in der Topbar (siehe #topbar-search-input) - Strg+K/⌘K fokussiert sie von überall
   (layout.php-Skript). Filtert auf dem Dashboard live die sichtbare Geräteliste (dashboard.php);
   auf anderen Seiten bislang nur fokussierbar, noch ohne eigene Wirkung dort. */
.topbar-search {
    display: flex; align-items: center; gap: 0.5rem; background: var(--surface-alt); border: 1px solid var(--border);
    border-radius: 8px; padding: 0.42rem 0.65rem; color: var(--text-muted); font-size: 0.78rem; width: 15rem; flex: 0 0 auto;
    cursor: text;
}
.topbar-search svg { flex: 0 0 auto; }
.topbar-search input {
    border: none; background: none; outline: none; color: var(--text); font: inherit; width: 100%; padding: 0;
}
.topbar-search input::placeholder { color: var(--text-muted); }
.topbar-search kbd {
    flex: 0 0 auto; font-family: var(--font-mono); font-size: 0.66rem; background: var(--surface);
    border: 1px solid var(--border); border-radius: 4px; padding: 0.05rem 0.32rem; color: var(--text-muted);
}

.topbar-right { display: flex; align-items: center; gap: 0.4rem; margin-left: auto; flex: 0 0 auto; }
.icon-btn {
    display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
    width: 2.1rem; height: 2.1rem; border-radius: var(--radius-sm); font-size: 1.05rem;
    border: 1px solid transparent; background: none; color: var(--text-muted); cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, transform 0.4s ease;
}
.icon-btn svg { width: 16px; height: 16px; }
.icon-btn:hover { background: var(--surface-alt); color: var(--text); }
/* Kurzes Erfolgs-Feedback nach Klick auf "Zwischengespeicherte Version leeren" (siehe layout.php,
   .reload-trigger) - es gibt davon zwei Instanzen im Markup (Topbar rechts Desktop + Fuss der
   Handy-Schublade), deshalb ueber eine gemeinsame Klasse statt einer einzelnen ID verkabelt. */
.reload-done { opacity: 0.5; pointer-events: none; }
.icon-btn.reload-done { transform: rotate(180deg); }
.account-who { display: flex; align-items: center; gap: 0.5rem; flex: 1 1 auto; min-width: 0; padding: 0.3rem; }
.avatar {
    width: 1.9rem; height: 1.9rem; border-radius: 50%; background: var(--accent-bg); color: var(--primary);
    display: flex; align-items: center; justify-content: center; font-size: 0.72rem; font-weight: 700; flex: 0 0 auto;
    border: none; cursor: pointer; font-family: inherit;
}
.who { font-size: 0.8rem; overflow: hidden; text-align: left; }
.who .u { font-weight: 600; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who .r { color: var(--text-muted); font-size: 0.74rem; }
.logout-form { flex: 0 0 auto; }
.dropdown-panel .logout-form { width: 100%; margin-top: 0.3rem; }
.dropdown-panel .logout-form .btn { width: 100%; }
.dropdown-panel-divider { height: 1px; background: var(--border); margin: 0.35rem 0.2rem; }

.hamburger { display: none; }

/* ============ Hauptspalte ============ */
.main-col { flex: 1 1 auto; min-width: 0; max-width: 1760px; margin: 2rem auto; padding: 0 1.25rem 3rem; }
.main-col.full-width { max-width: none; margin: 1.25rem auto; }
.main-col.auth-main {
    max-width: 34rem; margin: 0 auto; min-height: 100vh;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 1rem; padding: 1.25rem;
}
.main-col.auth-main .card { margin-bottom: 0; }
.auth-brand {
    display: flex; flex-direction: column; align-items: center; gap: 0.6rem; margin-bottom: 0.4rem;
}
.auth-brand img { height: 3.2rem; width: auto; }
.auth-brand span { font-weight: 700; color: var(--text-muted); font-size: 0.85rem; letter-spacing: 0.01em; }

/* ============ Mobile Navigations-Schublade (Handy, siehe @media weiter unten) ============ */
/* Ersetzt sowohl die frühere Bottom-Tab-Leiste als auch deren Bottom-Sheets - EIN Panel, das von
   links hereinfährt und alle Ziele gesammelt zeigt (Geräte/Sicherheit/Admin-Unterseiten/Konto),
   statt pro Ziel eine eigene mobile Interaktion zu erfinden (siehe layout.php-Skript). */
.mobile-nav-scrim {
    position: fixed; inset: 0; background: rgba(10, 13, 18, 0.5); z-index: 49;
    opacity: 0; pointer-events: none; transition: opacity 0.18s ease; display: none;
}
.mobile-nav-scrim.open { opacity: 1; pointer-events: auto; }
.mobile-nav {
    position: fixed; top: 0; left: 0; height: 100%; width: 17rem; max-width: 84vw; z-index: 50;
    background: var(--surface); border-right: 1px solid var(--border); box-shadow: var(--shadow-lg);
    transform: translateX(-100%); transition: transform 0.2s ease; display: none;
    flex-direction: column; padding: 1rem; overflow-y: auto;
}
.mobile-nav.open { transform: translateX(0); }
.mobile-nav-brand { display: flex; align-items: center; gap: 0.6rem; padding: 0.3rem 0.4rem 1rem; font-weight: 700; }
.mobile-nav-brand img { height: 1.9rem; width: auto; border-radius: 4px; }
.mobile-nav-nav { display: flex; flex-direction: column; gap: 0.15rem; }
.mobile-nav-foot { margin-top: auto; border-top: 1px solid var(--border); padding-top: 0.7rem; display: flex; align-items: center; gap: 0.4rem; }

h1 { font-size: 1.35rem; margin: 0 0 1rem; letter-spacing: -0.01em; }
h2 { font-size: 1.05rem; margin: 0 0 1rem; letter-spacing: -0.01em; }

.card {
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 1.5rem; box-shadow: var(--shadow); margin-bottom: 1.5rem;
}
.card.viewer-card { padding: 1rem; }
.card.auth-card { width: 100%; box-shadow: var(--shadow-lg); }

.table-scroll { overflow-x: auto; margin: -0.25rem; padding: 0.25rem; }
table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
th, td { text-align: left; padding: 0.7rem 0.9rem; border-bottom: 1px solid var(--border); white-space: nowrap; }
th { color: var(--text-muted); font-weight: 600; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; background: var(--surface-alt); font-family: inherit; }
/* Zelleninhalte (Hostnamen, Versionen, Zeitstempel, Zahlen, ...) in der Konsolen-typischen
   Monospace statt der UI-Schrift - passend zum Werkzeug-Charakter, ohne dass jede Tabelle in
   den Templates dafür einzeln angepasst werden musste (siehe --font-mono oben). */
td { font-family: var(--font-mono); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
tbody tr { transition: background 0.12s ease; }
tbody tr:hover { background: var(--surface-alt); }
tbody tr:last-child td { border-bottom: none; }

/* Eigenständige Datentabellen (immer in .table-scroll gewrappt, z.B. Geräte-/Benutzerlisten)
   bekommen einen abgerundeten Rahmen wie eine eigene Karte statt frei im Card-Padding zu
   schweben - macht aus der sonst recht rohen HTML-Standardtabelle etwas, das zur restlichen
   abgerundeten Kachel-/Button-Optik passt. border-collapse:separate + overflow:hidden ist
   nötig, damit der Radius die Zellhintergründe (Header-Tönung, Hover, Statuszeilen) sauber
   mitclippt - mit border-collapse:collapse ignorieren die meisten Browser den Radius komplett.
   Die kompakte Datei-Tabelle im Viewer-Sidepanel (.file-table) ist bewusst NICHT in
   .table-scroll gewrappt und bleibt dadurch unverändert schlank. */
.table-scroll > table {
    border: 1px solid var(--border); border-radius: var(--radius);
    border-collapse: separate; border-spacing: 0; overflow: hidden;
}

.status-online, .status-offline {
    /* Muss auf einem <span> sitzen, nicht direkt auf dem <td> - display:inline-flex auf
       einem Table-Cell selbst hebelt dessen display:table-cell aus und bringt die Zelle
       gegenueber den anderen Zeilenzellen aus der vertikalen Mitte (siehe Templates, die
       diese Klasse immer auf ein <span> innerhalb des <td> setzen). */
    display: inline-flex; align-items: center; gap: 0.35rem; font-weight: 600; font-size: 0.85rem;
}
.status-online { color: var(--success); }
.status-offline { color: var(--text-muted); }
.status-online::before, .status-offline::before {
    content: ''; width: 0.45rem; height: 0.45rem; border-radius: 999px; background: currentColor;
}

/* Neutral/gedämpft statt Warnfarbe - "nicht zugeordnet" ist ein Hinweis, kein Problem, das
   Handeln erfordert (das bleibt der Update-verfügbar-Markierung/warning-Farbe vorbehalten). */
.badge-unclaimed {
    display: inline-block; margin-left: 0.5rem; padding: 0.15rem 0.5rem; border-radius: 999px;
    background: var(--surface-alt); color: var(--text-muted); border: 1px dashed var(--border);
    font-size: 0.72rem; font-weight: 700;
}

/* Kleine farbige Kennzeichnung (Rolle/Status) - fuer Werte, die keine An/Aus-Auswahl mehr sind
   (siehe .status-online/-offline dafuer), sondern einen von mehreren festen Zuständen zeigen
   (Superadmin-Kennzeichnung in admin/users.php, Aufzeichnungsstatus in admin/recordings.php). */
.chip {
    display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.15rem 0.55rem; border-radius: 5px;
    font-size: 0.72rem; font-weight: 700; font-family: var(--font-mono); white-space: nowrap;
}
.chip.chip-accent { background: var(--accent-bg); color: var(--primary); }
.chip.chip-success { background: var(--success-bg); color: var(--success); }
.chip.chip-warning { background: var(--warning-bg); color: var(--warning-text); }
.chip.chip-danger { background: var(--danger-bg); color: var(--danger); }
.chip.chip-muted { background: var(--surface-alt); color: var(--text-muted); }

.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
    background: var(--primary); color: var(--primary-contrast); border: 1px solid transparent;
    padding: 0.5rem 1rem; border-radius: var(--radius-sm); cursor: pointer; text-decoration: none;
    font-size: 0.88rem; font-weight: 600; transition: background 0.15s ease, transform 0.05s ease;
}
.btn:hover { background: var(--primary-hover); }
.btn:active { transform: translateY(1px); }
.btn.secondary { background: transparent; color: var(--text); border-color: var(--border); }
.btn.secondary:hover { background: var(--surface-alt); }
.btn.secondary.active { background: var(--primary); color: var(--primary-contrast); border-color: var(--primary); }
.btn.secondary.active:hover { background: var(--primary-hover); }
.btn.danger { background: transparent; color: var(--danger); border-color: var(--border); }
.btn.danger:hover { background: var(--danger-bg); border-color: var(--danger); }

/* Eckiger statt runder Icon-Button - Werkzeugleisten-Optik (wie ein Instrumenten-Knopf) statt
   Avatar-Bubble, urspruenglich fuer die Kachelansicht der Geraeteliste gedacht (siehe
   dashboard.php), inzwischen auch von deren Listenansicht und der Admin-Geraeteliste
   (admin/devices.php) genutzt - deshalb hier zentral statt an mehreren Stellen dupliziert. */
.tile-icon-btn {
    display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
    width: 2rem; height: 2rem; border-radius: 7px; font-size: 1rem;
    border: 1px solid var(--border); background: transparent; color: var(--text);
    cursor: pointer; text-decoration: none; transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.tile-icon-btn:hover { border-color: var(--primary); color: var(--primary); background: var(--accent-bg); }
.tile-icon-btn.primary { background: var(--primary); border-color: var(--primary); color: var(--primary-contrast); }
.tile-icon-btn.primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
.tile-icon-btn.danger { color: var(--danger); }
.tile-icon-btn.danger:hover { background: var(--danger-bg); border-color: var(--danger); color: var(--danger); }

/* Plattform-Kürzel (WIN/LNX/AND) statt Icon-Avatar für Geräte-Zeilen (dashboard.php Listenansicht +
   admin/devices.php) - eine kleine Konsolen-Plakette wie ein Geräte-/Rack-Label statt eines runden
   Avatars (das war die Optik des vorigen Themes für Personen wie Geräte gleichermaßen). Das
   waehlbare Symbol (App\DeviceIcons) bleibt ausschließlich der Kachelansicht vorbehalten (siehe
   .device-tile-icon in dashboard.php) - hier zeigt die Zeile stattdessen die Plattform selbst, die
   in einer dichten Verwaltungsliste die relevantere Information ist. Kein eigener Statuspunkt mehr
   nötig, der Status-Streifen am linken Zeilenrand (siehe .device-row[data-status] unten) übernimmt
   dieses Signal bereits. */
.device-row-plat {
    flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
    min-width: 2.4rem; height: 1.6rem; padding: 0 0.4rem; border-radius: 6px;
    background: var(--surface-alt); border: 1px solid var(--border); color: var(--text-muted);
    font-family: var(--font-mono); font-size: 0.66rem; font-weight: 700; letter-spacing: 0.02em;
}
/* Status-Streifen am linken Rand jeder Geräte-Zeile (Dashboard + Admin-Geräteliste, beide setzen
   dasselbe data-status-Attribut) - ergänzt den kleinen Punkt am Icon um ein zweites Signal, das
   sich auch beim schnellen Überfliegen einer langen Liste noch abzeichnet. Rein additiv (nur
   border-left), das Zeilen-Padding/-Layout bleibt vollständig den seiten-eigenen <style>-Blöcken
   (dashboard.php/admin/devices.php) überlassen. */
.device-row { border-left: 3px solid transparent; }
.device-row[data-status="online"] { border-left-color: var(--success); }
.device-row[data-status="offline"] { border-left-color: var(--border); }

/* Ordner-/Mandanten-Aufklapper (details/summary) mit eigenem Pfeil-/Ordner-Icon statt des nackten
   Browser-Standarddreiecks, plus optionalem Geräte-Anzahl-Badge - von dashboard.php (#devices-card)
   und admin/devices.php (#admin-devices-card) genutzt, deshalb hier zentral. */
.folder-summary {
    display: flex; align-items: center; gap: 0.5rem; padding: 0.6rem 0.75rem;
    border-radius: var(--radius-sm); cursor: pointer; list-style: none;
    transition: background 0.15s ease;
}
.folder-summary::-webkit-details-marker { display: none; }
.folder-summary:hover { background: var(--surface-alt); }
.folder-chevron { display: flex; flex: 0 0 auto; color: var(--text-muted); transition: transform 0.15s ease; }
details[open] > .folder-summary .folder-chevron { transform: rotate(90deg); }
.folder-icon { display: flex; flex: 0 0 auto; color: var(--primary); font-size: 0.95rem; }
.folder-name { font-weight: 700; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.folder-count {
    background: var(--surface-alt); color: var(--text-muted); font-size: 0.72rem; font-weight: 700;
    padding: 0.1rem 0.5rem; border-radius: 999px; font-family: var(--font-mono);
}
.folder-body { padding: 0.4rem 0 0.4rem 0.9rem; border-left: 2px solid var(--border); margin-left: 0.65rem; }

input[type=text], input[type=password], input[type=file], select, textarea {
    padding: 0.55rem 0.7rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
    width: 100%; background: var(--surface); color: var(--text); font-size: 0.9rem;
    font-family: inherit; transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
input[type=text]:focus, input[type=password]:focus, select:focus, textarea:focus {
    outline: none; border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
}
/* Native Mehrfachauswahl-Listbox (z.B. Besitzer-Zuweisung in admin/devices.php) übernimmt sonst
   die System-Blau-Markierung für ausgewählte Einträge statt der App-Akzentfarbe. */
select[multiple] option:checked { background: var(--accent-bg); color: var(--primary); }
form.inline { display: inline-block; }
.error {
    color: var(--danger); background: var(--danger-bg); border-radius: var(--radius-sm);
    padding: 0.6rem 0.8rem; margin-bottom: 1rem; font-size: 0.9rem;
}
.success {
    color: var(--success); background: var(--success-bg); border-radius: var(--radius-sm);
    padding: 0.6rem 0.8rem; margin-bottom: 1rem; font-size: 0.9rem;
}
label { display: block; margin: 0.6rem 0 0.3rem; font-size: 0.85rem; font-weight: 500; color: var(--text-muted); }
code {
    background: var(--surface-alt); padding: 0.15rem 0.45rem; border-radius: 4px;
    font-size: 0.85em; font-family: var(--font-mono);
}
.hint { color: var(--text-muted); font-size: 0.85rem; margin-top: 0.75rem; }

/* Kopfzeile einer Karte (Überschrift + Aktions-Buttons rechts) - von mehreren Admin-/
   Dashboard-Karten genutzt (bisher an mehreren Stellen als identisches lokales <style>
   dupliziert, u.a. hat admin/recordings.php dadurch bislang GAR KEINE Regel dafür bekommen).
   Zentral hier statt dupliziert, damit auch der Mobile-Umbruch (flex-wrap) überall greift. */
.card-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.card-header h1, .card-header h2 { margin: 0; }

/* Zeitraum-/Geräte-Filterleiste (Statistik/Aufzeichnungen) - zentral statt pro Seite dupliziert,
   siehe .card-header-Kommentar oben für denselben Grund. */
.stats-filter {
    display: flex; align-items: flex-end; gap: 1rem; flex-wrap: wrap;
    padding-bottom: 1.25rem; margin-bottom: 1.25rem; border-bottom: 1px solid var(--border);
}
.stats-filter label { margin: 0 0 0.3rem; }
.stats-filter input[type=text] { width: 9rem; }
.stats-filter input[type=datetime-local] { width: 12.5rem; }
.stats-filter select { width: 14rem; }
.stats-filter-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-left: auto; }
/* Bordiertes Segment-Band wie die Kennzahlen-Leiste im Dashboard (siehe .stats/.stat) statt
   separater getönter Kacheln - dieselbe Optik für "Kennzahlen auf einen Blick" überall im Admin-
   Bereich (Statistik/Aufzeichnungen), nicht zwei verschiedene Bausteine für dasselbe Konzept.
   flex-wrap statt festem Grid, da die Anzahl der Kacheln je Seite variiert (Statistik: 5, künftig
   evtl. mehr/weniger) - jedes Segment bekommt dieselbe Mindestbreite wie zuvor die Grid-Spalte. */
.stats-tiles { display: flex; flex-wrap: wrap; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; margin-bottom: 1.5rem; background: var(--surface); }
.stats-tile { flex: 1 1 9.5rem; padding: 0.7rem 1rem; border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.stats-tile:last-child { border-right: none; }
.stats-tile-label { font-size: 0.7rem; color: var(--text-muted); margin-bottom: 0.35rem; text-transform: uppercase; letter-spacing: 0.05em; }
.stats-tile-value { font-size: 1.35rem; font-weight: 700; line-height: 1; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.stats-tile-value.stats-tile-text { font-size: 0.95rem; }

/* Kennzahlen-Leiste der Geräteübersicht (siehe dashboard.php) - zugleich Filter: Klick auf eine
   Kachel blendet die Geräteliste auf genau diesen Status. Eigenständige Kacheln mit Zwischenraum
   statt eines gemeinsam umrandeten Segment-Bands (frühere Version) - dort erzeugte eine
   angeklickte Kachel unschöne Ecken/Nahtstellen mit ihren Nachbarn und dem äußeren Rahmen.
   Aktiver Zustand: farbiger linker Rand + dezent getönter Hintergrund statt Umrandung. */
.device-stat-filters { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-bottom: 1.3rem; }
.device-stat-filter {
    flex: 1 1 9rem; display: flex; flex-direction: column; gap: 0.3rem; text-align: left;
    padding: 0.75rem 1rem; border-radius: var(--radius); border: 1px solid var(--border);
    border-left: 3px solid transparent; background: var(--surface); font: inherit; color: inherit;
    cursor: pointer; transition: border-color 0.15s ease, background 0.15s ease, transform 0.1s ease;
}
.device-stat-filter:hover { border-color: var(--primary); transform: translateY(-1px); }
.device-stat-filter-n { font-size: 1.35rem; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; font-family: var(--font-mono); }
.device-stat-filter-l { font-size: 0.7rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; }
.device-stat-filter.online .device-stat-filter-n { color: var(--success); }
.device-stat-filter.warn .device-stat-filter-n { color: var(--warning-text); }
.device-stat-filter.active { background: var(--accent-bg); border-left-color: var(--primary); }
.device-stat-filter.active.online { border-left-color: var(--success); }
.device-stat-filter.active.warn { border-left-color: var(--warning-text); }

details > summary { cursor: pointer; }
details.inline-details { display: inline-block; vertical-align: middle; }

/* Aktionsleiste in Tabellenzeilen (z.B. admin/users.php) - umbrechende Flex-Reihe statt der
   harten table-td white-space:nowrap-Zeile, die vorher Buttons abgeschnitten hat. */
.row-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; white-space: normal; }

/* Log-Stream (admin/audit-log.php) - ein fortlaufendes Ereignisprotokoll liest sich als Zeilen
   statt als breite Tabelle mit vielen duennen Spalten natuerlicher (naeher an einem echten
   Server-/Konsolen-Log) und bleibt dabei auf schmalen Bildschirmen lesbar, weil jede Zeile frei
   umbrechen darf statt Spalten zu quetschen. */
.logstream { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: var(--surface); }
.logline {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 0.7rem;
    padding: 0.55rem 0.9rem; font-family: var(--font-mono); font-size: 0.8rem;
    border-bottom: 1px solid var(--border);
}
.logline:last-child { border-bottom: none; }
.logline:hover { background: var(--surface-alt); }
.logline .ts { color: var(--text-muted); flex: 0 0 auto; font-variant-numeric: tabular-nums; }
.logline .tag {
    flex: 0 0 auto; font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
    color: var(--text-muted); background: var(--surface-alt); border-radius: 4px; padding: 0.05rem 0.35rem;
}
.logline .actor { color: var(--primary); flex: 0 0 auto; font-weight: 700; }
.logline .action { color: var(--text); flex: 1 1 240px; }
.logline .action .tgt { color: var(--text-muted); }
.logline .ip { color: var(--text-muted); flex: 0 0 auto; margin-left: auto; }

/* Changelog (admin/changelog.php) - je Datum eine Gruppe mit Überschrift + Liste, analog zum
   Log-Stream oben aber mit echten Absaetzen statt Konsolen-Zeilen, da Changelog-Texte laenger
   sind als ein einzeiliger Audit-Log-Eintrag. */
.changelog-group + .changelog-group { margin-top: 1.5rem; }
.changelog-date {
    font-size: 0.95rem; font-weight: 700; color: var(--text); margin: 0 0 0.6rem;
    padding-bottom: 0.35rem; border-bottom: 1px solid var(--border);
}
.changelog-entries { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.changelog-entries li { display: flex; align-items: baseline; gap: 0.6rem; }
.changelog-entries li > span:last-child { flex: 1 1 auto; color: var(--text); line-height: 1.5; }
.changelog-entries .chip { flex: 0 0 auto; }

/* Passwort-Reset-Dialog (admin/users.php): natives <dialog> statt Popover, weil ein per CSS
   positioniertes Popover innerhalb der Tabellenzelle vom overflow:hidden/auto der
   .table-scroll/.table-scroll > table-Regeln (Rundungs-Clipping) abgeschnitten wurde - <dialog>
   rendert im Top-Layer und entkommt diesem Clipping. */
.pw-reset-dialog {
    border: 1px solid var(--border); border-radius: var(--radius); padding: 1.1rem;
    background: var(--surface); color: var(--text); box-shadow: var(--shadow-lg); min-width: 18rem;
}
.pw-reset-dialog::backdrop { background: rgba(0, 0, 0, 0.5); }
.pw-reset-dialog h3 { margin: 0 0 0.9rem; font-size: 1rem; }
.pw-reset-dialog .dialog-actions { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 0.9rem; }

.viewer-header { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1rem; flex-wrap: wrap; position: relative; }
.viewer-header h1 { flex: 1 1 auto; margin: 0; }

/* Einzige schwebende Steuerleiste ueber dem Bildschirm-Canvas (siehe connect.php $vIcons/#viewer-
   toolbar-float) - vereint alle Bedienelemente (Tastatur/Monitor-/Aufloesungs-/Zoom-Auswahl/Codec/
   Bitrate/Audio/Privatsphaere/Aufnahme/Cursor/Steuerung/Praesentation/Vollbild) in einer einzigen,
   frei verschiebbaren/einklappbaren Leiste (Nutzerwunsch - fruehere zweite Leiste unten rechts
   entfernt). Icon-/Dropdown-Buttons brauchen so wenig Platz, dass alle Umschalter IMMER direkt
   sichtbar bleiben koennen, auf jeder Bildschirmbreite. Absolut positioniert relativ zu
   #canvas-wrap (position:relative, siehe .viewer-canvas-wrap), oben mittig als Default-Position
   (viewer.js setupToolbarDrag). */
.viewer-toolbar-float {
    position: absolute; top: 0.6rem; left: 50%; transform: translateX(-50%); z-index: 4;
    max-width: calc(100% - 1.2rem);
    display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
}
/* Sobald verschoben (oder eine gespeicherte Position wiederhergestellt wird) faellt die
   Zentrierungs-Transform weg - viewer.js setzt danach explizite left/top-Pixelwerte relativ zu
   #canvas-wrap, ein aktiver Transform wuerde diese sonst zusaetzlich verschieben. */
.viewer-toolbar-float.floating { left: auto; top: auto; transform: none; }
.viewer-toolbar-handle {
    display: flex; align-items: center; justify-content: center; gap: 0.5rem;
    background: rgba(17, 24, 39, 0.85); color: rgba(255, 255, 255, 0.85);
    border: 1px solid rgba(255, 255, 255, 0.3); border-radius: var(--radius-sm);
    padding: 0.1rem 0.2rem 0.1rem 0.5rem; cursor: move; touch-action: none;
    font-size: 0.8rem; user-select: none;
}
.viewer-toolbar-grip { opacity: 0.8; }
.viewer-toolbar-collapse {
    background: none; border: none; color: inherit; cursor: pointer; font-size: 1rem;
    line-height: 1.4; padding: 0 0.35rem;
}
.viewer-toolbar-collapse:hover { color: #fff; }
.viewer-toolbar-float.collapsed .viewer-toolbar-body { display: none; }
.viewer-toolbar-body {
    max-width: calc(100vw - 1.2rem);
    display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0.3rem;
    background: rgba(10, 13, 18, 0.85); backdrop-filter: blur(6px); border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 10px; padding: 0.3rem;
}
.vtool-btn {
    display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
    width: 2.1rem; height: 2.1rem; border-radius: 7px; font-size: 1.05rem;
    border: 1px solid transparent; background: transparent; color: rgba(255, 255, 255, 0.75); cursor: pointer;
    transition: background 0.15s, color 0.15s;
}
.vtool-btn svg { width: 17px; height: 17px; }
.vtool-btn:hover { background: rgba(255, 255, 255, 0.1); color: #fff; }
.vtool-btn.active { background: var(--primary); color: var(--primary-contrast); }
/* Aufnahme-Button: Punkt-Icon faerbt sich beim Aufzeichnen rot statt der ueblichen Akzentfarbe -
   eine aktive Aufnahme soll auch farblich unmissverstaendlich als "Achtung" statt als normaler
   "Funktion an"-Zustand herausstechen. */
.vtool-btn-record.active { background: var(--danger); color: #fff; }
.vtool-btn-record.active svg circle { fill: currentColor; }

.codec-switch { display: inline-flex; vertical-align: middle; border-radius: 999px; overflow: hidden; border: 1px solid rgba(255, 255, 255, 0.12); font-size: 0.62rem; margin: 0 0.15rem; }
.codec-switch .codec-btn { border: none; background: transparent; color: rgba(255, 255, 255, 0.6); font: inherit; font-weight: 700; letter-spacing: 0.02em; padding: 0.3rem 0.55rem; cursor: pointer; }
.codec-switch .codec-btn.active { background: var(--primary); color: var(--primary-contrast); cursor: default; }
.codec-switch .codec-btn:hover:not(.active) { background: rgba(255, 255, 255, 0.1); color: #fff; }
.connection-timer {
    display: inline-flex; vertical-align: middle; margin-left: 0.5rem; font-size: 0.8rem;
    font-variant-numeric: tabular-nums; color: var(--text-muted); font-family: var(--font-mono);
}
.disk-usage-bar {
    display: inline-block; width: 100%; max-width: 12rem; height: 8px; border-radius: 4px;
    background: var(--surface-alt); overflow: hidden; vertical-align: middle;
}
.disk-usage-bar-filled { height: 100%; background: var(--primary); }
.disk-usage-bar-filled.danger { background: var(--danger); }
.record-indicator {
    display: inline-flex; vertical-align: middle; margin-left: 0.5rem; font-size: 0.75rem;
    font-weight: 700; letter-spacing: 0.02em; color: var(--danger); animation: record-indicator-pulse 1.6s ease-in-out infinite;
}
@keyframes record-indicator-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
.viewer-status { font-size: 0.85rem; padding: 0.25rem 0.7rem; border-radius: 999px; background: var(--surface-alt); color: var(--text-muted); font-weight: 600; }
.viewer-status.online { background: var(--success-bg); color: var(--success); }
.viewer-status.offline { background: var(--danger-bg); color: var(--danger); }
.viewer-status.connecting { background: var(--warning-bg); color: var(--warning-text); }
.viewer-body { display: flex; gap: 1rem; align-items: flex-start; flex-wrap: wrap; }
.viewer-canvas-wrap {
    background: #111827; border-radius: var(--radius); display: flex; justify-content: center;
    align-items: center; position: relative; overflow: hidden; flex: 3 1 480px; min-width: 0;
    height: calc(100vh - 220px); min-height: 320px;
}
.viewer-canvas-wrap:fullscreen { height: 100vh; width: 100vw; border-radius: 0; }
#screen { max-width: 100%; max-height: 100%; width: auto; height: auto; display: block; outline: none; cursor: default; touch-action: none; }
/* Touch-Trackpad-Modus (siehe viewer.js): zeigt die virtuelle Mausposition an, da Fingertipps
   anders als ein echter Mauszeiger keine sichtbare Position auf dem Bildschirm haben. */
.touch-cursor {
    position: absolute; top: 0; left: 0; width: 22px; height: 30px;
    background: #ffffff;
    clip-path: polygon(0% 0%, 0% 70%, 25% 55%, 40% 92%, 56% 85%, 42% 50%, 72% 50%);
    filter: drop-shadow(0 0 1.5px #000000) drop-shadow(0 0 1.5px #000000);
    pointer-events: none; opacity: 0; z-index: 5;
    transition: opacity 0.2s ease, transform 0.08s ease;
}
.touch-cursor.visible { opacity: 0.95; }
.touch-cursor.tap { transform: scale(1.35); }
/* Container aus Hinweistext + Eingabefeld, das per Fokus die mobile Bildschirmtastatur oeffnet
   (siehe viewer.js). Ist per default display:none - viewer.js schaltet IMMER zuerst auf
   display:block (Klasse "visible"), bevor focus() auf dem Eingabefeld aufgerufen wird, da manche
   Mobilbrowser bei focus() auf ein noch display:none-Element gar keine Tastatur oeffnen. Schmaler
   als frueher (nicht mehr die volle Bildbreite) und mittig platziert, da ein volles Eingabefeld
   quer ueber das Bild zu viel vom Zielgeraet verdeckte. */
.keyboard-input-wrap {
    position: absolute; left: 50%; bottom: 0.75rem; z-index: 6;
    transform: translateX(-50%); width: min(80%, 22rem);
    display: none;
}
.keyboard-input-wrap.visible { display: block; }
.keyboard-input-hint {
    margin: 0 0 0.35rem; padding: 0.2rem 0.6rem; text-align: center;
    font-size: 0.8rem; color: #ffffff;
    background: var(--primary); border-radius: var(--radius-sm);
}
/* font-size mind. 16px verhindert iOS' Auto-Zoom-Effekt beim Fokussieren eines Eingabefelds -
   hier bewusst groesser (nicht nur das Minimum), damit Getipptes auch auf dem eigenen Bildschirm
   gut lesbar ist. */
.keyboard-input {
    box-sizing: border-box; width: 100%;
    padding: 0.85rem 0.9rem; border-radius: var(--radius-sm); border: 1px solid var(--border);
    font-size: 1.15rem; background: var(--surface); color: var(--text);
}
/* Monitor-/Aufloesungs-/Bitraten-Dropdowns sitzen seit dem Zusammenlegen zu einer einzigen
   Steuerleiste (Nutzerwunsch) direkt in .viewer-toolbar-body (siehe connect.php/app.css weiter
   oben) - hier nur noch das gemeinsame Aussehen der <select>-Elemente. */
.monitor-select {
    width: auto; background: rgba(17, 24, 39, 0.85); color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.3); border-radius: var(--radius-sm);
    padding: 0.35rem 0.5rem; font-size: 0.85rem;
}
/* Monitor-Auswahl als kleine Bildschirm-Rechtecke statt Dropdown (siehe connect.php #monitor-
   select/viewer.js handleMonitorList) - ein Klick auf das jeweilige Rechteck wechselt direkt,
   das aktive Rechteck ist per Akzentfarbe/Rahmen hervorgehoben statt eines Dropdown-Haekchens. */
.monitor-rect-group { display: flex; gap: 0.3rem; }
.monitor-rect-btn {
    width: 1.9rem; height: 1.2rem; padding: 0; cursor: pointer;
    background: rgba(17, 24, 39, 0.85); border: 1.5px solid rgba(255, 255, 255, 0.35); border-radius: 3px;
    transition: border-color 0.15s, background 0.15s;
}
.monitor-rect-btn:hover { border-color: rgba(255, 255, 255, 0.7); }
.monitor-rect-btn.active { border-color: var(--primary); background: color-mix(in srgb, var(--primary) 35%, rgba(17, 24, 39, 0.85)); }

/* Live-Übertragungsrate (siehe viewer.js trackFrameStats) - sitzt im Header neben dem
   Verbindungstimer statt als schwebender Chip über dem Bild (frühere Variante kostete dort
   dauerhaft reservierten Platz vom Video, auch im Vollbild - Nutzerwunsch, hierher verschoben). */
.viewer-hud {
    display: inline-flex; vertical-align: middle; margin-left: 0.5rem;
    font-family: var(--font-mono); font-size: 0.8rem; color: var(--text-muted);
    font-variant-numeric: tabular-nums; white-space: nowrap;
}
.viewer-hud b { color: var(--text); font-weight: 700; }
.viewer-hud b.lan-cert-link { cursor: pointer; text-decoration: underline dotted; text-underline-offset: 0.15em; }
.viewer-hud b.lan-cert-link:hover { color: var(--primary); }
.lan-direct-hint {
    display: inline-flex; align-items: center; gap: 0.4rem; margin-left: 0.5rem;
    font-size: 0.8rem; color: var(--text-muted); white-space: nowrap;
}
.lan-direct-hint a { color: var(--primary); }
.zoom-toolbar { display: flex; gap: 0.25rem; }
.zoom-toolbar .btn {
    background: rgba(17, 24, 39, 0.85); color: #fff; border: 1px solid rgba(255, 255, 255, 0.3);
    padding: 0.3rem 0.55rem; font-size: 0.85rem; min-width: 2.4rem;
}
.zoom-toolbar .btn.active { background: var(--primary); border-color: var(--primary); }
.viewer-hint { color: var(--text-muted); font-size: 0.85rem; margin-top: 0.75rem; }

/* Reiner Dateitransfer-Modus (?mode=files, siehe connect.php) - kein Bildschirm-Stream,
   das Dateien-Panel nimmt die volle Breite ein, die übrigen Tabs sind serverseitig gar
   nicht erst gerendert (siehe connect.php). */
.viewer-card.files-only #fullscreen-btn,
.viewer-card.files-only .viewer-canvas-wrap { display: none; }
.viewer-card.files-only .viewer-sidepanel { flex: 1 1 100%; height: auto; min-height: 420px; }

/* Shell-Sitzung (?mode=shell, nur Linux, siehe connect.php/viewer.js Phase O) - kein
   Bildschirm-Stream, #terminal-wrap (xterm.js) nimmt stattdessen den Platz von #screen im
   canvas-wrap ein. Anders als files-only bleibt .viewer-canvas-wrap sichtbar (gleiche Box/
   Fullscreen-Verhalten wie bei einer Bildschirm-Sitzung), nur der Inhalt wechselt - die
   video-spezifische schwebende Werkzeugleiste/Overlay-Steuerung ergibt für ein reines Terminal
   keinen Sinn und wird ausgeblendet (#viewer-hud sitzt seit dem Umzug in den Header ohnehin nicht
   mehr im Markup für Shell-Sitzungen, siehe connect.php). */
.terminal-wrap { display: none; width: 100%; height: 100%; }
.viewer-card.shell-only #screen,
.viewer-card.shell-only .viewer-toolbar-float,
.viewer-card.shell-only #keyboard-input-wrap,
.viewer-card.shell-only .touch-cursor { display: none; }
.viewer-card.shell-only .terminal-wrap { display: block; }
.viewer-card.shell-only .viewer-canvas-wrap { background: #1a1a1a; padding: 0.5rem; box-sizing: border-box; }
#terminal { width: 100%; height: 100%; }

/* Sofort-Support-Sitzung, bei der die hilfesuchende Person im Verbindungsdialog nur den
   Bildschirm freigegeben hat (siehe 0x0C SessionMode, docs/binary-protocol.md) - Dateien/
   Zwischenablage/Tastaturbefehle sind serverseitig ohnehin wirkungslos (CaptureHelper
   bekommt diese Frames gar nicht erst), die Bedienelemente dafuer werden deshalb ausgeblendet
   statt eine Illusion von Funktionalitaet zu zeigen. Klasse wird von viewer.js gesetzt. */
.viewer-card.view-only #keyboard-btn,
.viewer-card.view-only #control-toggle-btn,
.viewer-card.view-only .viewer-sidepanel { display: none; }
.viewer-card.view-only .viewer-canvas-wrap { flex: 1 1 100%; }
.view-only-badge { background: var(--warning-bg); color: var(--warning-text); }

.viewer-sidepanel {
    flex: 1 1 320px; display: flex; flex-direction: column;
    height: calc(100vh - 220px); min-height: 320px;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden;
}
.sidepanel-tabs { display: flex; border-bottom: 1px solid var(--border); flex: 0 0 auto; }
.sidepanel-tab {
    flex: 1 1 auto; background: none; border: none; padding: 0.65rem; cursor: pointer;
    font-size: 0.85rem; font-weight: 500; color: var(--text-muted); border-bottom: 2px solid transparent;
    transition: color 0.15s ease, border-color 0.15s ease;
}
.sidepanel-tab:hover { color: var(--text); }
.sidepanel-tab.active { color: var(--primary); border-bottom-color: var(--primary); font-weight: 600; }
.sidepanel-content {
    flex: 1 1 auto; overflow-y: auto; padding: 0.85rem; display: flex; flex-direction: column; gap: 0.6rem;
}
.file-toolbar { display: flex; align-items: center; gap: 0.5rem; }
.file-toolbar .btn { padding: 0.35rem 0.6rem; font-size: 0.85rem; }
.file-path {
    flex: 1 1 auto; font-size: 0.8rem; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap;
}
.file-upload-label { cursor: pointer; }
.file-bandwidth-box { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; color: var(--text-muted); }
.file-bandwidth-box select { font-size: 0.8rem; padding: 0.2rem 0.4rem; }
.file-filter-box input[type=text] { width: 100%; }
.file-bulk-toolbar {
    display: flex; align-items: center; gap: 0.5rem; font-size: 0.8rem; color: var(--text-muted);
}
.file-bulk-toolbar .btn { padding: 0.3rem 0.55rem; font-size: 0.8rem; }
.file-bulk-count { flex: 1 1 auto; }
.file-error { color: var(--danger); font-size: 0.85rem; }
.file-resume-banner {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.6rem; font-size: 0.85rem;
    background: var(--surface-alt); border: 1px solid var(--border); border-radius: var(--radius-sm);
    padding: 0.5rem 0.7rem;
}
.file-resume-banner #file-resume-label { flex: 1 1 auto; }
.file-resume-banner .btn { padding: 0.25rem 0.6rem; font-size: 0.8rem; }
.file-transfer-progress { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.5rem; font-size: 0.8rem; }
.file-transfer-progress #file-transfer-label {
    flex: 1 1 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.file-transfer-progress progress { flex: 1 1 auto; accent-color: var(--primary); }
.file-transfer-percent { flex: 0 0 auto; font-variant-numeric: tabular-nums; min-width: 3ch; text-align: right; }
/* Eigene Zeile statt "flex: 0 0 auto" auf der Balken-Zeile: die Restzeit-Schätzung schwankt
   stark in der Textlänge, wodurch sich sonst bei jedem Update die für <progress> übrige Breite
   (flex: 1 1 auto) und damit die sichtbare Balkenlänge mitverändert hätte. */
.file-transfer-speed { flex: 1 1 100%; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.file-transfer-progress .btn { padding: 0.25rem 0.5rem; font-size: 0.8rem; }
.file-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.file-table th { padding: 0.2rem 0.3rem; border-bottom: 1px solid var(--border); }
.file-table td { padding: 0.4rem 0.3rem; border-bottom: 1px solid var(--border); white-space: normal; }
.file-table tr.file-row { cursor: pointer; }
.file-table tr.file-row:hover { background: var(--surface-alt); }
.file-table td.file-size, .file-table td.file-mtime, .file-table th.file-size, .file-table th.file-mtime { color: var(--text-muted); white-space: nowrap; text-align: right; }
.file-table td.file-actions, .file-table th.file-actions { white-space: nowrap; text-align: right; width: 1%; }
.file-table td.file-checkbox-col, .file-table th.file-checkbox-col { width: 1%; white-space: nowrap; }
.file-table td.file-checkbox-col input, .file-table th.file-checkbox-col input { cursor: pointer; }
.file-table th.file-sortable {
    cursor: pointer; user-select: none; color: var(--text-muted); font-weight: 600;
    text-transform: uppercase; font-size: 0.72rem; letter-spacing: 0.03em;
}
.file-table th.file-sortable:hover { color: var(--primary); }
.file-sort-arrow { display: inline-block; width: 0.9em; opacity: 0.6; }
.file-table th.file-sortable[data-sort-active] { color: var(--primary); }
.file-table th.file-sortable[data-sort-active] .file-sort-arrow { opacity: 1; }
.file-action-btn {
    background: none; border: none; cursor: pointer; padding: 0.15rem 0.35rem; font-size: 0.9rem;
    color: var(--text-muted); border-radius: var(--radius-sm);
}
.file-action-btn:hover { color: var(--text); background: var(--surface-alt); }

/* Datei-Editor-Overlay (Stift-Symbol im Dateibrowser, siehe viewer.js openFileEditor) - gleiches
   position:fixed-Overlay-Muster wie #device-info-overlay in dashboard.php. Bewusst kein
   Backdrop-Klick/Escape zum Schließen (anders als dort) - hier drohen unsichtbare
   Datenverluste, das Schließen soll nur über die drei expliziten Buttons gehen. */
.file-editor-overlay {
    position: fixed; inset: 0; z-index: 60; background: rgba(16, 22, 21, 0.55);
    display: flex; align-items: center; justify-content: center; padding: 1.25rem;
}
.file-editor-modal {
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
    box-shadow: var(--shadow-lg); width: 56rem; min-width: 56rem; max-width: 96vw; height: 85vh;
    display: flex; flex-direction: column; overflow: hidden;
}
.file-editor-header {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    padding: 0.85rem 1.1rem; border-bottom: 1px solid var(--border); flex: 0 0 auto;
}
.file-editor-header h2 { margin: 0; font-size: 1rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.file-editor-encoding-label {
    display: flex; align-items: center; gap: 0.4rem; font-size: 0.78rem; color: var(--text-muted);
    white-space: nowrap; flex: 0 0 auto;
}
.file-editor-encoding-label select { font-size: 0.78rem; padding: 0.15rem 0.3rem; }
.file-editor-body { flex: 1 1 auto; min-height: 0; padding: 0.85rem 1.1rem; display: flex; flex-direction: column; gap: 0.5rem; }
.file-editor-textarea {
    flex: 1 1 auto; width: 100%; min-height: 0; resize: none; font-family: var(--font-mono);
    font-size: 0.85rem; line-height: 1.4; white-space: pre;
}
.file-editor-actions {
    display: flex; align-items: center; gap: 0.5rem; justify-content: flex-end;
    padding: 0.85rem 1.1rem; border-top: 1px solid var(--border); flex: 0 0 auto;
}
.file-editor-status { flex: 1 1 auto; font-size: 0.8rem; color: var(--text-muted); }
@media (max-width: 640px) {
    .file-editor-overlay { padding: 0; align-items: flex-end; }
    .file-editor-modal {
        width: 100%; min-width: 0; max-width: 100%; height: 92vh;
        border-radius: var(--radius) var(--radius) 0 0;
    }
}

/* Zertifikat-Popup der LAN-Direktverbindung (Klick auf "LAN" im HUD, siehe viewer.js
   showCertInfoPopup) - gleiches Overlay/Modal-Muster wie der Datei-Editor oben, aber deutlich
   kleiner und ohne feste Höhe, da der Inhalt nur eine kurze Definitionsliste ist. */
.cert-info-modal { width: 30rem; min-width: 0; max-width: 92vw; height: auto; max-height: 85vh; }
.cert-info-body { font-size: 0.88rem; }
.cert-info-grid { display: grid; grid-template-columns: auto 1fr; gap: 0.4rem 0.9rem; margin: 0; }
.cert-info-grid dt { color: var(--text-muted); }
.cert-info-grid dd { margin: 0; overflow-wrap: anywhere; }
.cert-info-badge {
    display: inline-block; padding: 0.15rem 0.5rem; border-radius: var(--radius-sm);
    font-size: 0.78rem; font-weight: 600;
}
.cert-info-badge.ca-issued { background: var(--success-bg); color: var(--success); }
.cert-info-badge.self-signed { background: var(--warning-bg); color: var(--warning-text); }
@media (max-width: 640px) {
    .cert-info-modal { height: auto; max-height: 88vh; }
}

.sidepanel-content textarea {
    width: 100%; min-height: 90px; resize: vertical;
}
.sidepanel-content label { margin: 0; font-weight: 600; color: var(--text); }
/* Bild statt Text empfangen/geladen (Screenshots) - ersetzt optisch das jeweilige <textarea> (siehe
   viewer.js showClipboardReceivedImage/setPendingSendImage), gleiche Rahmenoptik wie dieses. */
.clipboard-image-preview {
    display: block; max-width: 100%; max-height: 16rem; border: 1px solid var(--border);
    border-radius: var(--radius-sm); background: var(--surface-alt);
}
.clipboard-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.clipboard-actions .btn { font-size: 0.8rem; padding: 0.4rem 0.6rem; }
.shortcut-grid { display: flex; flex-direction: column; gap: 0.4rem; }
.shortcut-btn { font-size: 0.85rem; padding: 0.5rem 0.7rem; text-align: left; }

/* "Werkzeuge"-Tab (Phase M, siehe connect.php/viewer.js: 0x13 ToolsFrame) - Diagnose-Ausgabe,
   Dienste-Filter und -Liste. Nutzt bewusst .file-table/.viewer-status/.file-error/.hint statt
   eigener Duplikate. */
.tools-result {
    white-space: pre-wrap; font-family: inherit; font-size: 0.8rem; background: var(--surface-alt);
    border-radius: var(--radius-sm); padding: 0.5rem 0.6rem; max-height: 220px; overflow-y: auto;
    margin: 0;
}
.tools-result.error { color: var(--danger); }
.tools-service-filter {
    width: 100%; padding: 0.4rem 0.6rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
    background: var(--surface); color: var(--text); font-size: 0.85rem;
}
.tools-service-list-wrap { max-height: 320px; overflow-y: auto; }
.tools-service-table td { vertical-align: middle; }

/* "Zuschauer"-Tab (siehe connect.php/viewer.js: viewer_list/join_request/control_offer) -
   Zuschauerliste, offene Beitrittsanfragen (nur fuer den aktuellen Controller sichtbar) und die
   Kontrollangebot-Banner (nur fuer die angebotene Zielperson sichtbar). */
.viewers-list { list-style: none; margin: 0.5rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.viewer-list-item {
    display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
    padding: 0.5rem 0.65rem; border: 1px solid var(--border); border-radius: var(--radius-sm);
    font-size: 0.85rem;
}
.viewer-list-item.is-self { border-color: var(--primary); }
.viewer-list-item-name { display: flex; align-items: center; gap: 0.4rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.viewer-list-item-ip { color: var(--text-muted); font-size: 0.78rem; font-weight: normal; }
.viewer-list-item .btn { font-size: 0.78rem; padding: 0.35rem 0.55rem; flex: 0 0 auto; }

/* Schriftliche Fragen bei der Broadcast-Präsentation (broadcast-watch.js/broadcast-host.js) -
   reiner Einweg-Kanal Zuschauer -> Präsentator, wiederverwendet .viewer-list-item als Basis. */
.broadcast-question-item { align-items: flex-start; }
.broadcast-question-text { font-weight: normal; white-space: pre-wrap; word-break: break-word; }
.broadcast-question-form { display: flex; width: 100%; gap: 0.5rem; align-items: center; margin-top: 0.75rem; flex-wrap: wrap; }
.broadcast-question-form input[type="text"] { flex: 1 1 auto; width: 100%; min-width: 0; }

.guest-link-box { margin-top: 1rem; padding-top: 0.85rem; border-top: 1px solid var(--border); }
.guest-link-result { display: flex; gap: 0.4rem; margin-top: 0.5rem; }
.guest-link-result input { flex: 1 1 auto; min-width: 0; }
.viewer-controller-crown { flex: 0 0 auto; }
.join-requests { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.5rem; }
.join-requests:empty { margin-top: 0; }
.join-request-row, .control-offer-banner {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
    padding: 0.6rem 0.7rem; border-radius: var(--radius-sm); background: var(--accent-bg);
    border: 1px solid var(--primary); font-size: 0.85rem; margin-bottom: 0.75rem;
}
.join-request-row span, .control-offer-banner p { flex: 1 1 auto; margin: 0; min-width: 8rem; }
.join-request-row .btn, .control-offer-banner .btn { font-size: 0.78rem; padding: 0.35rem 0.55rem; }
.viewers-tab-badge {
    display: inline-block; margin-left: 0.3rem; min-width: 1.1rem; padding: 0 0.3rem; border-radius: 999px;
    background: var(--danger); color: #fff; font-size: 0.68rem; font-weight: 700; text-align: center; line-height: 1.3;
}

/* Ab hier: die horizontale Topbar-Navigation weicht dem Hamburger + der Navigations-Schublade -
   eigene, breitere Schwelle als die restlichen Mobile-Regeln weiter unten, weil die Tab-Leiste
   schon auf Tablet-Breite zu eng fuer alle Ziele wird, lange bevor der restliche Seiteninhalt
   (Karten/Tabellen) an der Handy-Schwelle unten selbst umgebaut werden muss. */
@media (max-width: 860px) {
    .topbar-nav, .topbar-search, .topbar-right .nav-dropdown, .topbar-right .reload-trigger { display: none; }
    .hamburger { display: flex; }
    .mobile-nav, .mobile-nav-scrim { display: flex; }
}

@media (max-width: 640px) {
    .main-col { margin-top: 1.25rem; margin-bottom: 1.25rem; padding-left: 1rem; padding-right: 1rem; }
    .card { padding: 1.1rem; }
    .viewer-canvas-wrap, .viewer-sidepanel { height: 60vh; min-height: 260px; flex: 1 1 100%; }
    th, td { padding: 0.5rem 0.5rem; }

    /* Touch-Ziele: Icon-Buttons/Sitzungstab-Knöpfe etwas großzügiger als auf Desktop, wo die
       Dichte für Maus-Bedienung optimiert ist. */
    .tile-icon-btn { width: 2.35rem; height: 2.35rem; font-size: 1.1rem; }
    .session-tab { max-width: 9.5rem; }
    .session-tab-close, .session-tab-reload { padding: 0.3rem 0.4rem; }

    /* Gerätezeile (Dashboard + Admin-Geräteliste): Icon/Name in einer Kopfzeile, Aktionen darunter
       über die volle Breite - verhindert, dass Name und die 3-4 Aktions-Buttons auf sehr schmalen
       Bildschirmen ineinander gequetscht werden. Die weitergehenden Überschreibungen für
       .device-row-actions/.device-tile-grid/#admin-devices-card .device-row-* stehen bewusst NICHT
       hier, sondern in den jeweiligen Seiten-eigenen <style>-Blöcken (dashboard.php/
       admin/devices.php) - deren unconditional Basisregeln für dieselben Eigenschaften stehen im
       Dokument NACH dieser app.css und würden eine hier stehende Regel bei gleicher Spezifität
       sonst überschreiben (CSS-Kaskade: gleiche Spezifität → spätere Quellposition gewinnt). */
    .device-row { flex-wrap: wrap; }

    /* Filterleisten (Statistik/Aufzeichnungen): Felder untereinander statt nebeneinander
       gequetscht, Aktionsknöpfe über die volle Breite. */
    .stats-filter { gap: 0.75rem; }
    .stats-filter > div { flex: 1 1 100%; }
    .stats-filter input[type=text], .stats-filter input[type=datetime-local], .stats-filter select { width: 100%; }
    .stats-filter-actions { margin-left: 0; width: 100%; }
    .stats-filter-actions .btn, .stats-filter-actions .btn.secondary { flex: 1 1 auto; text-align: center; }

    /* Passwort-Reset-Dialog (admin/users.php, natives <dialog>): auf Mobile nicht breiter als der
       Viewport. Das Bottom-Sheet-Verhalten für #device-info-overlay/-modal ist NICHT hier, sondern
       im Seiten-eigenen <style>-Block von dashboard.php - siehe Kommentar bei .device-row oben. */
    .pw-reset-dialog { max-width: 92vw; }

    /* Schwebende Werkzeugleiste (.viewer-toolbar-float): auf Handy-Breite etwas kompaktere
       Icon-Buttons, damit die ganze Reihe auch bei vielen Umschaltern (Windows-Ziel mit
       Audio/Privatsphäre/Aufnahme/Mauszeiger/Steuerung) in einer Zeile bleibt statt zu früh
       umzubrechen. */
    .vtool-btn { width: 1.9rem; height: 1.9rem; }
}

/* Eingebettete Fernwartungs-Tabs (siehe layout.php, persistenter Tab-Rahmen) - jede Sitzung laeuft
   in einem eigenen <iframe> auf /connect?...&embed=1, das layout_embed.php statt layout.php nutzt
   (kein doppelter Seiten-Header/Nav innerhalb des Tabs). Bewusst FESTE Hoehe fuer Canvas/Seitenpanel
   (per calc(100vh - 110px) geschaetzter Kopfzeilen-Hoehe) - der Viewer-Canvas soll exakt den
   verfuegbaren Platz fuellen, nicht selbst mitscrollen. Die 110px sind aber nur eine Schaetzung
   (Kopfzeile aendert ihre Hoehe z.B. mit der Anzahl sichtbarer Umschalt-Buttons) - overflow-y:auto
   auf der Karte selbst (statt overflow:hidden direkt auf body) ist deshalb ein Sicherheitsnetz:
   passt die Schaetzung nicht ganz exakt (z.B. der Tastatureingabe-Hinweistext unter dem Canvas),
   laesst sich der Rest herunterscrollen statt hart abgeschnitten und unerreichbar zu sein - genau
   dieses "abgeschnitten, kein Scrollen möglich" war schon einmal ein Bug (siehe Regel fuer
   schmale Viewports weiter unten, dort fuer Mobile bereits so geloest). */
body.embed { height: 100vh; overflow: hidden; }
/* .main-col statt main-Elementselektor: .main-col.full-width (2 Klassen, siehe oben) wuerde einen
   reinen Elementselektor sonst per Spezifitaet ausstechen und den beabsichtigten margin:0 NICHT
   anwenden (genau das war der Bug - sichtbar als unnoetig grosser Abstand ueber der Kopfzeile im
   eingebetteten Fernwartungs-Tab, verglichen mit z.B. "Meine Geräte"). padding jetzt komplett 0
   statt 0.6rem: das iframe (siehe .session-tab-content-wrap) sitzt schon exakt in der von der
   AEUSSEREN .main-col vorgegebenen, eingerueckten Spalte - ein zusaetzliches eigenes Padding HIER
   verschiebt die Karte nochmal zusaetzlich nach innen/unten und war der Grund, warum die Karte
   weder linksbuendig noch im Abstand zur Kopfzeile mit "Meine Geräte" (die direkt, ohne eigenes
   iframe, in derselben aeusseren .main-col liegt) uebereinstimmte. */
body.embed .main-col { margin: 0; padding: 0; height: 100vh; }
body.embed .card.viewer-card { height: 100%; display: flex; flex-direction: column; margin-bottom: 0; overflow-y: auto; }
body.embed .viewer-canvas-wrap, body.embed .viewer-sidepanel { height: calc(100vh - 110px); flex-shrink: 0; }
/* Ab min-width:641px ersetzt Flexbox die feste calc(100vh - 110px)-Schaetzung von .viewer-canvas-
   wrap/.viewer-sidepanel oben durch eine GENAU passende Hoehe (siehe "Fixiertes App-Fenster"
   weiter oben in dieser Datei fuer den vollen Hintergrund zur damit behobenen Scrollbalken-
   Verschachtelung) - .viewer-body bekommt per flex:1/min-height:0 exakt den Rest der Karte unter
   Kopfzeile/Hinweistext, Canvas/Seitenpanel fuellen das dann per height:100%. Bewusst nur Desktop:
   der max-width:640px-Block direkt darunter loest fuer schmale Viewports bereits denselben
   Zielkonflikt (Inhalt passt nicht in 100vh) auf eine andere, dort extra begruendete Art
   (Scrollen erlauben statt exakt einpassen) - beide Ansaetze gleichzeitig wuerden sich
   widersprechen, deshalb hier strikt getrennt per Media Query statt einer gemeinsamen Regel. */
@media (min-width: 641px) {
    body.embed .main-col { display: flex; flex-direction: column; }
    body.embed .card.viewer-card { height: auto; flex: 1 1 auto; min-height: 0; }
    body.embed .viewer-body { flex: 1 1 auto; min-height: 0; }
    body.embed .viewer-canvas-wrap, body.embed .viewer-sidepanel { height: 100%; flex-shrink: 1; }
}

/* Schmale Viewports (Handy/PWA): Canvas und Seitenpanel (Dateien/Zwischenablage/Tastaturbefehle)
   wrappen untereinander (.viewer-body: flex-wrap), bekommen ueber die Regel direkt darueber aber
   BEIDE fast die volle Bildschirmhoehe - zusammen deutlich mehr als 100vh. Mit dem festen
   height:100vh + overflow:hidden von body.embed war der untere Teil (das Seitenpanel mit den
   Tastaturbefehlen) dadurch unerreichbar abgeschnitten, ohne Moeglichkeit hinzuscrollen. Hier
   deshalb Scrollen erlauben und beiden Bereichen eine Hoehe geben, die zusammen in den Viewport
   passt, statt dass jeder fuer sich allein schon fast 100vh beansprucht. */
@media (max-width: 640px) {
    body.embed { height: auto; min-height: 100vh; overflow-y: auto; }
    body.embed .main-col { height: auto; min-height: 100vh; }
    body.embed .card.viewer-card { height: auto; }
    body.embed .viewer-canvas-wrap { height: 45vh; min-height: 220px; }
    body.embed .viewer-sidepanel { height: 45vh; min-height: 260px; }
}

/* Sicherheit/Admin/Uebersicht als Tab (siehe layout.php: window.top !== window) laufen dagegen
   ganz normal ueber layout.php selbst - anders als .embed oben braucht das KEINE feste Hoehe/
   overflow:hidden, sonst wird laengerer Seiteninhalt (z. B. Audit-Log-/Statistik-Tabellen) unten
   abgeschnitten und laesst sich nicht mehr scrollen. Blendet nur Topbar/Navigations-Schublade/
   den (in diesem verschachtelten Kontext ohnehin leeren) eigenen Tab-Rahmen aus, sonst normaler
   Dokumentfluss mit normalem Scrollen des iframes. */
body.tab-embed .topbar,
body.tab-embed .mobile-nav,
body.tab-embed .mobile-nav-scrim,
body.tab-embed .session-tabs-bar,
body.tab-embed #session-tabs-content { display: none !important; }
body.tab-embed .main-col { margin: 0; padding: 0.6rem; }

.session-tabs-bar {
    display: flex; align-items: flex-end; gap: 0.25rem; margin-bottom: 1rem;
    border-bottom: 1px solid var(--border); overflow-x: auto;
}
.session-tab {
    display: flex; align-items: center; gap: 0.5rem; flex: 0 0 auto; max-width: 16rem;
    background: var(--surface-alt); color: var(--text-muted); border: 1px solid var(--border);
    border-bottom: none; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    padding: 0.5rem 0.5rem 0.5rem 0.85rem; cursor: pointer; font-size: 0.85rem; font-weight: 500;
    transition: background 0.15s ease, color 0.15s ease;
}
.session-tab:hover { color: var(--text); }
.session-tab.active { background: var(--surface); color: var(--text); font-weight: 600; }
.session-tab-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.session-tab-close {
    flex: 0 0 auto; background: none; border: none; cursor: pointer; color: inherit; opacity: 0.65;
    font-size: 1rem; line-height: 1; padding: 0.1rem 0.3rem; border-radius: var(--radius-sm);
}
.session-tab-close:hover { opacity: 1; background: var(--danger-bg); color: var(--danger); }
.session-tab-reload {
    flex: 0 0 auto; background: none; border: none; cursor: pointer; color: inherit; opacity: 0.65;
    font-size: 0.95rem; line-height: 1; padding: 0.1rem 0.25rem; border-radius: var(--radius-sm);
}
.session-tab-reload:hover { opacity: 1; background: var(--accent-bg); }
.session-tab-content-wrap { display: none; height: calc(100vh - 170px); min-height: 420px; }
.session-tab-content-wrap.active { display: block; }
/* display:block ist hier kein Stil-Selbstzweck, sondern behebt einen eigenen kleinen Verschach-
   telungs-Fall: ein <iframe> ist per Default ein Inline-Element und bekommt dadurch (wie <img>)
   einen paar Pixel hohen "Descender"-Leerraum unterhalb sich selbst - macht den ansonsten exakt
   passenden .session-tab-content-wrap technisch ein paar Pixel hoeher als sichtbar und damit
   unnoetig scrollbar. */
.session-tab-content-wrap iframe { display: block; width: 100%; height: 100%; border: none; border-radius: var(--radius); }

/* ============ Fixiertes App-Fenster (Desktop) ============ */
/* Vorher wuchs die Seite bei zu wenig Platz einfach ueber den Viewport hinaus (aeusserer
   Dokument-Scrollbalken) UND der eingebettete Fernwartungs-Tab beanspruchte ueber
   calc(100vh - 170px) hier bzw. calc(100vh - 110px) bei body.embed weiter unten JEWEILS eine
   grob GESCHAETZTE eigene Hoehe - wich die tatsaechliche Kopf-/Tableistenhoehe auch nur wenig
   von der Schaetzung ab (z.B. Tab-Leiste bricht bei vielen offenen Tabs in eine zweite Zeile
   um), passten beide Schaetzungen nicht mehr exakt zusammen. Ergebnis: bis zu drei ineinander
   verschachtelte Scrollbalken (aeusseres Dokument, iframe-Karte, Dateiliste) gleichzeitig -
   genau das vom Nutzer gemeldete "scrollen in scrollen in scrollen". Ab hier deshalb echte
   Flexbox-Hoehen statt geschaetzter Pixelwerte: Kopfzeile/Tableiste behalten ihre natuerliche
   Hoehe, der jeweils aktive Inhaltsbereich (Home ODER Tab) bekommt darunter GENAU den Rest des
   Fensters, ohne selbst zu wachsen. overflow-y:auto bleibt trotzdem an mehreren Stellen als
   Sicherheitsnetz (wie schon bei body.embed weiter unten dokumentiert) - normalerweise ungenutzt,
   greift aber automatisch statt Inhalt hart abzuschneiden, falls doch mal etwas nicht exakt passt
   (z.B. .session-tab-content-wrap's min-height von 420px bei einem sehr kleinen Fenster). Nur
   Desktop (>640px) - auf Handy/PWA bleibt bewusst normales Dokument-Scrollen, siehe die Kommentare
   bei body.embed zum selben Thema (dort war eine feste Hoehe auf Mobile schon einmal ein Bug). */
@media (min-width: 641px) {
    body.app-authed { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
    body.app-authed .app-shell { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
    body.app-authed .topbar { flex: 0 0 auto; }
    /* width:100% ist hier Pflicht, keine Kosmetik: .main-col zentriert sich per margin: ... auto
       (Block-Layout-Trick, siehe Basisregel oben) - als Flex-Item eines Spalten-Containers
       (.app-shell, siehe direkt darueber) schrumpft ein Element mit automatischen Rand auf der
       Querachse aber auf seine Inhaltsbreite ZUSAMMEN statt zu stretchen (Flexbox-Spezifikation,
       Abschnitt Ausrichtung: auto-Raender "verbrauchen" den Freiraum statt zu stretchen). Ohne
       width:100% blieb die Karte dadurch nur so breit wie ihr Inhalt, zentriert in einem sonst
       leeren Fenster, statt wie vorher den vollen Platz auszufuellen - genau das war der Bug nach
       dem ersten Deploy dieses Fixes. */
    body.app-authed .main-col {
        display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; overflow-y: auto;
        width: 100%;
    }
    body.app-authed .session-tabs-bar { flex: 0 0 auto; }
    body.app-authed #home-tab-content { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
    /* display:none per Default (deckt den ersten Seitenaufruf ab, bevor ueberhaupt ein Tab
       geoeffnet wurde/das Skript unten aktiv eingreift) - layout.php's activateTab() schaltet
       auf 'block' um, sobald ein Fernwartungs-Tab aktiv ist, sonst wuerde dieser leere Container
       als zweiter flex:1-Bereich neben #home-tab-content dessen Hoehe grundlos halbieren. */
    body.app-authed #session-tabs-content { flex: 1 1 auto; min-height: 0; display: none; }
    body.app-authed .session-tab-content-wrap { height: 100%; }
}
