/* Lagebild three-column layout — left rail (320px) / center map (1fr) / right rail (340px).
   Side rails contain stacked panels; the center column hosts the map and its HUD overlays.

   ── Map-chrome layout contract (Polish v3, 2026-07-18 — verbindlich,
      per Playwright WidgetOverlapTests abgesichert) ─────────────────────────
   Every floating element over the map has ONE owned region; regions use
   PHYSICAL corners/offsets so RTL/LTR render identically. NO inset-inline-*
   on map chrome — logical insets flipped FABs onto occupied corners under
   RTL (the 2026-07-18 overlap reports). Slots:

     top-left      CommandCenterHud (top:10, ≈82px tall)
                   → Windy-FAB (top:106)
                   → DrawToolbar (top:162, top-anchored, NOT centred)
     bottom-left↑  AreaSelectToolbar (bottom:64) above cursor coords
                   (bottom:36) above MapLibre ScaleControl (bottom:10)
     top-centre    HelpRequestBanner only (transient SOS)
     top-right     .ms-map-rail-end column: WeatherChip → WeatherAdvisory-
                   Banner → MapFilterDock (AIS/Ebenen tabs). Flow layout —
                   members stack, never overlap.
     bottom-centre BridgeControlBar (bottom:12, layer toggles · zoom ·
                   N-reset · geolocate · fullscreen)
     bottom-right↑ .ms-weather-legend (bottom:96, only while wind layer on)
     bottom-right  MapLibre GeolocateControl + attribution; tablet
                   panels-FAB (bottom:44, above attribution)
     centre        map only. Exception: TrackDetailPanel drawer (foreground
                   task) — while open, colliding chrome fades via :has().

   Z-tiers (raise only with a region change, never to win a corner fight —
   regions guarantee non-overlap, z only orders transient foregrounds):
     3  map frame glow            (decor, ::after)
     4  marker hover tooltips
     5  region-owned chrome       (HUD, toolbars, coords)
     6  rails                     (.ms-map-rail-end, .ms-area-toolbar)
     8  track-detail drawer       (foreground task while open)
     30 attention banners         (.ms-help-banner)
     var(--z-controls) FABs; page-level tiers in ms-zindex.css. */

.ms-lagebild {
    flex: 1;
    display: grid;
    grid-template-columns: 320px 1fr 340px;
    min-height: 0;
    gap: 0;
    background: var(--ms-bg);
}

.ms-lagebild-col {
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
}
.ms-lagebild-col.left,
.ms-lagebild-col.right { gap: 0; }
.ms-lagebild-col.center {
    position: relative;
    overflow: hidden;
}

/* The map container fills the center column. ms-mapview is the canonical
   wrapper — pages use it for any tactical-map embedding. */
.ms-mapview {
    position: absolute;
    inset: 0;
    background: var(--ms-water);
    border-inline-start: 1px solid var(--ms-panel-border);
    border-inline-end: 1px solid var(--ms-panel-border);
}

/* ── UI polish (#ui-polish) ──────────────────────────────────────────────
   Map frame: rounded corners + a faint accent rim so the tactical picture
   reads as one "instrument" instead of a bare rectangle. The glow is inset
   so it can't bleed over the side rails; fullscreen drops the radius (the
   map then IS the viewport). */
.ms-lagebild-map { border-radius: 10px; }
/* The glow rim lives on an ::after overlay — painted on the container itself
   it would be covered by the absolutely-positioned .ms-mapview. The overlay
   is non-interactive so map hit-testing stays intact. */
.ms-lagebild-map::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    box-shadow:
        inset 0 0 0 1px color-mix(in srgb, var(--ms-accent) 16%, transparent),
        inset 0 0 26px color-mix(in srgb, var(--ms-accent) 5%, transparent);
    pointer-events: none;
    z-index: 3;
}
.ms-map-fullscreen .ms-lagebild-map,
.ms-map-fullscreen .ms-lagebild-map::after { border-radius: 0; }

/* Track-marker status ring — a soft halo that encodes contact state at a
   glance: green = identified/ok, amber = unidentified, red = alerting or
   hostile. data-status is set by applyTrackVisual() in maplibre-interop.js.
   ::after (not ::before) — the bulk multi-select ring already owns ::before. */
.ms-track-marker::after {
    content: "";
    position: absolute;
    inset: -2px;
    border-radius: 50%;
    border: 1.5px solid var(--ms-track-ship);
    opacity: 0.45;
    box-shadow: 0 0 6px color-mix(in srgb, var(--ms-track-ship) 55%, transparent);
    pointer-events: none;
    transition: border-color 200ms ease, box-shadow 200ms ease;
}
.ms-track-marker[data-status="warn"]::after {
    border-color: var(--ms-track-unknown);
    box-shadow: 0 0 6px color-mix(in srgb, var(--ms-track-unknown) 55%, transparent);
}
.ms-track-marker[data-status="alert"]::after {
    border-color: var(--ms-track-alert);
    opacity: 0.7;
    box-shadow: 0 0 8px color-mix(in srgb, var(--ms-track-alert) 65%, transparent);
}

/* Selected track: a slow cyan pulse ring around the marker, managed as a
   child element (.ms-track-sel-ring) by applyTrackVisual so it composes with
   both pseudo-element rings above. */
.ms-track-sel-ring {
    position: absolute;
    inset: -5px;
    border-radius: 50%;
    border: 2px solid var(--ms-accent);
    pointer-events: none;
    animation: ms-sel-pulse 1.8s ease-out infinite;
}
@keyframes ms-sel-pulse {
    0%   { transform: scale(0.85); opacity: 0.95; }
    70%  { transform: scale(1.35); opacity: 0;    }
    100% { transform: scale(1.35); opacity: 0;    }
}
@media (prefers-reduced-motion: reduce) {
    .ms-track-sel-ring { animation: none; opacity: 0.8; }
}

/* One-shot live-update pulse: a small cyan ripple dropped on a marker when a
   new position arrives over SignalR. Spawned per update in upsertTrack(),
   removed on animationend so the DOM never accumulates rings. */
.ms-track-upd-ring {
    position: absolute;
    inset: 2px;
    border-radius: 50%;
    border: 1.5px solid var(--ms-accent);
    pointer-events: none;
    animation: ms-upd-pulse 700ms ease-out forwards;
}
@keyframes ms-upd-pulse {
    0%   { transform: scale(0.6); opacity: 0.85; }
    100% { transform: scale(2.1); opacity: 0;    }
}
@media (prefers-reduced-motion: reduce) {
    .ms-track-upd-ring { animation-duration: 0.01s; }
}

/* Area toolbar buttons: real hover/press affordance instead of the flat
   filter-brightness default. */
.ms-area-toolbar .ms-btn {
    background: var(--ms-panel);
    border-radius: 6px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
    transition: border-color 140ms ease, box-shadow 140ms ease, transform 100ms ease;
}
.ms-area-toolbar .ms-btn:hover {
    border-color: var(--ms-accent);
    box-shadow: 0 3px 12px rgba(0, 0, 0, 0.4);
    filter: none;
}
.ms-area-toolbar .ms-btn:active { transform: translateY(1px); }

/* ---- Hover tooltip (Phase #106-A) ---- */
.ms-map-tooltip {
    position: absolute;
    transform: translate(-50%, calc(-100% - 14px));
    background: var(--ms-panel);
    color: var(--ms-text);
    border: 1px solid var(--ms-panel-border);
    padding: 6px 10px;
    min-width: 140px;
    max-width: 240px;
    font-family: var(--ms-font-ui);
    font-size: 11px;
    line-height: 1.35;
    pointer-events: none;
    z-index: 4;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
    opacity: 0;
    visibility: hidden;
    transition: opacity 90ms ease-out;
}
.ms-map-tooltip.visible { opacity: 1; visibility: visible; }
.ms-map-tooltip-name {
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ms-map-tooltip-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    margin-top: 4px;
}
.ms-map-tooltip-mono {
    font-family: var(--ms-font-mono);
    font-size: 10px;
    color: var(--ms-text-dim);
}
.ms-map-tooltip-age { color: var(--ms-text-mute); }
/* Second tooltip row (identity / fix quality) — quieter than the primary
   data row so the eye still lands on speed/heading first. */
.ms-map-tooltip-sub {
    margin-top: 2px;
    padding-top: 3px;
    border-top: 1px solid color-mix(in srgb, var(--ms-panel-border) 60%, transparent);
}
.ms-map-tooltip-sub .ms-map-tooltip-mono { font-size: 9px; }
.ms-map-tooltip-chip {
    display: inline-block;
    padding: 1px 6px;
    font-family: var(--ms-font-mono);
    font-size: 9px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    border: 1px solid currentColor;
    border-radius: 2px;
}
.ms-map-tooltip-chip.ok      { color: var(--ms-ok); }
.ms-map-tooltip-chip.unknown { color: var(--ms-unknown); }
.ms-map-tooltip-chip.alert   { color: var(--ms-alert); }
html[dir="rtl"] .ms-map-tooltip { text-align: start; }

