:root {
  /* MeshFox — "fox den at night". Night-violet surfaces, an ember → orange →
     amber fox gradient as the brand, and four bright accents (moss, sky,
     berry, sun) for play. Role and status colours are validated for colour
     vision deficiencies (dataviz validator) and always ship with icon/text. */
  --bg: #120D1C;
  --bg-2: #1C1529;
  --bg-3: #2A1F3D;
  --line: #3E2F57;
  --text: #FFF3E3;
  --muted: #B3A3C9;

  --accent: #2FAE72;      /* status: good */
  --warn: #D9A21B;        /* status: attention */
  --err: #E5484D;         /* status: bad */
  --chat: #2FAE72;
  --repeater: #5B8DEF;
  --room: #E0559F;
  --sensor: #C98412;
  --none: #7D7390;

  --brand-fox: #FF7B25;
  --brand-fox-deep: #E0620E;
  --brand-cream: #FFF3E3;
  --brand-eye: #1C9CCB;
  --fox-ember: #FF4E50;
  --fox-amber: #FFB627;
  --moss: #6FE3A5;
  --sky: #4CC9F0;
  --berry: #F15BB5;
  --sun: #FEE440;
  --grape: #9B5DE5;
  --grad-fox: linear-gradient(120deg, #FF4E50 0%, #FF7B25 48%, #FFB627 100%);
  --grad-aurora: linear-gradient(120deg, #9B5DE5 0%, #F15BB5 50%, #FF7B25 100%);
  --grad-sea: linear-gradient(120deg, #1C9CCB 0%, #4CC9F0 50%, #6FE3A5 100%);
  --surface-glass: rgba(28, 21, 41, 0.82);
  --shadow-fox: 0 16px 48px rgba(255,123,37,0.16), 0 24px 60px rgba(0,0,0,0.55);
  --radius-card: 18px;

  --font-display: "Fredoka", "Nunito", system-ui, -apple-system, sans-serif;
  --font-body: "Nunito", system-ui, -apple-system, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, Menlo, monospace;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }

/* --- Boot overlay: covers the app until the initial load is complete. --- */
#boot-overlay {
  position: fixed; inset: 0; z-index: 10000;
  background: rgba(12,8,20, 0.97);
  display: flex; align-items: center; justify-content: center;
  opacity: 1;
  transition: opacity 0.4s ease;
}
#boot-overlay.fade { opacity: 0; pointer-events: none; }
#boot-overlay.gone { display: none; }
.boot-card {
  background: var(--bg-2);
  border: 1px solid var(--line);
  box-shadow:
    inset 0 0 0 1px rgba(255,243,227,0.18),  /* cream callsign frame */
    var(--shadow-fox);
  border-radius: var(--radius-card);
  padding: 26px 28px;
  min-width: 420px; max-width: 540px;
  position: relative;
  display: grid;
  grid-template-columns: 104px 1fr;
  gap: 18px 22px;
  align-items: start;
}
.boot-card::before {
  /* faint radio-arc decoration behind the title — three concentric arcs */
  content: "";
  position: absolute; top: -20px; right: -10px;
  width: 120px; height: 60px;
  pointer-events: none;
  background:
    radial-gradient(closest-side at 50% 100%, transparent 26%, rgba(255,243,227,0.07) 27%, transparent 30%),
    radial-gradient(closest-side at 50% 100%, transparent 40%, rgba(255,243,227,0.06) 41%, transparent 44%),
    radial-gradient(closest-side at 50% 100%, transparent 54%, rgba(255,243,227,0.05) 55%, transparent 58%);
}
.boot-card > .boot-spinner,
.boot-card > .boot-title,
.boot-card > .boot-list,
.boot-card > .boot-hint { grid-column: 2; }
.df-boot-mascot {
  grid-column: 1; grid-row: 1 / span 4;
  width: 96px; height: 96px;
  position: relative;
  display: flex; align-items: center; justify-content: center;
}
.df-boot-mascot img {
  width: 96px; height: 96px;
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity 240ms ease;
  filter: drop-shadow(0 6px 14px rgba(0,0,0,0.45));
}
.df-boot-mascot img.active { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .df-boot-mascot img.active { animation: fox-bob 2.6s ease-in-out infinite; }
}
.boot-spinner {
  width: 22px; height: 22px; margin: 0 0 10px;
  border: 3px solid var(--line);
  border-top-color: var(--brand-fox);
  border-radius: 50%;
  animation: boot-spin 0.9s linear infinite;
}
@keyframes boot-spin { to { transform: rotate(360deg); } }
.boot-title {
  font-family: var(--font-display);
  font-size: 19px; font-weight: 600;
  margin-bottom: 14px;
  color: var(--brand-cream);
  letter-spacing: 0.01em;
}
.boot-list { list-style: none; margin: 0; padding: 0; }
.boot-list li {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 0; font-size: 13px;
  color: var(--muted);
}
.boot-icon {
  width: 16px; height: 16px;
  border: 1.5px solid #3a4258;
  border-radius: 50%;
  flex-shrink: 0;
  position: relative;
}
.boot-list li[data-state="active"] { color: var(--text); }
.boot-list li[data-state="active"] .boot-icon {
  border-color: var(--repeater);
  border-top-color: transparent;
  animation: boot-spin 0.8s linear infinite;
}
.boot-list li[data-state="done"] { color: var(--text); }
.boot-list li[data-state="done"] .boot-icon {
  border-color: var(--accent);
  background: var(--accent);
}
.boot-list li[data-state="done"] .boot-icon::after {
  content: "✓";
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--bg-2); font-size: 11px; font-weight: 700; line-height: 1;
}
.boot-list li[data-state="error"] { color: var(--err); }
.boot-list li[data-state="error"] .boot-icon {
  border-color: var(--err);
  background: var(--err);
}
.boot-list li[data-state="error"] .boot-icon::after {
  content: "!";
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--bg-2); font-size: 11px; font-weight: 700;
}
.boot-label { flex: 1; }
.boot-meta { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.boot-hint {
  margin-top: 16px; padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 11px; color: var(--muted);
  text-align: center;
}

body {
  font: 14px/1.5 var(--font-body);
  background: var(--bg);
  color: var(--text);
  display: flex;
  flex-direction: column;
}

.topbar {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 8px 14px;
  background: var(--bg-2);
  border-bottom: 1px solid var(--line);
  flex: 0 0 auto;
}
.brand {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: 0.04em;
  display: flex;
  align-items: center;
  gap: 0;
  color: var(--brand-cream);
  position: relative;
}
.df-brand-logo {
  height: 32px; width: auto; display: block;
  margin-right: 4px;
}
.df-brand-pulse {
  position: absolute;
  top: 7px; left: 7px;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--brand-fox);
  box-shadow: 0 0 8px var(--brand-fox);
  opacity: 0;
  pointer-events: none;
}
.df-brand-pulse.flash { animation: brand-pulse 1s ease-out; }
@keyframes brand-pulse {
  0%   { opacity: 0;    transform: scale(0.6); }
  18%  { opacity: 0.95; transform: scale(1); }
  100% { opacity: 0;    transform: scale(2.4); }
}
/* legacy .dot rule kept for safety in case any older code re-uses it */
.brand .dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--brand-fox);
  box-shadow: 0 0 8px var(--brand-fox);
}
.tabs { display: flex; gap: 2px; margin-left: 20px; }
.tab {
  background: transparent;
  border: none;
  color: var(--muted);
  padding: 7px 12px 8px;
  border-radius: 0;
  cursor: pointer;
  font: inherit;
  position: relative;
  letter-spacing: 0.02em;
  transition: color 140ms ease;
}
.tab::after {
  content: "";
  position: absolute;
  left: 12px; right: 12px; bottom: 2px;
  height: 2px;
  background: var(--brand-cream);
  border-radius: 1px;
  opacity: 0;
  transform: scaleX(0.4);
  transition: opacity 160ms ease, transform 160ms ease, box-shadow 160ms ease;
}
.tab:hover { color: var(--text); }
.tab:hover::after { opacity: 0.6; transform: scaleX(0.7); box-shadow: 0 0 6px var(--brand-fox); }
.tab.active {
  background: transparent;
  color: var(--brand-cream);
}
.tab.active::after {
  opacity: 1;
  transform: scaleX(1);
  box-shadow: 0 0 8px var(--brand-fox);
}
/* Small help button beside the tabs that re-opens the wizard from any view. */
.tab-help {
  background: transparent;
  border: 1px solid transparent;
  color: var(--muted);
  width: 26px; height: 26px;
  margin-left: 8px; align-self: center;
  border-radius: 50%;
  cursor: pointer;
  font: 600 13px/1 var(--font-display);
  display: flex; align-items: center; justify-content: center;
  transition: border-color 140ms ease, color 140ms ease;
}
.tab-help:hover {
  border-color: var(--brand-fox);
  color: var(--brand-fox);
  box-shadow: 0 0 8px rgba(255,123,37,0.35);
}
.status { margin-left: auto; display: flex; gap: 6px; align-items: center; }
.pill {
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--bg-3);
  border: 1px solid var(--line);
  color: var(--muted);
  font-size: 12px;
}
.pill.ok { color: var(--accent); border-color: rgba(47,174,114,0.4); }
.pill.warn { color: var(--warn); border-color: rgba(217,162,27,0.4); }
.pill.err { color: var(--err); border-color: rgba(229,72,77,0.4); }

