/* Grundstile, Teil 6 von 8: Sync-Anzeige, Mobilansicht, Kartensuche, Querformat, Ansichten-Umschalter.
   Die acht Dateien unter css/base/ werden in dieser Reihenfolge
   eingebunden; bei gleicher Spezifitaet gewinnt die spaetere Regel.
   Neue Regeln an die Datei anhaengen, zu der sie fachlich gehoeren,
   und dabei pruefen, ob eine spaetere Datei sie ueberschreibt. */

/* ===================================================================
 Sync-Pill in der Topbar + Sync-Modal.
 Pill ist sichtbar nur wenn pending>0 (hidden=true via JS sonst).
 Counter-Badge sitzt rechts oben am Icon. Bei failed-Items kriegt
 die Pill einen Amber-Tint, damit der User merkt "da ist was".
 =================================================================== */
/* coral als Akzentfarbe (Wunsch) - hebt sich von
 den anderen Topbar-Icons (neutral/gruen/violett/amber) klar ab.
 [hidden] muss explizit erzwungen werden weil .icon-btn ein
 display: inline-flex setzt das den User-Agent-Default
 display: none ueberschreibt - ohne diese Regel waere der Pill
 sichtbar obwohl JS hidden=true gesetzt hat. */
.sync-pill[hidden] {
    display: none !important;
}
.sync-pill {
    position: relative;
    color: #ff7f50 !important;
}
.sync-pill:hover {
    color: #e55934 !important;
    background: rgba(255, 127, 80, 0.14);
}
[data-theme="dark"] .sync-pill { color: #ff8c66 !important; }
[data-theme="dark"] .sync-pill:hover { color: #ffaa88 !important; }

.sync-pill--has-failed {
    /* Bei Failures kraeftiger und pulsierend, damit der User die
       Aufmerksamkeit kriegt - immer noch im Coral-Spektrum statt
       hartem rot. */
    color: #d8421e !important;
    animation: sync-pulse 2s ease-in-out infinite;
}
[data-theme="dark"] .sync-pill--has-failed { color: #ff6a3a !important; }

@keyframes sync-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.55; }
}

.sync-pill-count {
    position: absolute;
    top: 1px;
    right: 1px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    /* Counter-Hintergrund explizit dunkelrot statt
 currentColor (war bei Coral-Foreground unleserlich), Text weiss. */
    background: #b71c1c !important;
    color: #fff !important;
    font-size: 11px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    box-shadow: 0 0 0 1.5px var(--bg, #1a1d21);
    pointer-events: none;
}
[data-theme="light"] .sync-pill-count {
    box-shadow: 0 0 0 1.5px var(--bg-elevated, #fff);
}
.sync-pill-count::after {
    /* Damit der Counter-Text in der akzentfarbe der Pill bleibt aber
       die Schrift im Counter weiss ist - currentColor wird hier
       als Hintergrund genommen. */
    content: "";
}

/* Sync-Modal Body + Liste. */
.sync-modal-body {
    padding: 0 18px 12px;
    max-height: 60vh;
    overflow-y: auto;
}
.sync-modal-status {
    margin: 4px 0 12px;
    font-size: 0.92em;
}
.sync-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.sync-list-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 12px;
    background: var(--bg-elevated, #20242a);
    border: 1px solid var(--border, #2c333c);
    border-radius: 8px;
}
.sync-list-row-main {
    flex: 1;
    min-width: 0;
}
.sync-list-row-label {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    font-weight: 500;
    word-break: break-word;
}
.sync-list-row-meta {
    margin-top: 2px;
    font-size: 0.85em;
}
.sync-list-row-err {
    margin-top: 4px;
    font-size: 0.85em;
    color: #ef4444;
    word-break: break-word;
}
[data-theme="dark"] .sync-list-row-err { color: #fca5a5; }
.sync-list-row-actions {
    display: flex;
    flex-shrink: 0;
    gap: 6px;
}
/* Discard/Retry-Buttons im Sync-Modal groesser & klarer
 beschriftet. Touch-Targets >= 36px. */
.sync-list-row-actions .icon-btn,
.sync-list-row-actions button {
    min-width: 38px;
    min-height: 38px;
    padding: 6px 10px;
    font-size: 0.9em;
}
.sync-empty {
    text-align: center;
    padding: 20px 0 8px;
    opacity: 0.7;
}

/* Status-Badges in der Liste: queued (grau), sending (blau), failed (rot). */
.sync-badge {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 0.72em;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    line-height: 1.6;
    border: 1px solid transparent;
    white-space: nowrap;
}
.sync-badge--queued {
    background: rgba(148, 163, 184, 0.18);
    color: #94a3b8;
    border-color: rgba(148, 163, 184, 0.4);
}
.sync-badge--sending {
    background: rgba(59, 130, 246, 0.18);
    color: #60a5fa;
    border-color: rgba(59, 130, 246, 0.4);
}
.sync-badge--failed {
    background: rgba(239, 68, 68, 0.18);
    color: #ef4444;
    border-color: rgba(239, 68, 68, 0.45);
}
[data-theme="light"] .sync-badge--queued { color: #475569; }
[data-theme="light"] .sync-badge--sending { color: #1d4ed8; }
[data-theme="light"] .sync-badge--failed { color: #b91c1c; }

/* =====================================================================
 — Mobile Responsive Pass.
 Ziel:
 - Modals werden auf <=600px zu Bottom-Sheets / Vollbild
 - Topbar-Buttons bekommen min. 44x44 Touch-Target
 - Mess-Panel als Bottom-Sheet
 - Tabellen scrollen horizontal
 - Geom-Mini-Map kleiner
 ===================================================================== */
@media (max-width: 600px) {
    /* --- Topbar: Tighter spacing, scrollable wenn zu viele Icons. --- */
    .topbar {
        gap: 4px;
        padding: 6px 8px;
        flex-wrap: nowrap;
        overflow-x: auto;
    }
    .topbar-right {
        gap: 2px;
        flex-wrap: nowrap;
    }
    /* Topbar-Icon-Btns bleiben auf Mobile bei
 36x36 (genau wie der Desktop-Default `.topbar-right .icon-btn`),
 statt vorher auf 44x44 hochzuschalten. Grund: bei vielen Icons
 wurde die Topbar sonst zu breit + die Buttons wirkten ungleich
 gross gegenueber dem 36x36-User-Pill und der view-switcher-Spacer
 passte nicht mehr exakt unter den floating view-stack. */
    .topbar .icon-btn,
    .topbar .ghost {
        min-width: 36px;
        min-height: 36px;
        padding: 0;
    }
    /* Ausloeser und Listenfelder folgen demselben 36x36-Raster wie alle
       anderen Kopfzeilen-Symbole. */
    .topbar .view-switcher {
        width: 36px;
        height: 36px;
        flex: 0 0 36px;
    }
    .view-stack-item {
        width: 36px;
        height: 36px;
        min-width: 36px;
        max-width: 36px;
        min-height: 36px;
        max-height: 36px;
        flex: 0 0 36px;
    }
    /* User-Pill folgt dem 36x36-Default (siehe #user-pill.icon-btn
       oben), kein Mobile-Override mehr. */
    #user-pill.icon-btn {
        width: 36px;
        height: 36px;
    }
    .user-pill {
        font-size: 0.85em;
        max-width: 110px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* --- Modals: Vollbild auf Mobile. --- */
    .modal-card,
    .modal-card--wide,
    .modal-card--wider {
        max-width: 100vw !important;
        width: 100vw !important;
        max-height: 100dvh;
        height: 100dvh;
        border-radius: 0 !important;
        margin: 0 !important;
        display: flex;
        flex-direction: column;
    }
    .modal-card--sm {
        max-width: 92vw !important;
        width: 92vw !important;
        max-height: calc(100dvh - 48px);
        height: auto;
    }
    /* Confirm-Dialoge (.modal--confirm) auf Mobile
 * dann doch in Vollbild — sonst wirkt der Abmelden-Confirm wie
 * ein zufaelliges Pop-up auf der Karte. Andere Klein-Modals
 * (bug-modal, image-lightbox) sind nicht .modal--confirm und
 * bleiben damit unangefasst (Lightbox haengt sowieso eigene
 * Fullscreen-Regel). */
    .modal.modal--confirm .modal-card.modal-card--sm {
        max-width: 100vw !important;
        width: 100vw !important;
        height: 100dvh !important;
        max-height: 100dvh !important;
        border-radius: 0 !important;
        margin: 0 !important;
    }
    .modal.modal--confirm .modal-card.modal-card--sm > .modal-text,
    .modal.modal--confirm .modal-card.modal-card--sm > .confirm-checkbox {
        padding: 12px 16px;
        margin: 0;
        flex: 1 1 auto;
        align-self: flex-start;
        font-size: 1rem;
    }
    /* Selektor jetzt auf DIREKTE Modal-Card-Kinder eingeschraenkt
     * (".modal-card > .modal-form" etc.), genau so wie das
     * Vollbild-Pattern es macht. Geschachtelte Forms bekommen ihre
     * natuerliche Hoehe und scrollen nicht selbst — der Body scrollt. */
    .modal-card > .modal-body,
    .modal-card > .modal-form,
    .modal-card > .profile-modal-body,
    .modal-card > .pane,
    .modal-card > .xt-create-form {
        flex: 1 1 auto;
        overflow-y: auto !important;
        -webkit-overflow-scrolling: touch;
        padding-bottom: 12px;
    }
    /* innere Forms im Profil-Modal explizit zurueck auf
     * natuerliche Hoehe + sichtbares Overflow (Safety-Net, falls
     * andere alte Regeln per Spezifitaet noch zugreifen). */
    #profile-modal .profile-modal-body > form.modal-form,
    #profile-modal .profile-gruppe > form.modal-form,
    #profile-modal .modal-body > form.modal-form {
        flex: 0 0 auto !important;
        overflow: visible !important;
        height: auto !important;
        min-height: 0 !important;
    }
    .modal-header {
        position: sticky;
        top: 0;
        z-index: 2;
        background: var(--bg-card, #fff);
        border-bottom: 1px solid var(--border);
    }
    .modal-footer {
        position: sticky;
        bottom: 0;
        background: var(--bg-card, #fff);
        border-top: 1px solid var(--border);
        padding: 8px 10px;
    }
    .modal-actions {
        flex-wrap: wrap;
        gap: 6px;
    }
    .modal-actions button {
        min-height: 44px;
        flex: 1 1 auto;
    }

    /* --- Tabellen scrollen horizontal statt zu zerquetschen. --- */
    .trash-table,
    .modal-card table {
        display: block;
        overflow-x: auto;
        white-space: nowrap;
    }

    /* --- Geom-Edit-Mini auf Mobile: kompakter. --- */
    .feature-geomedit-mini {
        height: 160px !important;
    }

    /* --- Mess-Panel als Bottom-Sheet. --- */
    .geolyser-measure-panel {
        right: 0;
        bottom: 0;
        width: 100vw;
        max-width: 100vw;
        border-radius: 12px 12px 0 0;
    }

    /* --- Sync-Modal-Liste: Items vertikal stapeln. --- */
    .sync-list-row {
        flex-wrap: wrap;
        gap: 4px;
    }

    /* --- Login-Card: weniger Padding. --- */
    .login-card {
        padding: 16px !important;
    }

    /* --- Marker-Icon-Picker: kleinere Buttons. --- */
    .marker-icon-picker {
        gap: 4px;
    }
    .marker-icon-picker button {
        min-width: 44px;
        min-height: 44px;
    }

    /* --- Tag-Picker-Suggestions als Dropdown statt absoluter Overlay. --- */
    .tag-suggestions {
        max-height: 50vh;
    }

    /* --- DB-Modal: Filter-Bar auf zwei Zeilen wenn zu eng. --- */
    .db-filter-bar {
        flex-wrap: wrap;
        gap: 6px;
    }
    .db-filter-search {
        flex: 1 1 100% !important;
    }

    /* --- Layer-Control auf Mobile etwas groesser fuer Touch. --- */
    .leaflet-control-layers-expanded label {
        padding: 6px 4px;
        min-height: 32px;
        display: flex;
        align-items: center;
    }

    /* --- Image-Slots auf Mobile in 2 Spalten. --- */
    .image-slots {
        grid-template-columns: repeat(2, 1fr) !important;
    }

    /* --- GPS-Tools-Stack: kleiner und weiter rechts vom Locate-Btn. --- */
    .gps-tools {
        left: auto;
        width: max-content;
        max-width: calc(100vw - 16px);
        right: 8px;
        bottom: 70px;
    }
}

/* Sehr schmale Mobile (z. B. iPhone SE): noch dichter. */
@media (max-width: 380px) {
    .topbar h1, .topbar .brand-text {
        display: none;
    }
    .modal-title {
        font-size: 1em;
    }
    .feature-geomedit-mini {
        height: 130px !important;
    }
}

/* Touch-only-Geraete: Hover-Effekte reduzieren, Buttons groesser. */
@media (hover: none) and (pointer: coarse) {
    button, .icon-btn, .ghost, .primary {
        min-height: 40px;
    }
    /* Karte: Tap-Highlight von iOS abschalten. */
    #map, .leaflet-container {
        -webkit-tap-highlight-color: transparent;
    }
}

/* =====================================================================
 — Komplette Mobile-Ueberholung (zusaetzlich zur
 Basis oben). Zusaetzliche Fixes:
 - Body kein horizontales Scrolling (Karte wirkte abgeschnitten weil
 eine zu breite Sidebar das viewport scrolling triggerte)
 - Tablet-Breakpoint 768px
 - Sidebars als full-screen-overlays auf Mobile
 - Mess-Panel auf Mobile als Bottom-Sheet, nicht in-map
 - Locate/Geoman/Mess controls bekommen 36-40px touch-targets
 ===================================================================== */
html, body {
    overflow-x: hidden;
}
@media (max-width: 768px) {
    /* Sidebars als slide-in-Overlay statt feste Spalte. Das app-shell
       grid wird zu einem 1-Spalten-Layout, die Sidebars liegen darueber
       wenn sie geoeffnet sind. */
    .app-shell {
        grid-template-columns: 1fr !important;
        grid-template-areas: "main" !important;
    }
    .sidebar-left,
    .sidebar-right {
        position: fixed;
        top: 0;
        height: 100dvh;
        z-index: 1700;
        width: min(86vw, 360px);
        max-width: 86vw;
        background: var(--bg-card, #fff);
        box-shadow: 0 0 24px rgba(0, 0, 0, 0.4);
        transition: transform .22s ease;
    }
    .sidebar-left {
        left: 0;
        transform: translateX(-101%);
    }
    .sidebar-right {
        right: 0;
        transform: translateX(101%);
    }
    .sidebar-left.is-open-mobile { transform: translateX(0); }
    .sidebar-right.is-open-mobile { transform: translateX(0); }
    /* Resizer-Indikatoren ausblenden auf Mobile - Sidebars sind
       Overlays, nicht resizable. */
    .resize-handle, .resize-indicator { display: none !important; }
    /* Mess-Panel: auf Mobile NICHT als in-map-Sticker, sondern wieder
       als Bottom-Sheet damit es nicht halb auf der Karte wegfaellt. */
    .geolyser-measure-panel--in-map {
        position: fixed !important;
        top: auto !important;
        right: 0 !important;
        bottom: 0 !important;
        width: 100vw !important;
        max-width: 100vw !important;
        border-radius: 12px 12px 0 0;
        opacity: 1 !important;
    }
    /* Topbar: Logo verkleinern, Username darf umgebrochen werden. */
    .topbar .brand-logo, .topbar img.logo {
        max-height: 32px !important;
    }
    /* Geoman-Toolbar etwas weiter unten damit sie nicht hinter der
       Topbar liegt. */
    .leaflet-top.leaflet-left {
        top: 8px;
    }
    /* Modals auf Mobile (<= 768px) als Fullscreen-Sheet.
 So ist der Inhalt mit dem 100dvh-Viewport sichtbar (mobile
 Browser-URL-Leiste!) und der Footer mit Aktionen klebt unten,
 statt dass das Modal halb hinter dem Tastatur-Bereich
 verschwindet. .modal-card--sm Modals duerfen trotzdem klein
 bleiben (sind kurze Confirm-Dialoge). */
    .modal {
        align-items: stretch;
        padding: 0;
    }
    .modal .modal-card:not(.modal-card--sm) {
        width: 100vw;
        max-width: 100vw;
        height: 100dvh;
        max-height: 100dvh;
        border-radius: 0;
        display: flex;
        flex-direction: column;
    }
    .modal .modal-card:not(.modal-card--sm) .modal-body {
        flex: 1 1 auto;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
    }
    .modal .modal-card:not(.modal-card--sm) .modal-footer {
        position: sticky;
        bottom: 0;
        background: var(--bg-card, #fff);
        border-top: 1px solid var(--border);
        padding: 10px 14px;
        margin: 0;
        z-index: 2;
    }
    /* Save-Options-Karten: Beschreibungstext wrappen erlauben. */
    .save-options-card-desc {
        white-space: normal;
    }
    /* DSM-Picker und vergleichbare dynamische Modals: max-width
       cap weg fuer Mobile damit sie auch fullscreen sind. */
    .modal .modal-card[style*="max-width"] {
        max-width: 100vw !important;
    }
}

/* Viewport-Fix fuer mobile Browser-URL-Leiste. Auf
 Geraeten ohne 100dvh-Support (alte iOS/Android-Browser) bleibt
 100vh als Fallback - dort kann's vorkommen dass die Bottom-Sheet
 leicht hinter der URL-Leiste liegt, ist aber lebbar. */
@supports not (height: 100dvh) {
    @media (max-width: 768px) {
        .modal .modal-card:not(.modal-card--sm) {
            height: 100vh;
            max-height: 100vh;
        }
        .sidebar-left, .sidebar-right { height: 100vh; }
    }
}

@media (max-width: 600px) {
    /* Image-Slots fuellen die verfuegbare Breite auto-fit (mind. 80px
     * pro Slot). Mit auto-fit + minmax(80px, 1fr) sitzen sie
     * nebeneinander wo Platz ist und brechen erst um wenn Slots sonst
     * kleiner als 80px wuerden. */
    @supports (display: grid) {
        .image-slots {
            grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)) !important;
        }
    }
    /* Topbar Brand-Text/Untertitel ausblenden, nur das Logo behalten. */
    .topbar .brand-sub {
        display: none;
    }
    /* Sync-Pill-Counter etwas groesser damit Touch-Tap zielt. */
    .sync-pill-count {
        min-width: 18px;
        height: 18px;
        font-size: 11px;
        line-height: 18px;
    }
    /* DB-Modal: Filter-Bar als Akkordeon-Button (Wrap) damit sie nicht
       die ganze Breite stiehlt. */
    .db-modal-filters {
        flex-wrap: wrap;
    }
}

/* sortierbare Tabellen-Header. Cursor + Hover-Hint.
 Wird im Admin-Modal von audit-table, admin-users-table, admin-xt-table
 und admin-fromothers-table genutzt. Bewusst dezenter als Buttons -
 Klick-Sort soll nicht wie eine primary action wirken. */
.trash-table th.is-sortable {
    cursor: pointer;
    user-select: none;
}
.trash-table th.is-sortable:hover {
    background: var(--bg-elevated, var(--input-bg));
}

/* DSM-Source-Dropdown unter den Mess-Tool-Buttons
 (Volumen/Profil) und unter den Retro-Tool-Buttons im Feature-Edit-
 Modal. Ersetzt das alte modal-basierte pickDsmSource (78.32). */
/* DSM-Source + Volume-Ref als einheitliches
 Picker-Block. Beide Rows im selben Grid mit fester Label-Spalte,
 sodass die Dropdowns vertikal fluchten. Wird im Mess-Tool und im
 Feature-Modal verwendet. */
.geolyser-picker-group {
    margin-top: 6px;
    border-top: 1px solid var(--border);
    padding-top: 2px;
}
.geolyser-picker-row {
    display: grid;
    grid-template-columns: 78px 1fr;
    gap: 8px;
    align-items: center;
    padding: 4px 8px;
}
.geolyser-picker-row[hidden] {
    display: none;
}
.geolyser-picker-row--ref {
    /* Wenn der Custom-Z-Input sichtbar ist, bekommen wir eine dritte
       Spalte. Ist er hidden (Default), greift die zweite Regel weiter
       unten und das Select fuellt die ganze Spalte (wie bei der
       DSM-Source-Row). */
    grid-template-columns: 78px 1fr 80px;
}
.geolyser-picker-row--ref:has(.geolyser-picker-customz[hidden]) {
    grid-template-columns: 78px 1fr;
}
.geolyser-picker-label {
    font-size: 0.78em;
    color: var(--muted-hi);
    justify-self: start;
    align-self: center;
}
.geolyser-picker-select {
    width: 100%;
    padding: 4px 6px;
    font-size: 0.85em;
    background: var(--input-bg);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 4px;
}
.geolyser-picker-customz {
    width: 100%;
    padding: 4px 6px;
    font-size: 0.85em;
    background: var(--input-bg);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 4px;
    font-variant-numeric: tabular-nums;
}
.geolyser-picker-customz[hidden] {
    display: none;
}

/* Beschreibung + Geometrie-Mini-Vorschau nebeneinander
 als feste 260x260px-Quadrate. Description-Textarea fuellt den
 verfuegbaren Platz im Quadrat aus. Wenn keine Geometrie da ist
 (Create-Pfad), bleibt nur die Description-Box stehen, immer noch
 260x260. */
.feature-modal-desc-row {
    display: flex;
    gap: 12px;
    /* flex-start statt stretch, damit die
     etwas niedrigere Beschreibung NICHT auf die 260px-Hoehe der
     Mini-Map hochgezogen wird — darunter ist jetzt Platz fuer den
     Audioaufnahme-Button. */
    align-items: flex-start;
    flex-wrap: wrap;          /* bei sehr schmalem Modal Umbruch */
    margin: 4px 0;
}
.feature-modal-desc-row .feature-modal-desc-field {
    /* Description ist flexibel — sie nimmt den
 verbleibenden Platz zwischen linker Modal-Edge und der 260px
 breiten Mini-Map. Die Hoehe wurde von 260 auf 200 reduziert,
 damit darunter der Audioaufnahme-Button Platz hat. */
    flex: 1 1 0;
    min-width: 220px;
    height: 238px;
    margin: 0;
    display: flex;
    flex-direction: column;
}
.feature-modal-desc-row .feature-modal-desc-field textarea {
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
    resize: none;             /* feste Box, kein User-Resize */
}
.feature-modal-desc-row .feature-modal-geom-preview {
    flex: 0 0 260px;
    width: 260px;
    /* Hoehe an die Beschreibung (238px) angeglichen. */
    height: 238px;
}
.feature-modal-desc-row .feature-modal-geom-preview[hidden] {
    display: none;
}
.feature-modal-desc-row .feature-modal-geom-preview .feature-geomedit-mini {
    width: 100%;
    height: 100%;
    border-radius: var(--radius-sm, 4px);
    overflow: hidden;
    border: 1px solid var(--border);
}
/* Smartphone: Mini-Karte breiter (378px) aber flacher (160px). */
@media (max-width: 768px) {
    .feature-modal-desc-row .feature-modal-geom-preview {
        flex: 0 0 auto;
        width: 378px;
        max-width: 100%;
        height: 160px;
    }
}

/* Picker-Block sitzt jetzt unter den Retro-Tools-Buttons
 (DSM-Quelle ist nicht mehr inline in der Buttons-Zeile). Keine
 spezielle Regel mehr noetig — die .geolyser-picker-group-Klasse
 stylet beide Rows einheitlich. */


/* Flash-Highlight auf das Geometrie-Werte-Feld nach
 einer erfolgreichen Retro-Aktion (sample-z / profile / volume). Soll
 nur kurz aufflashen, nicht als persistent gruen-getoenetes Feld stehen
 bleiben. Animation 800ms, danach automatisch zurueck. */
.feature-geom-stats.is-flash {
    animation: geolyser-stats-flash 0.8s ease-out;
}
@keyframes geolyser-stats-flash {
    0%   { background: var(--c-signal-dim, rgba(36, 200, 130, 0.35));
           box-shadow: 0 0 0 4px var(--c-signal-dim, rgba(36, 200, 130, 0.25)); }
    100% { background: transparent; box-shadow: 0 0 0 0 transparent; }
}

/* Profil-Icon-Span in den Geometrie-Werten. Klick
 oeffnet das Profil-Chart-Modal (showProfileChart aus measurement-tool). */
.feature-profile-stat {
    cursor: pointer;
    transition: background 0.12s ease;
}
.feature-profile-stat:hover,
.feature-profile-stat:focus {
    background: var(--bg-elevated, var(--input-bg));
    outline: none;
}

/* Progress-Toast fuer DSM-Sampling-Loops.
 Eine eigene Klasse oben auf .geolyser-toast - bekommt eine Progress-
 Bar darunter die ueber die Lebenszeit von 0% auf 100% wandert. */
.geolyser-progress-toast {
    min-width: 240px;
}
.geolyser-progress-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 4px;
}
.geolyser-progress-label {
    font-size: 0.9em;
    font-weight: 600;
}
.geolyser-progress-count {
    font-size: 0.78em;
    color: var(--muted-hi);
    font-variant-numeric: tabular-nums;
}
/* Abbruchkreuz im Fortschrittshinweis. Nur da,
   wenn der Aufrufer ein onCancel mitgibt. */
.geolyser-progress-cancel {
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    margin: -2px -4px -2px 0;
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm, 6px);
    background: transparent;
    color: inherit;
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
    opacity: 0.75;
}
.geolyser-progress-cancel:hover {
    background: rgba(255, 255, 255, 0.14);
    opacity: 1;
}
.geolyser-progress-cancel:disabled {
    opacity: 0.35;
    cursor: default;
}
.geolyser-progress-bar {
    height: 4px;
    background: rgba(255, 255, 255, 0.16);
    border-radius: 2px;
    overflow: hidden;
}
.geolyser-progress-bar-fill {
    height: 100%;
    background: var(--accent, #3BBDE8);
    width: 0%;
    transition: width 0.18s ease-out;
}

/* Spinner im Progress-Toast. Sichtbar solange noch keine
 Phasen-Updates eingelaufen sind (Bar bei 0%). Verschwindet sobald
 setPercent(>0) feuert. */
.geolyser-progress-spinner {
    display: none;
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    border: 2px solid rgba(255, 255, 255, 0.25);
    border-top-color: var(--accent, #3BBDE8);
    border-radius: 50%;
    animation: geolyser-progress-spin 0.8s linear infinite;
}
.geolyser-progress-toast.is-pending .geolyser-progress-spinner {
    display: inline-block;
}
.geolyser-progress-toast.is-pending .geolyser-progress-bar {
    /* Indeterminate-Stripe-Effekt waehrend der Bar bei 0 steht. */
    background: linear-gradient(
        90deg,
        rgba(255, 255, 255, 0.12) 0%,
        rgba(255, 255, 255, 0.22) 50%,
        rgba(255, 255, 255, 0.12) 100%
    );
    background-size: 200% 100%;
    animation: geolyser-progress-shimmer 1.4s linear infinite;
}
@keyframes geolyser-progress-spin {
    to { transform: rotate(360deg); }
}
@keyframes geolyser-progress-shimmer {
    0%   { background-position: -200% 0; }
    100% { background-position: 200% 0; }
}

/* Per-Feature Color-Picker im Feature-Edit-Modal.
 Pattern: 8x4 Grid mit Square-Tiles. Erstes Tile = "Auto" (= Layer-Farbe). */
.feature-color-picker {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.feature-color-pick {
    width: 28px;
    height: 28px;
    border-radius: 6px;
    border: 2px solid transparent;
    cursor: pointer;
    padding: 0;
    transition: transform 0.1s ease, border-color 0.12s ease;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.feature-color-pick:hover {
    transform: scale(1.08);
}
.feature-color-pick.is-active {
    border-color: var(--fg);
    box-shadow: 0 0 0 1px var(--bg-card);
}
/* Der freie Color-Picker ist ein <label> (kein <button>)
   und bekam in der Mobile-Ansicht die hoehere Touch-Hoehe der Geschwister-
   Swatches nicht ab — er blieb quadratisch (28x28 / 18x18 / 26x26). Hier
   explizit auf 40 px Hoehe setzen; die Breite kommt aus der Kontext-Klasse
   (Feature 28 / Layer 18 / Tag 26). */
/* .color-pick-native/.color-pick-confirm-Selektoren
   entfernt — der native Picker + Checkmark-Tile sind ausgebaut, alle
   Custom-Farb-Stellen nutzen das Popover (color-pick-custom). */
@media (max-width: 768px) {
    .feature-color-pick.color-pick-custom,
    .layer-color-pick.color-pick-custom,
    .tag-color-swatch.color-pick-custom {
        height: 40px !important;
    }
}
/* "Keine eigene Farbe"-Kachel im Farbwaehler. Drei Stellen benutzen
   sie: Feature-Modal (map.js), Massenbearbeitung in der DB-Vollansicht
   (db-modal.js) und das Platzieren einer Vorlage (vorlagen/liste.js).

   Die Regel war fuer ein KIND geschrieben, das
   seine Kachel ausfuellt - daher `width: 100%` und `height: 100%`.
   Getragen hat die Klasse aber der KNOPF selbst, und dort heisst 100 %
   die volle Breite des Farbwaehlers. Die Kachel lief als Balken ueber
   die ganze Panelbreite, und die Palette rutschte in die naechste
   Zeile. `font-size` und `font-weight` stammten aus der Zeit, als hier
   ein "A" stand statt des Symbols, und wirkten schon lange auf nichts
   mehr.

   Merksatz: `width: 100%` an einem Element, das selbst die Kachel IST,
   misst den Elternkasten aus, nicht sich selbst.

   Vorbild ist `.tag-color-swatch--clear`: gestrichelter Rand heisst
   leer, geschlossener Rand heisst gewaehlt. Groesse, Radius und
   Zentrierung kommen aus `.feature-color-pick`; hier steht nur, was
   anders ist. */
.feature-color-pick-auto {
    background: var(--input-bg);
    border: 2px dashed var(--border-hi);
}
.feature-color-pick-auto.is-active {
    border-style: solid;
}
/* Das Symbol (clear-svgrepo) ist mit `stroke="#000000"` fest schwarz. */
.feature-color-pick-auto-icon {
    opacity: 0.62;
    pointer-events: none;
}
[data-theme="dark"] .feature-color-pick-auto-icon {
    filter: invert(1);
}

/* Opacity-Slider unter jedem Overlay-Layer im
 Leaflet-Layer-Control. Slider full-width, Wert rechts. */
.geolyser-opacity-slider-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 2px 4px 4px 24px;
    flex-basis: 100%;
}
.geolyser-opacity-icon {
    color: var(--muted-hi);
    font-size: 0.85em;
    flex-shrink: 0;
}
.geolyser-opacity-slider {
    flex: 1;
    min-width: 0;
    height: 14px;
    cursor: pointer;
}
.geolyser-opacity-val {
    font-size: 0.72em;
    color: var(--muted-hi);
    min-width: 32px;
    text-align: right;
    font-variant-numeric: tabular-nums;
    flex-shrink: 0;
}

/* zusaetzliche Modal-Footer-Modifier. */
.modal-footer--center {
    justify-content: center;
}
.modal-footer--split {
    justify-content: space-between;
}
.primary.primary-wide {
    min-width: 220px;
    padding: 10px 24px;
    font-size: 1em;
}

/* Audit-Toolbar vertikal zentrieren. */
.audit-toolbar.audit-toolbar--centered {
    display: flex;
    align-items: end;
    gap: 12px;
}
.audit-toolbar.audit-toolbar--centered .audit-toolbar-filter {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
/* Refresh-Btn vertikal mit dem select-Element des Filters
 alignen, nicht mit der Label-Zeile darueber. */
.audit-toolbar.audit-toolbar--centered [data-action="audit-refresh"] {
    align-self: end;
    margin-bottom: 0;
}

/* =====================================================================
 - Mobile UI Refresh
 ----------------------------------------------------------------------
 Sammlung der Mobile-Feinschliff-Punkte. Greift on top der
 bestehenden Mobile-Regeln. Bewusst als
 eigener Block am Ende der Datei, damit die Spezifitaet greift ohne
 bestehende Regeln zu touchieren.
 ===================================================================== */

/* 8.1 — UI-Elemente kleiner. Topbar-Buttons hatten 44px-Touch-Targets,
   das ist auf modernen Geraeten oft uebertrieben. 36px reicht und gibt
   mehr horizontalen Platz. */
@media (max-width: 768px) {
    .topbar .icon-btn,
    .topbar .ghost {
        min-width: 36px !important;
        min-height: 36px !important;
        padding: 6px !important;
    }
    .topbar h1, .topbar .brand-text {
        font-size: 1em;
    }
    /* Sidebar-Header etwas kompakter. */
    .sidebar-header .icon-btn,
    .features-panel-rail .icon-btn {
        min-width: 36px;
        min-height: 36px;
    }
    /* Layer-Row-Buttons kompakter. */
    .layer-row .icon-btn,
    .layer-row .layer-toggle {
        min-width: 30px;
        min-height: 30px;
        padding: 4px;
    }
}

/* 8.2 — Modal-Stil im Mobile eigen. Im Fullscreen-Mode keine
 "halb-abgerundeten" Ecken (border-radius war 12px aussen, im Vollbild
 das ist kein Vollbild-Look). Eckig + bestaendig. setzte
 border-radius:0 auf .modal-card:not(.modal-card--sm), wir ziehen das
 konsequent durch. */
@media (max-width: 768px) {
    .modal .modal-card:not(.modal-card--sm) {
        border-radius: 0 !important;
        box-shadow: none !important;
    }
    .modal .modal-card:not(.modal-card--sm) .modal-header,
    .modal .modal-card:not(.modal-card--sm) .modal-footer {
        border-radius: 0 !important;
    }
}

/* 8.3 — Anleitung (Help-Modal) Mobile-Anpassung.
   - Zurueck-Pfeil im Detail-View nach oben RECHTS verschieben (war
     standard links).
   - Bug-Melden-Btn nur in der Kapitel-Uebersicht (Liste), nicht im
     Detail-Mode.
   - Kapitel-Liste vertikal mit groesserem Text. */
@media (max-width: 768px) {
    .help-shell.is-detail-mode .help-back-btn {
        position: absolute;
        top: 12px;
        right: 12px;
        left: auto;
    }
    .help-shell.is-detail-mode .help-bug-report-btn {
        display: none !important;
    }
    .help-shell .help-toc {
        display: flex;
        flex-direction: column;
        gap: 8px;
    }
    .help-shell .help-toc button[data-chapter] {
        padding: 12px 14px;
        font-size: 1.05em;
        text-align: left;
        border-radius: 6px;
    }
}

/* 8.4 — Settings / Admin-Einstellungen im selben Stil wie das Help-Modal:
   eckig, sticky-Header, Tab-Bar groesser fuer Touch. */
@media (max-width: 768px) {
    #admin-modal .modal-card,
    #profile-modal .modal-card {
        border-radius: 0 !important;
    }
    #admin-modal .modal-header-tabs button,
    #profile-modal .modal-header-tabs button {
        min-height: 40px;
        padding: 8px 12px;
        font-size: 0.95em;
    }
    /* Admin-Tabellen breiter scrollbar damit Action-Buttons rechts
       erreichbar sind ohne dass die Inputs aus der Zeile rauspoppen. */
    #admin-modal .trash-table {
        font-size: 0.85em;
    }
}

/* Image-Slot von 110px auf 70px Hoehe, action-Buttons
 von 32px auf 24px. Bild-Thumbnails bleiben quadratisch via aspect-ratio
 und nutzen den verfuegbaren Grid-Slot. */
@media (max-width: 768px) {
    .image-slots .image-slot {
        min-height: 70px !important;
        max-height: 90px;
    }
    .image-slot .image-slot-action,
    .image-slot button {
        min-width: 24px !important;
        min-height: 24px !important;
        padding: 2px;
    }
    /* Remove-Btn am Slot bleibt erkennbar gross genug fuer Touch (24px). */
    .image-slot-remove {
        width: 24px !important;
        height: 24px !important;
        font-size: 14px;
    }
    /* Hint-Text auf Mobile sehr kompakt. */
    .image-slot-hint {
        font-size: 10px !important;
        line-height: 1.1;
    }
}

/* 8.6 — DB-Vollansicht: Toolbar mit zweiter Reihe wenn zu viele Buttons.
   Bisher schoben sich die Buttons (Import / Export / Bulk-Edit / Attach /
   Delete) bei kleinem Viewport ueber den Rand. Wir wrappen jetzt in eine
   zweite Reihe. */
@media (max-width: 768px) {
    /* alte Regel zielte auf `.db-toolbar`,
 * eine Klasse die im aktuellen HTML nicht existiert. Tatsaechlich
 * heisst die Toolbar im DB-Modal jetzt `.db-filter-bar` (oben)
 * + `.modal-footer.modal-footer--compact` (unten mit Import-Btn).
 * Der Import-Btn-Cutoff sitzt im Footer; wir wrappen den im Mobile. */
    .db-filter-bar,
    #db-modal .modal-footer.modal-footer--compact,
    .db-bulk-bar {
        flex-wrap: wrap !important;
        gap: 6px !important;
    }
    /* Buttons im DB-Modal-Footer behalten ihre Breite (kein flex-shrink),
     * aber duerfen in zweite Reihe wrappen. */
    #db-modal .modal-footer.modal-footer--compact > * {
        flex-shrink: 0;
    }
    /* Sel-Count + Import-Btn-Trenner in eigene Zeile damit nichts
     * abgeschnitten wird. */
    #db-modal [data-role="sel-count"] {
        margin-left: 0 !important;
        flex-basis: 100%;
        order: -1;
        font-size: 0.85em;
    }
    /* DB-Modal-Card im Mobile breiter als der Default `modal-card`,
     * sonst landet die Toolbar mit ihren Wraps unter dem Footer-Edge. */
    #db-modal .modal-card.modal-card--wider {
        max-width: 100vw;
        width: 100vw;
    }
    /* DB-Vollansicht Scrollbalken
 auf Smartphone: dicker, tuerkis, immer sichtbar — vertikal + horizontal. */
    #db-modal .modal-body {
        scrollbar-color: var(--c-cyan, #3BBDE8) rgba(0,0,0,0.08);
        scrollbar-width: auto;
        overflow-y: scroll;
        overflow-x: auto;
    }
    #db-modal .modal-body::-webkit-scrollbar {
        width: 8px;   /* vertikal */
        height: 8px;  /* horizontal */
    }
    #db-modal .modal-body::-webkit-scrollbar-track {
        background: rgba(0,0,0,0.06);
        border-radius: 4px;
    }
    #db-modal .modal-body::-webkit-scrollbar-thumb {
        background: var(--c-cyan, #3BBDE8);
        border-radius: 4px;
        min-width: 32px;  /* verhindert zu schmales Thumb bei kurzem Inhalt */
        min-height: 32px;
    }
    #db-modal .modal-body::-webkit-scrollbar-thumb:hover {
        background: #2aa6cf;
    }
    #db-modal .modal-body::-webkit-scrollbar-corner {
        background: rgba(0,0,0,0.06);
    }
}