/* ---- Weather chip (Open-Meteo overlay) ----
   Lives at the TOP of the .ms-map-rail-end column since the 2026-06-12
   layout cleanup ("oben rechts: Wetter + Alerts, klar gestapelt") — in
   flow, so the advisory banner and the filter dock stack below it with
   fixed padding and can never overlap it. The earlier bottom-corner
   posture collided twice (scale bar #236, attribution) — flow layout
   ends that class of bug. */
.ms-weather-chip {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    background: var(--ms-panel);
    border: 1px solid var(--ms-panel-border);
    color: var(--ms-text);
    font-family: var(--ms-font-mono);
    font-size: 11px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
    pointer-events: auto;
}
.ms-weather-arrow {
    display: inline-block;
    width: 14px;
    text-align: center;
    color: var(--ms-accent);
    font-size: 14px;
    line-height: 1;
}
.ms-weather-wind { color: var(--ms-text); font-weight: 600; }
.ms-weather-wave { color: var(--ms-friend); }
.ms-weather-temp { color: var(--ms-text-dim); }
.ms-weather-unit { color: var(--ms-text-mute); font-size: 9px; }

/* MapLibre ScaleControl (added in command-hud.js, always physical
   bottom-left). Themed to the console panels and lifted off the viewport
   edge so it stays readable near the browser chrome on small / high-DPI
   screens. The weather chip lives in the opposite (bottom-right) corner,
   so the two can never overlap regardless of text direction. */
.ms-lagebild-map .maplibregl-ctrl-bottom-left {
    bottom: 10px;
    left: 10px;
}
.ms-lagebild-map .maplibregl-ctrl-scale {
    background: var(--ms-panel);
    color: var(--ms-text);
    border-color: var(--ms-panel-border);
    font-family: var(--ms-font-mono);
    font-size: 10px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}

/* Geolocate control ("center on me") — re-skin MapLibre's stock white
   button group onto the panel tokens so it doesn't glare on the dark
   operational themes. The icon is an inline SVG with its own fill; only
   the chrome needs theming. */
.ms-lagebild-map .maplibregl-ctrl-group {
    background: var(--ms-panel);
    border: 1px solid var(--ms-panel-border);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.ms-lagebild-map .maplibregl-ctrl-group button {
    background: transparent;
}
.ms-lagebild-map .maplibregl-ctrl-group button:hover {
    background: var(--ms-panel-header);
}
/* Invert the idle icon only on the dark palettes; the active /
   waiting states use MapLibre's blue icon, which must keep its hue so
   "tracking on" stays recognisable. Light theme keeps stock colors. */
html[data-theme="dark"] .ms-lagebild-map .maplibregl-ctrl button:not(.maplibregl-ctrl-geolocate-active):not(.maplibregl-ctrl-geolocate-waiting) .maplibregl-ctrl-icon,
html[data-theme="hicontrast"] .ms-lagebild-map .maplibregl-ctrl button:not(.maplibregl-ctrl-geolocate-active):not(.maplibregl-ctrl-geolocate-waiting) .maplibregl-ctrl-icon {
    filter: invert(0.85);
}

/* Compact bottom row on narrow / mobile viewports — smaller chip, still
   clear of the scale bar (opposite corner) and the bottom edge. */
@media (max-width: 768px) {
    .ms-weather-chip {
        bottom: 40px;
        right: 8px;
        gap: 6px;
        padding: 4px 8px;
        font-size: 10px;
    }
    .ms-lagebild-map .maplibregl-ctrl-scale { font-size: 9px; }
}

/* ---- AIS layer filter (multi-source overlay) ----
   Sits on the OPPOSITE side from .ms-layer-filter so the two top-of-map
   panels never collide. LayerFilter is anchored at inset-inline-end
   (visual right in LTR, visual left in RTL); AisFilter must be at the
   inline-start side. We had inset-inline-start: 12px here originally but
   it resolved to the same physical edge as inset-inline-end on Chromium
   in RTL (browser quirk in this nested-flex layout), causing the AIS
   panel to overlap LayerFilter. Direction-explicit left/right is
   bulletproof.

   top: 48px, NOT 12px — the area-select toolbar (#mission-area-ux) sits
   at top:10px on the same inline-start edge with z-index:6 and was
   intercepting every click on the AIS panel header ("panel cannot be
   closed", customer bug). Sitting below it keeps both clickable. */
/* The AIS filter lives inside the MapFilterDock (top-end rail) since the
   2026-06-12 layout cleanup — docked it is plain flow content, the rail
   guarantees it can't overlap any other chrome. The standalone absolute
   posture only remains for the (currently unused) non-docked fallback. */
.ms-ais-filter {
    background: var(--ms-panel);
    border: 1px solid var(--ms-panel-border);
    color: var(--ms-text);
    font-size: 11px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
    max-width: min(300px, calc(100vw - 24px));
}
.ms-ais-filter.docked {
    width: 100%;
    max-width: none;
    border: none;
    box-shadow: none;
    background: transparent;
}
/* Docked, the dock body is the scroll container — drop the panel's own
   max-height so there is exactly one scrollbar. */
.ms-ais-filter.docked .ms-ais-filter-body {
    max-height: none;
    overflow-y: visible;
    border-top: none;
    min-width: 0;
    padding: 2px 0 4px;
}

/* ── Top-end rail + filter dock (#ais-ui-cleanup 2026-06-12) ─────────────
   ONE absolutely-positioned column at the map's top-right; the weather
   chip, advisory banner and the tabbed filter dock stack inside it in
   normal flow — overlap between them is impossible by construction.
   Physical right (not inset-inline-end) so the layout is identical in
   RTL and LTR: the left edge belongs to the HUD + toolbars. */
.ms-map-rail-end {
    position: absolute;
    top: 10px;
    right: 12px;
    z-index: 6;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 8px;
    /* Shrink-to-content, capped so the rail can never reach the (variable-
       width, ~330px) CommandCenterHud at the opposite corner even when the
       map column narrows to ~620px on a 1280px console. */
    width: auto;
    max-width: min(320px, calc(100% - 364px));
    max-height: calc(100% - 60px);
    pointer-events: none;          /* gaps stay click-through to the map */
}
.ms-map-rail-end > * { pointer-events: auto; }

.ms-filter-dock {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    width: min(300px, 100%);
}
.ms-filter-dock-tabs {
    display: flex;
    justify-content: flex-end;
    gap: 6px;
}
.ms-filter-dock-tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    background: var(--ms-panel);
    border: 1px solid var(--ms-panel-border);
    border-radius: 6px;
    color: var(--ms-text-dim);
    font-size: 11px;
    cursor: var(--ms-cursor-pointer);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.ms-filter-dock-tab:hover { color: var(--ms-text); border-color: var(--ms-accent); }
.ms-filter-dock-tab.active {
    color: var(--ms-accent);
    border-color: var(--ms-accent);
}
.ms-filter-dock-tab-count {
    font-family: var(--ms-font-mono);
    font-size: 10px;
    color: var(--ms-text-mute);
}
.ms-filter-dock-tab.active .ms-filter-dock-tab-count { color: var(--ms-accent); }
.ms-filter-dock-tab-legend {
    min-width: 28px;
    justify-content: center;
    font-weight: 700;
}

/* NATO symbology legend (APP-6 / MIL-STD-2525) — the "?" dock tab. */
.ms-map-legend { font-size: 11px; color: var(--ms-text-dim); }
.ms-map-legend-heading {
    font-family: var(--ms-font-mono);
    font-size: 9px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ms-text-mute);
    padding: 6px 0 2px;
}
.ms-map-legend-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 2px 0;
}
.ms-map-legend-dot {
    width: 11px; height: 11px;
    border-radius: 50%;
    border: 1px solid var(--ms-panel-border);
    flex: none;
}
.ms-map-legend-glyph {
    width: 14px;
    text-align: center;
    color: var(--ms-nato-friend);
    flex: none;
}
.ms-map-legend-note {
    padding-top: 6px;
    font-size: 10px;
    color: var(--ms-text-mute);
    line-height: 1.4;
}

.ms-filter-dock-body {
    margin-top: 6px;
    background: var(--ms-panel);
    border: 1px solid var(--ms-panel-border);
    border-radius: 6px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
    padding: 4px 8px;
    overflow-y: auto;
    max-height: min(62vh, 560px);
}

