/* ========== MARKERS ========== */
.custom-marker {
  border: 3px solid white; border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg); width: 36px; height: 36px;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 14px rgba(0,0,0,0.6); cursor: pointer;
}
.custom-marker .icon { transform: rotate(45deg); font-size: 16px; }

/* ========== ASSETS ========== */
.asset-icon { background: transparent !important; border: none !important; }
.asset-wrapper {
  width: 72px; height: 72px;
  display: flex; align-items: center; justify-content: center;
  filter: drop-shadow(0 4px 8px rgba(0,0,0,0.7));
  pointer-events: auto;
  position: relative;
  /* Compose rotation + zoom-scale in the stylesheet so CSS-driven rotation
     (via --rot) survives; rotateAssetMarker writes --rot instead of transform. */
  transform: rotate(var(--rot, 0deg)) scale(var(--zoom-scale, 1));
  transform-origin: center center;
  transition: transform 0.25s ease-out;
}
.asset-wrapper svg { width: 100%; height: 100%; overflow: visible; }
/* Pulse assets animate a scaling ring; a drop-shadow filter on the wrapper would
   re-rasterize the whole marker every animation frame (idle jank with many pulse
   markers). Their cores already carry their own box-shadow glow, so drop the
   wrapper filter for pulse types only. */
.asset-wrapper[data-type^="pulse_"] { filter: none; }
.asset-wrapper.selected::before {
  content: ''; position: absolute;
  inset: -10px; border-radius: 50%;
  border: 2.5px dashed var(--accent);
  animation: spin-slow 8s linear infinite, selection-pulse 1.4s ease-in-out infinite;
  pointer-events: none;
}
.marker-label-tooltip {
  background: rgba(20,25,39,0.96) !important;
  border: 1px solid rgba(255,255,255,0.25) !important;
  border-radius: 7px !important;
  padding: 6px 10px !important;
  color: white !important;
  font-size: 12px !important;
  font-weight: 600 !important;
  box-shadow: 0 6px 18px rgba(0,0,0,0.5) !important;
}
.marker-label-tooltip::before { display: none !important; }

.country-highlight-label {
  color: white !important;
  border: none !important;
  border-radius: 6px !important;
  padding: 5px 12px !important;
  font-weight: 700 !important;
  font-size: 13px !important;
  text-transform: uppercase;
  letter-spacing: 1px;
  box-shadow: 0 4px 14px rgba(0,0,0,0.5) !important;
}
.country-highlight-label::before { display: none !important; }


/* ===== LIVE AIS SHIPS ===== */
.ship-marker {
  width: 24px; height: 32px;
  display: flex; align-items: center; justify-content: center;
  filter: drop-shadow(0 2px 3px rgba(0,0,0,0.7));
  pointer-events: auto;
  z-index: 600;
  transform: rotate(var(--rot, 0deg)) scale(var(--zoom-scale, 1));
  transform-origin: center;
  transition: transform 0.3s ease-out;
}
.ship-marker img {
  width: 100%; height: 100%;
  object-fit: contain;
  user-select: none; -webkit-user-drag: none;
  pointer-events: none;
}
.ship-icon-wrapper {
  width: 22px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  transform-origin: center center;
  transition: transform 0.6s ease;
}
.ship-icon-wrapper svg { display: block; }
/* Flight markers */
.flight-icon-wrapper {
  width: 36px; height: 36px; 
  display: flex; align-items: center; justify-content: center;
  transform-origin: center center;
  transition: transform 0.5s ease;
}
.flight-icon-wrapper svg { 
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5));
}


/* Live flight + ship markers using uploaded PNGs */
.flight-marker { background: transparent; border: none; }

.ship-icon-wrapper { background: transparent; border: none; }