/* 8.7 — Mein-Konto: Passwort + Zeitzone vollständig sichtbar im Mobile.
 Inputs full-width, Labels/Hint-Text wrappen. Kein horizontales Scrollen.
 Submit-Button vollbreit damit er erreichbar bleibt.
 zusaetzlich modal-Card auf 100vw damit
 die Inputs nicht ueber das Card-Edge ragen. Plus alle Input-Typen
 (nicht nur password/text/select) abdecken — die alte Regel hatte
 `email`, `number`, `tel` nicht drin. */
@media (max-width: 768px) {
    #profile-modal .modal-card {
        max-width: 100vw !important;
        width: 100vw !important;
    }
    #profile-modal .field input,
    #profile-modal .field select,
    #profile-modal .field textarea {
        width: 100%;
        max-width: 100%;
        box-sizing: border-box;
    }
    #profile-modal .field {
        width: 100%;
        box-sizing: border-box;
    }
    #profile-modal .form-row {
        flex-wrap: wrap;
    }
    #profile-modal .modal-actions button.primary {
        width: 100%;
    }
    /* Modal-Body innen mit Padding, sonst kleben Inputs am Card-Edge. */
    #profile-modal .modal-body {
        padding-left: 12px !important;
        padding-right: 12px !important;
    }

    /* timezone-Select sah extrem
 * flach + abgeschnitten aus, Passwort-Inputs ebenso. Ursache: keine
 * explizite min-height fuer Inputs/Selects auf Mobile, plus die
 * native iOS-Select-Hoehe ist sehr klein wenn nur padding gesetzt
 * ist. Wir fixen es mit min-height: 44px (Touch-Standard) +
 * Font-Size 16px (vermeidet iOS-Safari Zoom-on-Focus) + appearance:
 * none + Custom-Chevron, damit das Select in beiden Themes gleich
 * gross dargestellt wird. Gleicher Min-Height fuer Inputs damit
 * Passwort-Felder auch klar sichtbar sind.
 * Ein frueherer Anlauf hat trotz !important nicht
 * gegriffen — vermutlich greifen weiter unten in der CSS andere
 * Regeln auf .field input mit hoeherer Spezifitaet (z.B. via
 * #profile-modal .modal-form .field input). Sicherheitshalber
 * alle Properties mit !important markieren und zusaetzlich
 * height-Wert (nicht nur min-height) setzen damit nichts mehr
 * stauchen kann. Plus inkl. type-spezifischer Selektoren. */
    #profile-modal .field input,
    #profile-modal .field input[type="text"],
    #profile-modal .field input[type="password"],
    #profile-modal .field input[type="email"],
    #profile-modal .field input[type="number"],
    #profile-modal .field input[type="tel"],
    #profile-modal .field input[type="url"],
    #profile-modal .field input[type="search"],
    #profile-modal .field select,
    #profile-modal .field textarea,
    #profile-modal .modal-form .field input,
    #profile-modal .modal-form .field select,
    #profile-modal .modal-form .field textarea,
    #profile-modal form .field input,
    #profile-modal form .field select,
    #profile-modal form .field textarea {
        min-height: 44px !important;
        height: 44px !important;
        font-size: 16px !important;
        line-height: 1.3 !important;
        padding: 10px 12px !important;
        box-sizing: border-box !important;
        display: block !important;
    }
    #profile-modal .field textarea,
    #profile-modal .modal-form .field textarea {
        height: auto !important;
        min-height: 88px !important;
    }
    #profile-modal .field select,
    #profile-modal .modal-form .field select {
        appearance: none !important;
        -webkit-appearance: none !important;
        -moz-appearance: none !important;
        background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2306829c' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") !important;
        background-repeat: no-repeat !important;
        background-position: right 10px center !important;
        background-size: 18px 18px !important;
        padding-right: 36px !important;
    }
    /* Fields untereinander mit klarem Abstand, kein Stauchen. */
    #profile-modal .field {
        margin-bottom: 4px;
    }
    /* Submit-Btn der inneren Forms (Zeitzone, Passwort) bekommt
     * touch-freundliche Hoehe. */
    #profile-modal .modal-body form.modal-form .modal-actions {
        margin-top: 12px;
    }
    #profile-modal .modal-body form.modal-form .modal-actions button {
        min-height: 44px;
        font-size: 15px;
    }
    /* H4-Sections im profile-modal-body bekommen oben etwas mehr
     * Luft, damit Speicher/Daten/Loeschen klar trennbar bleiben. */
    #profile-modal .profile-modal-body h4 {
        margin-top: 18px;
        font-size: 1.05em;
    }
    #profile-modal .profile-modal-body h4:first-child {
        margin-top: 0;
    }
}