/* Header row: title-toggle + a ✕ close button (same posture as the
   DrawToolbar collapse, #ui-toggles). Collapsed, the whole panel shrinks
   to the antenna FAB below — the @if in the markup removes the rest. */
.ms-ais-filter-head {
    display: flex;
    align-items: center;
}
.ms-ais-filter-toggle {
    border: none;
    flex: 1 1 auto;
    text-align: start;
}
.ms-ais-filter-close {
    appearance: none;
    border: none;
    background: transparent;
    color: var(--ms-text-dim);
    cursor: var(--ms-cursor-pointer);
    font-size: 13px;
    line-height: 1;
    padding: 2px 4px;
    margin-inline-end: 4px;
    border-radius: 4px;
}
.ms-ais-filter-close:hover { color: var(--ms-text); background: color-mix(in srgb, var(--ms-text) 10%, transparent); }
.ms-ais-filter.collapsed { padding: 0; }
.ms-ais-filter-fab {
    display: flex;
    align-items: center;
    gap: 6px;
    border: none;
    background: transparent;
    color: var(--ms-text-dim);
    cursor: var(--ms-cursor-pointer);
    padding: 6px 8px;
}
.ms-ais-filter-fab:hover { color: var(--ms-text); }
.ms-ais-filter-fab-count {
    font-family: var(--ms-font-mono);
    font-size: 10px;
    color: var(--ms-text-dim);
}
.ms-ais-filter-body {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 6px 8px 8px;
    border-top: 1px solid var(--ms-panel-border);
    min-width: 240px;
    /* The display group grew the panel — scroll instead of growing past the
       viewport (the FAB row, weather chip and scale bar live below). */
    max-height: min(62vh, 520px);
    overflow-y: auto;
}
.ms-ais-filter-row {
    display: grid;
    /* checkbox · dot · name · state · last-seen · count */
    grid-template-columns: 16px 12px 1fr auto auto auto;
    gap: 6px;
    align-items: center;
    cursor: var(--ms-cursor-pointer);
    padding: 2px 0;
}
.ms-ais-filter-dot {
    width: 10px; height: 10px;
    border-radius: 50%;
    border: 1px solid var(--ms-panel-border);
}
.ms-ais-src-1 { background: var(--ms-friend); }      /* AisStream */
.ms-ais-src-2 { background: var(--ms-link); }        /* MarineTraffic */
.ms-ais-src-3 { background: var(--ms-ok); }          /* AisCatcher */
.ms-ais-src-4 { background: var(--ms-accent2); }     /* AisHub */
.ms-ais-src-5 { background: var(--ms-warn-strong); } /* Global Fishing Watch */
.ms-ais-src-6 { background: var(--ms-alert); }       /* Sentinel-1 (SAR) */
.ms-ais-src-7 { background: var(--ms-warn); }        /* MyShipTracking */
.ms-ais-src-8 { background: var(--ms-text-dim); }    /* Voyage Radar */
.ms-ais-src-9 { background: var(--ms-ok-bright); }   /* VesselFinder */
.ms-ais-src-10 { background: var(--ms-accent); }     /* Demo (Simulation) */

/* Vessel-type category dots — kept 1:1 in sync with aisPointColorExpr()
   in maplibre-interop.js so the panel doubles as a legend. The N index
   is (int)AisShipCategory; if a new category is added on either side
   the other has to follow. */
.ms-ais-cat-0 { background: var(--ms-text-mute);   } /* unknown */
.ms-ais-cat-1 { background: var(--ms-warn-strong); } /* fishing */
.ms-ais-cat-2 { background: var(--ms-ok);          } /* passenger */
.ms-ais-cat-3 { background: var(--ms-friend);      } /* cargo */
.ms-ais-cat-4 { background: var(--ms-alert);       } /* tanker */
.ms-ais-cat-5 { background: var(--ms-accent2);     } /* high-speed */
.ms-ais-cat-6 { background: var(--ms-link);        } /* tug / service */
.ms-ais-cat-7 { background: var(--ms-warn);        } /* military */
.ms-ais-cat-8 { background: var(--ms-ok-bright);   } /* SAR */
.ms-ais-cat-9 { background: var(--ms-text-dim);    } /* other */

/* Section heading inside the AIS filter body that separates the
   provider rows from the category rows. Visual cue only — keeps the
   12-row stack scannable without breaking up the grid layout of the
   existing filter rows. */
.ms-ais-filter-section {
    font-family: var(--ms-font-mono);
    font-size: 9px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ms-text-mute);
    padding: 6px 0 2px 0;
    margin-block-start: 4px;
    border-block-start: 1px solid var(--ms-panel-border);
}

/* Collapsible sub-group header (AIS sources / vessel types). Looks like the
   old .ms-ais-filter-section label but is a full-width clickable toggle with
   a leading ▸/▾ disclosure arrow. */
.ms-ais-filter-group-head {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    background: none;
    border: none;
    border-block-start: 1px solid var(--ms-panel-border);
    cursor: pointer;
    font-family: var(--ms-font-mono);
    font-size: 9px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ms-text-mute);
    padding: 6px 0 4px 0;
    margin-block-start: 4px;
    text-align: start;
}
.ms-ais-filter-group-head:hover { color: var(--ms-text); }
.ms-ais-filter-group-arrow {
    display: inline-block;
    width: 0.8em;
    font-size: 10px;
    line-height: 1;
}

.ms-ais-filter-name { font-family: var(--ms-font-ui); }
.ms-ais-filter-state {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-family: var(--ms-font-mono);
    font-size: 9px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 1px 5px;
    border: 1px solid color-mix(in srgb, currentColor 45%, transparent);
    border-radius: 2px;
}
/* Filled status indicator in front of the label — ●Live / ●WaitingForKey /
   ●Error read at a glance; the Live dot breathes so a healthy feed is
   recognisably *alive*, not just green. */
.ms-ais-filter-state::before {
    content: "";
    width: 6px; height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}
.ms-ais-filter-state.ok    { color: var(--ms-ok); }
.ms-ais-filter-state.ok::before { animation: ms-ais-live-pulse 2.4s ease-in-out infinite; }
.ms-ais-filter-state.warn  { color: var(--ms-warn); }
.ms-ais-filter-state.alert { color: var(--ms-alert); }
.ms-ais-filter-state.mute  { color: var(--ms-text-mute); }
@keyframes ms-ais-live-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.35; }
}
@media (prefers-reduced-motion: reduce) {
    .ms-ais-filter-state.ok::before { animation: none; }
}

/* Empty-state hint: no provider delivering + nothing cached — tell the
   operator why the layer is empty and where the keys go. */
.ms-ais-filter-hint {
    display: block;
    margin: 2px 0 6px;
    padding: 6px 8px;
    font-size: 10px;
    line-height: 1.45;
    color: var(--ms-text-dim);
    background: color-mix(in srgb, var(--ms-warn) 8%, transparent);
    border: 1px solid color-mix(in srgb, var(--ms-warn) 30%, transparent);
    border-radius: 2px;
}
.ms-ais-filter-hint a {
    color: var(--ms-accent);
    text-decoration: none;
}
.ms-ais-filter-hint a:hover { text-decoration: underline; }
.ms-ais-filter-lastseen {
    font-family: var(--ms-font-mono);
    font-size: 9px;
    color: var(--ms-text-mute);
    min-width: 24px;
    text-align: end;
}
.ms-ais-filter-count {
    font-family: var(--ms-font-mono);
    font-size: 10px;
    color: var(--ms-text-dim);
    min-width: 28px;
    text-align: end;
}
.ms-ais-filter-refresh {
    margin-top: 4px;
    font-size: 9px;
}

/* "Showing X of Y" status line (#ais-ui-cleanup) — first row of the body so
   the operator always sees how hard the filters are biting + the reset. */
