/* Root variables & base */
:root {
  --ink: #1c2128;
  --muted: #6b7280;
  --border: rgba(15, 23, 42, 0.08);
  --card-bg: rgba(255, 255, 255, 0.86);
}
html, body { margin: 0; padding: 0; height: 100%; }
/* position: fixed (not absolute) is what makes the map a true backdrop: its
   containing block becomes the viewport itself rather than the document, so
   it can never move even if the page scrolls (see #hud below — the one
   element still allowed to make the document taller than the viewport). */
#map { position: fixed; inset: 0; background: #eef1f4; }

/* HUD panel with its own internal scrolling. Fixed position so the scrollbar
   scrolls within the panel bounds, not the viewport. */
#hud {
  /* top starts below #account-bar (fixed at top:14px, ~40px tall) plus a
     gap, so the two floating panels never overlap. */
  position: fixed; top: 64px; right: 14px; bottom: 14px; z-index: 1000;
  width: 216px; overflow-y: auto;
  background: var(--card-bg);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: 0 12px 28px rgba(15, 23, 42, 0.16), 0 2px 6px rgba(15, 23, 42, 0.08);
  padding: 14px 16px;
  font: 13px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  color: var(--ink);
}

#hud .count-row { display: flex; align-items: baseline; gap: 7px; }
#hud .live-dot {
  width: 8px; height: 8px; border-radius: 50%; background: #22c55e;
  box-shadow: 0 0 0 rgba(34, 197, 94, 0.5);
  animation: pulse 2s infinite; flex: none; margin-bottom: 2px;
}
#hud .count { font-size: 26px; font-weight: 700; letter-spacing: -0.02em; }
#hud .label { font-size: 11px; color: var(--muted); margin: 1px 0 12px; }

/* #search-block is a .filter-block, whose divider/spacing rule only adds
   room above itself (border-top/padding-top/margin-top) — nothing pushes
   .sources away from it below, so the search input reads as glued to the
   first source row without this. */
#hud #search-block { margin-bottom: 12px; }
#hud .sources { display: flex; flex-direction: column; gap: 8px; }
#hud .source-row { display: flex; align-items: center; gap: 8px; position: relative; }
#hud .swatch {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.6) inset;
}
#hud .source-name { font-weight: 500; }
#hud .source-count {
  padding: 1px 7px; border-radius: 999px;
  background: rgba(15, 23, 42, 0.06); color: var(--muted);
  font-size: 11px; font-variant-numeric: tabular-nums;
}
/* A disabled source renders an empty count — collapse the pill entirely
   rather than leave a bare rounded blob next to the toggle. */
#hud .source-count:empty { display: none; }
/* Between enabling a source and its first numbers arriving, the slot holds a
   spinner instead of the pill: an enabled source with no pill at all reads
   as doing nothing for the second or two its first fetch takes. */
#hud .source-count.loading { background: none; padding: 0; }
#hud .count-spinner {
  display: block; width: 10px; height: 10px; border-radius: 50%;
  border: 1.5px solid rgba(15, 23, 42, 0.14); border-top-color: var(--muted);
  animation: gallery-spin .7s linear infinite;
}
#hud .count-spinner[hidden] { display: none; }
#hud .source-help {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border: none; background: none; line-height: 0;
  color: #aab0b9; cursor: pointer;
}
#hud .source-help:hover { color: var(--muted); }
#hud .source-help-popover {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 1100;
  width: 200px; padding: 8px 10px;
  background: var(--card-bg); backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 8px 20px rgba(15, 23, 42, 0.18);
  font-size: 11px; line-height: 1.35; color: var(--ink);
}
#hud .source-help-popover[hidden] { display: none; }
/* Denser content than the other help popovers (several labeled sections,
   not one short quota sentence) — the shared 200px width read as an
   overly narrow, heavily-wrapped column for this one specifically. */
#dev-mode-help-popover { width: 260px; }
#hud .source-row.locked .source-name,
#hud .source-row.locked .source-count { opacity: 0.55; }
#hud .source-row.locked .switch { opacity: 0.5; }
#hud .switch input:disabled { cursor: not-allowed; }
#hud .switch:has(input:disabled) { opacity: 0.5; }

#hud .switch { position: relative; width: 30px; height: 17px; flex: none; margin-left: auto; cursor: pointer; display: inline-block; }
#hud .switch:focus-visible { outline: 2px solid var(--row-color, #1a73e8); outline-offset: 2px; }
#hud .switch input {
  position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; z-index: 1;
}
#hud .switch-track {
  position: absolute; inset: 0; background: #d7dbe1; border-radius: 999px;
  transition: background .15s ease;
}
#hud .switch-track::before {
  content: ''; position: absolute; top: 2px; left: 2px; width: 13px; height: 13px;
  background: #fff; border-radius: 50%; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  transition: transform .15s ease;
}
#hud .switch input:checked + .switch-track { background: var(--row-color, #1a73e8); }
#hud .switch input:checked + .switch-track::before { transform: translateX(13px); }

#hud .filter-block { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
#hud .filter-title { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--muted); margin-bottom: 6px; }
#hud .filter-block .source-row { margin-bottom: 4px; }
#hud .filter-block .source-row:last-child { margin-bottom: 0; }

/* Airports per-size checklist — deliberately plain native checkboxes, not
   the .switch pill every source/layer toggle above uses: these are
   sub-filters of one already-on layer, not independent data sources, so a
   lighter, more compact affordance keeps the list from reading as six more
   toggles of equal weight to "Show airports" itself. Revealed only while
   that toggle is checked (state-filters.js sets/clears [hidden]), and
   indented slightly to read as nested under it. */