/* ConfirmDialog mit X oben rechts +
 centered primary-wide statt zwei Buttons unten. */
.confirm-card.confirm-card--has-close {
    position: relative;
    padding-top: 36px;
}
.confirm-card .confirm-close {
    position: absolute;
    top: 8px;
    right: 8px;
}
.modal-actions.modal-actions--center {
    justify-content: center;
}

/* Help-Modal-Suche im Footer. */
.help-modal-footer {
    align-items: center;
    gap: 12px;
}
.help-modal-search {
    display: flex;
    align-items: center;
    gap: 6px;
    background: var(--input-bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 4px 8px;
    flex: 0 1 260px;
}
.help-modal-search-icon {
    color: var(--muted-hi);
    flex-shrink: 0;
}
.help-modal-search-input {
    border: none;
    background: transparent;
    outline: none;
    color: var(--fg);
    font-size: 0.92em;
    flex: 1;
    min-width: 0;
}
.help-modal-search-input:focus {
    outline: none;
}
mark.help-search-hit {
    background: var(--c-amber, #f0c270);
    color: #1f1500;
    padding: 0 1px;
    border-radius: 2px;
}
@media (max-width: 768px) {
    .help-modal-footer {
        flex-wrap: wrap;
    }
    .help-modal-search {
        flex-basis: 100%;
        order: -1;
    }
}

/* Presence-Bar als floating Container unten rechts
 neben dem Locate-Control. Chips horizontal, wrap bei vielen Usern.
 Tuerkise Border = User teilt Location (klickbar = pan-to-user). */
.presence-bar.presence-bar--floating {
    position: fixed;
    right: 8px;
    bottom: 110px;  /* knapp ueber Scale-Bar + Zoom-Steuerung */
    z-index: 900;   /* sehr hoch - ueber leaflet-controls (z 600/800) */
    display: flex !important;  /* hidden-Attribut bleibt ueber [hidden] respektiert */
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 4px;
    pointer-events: auto;
    max-width: 260px;
    /* ein Mini-Border-Frame, sonst sieht der "0 weitere"-
 Chip wie ein verirrter Tooltip aus und ist schwer zu finden. */
    visibility: visible !important;
}
.presence-bar.presence-bar--floating[hidden] { display: none !important; }
.presence-bar.presence-bar--floating[hidden] {
    display: none;
}
.presence-bar.presence-bar--floating .presence-chip {
    cursor: default;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
}
.presence-bar.presence-bar--floating .presence-chip.is-location-sharing,
.sidebar-presence-list .presence-chip.is-location-sharing {
    /* Coral statt tuerkis als Indikator-Border,
 pulsiert. Click pant zur Position des Users. */
    cursor: pointer;
    border: 2px solid #ff7f7f;  /* coral */
    box-shadow: 0 0 0 0 rgba(255, 127, 127, 0.55),
                0 2px 6px rgba(0, 0, 0, 0.18);
    transition: transform 0.12s ease;
    animation: presence-chip-location-pulse 1.8s ease-in-out infinite;
}
@keyframes presence-chip-location-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(255, 127, 127, 0.55),
                           0 2px 6px rgba(0, 0, 0, 0.18); }
    50%      { box-shadow: 0 0 0 6px rgba(255, 127, 127, 0),
                           0 2px 6px rgba(0, 0, 0, 0.18); }
}
.presence-bar.presence-bar--floating .presence-chip.is-location-sharing:hover,
.sidebar-presence-list .presence-chip.is-location-sharing:hover {
    transform: scale(1.08);
}
.sidebar-presence-list .presence-chip {
    cursor: default;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
}
/* "0 weitere"-Hinweis fuer leere Presence-Bar. */
.presence-empty-hint {
    background: var(--bg-card);
    color: var(--muted-hi);
    font-size: 0.75em;
    padding: 4px 10px;
    border: 1px solid var(--border);
    border-radius: 999px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
    white-space: nowrap;
    pointer-events: auto;
}
[data-theme="dark"] .presence-empty-hint {
    background: var(--surface-elevated, #1f2024);
    color: #aab;
    border-color: rgba(255,255,255,0.18);
}
@media (max-width: 768px) {
    .presence-bar.presence-bar--floating {
        bottom: 96px;
        max-width: 50vw;
    }
}

/* =====================================================================
 Karten-Suche (Geocoding via Nominatim-Proxy)
 ----------------------------------------------------------------------
 Position links neben der Geoman-Toolbar (oben links). Box
 ist standardmaessig hidden; erst Klick auf Toggle-Btn faltet das
 Input-Feld auf. Dark-Mode-Hintergrund opak (vorher transparent +
 schlecht lesbar).
 ===================================================================== */
.map-search {
    /* Relativ zum Map-Container (mountGeocoding haengt das Element
 jetzt in map.getContainer ein), nicht zum Window. So bleibt der
 Search-Btn IMMER ueber der Karte und verschwindet nicht unter dem
 Sidepanel. von oben-links nach UNTEN-LINKS verschoben, neben den
 Locate-Btn (bottomleft). */
    position: absolute;
    bottom: 9px;
    left: 46px;
    top: auto;
    z-index: 800;
    display: flex !important;
    align-items: flex-end;
    gap: 4px;
    pointer-events: auto;
    visibility: visible !important;
}
/* Wenn die Search-Box offen ist, soll sie
 * ueber allen Karten-Controls liegen (Layers, Scale, Attribution,
 * GPS-Tools usw.). Unter Modals (z 2000) bleibt sie weiterhin. */
.map-search.is-open {
    z-index: 1900;
}
.map-search,
.map-search > * { box-sizing: border-box; }
/* Search-Toggle pixel-identisch zur Geoman-Toolbar
 (siehe .leaflet-pm-toolbar a). Gleicher BG, Border-Radius, 30x30,
 tuerkiser Hover-Border. */
.map-search-toggle {
    /* 30->32 px damit der Btn
 * etwas dominanter wirkt (matched besser den Locate-Btn nebenan).
 * min-height + min-width explizit auf 32
 * gesetzt, sonst greift die globale `button { min-height: 40 }`-Regel
 * aus `@media (hover: none) and (pointer: coarse)` und stretcht den
 * Btn auf 32x40. Das ist die ueberall-greifende Variante (Mobile +
 * Tablet + Desktop). */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    min-width: 32px;
    min-height: 32px;
    border-radius: var(--radius-sm, 6px);
    border: 1px solid var(--border);
    background: var(--bg-elevated);
    color: var(--fg);
    cursor: pointer;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
[data-theme="dark"] .map-search-toggle {
    background: #20262a;
}
.map-search-toggle svg { width: 16px; height: 16px; }
.map-search-toggle:hover {
    background: var(--bg-elevated);
    color: var(--c-primary-hi);
    border-color: var(--c-primary-hi);
}
[data-theme="dark"] .map-search-toggle:hover {
    background: #2b3338;
    border-color: var(--c-primary-hi);
}
.map-search.is-open .map-search-toggle {
    color: var(--c-primary-hi);
    background: var(--bg-elevated);
    border-color: var(--c-primary-hi);
}
.map-search-box {
    display: flex;
    align-items: center;
    /* Hintergrund opak gegen Karte/Tiles, sonst im Dark-Mode
 fast nicht lesbar. surface-elevated als Fallback, sonst bg-card.
 padding reduziert (4px 8px -> 0 6px) damit die Box
 die gleiche 30px-Hoehe wie der Toggle-Btn + die Geoman-Btns hat. */
    background: var(--surface-elevated, var(--bg-card));
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0 6px;
    box-shadow: var(--shadow-card);
    min-width: 240px;
    max-width: 360px;
    position: relative;
    height: 30px;
}
/* Box explizit ausblenden wenn [hidden] gesetzt. UA-Default
 reicht meistens, aber das `display: flex` der Eltern macht das manchmal
 wackelig. */
.map-search-box[hidden] { display: none !important; }
[data-theme="dark"] .map-search-box,
[data-theme="dark"] .map-search-dropdown {
    background: var(--surface-elevated, #1f2024);
}
.map-search-icon {
    color: var(--muted-hi);
    flex-shrink: 0;
    margin-right: 6px;
    display: inline-flex;
    align-items: center;
}
.map-search-input {
    /* Input-Hoehe exakt auf 28px (die Box ist 30 - 2px Border),
 padding/margin auf 0, damit die Gesamthoehe gleich der Geoman-Btns
 (30px) bleibt. Font-Size bleibt unveraendert. */
    flex: 1;
    border: none;
    background: transparent;
    outline: none;
    color: var(--fg);
    font-size: 0.95em;
    min-width: 0;
    height: 28px;
    padding: 0;
    margin: 0;
    line-height: 28px;
}
.map-search-clear {
    background: transparent;
    border: none;
    color: var(--muted-hi);
    cursor: pointer;
    padding: 4px;
    display: inline-flex;
    align-items: center;
    border-radius: var(--radius-sm);
}
.map-search-clear:hover { color: var(--fg); }
.map-search-dropdown {
    /* Da der Search-Toggle jetzt unten links
 * sitzt, gibt es nach unten keinen Platz fuer die Vorschlagsliste —
 * sie wuerde aus dem Map-Container fallen. Wir flippen das Dropdown
 * deshalb nach OBEN: `bottom: calc(100% + 4px)` plus `top: auto`. */
    position: absolute;
    top: auto;
    bottom: calc(100% + 4px);
    left: 0;
    right: 0;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    z-index: 1900;
    box-shadow: var(--shadow-card);
    max-height: 60vh;
    overflow-y: auto;
    z-index: 1;
}
.map-search-empty {
    padding: 10px 12px;
    color: var(--muted-hi);
    font-size: 0.9em;
}
.map-search-empty--err { color: var(--c-warn, #c33); }
.map-search-result {
    display: block;
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    padding: 8px 12px;
    cursor: pointer;
    color: var(--fg);
    border-bottom: 1px solid var(--border);
    transition: background 0.08s;
}
.map-search-result:last-child { border-bottom: none; }
.map-search-result:hover {
    background: var(--bg-elevated, rgba(0, 0, 0, 0.04));
}
.map-search-result-name {
    font-size: 0.92em;
    line-height: 1.3;
}
.map-search-result-meta {
    margin-top: 2px;
    font-size: 0.78em;
    color: var(--muted-hi);
}
.map-search-pin {
    background: transparent !important;
    border: none !important;
    pointer-events: auto;
}
.map-search-popup { font-size: 0.9em; line-height: 1.3; max-width: 220px; }

@media (max-width: 768px) {
    /* Toggle-Btn sitzt unten-links neben dem Locate-Btn (siehe
     * Standard-Block `bottom: 9px; left: 46px`). Wir lassen die
     * Standard-Positionierung greifen und beschraenken nur die
     * innere Search-Box-Breite. */
    .map-search-box {
        min-width: 0;
        max-width: calc(100vw - 80px);
        width: auto;
    }
}

/* Feature-Modal-Footer (.modal-actions) sticky am unteren
 Rand. Der Form-Inhalt scrollt darueber, der Footer mit Speichern/
 Verwerfen/Loeschen bleibt immer sichtbar. */
#feature-modal .modal-card {
    /* breiteres Modal damit Footer-Buttons + Inhalt
 horizontal nicht ueberlaufen. Vorher: 480px Default. */
    max-width: 640px;
    width: min(640px, calc(100vw - 24px));
    overflow-x: hidden;
    overflow-y: hidden;  /* Card selbst scrollt nicht — die Form drin */
    height: auto;
    max-height: calc(100dvh - 16px);
    /* Card ist 3-Slot-Layout: Header (Title) -> Form
 (scrollable Body) -> Footer (modal-actions). Display:flex column
 kommt schon aus der globalen .modal-card-Definition. */
}
/* Form scrollt intern wenn ihr Content die verbleibende
 Card-Hoehe ueberlaeuft. Footer (.modal-actions als Geschwister, NICHT
 mehr in der Form) bleibt visuell ausserhalb des Scroll-Containers —
 damit landet der Scrollbar NUR neben dem Form-Body, nicht neben den
 Buttons. */
#feature-modal .modal-card > .modal-form {
    flex: 1 1 auto;
    overflow-y: auto;
    overflow-x: hidden;
    min-height: 0;
}
/* Footer als Geschwister der Form. KEIN sticky mehr —
 er ist sowieso schon ausserhalb des Scroll-Containers. */
#feature-modal .modal-card > .modal-actions {
    flex: 0 0 auto;
    /* Identisch zum Card-Hintergrund — der Footer wirkt visuell als
       Erweiterung der Card-Flaeche. */
    background: var(--bg-elevated);
    /* Bottom-margin auf 0 — der Footer sitzt sauber im unteren
 Card-Padding-Bereich, gibt den 22px-Padding-Abstand zum Card-Rand
 frei. */
    margin: 8px -24px 0;
    padding: 12px 24px env(safe-area-inset-bottom, 12px);
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    justify-content: flex-end;
    align-items: center;
}
/* modal-actions ist jetzt Geschwister der Form
 (nicht mehr drinnen). Die obigen #feature-modal .modal-card >
 .modal-actions-Regeln gelten. */
/* Immortal-Checkbox-Label im Footer. Kompakt, mit
 kleinem Shield-Icon. Zwischen Loeschen und Abbrechen platziert. */
.feature-modal-immortal-footer-label {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    cursor: pointer;
    color: var(--muted-hi, #555);
    font-size: 0.85em;
    user-select: none;
    /* Loeschen-Btn hat margin-right:auto inline — das pusht alles
       Andere nach rechts. Immortal-Label sitzt in der rechten Gruppe
       direkt vor Cancel. */
    padding: 4px 6px;
    border-radius: var(--radius-sm, 4px);
    border: 1px solid transparent;
}
/* HTML-[hidden]-Attribut MUSS gegen inline-flex stechen,
 sonst bleibt das Label sichtbar wenn JS hidden=true setzt. Auch fuer
 die alte versteckte Spiegel-Checkbox + den Clone-Btn. */
.feature-modal-immortal-footer-label[hidden],
#feature-modal-clone-btn[hidden],
#feature-modal-immortal-cb[hidden] {
    display: none !important;
}
.feature-modal-immortal-footer-label:hover {
    background: var(--bg-card);
    border-color: var(--border);
}
.feature-modal-immortal-footer-label input[type="checkbox"]:checked + svg {
    color: var(--c-primary, #06829c);
}
.feature-modal-immortal-footer-label input[type="checkbox"]:checked ~ span {
    color: var(--c-primary, #06829c);
    font-weight: 600;
}

/* Retro-Tool-Icons groesser + Dark-Mode-invert. */
.retro-tool-btn .retro-tool-icon-img {
    width: 22px;
    height: 22px;
    display: block;
}
[data-theme="dark"] .retro-tool-btn .retro-tool-icon-img {
    filter: invert(0.9) hue-rotate(180deg);
}

/* Mask-basierte Icon-Variante fuer retro-surface-plane +
 retro-surface. Die externen SVG-Dateien haben hartkodierte Farben;
 wir nutzen sie als CSS-Mask, damit das Icon currentColor folgt (wie
 die anderen retro-Buttons mit inline-SVG + stroke="currentColor"). */
.retro-tool-btn .retro-tool-icon-mask {
    display: inline-block;
    width: 22px;
    height: 22px;
    background-color: currentColor;
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-size: contain;
            mask-size: contain;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    vertical-align: middle;
}
.retro-tool-icon-mask--plane {
    -webkit-mask-image: url("/img/icons/icon_3d_plane.svg");
            mask-image: url("/img/icons/icon_3d_plane.svg");
}
.retro-tool-icon-mask--heatmap {
    -webkit-mask-image: url("/img/icons/icon_heatmap.svg");
            mask-image: url("/img/icons/icon_heatmap.svg");
}

/* Up/Down-Pfeile pro Overlay im Leaflet-Layer-Control. */
.geolyser-layer-sort-arrows {
    display: inline-flex;
    flex-direction: column;
    gap: 1px;
    margin-left: 4px;
    vertical-align: middle;
}
.geolyser-layer-sort-arrow {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 2px;
    color: var(--muted-hi);
    cursor: pointer;
    font-size: 8px;
    line-height: 1;
    padding: 1px 3px;
    width: 18px;
}
.geolyser-layer-sort-arrow:hover {
    color: var(--c-primary, #36b);
    border-color: var(--c-primary, #36b);
}

/* Inkognito-Modus-Button in Topbar (emerald). */
#incognito-btn {
    color: #10b981;  /* emerald-500 */
}
#incognito-btn:hover { color: #059669; }
[data-theme="dark"] #incognito-btn {
    color: #34d399;  /* emerald-400 */
}
[data-theme="dark"] #incognito-btn:hover { color: #6ee7b7; }
#incognito-btn.is-active {
    background: #10b981;
    color: #fff;
    border-color: #047857;
    box-shadow: inset 0 0 0 2px #064e3b;
}
[data-theme="dark"] #incognito-btn.is-active {
    background: #34d399;
    color: #052e16;
    border-color: #10b981;
}
/* Sprach-Toggle in violet. */
#lang-toggle {
    color: #8b5cf6;  /* violet-500 */
}
#lang-toggle:hover { color: #7c3aed; }
[data-theme="dark"] #lang-toggle {
    color: #a78bfa;  /* violet-400 */
}
[data-theme="dark"] #lang-toggle:hover { color: #c4b5fd; }
#incognito-btn.is-active::after {
    content: "";
    display: block;
    position: absolute;
    width: 6px; height: 6px;
    background: #ff4081;
    border-radius: 50%;
    top: 4px; right: 4px;
    box-shadow: 0 0 0 2px var(--bg-card);
}
#incognito-btn { position: relative; }