.ms-ais-filter-status {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    font-family: var(--ms-font-mono);
    font-size: 10px;
    color: var(--ms-text-dim);
    padding: 2px 0 4px 0;
}
.ms-ais-filter-status-active {
    font-size: 9px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ms-warn);
    border: 1px solid color-mix(in srgb, var(--ms-warn) 45%, transparent);
    border-radius: 2px;
    padding: 0 4px;
}
.ms-ais-filter-reset {
    margin-inline-start: auto;
    background: none;
    border: none;
    padding: 0;
    cursor: var(--ms-cursor-pointer);
    font-family: var(--ms-font-mono);
    font-size: 9px;
    color: var(--ms-accent);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.ms-ais-filter-reset:hover { filter: brightness(1.2); }

/* "All on / all off" quick toggles above the provider rows. */
.ms-ais-filter-bulk {
    display: flex;
    gap: 6px;
    padding: 2px 0 4px 0;
}
.ms-ais-filter-bulk .ms-btn.small { font-size: 9px; }

/* Provider silent despite a Live badge — amber so it reads as a caution,
   not an error (the feed may simply have no traffic in the bbox). */
.ms-ais-filter-lastseen.silent {
    color: var(--ms-warn);
    min-width: 38px;
}

/* Display-limits group (max count / distance / size window). Each control
   is label + live value + input on one compact grid row. */
.ms-ais-filter-display {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 2px 0 4px 0;
}
.ms-ais-filter-ctl {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 2px 8px;
    font-size: 10px;
    color: var(--ms-text-dim);
}
.ms-ais-filter-ctl-name { font-family: var(--ms-font-ui); }
.ms-ais-filter-ctl-value {
    font-family: var(--ms-font-mono);
    font-size: 9px;
    color: var(--ms-text-mute);
    text-align: end;
}
.ms-ais-filter-ctl input[type="range"] {
    grid-column: 1 / -1;
    width: 100%;
    margin: 0;
    accent-color: var(--ms-accent);
}
.ms-ais-filter-ctl input[type="number"] {
    width: 64px;
    background: var(--ms-bg);
    color: var(--ms-text);
    border: 1px solid var(--ms-panel-border);
    border-radius: 2px;
    font-family: var(--ms-font-mono);
    font-size: 10px;
    padding: 2px 4px;
    text-align: end;
}
.ms-ais-filter-ctl input[type="number"]:focus {
    outline: none;
    border-color: var(--ms-accent);
}

/* ---- AIS cluster vessel list (popup content, built in JS) ---- */
.ms-ais-cluster-list { color: #0b1220; min-width: 200px; }
.ms-ais-cluster-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 4px;
}
.ms-ais-cluster-zoom {
    background: none;
    border: 1px solid rgba(11, 18, 32, 0.25);
    border-radius: 2px;
    font-size: 10px;
    padding: 1px 6px;
    cursor: pointer;
    color: #1d4ed8;
}
.ms-ais-cluster-zoom:hover { background: rgba(29, 78, 216, 0.08); }
.ms-ais-cluster-list ul {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 180px;
    overflow-y: auto;
}
.ms-ais-cluster-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 2px;
    cursor: pointer;
    border-radius: 2px;
    font-size: 11px;
}
.ms-ais-cluster-row:hover { background: rgba(29, 78, 216, 0.08); }
.ms-ais-cluster-dot {
    flex: none;
    width: 9px; height: 9px;
    border-radius: 50%;
    border: 1px solid rgba(11, 18, 32, 0.35);
}
/* Lazy mini-thumbnail; replaces the dot once the photo actually loads. */
.ms-ais-cluster-thumb {
    flex: none;
    width: 28px; height: 20px;
    border-radius: 3px;
    overflow: hidden;
}
.ms-ais-cluster-thumb img {
    display: block;
    width: 100%; height: 100%;
    object-fit: cover;
}

/* Vessel photo in the track-detail drawer (AIS-materialized tracks). */
.ms-track-photo {
    margin: 0 0 8px;
}
.ms-track-photo img {
    display: block;
    width: 100%;
    max-height: 150px;
    object-fit: cover;
    border-radius: 4px;
    border: 1px solid var(--ms-panel-border);
}
.ms-track-photo figcaption {
    font-size: 9px;
    color: var(--ms-text-mute);
    padding-top: 2px;
}
.ms-ais-cluster-name {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ms-ais-cluster-meta {
    flex: none;
    font-size: 9px;
    opacity: 0.7;
    font-variant-numeric: tabular-nums;
}
.ms-ais-cluster-more {
    padding: 3px 2px;
    font-size: 10px;
    opacity: 0.6;
}

/* "No photo" placeholder inside the AIS popup photo slot. */
.ms-ais-photo-missing {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    margin-bottom: 6px;
    border: 1px dashed rgba(11, 18, 32, 0.25);
    border-radius: 4px;
    color: rgba(11, 18, 32, 0.55);
    font-size: 10px;
}

/* ---- AIS hover mini-tooltip ---- */
.ms-ais-tip .maplibregl-popup-content {
    padding: 4px 8px;
    font-size: 11px;
    line-height: 1.35;
    color: #0b1220;
    pointer-events: none;
}
.ms-ais-tip { pointer-events: none; }

/* (Older RTL override that pinned the AIS filter to inset-inline-end
   was removed — it forced it onto the same side as the LayerFilter,
   producing an overlap. The direction-explicit left/right rule next to
   the .ms-ais-filter base block above already handles RTL correctly.) */

/* Panels in the side rails fill available vertical space proportionally. */
.ms-lagebild-col > .ms-panel { flex: 1 1 0; min-height: 0; }

/* Lightning strikes fade from full opacity to zero across their TTL window
   (LightningOptions.StrikeTtl = 15 min). Operators can read freshness at a
   glance — bright = just hit, dim = nearly evicted by the prune loop.
   `forwards` keeps the final state pinned in case a marker survives slightly
   past TTL between janitor ticks. */
@keyframes ms-fade-strike {
    from { opacity: 1; }
    to   { opacity: 0; }
}
.ms-lightning-marker {
    animation: ms-fade-strike 900s linear forwards;
}

/* Stack at narrower widths — tablet portrait or smaller windows.
   Below this, the map gets full width and panels collapse to a single column above/below. */
@media (max-width: 1280px) {
    .ms-lagebild {
        grid-template-columns: 1fr;
        grid-auto-rows: minmax(140px, auto);
    }
    .ms-lagebild-col.center {
        position: relative;
        height: 50vh;
    }
}

/* ---- Floating action buttons (FABs) ----
   Round, theme-coloured buttons that float over the map. The panels FAB is
   tablet-only (revealed in ms-draggable-panels.css); the fullscreen-exit FAB
   only renders while fullscreen is active. */
.ms-map-fab {
    position: absolute;
    z-index: var(--z-controls); /* was 50 */
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--ms-panel);
    color: var(--ms-accent);
    border: 1px solid var(--ms-panel-border);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
    cursor: var(--ms-cursor-pointer);
    font-size: 18px;
    line-height: 1;
    padding: 0;
}
.ms-map-fab:hover { filter: brightness(1.15); border-color: var(--ms-accent); }
/* FAB-Zonen (Polish v3, Tarek 2026-07-18): alle FABs sitzen auf PHYSISCHEN
   Koordinaten. Die früheren inset-inline-*-Angaben wanderten unter RTL
   (Arabisch = Default) auf die jeweils andere Seite und kollidierten dort
   mit dem top-right-Rail (Wetter-Chip/Filter-Dock) bzw. dem HUD — genau
   die gemeldeten Overlaps. Zonen siehe Layout-Contract oben. */
.ms-map-fab-panels {
    /* Tablet-only Drawer-Toggle: physisch unten rechts, oberhalb der
       MapLibre-Attribution (~24px) — in beiden Schreibrichtungen gleich. */
    bottom: 44px;
    right: 12px;
}
/* (Der frühere Fullscreen-FAB ist in die BridgeControlBar gewandert —
   Bridge-Layout 2026-07-18.) */
/* 🌊 Windy weather overlay FAB — physisch oben links, DIREKT UNTER dem
   CommandCenterHud (top:10px, ≈82px hoch — Sync-Text ist zweizeilig): eigener Slot in der
   top-left-Zone, kollidiert weder mit dem HUD noch mit der vertikal
   zentrierten DrawToolbar. Stays visible in fullscreen — the embed is
   useful there too. */
.ms-map-fab-weather {
    top: 106px;
    left: 12px;
    font-size: 20px;
    /* Brass tint for the wind-streamlines glyph (customer 2026-06-15) — the
       SVG strokes use currentColor, so this colours all three lines. */
    color: var(--ms-warn);
}
.ms-map-fab-weather svg { display: block; }
/* Brass streamlines next to the panel title too, vertically centred. */
.ms-windy-title svg { vertical-align: -3px; color: var(--ms-warn); }

/* ---- Windy weather panel ----
   Right-docked side panel over the map (~half width) on desktop; a
   full-screen sheet on narrow/PWA viewports. Above the rail (z6) and the
   FABs (z50) but below true modals/toasts. The iframe fills the body. */