#hud .airport-type-list {
  display: flex; flex-direction: column; gap: 4px;
  margin-top: 8px;
}
#hud .airport-type-list[hidden] { display: none; }
#hud .airport-type-list .source-row { margin-bottom: 0; }
#hud .segmented {
  display: flex; background: #eef0f3; border-radius: 9px; padding: 2px; gap: 2px;
}
#hud .seg-btn {
  flex: 1; border: none; background: transparent; color: var(--muted);
  font: inherit; font-size: 11px; font-weight: 500; padding: 5px 4px;
  border-radius: 7px; cursor: pointer; transition: background .15s ease, color .15s ease;
}
#hud .seg-btn.active { background: #fff; color: var(--ink); box-shadow: 0 1px 3px rgba(15, 23, 42, 0.15); }
#hud .seg-btn:hover:not(.active) { color: var(--ink); }
#hud .seg-btn:disabled { opacity: 0.5; cursor: not-allowed; }

#hud .dropdown { position: relative; }
#hud .dropdown-trigger {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  font: inherit; font-size: 12px; color: var(--ink);
  background: #eef0f3; border: 1px solid var(--border); border-radius: 7px;
  padding: 6px 9px; cursor: pointer; transition: background .15s ease;
}
#hud .dropdown-trigger:hover { background: #e5e8ec; }
#hud .dropdown-trigger:disabled { opacity: 0.5; cursor: not-allowed; }
#hud .dropdown-value-wrap { display: flex; align-items: center; gap: 8px; min-width: 0; }
#hud .dropdown-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#hud .dropdown-caret { color: var(--muted); flex: none; margin-left: 8px; transition: transform .15s ease; }
#hud .dropdown.open .dropdown-caret { transform: rotate(180deg); }
#hud .dropdown-menu {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 10;
  background: #fff; border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 14px 32px rgba(15, 23, 42, 0.2);
  padding: 4px; max-height: 216px; overflow-y: auto;
  display: none;
}
#hud .dropdown.open .dropdown-menu { display: block; }
#hud .dropdown-option {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 9px; font-size: 12px; border-radius: 6px; cursor: pointer;
  color: var(--ink); line-height: 1.4;
}
#hud .dropdown-option:hover { background: #f2f4f6; }
#hud .dropdown-option.active { background: rgba(26, 115, 232, 0.1); color: #1a73e8; font-weight: 600; }
#hud .dropdown-icon { flex: none; color: var(--muted); }
#hud .dropdown-option.active .dropdown-icon { color: #1a73e8; }

/* Zone-search box (#zone-search-block): a text <input>, not a click-only
   .dropdown-trigger button like the basemap/category pickers — styled to
   match their look (same background/border/radius/padding) but without
   `display:flex`, which behaves inconsistently on a replaced <input>
   element. Reuses .dropdown/.dropdown-menu/.dropdown-option as-is for the
   results list/open-state toggling. */
#hud .zone-search-input {
  width: 100%; box-sizing: border-box; font: inherit; font-size: 12px; color: var(--ink);
  background: #eef0f3; border: 1px solid var(--border); border-radius: 7px;
  padding: 6px 9px;
}
#hud .zone-search-input:focus { outline: none; border-color: #1a73e8; }
#hud .zone-search-option { flex-direction: column; align-items: flex-start; gap: 1px; }
#hud .zone-search-result-name { font-weight: 600; }
#hud .zone-search-result-meta { font-size: 11px; color: var(--muted); }
#hud .zone-search-status { font-size: 11px; color: var(--muted); margin-top: 6px; }
#hud .zone-search-status.error { color: #dc2626; }

#hud .hud-footer { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
#hud .status { font-size: 11px; color: var(--muted); }
#hud .status + .status { margin-top: 2px; }
/* The track status carries its own "(?)", so it's a row rather than a bare
   line — and the popover opens upward, since it sits at the HUD's bottom. */
#hud .status-row { display: flex; align-items: center; gap: 5px; position: relative; }
#hud .status-row .emergency { color: #dc2626; font-weight: 600; }
#hud .source-help-popover.up { top: auto; bottom: calc(100% + 6px); }

@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.55); }
  70% { box-shadow: 0 0 0 6px rgba(34, 197, 94, 0); }
  100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); }
}

/* Marker icons (map + dropdown) */
.plane-icon-rotate { transition: transform .4s linear; }
.plane-icon:not(.surface-obstacle-icon):not(.airport-icon) {
  filter: drop-shadow(1px 3px 0 rgba(15, 23, 42, 0.25));
}
.plane-icon svg path,
.plane-icon svg ellipse,
.plane-icon svg circle {
  stroke: #fff;
  stroke-width: 0.5px;
  vector-effect: non-scaling-stroke;
}

.dropdown-icon-container {
  display: inline-block;
  width: 16px;
  height: 16px;
}

.dropdown-icon-container svg {
  width: 100%;
  height: 100%;
}

/* Sidebar panel. position: fixed (not absolute) so it stays anchored to the
   viewport rather than the document — same reasoning as #map above, needed
   now that the page can genuinely scroll to reveal #hud's own overflow. */
#sidebar {
  position: fixed; top: 14px; left: 14px; bottom: 14px; z-index: 1150;
  width: 300px; max-width: 82vw;
  background: var(--card-bg);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: 0 12px 28px rgba(15, 23, 42, 0.16), 0 2px 6px rgba(15, 23, 42, 0.08);
  transform: translateX(calc(-100% - 14px));
  transition: transform .25s ease;
  overflow-y: auto;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
}
#sidebar.open { transform: translateX(0); }
/* While the dev-mode aircraft table is showing, the sidebar docks to its
   right instead of on top of it (368px = the table's own 14px inset +
   340px width + 14px gap) — both stay visible/usable at once rather than
   the sidebar (higher z-index) simply covering the table when an aircraft
   is selected. */