/* ============================================================
 * Landscape-Layout fuer Mobile/Tablet
 * ------------------------------------------------------------
 * Im Querformat ist die Hoehe knapp, die Breite ueppig — eine
 * vertikale Geoman-Toolbar mit 8-12 Buttons + GPS-Tools darunter
 * sprengt den sichtbaren Bereich vertikal. Wir drehen beide
 * Toolbars horizontal damit alle Buttons im Bild bleiben.
 * Greift NUR auf Mobile/Tablet (max-width: 1024) im Landscape.
 * ============================================================ */
@media (orientation: landscape) and (max-width: 1024px) {
    /* Geoman-Toolbar (PM = Phase Manager) — horizontal flex. */
    .leaflet-pm-toolbar {
        display: flex !important;
        flex-direction: row !important;
        flex-wrap: wrap !important;
    }
    .leaflet-pm-toolbar a {
        margin-bottom: 0 !important;
        margin-right: 4px !important;
        margin-top: 0 !important;
    }
    .leaflet-pm-toolbar a:last-child {
        margin-right: 0 !important;
    }
    /* Geoman-Action-Bar (Cancel / Trash / Finish) im Drag-Mode bleibt
     * unter dem Tool-Btn — kein Override hier noetig. */

    /* GPS-Tools (Marker / Track-Line / Track-Polygon) horizontal.
     * Sowohl der Standalone-#gps-tools-Container als auch die Leaflet-
     * Control-Variante (.leaflet-bottom .gps-tools) abdecken. */
    #gps-tools.gps-tools,
    #map > #gps-tools.gps-tools,
    .leaflet-bottom .gps-tools {
        flex-direction: row !important;
        gap: 4px;
        align-items: center !important;
    }
    /* Status-Badge des GPS-Tracks zeigt sonst nach rechts vom Stack —
     * im horizontalen Layout will der User ihn unter den Buttons. */
    .leaflet-bottom .gps-tools .gps-tools-status,
    #gps-tools .gps-tools-status {
        position: absolute !important;
        left: 0 !important;
        bottom: calc(100% + 6px) !important;
        right: auto !important;
        top: auto !important;
        white-space: nowrap;
    }
}