.ms-windy-panel {
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    width: 50%;
    min-width: 360px;
    z-index: var(--z-panel); /* was 70 */
    display: flex;
    flex-direction: column;
    background: var(--ms-panel);
    border-inline-start: 1px solid var(--ms-panel-border);
    box-shadow: -6px 0 24px rgba(0, 0, 0, 0.45);
}
.ms-windy-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--ms-panel-border);
    background: var(--ms-panel);
}
.ms-windy-title {
    font-weight: 700;
    color: var(--ms-text);
    white-space: nowrap;
}
.ms-windy-overlay {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-inline-start: auto;
}
.ms-windy-overlay-label {
    color: var(--ms-text-dim);
    font-size: 12px;
}
.ms-windy-panel select {
    background: var(--ms-bg2);
    color: var(--ms-text);
    border: 1px solid var(--ms-panel-border);
    border-radius: 6px;
    padding: 4px 8px;
    font-size: 12px;
    cursor: var(--ms-cursor-pointer);
}
.ms-windy-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 6px;
    background: transparent;
    border: 1px solid var(--ms-panel-border);
    color: var(--ms-text);
    cursor: var(--ms-cursor-pointer);
    font-size: 14px;
    line-height: 1;
    padding: 0;
}
.ms-windy-close:hover { border-color: var(--ms-accent); color: var(--ms-accent); }
.ms-windy-body {
    flex: 1;
    min-height: 0;
}
.ms-windy-iframe {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
}
/* Mobile / PWA: full-screen — no half-width side panel (customer brief). */
@media (max-width: 720px) {
    .ms-windy-panel {
        width: 100%;
        min-width: 0;
        inset-inline-start: 0;
        border-inline-start: none;
    }
}

/* ---- Fullscreen map (F shortcut) ----
   The shell stretches to cover the viewport and the floating HUD groups +
   tablet drawer chrome are hidden, leaving just the map (and the
   fullscreen-exit FAB). The map container already fills the shell via
   inset:0, so the only work here is hiding the overlays. */
.ms-lagebild.ms-map-fullscreen {
    position: fixed;
    inset: 0;
    z-index: var(--z-panel); /* was 1000 */
}
.ms-map-fullscreen .ms-hud-groups,
.ms-map-fullscreen .ms-panels-backdrop,
.ms-map-fullscreen .ms-map-fab-panels {
    display: none !important;
}

/* AIS popup vessel photo — Wikidata-backed, fetched after popup mount.
   Displayed only when the contact carries an IMO and Wikidata returned
   a Wikimedia Commons image; otherwise the slot is removed entirely so
   no empty box leaks into the popup. */
.ms-ais-photo {
    margin-bottom: 6px;
    line-height: 0; /* kill the gap below the inline-block <img> */
}
.ms-ais-photo a,
.ms-ais-photo img {
    display: block;
    width: 100%;
    max-width: 280px;
    max-height: 180px;
    object-fit: cover;
    border-radius: 4px;
    /* was var(--ms-bg-mute, #e5e7eb) — that token never existed, so the
       fallback always won; --ms-bg2 is the real "muted surface" token. */
    background: var(--ms-bg2);
}

/* Loading shimmer for the AIS popup vessel photo. Reserves the layout
   slot the photo will fill so the popup doesn't reflow when Wikidata
   responds, and gives a perceptible "fetching" cue during the multi-
   second cold-cache SPARQL latency. Removed by paintVesselPhoto on
   200; .ms-ais-photo itself is removed on 404 / error. */
.ms-ais-photo-loading {
    position: relative;
    height: 120px;
    border-radius: 4px;
    background: linear-gradient(
        90deg,
        var(--ms-bg2) 0%,
        var(--ms-panel-header) 50%,
        var(--ms-bg2) 100%);
    background-size: 200% 100%;
    animation: ms-ais-photo-shimmer 1.4s linear infinite;
}
@keyframes ms-ais-photo-shimmer {
    0%   { background-position: 100% 0; }
    100% { background-position: -100% 0; }
}
@media (prefers-reduced-motion: reduce) {
    .ms-ais-photo-loading { animation: none; }
}

/* ---- Mission dispatch UX (#dispatch-ux) ---------------------------------- */

/* Draggable own-unit marker. The glyph + colour are set inline in JS
   (makeUnitEl) so the marker survives a stylesheet-less first paint; this
   block adds the interaction affordances. */