#sidebar.dev-shifted { left: 368px; }
/* The closed-state translateX distance (calc(-100% - 14px) normally) is
   calibrated to fully clear the sidebar's own `left` offset — with
   dev-shifted's larger left:368px, that same distance would leave it
   partially on-screen while "closed", so it needs its own larger offset. */
#sidebar.dev-shifted:not(.open) { transform: translateX(calc(-100% - 368px)); }
#sidebar-close, #sidebar-center-map {
  position: absolute; top: 10px; z-index: 2;
  width: 28px; height: 28px; border-radius: 50%; border: none;
  background: rgba(255, 255, 255, 0.92); color: var(--ink);
  font-size: 18px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 6px rgba(15, 23, 42, 0.18);
}
#sidebar-close { right: 10px; }
/* Left, mirroring #sidebar-close on the right — a small crosshair icon
   rather than text, since "center on this aircraft" doesn't have an
   obvious single-character glyph the way "close" has "×". */
#sidebar-center-map { left: 10px; }
#sidebar-center-map:hover { background: rgba(255, 255, 255, 1); }

/* Header masthead: identity essentials (registration/ICAO, callsign,
   aircraft type) at a glance, above the gallery. #sidebar-close/
   #sidebar-center-map (28px circles, top:10px) float over the top of this
   block — padding-top is large enough (40px) that the title text starts
   entirely *below* both buttons rather than sharing their vertical band
   (the two used to nearly touch). Empty (no margin/padding) when nothing
   is selected yet, so it doesn't leave a visible gap. */
#sidebar-header {
  padding: 40px 18px 10px 18px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
}
#sidebar-header:empty { padding: 0; }
.sidebar-header-title { font-size: 18px; font-weight: 700; color: var(--ink); line-height: 1.25; }
.sidebar-header-subtitle { font-size: 12px; color: var(--muted); margin-top: 2px; }
.sidebar-header-sep { opacity: .6; padding: 0 1px; }

#sidebar-details {
  padding: 16px 18px 20px;
  font: 13px/1.7 -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  color: var(--ink);
}
#sidebar-details b { display: inline-block; min-width: 96px; color: var(--muted); font-weight: 600; }
/* The four identity rows carrying a "(?)" icon (Operator/Operator Country/
   Registered Owner/Registration Country) read as too heavy at the default
   label weight once stacked with their own icons — a lighter weight and
   slightly smaller size keeps them from visually dominating the group.
   min-width:0/display:inline overrides the general #sidebar-details b rule
   above so the icon sits flush against the label text itself, not pushed
   past an invisible min-width box. The column-alignment min-width instead
   moves to .identity-label-wrap (label + icon together, see below), so
   values still line up the same way every other detailRow's value does. */
#sidebar-details b.identity-label { font-weight: 500; font-size: 12px; min-width: 0; display: inline; }
#sidebar-details .identity-label-wrap { display: inline-block; min-width: 96px; }
#sidebar-details .emergency { color: #dc2626; font-weight: 700; }
#sidebar-details .detail-group { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
#sidebar-details .detail-group:first-child { margin-top: 0; padding-top: 0; border-top: none; }
#sidebar-details .detail-group-title {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); margin-bottom: 5px;
  display: flex; align-items: center; gap: 5px;
}
/* One icon per detail *group* (Material Design Icons, see GROUP_ICONS in
   render-details.js) — not per field, which would be too busy at this
   density. Inherits the title's own muted color via currentColor. */
.detail-group-icon { display: inline-flex; flex: none; opacity: .75; }

/* Route card — its own visual block between the gallery and the rest of
   the details (#sidebar-route, a sibling of #sidebar-details, not a
   .detail-group inside it), so a resolved route reads as a distinct,
   glanceable unit rather than one more text row in a field list: big
   airport codes, small city names, a direction arrow between them. */
#sidebar-route:empty { display: none; }
.route-card {
  margin: 0 18px 14px; padding: 12px 14px;
  background: rgba(15, 23, 42, 0.035);
  border: 1px solid var(--border); border-radius: 12px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
}
.route-card-title {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); margin-bottom: 8px;
  display: flex; align-items: center; gap: 6px;
}
.route-card-tag { font-size: 10px; font-weight: 700; text-transform: none; letter-spacing: 0; color: #b45309; }
.route-card-unconfirmed .route-card-tag, .route-card-unconfirmed .route-card-title { color: var(--muted); }
/* Dev-mode-only tag on an Identity row (Operator/Operator Country) whose
   value is a callsign-decoded match that conflicts with the aircraft's own
   ICAO24 hex-address country — same visual language as .route-card-tag's
   "⚠ Unverified", just for a plain field row rather than the Route card. */
.field-unconfirmed-tag { font-size: 10px; font-weight: 700; color: #b45309; }
.route-card-endpoints { display: flex; align-items: flex-start; }
.route-card-endpoint { flex: 1 1 0; min-width: 0; }
.route-card-endpoint:last-child { text-align: right; }
.route-card-code {
  font-size: 22px; font-weight: 700; letter-spacing: .01em; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
/* Wraps rather than truncating — an airport name cut off mid-word ("Skiathos
   Island ...") reads as broken/unfinished, and there's no real space
   pressure that truncation was solving here (the card is free to grow
   taller). */
.route-card-city { font-size: 11px; color: var(--muted); margin-top: 2px; }
.route-card-arrow {
  flex: 0 0 auto; color: var(--muted); padding: 0 10px;
  display: flex; align-items: center; justify-content: center;
}
.route-card-empty-text { font-size: 13px; color: var(--muted); }
.route-card-footer:not(:empty) { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--border); }
.route-confidence { font-size: 11px; color: var(--muted); }
.route-confidence-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  margin-right: 5px; vertical-align: middle;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.6) inset;
}