/* Pending-Delete-Section unter Users-Tabelle. */
.admin-pending-delete {
    margin-top: 24px;
    padding-top: 16px;
    border-top: 2px dashed var(--border);
}
.admin-pending-delete-title {
    margin: 0 0 6px;
    font-size: 1.0em;
    color: var(--c-warn, #c93);
}
.admin-pending-delete-table {
    margin-top: 8px;
}

/* Snapshot-Modal Filter + Load-More. */
.snap-filter-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin: 8px 0;
}
.snap-filter-row label {
    font-size: 0.85em;
    color: var(--muted-hi);
    display: flex;
    align-items: center;
    gap: 4px;
}
.snap-filter-row input[type="date"] {
    padding: 3px 6px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--input-bg, var(--bg-card));
    color: var(--fg);
}
.snap-loadmore-row {
    text-align: center;
    margin-top: 10px;
}

/* Server-Side-Cluster-Marker. */
.geolyser-cluster {
    background: transparent !important;
    border: none !important;
    pointer-events: auto;
}
.geolyser-cluster-inner {
    border-radius: 50%;
    color: #fff;
    text-align: center;
    font-weight: 600;
    font-size: 0.85em;
    box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.7),
                0 2px 6px rgba(0, 0, 0, 0.25);
    user-select: none;
    cursor: pointer;
    transition: transform 0.12s;
}
.geolyser-cluster-inner:hover { transform: scale(1.08); }

/* (Die alte .map-undo-bar unten rechts wurde entfernt — der
   sichtbare Undo-Button lebt jetzt kontextuell in der Geoman-Toolbar neben
   "Beenden", s. .geolyser-pm-undo.) */

/* Zeitfilter-Popover im DB-Modal. */
.db-time-popover {
    padding: 10px 12px;
    min-width: 280px;
}
.db-time-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
    flex-wrap: wrap;
}
.db-time-row label {
    min-width: 80px;
    font-size: 0.88em;
    color: var(--muted-hi);
}
.db-time-row input[type="date"] {
    flex: 1 1 110px;
    min-width: 110px;
    padding: 4px 6px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--input-bg, var(--bg-card));
    color: var(--fg);
}
.db-time-presets {
    display: flex;
    gap: 6px;
    margin-top: 6px;
    flex-wrap: wrap;
}

/* Kompakte Bulk-Edit-Reihen. Jede Sektion
 * eine flex-row mit Label links + Controls inline daneben. Wraps wenn
 * der Container zu schmal wird (Smartphone). */
.bulk-edit-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    min-height: 36px;
}
.bulk-edit-row-label {
    flex: 0 0 auto;
    min-width: 70px;
    color: var(--muted-hi);
    font-weight: 500;
}
.bulk-edit-row > *:not(.bulk-edit-row-label) {
    flex: 1 1 auto;
    min-width: 0;
}
/* Bulk-Tag-Picker inline mit Label: braucht relative-Position fuer
 * den absoluten suggestions-Dropdown. */