.ms-unit-marker {
    box-shadow: 0 1px 3px rgba(3, 9, 17, 0.5);
    transition: transform 0.08s ease-out, box-shadow 0.08s ease-out;
    user-select: none;
}
.ms-unit-marker:hover {
    transform: scale(1.15);
    box-shadow: 0 0 8px var(--ms-friend, #a5b4fc);
}

/* One-shot "tasking sent" ping dropped at the target track on mission start.
   An expanding, fading accent ring; the JS removes the marker after the run. */
.ms-dispatch-ping {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 2px solid var(--ms-accent, #22d3ee);
    background: transparent;
    pointer-events: none;
    animation: ms-dispatch-ping 1.2s ease-out forwards;
}
@keyframes ms-dispatch-ping {
    0%   { transform: scale(0.4); opacity: 0.9; }
    70%  { opacity: 0.5; }
    100% { transform: scale(3.2); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
    .ms-dispatch-ping { animation-duration: 0.01s; }
}

/* Track / unit popups opened by click-to-navigate from the side panels.
   Rendered inside MapLibre's default (light) popup, so dark text. */
.ms-map-popup { min-width: 120px; font-size: 12px; color: #0b1220; }
.ms-map-popup-title { font-weight: 700; margin-bottom: 4px; }
.ms-map-popup-row {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
    color: #334155;
}
.ms-map-popup-row + .ms-map-popup-row { margin-top: 2px; }

/* Click-to-navigate highlight (focusOnMap): a longer-lived accent ring that
   pulses for 3 s so the operator's eye lands on the target they just clicked
   in the TracksPanel ("أهداف الرادار") or AssetsPanel ("الوحدات"). */
.ms-focus-pulse {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    border: 3px solid var(--ms-accent, #22d3ee);
    background: transparent;
    pointer-events: none;
    box-shadow: 0 0 12px var(--ms-accent, #22d3ee);
    animation: ms-focus-pulse 1s ease-out 3;
}
@keyframes ms-focus-pulse {
    0%   { transform: scale(0.5); opacity: 0.95; }
    100% { transform: scale(2.6); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
    .ms-focus-pulse { animation: none; opacity: 0.9; }
}

/* ── Area-selection + bulk dispatch (#mission-area-ux) ───────────────────── */

/* Floating area-select toolbar, top-start over the map. */
.ms-area-toolbar {
    position: absolute;
    /* Bottom-left tool corner, above the cursor-coords chip + scale bar.
       The top-left belongs to the (variable-height) CommandCenterHud and
       the left edge's vertical centre to the DrawToolbar — this corner is
       the only left-side slot that can't collide with either. Physical
       left so it never swaps onto the filter-rail side under RTL (#236). */
    bottom: 64px;
    left: 12px;
    z-index: 6;
    display: flex;
    gap: 6px;
    align-items: center;
}

/* Rubber-band selection rectangle drawn while the area-select tool is armed. */
.ms-area-select-box {
    border: 1.5px dashed var(--ms-accent, #22d3ee);
    background: color-mix(in srgb, var(--ms-accent, #22d3ee) 14%, transparent);
    border-radius: 2px;
}

/* Grouping ring on every track framed for a bulk dispatch — a dashed accent
   halo behind the marker so the selected set reads as one group at a glance. */
.ms-track-marker.ms-track-multiselect::before {
    content: "";
    position: absolute;
    inset: -6px;
    border-radius: 50%;
    border: 2px dashed var(--ms-accent, #22d3ee);
    box-shadow: 0 0 8px color-mix(in srgb, var(--ms-accent, #22d3ee) 60%, transparent);
    pointer-events: none;
}

/* Map toolbar button for the area-select tool (lives in the draw toolbar row). */
.ms-area-select-btn.active {
    background: var(--ms-accent, #22d3ee);
    color: var(--ms-bg, #0b1220);
    border-color: var(--ms-accent, #22d3ee);
}

/* ── DispatchModal polish (#mission-area-ux) ────────────────────────────── */

/* Mission-type chooser as big icon buttons instead of a dropdown. */
.ms-mtype-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
    margin-top: 4px;
}
.ms-mtype-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    border: 1px solid var(--ms-panel-border, #1e293b);
    border-radius: 8px;
    background: var(--ms-panel, #0f172a);
    color: var(--ms-text, #e2e8f0);
    cursor: pointer;
    text-align: start;
    transition: border-color 0.12s, background 0.12s, transform 0.06s;
}
.ms-mtype-btn:hover { border-color: var(--ms-accent, #22d3ee); }
.ms-mtype-btn:active { transform: translateY(1px); }
/* Fluid colour-fill on selection: the accent tint is a background-image that
   grows from the inline-start edge (background-size 0 → 100%) instead of
   popping in. RTL flips the anchor edge via background-position. */
.ms-mtype-btn {
    background-image: linear-gradient(
        color-mix(in srgb, var(--ms-accent, #22d3ee) 16%, var(--ms-panel, #0f172a)),
        color-mix(in srgb, var(--ms-accent, #22d3ee) 16%, var(--ms-panel, #0f172a)));
    background-repeat: no-repeat;
    background-position: 0 0;
    background-size: 0% 100%;
    transition: border-color 0.12s, background-size 0.26s ease-out, transform 0.06s, box-shadow 0.12s;
}
html[dir="rtl"] .ms-mtype-btn { background-position: 100% 0; }
.ms-mtype-btn.selected {
    border-color: var(--ms-accent, #22d3ee);
    background-size: 100% 100%;
    box-shadow: inset 0 0 0 1px var(--ms-accent, #22d3ee);
}
@media (prefers-reduced-motion: reduce) {
    .ms-mtype-btn { transition-duration: 0.01s; }
}
.ms-mtype-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.ms-mtype-btn .ms-mtype-ico { font-size: 18px; line-height: 1; }
.ms-mtype-btn .ms-mtype-txt { display: flex; flex-direction: column; }
.ms-mtype-btn .ms-mtype-name { font-weight: 600; font-size: 13px; }
.ms-mtype-btn .ms-mtype-obj { font-size: 11px; color: var(--ms-text-mute, #64748b); }

/* Priority slider with a Low→Critical colour gradient track. */
.ms-prio-slider-wrap { margin-top: 4px; }
.ms-prio-slider {
    width: 100%;
    appearance: none;
    height: 6px;
    border-radius: 3px;
    background: linear-gradient(to right,
        var(--ms-ok, #34d399) 0%,
        var(--ms-accent, #22d3ee) 33%,
        var(--ms-warn, #fbbf24) 66%,
        var(--ms-alert, #f43f5e) 100%);
    outline: none;
}
/* Range inputs mirror in RTL (Low sits on the visual right) — mirror the
   severity gradient with them so colour and value stay aligned. */
html[dir="rtl"] .ms-prio-slider {
    background: linear-gradient(to left,
        var(--ms-ok, #34d399) 0%,
        var(--ms-accent, #22d3ee) 33%,
        var(--ms-warn, #fbbf24) 66%,
        var(--ms-alert, #f43f5e) 100%);
}
.ms-prio-slider::-webkit-slider-thumb {
    appearance: none;
    width: 18px; height: 18px;
    border-radius: 50%;
    background: var(--ms-text, #e2e8f0);
    border: 2px solid var(--ms-bg, #0b1220);
    cursor: pointer;
}
.ms-prio-slider::-moz-range-thumb {
    width: 18px; height: 18px;
    border-radius: 50%;
    background: var(--ms-text, #e2e8f0);
    border: 2px solid var(--ms-bg, #0b1220);
    cursor: pointer;
}
.ms-prio-label {
    margin-top: 4px;
    font-weight: 700;
    font-size: 13px;
}
.ms-prio-label[data-prio="Low"]      { color: var(--ms-ok, #34d399); }
.ms-prio-label[data-prio="Normal"]   { color: var(--ms-accent, #22d3ee); }
.ms-prio-label[data-prio="High"]     { color: var(--ms-warn, #fbbf24); }
.ms-prio-label[data-prio="Critical"] { color: var(--ms-alert, #f43f5e); }

/* Per-step tick labels under the priority slider — all four stops visible at
   once so the gradient has a readable scale, the active stop lights up in its
   severity colour. Follows document direction: range inputs mirror in RTL,
   and so does this flex row. */
.ms-prio-ticks {
    display: flex;
    justify-content: space-between;
    margin-top: 3px;
    font-size: 9px;
    font-family: var(--ms-font-mono);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ms-text-mute);
}
.ms-prio-ticks span { transition: color 140ms ease; }
.ms-prio-ticks span.on { font-weight: 700; }
.ms-prio-ticks span.on[data-prio="Low"]      { color: var(--ms-ok); }
.ms-prio-ticks span.on[data-prio="Normal"]   { color: var(--ms-accent); }
.ms-prio-ticks span.on[data-prio="High"]     { color: var(--ms-warn); }
.ms-prio-ticks span.on[data-prio="Critical"] { color: var(--ms-alert); }

/* Unit recommendation row: type glyph as a small circular avatar with a
   status dot (green = available), plus the availability chip on the best row. */
.ms-rec-row .ms-rec-ico {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--ms-panel-header, #122336);
    border: 1px solid var(--ms-panel-border, #1e293b);
    color: var(--ms-accent, #22d3ee);
    font-size: 13px;
    flex: 0 0 auto;
}
.ms-rec-row .ms-rec-ico::after {
    content: "";
    position: absolute;
    inset-block-end: -1px;
    inset-inline-end: -1px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--ms-ok, #34d399);
    border: 1.5px solid var(--ms-bg2, #0a1622);
    box-shadow: 0 0 4px color-mix(in srgb, var(--ms-ok, #34d399) 70%, transparent);
}
.ms-rec-status {
    font-size: 10px;
    padding: 1px 6px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--ms-ok, #34d399) 22%, transparent);
    color: var(--ms-ok, #34d399);
    white-space: nowrap;
}

/* Notes field with a voice-dictation button. */
.ms-notes-row { position: relative; }
.ms-voice-btn {
    position: absolute;
    inset-inline-end: 6px;
    bottom: 6px;
    width: 30px; height: 30px;
    border-radius: 6px;
    border: 1px solid var(--ms-panel-border, #1e293b);
    background: var(--ms-panel, #0f172a);
    color: var(--ms-text, #e2e8f0);
    cursor: pointer;
    font-size: 15px;
    line-height: 1;
}
.ms-voice-btn.listening {
    color: var(--ms-alert, #f43f5e);
    border-color: var(--ms-alert, #f43f5e);
    animation: ms-voice-pulse 1.1s ease-in-out infinite;
}
@keyframes ms-voice-pulse {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ms-alert, #f43f5e) 60%, transparent); }
    50%      { box-shadow: 0 0 0 5px color-mix(in srgb, var(--ms-alert, #f43f5e) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
    .ms-voice-btn.listening { animation: none; }
}

/* Big confirm button at the modal foot. Press-bounce on click; flips to a
   green ✓ state (.ms-sent) for the success beat before the dialog closes. */
.ms-btn.ms-confirm-lg {
    flex: 1;
    padding: 12px;
    font-size: 14px;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    transition: transform 110ms ease, color 160ms ease, border-color 160ms ease, background 160ms ease;
}
.ms-btn.ms-confirm-lg:active:not(:disabled) { transform: scale(0.97); }
.ms-btn.ms-confirm-lg.ms-sent {
    color: var(--ms-ok, #34d399);
    border-color: var(--ms-ok, #34d399);
    background: color-mix(in srgb, var(--ms-ok, #34d399) 14%, transparent);
    opacity: 1;
}
.ms-sent-check {
    display: inline-block;
    animation: ms-sent-pop 320ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes ms-sent-pop {
    0%   { transform: scale(0.3); opacity: 0; }
    100% { transform: scale(1);   opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .ms-sent-check { animation: none; }
}
.ms-spinner {
    width: 14px; height: 14px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    display: inline-block;
    animation: ms-spin 0.7s linear infinite;
}
@keyframes ms-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .ms-spinner { animation-duration: 2s; }
}

/* ── BulkDispatchModal (#mission-area-ux) ───────────────────────────────── */
.ms-bulk-modal { max-width: 560px; width: min(560px, 94vw); }
.ms-bulk-summary {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; margin-bottom: 8px;
    font-size: 13px; color: var(--ms-text-dim, #94a3b8);
}
.ms-bulk-list { display: flex; flex-direction: column; gap: 8px; max-height: 46vh; overflow-y: auto; }
.ms-bulk-target {
    border: 1px solid var(--ms-panel-border, #1e293b);
    border-radius: 8px;
    padding: 8px 10px;
    background: var(--ms-panel, #0f172a);
}
.ms-bulk-target-head {
    display: flex; align-items: center; gap: 8px; justify-content: space-between;
    margin-bottom: 6px;
}
.ms-bulk-target-name { font-weight: 600; font-size: 13px; }
.ms-bulk-unit-select {
    width: 100%;
    background: var(--ms-bg, #0b1220);
    color: var(--ms-text, #e2e8f0);
    border: 1px solid var(--ms-panel-border, #1e293b);
    border-radius: 6px;
    padding: 6px 8px;
    font-size: 12px;
}
.ms-bulk-target.unassigned { border-color: var(--ms-warn, #fbbf24); opacity: 0.85; }
.ms-bulk-target-meta { font-size: 11px; color: var(--ms-text-mute, #64748b); }
.ms-bulk-result-ok   { color: var(--ms-ok, #34d399); }
.ms-bulk-result-fail { color: var(--ms-alert, #f43f5e); font-size: 11px; }

/* Right-click track → dispatch context menu (#dispatch-ux). The scrim is a
   full-viewport transparent catch so the next click anywhere dismisses; the
   menu sits above it, anchored at the cursor in viewport coordinates. */
.ms-map-ctxmenu-scrim {
    position: fixed;
    inset: 0;
    z-index: var(--z-dropdown); /* context menus = dropdown tier (2000) */
    background: transparent;
}
.ms-map-ctxmenu {
    position: fixed;
    z-index: calc(var(--z-dropdown) + 1);
    min-width: 168px;
    padding: 4px;
    background: var(--ms-panel, #0f1b2d);
    border: 1px solid var(--ms-panel-border, #1e2d44);
    border-radius: 6px;
    box-shadow: 0 6px 20px rgba(3, 9, 17, 0.55);
}
.ms-map-ctxmenu-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    background: transparent;
    border: none;
    border-radius: 4px;
    color: var(--ms-text, #e2e8f0);
    font: inherit;
    font-size: 13px;
    text-align: start;
    cursor: pointer;
}
.ms-map-ctxmenu-item:hover {
    /* was var(--ms-accent-soft, …) — that token never existed; derive the
       soft tint from the real accent so it re-themes. */
    background: color-mix(in srgb, var(--ms-accent) 15%, transparent);
    color: var(--ms-accent, #22d3ee);
}

/* ── Single-panel dispatch (#single-panel-dispatch) ─────────────────────── */
/* The track-detail drawer now hosts the dispatch UI inline, so it can grow
   tall — let it scroll instead of overflowing the viewport. */
.ms-track-detail {
    max-height: calc(100vh - 24px);
    overflow-y: auto;
}
/* Inline dispatch section, set off from the detail read-out above it. */
.ms-dispatch-inline {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--ms-panel-border, #1e293b);
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* Two-column dispatch layout inside the WIDE drawer (customer ask #8).
   Container query, not a viewport media query: the drawer lives in the map
   column whose width is independent of the screen — on a narrow container
   (mobile, slim centre column) the grid stacks back to one column with the
   unit picker first, i.e. the familiar vertical flow. */
.ms-dispatch-grid {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-areas: "units" "setup";
    gap: 10px 18px;
}
.ms-dispatch-col { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.ms-dispatch-col-units { grid-area: units; }
.ms-dispatch-col-setup { grid-area: setup; }
@container (min-width: 640px) {
    .ms-dispatch-grid {
        /* Setup inline-start, unit picker inline-end (flips with RTL). */
        grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
        grid-template-areas: "setup units";
        align-items: start;
    }
    .ms-dispatch-col-units {
        max-height: 320px;
        overflow-y: auto;
        padding-inline-end: 4px;
    }
}
.ms-dispatch-section-title {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ms-text-dim, #94a3b8);
    margin-top: 2px;
}
.ms-dispatch-send { width: 100%; margin-top: 6px; }

/* ── Karten-Cursor (Kundenfeedback 2026-07-18) ──────────────────────────
   Konsistentes Cursor-Vokabular:
     Karte           → Pfeil (Standard); Greifhand NUR während des Pannens
     Panels/Widgets  → Pfeil (erben default)
     Buttons/Klickbares → einheitlich var(--ms-cursor-pointer)
     Marker-Hover    → pointer (per JS inline, gewinnt gegen diese Regeln)
   User-Setting (Admin → Persönliche Einstellungen) via
   html[data-map-cursor]: auto (Pfeil + Hand beim Pannen) · arrow (immer
   Pfeil) · hand (klassische MapLibre-Greifhand). */
:root { --map-cursor: default; }
html[data-map-cursor="hand"]  { --map-cursor: grab; }
html[data-map-cursor="arrow"] { --map-cursor: default; }

.ms-mapview .maplibregl-canvas-container.maplibregl-interactive {
    cursor: var(--map-cursor, default);
}
/* Aktiv gedrückt = Pan läuft → Greifhand (außer explizit "arrow"). */
.ms-mapview .maplibregl-canvas-container.maplibregl-interactive:active {
    cursor: grabbing;
}
html[data-map-cursor="arrow"] .ms-mapview .maplibregl-canvas-container.maplibregl-interactive:active {
    cursor: default;
}

/* ── Bridge-Layout-Chrome (2026-07-18) ─────────────────────────────────── */

/* Bottom-centre control strip — ECDIS posture. Physical coords (zone
   contract); dir="ltr" in markup so the button order is identical in RTL. */
.ms-bridge-bar {
    position: absolute;
    bottom: 12px;
    left: 50%;
    transform: translateX(-50%);
    z-index: var(--z-controls);
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 5px 8px;
    background: var(--ms-panel);
    border: 1px solid var(--ms-panel-border);
    border-radius: 10px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
    backdrop-filter: blur(6px);
}
.ms-bridge-btn {
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid transparent;
    border-radius: 7px;
    background: transparent;
    color: var(--ms-text-dim);
    font-size: 15px;
    line-height: 1;
    padding: 0;
    cursor: var(--ms-cursor-pointer);
}
.ms-bridge-btn:hover { color: var(--ms-text); background: var(--ms-panel-header); }
.ms-bridge-btn.active {
    color: var(--ms-accent);
    border-color: color-mix(in srgb, var(--ms-accent) 45%, transparent);
    background: color-mix(in srgb, var(--ms-accent) 14%, transparent);
}
.ms-bridge-divider { width: 1px; align-self: stretch; margin: 3px 2px; background: var(--ms-panel-border); }
/* Narrow console: the four layer quick-toggles duplicate the filter dock —
   drop them so the bar can't reach the bottom-left stack (scale bar). */
@media (max-width: 1280px) {
    .ms-bridge-bar .ms-bridge-btn:nth-child(-n+4),
    .ms-bridge-bar .ms-bridge-divider:first-of-type { display: none; }
}

/* Wind-speed colour legend, bottom-right ABOVE the geolocate control
   (geolocate + attribution occupy bottom-offsets ~34-86px — measured, see
   WidgetOverlapTests). Only rendered while the wind layer is on. */
.ms-weather-legend {
    position: absolute;
    bottom: 96px;
    right: 12px;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 0;
    padding: 3px 6px;
    background: var(--ms-panel);
    border: 1px solid var(--ms-panel-border);
    border-radius: 6px;
    font-family: var(--ms-font-mono);
    font-size: 9px;
    color: var(--ms-text-dim);
    pointer-events: none;
}
.ms-weather-legend-title { margin-inline-end: 5px; font-weight: 700; }
.ms-weather-legend-band {
    padding: 1px 5px;
    color: #06121d;
    background: var(--band);
    font-weight: 600;
}
.ms-weather-legend-band:first-of-type { border-radius: 3px 0 0 3px; }
.ms-weather-legend-band:last-of-type  { border-radius: 0 3px 3px 0; }

/* ── Popup avatar (self-location & avatars 2026-06-12) ──
   (Die "Wo bin ich?"-Pill ist entfernt, Tarek 2026-07-18 — bottom-centre
   ist wieder frei; der MapLibre-GeolocateControl unten rechts bleibt.) */

.ms-popup-avatar {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid var(--ms-panel-border);
}

/* ── Help-request SOS marker (unit operator "Hilfe brauche", #cc-unit) ──
   Big red pulsing rings around an SOS dot — must out-shout every other
   marker on the map. Acknowledged calls calm down to the warn tone with a
   slower pulse; resolved ones are removed entirely. */
.ms-help-marker {
    position: relative;
    width: 34px;
    height: 34px;
    cursor: pointer;
    z-index: 6;
}
.ms-help-marker-dot {
    position: absolute;
    inset: 4px;
    border-radius: 50%;
    background: var(--ms-alert);
    color: #fff;
    font-size: 13px;
    line-height: 26px;
    text-align: center;
    box-shadow: 0 0 10px var(--ms-alert);
}
.ms-help-marker-ring {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 3px solid var(--ms-alert);
    animation: ms-help-ring 1.6s ease-out infinite;
    pointer-events: none;
}
.ms-help-marker-ring2 { animation-delay: 0.8s; }
@keyframes ms-help-ring {
    0%   { transform: scale(0.6); opacity: 0.9; }
    100% { transform: scale(2.4); opacity: 0; }
}
.ms-help-marker.acked .ms-help-marker-dot {
    background: var(--ms-warn);
    box-shadow: 0 0 6px var(--ms-warn);
}
.ms-help-marker.acked .ms-help-marker-ring {
    border-color: var(--ms-warn);
    animation-duration: 3s;
}
@media (prefers-reduced-motion: reduce) {
    .ms-help-marker-ring { animation: none; opacity: 0.5; }
}

.ms-help-popup { display: flex; flex-direction: column; gap: 4px; min-width: 160px; }
.ms-help-popup-title { font-weight: 700; color: var(--ms-alert); }
.ms-help-popup-reason { font-size: 12px; }
.ms-help-popup-time { font-size: 11px; color: var(--ms-text-mute); font-family: var(--ms-font-mono); }
.ms-help-popup-chat {
    display: inline-block;
    margin-top: 2px;
    padding: 4px 10px;
    border: 1px solid var(--ms-accent);
    border-radius: 6px;
    color: var(--ms-accent);
    text-decoration: none;
    font-size: 12px;
    text-align: center;
}
.ms-help-popup-chat:hover { background: var(--ms-accent); color: var(--ms-bg); }

/* ── AIS popup dispatch button (customer fix 2026-06-12) ── */

.maplibregl-popup-content .ms-ais-dispatch {
    display: block;
    width: 100%;
    margin-top: 8px;
    padding: 7px 10px;
    border: none;
    border-radius: 7px;
    background: #0e7490;
    color: #f8fafc;
    font: inherit;
    font-size: 12.5px;
    font-weight: 700;
    cursor: pointer;
}
.maplibregl-popup-content .ms-ais-dispatch:hover { background: #155e75; }

/* ── Weather advisory banner ─────────────────────────────────────────────
   Moved here from WeatherAdvisoryBanner.razor.css: App.razor never links
   the CSS-isolation bundle (MariSight.Web.styles.css), so scoped files
   are dead in this app — global ms-*.css is the working convention. */

.ms-weather-banner {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    flex-wrap: wrap;
    padding: 7px 12px;
    border-radius: 10px;
    font-size: 0.82rem;
    line-height: 1.2;
    color: var(--ms-bg, #0b1220);
    background: var(--ms-warn, #fbbf24);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
    border: 1px solid rgba(0, 0, 0, 0.18);
    pointer-events: auto;
}

/* RTL: the translateX(-50%) centring still holds; flip nothing else —
   the flex row already follows the logical writing direction. */

.ms-weather-banner.alert {
    background: var(--ms-alert, #ef4444);
    color: #fff;
    animation: ms-weather-banner-pulse 1.6s ease-in-out infinite;
}

@keyframes ms-weather-banner-pulse {
    0%, 100% { box-shadow: 0 4px 16px rgba(239, 68, 68, 0.35); }
    50%      { box-shadow: 0 4px 22px rgba(239, 68, 68, 0.75); }
}

.ms-weather-banner-icon {
    font-size: 1rem;
    flex: 0 0 auto;
}

.ms-weather-banner-title {
    font-weight: 700;
    flex: 0 0 auto;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.ms-weather-banner-items {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 12px;
    min-width: 0;
}

.ms-weather-banner-item {
    white-space: nowrap;
}

.ms-weather-banner-detail {
    opacity: 0.85;
    font-variant-numeric: tabular-nums;
    margin-inline-start: 4px;
}

.ms-weather-banner-close {
    flex: 0 0 auto;
    margin-inline-start: auto;
    background: transparent;
    border: 0;
    color: inherit;
    font-size: 0.95rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0.7;
    padding: 2px 4px;
}

.ms-weather-banner-close:hover { opacity: 1; }


/* Voice-briefing toggle in the dispatch panel (#v2-voice-briefing) */
.ms-voice-briefing-toggle {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 6px 0 8px;
    font-size: 0.8rem;
    color: var(--ms-text-dim);
    cursor: var(--ms-cursor-pointer, pointer);
    user-select: none;
}
.ms-voice-briefing-toggle input { accent-color: var(--ms-accent); width: 15px; height: 15px; }

/* Demo-Mode banner (#v2-demo) — prominent amber strip at the top of the map
   whenever synthetic demo contacts are active, so it's never mistaken for live. */
.ms-demo-banner {
    position: absolute;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
    z-index: var(--z-banner, 1200);
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 6px 14px;
    border-radius: 999px;
    background: var(--ms-warn, #fbbf24);
    color: #0a0e14;
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    box-shadow: 0 4px 16px rgba(0, 0, 0, .4);
}
.ms-demo-dot {
    width: 9px; height: 9px; border-radius: 50%;
    background: #0a0e14;
    animation: ms-demo-pulse 1.2s ease-in-out infinite;
}
@keyframes ms-demo-pulse { 0%,100% { opacity: 1; } 50% { opacity: .3; } }
.ms-demo-stop {
    border: 1px solid #0a0e14;
    background: transparent;
    color: #0a0e14;
    font-weight: 700;
    font-size: 0.72rem;
    padding: 2px 9px;
    border-radius: 999px;
    cursor: var(--ms-cursor-pointer, pointer);
}
.ms-demo-stop:hover { background: #0a0e14; color: var(--ms-warn, #fbbf24); }

/* Basic AR view (#v2-ar) — full-screen rear-camera overlay */
.ms-ar-root { position: fixed; inset: 0; z-index: var(--z-modal, 1100); background: #000; overflow: hidden; }
.ms-ar-video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ms-ar-overlay { position: absolute; inset: 0; pointer-events: none; }
.ms-ar-chip {
    position: absolute; transform: translate(-50%, -50%);
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    padding: 4px 9px; border-radius: 8px;
    background: rgba(6, 12, 20, 0.72);
    border: 1.5px solid var(--ar-cls, #fbbf24);
    box-shadow: 0 0 10px rgba(0,0,0,.6);
    white-space: nowrap;
}
.ms-ar-chip::after {
    content: ""; position: absolute; bottom: -9px; left: 50%; transform: translateX(-50%);
    width: 0; height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent;
    border-top: 8px solid var(--ar-cls, #fbbf24);
}
.ms-ar-chip-name { font-size: 0.78rem; font-weight: 700; color: #fff; }
.ms-ar-chip-meta { font-size: 0.66rem; font-family: var(--ms-font-mono, monospace); color: var(--ar-cls, #fbbf24); }
.ms-ar-reticle {
    position: absolute; top: 42%; left: 50%; width: 26px; height: 26px; margin: -13px 0 0 -13px;
    border: 1.5px solid rgba(255,255,255,.55); border-radius: 50%;
}
.ms-ar-reticle::before, .ms-ar-reticle::after {
    content: ""; position: absolute; background: rgba(255,255,255,.55);
}
.ms-ar-reticle::before { left: 50%; top: -8px; width: 1px; height: 42px; margin-left: -0.5px; }
.ms-ar-reticle::after { top: 50%; left: -8px; height: 1px; width: 42px; margin-top: -0.5px; }
.ms-ar-status {
    position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    max-width: 80%; text-align: center; padding: 14px 18px; border-radius: 10px;
    background: rgba(6, 12, 20, 0.85); color: var(--ms-warn, #fbbf24);
    font-size: 0.9rem; border: 1px solid var(--ms-warn, #fbbf24);
}
.ms-ar-topbar {
    position: absolute; inset-inline: 0; top: 0; display: flex; align-items: center; justify-content: space-between;
    padding: 10px 14px; padding-top: max(10px, env(safe-area-inset-top));
    background: linear-gradient(rgba(0,0,0,.6), transparent); color: #fff;
}
.ms-ar-back {
    background: rgba(6,12,20,.7); border: 1px solid rgba(255,255,255,.25); color: #fff;
    padding: 6px 12px; border-radius: 8px; font-size: 0.82rem; cursor: pointer;
}
.ms-ar-heading { font-weight: 700; font-size: 0.9rem; }
.ms-ar-count { font-family: var(--ms-font-mono, monospace); font-size: 0.82rem; color: var(--ms-accent, #22d3ee); }
.ms-ar-note {
    position: absolute; inset-inline: 0; bottom: 0; text-align: center;
    padding: 8px 14px; padding-bottom: max(8px, env(safe-area-inset-bottom));
    background: linear-gradient(transparent, rgba(0,0,0,.6)); color: rgba(255,255,255,.75); font-size: 0.68rem;
}

/* ── Wind/Strömungs-Partikel-Canvas (Perf-QS 2026-07-20) ─────────────────────
   Stacking INNERHALB des .maplibregl-canvas-container: das Partikel-Canvas
   liegt auf Karten-Ebene (--z-map), alle DOM-Marker (Tracks/Units/AIS) und
   Popups darüber (--z-overlay). Ohne diese Regeln entschied die DOM-Reihen-
   folge — Marker, die VOR dem Canvas erzeugt wurden, verschwanden unter den
   Streifen ("Wind funktioniert nicht richtig", Tarek 2026-07-20). */
.ms-flow-canvas { z-index: var(--z-map); }
#marisight-map .maplibregl-marker,
#marisight-map .maplibregl-popup { z-index: var(--z-overlay); }