/* Country flag (flag-icons SVG library, see static/flag-icons/) inline
   before the country name in the sidebar's Identity group. flag-icons
   sizes .fi via line-height + a ":before{content:' '}" trick, which Safari
   and Chromium compute differently against this sidebar's own line-height
   (1.7) — Safari lands the flag a few px below the text (confirmed via
   WebKit's own engine, not just Chromium, which looked fine on its own).
   Pinning an explicit height and switching to vertical-align: middle
   (relative to the whole line box, not a hand-tuned baseline offset) fixes
   it consistently across engines instead of chasing another magic number. */
#sidebar-details .fi {
  width: 18px; height: 13px;
  vertical-align: middle;
  border-radius: 2px;
}

/* Airline logo (see airlineLogoHtml(), static/airline-logos/) leading the
   Operator row — sized/aligned consistently with the .fi flag above it so
   both read as one visual family of "small icon before a text value" rows. */
#sidebar-details .airline-logo {
  width: 18px; height: 18px;
  vertical-align: middle;
  margin-right: 2px;
  object-fit: contain;
}

/* Dev mode: per-field source provenance dots + their click-to-toggle
   tooltip. Scoped to all three sidebar containers now, not just
   #sidebar-details — ICAO/Callsign/Registration/Aircraft type moved to
   #sidebar-header and Route to #sidebar-route, and their badges rendered
   invisibly (no size/shape at all) until this rule covered those
   containers too. */
#sidebar-details .source-badge, #sidebar-header .source-badge, #sidebar-route .source-badge {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  margin-left: 4px; cursor: pointer; vertical-align: middle;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.6) inset;
}
#source-tooltip {
  position: absolute; z-index: 1200; padding: 6px 10px;
  max-width: 260px; white-space: normal;
  background: var(--card-bg); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 20px rgba(15, 23, 42, 0.18);
  font: 11px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  color: var(--ink); pointer-events: none;
}
#source-tooltip[hidden] { display: none; }
/* Shared click-to-toggle tooltip trigger for inline informational content
   (category explanations, route confidence detail) — the same
   #source-tooltip popover every other tooltip in this app uses, so there's
   one consistent tooltip pattern rather than several. A dotted underline
   signals "there's more info here" the way source-badge dots signal
   provenance. */
#sidebar-details .info-tip, #sidebar-route .info-tip, #sidebar-header .info-tip {
  cursor: pointer; border-bottom: 1px dotted var(--muted);
}
/* Icon-based triggers (the identity rows' circled-"?", HELP_ICON_SVG in
   render-details.js) already read as clickable on their own — a dotted
   underline under a circle icon looks like a rendering glitch, not an
   affordance, so it's dropped specifically for those. */
#sidebar-details .info-tip:has(svg) {
  border-bottom: none; display: inline-flex; align-items: center;
  vertical-align: -2px; color: #aab0b9; margin-left: 4px;
}
#sidebar-details .info-tip:has(svg):hover { color: var(--muted); }

/* Dev-mode "all aircraft" table — a debugging aid. A tall, narrow vertical
   column (mirrors #sidebar's own footprint on the same left edge — #sidebar
   sits above it, z-index-wise, and simply covers it while a specific
   aircraft is open) rather than a short wide bar, so many single-line rows
   are visible at once instead of just a handful. Columns are kept narrow
   with ellipsis truncation rather than dropped, so the full Identity-field
   breadth (including Route) still fits without wrapping or horizontal
   scroll. */
/* position: fixed for the same reason as #sidebar above — stays put
   regardless of any document scroll #hud's own overflow triggers. */
#dev-aircraft-panel {
  position: fixed; left: 14px; top: 14px; bottom: 14px; z-index: 1100;
  width: 340px; max-width: 82vw;
  background: var(--card-bg);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: 0 12px 28px rgba(15, 23, 42, 0.16), 0 2px 6px rgba(15, 23, 42, 0.08);
  font: 11px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  color: var(--ink);
  display: flex; flex-direction: column;
  overflow: hidden;
}
#dev-aircraft-panel-header {
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 10px 14px; font-weight: 600; flex: none;
  border-bottom: 1px solid var(--border);
}
#dev-aircraft-count { color: var(--muted); font-weight: 500; }
#dev-identity-stats {
  padding: 6px 14px; font-weight: 400; color: var(--muted); flex: none;
  border-bottom: 1px solid var(--border);
}
#dev-aircraft-panel-scroll { overflow: auto; }
#dev-aircraft-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
#dev-aircraft-table th, #dev-aircraft-table td {
  text-align: left; padding: 3px 6px; border-bottom: 1px solid var(--border);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#dev-aircraft-table thead th {
  position: sticky; top: 0; background: var(--card-bg);
  color: var(--muted); font-weight: 600;
}
/* Fixed narrow widths for the short fields, leaving Route (the most
   variable-length one) to take whatever's left. */
#dev-aircraft-table col.icao { width: 15%; }
#dev-aircraft-table col.callsign { width: 15%; }
#dev-aircraft-table col.reg { width: 15%; }
#dev-aircraft-table col.type { width: 15%; }
#dev-aircraft-table col.route { width: 40%; }
#dev-aircraft-table tbody tr { cursor: pointer; }
#dev-aircraft-table tbody tr:hover { background: rgba(15, 23, 42, 0.05); }
#dev-aircraft-table tbody td[title] { cursor: help; }

/* Photo gallery slider container — fixed 16:9 aspect ratio, set explicitly
   so the sidebar doesn't reflow/jump as photos of different aspect ratios
   load in. Holds all slides with GPU-accelerated transform animations for
   smooth swipe/drag; .gallery-credit is positioned relative to this fixed
   box, not to any individual photo, so it always sits at the same corner
   regardless of what's loaded. */