/* Live sessions pill — shows how many clients are connected to the WS
   endpoint right now. The dot's box-shadow drives a one-shot flash when
   a new session joins. */
.sessions-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  color: var(--brand-cream);
  border-color: rgba(255,123,37,0.4);
  background: rgba(255,123,37,0.08);
  cursor: default;
  position: relative;
}
.sessions-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--brand-fox);
  box-shadow: 0 0 6px var(--brand-fox);
  flex: 0 0 auto;
  transition: box-shadow 200ms ease;
}
.sessions-count {
  color: var(--brand-fox);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  min-width: 1.4em;
  text-align: center;
}
.sessions-count.bump { animation: sessions-bump 700ms cubic-bezier(.2,.7,.2,1); }
.sessions-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
}
/* Ham-radio operator pill — shows live count of resolved callsigns and
   doubles as a quick filter toggle when the user is on the Node Map. */
.ham-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  color: var(--brand-cream);
  border-color: rgba(255,123,37,0.4);
  background: rgba(255,123,37,0.08);
  cursor: pointer;
  font: inherit;
  padding-top: 2px; padding-bottom: 2px;
  transition: border-color 140ms ease, background 140ms ease, box-shadow 140ms ease;
}
.ham-pill:hover {
  border-color: var(--brand-fox);
  box-shadow: 0 0 8px rgba(255,123,37,0.3);
}
.ham-pill .ham-icon { font-size: 13px; line-height: 1; }
.ham-pill .ham-count {
  color: var(--brand-fox);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.ham-pill .ham-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
}
.ham-pill[data-mode="highlight"] {
  border-color: var(--brand-fox);
  background: rgba(255,123,37,0.16);
  box-shadow: 0 0 8px rgba(255,123,37,0.25);
}
.ham-pill[data-mode="only"] {
  border-color: var(--brand-cream);
  background: rgba(255,243,227,0.12);
  box-shadow: inset 0 0 0 1px var(--brand-fox), 0 0 10px rgba(255,123,37,0.35);
}
.ham-pill[data-mode="only"] .ham-label { color: var(--brand-cream); }
.ham-count.flash { animation: sessions-bump 700ms cubic-bezier(.2,.7,.2,1); }