.bulk-edit-tag-block .bulk-edit-tag-picker {
    flex: 1 1 200px;
    min-width: 0;
}
/* Color-Tiles + Marker-Tiles im inline-Layout: max-width damit sie
 * nicht den ganzen restlichen Platz fressen. */
.bulk-edit-color-block .bulk-edit-color-tiles,
.bulk-edit-marker-block .bulk-edit-marker-tiles {
    flex: 1 1 auto;
    min-width: 0;
}

/* Unified Bulk-Tag-Picker — alle Tags der
 * Selection als Chips mit X (Klick = pending-remove, durchgestrichen
 * mit ↩-Btn zum Zurueckholen). Neue Tags via Input+Enter werden mit
 * is-add hervorgehoben (cyan-Akzent). Pattern wie Feature-Edit-Modal.
 *
 * KEIN flex-direction: column auf dem
 * .bulk-edit-tag-block (sonst landet die Picker-Box untereinander mit
 * dem Label und der `flex: 1 1 auto`-Vererbung vom .bulk-edit-row laesst
 * den Picker auf VERTIKALER Hauptachse wachsen → 200px hohe Leerflaeche).
 * Inline-Layout aus .bulk-edit-row erbt sauber, Picker bekommt feste
 * Max-Hoehe + overflow:auto fuer die Chip-Liste. */
.bulk-edit-tag-block {
    /* Inline-Layout aus .bulk-edit-row (display:flex; align-items:center)
     * NICHT mehr ueberschreiben. */
}
.bulk-edit-tag-picker {
    position: relative;
    /* Picker darf breit werden (~3x so breit wie vorher), aber nicht
     * mehr als 64px hoch. Chip-Area scrollt intern wenn nötig. */
    max-height: 64px;
    overflow: hidden;
    flex: 1 1 600px !important;
    min-width: 0;
    padding: 4px 8px;
    gap: 4px;
}
.bulk-edit-tag-picker .tag-picker-chips {
    max-height: 28px;
    overflow-y: auto;
    overflow-x: hidden;
    align-content: flex-start;
    padding-right: 2px;
}
.bulk-edit-tag-picker .tag-picker-input {
    min-height: 22px;
}
/* Suggestion-Dropdown soll relativ zum Picker positioniert sein und
 * UEBER das Modal layern (z-index hoeher als Bulk-Edit-Liste). */