.gallery-slider-container {
  position: relative; overflow: hidden;
  width: 100%; aspect-ratio: 16 / 9;
}
.gallery-slider-track {
  display: flex; width: 100%; height: 100%;
  will-change: transform;
}
.gallery-slide {
  flex: 0 0 100%; width: 100%; height: 100%;
  overflow: hidden;
}
/* The <a> wrapping each <img> must be an explicit width:100%/height:100%
   block — an anchor is inline by default, and even as a flex item it
   shrinks to its content's intrinsic size unless stretched, leaving the
   img's own width:100%/height:100% with no determinate box to resolve
   percentages against (a real bug hit here: object-fit: cover silently
   failed to actually cover, since the img's "100%" height had nothing
   concrete to be 100% of). */
.gallery-slide a { display: block; width: 100%; height: 100%; }
.gallery-slide img { display: block; }
/* object-fit: cover fills the fixed 3:2 box completely — crops a small
   amount off the top/bottom of photos with a taller aspect ratio, which is
   usually the least informative part of an aircraft photo (sky/tarmac),
   in exchange for a stable, non-jumping layout and no letterbox gaps. */
.gallery-slide.stretch img { width: 100%; height: 100%; object-fit: cover; }
/* Exception: adsbdb's thumbnail-only last-resort photo (see "adsbdb.com"
   below) is a genuinely tiny ~2-4KB image — covering it into a 3:2 box
   would visibly blur it, so it stays at native size/contain instead,
   centered rather than filling the box. */
.gallery-slide.native {
  display: flex; align-items: center; justify-content: center; padding: 10px 0;
}
.gallery-slide.native a { width: auto; height: auto; }
.gallery-slide.native img { max-width: 100%; max-height: 220px; width: auto; height: auto; object-fit: contain; }

/* Legacy .gallery-image-wrap (kept for compat if used elsewhere) */
.gallery-image-wrap {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.gallery-image-wrap img { display: block; }
.gallery-image-wrap.stretch img { width: 100%; height: auto; max-height: 280px; object-fit: contain; }
.gallery-image-wrap.native { padding: 10px 0; }
.gallery-image-wrap.native img { max-width: 100%; max-height: 220px; }
/* Same fixed 16:9 box as .gallery-slider-container (not a flat 180px height)
   — the loading spinner and "no photos found" text are both shown in this
   box before any real photo has loaded, and matching its aspect ratio to
   the eventual photo box is what keeps the sidebar from jumping in height
   the moment the real gallery (or its dots row, see .gallery-dots above)
   replaces it. */
.gallery-placeholder {
  display: flex; align-items: center; justify-content: center;
  width: 100%; aspect-ratio: 16 / 9;
  background: #f2f4f6; color: var(--muted); font-size: 12px;
}
.gallery-spinner {
  width: 24px; height: 24px; border-radius: 50%;
  border: 3px solid #e3e6ea; border-top-color: #1a73e8;
  animation: gallery-spin .8s linear infinite;
}
.gallery-placeholder.loading { background: none; }
@keyframes gallery-spin { to { transform: rotate(360deg); } }

/* Route card arrow animation: gentle oscillation when climbing/descending */
@keyframes route-arrow-climb { 0%, 100% { transform: rotate(80deg); } 50% { transform: rotate(70deg); } }
@keyframes route-arrow-descend { 0%, 100% { transform: rotate(100deg); } 50% { transform: rotate(110deg); } }
.route-card-arrow .route-arrow-climbing { animation: route-arrow-climb 1.8s ease-in-out infinite; }
.route-card-arrow .route-arrow-descending { animation: route-arrow-descend 1.8s ease-in-out infinite; }

/* Map loader overlay */
/* Initial-load overlay: shown until the first poll resolves, then fades out.
   position: fixed for the same reason as #sidebar/#dev-aircraft-panel above. */
#map-loader {
  position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
  z-index: 900; display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 20px 26px;
  background: var(--card-bg);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--border); border-radius: 16px;
  box-shadow: 0 12px 28px rgba(15, 23, 42, 0.16), 0 2px 6px rgba(15, 23, 42, 0.08);
  font: 13px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  color: var(--muted); transition: opacity .4s ease;
}
#map-loader.hidden { opacity: 0; pointer-events: none; }
#map-loader .loader-spinner {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid #e3e6ea; border-top-color: #1a73e8;
  animation: gallery-spin .8s linear infinite;
}
/* Overlaid on the image, vertically centered — but with no background
   shape behind them at all, just the bare chevron glyph plus a subtle
   drop-shadow for legibility over any photo. Two earlier attempts (a
   filled dark circle, then a light glass pill; also tried a separate row
   below the image) all either sat visibly "on top of" the photo or looked
   out of place in this layout — a shape-less glyph avoids that while
   staying in the expected carousel position. */
.gallery-nav {
  position: absolute; top: 0; bottom: 0;
  width: 56px; border: none; background: none;
  color: #fff; font-size: 22px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: flex-start;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.7));
  padding: 0 6px; z-index: 10;
}
.gallery-prev { left: 0; justify-content: flex-start; }
.gallery-next { right: 0; justify-content: flex-end; }
/* min-height matches .gallery-dot's own 6px so this row holds its height
   even when empty (a single-photo gallery renders this row with zero .gallery-dot
   children — see renderGallery(), static/js/sidebar-track.js) — otherwise the
   sidebar visibly grew/shifted the moment a 1-photo gallery topped up to 2+
   and this row's dots suddenly appeared. */