.sessions-pill.joined .sessions-dot { animation: sessions-pulse 1.4s ease-out 2; }
.sessions-pill.joined { animation: sessions-ring 1.4s ease-out 2; }
@keyframes sessions-pulse {
  0%   { transform: scale(1);    background: var(--brand-fox); box-shadow: 0 0 6px var(--brand-fox); }
  35%  { transform: scale(1.55); background: #FFB066;          box-shadow: 0 0 18px var(--brand-fox); }
  100% { transform: scale(1);    background: var(--brand-fox); box-shadow: 0 0 6px var(--brand-fox); }
}
@keyframes sessions-ring {
  0%   { box-shadow: 0 0 0 0 rgba(255,123,37,0.45); }
  100% { box-shadow: 0 0 0 14px rgba(255,123,37,0); }
}
@keyframes sessions-bump {
  0%   { transform: scale(1);    color: var(--brand-fox); }
  30%  { transform: scale(1.35); color: #FFB066; }
  100% { transform: scale(1);    color: var(--brand-fox); }
}

main {
  flex: 1 1 auto;
  display: grid;
  /* Only the radar keeps a right sidebar (its message list); every other
     view is full width (body.no-sidebar). */
  grid-template-columns: 1fr 340px;
  overflow: hidden;
}
body.radar-active main { grid-template-columns: 1fr 340px; }
.view {
  position: relative;
  display: none;
  overflow: hidden;
}
.view.active { display: block; }
.leaflet { height: 100%; width: 100%; background: #0d0914; }

.map-controls {
  position: absolute;
  top: 12px; right: 12px;
  background: rgba(28,21,41,0.92);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px 12px;
  display: flex; flex-direction: column; gap: 6px;
  font-size: 12px;
  color: var(--text);
  z-index: 500;
  backdrop-filter: blur(6px);
  min-width: 200px;
}
/* Collapsible header for the node-map filter card. Lets the user fold the
   panel away to see the full map; state is persisted in localStorage. */
.ctl-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ctl-title { font-weight: 600; color: var(--text); letter-spacing: 0.02em; }
.ctl-collapse {
  background: transparent; color: var(--muted);
  border: 1px solid var(--line); border-radius: 4px;
  width: 20px; height: 20px; line-height: 1;
  font-size: 14px; cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center;
}
.ctl-collapse:hover { color: var(--text); border-color: var(--text); }
.ctl-body { display: flex; flex-direction: column; gap: 6px; }
.map-controls.collapsed { min-width: 0; gap: 0; }
.map-controls.collapsed .ctl-body { display: none; }
.ctl-row { display: flex; align-items: center; gap: 8px; color: var(--muted); }
.ctl-row select, .ctl-row input[type=range] {
  background: var(--bg-3); color: var(--text);
  border: 1px solid var(--line); border-radius: 4px;
  padding: 1px 4px; font: inherit;
}

/* Floating map control panels share the same shell + position as
   .map-controls — top-right, panel-styled — so the UI is consistent. */
.radar-legend {
  position: absolute;
  top: 12px; right: 12px;
  background: rgba(28,21,41,0.92);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px 12px;
  display: flex; flex-direction: column; gap: 6px;
  font-size: 12px;
  color: var(--text);
  z-index: 500;
  backdrop-filter: blur(6px);
  min-width: 200px;
  max-width: 320px;
}

.sidebar {
  position: relative;
  background: var(--bg-2);
  border-left: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: width 0.18s ease, min-width 0.18s ease;
}
body.sidebar-collapsed main { grid-template-columns: 1fr 32px; }
body.sidebar-collapsed .sidebar { min-width: 32px; }
body.sidebar-collapsed .sidebar-head,
body.sidebar-collapsed .feed,
body.sidebar-collapsed .obs-panel { display: none; }
body.sidebar-collapsed .sidebar-collapse::before { content: "‹"; }

/* The sidebar only belongs to the radar; other views fill the screen. */
body.no-sidebar main { grid-template-columns: 1fr; }
body.no-sidebar .sidebar { display: none; }

/* Radar canvas: when the main sidebar is collapsed, the obs-panel is
   hidden too (rule above). Without this override the map auto-flows
   into the still-allocated 360px column and ends up half-width. */
body.sidebar-collapsed .view-radar.active { grid-template-columns: 1fr; }
.sidebar-collapse {
  position: absolute;
  top: 6px; right: 6px;
  width: 22px; height: 22px;
  background: var(--bg-3);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 4px;
  font: inherit;
  font-size: 14px;
  cursor: pointer;
  z-index: 2;
  display: flex; align-items: center; justify-content: center;
  padding: 0;
}
.sidebar-collapse::before { content: "›"; }
.sidebar-collapse:hover { border-color: var(--accent); color: var(--accent); }
.sidebar-head {
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 12px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.sidebar-head .filter { text-transform: none; letter-spacing: 0; color: var(--text); display: flex; align-items: center; gap: 5px; font-size: 13px; }

/* Color legend bar (gradient strip) under the "color by" selector */
.color-legend { flex-direction: column; align-items: stretch; gap: 3px; }
.legend-bar {
  height: 10px; border-radius: 4px;
  background: linear-gradient(to right,
    rgb(12,28,64),
    rgb(62,116,200),
    rgb(180,200,80),
    rgb(220,170,60),
    rgb(220,90,60));
  border: 1px solid var(--line);
}
.legend-axis {
  display: flex; justify-content: space-between;
  font-size: 10px; color: var(--muted);
  font-family: ui-monospace, monospace;
}

/* Loading banner + spinner (positioned absolutely inside a view) */
.loading-banner {
  position: absolute;
  top: 12px; left: 50%;
  transform: translateX(-50%);
  background: rgba(28,21,41,0.92);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 8px 14px 8px 12px;
  display: flex; align-items: center; gap: 10px;
  font-size: 12px;
  color: var(--text);
  z-index: 600;
  backdrop-filter: blur(6px);
  box-shadow: 0 2px 12px rgba(0,0,0,0.4);
  min-width: 220px; max-width: 80%;
}
.loading-banner[hidden] { display: none; }
.loading-text-wrap { display: flex; flex-direction: column; line-height: 1.3; min-width: 0; flex: 1; }
.loading-sub {
  font-size: 11px; color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.loading-sub:empty { display: none; }
.spinner {
  width: 14px; height: 14px;
  border: 2px solid rgba(47,174,114,0.25);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.feed {
  margin: 0; padding: 0;
  list-style: none;
  overflow-y: auto;
  flex: 1 1 auto;
}
.feed li {
  padding: 7px 12px;
  border-bottom: 1px solid var(--line);
  font-size: 12px;
  display: grid;
  grid-template-columns: 60px 1fr auto;
  gap: 6px 10px;
}
.feed li.flash { animation: flash 1.4s ease-out; }
@keyframes flash {
  from { background: rgba(47,174,114,0.18); }
  to { background: transparent; }
}
.feed .ptype { color: var(--muted); font-family: ui-monospace, Menlo, monospace; }
.feed .ptype.advert { color: var(--accent); }
.feed .origin { color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.feed .meta { color: var(--muted); font-family: ui-monospace, Menlo, monospace; text-align: right; }
.feed li.advert .ptype { color: var(--accent); }

/* Marker popups */
.leaflet-popup-content-wrapper {
  background: var(--bg-2);
  color: var(--text);
  border: 1px solid var(--line);
}
.leaflet-popup-content {
  margin: 8px 12px;
  font-size: 12px;
  line-height: 1.5;
}
.leaflet-popup-tip { background: var(--bg-2); }
.popup-title { font-weight: 600; margin-bottom: 4px; }
.popup-role {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 3px;
  font-size: 11px;
  margin-left: 6px;
}
.popup-meta { color: var(--muted); font-family: ui-monospace, monospace; font-size: 11px; }

.role-chat { background: var(--chat); color: #0a1a14; }
.role-repeater { background: var(--repeater); color: #06162a; }
.role-room { background: var(--room); color: #1a0a2a; }
.role-sensor { background: var(--sensor); color: #2a1606; }
.role-none, .role-unknown { background: var(--none); color: #0a1116; }

/* Custom div icons */
.node-marker {
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.5);
  box-shadow: 0 0 6px rgba(0,0,0,0.6);
  transition: transform 140ms ease, opacity 140ms ease;
  transform-origin: center;
}
/* Marker-style options driven by the map-controls select. Shrinking via
   transform keeps the click target at 14×14 while the visible dot becomes
   ~3 px (pixel) or invisible (hidden) — preserves popup interactivity in
   pixel mode without disturbing the rest of the layout. */
body.markers-pixel .node-marker {
  transform: scale(0.22);
  border-width: 1px;
  box-shadow: 0 0 2px rgba(0,0,0,0.5);
}
body.markers-hidden .node-marker {
  opacity: 0;
  pointer-events: none;
  transform: scale(0);
}
/* When markers are hidden, suppress their permanent name labels too —
   floating callouts with no anchor look orphaned. Forced labels (clicked
   per-node reveals) stay visible because the user explicitly asked for
   them. */
body.markers-hidden .leaflet-tooltip.node-label:not(.forced) {
  display: none;
}
.node-marker.chat { background: var(--chat); }
.node-marker.repeater { background: var(--repeater); }
.node-marker.room { background: var(--room); }
.node-marker.sensor { background: var(--sensor); }
.node-marker.none, .node-marker.unknown { background: var(--none); }

/* Amateur-radio callsign markers — three states driven by the .map-controls
   "ham" select via body classes:
     callsigns-off       — no special styling
     callsigns-highlight — ring around the marker (verified = solid cream,
                           pending lookup = dashed muted) + soft glow
     callsigns-only      — non-callsign markers hidden entirely; callsign
                           markers get the highlight treatment for clarity. */
body.callsigns-highlight:not(.markers-hidden) .node-marker.has-callsign,
body.callsigns-only:not(.markers-hidden)      .node-marker.has-callsign {
  /* Make ham markers visibly larger than the surrounding dots — even
     when the user has put the global marker style on "pixel". The cream
     inner ring + double orange glow halo lifts them off the dark map.
     Guarded against markers-hidden so "hide" still hides everything. */
  transform: scale(1.45);
  border: 2px solid var(--brand-cream);
  background-image: linear-gradient(0deg, rgba(255,255,255,0.15), rgba(255,255,255,0));
  box-shadow:
    0 0 0 2px var(--brand-fox),
    0 0 12px 2px rgba(255,123,37,0.85),
    0 0 24px 6px rgba(255,123,37,0.35);
  z-index: 600;
}
body.callsigns-highlight:not(.markers-hidden) .node-marker.has-callsign.callsign-verified,
body.callsigns-only:not(.markers-hidden)      .node-marker.has-callsign.callsign-verified {
  /* QRZ-verified gets a tighter, brighter halo — the eye picks them out
     among the surrounding orange rings. Plus a slow breathing pulse so
     they're discoverable on a dense map. */
  transform: scale(1.6);
  box-shadow:
    0 0 0 2px #FFD58A,
    0 0 14px 3px var(--brand-fox),
    0 0 30px 8px rgba(255,123,37,0.55);
}
@media (prefers-reduced-motion: no-preference) {
  body.callsigns-highlight:not(.markers-hidden) .node-marker.has-callsign.callsign-verified,
  body.callsigns-only:not(.markers-hidden)      .node-marker.has-callsign.callsign-verified {
    animation: ham-verified-breathe 2.4s ease-in-out infinite;
  }
}
@keyframes ham-verified-breathe {
  0%, 100% {
    box-shadow:
      0 0 0 2px #FFD58A,
      0 0 14px 3px var(--brand-fox),
      0 0 30px 8px rgba(255,123,37,0.55);
  }
  50% {
    box-shadow:
      0 0 0 2px #FFEDB8,
      0 0 18px 5px var(--brand-fox),
      0 0 42px 12px rgba(255,123,37,0.75);
  }
}
body.callsigns-only .node-marker:not(.has-callsign) {
  opacity: 0;
  pointer-events: none;
  transform: scale(0);
}
/* In "only" mode the marker pane gets a subtle vignette so the ham dots
   look anchored rather than floating in space. */
body.callsigns-only .leaflet-marker-pane::before {
  content: "";
  position: absolute; inset: 0;
  background: radial-gradient(ellipse at center, transparent 55%, rgba(255,123,37,0.04));
  pointer-events: none;
}
body.callsigns-only .leaflet-tooltip.node-label:not(.forced) {
  /* Hide auto-labels on non-callsign nodes (forced clicks stay visible). */
  display: none;
}
.ctl-stat {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--muted);
  margin-left: auto;
  padding-left: 6px;
  letter-spacing: 0.04em;
}

/* Popup ham-radio block. Sits between the title and the meta lines.
   Brand-orange callsign link, optional QRZ-verified pill. */
.popup-ham {
  margin: 6px 0 8px;
  padding: 6px 8px;
  background: rgba(255,123,37,0.08);
  border: 1px solid rgba(255,123,37,0.35);
  border-radius: 6px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--brand-cream);
}
.popup-ham-pending { background: rgba(255,243,227,0.06); border-color: rgba(255,243,227,0.18); }
.popup-ham-call {
  font-family: var(--font-mono);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--brand-fox);
  text-decoration: none;
  border-bottom: 1px dashed rgba(255,123,37,0.5);
}
.popup-ham-call:hover { color: #FFB066; border-bottom-color: var(--brand-fox); }
.popup-ham-verified {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  margin-left: 6px;
  padding: 1px 5px;
  border-radius: 3px;
  background: rgba(52,199,89,0.15);
  border: 1px solid rgba(52,199,89,0.5);
  color: #6BD693;
  letter-spacing: 0.06em;
}
/* Node name labels (Leaflet permanent tooltips) */
.leaflet-tooltip.node-label {
  background: rgba(28,21,41,0.78);
  border: 1px solid var(--line);
  color: var(--text);
  font-size: 11px;
  padding: 1px 6px;
  border-radius: 3px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.5);
  backdrop-filter: blur(2px);
  white-space: nowrap;
}
.leaflet-tooltip.node-label::before { display: none; }
.leaflet-tooltip.node-label.forced {
  background: rgba(40,28,60,0.88);
  border-color: rgba(199,156,255,0.5);
  color: #ffe;
  font-weight: 500;
}
.leaflet-tooltip.node-label.dim { opacity: 0.55; }

.node-marker.pulse { animation: pulse 1.2s ease-out; }
@keyframes pulse {
  0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(47,174,114,0.7); }
  100% { transform: scale(1); box-shadow: 0 0 0 14px rgba(47,174,114,0); }
}

/* --- Radar --- */
.view-radar { display: none; }
.view-radar.active { display: grid; grid-template-columns: 360px 1fr; }
body:not(.radar-active) .view-radar.active { grid-template-columns: 1fr; }
body:not(.radar-active) .obs-panel { display: none; }

.obs-panel {
  background: var(--bg-2);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-width: 0;
}
.obs-head {
  padding: 9px 12px;
  border-bottom: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 12px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.obs-count {
  background: var(--bg-3);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 8px;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text);
}
.obs-msgbody {
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
  font-size: 13px;
  line-height: 1.4;
  max-height: 30vh;
  overflow-y: auto;
}
.obs-msgbody .obs-empty { color: var(--muted); }
.obs-msgbody .obs-text { color: var(--text); white-space: pre-wrap; word-break: break-word; margin: 4px 0; }
.obs-msgbody .obs-sender { color: var(--accent); font-weight: 600; }
.obs-msgbody .obs-row { display: flex; justify-content: space-between; gap: 8px; color: var(--muted); font-size: 11px; margin-top: 4px; }
.obs-msgbody .obs-row b { color: var(--text); font-weight: 500; }
.obs-tablewrap { overflow-y: auto; flex: 1 1 auto; }
.obs-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11px;
}
.obs-table th, .obs-table td {
  padding: 5px 8px;
  text-align: left;
  border-bottom: 1px solid var(--line);
}
.obs-table th {
  position: sticky; top: 0;
  background: var(--bg-3);
  color: var(--muted);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  font-size: 10px;
}
.obs-table tbody tr { cursor: pointer; }
.obs-table tbody tr:hover { background: var(--bg-3); }
.obs-table tbody tr.focused { background: rgba(255,201,85,0.15); }
.obs-table td.observer { color: var(--text); font-weight: 500; max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.obs-table td.path { font-family: ui-monospace, monospace; color: var(--muted); max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Sidebar header dropdowns */
.dd-label { display: flex; gap: 6px; align-items: center; color: var(--text); text-transform: none; letter-spacing: 0; font-size: 12px; }
.dd-label select {
  background: var(--bg-3);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 3px 6px;
  font: inherit;
}
.dd-label select option:disabled { color: var(--muted); }
/* The channel dropdown on the Radar tab is intentionally prominent.
   Previous version used translucent mint on the teal sidebar which left
   the select almost invisible. Now: solid raised surface, cream text for
   high contrast, brand-fox border so it reads as the key control. */
.dd-label.channel-dd {
  font-size: 13px;
  color: var(--muted);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 11px;
}
.dd-label.channel-dd select {
  background: var(--bg-3);
  border: 1px solid rgba(255,123,37,0.55);
  color: var(--brand-cream);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 13px;
  letter-spacing: 0.02em;
  padding: 5px 12px;
  border-radius: 6px;
  box-shadow:
    inset 0 0 0 1px rgba(255,243,227,0.08),
    0 1px 3px rgba(0,0,0,0.3);
  transition: border-color 140ms ease, box-shadow 140ms ease;
}
.dd-label.channel-dd select:hover,
.dd-label.channel-dd select:focus {
  border-color: var(--brand-fox);
  outline: none;
  box-shadow:
    inset 0 0 0 1px rgba(255,243,227,0.12),
    0 0 0 3px rgba(255,123,37,0.18);
}
.dd-label.channel-dd select option {
  background: var(--bg-2);
  color: var(--brand-cream);
}

/* Radar header holds the channel + region dropdowns side by side. */
.ctx-radar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
/* Region filter: same shape as the channel control but a neutral border, so
   the channel stays the primary selector and the region reads as secondary. */
.dd-label.region-dd {
  font-size: 13px; color: var(--muted); font-weight: 500;
  letter-spacing: 0.04em; text-transform: uppercase;
}
.dd-label.region-dd select {
  background: var(--bg-3); border: 1px solid var(--line);
  color: var(--brand-cream); font-family: var(--font-display);
  font-weight: 600; border-radius: 5px; padding: 4px 8px;
}
.dd-label.region-dd select:hover,
.dd-label.region-dd select:focus { border-color: var(--brand-fox); outline: none; }
.dd-label.region-dd select option { background: var(--bg-2); color: var(--brand-cream); }

/* (.radar-legend shell defined above with other floating panels) */
.rl-row { display: flex; align-items: center; gap: 8px; color: var(--muted); }
.rl-dot { width: 12px; height: 12px; border-radius: 50%; display: inline-block; }
.rl-dot.rl-origin { background: #ff5577; box-shadow: 0 0 8px #ff5577; }
.rl-dot.rl-hop { background: #ffc955; }
.rl-dot.rl-observer { background: #2FAE72; border: 2px solid #2FAE72; background: transparent; }
.rl-line { display: inline-block; width: 22px; height: 2px; background: linear-gradient(90deg, #ff5577, #2FAE72); }
.rl-circle { display: inline-block; width: 14px; height: 14px; border-radius: 50%; box-sizing: border-box; }
.rl-circle.rl-circle-hop { border: 2px solid rgba(255,201,85,0.7); background: rgba(255,201,85,0.15); }
.rl-circle.rl-circle-obs { border: 2px dashed rgba(47,174,114,0.7); background: rgba(47,174,114,0.10); }
.rl-msg {
  margin-top: 6px;
  color: var(--text);
  font-style: italic;
  white-space: pre-wrap;
  word-break: break-word;
  border-top: 1px solid var(--line);
  padding-top: 6px;
}

.radar-marker { border: 2px solid rgba(255,255,255,0.5); border-radius: 50%; box-shadow: 0 0 8px rgba(0,0,0,0.6); }
.radar-marker.origin { background: #ff5577; width: 22px; height: 22px; box-shadow: 0 0 18px #ff5577; animation: pulse-origin 1.8s infinite; }
.radar-marker.hop { background: #ffc955; width: 12px; height: 12px; }
.radar-marker.observer { background: rgba(47,174,114,0.2); border-color: #2FAE72; width: 16px; height: 16px; }
@keyframes pulse-origin {
  0% { box-shadow: 0 0 0 0 rgba(255,85,119,0.7); }
  100% { box-shadow: 0 0 0 24px rgba(255,85,119,0); }
}

/* Public-channel message rows */
.feed.msgs li {
  grid-template-columns: 1fr;
  gap: 4px;
  cursor: pointer;
  padding: 10px 12px 11px;
}
.feed.msgs li:hover { background: var(--bg-3); }
.feed.msgs li.selected { background: rgba(255,201,85,0.12); border-left: 2px solid #ffc955; }
.feed.msgs .msg-head {
  display: flex; justify-content: space-between; align-items: baseline;
  color: var(--muted); font-size: 11px;
}
.feed.msgs .msg-sender {
  color: var(--text); font-weight: 600; font-size: 13px;
}
.feed.msgs .msg-time { font-variant-numeric: tabular-nums; }
.feed.msgs .msg-body {
  color: var(--text); white-space: pre-wrap; word-break: break-word;
  font-size: 13px; line-height: 1.35;
}

/* Pills row — channel / region / hops. Each pill has its own accent color
   so the eye can pick out specific metadata at a glance. */
.feed.msgs .msg-pills {
  display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px;
}
.feed.msgs .msg-pill {
  font-size: 10px; font-weight: 600;
  padding: 2px 7px;
  border-radius: 999px;
  background: var(--bg-3);
  color: var(--muted);
  border: 1px solid var(--line);
  letter-spacing: 0.2px;
  line-height: 1.4;
}
.feed.msgs .msg-pill.chan {
  color: var(--accent);
  border-color: rgba(47,174,114,0.4);
  background: rgba(47,174,114,0.08);
}
.feed.msgs .msg-pill.region {
  color: var(--repeater);
  border-color: rgba(111,180,255,0.4);
  background: rgba(111,180,255,0.08);
  font-family: ui-monospace, Menlo, monospace;
}
.feed.msgs .msg-pill.hops {
  color: var(--warn);
  border-color: rgba(217,162,27,0.4);
  background: rgba(217,162,27,0.08);
  font-family: ui-monospace, Menlo, monospace;
}
.feed.msgs .msg-pill.observers {
  color: var(--accent);
  border-color: rgba(47,174,114,0.4);
  background: rgba(47,174,114,0.08);
  font-family: ui-monospace, Menlo, monospace;
  transition: transform 0.2s ease, background 0.2s ease;
}
.feed.msgs .msg-pill.observers.just-bumped {
  animation: pill-bump 0.5s ease-out;
}
@keyframes pill-bump {
  0%   { transform: scale(1);    background: rgba(47,174,114,0.45); }
  35%  { transform: scale(1.18); background: rgba(47,174,114,0.45); }
  100% { transform: scale(1);    background: rgba(47,174,114,0.08); }
}
/* Role pill on adverts: reuses the existing role-* color palette but
   keeps the .msg-pill shape (rounded, small). */
.feed.msgs .msg-pill.role {
  border-color: transparent;
  font-weight: 700;
}
.feed.msgs .advert-pk {
  font-family: ui-monospace, Menlo, monospace;
  font-size: 11px;
  color: var(--muted);
  letter-spacing: 0.3px;
}

/* Signal gauges — SNR + RSSI as horizontal bars, color tracks quality. */
.feed.msgs .msg-signals {
  display: flex; flex-direction: column; gap: 3px; margin-top: 4px;
}
.feed.msgs .msg-sig {
  display: grid;
  grid-template-columns: 34px 1fr 64px;
  align-items: center;
  gap: 8px;
  font-size: 10px;
  color: var(--muted);
  font-family: ui-monospace, Menlo, monospace;
}
.feed.msgs .sig-label {
  letter-spacing: 0.4px;
  text-transform: uppercase;
  font-size: 9px;
}
.feed.msgs .sig-bar {
  height: 5px;
  background: rgba(255,255,255,0.04);
  border-radius: 3px;
  overflow: hidden;
  border: 1px solid rgba(255,255,255,0.04);
}
.feed.msgs .sig-fill {
  display: block; height: 100%; border-radius: 3px;
  transition: width 0.25s ease;
}
.feed.msgs .sig-val {
  text-align: right; color: var(--text);
  font-variant-numeric: tabular-nums;
}

.feed.msgs .msg-foot {
  font-size: 10px; color: var(--muted); margin-top: 4px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.feed.msgs .msg-foot-obs { color: var(--text); }

.feed.msgs .msg-meta { color: var(--muted); font-size: 11px; }

/* =========================================================================
   DF0X surface chrome: consent banner, first-visit wizard, glass-panel base,
   Leaflet attribution badge, keyframes, reduced-motion overrides.
   These were added alongside the boot overlay reskin; they live at the end
   of the stylesheet so the cascade lets them override existing component
   rules where they share class names. Do not move above the boot rules.
   ========================================================================= */

.glass-panel {
  background: var(--surface-glass);
  border: 1px solid var(--line);
  box-shadow:
    inset 0 0 0 1px rgba(255,243,227,0.18),
    var(--shadow-fox);
  border-radius: var(--radius-card);
  backdrop-filter: blur(8px);
}

/* ----- Pre-boot consent banner ----- */
.df-consent {
  position: fixed; inset: 0;
  z-index: 10001;
  background: radial-gradient(ellipse at center, rgba(28,21,41,0.85), rgba(12,8,20,0.95));
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
}
.df-consent[hidden] { display: none; }
.df-consent__card {
  width: min(560px, 100%);
  padding: 28px 32px 22px;
  display: grid;
  grid-template-columns: 96px 1fr;
  gap: 8px 24px;
  align-items: start;
  animation: consent-rise 280ms cubic-bezier(.2,.7,.2,1) both;
}
.df-consent__mascot {
  grid-column: 1; grid-row: 1 / span 5;
  width: 96px; height: 96px;
  border-radius: 50%;
  border: 2px solid rgba(255,243,227,0.35);
  box-shadow: 0 8px 24px rgba(0,0,0,0.45);
  margin-top: 4px;
}
.df-consent__eyebrow {
  grid-column: 2;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--brand-fox);
  margin: 0 0 4px;
}
.df-consent__card h1 {
  grid-column: 2;
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 600;
  color: var(--brand-cream);
  letter-spacing: 0.01em;
}
.df-consent__lead {
  grid-column: 2;
  margin: 0 0 14px;
  color: var(--text);
  line-height: 1.5;
}
.df-consent__lead code {
  font-family: var(--font-mono);
  background: rgba(255,243,227,0.08);
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 0.88em;
  color: var(--brand-cream);
}
.df-consent__stats {
  grid-column: 2;
  list-style: none; padding: 0; margin: 0 0 16px;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}
.df-consent__stats li {
  background: rgba(255,123,37,0.06);
  border: 1px solid rgba(255,123,37,0.22);
  border-radius: 8px;
  padding: 6px 8px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--brand-cream);
}
.df-consent__stats li b {
  display: block;
  font-size: 13px;
  color: var(--brand-fox);
  font-weight: 600;
  letter-spacing: 0.02em;
}
.df-consent__cta {
  grid-column: 2;
  background: linear-gradient(180deg, #FF9133, var(--brand-fox-deep));
  color: #1A1A1A;
  border: none;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 11px 22px;
  border-radius: 999px;
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.3),
    0 6px 18px rgba(255,123,37,0.35);
  transition: transform 120ms ease, box-shadow 120ms ease;
  justify-self: start;
}
.df-consent__cta:hover {
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.35),
    0 10px 24px rgba(255,123,37,0.45);
}
.df-consent__cta:active { transform: translateY(0); }
.df-consent__dontshow {
  grid-column: 2;
  margin-top: 12px;
  display: flex; align-items: center; gap: 8px;
  color: var(--muted);
  font-size: 12px;
  cursor: pointer;
  user-select: none;
}
.df-consent__dontshow input { accent-color: var(--brand-fox); }
.df-consent__foot {
  grid-column: 1 / -1;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid rgba(255,243,227,0.18);
  font-size: 11px;
  font-family: var(--font-mono);
  color: var(--muted);
  text-align: center;
  letter-spacing: 0.04em;
}

/* ----- First-visit wizard ----- */
.df-wizard {
  position: fixed; inset: 0;
  z-index: 9000;
  background: rgba(12,8,20,0.72);
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  backdrop-filter: blur(2px);
}
.df-wizard[hidden] { display: none; }
.df-wizard__panel {
  width: min(820px, 100%);
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  padding: 24px 28px 20px;
  position: relative;
  animation: consent-rise 240ms cubic-bezier(.2,.7,.2,1) both;
}
.df-wizard__close {
  position: absolute; top: 12px; right: 14px;
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 22px;
  width: 30px; height: 30px;
  border-radius: 50%;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 120ms ease, color 120ms ease;
}
.df-wizard__close:hover {
  background: rgba(255,243,227,0.08);
  color: var(--brand-cream);
}
.df-wizard__hero {
  display: grid;
  grid-template-columns: 96px 1fr;
  gap: 6px 22px;
  align-items: center;
  margin-bottom: 18px;
}
.df-wizard__fox {
  width: 96px; height: 96px;
  grid-row: 1 / span 2;
}
.df-wizard__hero h2 {
  margin: 0 0 4px;
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 600;
  color: var(--brand-cream);
}
.df-wizard__hero p {
  margin: 0;
  color: var(--text);
  line-height: 1.5;
}
.df-wizard__presets {
  list-style: none;
  padding: 0; margin: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
.df-wizard__presets li { display: contents; }
.df-preset-card {
  background: rgba(255,243,227,0.04);
  border: 1px solid rgba(255,243,227,0.15);
  border-radius: 10px;
  padding: 12px 14px;
  text-align: left;
  font: inherit;
  color: var(--text);
  cursor: pointer;
  display: flex; flex-direction: column; gap: 6px;
  transition: border-color 140ms ease, background 140ms ease, transform 140ms ease;
}
.df-preset-card:hover {
  border-color: var(--brand-fox);
  background: rgba(255,123,37,0.06);
  transform: translateY(-1px);
}
.df-preset-card:focus-visible {
  outline: 2px solid var(--brand-fox);
  outline-offset: 2px;
}
.df-preset-card__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 14px;
  color: var(--brand-cream);
  display: flex; align-items: center; gap: 8px;
}
.df-preset-card__title::before {
  content: "";
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--brand-fox);
  box-shadow: 0 0 6px var(--brand-fox);
}
.df-preset-card__chips {
  display: flex; flex-wrap: wrap; gap: 4px;
}
.df-preset-card__chip {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.02em;
  padding: 2px 7px;
  border-radius: 999px;
  background: rgba(74,144,184,0.14);
  color: var(--brand-eye);
  border: 1px solid rgba(74,144,184,0.32);
}
.df-preset-card__why {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.4;
}
.df-preset-card.reset { background: rgba(255,255,255,0.03); }
.df-preset-card.reset .df-preset-card__title::before { background: var(--muted); box-shadow: none; }
.df-wizard__foot {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid rgba(255,243,227,0.18);
  display: flex; justify-content: space-between; align-items: center;
  font-size: 12px; color: var(--muted);
}
.df-wizard__dontshow {
  display: flex; align-items: center; gap: 6px;
  cursor: pointer; user-select: none;
}
.df-wizard__dontshow input { accent-color: var(--brand-fox); }
.df-wizard__hint {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
}

/* Leaflet attribution gets a small cream badge. The Leaflet zoom-control
   buttons inherit a tighter dark style via these overrides. */
.leaflet-control-attribution {
  background: rgba(28,21,41,0.85) !important;
  color: var(--muted) !important;
  font-family: var(--font-mono);
  font-size: 10px;
  border-top-left-radius: 8px !important;
  padding: 2px 8px !important;
  border: 1px solid var(--line);
}
.leaflet-control-attribution a {
  color: var(--brand-cream) !important;
}
.leaflet-attribution-badge {
  display: inline-block;
  margin-right: 6px;
  padding: 0 6px;
  border-radius: 999px;
  background: rgba(255,243,227,0.14);
  color: var(--brand-cream);
  font-weight: 600;
  letter-spacing: 0.04em;
}
.leaflet-bar a {
  background: rgba(28,21,41,0.92) !important;
  color: var(--brand-cream) !important;
  border-color: var(--line) !important;
}
.leaflet-bar a:hover {
  background: var(--bg-2) !important;
  color: var(--brand-fox) !important;
}

/* ----- keyframes ----- */
@keyframes consent-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes fox-bob {
  0%, 100% { transform: translateY(0) rotate(-1.5deg); }
  50%      { transform: translateY(-3px) rotate(1.5deg); }
}

/* ----- reduced motion: cancel decorative motion (bob, rise) but keep
   spinners turning since they convey "loading is happening". ----- */
@media (prefers-reduced-motion: reduce) {
  .df-boot-mascot img.active,
  .df-consent__card,
  .df-wizard__panel,
  .df-brand-pulse.flash,
  .feed li.flash,
  .focus-node-marker,
  .node-analysis,
  .pill.feed-stalled,
  .chat-panel,
  .feed.msgs .msg-pill.observers.just-bumped { animation: none !important; }
}

/* Upstream feed stalled — pulse the status pill so it's obvious at a glance. */
.pill.feed-stalled { animation: feedStallPulse 1.1s ease-in-out infinite; }
@keyframes feedStallPulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(229,72,77,0.55); }
  50%      { opacity: 0.7; box-shadow: 0 0 0 5px rgba(229,72,77,0); }
}

/* ===================================================================
   Mapper chat (floating bottom-left widget)
   =================================================================== */
.chat-widget {
  position: fixed;
  left: 14px; bottom: 14px;
  z-index: 800;          /* above analysis panel + sidebar, below modals */
  font-size: 13px;
}
.chat-toggle {
  width: 46px; height: 46px; border-radius: 50%;
  background: var(--accent); color: #0c2a22;
  border: none; cursor: pointer;
  box-shadow: 0 6px 18px rgba(0,0,0,0.45);
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; line-height: 1; position: relative;
  transition: transform 0.15s ease, filter 0.15s ease;
}
.chat-toggle:hover { transform: translateY(-1px); filter: brightness(1.08); }
.chat-toggle-badge {
  position: absolute; top: -4px; right: -4px;
  min-width: 18px; height: 18px; padding: 0 5px; box-sizing: border-box;
  background: var(--err); color: #fff;
  border-radius: 9px; font-size: 11px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid var(--bg);
}
.chat-widget[data-collapsed="false"] .chat-toggle { display: none; }

.chat-panel {
  width: min(330px, 92vw);
  height: 380px;
  display: flex; flex-direction: column;
  padding: 0; overflow: hidden;
  animation: chatRise 0.18s ease;
}
@keyframes chatRise { from { transform: translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }
.chat-panel[hidden] { display: none; }

.chat-head {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-2);
}
.chat-title {
  font-weight: 600; color: var(--text); flex: 1;
}
.chat-presence {
  color: var(--muted); font-size: 11px;
  display: inline-flex; align-items: center; gap: 4px;
}
.chat-presence-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 6px var(--accent);
}
.chat-close {
  background: none; border: none; color: var(--muted);
  font-size: 20px; line-height: 1; cursor: pointer; padding: 2px 4px;
}
.chat-close:hover { color: var(--text); }

.chat-list {
  list-style: none; margin: 0; padding: 8px 10px;
  flex: 1 1 auto; overflow-y: auto;
  display: flex; flex-direction: column; gap: 6px;
  background: var(--bg);
}
.chat-empty {
  color: var(--muted); font-size: 12px; text-align: center;
  padding: 16px 4px;
}
.chat-msg {
  display: flex; flex-direction: column; gap: 1px;
  padding: 5px 8px;
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: 6px;
  word-break: break-word; overflow-wrap: anywhere;
}
.chat-msg-meta {
  display: flex; gap: 6px; align-items: baseline;
  font-size: 10.5px; color: var(--muted);
}
.chat-msg-name { color: var(--accent); font-weight: 600; }
.chat-msg-name.is-me { color: var(--repeater); }
.chat-msg-text { color: var(--text); font-size: 12.5px; line-height: 1.35; }

.chat-form {
  display: flex; flex-direction: column; gap: 5px;
  padding: 8px 10px 10px;
  border-top: 1px solid var(--line);
  background: var(--bg-2);
}
.chat-name {
  background: var(--bg); color: var(--text);
  border: 1px solid var(--line); border-radius: 5px;
  padding: 4px 7px; font: inherit; font-size: 11.5px;
  outline: none;
}
.chat-name:focus, .chat-input:focus { border-color: var(--accent); }
.chat-row { display: flex; gap: 5px; }
.chat-input {
  flex: 1 1 auto;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--line); border-radius: 5px;
  padding: 6px 8px; font: inherit; font-size: 12.5px;
  outline: none;
}
.chat-send {
  background: var(--accent); color: #0c2a22;
  border: none; border-radius: 5px;
  padding: 0 10px; font: inherit; font-weight: 700; cursor: pointer;
  min-width: 34px;
}
.chat-send:hover { filter: brightness(1.08); }
.chat-send:disabled { opacity: 0.45; cursor: not-allowed; filter: none; }
.chat-hint { color: var(--warn); font-size: 11px; }
.chat-hint[hidden] { display: none; }

/* ===================================================================
   Node search + per-node focus analysis (Node Map)
   =================================================================== */
.node-search {
  position: absolute;
  top: 12px; left: 12px;
  /* Above the analysis panel (z 600) so a new search can be typed — and its
     results dropdown shown — while node details are open, without closing them. */
  z-index: 700;
  width: min(340px, 42vw);
}
.node-search input {
  width: 100%; box-sizing: border-box;
  background: rgba(28,21,41,0.92); color: var(--text);
  border: 1px solid var(--line); border-radius: 8px;
  padding: 9px 12px; font: inherit; font-size: 13px;
  backdrop-filter: blur(6px); outline: none;
}
.node-search input:focus { border-color: var(--accent); }
.node-search input::placeholder { color: var(--muted); }

.node-search-results {
  list-style: none; margin: 6px 0 0; padding: 4px;
  background: rgba(28,21,41,0.96); border: 1px solid var(--line);
  border-radius: 8px; backdrop-filter: blur(6px);
  max-height: 46vh; overflow-y: auto;
  box-shadow: 0 8px 24px rgba(0,0,0,0.4);
}
.node-search-results[hidden] { display: none; }
.ns-item {
  display: flex; flex-direction: column; gap: 1px;
  padding: 6px 9px; border-radius: 6px; cursor: pointer;
  border-left: 2px solid transparent;
}
/* .active is the keyboard/mouse highlight (kept in sync in JS); the accent
   border marks which row Enter will pick. */
.ns-item:hover, .ns-item:focus, .ns-item.active { background: var(--bg-3); outline: none; }
.ns-item.active { border-left-color: var(--accent); }
.ns-row { display: flex; align-items: baseline; gap: 8px; }
.ns-name { color: var(--text); font-size: 13px; font-weight: 500; flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* "Xm ago" recency, right-aligned and dimmed so it reads as metadata. */
.ns-seen { color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums;
  white-space: nowrap; flex-shrink: 0; }
.ns-sub { color: var(--muted); font-size: 11px; }
/* "How & where last seen" — the node's most recent observed activity. */
.ns-last {
  display: block; margin-top: 2px;
  color: var(--accent); font-size: 11px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ns-empty { padding: 8px 9px; color: var(--muted); font-size: 12px; }

/* Base node markers stay fully visible while a node is focused, so the filter
   card (marker style, ham, names) keeps working map-wide. The focus emphasis
   comes from the #focusPane overlay (zIndex 640): coloured neighbour edges, a
   pulsing ring on the focused node, and forced labels — all drawn on top. */
#map .leaflet-marker-pane { transition: opacity 0.25s; }

.focus-node-marker {
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid #ffd34d; background: rgba(255,211,77,0.25);
  animation: focusPulse 1.8s ease-out infinite;
}
@keyframes focusPulse {
  0%   { box-shadow: 0 0 0 0 rgba(255,211,77,0.45); }
  70%  { box-shadow: 0 0 0 14px rgba(255,211,77,0); }
  100% { box-shadow: 0 0 0 0 rgba(255,211,77,0); }
}

/* Analysis panel — slides in on the left, under the search box. */
.node-analysis {
  position: absolute;
  top: 58px; left: 12px; bottom: 12px;
  z-index: 600; width: min(340px, 42vw);
  padding: 14px 14px 16px;
  display: flex; flex-direction: column;
  color: var(--text); overflow-y: auto;
  animation: naSlideIn 0.25s ease;
}
.node-analysis[hidden] { display: none; }
@keyframes naSlideIn {
  from { transform: translateX(-12px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.na-close {
  position: absolute; top: 8px; right: 10px;
  background: none; border: none; color: var(--muted);
  font-size: 22px; line-height: 1; cursor: pointer; padding: 2px 6px;
}
.na-close:hover { color: var(--text); }
.na-head { padding-right: 26px; margin-bottom: 10px; }
.na-name { font-size: 15px; font-weight: 600; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.na-ham { font-size: 12px; margin-top: 3px; }
.na-ham a { color: var(--accent); text-decoration: none; }
.na-meta { color: var(--muted); font-size: 11px; margin-top: 3px; font-family: ui-monospace, monospace; }

.na-stats {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px;
  margin-bottom: 4px;
}
.na-stat {
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: 6px; padding: 6px 4px; text-align: center;
}
.na-stat-v { display: block; font-size: 14px; font-weight: 600; color: var(--text); }
.na-stat-l { display: block; font-size: 10px; color: var(--muted); margin-top: 1px; }

.na-section-title {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); margin: 12px 0 6px;
}
.na-hint { text-transform: none; letter-spacing: 0; }

.na-spark { width: 100%; height: 40px; display: block; }
.na-spark rect { fill: var(--repeater); }
.na-spark-cap { font-size: 11px; color: var(--muted); margin-top: 3px; }
.na-spark-empty { font-size: 12px; color: var(--muted); }

/* Last-advert summary */
.na-la-line { font-size: 12px; color: var(--text); }
.na-la-when { color: var(--text); font-variant-numeric: tabular-nums; }
.na-la-who { color: var(--accent); }
.na-la-path { margin-top: 3px; font-size: 11.5px; line-height: 1.45; }
.na-la-chain { color: var(--muted); word-break: break-word; }
.na-la-dim { color: var(--muted); font-style: italic; }
/* "Last" recency column in the neighbours table */
.na-cell-last { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

.na-tablewrap {
  flex: 1 1 auto; overflow-y: auto; min-height: 90px;
  border: 1px solid var(--line); border-radius: 6px;
}
.na-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.na-table th, .na-table td { padding: 4px 8px; text-align: right; }
.na-table th:first-child, .na-table td:first-child { text-align: left; }
.na-table th {
  position: sticky; top: 0; background: var(--bg-3); color: var(--muted);
  font-weight: 500; cursor: pointer; user-select: none; white-space: nowrap;
}
.na-table th.sorted { color: var(--text); }
.na-table th.sorted[data-dir="asc"]::after  { content: " ▲"; font-size: 9px; }
.na-table th.sorted[data-dir="desc"]::after { content: " ▼"; font-size: 9px; }
.na-table tbody tr { cursor: pointer; border-top: 1px solid var(--line); }
.na-table tbody tr:hover { background: var(--bg-3); }
.na-cell-name { color: var(--text); max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.na-row-nearby .na-cell-name { color: var(--muted); }
.na-near-tag { font-size: 9px; color: var(--sensor); border: 1px solid var(--line); border-radius: 3px; padding: 0 3px; }

.popup-analyze {
  margin-top: 8px; width: 100%;
  background: var(--accent); color: #0c2a22;
  border: none; border-radius: 6px; padding: 6px 8px;
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
}
.popup-analyze:hover { filter: brightness(1.08); }

/* =========================================================================
   Antenna test view
   ========================================================================= */
.view-observer { overflow-y: auto; background: var(--bg); }
.ant-scroll {
  max-width: 1320px; margin: 0 auto;
  padding: 16px 20px 40px;
  display: flex; flex-direction: column; gap: 14px;
  color: var(--text);
}

/* header */
/* position:relative + z-index lifts the whole header (and its combobox popup)
   above the cards below — each glass-panel card makes its own stacking context
   via backdrop-filter, so the popup's own z-index alone can't escape them. */
.ant-head { padding: 12px 16px; display: flex; flex-direction: column; gap: 10px; position: relative; z-index: 5; }
.ant-head-row { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
.ant-spacer { flex: 1 1 auto; }
.ant-field { display: flex; flex-direction: column; gap: 3px; }
.ant-field-label { color: var(--muted); font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; }
.ant-field select, .ant-field input, #ant-label {
  background: var(--bg-3); color: var(--text);
  border: 1px solid var(--line); border-radius: 5px;
  padding: 4px 8px; font: inherit; font-size: 13px;
}
.ant-field-obs { min-width: 280px; }

/* searchable observer combobox */
.ant-combo { position: relative; }
.ant-combo-btn {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  background: var(--bg-3); color: var(--text);
  border: 1px solid var(--line); border-radius: 5px;
  padding: 6px 10px; font: inherit; font-size: 13px; cursor: pointer; text-align: left;
}
.ant-combo-btn:hover, .ant-combo.open .ant-combo-btn { border-color: var(--accent); }
.ant-combo-val { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.ant-combo-val.placeholder { color: var(--muted); }
.ant-combo-caret { color: var(--muted); font-size: 10px; flex: none; transition: transform 0.12s ease; }
.ant-combo.open .ant-combo-caret { transform: rotate(180deg); }
.ant-combo-pop {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 800;
  width: min(380px, 88vw);
  background: rgba(28,21,41,0.97); border: 1px solid var(--line);
  border-radius: 8px; backdrop-filter: blur(6px);
  box-shadow: 0 8px 24px rgba(0,0,0,0.45);
  padding: 6px; display: flex; flex-direction: column; gap: 6px;
}
.ant-combo-pop[hidden] { display: none; }
.ant-combo-search {
  width: 100%; box-sizing: border-box;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--line); border-radius: 6px;
  padding: 7px 10px; font: inherit; font-size: 13px; outline: none;
}
.ant-combo-search:focus { border-color: var(--accent); }
.ant-combo-search::placeholder { color: var(--muted); }
.ant-combo-list {
  list-style: none; margin: 0; padding: 0;
  max-height: 44vh; overflow-y: auto;
  display: flex; flex-direction: column; gap: 2px;
}
.ant-combo-item {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 7px 9px; border-radius: 6px; cursor: pointer;
  border-left: 2px solid transparent;
}
.ant-combo-item:hover, .ant-combo-item.active { background: var(--bg-3); }
.ant-combo-item.active { border-left-color: var(--accent); }
.ant-combo-item.selected { border-left-color: var(--brand-fox); }
.ant-combo-name { color: var(--text); font-size: 13px; font-weight: 500; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ant-combo-name b { color: var(--accent); font-weight: 700; }
.ant-combo-meta { color: var(--muted); font-size: 11px; flex: none; font-variant-numeric: tabular-nums; }
.ant-combo-empty { color: var(--muted); font-size: 12px; padding: 8px 9px; text-align: center; }
.ant-obs-meta { color: var(--muted); font-size: 12px; font-family: var(--font-mono); padding-bottom: 4px; }
.ant-obs-pk { color: var(--muted); }
.ant-antctl { align-items: center; }
.ant-antctl .ant-field-label { align-self: center; }
#ant-label { min-width: 240px; }
.ant-btn {
  background: var(--brand-fox); color: #1b1206;
  border: none; border-radius: 6px; padding: 6px 12px;
  font: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
}
.ant-btn:hover { filter: brightness(1.08); }
.ant-seglist { display: flex; gap: 6px; flex-wrap: wrap; }
.ant-seg { font-size: 11px; color: var(--muted); border: 1px solid var(--line); border-radius: 12px; padding: 2px 9px; }
.ant-seg.current { color: #1b1206; background: var(--brand-fox); border-color: var(--brand-fox); font-weight: 700; }

/* placeholder vs grid toggle */
.view-observer .ant-grid { display: none; }
.view-observer.ant-ready .ant-grid { display: grid; }
.view-observer.ant-ready .ant-placeholder { display: none; }
.ant-placeholder { display: flex; justify-content: center; padding: 40px 0; }
.ant-placeholder-card { max-width: 540px; padding: 28px 30px; text-align: center; }
.ant-placeholder-icon { font-size: 40px; }
.ant-placeholder-card h2 { margin: 8px 0 6px; }
.ant-placeholder-card p { color: var(--muted); font-size: 13px; line-height: 1.55; margin: 0; }

/* grid */
.ant-grid { grid-template-columns: minmax(0, 1.7fr) minmax(320px, 1fr); gap: 14px; align-items: start; }
.ant-col-left, .ant-col-right { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
@media (max-width: 1000px) { .ant-grid { grid-template-columns: 1fr; } }

/* cards */
.ant-card { padding: 14px 16px 16px; }
.ant-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.ant-card-title { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text); }
.ant-card-sub { font-size: 11px; color: var(--muted); font-family: var(--font-mono); }
.ant-count { background: var(--bg-3); border-radius: 10px; padding: 0 7px; font-size: 11px; color: var(--accent); }

/* chart */
.ant-chart-wrap { display: flex; gap: 6px; height: 260px; }
.ant-chart-yaxis {
  display: flex; flex-direction: column; justify-content: space-between;
  color: var(--muted); font-size: 10px; font-family: var(--font-mono);
  text-align: right; width: 30px; padding: 6px 0;
}
.ant-chart-plot {
  position: relative; flex: 1 1 auto; min-width: 0;
  background: rgba(0,0,0,0.18); border: 1px solid var(--line); border-radius: 6px; overflow: hidden;
}
.ant-chart { width: 100%; height: 100%; display: block; }
.ant-chart-marks { position: absolute; inset: 0; pointer-events: none; }
.ant-mark {
  position: absolute; top: 2px;
  color: var(--brand-fox); font-size: 10px; font-weight: 600; white-space: nowrap;
  background: rgba(28,21,41,0.72); padding: 0 3px; border-radius: 3px;
}
.ant-chart-axis { display: flex; justify-content: space-between; color: var(--muted); font-size: 10.5px; margin-top: 4px; font-family: var(--font-mono); padding-left: 36px; }

/* current-antenna summary */
.ant-summary { background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius-card); padding: 12px 16px; }
.ant-summary-head { font-size: 11px; color: var(--muted); margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.06em; }
.ant-stats { display: flex; gap: 22px; flex-wrap: wrap; }
.ant-stat { display: flex; flex-direction: column; gap: 2px; }
.ant-stat-v { font-family: var(--font-mono); font-size: 20px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; line-height: 1; }
.ant-stat-l { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }

/* live packet log */
.ant-logwrap { max-height: 240px; overflow-y: auto; }
.ant-log { width: 100%; border-collapse: collapse; font-size: 12px; }
.ant-log th, .ant-log td { padding: 3px 8px; text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.ant-log th:nth-child(2), .ant-log td:nth-child(2), .ant-log th:nth-child(3), .ant-log td:nth-child(3) { text-align: left; }
.ant-log th { position: sticky; top: 0; background: var(--bg-3); color: var(--muted); font-weight: 500; }
.ant-log td:nth-child(2) { max-width: 170px; overflow: hidden; text-overflow: ellipsis; color: var(--text); }
.ant-log tr.relay td { color: var(--muted); }
.ant-log tr.direct td:nth-child(2) { color: var(--accent); }

/* directly-received neighbours */
.ant-neighsort { display: flex; align-items: center; gap: 6px; color: var(--muted); font-size: 11px; margin-bottom: 8px; }
.ant-neighsort select { background: var(--bg-3); color: var(--text); border: 1px solid var(--line); border-radius: 5px; padding: 2px 6px; font: inherit; font-size: 12px; }
.ant-toggle { display: flex; align-items: center; gap: 5px; color: var(--muted); font-size: 11px; cursor: pointer; }
.ant-neighlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; max-height: 560px; overflow-y: auto; }
.ant-neigh { background: var(--bg-2); border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px; cursor: pointer; }
.ant-neigh:hover { border-color: var(--accent); }
.ant-neigh.selected { border-color: var(--brand-fox); box-shadow: inset 0 0 0 1px var(--brand-fox); }
.ant-neigh-top { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.ant-neigh-name { font-weight: 600; color: var(--text); display: flex; align-items: center; gap: 5px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ant-neigh-time { color: var(--muted); font-size: 11px; font-family: var(--font-mono); flex: none; }
.ant-neigh-mid { display: flex; align-items: baseline; gap: 14px; margin: 4px 0 3px; }
.ant-neigh-sig b { font-family: var(--font-mono); font-size: 17px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.ant-neigh-sig i { font-style: normal; color: var(--muted); font-size: 10px; margin-left: 3px; }
.ant-neigh-n { margin-left: auto; color: var(--muted); font-size: 11px; }
.ant-neigh-rel { color: var(--accent); }
.ant-neigh-spark { height: 24px; }
.ant-spark { width: 100%; height: 24px; display: block; }
.ant-badge { font-size: 12px; font-weight: 700; flex: none; }
.ant-badge.ok { color: var(--accent); }
.ant-badge.warn { color: var(--warn); }

/* A/B comparison */
.ant-collapse { background: transparent; color: var(--muted); border: 1px solid var(--line); border-radius: 4px; width: 22px; height: 22px; line-height: 1; cursor: pointer; padding: 0; }
.ant-collapse:hover { color: var(--text); border-color: var(--text); }
.ant-compare-body { overflow-x: auto; }
.ant-cmp { width: 100%; border-collapse: collapse; font-size: 12px; }
.ant-cmp th, .ant-cmp td { padding: 4px 8px; text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.ant-cmp th:first-child, .ant-cmp td:first-child { text-align: left; }
.ant-cmp th { color: var(--muted); font-weight: 500; border-bottom: 1px solid var(--line); }
.ant-cmp td { color: var(--text); border-top: 1px solid rgba(62,47,87,0.3); }
.ant-cmp-name { max-width: 150px; overflow: hidden; text-overflow: ellipsis; }
.ant-cmp-n { color: var(--muted); font-size: 10px; }
.ant-cmp-delta { font-weight: 700; }
.ant-cmp-delta.pos { color: var(--accent); }
.ant-cmp-delta.neg { color: var(--err); }
.ant-cmp-foot { color: var(--muted); font-size: 10.5px; margin-top: 8px; line-height: 1.4; }

.ant-empty { color: var(--muted); font-size: 12.5px; padding: 14px 4px; text-align: center; }

/* Overview mode (canvas dots, no DOM markers): a click zooms in to the nearest
   node, so signal that with the cursor. Toggled by syncVisibleMarkers(). */
body.markers-overview #map.leaflet-container { cursor: zoom-in; }