.bulk-edit-tag-picker .tag-picker-suggestions {
    z-index: 60;
}
.bulk-tag-chip {
    transition: opacity 0.12s, text-decoration 0.12s;
}
.bulk-tag-chip.is-remove-pending {
    opacity: 0.55;
    text-decoration: line-through;
    border-color: var(--c-error, #c44) !important;
}
.bulk-tag-chip.is-remove-pending .bulk-tag-chip-label {
    color: var(--c-error, #c44);
}
.bulk-tag-chip.is-add {
    border-color: var(--c-cyan, #3BBDE8) !important;
    background: rgba(59, 189, 232, 0.10);
}
.bulk-tag-chip.is-add .bulk-tag-chip-label {
    color: var(--c-cyan, #3BBDE8);
    font-weight: 500;
}
.bulk-tag-chip.is-locked {
    opacity: 0.7;
    font-style: italic;
}
/* v2 Chip-Body als Klick-Bereich fuer das
 * Tag-Color-Popup. Swatch-Dot zeigt aktuelle Tag-Farbe. */
.bulk-tag-chip-body {
    background: transparent;
    border: none;
    padding: 0;
    margin: 0;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: inherit;
    font: inherit;
}
.bulk-tag-chip-body:hover .bulk-tag-chip-label {
    text-decoration: underline;
}
.bulk-tag-chip-swatch {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    border: 1px solid var(--border);
    flex-shrink: 0;
}
.bulk-tag-chip-swatch--none {
    background: transparent;
    border-style: dashed;
}

/* Bulk-Name-Block — Mode + X + Y nebeneinander.
 * Mode-Select hat feste Breite, beide Inputs kompakt (1 1 110px) damit
 * X und Y auf derselben Zeile bleiben. Bei "replace"-Mode wird X via JS
 * eingeblendet. */
.bulk-edit-name-block {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.bulk-edit-name-mode {
    min-width: 150px;
    max-width: 200px;
}
.bulk-edit-name-input--compact {
    flex: 1 1 110px;
    min-width: 80px;
    max-width: 180px;
}
@media (max-width: 600px) {
    .bulk-edit-name-block {
        flex-direction: row;
        align-items: stretch;
        gap: 4px;
    }
    .bulk-edit-name-input--compact {
        flex: 1 1 90px;
    }
}

/* Bulk-Marker-Block — alle Icons in EINER Zeile
 * unter dem Farb-Picker. flex-wrap-Default damit bei sehr schmalen
 * Viewports doch umgebrochen wird, aber default versucht Inline-Row. */
.bulk-edit-marker-block {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.bulk-edit-marker-tiles {
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: wrap;
    gap: 4px;
    align-items: center;
}
/* ALLE Bulk-Marker-Tiles einheitlich 36x36 -
   None, Clear, Symbol-Tiles, Custom-Icons und der "…"-Button. Innere
   SVG/IMG werden auf eine gemeinsame Hoehe begrenzt und zentriert. */
.bulk-edit-marker-tiles .marker-icon-pick {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    padding: 0;
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.bulk-edit-marker-tiles .marker-icon-pick svg,
.bulk-edit-marker-tiles .marker-icon-pick img {
    width: auto;
    height: 26px;
    max-width: 28px;
    object-fit: contain;
    display: block;
}
.bulk-marker-tile-none,
.bulk-marker-tile-clear {
    font-size: 1.2em;
    color: var(--muted-hi);
}
.bulk-marker-tile-none.is-active,
.bulk-marker-tile-clear.is-active {
    color: var(--c-cyan, #3BBDE8);
    border-color: var(--c-cyan, #3BBDE8) !important;
}

/* Loading-Overlay waehrend Bulk-Apply.
 * Liegt absolut innerhalb der Modal-Card (.modal-card hat
 * position:relative). Semi-transparenter Layer + Spinner aus dem
 * bestehenden .spinner-Pattern (siehe Zeile ~1016). Eingeblendet via
 * JS durch entfernen des [hidden]-Attributs. */
.bulk-edit-loading {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    z-index: 50;
    border-radius: var(--radius-lg);
    color: #fff;
    pointer-events: all;
}
.bulk-edit-loading[hidden] { display: none; }
.bulk-edit-loading .spinner {
    width: 36px; height: 36px;
    border-width: 3px;
    border-color: rgba(255,255,255,0.25);
    border-top-color: var(--c-cyan, #3BBDE8);
}
.bulk-edit-loading-text {
    font-size: 0.95em;
    font-weight: 500;
    color: #fff;
    text-shadow: 0 1px 2px rgba(0,0,0,0.45);
    text-align: center;
    padding: 0 14px;
}
[data-theme="light"] .bulk-edit-loading {
    background: rgba(255, 255, 255, 0.75);
    color: var(--fg);
}
[data-theme="light"] .bulk-edit-loading-text {
    color: var(--fg);
    text-shadow: none;
}

/* =====================================================================
 * End-of-file Override fuer Mess-Tool-Btns
 * + Geoman pm-actions auf Smartphone
 * ---------------------------------------------------------------------
 * Die Hover-Regeln lagen VOR der Hoehen-Regel
 * (Zeile 2431) und wurden bei gleicher Specificity + !important durch
 * Source-Order ueberschrieben. Beenden blieb 26px, Vollbild 32px,
 * Vollbild-Farbe falsch . Plus: display:flex !important auf
 * .geolyser-measure-actions-container hat die JS-inline-display:none-
 * Steuerung uebersteuert → permanent sichtbar. Hier final am Datei-
 * Ende rauschen — wins by source order auch ohne extra !important.
 * ===================================================================== */
@media (max-width: 768px) {
    /* Mess-Tool-Actions-Container vertikal-stapeln, aber Visibility
     * weiter via JS (measurement-tool.js _setActionsVisible). display
     * OHNE !important damit JS-inline `style.display = "none"` greift. */
    .geolyser-measure-actions-container {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: 4px;
    }
    /* Beide Mess-Btns exakt 160x30, gleiche Farbe + Border. Selektor
     * mit doppelter Class + .leaflet-touch sicher hochgezogen. */
    .leaflet-bar a.geolyser-measure-end-action,
    .leaflet-bar a.geolyser-measure-maximize-action,
    .leaflet-touch .leaflet-bar a.geolyser-measure-end-action,
    .leaflet-touch .leaflet-bar a.geolyser-measure-maximize-action,
    a.geolyser-measure-end-action,
    a.geolyser-measure-maximize-action {
        display: block !important;
        width: 160px !important;
        min-width: 160px !important;
        height: 30px !important;
        line-height: 28px !important;
        padding: 0 12px !important;
        font-size: 0.86em !important;
        text-align: center !important;
        margin: 0 !important;
        box-sizing: border-box !important;
        border-radius: 3px !important;
        background-color: var(--bg-elevated) !important;
        color: var(--text) !important;
        border: 1px solid var(--border) !important;
    }
    [data-theme="dark"] .leaflet-bar a.geolyser-measure-end-action,
    [data-theme="dark"] .leaflet-bar a.geolyser-measure-maximize-action,
    [data-theme="dark"] a.geolyser-measure-end-action,
    [data-theme="dark"] a.geolyser-measure-maximize-action {
        background-color: #2c2f33 !important;
        color: #eaeaea !important;
        border-color: rgba(255, 255, 255, 0.10) !important;
    }
    /* Hover + Touch-Active fuer beide Mess-Btns gleich. */
    .leaflet-bar a.geolyser-measure-end-action:hover,
    .leaflet-bar a.geolyser-measure-end-action:focus,
    .leaflet-bar a.geolyser-measure-maximize-action:hover,
    .leaflet-bar a.geolyser-measure-maximize-action:focus,
    a.geolyser-measure-end-action:hover,
    a.geolyser-measure-end-action:focus,
    a.geolyser-measure-maximize-action:hover,
    a.geolyser-measure-maximize-action:focus {
        background-color: var(--bg-card) !important;
        color: var(--c-primary-hi) !important;
        border-color: var(--c-primary-hi) !important;
        cursor: pointer;
    }
    [data-theme="dark"] .leaflet-bar a.geolyser-measure-end-action:hover,
    [data-theme="dark"] .leaflet-bar a.geolyser-measure-end-action:focus,
    [data-theme="dark"] .leaflet-bar a.geolyser-measure-maximize-action:hover,
    [data-theme="dark"] .leaflet-bar a.geolyser-measure-maximize-action:focus,
    [data-theme="dark"] a.geolyser-measure-end-action:hover,
    [data-theme="dark"] a.geolyser-measure-end-action:focus,
    [data-theme="dark"] a.geolyser-measure-maximize-action:hover,
    [data-theme="dark"] a.geolyser-measure-maximize-action:focus {
        background-color: #3b3f44 !important;
        color: var(--c-primary-hi) !important;
        border-color: var(--c-primary-hi) !important;
    }
    .leaflet-bar a.geolyser-measure-end-action:active,
    .leaflet-bar a.geolyser-measure-maximize-action:active,
    a.geolyser-measure-end-action:active,
    a.geolyser-measure-maximize-action:active {
        background-color: var(--c-cyan, #3BBDE8) !important;
        color: #fff !important;
        border-color: var(--c-cyan, #3BBDE8) !important;
    }

    /* Geoman pm-actions: Hover + Touch-Active (klick-Feedback). End-
     * of-file Position garantiert dass diese Regeln gewinnen. */
    .leaflet-pm-toolbar .button-container .leaflet-pm-actions-container
        .leaflet-pm-action:hover,
    .leaflet-pm-toolbar .button-container .leaflet-pm-actions-container
        .leaflet-pm-action:focus {
        background-color: var(--bg-card) !important;
        color: var(--c-primary-hi) !important;
        border-color: var(--c-primary-hi) !important;
        cursor: pointer !important;
        text-decoration: none !important;
    }
    [data-theme="dark"] .leaflet-pm-toolbar .button-container
        .leaflet-pm-actions-container .leaflet-pm-action:hover,
    [data-theme="dark"] .leaflet-pm-toolbar .button-container
        .leaflet-pm-actions-container .leaflet-pm-action:focus {
        background-color: #3b3f44 !important;
        color: var(--c-primary-hi) !important;
        border-color: var(--c-primary-hi) !important;
    }
    .leaflet-pm-toolbar .button-container .leaflet-pm-actions-container
        .leaflet-pm-action:active {
        background-color: var(--c-cyan, #3BBDE8) !important;
        color: #fff !important;
        border-color: var(--c-cyan, #3BBDE8) !important;
    }
}

/* =====================================================================
 * pm-actions auch im LANDSCAPE vertikal
 * ---------------------------------------------------------------------
 * Smartphone-Landscape hat Viewport-Width > 768px (z.B. iPhone 932px
 * horizontal), die 768px-Regeln greifen also nicht. Trotzdem ist die
 * Toolbar horizontal, und pm-actions wurden default
 * horizontal angeordnet → Buttons zu schmal, Texte ueberlappen.
 * Selbe Vertikal-Stapel + 160x30 + Hover/Active wie im 768px-Block,
 * hier zusaetzlich fuer orientation:landscape + max-width:1024px.
 * ===================================================================== */
@media (orientation: landscape) and (max-width: 1024px) {
    /* Im Landscape sitzt
 * die Toolbar oben — bei vertikalem Stapel mit top:50% +
 * translateY(-50%) (aus ) wandert der erste Btn
 * ("Beenden") oberhalb der Toolbar aus dem Viewport. Im Landscape
 * Actions UNTER den Btn anker statt zentriert. */
    .leaflet-pm-toolbar .button-container .leaflet-pm-actions-container {
        display: flex !important;
        flex-direction: column !important;
        align-items: stretch;
        gap: 4px;
        top: calc(100% + 4px) !important;
        left: 0 !important;
        right: auto !important;
        transform: none !important;
    }
    /* Mess-Tool-Actions-Container im Landscape ebenfalls UNTER dem
     * Mess-Btn anker (selber Grund). */
    .geolyser-measure-actions-container {
        top: calc(100% + 4px) !important;
        left: 0 !important;
        right: auto !important;
        transform: none !important;
    }
    .leaflet-pm-toolbar .button-container .leaflet-pm-actions-container
        .leaflet-pm-action {
        display: block !important;
        white-space: nowrap;
        padding: 0 12px !important;
        font-size: 0.86em !important;
        height: 30px !important;
        line-height: 30px !important;
        min-width: 160px;
        text-align: center;
        margin: 0 !important;
        box-sizing: border-box;
        border-radius: 3px !important;
        border-right: 1px solid var(--border) !important;
    }
    /* Inaktive pm-actions weiter ausblenden — Aequivalent zur Defensiv-Regel oben
     * fuer Landscape (sonst zeigen alle Tools ihre Actions parallel). */
    .leaflet-pm-toolbar .button-container:not(.active) .leaflet-pm-actions-container {
        display: none !important;
    }
    /* Mess-Tool Actions-Container vertikal, OHNE !important auf display
     * — JS-Inline-style.display: none soll weiter wirken. */
    .geolyser-measure-actions-container {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: 4px;
    }
    .leaflet-bar a.geolyser-measure-end-action,
    .leaflet-bar a.geolyser-measure-maximize-action,
    .leaflet-touch .leaflet-bar a.geolyser-measure-end-action,
    .leaflet-touch .leaflet-bar a.geolyser-measure-maximize-action,
    a.geolyser-measure-end-action,
    a.geolyser-measure-maximize-action {
        display: block !important;
        width: 160px !important;
        min-width: 160px !important;
        height: 30px !important;
        line-height: 28px !important;
        padding: 0 12px !important;
        font-size: 0.86em !important;
        text-align: center !important;
        margin: 0 !important;
        box-sizing: border-box !important;
        border-radius: 3px !important;
        background-color: var(--bg-elevated) !important;
        color: var(--text) !important;
        border: 1px solid var(--border) !important;
    }
    [data-theme="dark"] .leaflet-bar a.geolyser-measure-end-action,
    [data-theme="dark"] .leaflet-bar a.geolyser-measure-maximize-action,
    [data-theme="dark"] a.geolyser-measure-end-action,
    [data-theme="dark"] a.geolyser-measure-maximize-action {
        background-color: #2c2f33 !important;
        color: #eaeaea !important;
        border-color: rgba(255, 255, 255, 0.10) !important;
    }

    /* =================================================================
 * Fullscreen-Modals im
 * Landscape mit Scrollbars wenn der Inhalt ueberlaeuft.
 * -----------------------------------------------------------------
 * Im Smartphone-Querformat ist die Hoehe knapp (z.B. 430px), viele
 * Modals (Mein Konto, Admin, DB-Vollansicht) ueberlaufen. Die
 * Vollbild-Pattern und die Body-Scroll-Regeln greifen nur im
 * 768px-Block — Landscape Phones haben aber haeufig 932x430 oder
 * aehnlich, also Width > 768. Hier dasselbe Pattern fuer Landscape.
 * ================================================================= */
    .modal-card {
        padding: 0 !important;
        overflow: hidden !important;
        max-width: 100vw !important;
        width: 100vw !important;
        max-height: 100dvh !important;
        height: 100dvh !important;
        margin: 0 !important;
        border-radius: 0 !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .modal-card > .modal-header,
    .modal-card > h3.modal-title {
        margin: 0 !important;
        padding: 10px 14px !important;
        background: var(--bg-elevated) !important;
        border-bottom: 1px solid var(--border);
        flex-shrink: 0;
        position: relative;
        z-index: 3;
        width: 100%;
    }
    .modal-card > .modal-footer,
    .modal-card > .modal-actions {
        margin: auto 0 0 0 !important;
        padding: 8px 12px !important;
        background: var(--bg-elevated) !important;
        border-top: 1px solid var(--border);
        flex-shrink: 0;
        position: relative;
        z-index: 3;
        width: 100%;
    }
    .modal-card > .modal-body,
    .modal-card > .modal-form,
    .modal-card > [data-role="body"] {
        margin: 0 !important;
        padding: 10px 14px !important;
        flex: 1 1 auto !important;
        overflow-y: auto !important;
        overflow-x: hidden !important;
        -webkit-overflow-scrolling: touch;
        min-height: 0 !important;
    }
    /* Tuerkise Scrollbar im Landscape ebenfalls (wie im Vollbild-Pattern). */
    .modal-card,
    .modal-card * {
        /* scrollbar-color mit sichtbarem Track. */
        scrollbar-color: var(--c-cyan, #3BBDE8) rgba(0,0,0,0.10);
        scrollbar-width: thin;
    }
    .modal-card ::-webkit-scrollbar,
    .modal-card::-webkit-scrollbar {
        /* etwas breiter fuer bessere Touch-Greifbarkeit. */
        width: 12px;
        height: 12px;
    }
    .modal-card ::-webkit-scrollbar-thumb,
    .modal-card::-webkit-scrollbar-thumb {
        background: var(--c-cyan, #3BBDE8) !important;
        border-radius: 5px;
        min-height: 40px;
    }
    .modal-card ::-webkit-scrollbar-track,
    .modal-card::-webkit-scrollbar-track {
        /* sichtbarer Track. */
        background: rgba(0, 0, 0, 0.10);
        border-radius: 6px;
    }
    /* Profile-Modal innere Forms im Landscape — die
     * .modal-form-Regel oben mit flex:1 1 auto wuerde sonst innere
     * timezone-/password-forms wieder auf ~12px quetschen. */
    #profile-modal .profile-modal-body > form.modal-form,
    #profile-modal .profile-gruppe > form.modal-form,
    #profile-modal .modal-body > form.modal-form {
        flex: 0 0 auto !important;
        overflow: visible !important;
        height: auto !important;
        min-height: 0 !important;
    }
}

/* ================================================================
 View-Switcher als vertikaler Selector-
 Stack zwischen Anleitung und Sprache. Im Closed-State zeigt der
 Stack nur das aktive Item — sieht aus wie ein normaler Topbar-
 Icon-Btn. Klick darauf klappt die anderen Views darunter aus
 (icon-only). Klick auf ein anderes wechselt die View, die wird
 sofort zum neuen Top-Item.
 Farben pro View: Karte indigo , Punktwolke rose (#e11d48),
 3D-Mesh blau . Aktive Item = voller Background, inaktive
 = neutraler Background mit farbigem Icon.
 ================================================================ */
/* kein Platzhalter mehr. Hier steht jetzt der
   echte Ausloeseknopf, genau wie beim Sprachauswahl-Knopf. Er laeuft im
   Fluss der Kopfzeile mit und kann deshalb nicht mehr daneben liegen,
   wenn sich die Nachbarknoepfe verschieben. Nur die Liste darunter
   schwebt, und die auch nur, solange sie offen ist. */
.view-switcher {
    width: 36px;
    height: 36px;
    flex: 0 0 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: relative;
}
.view-switcher[hidden] {
    display: none !important;
}
/* Der Ausloeser ist ein gewoehnlicher Kopfzeilen-Knopf; die Akzentfarbe
   der gerade aktiven Ansicht setzt das Skript per Zusatzklasse. */
.view-trigger {
    width: 36px;
    height: 36px;
    min-width: 36px;
    min-height: 36px;
    padding: 0;
    line-height: 0;
}
.view-trigger svg { display: block; }
.view-trigger--map        { color: #4f46e5; }
.view-trigger--pointcloud { color: #e11d48; }
.view-trigger--mesh3d     { color: #2563eb; }
.view-trigger--map.is-open,
.view-trigger--map:hover {
    background:
        linear-gradient(rgba(79, 70, 229, 0.16), rgba(79, 70, 229, 0.16)),
        var(--bg-elevated);
    border-color: #4f46e5;
}
.view-trigger--pointcloud.is-open,
.view-trigger--pointcloud:hover {
    background:
        linear-gradient(rgba(225, 29, 72, 0.16), rgba(225, 29, 72, 0.16)),
        var(--bg-elevated);
    border-color: #e11d48;
}
.view-trigger--mesh3d.is-open,
.view-trigger--mesh3d:hover {
    background:
        linear-gradient(rgba(37, 99, 235, 0.16), rgba(37, 99, 235, 0.16)),
        var(--bg-elevated);
    border-color: #2563eb;
}
[data-theme="dark"] .view-trigger--map        { color: #a5a4f6; }
[data-theme="dark"] .view-trigger--pointcloud { color: #f87a93; }
[data-theme="dark"] .view-trigger--mesh3d     { color: #7aa6f4; }
.view-stack {
    display: flex;
    flex-direction: column;
    /* gap analog zu .topbar-right { gap: 10px }
 damit der vertikale Abstand zwischen Items mit dem horizontalen
 Abstand zwischen den anderen Topbar-Buttons matched. */
    gap: 10px;
    /* Items haben Akzent-Farben; im closed-state sind alle nicht-
       aktiven items per Regel weiter unten hidden. Open-State wird
       von JS per .is-open-Klasse gesteuert. */
}
/* Die Liste selbst wird per Skript in document.body verschoben und dort
   nur waehrend des Oeffnens positioniert. z-index 1700 = ueber Sidebar
   (1100) und iframe-Huelle (1500), unter Modals (2000). Rahmen und
   Schatten wie beim Sprachmenue, damit beide gleich lesen. */
.view-stack--floating {
    position: fixed;
    z-index: 1700;
    padding: 6px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-elevated);
    box-shadow: var(--shadow-card, 0 18px 40px rgba(0,0,0,.45));
}
.view-stack[hidden] {
    display: none !important;
}
/* Items brauchen einen SOLIDEN Hintergrund.
 Vorher rgba(0,0,0,0.04) bzw. rgba(255,255,255,0.06) — im Dark-Mode
 gegen den bunten Karten-Tile-Hintergrund praktisch unsichtbar
 (Items expandieren ueber die Karte raus aus dem Topbar-Bereich).
 Loesung: var(--bg-elevated) = Modal/Card-Farbe, undurchsichtig und
 konsistent mit anderen schwebenden UI-Elementen (Dropdowns, Toasts). */
.view-stack-item {
    /* Box-Dimensionen mit min/max gegen jegliche
 Drift-Quellen festnageln (Flex-Stretch, browser-default button
 min-height, Touch-Target-Heuristiken auf Mobile). Pointcloud-Item
 wurde auf manchen Tablets mit 36x40 gerendert, jetzt strikt
 36x36. */
    width: 36px;
    height: 36px;
    min-width: 36px;
    max-width: 36px;
    min-height: 36px;
    max-height: 36px;
    border: 1px solid var(--border);
    /* border-radius an die uebrigen Topbar-
 Buttons angeglichen (vorher 6px, jetzt var(--radius) = 10px). */
    border-radius: var(--radius);
    background: var(--bg-elevated);
    color: var(--fg);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background .12s, color .12s, border-color .12s;
    /* Box-Shadow nur noch im Open-State, damit
 das aktive Item im Closed-State optisch nicht groesser/erhabener
 wirkt als die anderen Topbar-Icon-Btns. Open-State (Dropdown ist
 ausgeklappt) kriegt den Shadow zurueck (siehe `.is-open`-Regel). */
    padding: 0;
    /* flex-basis als shorthand `flex: 0 0 36px` haengt
 in flex-column an der Main-Axis (Hoehe). Auf Mobile-Browsern, die
 Form-Controls eine User-Agent-min-height von 40px geben, blieb der
 Button trotz height:36px auf 40px stehen. flex-basis bleibt bei
 36px, aber min-height oben killt den UA-Override sauber. */
    flex: 0 0 36px;
    box-sizing: border-box;
    line-height: 0;
}

.view-stack-item[hidden] {
    display: none !important;
}
.view-stack-item svg {
    display: block;
    flex: 0 0 auto;
}
/* CSS-order: aktive Item immer ganz oben im Flex-Column-Stack. Andere
   bleiben in DOM-Reihenfolge. */
.view-stack-item.is-active {
    order: -1;
}
/* Der frueher noetige Geschlossen-Zustand (nur das aktive Feld sichtbar)
   ist entfallen: geschlossen ist die ganze Liste ausgeblendet, offen
   zeigt sie alle verfuegbaren Ansichten, die aktive oben und markiert.
   Dieselbe Logik wie im Sprachmenue. */
/* Inaktive Items: farbiges Icon, neutraler ghost-Hintergrund (oben). */
.view-stack-item--map:not(.is-active)        { color: #4f46e5; } /* indigo */
.view-stack-item--pointcloud:not(.is-active) { color: #e11d48; } /* rose */
.view-stack-item--mesh3d:not(.is-active)     { color: #2563eb; } /* blau */
/* Hover auf inaktivem Item: Akzent-Tint UEBER dem soliden
   bg-elevated. Wichtig: `background` ist eine Stack-Property — wenn
   wir nur `background: rgba(...)` setzen, ueberschreibt das den
   soliden bg-elevated und der Button wirkt transparent.
   Loesung: Multi-Layer-Background mit linear-gradient (das
   "Tint" als oberster Layer) UND var(--bg-elevated) als solidem
   unteren Layer. Browser kombiniert beide → solides Endergebnis
   mit Akzent-Farbschimmer. */
.view-stack-item--map:not(.is-active):hover {
    background:
        linear-gradient(rgba(79, 70, 229, 0.16), rgba(79, 70, 229, 0.16)),
        var(--bg-elevated);
    border-color: #4f46e5;
}
.view-stack-item--pointcloud:not(.is-active):hover {
    background:
        linear-gradient(rgba(225, 29, 72, 0.16), rgba(225, 29, 72, 0.16)),
        var(--bg-elevated);
    border-color: #e11d48;
}
.view-stack-item--mesh3d:not(.is-active):hover {
    background:
        linear-gradient(rgba(37, 99, 235, 0.16), rgba(37, 99, 235, 0.16)),
        var(--bg-elevated);
    border-color: #2563eb;
}
/* Dark-Mode: gleiches Prinzip, etwas hoehere Tint-Alpha damit der
   Farbschimmer gegen den dunkleren bg-elevated sichtbar wird. */
[data-theme="dark"] .view-stack-item--map:not(.is-active):hover {
    background:
        linear-gradient(rgba(79, 70, 229, 0.28), rgba(79, 70, 229, 0.28)),
        var(--bg-elevated);
}
[data-theme="dark"] .view-stack-item--pointcloud:not(.is-active):hover {
    background:
        linear-gradient(rgba(225, 29, 72, 0.28), rgba(225, 29, 72, 0.28)),
        var(--bg-elevated);
}
[data-theme="dark"] .view-stack-item--mesh3d:not(.is-active):hover {
    background:
        linear-gradient(rgba(37, 99, 235, 0.28), rgba(37, 99, 235, 0.28)),
        var(--bg-elevated);
}
/* Aktive Items: dezenter Akzent-Tint UEBER var(--bg-elevated)
   (gestapelt analog zum Hover), Border + Icon in der vollen Akzent-
   Farbe — eingefaerbt, nicht vollfarbig. */
.view-stack-item.is-active.view-stack-item--map {
    background:
        linear-gradient(rgba(79, 70, 229, 0.20), rgba(79, 70, 229, 0.20)),
        var(--bg-elevated);
    color: #4f46e5;
    border-color: rgba(79, 70, 229, 0.55);
}
.view-stack-item.is-active.view-stack-item--pointcloud {
    background:
        linear-gradient(rgba(225, 29, 72, 0.20), rgba(225, 29, 72, 0.20)),
        var(--bg-elevated);
    color: #e11d48;
    border-color: rgba(225, 29, 72, 0.55);
}
.view-stack-item.is-active.view-stack-item--mesh3d {
    background:
        linear-gradient(rgba(37, 99, 235, 0.20), rgba(37, 99, 235, 0.20)),
        var(--bg-elevated);
    color: #2563eb;
    border-color: rgba(37, 99, 235, 0.55);
}
/* Dark-Mode: gleiches Konzept, aber der Tint wird etwas heller, weil
   gegen dunklen Bg sonst zu unscheinbar; Text-Color wird auf einen
   leichteren Ton verschoben fuer Lesbarkeit. */
[data-theme="dark"] .view-stack-item.is-active.view-stack-item--map {
    background:
        linear-gradient(rgba(79, 70, 229, 0.32), rgba(79, 70, 229, 0.32)),
        var(--bg-elevated);
    color: #a5a4f6;
    border-color: rgba(79, 70, 229, 0.70);
}
[data-theme="dark"] .view-stack-item.is-active.view-stack-item--pointcloud {
    background:
        linear-gradient(rgba(225, 29, 72, 0.32), rgba(225, 29, 72, 0.32)),
        var(--bg-elevated);
    color: #f87a93;
    border-color: rgba(225, 29, 72, 0.70);
}
[data-theme="dark"] .view-stack-item.is-active.view-stack-item--mesh3d {
    background:
        linear-gradient(rgba(37, 99, 235, 0.32), rgba(37, 99, 235, 0.32)),
        var(--bg-elevated);
    color: #7aa6f4;
    border-color: rgba(37, 99, 235, 0.70);
}
/* Aktive Hover: minimaler Helligkeitsboost als Klick-Feedback (Stack
   oeffnet/schliesst beim Klick aufs aktive Item). */
.view-stack-item.is-active:hover {
    filter: brightness(1.06);
}

/* Performance-Optimierung fuer iframe-Modus
 (besonders 3D-Mesh / Cesium, das mit 60 FPS rendert).
 - Topbar-backdrop-filter aus: Browser muss sonst bei jedem Cesium-
 Frame den Blur-Stack hinter der Topbar neu berechnen (= teuerer
 GPU-Compositing-Hit pro Frame). Stattdessen solid background.
 - Map-Shell wird ge-display:none-d: Leaflet-Tiles tickern dann
 nicht im Hintergrund weiter, Browser muss die DOM-Subtree nicht
 mehr layouten/painten.
 - Iframe-Shell bekommt contain:strict + will-change:transform:
 Hint an den Browser dass es ein eigener Rendering-Layer ist;
 GPU bekommt eine eigene Compositing-Layer fuer den iframe. */
body.external-view-active .topbar {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    background: var(--bg-elevated) !important;
}
body.external-view-active .map-shell {
    display: none !important;
}
body.external-view-active .external-view-shell {
    contain: strict;
    will-change: transform;
}
body.external-view-active .external-view-shell > iframe {
    will-change: transform;
}

/* External-View-Shell: ueber main + alle In-App-UI, unter Modals.
   z-index-Landschaft: Leaflet-Tiles 200..700, Sidebar 1100, Features-
   Panel 1100, Modals 2000+. 1500 sitzt drueber Sidebar+Features-Panel
   (sonst ueberdeckt die Sidebar das iframe rechts oder links), drunter
   Modals/Confirms — Admin-Modal etc. kann weiter aufgemacht werden
   ohne das iframe schliessen zu muessen. */
.external-view-shell {
    position: fixed;
    top: var(--topbar-h, 56px);
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--bg); /* --bg-app war nie definiert (Shell transparent) */
    z-index: 1500;
    display: flex;
    flex-direction: column;
}
/* das HTML-hidden-Attribut hat per UA-
 Default display:none, wird aber von .external-view-shell { display:flex }
 ueberschrieben — Folge: Shell liegt permanent ueber map + faängt
 alle Klicks ab. Explizit ueberschreiben.
 Das gleiche gilt fuer das interne Loading-Overlay. */
.external-view-shell[hidden],
.external-view-loading[hidden] {
    display: none !important;
}
.external-view-shell > iframe {
    flex: 1 1 auto;
    width: 100%;
    height: 100%;
    min-height: 0;             /* sonst kann flex-child auf 0 collabieren */
    border: 0;
    display: block;
    background: transparent;   /* iframe-Content soll nicht ueberdeckt werden */
}
.external-view-loading {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    background: var(--bg); /* --bg-app war nie definiert (Shell transparent) */
    color: var(--fg); /* --text-primary war nie definiert */
    font-size: 0.95em;
}
.external-view-loading-spinner {
    width: 18px; height: 18px;
    border: 2px solid var(--border);
    border-top-color: var(--c-primary);
    border-radius: 50%;
    animation: external-view-spin 0.8s linear infinite;
}
@keyframes external-view-spin {
    to { transform: rotate(360deg); }
}

/* Footer-Leiste in iframe-shell mit
 Geolyser-Copyright, Impressum, Datenschutz. Liegt UNTER dem
 iframe (flex-row in shell), klein, mit Akzent-Background damit
 sie sich vom iframe-Content abgrenzt. */
.external-view-footer {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 6px 12px;
    background: var(--bg-panel);
    border-top: 1px solid var(--border);
    color: var(--muted-hi);
    font-size: 0.78em;
    white-space: nowrap;
    overflow-x: auto;
}
.external-view-footer a {
    color: var(--muted-hi);
    text-decoration: none;
    transition: color .12s;
}
.external-view-footer a:hover {
    color: var(--c-primary);
    text-decoration: underline;
}
.external-view-footer .sep {
    opacity: 0.5;
}
@media (max-width: 480px) {
    .external-view-footer { font-size: 0.72em; gap: 6px; padding: 5px 8px; }
}

/* Hover-Outline fuer Punkt-Marker.
 * Vector-Layer (Polygon/Linie/Kreis) bekommen ihren Hover-Outline ueber
 * setStyle in map.js, Marker (Points) brauchen einen CSS-Drop-Shadow weil
 * setStyle dort nicht definiert ist. --hover-color wird vom Mouseover-
 * Handler gesetzt (= Layer-Farbe-Hex). */
.geolyser-pin.is-hover {
    filter:
        drop-shadow(0 0 4px var(--hover-color, #fff))
        drop-shadow(0 0 8px var(--hover-color, #fff));
    z-index: 1000 !important;
}

/* ---- F27-Fix: .leaflet-tooltip theming ----
 * Leaflet-Default-Tooltips (u.a. Geoman-Zeichenhinweise) waren als
 * einzige Karten-UI nicht tokenisiert (weiss/#222 auch im Dark Mode).
 * Pfeilspitzen muessen die Wrapper-Farbe spiegeln. */
.leaflet-tooltip {
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-card);
}
.leaflet-tooltip-top::before    { border-top-color: var(--bg-elevated); }
.leaflet-tooltip-bottom::before { border-bottom-color: var(--bg-elevated); }
.leaflet-tooltip-left::before   { border-left-color: var(--bg-elevated); }
.leaflet-tooltip-right::before  { border-right-color: var(--bg-elevated); }

/* ---- Doppel-Quota: Bilder + Audio nebeneinander ----
 * Bilder und Audio sind getrennte 1-GB-Kontingente; das Profil-Modal
 * zeigt zwei Balken in einer Reihe. Auf schmalen Screens (<540px)
 * stapeln sie sich. Balken-Styles erben von .quota-bar (Bestand). */
.quota-box--dual {
    display: flex;
    /* Bestands-.quota-box setzt flex-direction:column — hier explizit
       row, sonst stapeln die zwei Spalten. */
    flex-direction: row;
    gap: 16px;
}
.quota-box--dual .quota-col {
    flex: 1 1 0;
    min-width: 0;
}
.quota-col-title {
    margin: 0 0 4px;
    font-weight: 600;
    color: var(--muted-hi);
}
@media (max-width: 540px) {
    .quota-box--dual { flex-direction: column; gap: 10px; }
}

/* ---- Demo-Badge: Topbar-Kennzeichnung Demo-Modus -- */
.demo-badge {
    display: inline-flex;
    align-items: center;
    height: 24px;
    padding: 0 10px;
    border-radius: 999px;
    font-size: .72em;
    font-weight: 700;
    letter-spacing: .08em;
    color: var(--c-warn);
    background: rgba(201, 138, 0, 0.14);
    border: 1px solid rgba(201, 138, 0, 0.5);
    cursor: help;
}
[data-theme="light"] .demo-badge { color: var(--c-amber); }

/* Demo-Start-Button: Puls-Hinweis "hier druecken" nach der
 * Schein-Login-Animation. */
.demo-cta-pulse {
    animation: demo-cta-pulse 1.4s ease-in-out infinite;
}
@keyframes demo-cta-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(0, 160, 210, 0.55); }
    55%      { box-shadow: 0 0 0 10px rgba(0, 160, 210, 0); }
}
@media (prefers-reduced-motion: reduce) {
    .demo-cta-pulse { animation: none; outline: 2px solid var(--c-primary-hi); }
}

/* Skull-Icon im Projekt-Loeschen-Button. Das SVG ist
   schwarz — im Light-Mode ok, im Dark-Mode unsichtbar. Im Dark-Mode auf
   Weiss invertieren (brightness(0) -> reines Schwarz, invert(1) -> Weiss). */
[data-theme="dark"] .pm-del-skull { filter: brightness(0) invert(1); }

/* Projekt-Switcher — Noch-nicht-Mitglied ausgegraut +
   Enter-Button zum Selbst-Beitreten. */
.project-item-disabled .project-item-name { opacity: 0.5; }
.project-item-enter {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    padding: 1px 3px;
    border-radius: 4px;
}
.project-item-enter:hover { background: rgba(127, 127, 127, 0.18); }
/* Beitreten-Icon etwas kleiner. */
.project-enter-icon { width: 13px; height: 13px; display: block; }
[data-theme="dark"] .project-enter-icon { filter: brightness(0) invert(1); }
/* Drag&Drop-Reihenfolge in der Projektverwaltung. */
.project-row--drag { cursor: grab; }
.project-row--drag.is-dragging { opacity: 0.45; cursor: grabbing; }
/* Export/Import-Icons (cloud-download/-upload) im Dark-Mode sichtbar. */
[data-theme="dark"] .pm-icon-img { filter: brightness(0) invert(1); }
/* "Projekte verwalten"-Settings-Icon im Dark-Mode sichtbar. */
[data-theme="dark"] .project-manage-icon { filter: brightness(0) invert(1); }