.gallery-dots { display: flex; align-items: center; justify-content: center; gap: 6px; padding: 8px 0; min-height: 6px; }
.gallery-dot {
  width: 6px; height: 6px; border-radius: 50%; background: #d7dbe1; cursor: pointer;
}
.gallery-dot.active { background: #1a73e8; }
/* Overlaid on .gallery-slider-container's top-right corner (bottom-right
   was tried first, but that's where a long attribution/copyright panel
   sometimes sits below the gallery, and top-left is already occupied
   elsewhere in the sidebar) — same treatment as .gallery-nav (translucent
   white text + drop-shadow, no background shape) since it sits on top of
   arbitrary photo content rather than the card background. Anchored to
   the fixed-aspect-ratio *container*, not any individual photo, so it
   always lands on real photo pixels (see "Gallery tuning" below — the
   container's own object-fit: cover always fills the box completely, no
   letterbox gap for it to collide with). */
.gallery-credit {
  position: absolute; right: 8px; top: 6px;
  font-size: 11px; color: rgba(255, 255, 255, 0.65);
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.5));
}
.gallery-credit a { color: inherit; text-decoration: none; }
#sidebar-icon-credit { font-size: 11px; color: var(--muted); padding: 10px 18px 14px; border-top: 1px solid var(--border); text-align: left; }
#sidebar-icon-credit a { color: var(--muted); text-decoration: underline; }
#sidebar-icon-credit a:hover { color: var(--ink); }

/* Scan-radius range ring labels (map-init.js's L.divIcon markers) —
   pointer-events:none so they never intercept map clicks/drags. */
.radius-ring-label {
  font-size: 10px; color: #64748b; background: rgba(255, 255, 255, 0.85);
  padding: 1px 5px; border-radius: 3px; white-space: nowrap;
  pointer-events: none; transform: translate(-50%, -50%);
}
.radius-ring-label-edge { color: #2563eb; font-weight: 600; }

/* Account widget — pinned to the extreme top-right corner (the common
   "avatar in the corner" placement: Gmail/Drive/YouTube) rather than living
   inside #hud's own filter-block flow. Sits above #hud in the stacking
   order (#hud is 1000) since it's visually a separate, more prominent
   piece of chrome, not one more filter row. #hud's own top offset (below)
   is pushed down to leave room under it. */
#account-bar {
  position: fixed; top: 14px; right: 14px; z-index: 1400;
  display: flex; justify-content: flex-end;
  font: 13px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
}

/* "Sign in with Google" button, styled per Google's own sign-in button
   guidelines (white pill, #dadce0 border, the real four-color "G" mark) —
   the shape users already recognize, rather than a custom-branded button. */
.google-signin-btn {
  display: flex; align-items: center; gap: 10px;
  background: #fff; color: #3c4043;
  border: 1px solid #dadce0; border-radius: 20px;
  padding: 9px 16px 9px 12px;
  font: 600 13px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(15, 23, 42, 0.14);
  transition: box-shadow .15s ease, background .15s ease;
}
.google-signin-btn:hover { background: #f8f9fa; box-shadow: 0 4px 12px rgba(15, 23, 42, 0.2); }
.google-signin-btn:active { background: #f1f3f4; }

/* Signed-in state: an avatar pill (photo) that opens a dropdown menu —
   replaces the old plain "Hi, Name · Logout" text link. */
#user-menu { position: relative; }
.user-menu-trigger {
  display: flex; align-items: center; gap: 8px;
  background: var(--card-bg); border: 1px solid var(--border); border-radius: 20px;
  padding: 5px 12px 5px 5px;
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  cursor: pointer; color: var(--ink);
  font: 600 13px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  box-shadow: 0 2px 6px rgba(15, 23, 42, 0.14);
  transition: box-shadow .15s ease;
}
.user-menu-trigger:hover { box-shadow: 0 4px 12px rgba(15, 23, 42, 0.2); }
.user-avatar {
  width: 28px; height: 28px; border-radius: 50%; object-fit: cover;
  display: block; background: #cbd5e1; flex: none;
}
.user-menu-name { max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-menu-caret { color: var(--muted); flex: none; transition: transform .15s ease; }
#user-menu.open .user-menu-caret { transform: rotate(180deg); }

/* Dropdown: kept in the DOM at all times (not hidden/unhidden) so opening
   it can transition — opacity/transform + pointer-events, same idea as
   .source-help-popover but animated, toggled purely via #user-menu.open. */
.user-menu-dropdown {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 1;
  min-width: 220px;
  background: var(--card-bg); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18), 0 2px 6px rgba(15, 23, 42, 0.08);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  padding: 8px;
  opacity: 0; transform: translateY(-6px) scale(.97); pointer-events: none;
  transition: opacity .14s ease, transform .14s ease;
}
#user-menu.open .user-menu-dropdown { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
.user-menu-email {
  padding: 6px 10px 8px; font-size: 12px; color: var(--muted);
  border-bottom: 1px solid var(--border); margin-bottom: 6px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.user-menu-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  font: 500 13px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  color: var(--ink); background: none; border: none; border-radius: 8px;
  padding: 9px 10px; cursor: pointer; text-align: left;
}
.user-menu-item:hover { background: rgba(15, 23, 42, 0.06); }
.user-menu-item svg { flex: none; color: var(--muted); }
.user-menu-item-danger:hover { background: rgba(220, 38, 38, 0.08); color: #dc2626; }
.user-menu-item-danger:hover svg { color: #dc2626; }

#google-signin-btn[hidden], #user-menu[hidden] { display: none; }

/* Sidebar's third circular button, alongside #sidebar-close/
   #sidebar-center-map — same 28px-circle rule shape, positioned to the
   left of #sidebar-center-map so all three sit in one row without
   overlapping. */
#sidebar-save-collection {
  position: absolute; top: 10px; left: 46px; z-index: 2;
  width: 28px; height: 28px; border-radius: 50%; border: none;
  background: rgba(255, 255, 255, 0.92); color: var(--ink);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 6px rgba(15, 23, 42, 0.18);
}
#sidebar-save-collection:hover { background: rgba(255, 255, 255, 1); }
/* Filled vs outline is the save/unsave state — the SVG path already ships
   fill="none" (an outline), and this rule overrides that presentation
   attribute (any CSS rule targeting the element always wins over a
   presentation attribute, so no duplicate markup is needed for the two
   states). */
#sidebar-save-collection.saved { color: #1a73e8; }
#sidebar-save-collection.saved svg path { fill: currentColor; }
/* Not-collectible aircraft (a "C0" ADS-B code, or a ground vehicle/tower
   flagged by looksLikeGroundVehicle()) get no save button at all, rather
   than a disabled one — an ID selector's specificity beats the [hidden]
   attribute selector's, so the base rule's display:flex above would
   otherwise silently win and keep the button visible. */
#sidebar-save-collection[hidden] { display: none; }

/* Collection overlay — a true fullscreen takeover (not a centered modal):
   the owner asked for it to fully replace the map view while open, so
   there's no backdrop to dim either. Still uses the same glass-card
   language (blur, border, shadow removed since there's no edge to cast one
   against) as #hud/#sidebar for visual consistency. */
#collection-panel {
  /* Above #account-bar (1400) too — a true fullscreen takeover must cover
     every other piece of floating chrome, including the corner widget that
     opened it, not just #hud/#sidebar. */
  position: fixed; inset: 0; z-index: 1500;
  background: var(--card-bg);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  color: var(--ink);
  display: flex; flex-direction: column;
}
#collection-panel[hidden] { display: none; }
/* A subtle fade + rise-in on open — an @keyframes animation rather than a
   transition, since the element flips straight from display:none (the
   [hidden] rule above) to displayed, and CSS transitions can't animate out
   of display:none; an animation on the visible state runs the moment it's
   rendered, no JS timing needed. */
#collection-panel:not([hidden]) { animation: collection-panel-in .22s ease; }
@keyframes collection-panel-in {
  from { opacity: 0; transform: scale(.98) translateY(6px); }
  to { opacity: 1; transform: scale(1) translateY(0); }
}
#collection-panel-header {
  display: flex; justify-content: space-between; align-items: center;
  padding: 18px 24px; font-weight: 700; font-size: 16px; flex: none;
  border-bottom: 1px solid var(--border);
}
#collection-panel-header-actions { display: flex; align-items: center; gap: 14px; }
#collection-panel-close {
  width: 32px; height: 32px; border-radius: 50%; border: none;
  background: rgba(15, 23, 42, 0.06); color: var(--ink);
  font-size: 18px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
#collection-panel-close:hover { background: rgba(15, 23, 42, 0.12); }
/* Cards / Compact display switcher — a segmented control, same visual
   family as #motion-filter's three-button group. */
#collection-view-toggle {
  display: flex; background: rgba(15, 23, 42, 0.06); border-radius: 999px; padding: 3px;
}
.collection-view-btn {
  font: inherit; font-size: 12px; font-weight: 600; color: var(--muted);
  background: none; border: none; border-radius: 999px; cursor: pointer;
  padding: 5px 12px;
}
.collection-view-btn.active { background: var(--card-bg); color: var(--ink); box-shadow: 0 1px 3px rgba(15, 23, 42, 0.18); }
#collection-panel-grid {
  overflow: auto; padding: 20px 24px 32px; flex: 1;
  max-width: 1100px; width: 100%; margin: 0 auto;
}
.collection-group-header {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); margin: 22px 0 10px;
}
.collection-group-header:first-child { margin-top: 0; }
.collection-group-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 20px;
}
#collection-panel-empty {
  height: 100%; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center;
  color: var(--muted); padding: 30px;
}
.collection-empty-icon { width: 96px; height: 96px; color: rgba(15, 23, 42, 0.16); }
.collection-empty-title { font-size: 15px; font-weight: 700; color: var(--ink); margin-top: 14px; }
.collection-empty-hint { font-size: 13px; color: var(--muted); margin-top: 6px; max-width: 320px; }