/* Combat overlay assets */
.combat-asset {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.combat-asset svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* Pulsing markers */
.pulse-red, .pulse-orange {
  position: relative;
  width: 100%;
  height: 100%;
}
.pulse-core {
  position: absolute;
  top: 50%; left: 50%;
  width: 30%; height: 30%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  z-index: 2;
}
.pulse-red .pulse-core { background: #ff3b30; box-shadow: 0 0 12px #ff3b30; }
.pulse-orange .pulse-core { background: #ff9500; box-shadow: 0 0 12px #ff9500; }
.pulse-ring {
  position: absolute;
  top: 50%; left: 50%;
  width: 30%; height: 30%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 2px solid currentColor;
  will-change: transform, opacity;
  animation: pulseRing 2s ease-out infinite;
}
.pulse-red .pulse-ring { color: #ff3b30; }
.pulse-orange .pulse-ring { color: #ff9500; }
.pulse-ring-2 { animation-delay: 1s; }
/* Compositor-only pulse: fixed 30% box, animate transform scale + opacity (no
   width/height → zero layout/paint per frame). 200/30 ≈ 6.67. */
@keyframes pulseRing {
  0%   { transform: translate(-50%, -50%) scale(1);    opacity: 0.9; }
  100% { transform: translate(-50%, -50%) scale(6.67); opacity: 0; }
}

/* Targeting reticle slow rotation */
.target-reticle svg {
  animation: reticleRotate 6s linear infinite;
}
@keyframes reticleRotate {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}


.pulse-yellow .pulse-core { background: #ffcc00; box-shadow: 0 0 12px #ffcc00; }
.pulse-yellow .pulse-ring { color: #ffcc00; }
.pulse-green .pulse-core { background: #34c759; box-shadow: 0 0 12px #34c759; }
.pulse-green .pulse-ring { color: #34c759; }

.radio-signal svg path {
  animation: radioWave 1.8s ease-out infinite;
}
.radio-signal svg path:nth-of-type(2) { animation-delay: 0.3s; }
.radio-signal svg path:nth-of-type(3) { animation-delay: 0.6s; }
@keyframes radioWave {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 1; }
}

.arrow-marker svg {
  animation: arrowBob 1.5s ease-in-out infinite;
}
@keyframes arrowBob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-3px); }
}


/* CRITICAL: Contain pulse animations within asset palette buttons */
.combat-asset {
  overflow: hidden;
  position: relative;
}
/* Cap pulse ring expansion in PALETTE buttons (smaller) — full size on map */
.asset-palette .combat-asset {
  /* Inside palette: limit ring size */
}
.asset-palette .combat-asset .pulse-ring {
  animation: pulseRingPalette 2s ease-out infinite !important;
}
.asset-palette .combat-asset .pulse-ring-2 {
  animation-delay: 1s;
}
/* Compositor-only palette pulse: 95/30 ≈ 3.17. */
@keyframes pulseRingPalette {
  0%   { transform: translate(-50%, -50%) scale(1);    opacity: 0.9; }
  100% { transform: translate(-50%, -50%) scale(3.17); opacity: 0; }
}


/* ===== MAP LABELS — FINAL AUTHORITATIVE STYLING ===== */
/* Base wrapper — neutral, no background. Children control their own look. */
.map-label {
  background: transparent !important;
  border: none !important;
  pointer-events: none !important;
  user-select: none;
  white-space: nowrap;
  text-align: center;
  font-family: 'Inter', 'Helvetica Neue', -apple-system, sans-serif;
}

/* ================================================================
   PLACE LABELS — one typographic system for country / capital / city.
   A single hierarchy (size · weight · tracking · colour), a shared halo
   recipe so every label sits legibly on satellite or dark basemaps, and
   a dot/star marker instead of ad-hoc decoration. Sizes scale gently
   with zoom via --label-scale so a world view isn't shouted at.
   ================================================================ */
.map-label-country,
.map-label-capital,
.map-label-city {
  display: inline-block;
  white-space: nowrap;
  font-family: 'Inter', 'Helvetica Neue', system-ui, sans-serif !important;
  /* Shared halo: tight black rim for contrast + a soft spread so the text
     never dissolves into bright terrain. */
  text-shadow:
    -1px -1px 0 rgba(0,0,0,0.95),  1px -1px 0 rgba(0,0,0,0.95),
    -1px  1px 0 rgba(0,0,0,0.95),  1px  1px 0 rgba(0,0,0,0.95),
     0 0 5px rgba(0,0,0,0.9), 0 0 12px rgba(0,0,0,0.55) !important;
  transform: scale(var(--label-scale, 1));
  transform-origin: center;
}

/* TIER 1 — COUNTRY: name sits on a soft grey plate so it reads on any
   basemap without shouting (the highlight's own label stays the loud one). */
.map-label-country {
  color: rgba(255,255,255,0.96) !important;
  font-weight: 700 !important;
  font-size: 12px !important;
  letter-spacing: 2.4px !important;
  text-transform: uppercase !important;
  padding: 3px 10px;
  border-radius: 4px;
  background: rgba(28, 33, 46, 0.78) !important;
  border: 1px solid rgba(255,255,255,0.20) !important;
  backdrop-filter: blur(2px);
  /* Plate carries the contrast, so the halo can be light */
  text-shadow: 0 1px 2px rgba(0,0,0,0.85), 0 0 6px rgba(0,0,0,0.5) !important;
}

/* TIER 2 — CAPITAL: its own identity — amber star on a dark amber-tinted
   plate, clearly a different class of place from a plain city. */
.map-label-capital {
  color: #ffe08a !important;
  font-weight: 700 !important;
  font-size: 11.5px !important;
  letter-spacing: 0.7px !important;
  padding: 2px 8px 2px 6px;
  border-radius: 3px;
  background: rgba(58, 42, 12, 0.72) !important;
  border: 1px solid rgba(255, 200, 90, 0.5) !important;
  box-shadow: 0 2px 8px rgba(0,0,0,0.45);
}
.map-label-capital::before {
  content: "★";
  display: inline-block;
  margin-right: 5px;
  font-size: 10px;
  color: #ffc84a;
  text-shadow: 0 0 6px rgba(255,200,74,0.9);
  vertical-align: baseline;
}

/* TIER 3 — CITY: no plate at all so it recedes; a small ring marker plus a
   crisp halo keeps it readable over bright terrain. */
.map-label-city {
  color: rgba(255,255,255,0.88) !important;
  font-weight: 600 !important;
  font-size: 10px !important;
  letter-spacing: 0.3px !important;
  padding: 0 2px;
}
.map-label-city::before {
  content: "";
  display: inline-block;
  width: 4px; height: 4px;
  margin-right: 4px;
  border-radius: 50%;
  background: rgba(255,255,255,0.9);
  box-shadow: 0 0 0 1.5px rgba(0,0,0,0.75);
  vertical-align: middle;
}

/* ===== LIVE FLIGHTS — plane with FIXED-direction shadow for altitude feel =====
   Architecture:
   .plane-wrap (zoom-scale only, no rotation)
     ├─ .plane-shadow (translated down-right, NO rotation, image rotated to match shape)
     └─ .plane-marker (rotated to plane heading, no translate)
*/
.plane-wrap {
  width: 56px; height: 56px;
  position: relative;
  display: block;
  pointer-events: auto;
  /* Only zoom-scale here — no rotation, so shadow offset is consistent */
  transform: scale(var(--zoom-scale, 1));
  transform-origin: center center;
}
/* Shadow: stays at fixed offset (down-right) regardless of plane heading */
.plane-shadow {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  /* Fixed offset — sun is always overhead-left.
     No blur() filter: a live blur surface per plane re-rasterizes on every
     pan/zoom frame (N planes = N filter surfaces). A flat dark silhouette
     reads the same on a broadcast map at far lower GPU cost. */
  transform: translate(9px, 13px);
  filter: brightness(0) opacity(0.32);
  z-index: 1;
  pointer-events: none;
}
/* The shadow IMAGE rotates inside the offset shadow div — so shadow shape matches plane */
.plane-shadow img {
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
  pointer-events: none;
  user-select: none; -webkit-user-drag: none;
  transform: rotate(var(--rot, 0deg));
  transform-origin: center;
  transition: transform 0.6s linear;
}
/* The plane itself: rotates with heading, sits on top */
.plane-marker {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  transform: rotate(var(--rot, 0deg));
  transform-origin: center;
  transition: transform 0.6s linear;
  z-index: 2;
}
.plane-marker img {
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
  pointer-events: none;
  user-select: none; -webkit-user-drag: none;
  filter: drop-shadow(0 1px 1px rgba(0,0,0,0.3));
}



/* ===== CUSTOM TEXT LABELS (place names) ===== */
.custom-text-label {
  position: absolute;
  background: rgba(15, 18, 30, 0.92);
  border: 1.5px solid var(--accent);
  border-radius: 6px;
  padding: 6px 12px;
  color: #fff;
  font-family: 'Inter', sans-serif;
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.5px;
  white-space: nowrap;
  pointer-events: auto;
  cursor: move;
  user-select: none;
  box-shadow: 0 4px 16px rgba(0,0,0,0.4);
  /* No backdrop-filter: these labels ride the moving map pane, so a blur here
     forces a per-frame backdrop readback during pan/zoom. Background is already
     opaque enough to read cleanly over the dark map. */
  z-index: 800;
  transform-origin: center center;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.custom-text-label:hover {
  border-color: #fff;
  box-shadow: 0 6px 24px rgba(0,184,212,0.4);
}
.custom-text-label.selected {
  border-color: #fff;
  box-shadow: 0 0 0 2px var(--accent), 0 6px 24px rgba(0,184,212,0.6);
}
.custom-text-label::before {
  content: "";
  position: absolute;
  bottom: -7px;
  left: 50%;
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 7px solid var(--accent);
}

/* Custom map labels styling */
.custom-map-label {
  background: transparent;
  border: none;
  pointer-events: auto;
}
.custom-map-label-inner {
  display: inline-block;
  padding: 4px 10px;
  background: rgba(0, 0, 0, 0.82);
  color: white;
  font-family: 'Inter', sans-serif;
  font-size: 12px;
  font-weight: 600;
  border-radius: 4px;
  white-space: nowrap;
  border: 1px solid rgba(255,255,255,0.2);
  /* No backdrop-filter: rides the moving map pane (per-frame readback). */
  box-shadow: 0 2px 8px rgba(0,0,0,0.4);
  cursor: move;
  user-select: none;
}
.custom-map-label-inner.style-city {
  font-size: 11px;
  background: rgba(0, 0, 0, 0.6);
  letter-spacing: 0.3px;
}
.custom-map-label-inner.style-country {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 3px;
  text-transform: uppercase;
  background: rgba(0, 0, 0, 0.75);
  border-color: rgba(0, 184, 212, 0.4);
  color: #00d4f0;
  text-shadow: 0 0 12px rgba(0, 212, 240, 0.6);
}
.custom-map-label-inner.style-capital {
  font-size: 12px;
  font-weight: 700;
  background: rgba(0, 0, 0, 0.75);
  color: #ffd54a;
  text-shadow: 0 0 8px rgba(255, 213, 74, 0.5);
  border-color: rgba(255, 213, 74, 0.3);
}
.custom-map-label-inner.style-region {
  font-size: 13px;
  letter-spacing: 1.2px;
  background: rgba(45, 30, 80, 0.7);
  border-color: rgba(140, 100, 220, 0.4);
  color: #d0b8ff;
}
.custom-map-label-inner.style-warning {
  background: rgba(80, 20, 0, 0.85);
  border-color: rgba(255, 100, 50, 0.5);
  color: #ffb380;
  font-weight: 700;
  animation: warn-pulse 1.8s ease-in-out infinite;
}
@keyframes warn-pulse {
  0%, 100% { box-shadow: 0 2px 8px rgba(0,0,0,0.4), 0 0 0 0 rgba(255, 100, 50, 0.4); }
  50% { box-shadow: 0 2px 8px rgba(0,0,0,0.4), 0 0 0 8px rgba(255, 100, 50, 0); }
}
.custom-map-label-inner.is-selected {
  outline: 2px solid #00d4f0;
  outline-offset: 2px;
}


/* =====================================================================
   ANIMATE TOOL — path editing + playback (js/animate.js)
   ===================================================================== */
/* Legacy Loop checkbox is superseded by the dynamically injected Mode
   segment (Once / Loop / Ping-pong). index.html belongs to another
   workstream, so it is hidden from CSS instead of edited out. */
#animLoop { display: none !important; }

/* While the animator drives per-frame heading via --rot, the wrapper's
   0.25s transform transition would lag and fight the JS shortest-arc
   smoothing — disable it for the duration of playback. */
.asset-wrapper.animating { transition: none; }

/* Draggable waypoint handles (small circles) for precise path editing */
.anim-wp-handle {
  width: 14px; height: 14px; border-radius: 50%;
  background: rgba(10, 14, 26, 0.9);
  border: 2.5px solid #ffcc00;
  box-shadow: 0 0 0 2px rgba(0,0,0,0.35), 0 2px 6px rgba(0,0,0,0.5);
  cursor: grab;
  transition: transform 0.12s ease-out, border-color 0.12s;
}
.anim-wp-handle:hover { transform: scale(1.35); border-color: #fff; }
.anim-wp-handle.start { background: #ffcc00; border-color: #fff; }

/* Total-distance readout near the last waypoint while editing a path */
.anim-dist-label {
  background: rgba(20, 25, 39, 0.94);
  border: 1px solid rgba(255, 204, 0, 0.55);
  color: #ffcc00;
  font: 700 11px/1.4 'Inter', sans-serif;
  letter-spacing: 0.4px;
  padding: 3px 8px;
  border-radius: 5px;
  white-space: nowrap;
  box-shadow: 0 3px 10px rgba(0,0,0,0.5);
  pointer-events: none;
}

/* =====================================================================
   PRESENTER FX — 🚀 Throw · 💥 Explosion · ⭕ Pulse (js/assets-tool.js)
   All keyframes animate transform/opacity ONLY (compositor-cheap) and the
   effect roots multiply by the shared --zoom-scale so they size with zoom.
   ===================================================================== */
.fx-toolbar {
  position: fixed; left: 14px; bottom: 110px;
  z-index: 1200;
  display: flex; flex-direction: column; gap: 6px;
  background: rgba(13, 17, 28, 0.92);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: 12px; padding: 7px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.fx-btn {
  width: 34px; height: 34px; border-radius: 8px;
  border: 1px solid transparent; background: transparent;
  font-size: 17px; line-height: 1; padding: 0; cursor: pointer;
  transition: background 0.12s, border-color 0.12s, transform 0.12s;
}
.fx-btn:hover { background: rgba(255,255,255,0.08); transform: scale(1.08); }
.fx-btn.active {
  background: rgba(0,184,212,0.25); border-color: var(--accent);
  box-shadow: 0 0 10px rgba(0,184,212,0.35);
}
/* Visible in build AND presenter (the anchor uses it live); hidden in clean */
body[data-mode="clean"] .fx-toolbar { display: none !important; }

/* Ephemeral effect icons: no leaflet default box, never block map clicks */
.fx-ephemeral-icon { background: transparent !important; border: none !important; pointer-events: none !important; }

/* 💥 Explosion — flash → fireball → shockwave → smoke, self-removing */
.fx-explosion {
  position: relative; width: 160px; height: 160px;
  transform: scale(var(--zoom-scale, 1)); transform-origin: center;
  pointer-events: none;
}
.fx-flash {
  position: absolute; inset: 30%; border-radius: 50%;
  background: radial-gradient(circle, #ffffff, #ffe9a8 45%, rgba(255,190,60,0) 70%);
  animation: fxFlash 0.45s ease-out forwards;
  will-change: transform, opacity;
}
@keyframes fxFlash {
  0%   { transform: scale(0.2); opacity: 1; }
  60%  { transform: scale(2.4); opacity: 0.95; }
  100% { transform: scale(3);   opacity: 0; }
}
.fx-fireball {
  position: absolute; inset: 12%; opacity: 0;
  animation: fxFireball 1.5s ease-out 0.08s forwards;
  will-change: transform, opacity;
}
.fx-fireball img { width: 100%; height: 100%; object-fit: contain; }
@keyframes fxFireball {
  0%   { transform: scale(0.25) rotate(-6deg); opacity: 0; }
  12%  { opacity: 1; }
  55%  { transform: scale(1) rotate(3deg); opacity: 1; }
  100% { transform: scale(1.15) rotate(6deg); opacity: 0; }
}
.fx-shockwave {
  position: absolute; inset: 38%; border-radius: 50%;
  border: 3px solid rgba(255, 220, 150, 0.9); opacity: 0;
  animation: fxShock 1.3s cubic-bezier(0.2, 0.7, 0.3, 1) 0.12s forwards;
  will-change: transform, opacity;
}
@keyframes fxShock {
  0%   { transform: scale(0.3); opacity: 0.95; }
  100% { transform: scale(3.4); opacity: 0; }
}
.fx-smoke {
  position: absolute; inset: 30%; opacity: 0;
  animation: fxSmoke 1.7s ease-out 0.35s forwards;
  will-change: transform, opacity;
}
/* static filter (never animated) — transform/opacity still composite */
.fx-smoke img { width: 100%; height: 100%; object-fit: contain; filter: grayscale(0.6) brightness(0.55) blur(0.5px); }
@keyframes fxSmoke {
  0%   { transform: scale(0.5) translateY(3%); opacity: 0; }
  30%  { opacity: 0.5; }
  100% { transform: scale(0.95) translateY(-14%); opacity: 0; }
}

/* ⭕ Pulse beacon — persistent expanding radar rings in currentColor.
   Only the small core is clickable so the 140px box never swallows map
   clicks; clicks on the core bubble to the marker root for Erase. */
.fx-pulse-icon { background: transparent !important; border: none !important; pointer-events: none !important; }
.fx-pulse-icon .fx-pulse-core { pointer-events: auto; cursor: pointer; }
.fx-pulse {
  position: relative; width: 140px; height: 140px;
  transform: scale(var(--zoom-scale, 1)); transform-origin: center;
}
.fx-pulse-core {
  position: absolute; top: 50%; left: 50%;
  width: 14px; height: 14px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 14px currentColor, 0 0 30px currentColor;
}
.fx-pulse-ring {
  position: absolute; top: 50%; left: 50%;
  width: 120px; height: 120px; margin: -60px 0 0 -60px;
  border-radius: 50%;
  border: 2.5px solid currentColor;
  opacity: 0;
  animation: fxPulseRing 2.4s cubic-bezier(0.25, 0.6, 0.35, 1) infinite;
  will-change: transform, opacity;
}
.fx-pulse-ring.r2 { animation-delay: 0.8s; }
.fx-pulse-ring.r3 { animation-delay: 1.6s; }
@keyframes fxPulseRing {
  0%   { transform: scale(0.12); opacity: 0.95; }
  70%  { opacity: 0.35; }
  100% { transform: scale(1);    opacity: 0; }
}
/* pause looping FX rings during camera flights (same cheapening contract) */
.map-flying .fx-pulse-ring { animation-play-state: paused !important; }

/* =====================================================================
   CAMERA-FLIGHT CHEAPENING
   body.map-flying is toggled on movestart/zoomstart and off shortly after
   moveend/zoomend (see js/map-init.js). During any continuous camera flight
   (pan / zoom / flyTo / animated setView) we pause per-marker animations and
   drop heavy per-marker filters so the compositor only has to translate the
   marker pane instead of re-rastering filtered/animated layers every frame.
   Visuals resume the instant the camera settles — broadcast-clean.
   ===================================================================== */
.map-flying .pulse-ring,
.map-flying .target-reticle svg,
.map-flying .radio-signal svg path,
.map-flying .arrow-marker svg,
.map-flying .custom-map-label-inner.style-warning {
  animation-play-state: paused !important;
}
.map-flying .asset-wrapper,
.map-flying .ship-marker,
.map-flying .plane-marker img {
  filter: none !important;
}
.map-flying .plane-shadow {
  display: none;  /* the shadow silhouette is decorative — hide it mid-flight */
}

/* Live ruler readout — follows the finger while measuring, then the
   permanent label takes over on the second tap. */
/* Distance chips — kilometres only, black plate + white text, sized by the
   number itself so the chip stays as small as possible on air. */
.measure-live, .measure-final {
  display: inline-block;
  width: max-content;
  background: rgba(0, 0, 0, 0.82);
  color: #fff;
  padding: 1px 5px;
  border-radius: 3px;
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 10px;
  font-weight: 700;
  line-height: 1.3;
  white-space: nowrap;
  box-shadow: 0 2px 6px rgba(0,0,0,0.5);
  pointer-events: none;
}

/* Zoom glide: while Leaflet runs its tile zoom animation it puts
   .leaflet-zoom-anim on the container. Scoping the transition to that window
   lets markers/labels grow WITH the map, without adding any lag to the throw
   animation's own per-frame transforms outside a zoom. */
.leaflet-zoom-anim .asset-wrapper,
.leaflet-zoom-anim .plane-wrap,
.leaflet-zoom-anim .ship-marker,
.leaflet-zoom-anim .map-label,
.leaflet-zoom-anim .country-label-pro {
  transition: transform .25s cubic-bezier(0, 0, .25, 1);
}