.collection-card {
  border: 1px solid var(--border); border-radius: 14px; overflow: hidden;
  background: rgba(15, 23, 42, 0.02); position: relative;
}
.collection-card-photo-wrap {
  width: 100%; aspect-ratio: 16 / 9; background: rgba(15, 23, 42, 0.05);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.collection-card-photo-wrap img { width: 100%; height: 100%; object-fit: cover; }
.collection-card-photo-placeholder { color: var(--muted); font-size: 13px; }
.collection-card-body { padding: 14px 16px 16px; }
.collection-card-title { font-size: 17px; font-weight: 700; color: var(--ink); }
.collection-card-subtitle { font-size: 13px; color: var(--muted); margin-top: 3px; }

/* Category/operator/manufacturer rows added below the title+subtitle —
   same "small icon (flag/logo) + plain text" family as #sidebar-details'
   own .fi/.airline-logo rows, sized identically so a card reads as a
   miniature of the sidebar rather than a visually distinct language. */
.collection-card-row-icon { display: inline-flex; align-items: center; vertical-align: middle; margin-right: 4px; }
.collection-card-row-icon .fi { width: 18px; height: 13px; border-radius: 2px; }
.collection-card-row-icon .airline-logo { width: 18px; height: 18px; object-fit: contain; }
.collection-card-category {
  display: inline-block; margin-top: 8px; font-size: 12px; font-weight: 700;
  color: var(--ink); background: rgba(15, 23, 42, 0.06);
  padding: 2px 8px; border-radius: 999px;
}
.collection-card-meta { font-size: 13px; color: var(--ink); margin-top: 5px; }
.collection-card-footer {
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border);
}
.collection-card-footer-row { font-size: 12px; color: var(--muted); margin-top: 3px; }
.collection-card-footer-row:first-child { margin-top: 0; }
/* The panel's own save/unsave toggle — same bookmark glyph + fill rule as
   #sidebar-save-collection above, always rendered filled here (a live card
   in this list is, by definition, currently saved). */
.collection-card-icon-btn {
  position: absolute; top: 10px; right: 10px; z-index: 2;
  width: 34px; height: 34px; border-radius: 50%; border: none;
  background: rgba(255, 255, 255, 0.92); color: #1a73e8;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 6px rgba(15, 23, 42, 0.18);
}
.collection-card-icon-btn svg { width: 20px; height: 20px; }
.collection-card-icon-btn:hover { background: #fff; }
.collection-card-icon-btn.saved svg path { fill: currentColor; }

/* "Elegant" removal: the card stays in place, dimmed, with an Undo action —
   real deletion already happened server-side (see removeCardWithUndo in
   auth-collection.js), this is purely a session-scoped grace period. The
   dimming applies only to the underlying photo/body, never to the overlay
   itself — an earlier version put `opacity` on `.collection-card` as a
   whole, which also washed out the Undo button to 45% opacity, making a
   fully-clickable, non-disabled control read as greyed-out/inert. */
.collection-card.removed .collection-card-photo-wrap,
.collection-card.removed .collection-card-body { opacity: 0.45; filter: grayscale(0.6); }
.collection-card-removed-overlay {
  position: absolute; inset: 0; z-index: 2;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; background: rgba(255, 255, 255, 0.55);
}
.collection-card-removed-label {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink); background: rgba(255, 255, 255, 0.9);
  padding: 2px 8px; border-radius: 999px;
}
/* Primary-style, high-contrast — deliberately not the muted white pill this
   used to share with the "Removed" label, so Undo reads as the one clearly
   actionable control on an otherwise dimmed card. */
.collection-card-undo-btn {
  font: inherit; font-size: 13px; font-weight: 700; color: #fff;
  background: #1a73e8; border: none; border-radius: 999px;
  padding: 6px 16px; cursor: pointer; box-shadow: 0 2px 8px rgba(26, 115, 232, 0.45);
}
.collection-card-undo-btn:hover { background: #1558b0; }
.collection-card-undo-hint {
  font-size: 10px; color: var(--muted); background: rgba(255, 255, 255, 0.85);
  padding: 1px 8px; border-radius: 999px;
}

/* Compact view — same card markup as above, reflowed into a small photo +
   single-column row rather than a photo-topped grid tile. Reuses every
   .collection-card-* class as-is (no separate DOM builder) so both view
   modes stay in sync automatically as the card content evolves. */
.collection-group-grid.compact {
  display: flex; flex-direction: column; gap: 8px;
}
.collection-group-grid.compact .collection-card {
  display: flex; flex-direction: row; align-items: center; border-radius: 10px;
}
.collection-group-grid.compact .collection-card-photo-wrap {
  width: 64px; height: 64px; aspect-ratio: unset; flex: none;
}
.collection-group-grid.compact .collection-card-photo-placeholder { font-size: 10px; text-align: center; }
.collection-group-grid.compact .collection-card-body {
  padding: 6px 12px; flex: 1; min-width: 0;
}
.collection-group-grid.compact .collection-card-title { font-size: 14px; }
.collection-group-grid.compact .collection-card-subtitle {
  font-size: 12px; margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.collection-group-grid.compact .collection-card-category {
  margin-top: 4px; font-size: 11px; padding: 1px 7px;
}
/* Operator/country/manufacturer meta rows and the saved-at/location footer
   are dropped in compact mode — that's the whole point of "compact": one
   row per aircraft, not a stack of detail lines. Still one click away via
   the Cards toggle. */
.collection-group-grid.compact .collection-card-meta,
.collection-group-grid.compact .collection-card-footer { display: none; }
.collection-group-grid.compact .collection-card-icon-btn {
  position: static; margin: 0 10px; flex: none;
  width: 30px; height: 30px; box-shadow: none; background: transparent;
}
.collection-group-grid.compact .collection-card-icon-btn:hover { background: rgba(15, 23, 42, 0.08); }
.collection-group-grid.compact .collection-card-removed-overlay { flex-direction: row; gap: 10px; padding: 0 12px; }

/* Airport marker popup (airportPopupHtml() in map-init.js) — reworked from
   Leaflet's plain default popup (unstyled <b>/<br> text, the same look
   METAR/SIGMET popups still use) into the same glass-card language as
   #hud/#sidebar (var(--card-bg) + blur + var(--border) + a soft shadow),
   so it reads as part of this app rather than stock Leaflet chrome. The
   className passed to bindPopup() lands on the outer .leaflet-popup
   wrapper, which is what these selectors target. */
.leaflet-popup.airport-popup .leaflet-popup-content-wrapper {
  background: var(--card-bg); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--border); border-radius: 14px;
  box-shadow: 0 12px 28px rgba(15, 23, 42, 0.16), 0 2px 6px rgba(15, 23, 42, 0.08);
  padding: 0;
}
.leaflet-popup.airport-popup .leaflet-popup-content { margin: 0; width: auto !important; }
.leaflet-popup.airport-popup .leaflet-popup-tip { background: var(--card-bg); }
.airport-popup-card {
  padding: 12px 14px; min-width: 170px;
  font: 13px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  color: var(--ink);
}
.airport-popup-header { display: flex; align-items: center; gap: 9px; }
.airport-popup-icon { flex: none; display: flex; color: #475569; }
.airport-popup-name { font-size: 14px; font-weight: 700; line-height: 1.3; }
.airport-popup-type {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); margin-top: 1px;
}
.airport-popup-codes { display: flex; gap: 6px; margin-top: 9px; }
.airport-popup-code-chip {
  font-family: monospace; font-size: 11px; font-weight: 700; letter-spacing: .02em;
  background: rgba(15, 23, 42, 0.06); color: var(--ink);
  padding: 2px 7px; border-radius: 6px;
}
.airport-popup-row {
  display: flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--ink); margin-top: 6px;
}
.airport-popup-row.airport-popup-row-muted { color: var(--muted); }
.airport-popup-row .fi { flex: none; border-radius: 2px; }
