/* === Reset & Base === */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* Visually hidden text used for screen-reader-only labels (e.g. icon buttons). */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

:root {
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-display: 'Space Grotesk', 'Inter', sans-serif;
  /* Globe-customizable tokens — overridden at runtime by app.js applySettings() */
  --color-globe-text: #ffffff;
  --color-globe-bg: #06070d;
  --color-bg: #06070d;
  --color-surface: #0d0f18;
  --color-text: #e0e0e6;
  --color-text-muted: #6b6d7b;
  --color-text-faint: #3d3f4d;
  --color-border: rgba(255, 255, 255, 0.08);
  --color-accent: #4f98a3;

  /* Shared "dark dropdown" surfaces — Region select is the reference */
  --select-bg: rgba(10, 14, 24, 0.85);
  --select-bg-hover: rgba(16, 22, 36, 0.95);
  --select-border: rgba(255, 255, 255, 0.12);
  --select-border-hover: rgba(103, 232, 249, 0.32);
  --select-text: #ffffff;
  --select-focus: rgba(103, 232, 249, 0.45);
  --select-option-bg: #0b1020;
  --select-option-text: #ffffff;

  /* Region colors */
  --color-north-america: #38BDF8;
  --color-europe: #F472B6;
  --color-asia: #ff4081;
  --color-oceania: #69f0ae;
  --color-south-america: #ff6e40;
  --color-antarctica: #00e5ff;

  --text-xs: clamp(0.55rem, 0.52rem + 0.12vw, 0.68rem);
  --text-sm: clamp(0.68rem, 0.64rem + 0.16vw, 0.78rem);
  --text-base: clamp(0.78rem, 0.72rem + 0.2vw, 0.92rem);
  --text-lg: clamp(0.9rem, 0.82rem + 0.35vw, 1.1rem);
  --text-xl: clamp(1rem, 0.9rem + 0.45vw, 1.4rem);
  --text-2xl: clamp(1.1rem, 0.95rem + 0.7vw, 1.8rem);

  /* Layout column widths */
  --col-right: 320px;
  --col-right-collapsed: 56px;
}

html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  min-height: 100dvh;
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--color-text);
  background: var(--color-globe-bg);
  overflow: hidden;
  position: relative;
}

/* ============================================================
   2-column layout — main column (title/stats + globe) + right rail
   ============================================================ */
.layout {
  position: fixed;
  inset: 0;
  display: grid;
  grid-template-columns: 1fr var(--col-right);
  gap: 16px;
  padding: 16px;
  z-index: 1;
  transition: grid-template-columns 220ms ease;
}

.layout[data-right-collapsed="true"] {
  grid-template-columns: 1fr var(--col-right-collapsed);
}

/* Main column — two rows that visually blend (shared rounded surface) */
.layout-main {
  display: grid;
  grid-template-rows: auto 1fr;
  gap: 0;
  min-width: 0;
  min-height: 0;
  background: linear-gradient(180deg, rgba(10,14,24,0.72), rgba(10,14,24,0.42));
  border: 1px solid var(--color-border);
  border-radius: 18px;
  backdrop-filter: blur(16px);
  overflow: hidden;
}

.main-top {
  padding: 18px 22px 16px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  /* Subtle divider that blends instead of looking like a separate panel */
  border-bottom: 1px solid rgba(255,255,255,0.06);
  background: linear-gradient(180deg, rgba(10,14,24,0.55), rgba(10,14,24,0.18));
}

.main-globe {
  position: relative;
  overflow: hidden;
  background: var(--color-globe-bg);
  min-width: 0;
  min-height: 0;
  /* Globe + zoom must stay clipped here, never bleed into main-top */
  contain: paint;
}

/* Right rail */
.layout-right {
  position: relative;
  background: linear-gradient(180deg, rgba(10,14,24,0.72), rgba(10,14,24,0.42));
  border: 1px solid var(--color-border);
  border-radius: 18px;
  backdrop-filter: blur(16px);
  padding: 16px;
  padding-top: 48px; /* room for the absolute-positioned collapse button */
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 0;
  transition: padding 220ms ease;
}

/* Custom scrollbar for the rail */
.layout-right::-webkit-scrollbar { width: 8px; }
.layout-right::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,0.08);
  border-radius: 4px;
}

/* ---------- Right-rail collapse button ---------- */
.rail-collapse-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 10px;
  border-radius: 8px;
  border: 1px solid var(--select-border);
  background: var(--select-bg);
  color: var(--select-text);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease, transform 0.12s ease;
}
.rail-collapse-btn:hover {
  border-color: var(--select-border-hover);
  background: var(--select-bg-hover);
}
.rail-collapse-btn:active { transform: scale(0.97); }
.rail-collapse-arrows {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1;
  letter-spacing: 0.04em;
  font-weight: 700;
}
.layout-right.collapsed .rail-collapse-btn {
  left: 50%;
  right: auto;
  transform: translateX(-50%);
  padding: 0 8px;
}
.layout-right.collapsed .rail-collapse-btn:active {
  transform: translateX(-50%) scale(0.97);
}


/* ---------- Collapsed rail (icon-only) ---------- */
.layout-right.collapsed {
  padding: 48px 6px 12px;
  gap: 10px;
  align-items: center;
  overflow-x: hidden;
}
.layout-right.collapsed .rail-section {
  width: 40px;
  height: 40px;
  padding: 0;
  margin: 0;
  border-radius: 10px;
  border: 1px solid var(--select-border);
  background: var(--select-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: border-color 0.18s ease, background 0.18s ease;
}
.layout-right.collapsed .rail-section:hover {
  border-color: var(--select-border-hover);
  background: var(--select-bg-hover);
}
.layout-right.collapsed .rail-section::before {
  content: attr(data-rail-icon);
  font-size: 18px;
  line-height: 1;
}
/* Hide all inner content when collapsed; the icon is rendered via ::before */
.layout-right.collapsed .rail-section > * {
  display: none !important;
}
/* Collapsed mode: only show icons for user-actionable sections. Decorative
   panels (fun facts, search insights, sponsored ad) carry no
   data-rail-actionable attribute and are hidden entirely. */
.layout-right.collapsed .rail-section:not([data-rail-actionable="true"]) {
  display: none !important;
}
/* The flexible spacer collapses with the rail */

/* Swap the collapsed admin icon to reflect current auth state. The signed-in
   silhouette becomes a door arrow when only Sign In is available, so a user
   can tell at a glance which action a click will trigger. */
body[data-auth-state="guest"] .layout-right.collapsed .rail-section-admin::before {
  content: "🔑";
}
body[data-auth-state="user"] .layout-right.collapsed .rail-section-admin::before {
  content: "⚙️";
}

/* === Globe Title Block (top row of the main column) === */
.globe-title-block {
  position: relative;
  text-align: center;
  pointer-events: none;
  padding: 0;
}

.globe-title-block[hidden] { display: none; }

.globe-title {
  font-family: var(--font-display, var(--font-body));
  font-size: clamp(1.1rem, 0.95rem + 0.7vw, 1.55rem);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: rgba(245, 249, 255, 0.95);
  margin: 0;
  line-height: 1.15;
  text-shadow: 0 1px 8px rgba(0,0,0,0.45);
  word-wrap: normal;
  overflow-wrap: break-word;
  hyphens: auto;
}


.globe-subtitle {
  font-family: var(--font-body);
  font-size: clamp(0.74rem, 0.66rem + 0.22vw, 0.92rem);
  font-weight: 400;
  color: rgba(245, 249, 255, 0.62);
  margin: 4px 0 0;
  line-height: 1.35;
  letter-spacing: -0.005em;
}

/* === Country Labels overlay (inside globe column) === */
.country-labels {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 30;
  overflow: hidden;
}

.country-label {
  position: absolute;
  left: 0;
  top: 0;
  max-width: 90px;
  /* Falls back to white if --color-globe-text isn't set (e.g. before
     applyDomSettings runs). The user's textColor setting drives this
     variable, so picking a tint in Customize now actually changes the
     label color instead of being silently dropped. */
  color: var(--color-globe-text, rgba(255,255,255,0.72));
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-shadow: 0 1px 8px rgba(0,0,0,0.8);
  text-align: center;
  line-height: 1.1;
  white-space: normal;
  word-break: break-word;
  transform: translate(-50%, -50%);
  pointer-events: none;
  will-change: transform, opacity;
  /* Soft opacity fade. The JS visibility/clutter pass updates the target
     opacity every ~250ms; without a CSS transition any rapid 0↔1 flip
     reads as a hard flicker. A short 180ms ease bridges the gap so even
     when a label is on the edge of the front face it dissolves rather
     than blinks. Transform stays untransitioned so labels still track
     globe rotation snappily. */
  transition: opacity 180ms ease;
}

.country-label.visited {
  /* Inherit color from .country-label (driven by textColor setting); the
     visited modifier only adds the cyan glow halo. */
  text-shadow: 0 0 10px rgba(103, 232, 249, 0.65);
}

.country-label.reference {
  /* Reference (unvisited) labels: inherit the user-chosen color and use a
     plain dark shadow so they don't fight with visited labels. JS already
     applies a separate target opacity for reference vs visited so we
     can't dim with opacity here. */
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.8);
}

/* Night Lights preset: the NASA city-lights texture is bright amber/white
   over a near-black globe, so the default cyan-tinted shadow on visited
   labels gets washed out wherever lights cluster (Europe, NE US, Japan).
   These overrides give labels a warm white core and a thick black halo
   so they stay readable both over the bright city sprawl and over the
   surrounding dark ocean — no neon, just contrast. */
.country-label.preset-night-lights {
  color: #ffffff;
  /* Layered black halo: a tight 0/0/4 carries weight against bright
     city-light cores; the wider 12px softens edges against dark sky.
     The warm 6px ring blends back to the NASA amber so labels feel
     part of the preset rather than a foreign overlay. */
  text-shadow:
    0 0 4px rgba(0, 0, 0, 0.95),
    0 0 8px rgba(0, 0, 0, 0.85),
    0 0 12px rgba(0, 0, 0, 0.6),
    0 0 6px rgba(255, 200, 120, 0.4);
}

.country-label.preset-night-lights.visited {
  color: #ffffff;
  /* Same halo as the base preset override; we keep .visited's cyan
     glow off because Night Lights is a warm preset. */
  text-shadow:
    0 0 4px rgba(0, 0, 0, 0.95),
    0 0 8px rgba(0, 0, 0, 0.85),
    0 0 12px rgba(0, 0, 0, 0.65),
    0 0 6px rgba(255, 220, 150, 0.5);
}

.country-label.preset-night-lights.reference {
  /* Reference (unvisited) labels lean warm amber so visited stays the
     primary white "spotlight" against the bright city-light sprawl. */
  color: rgba(255, 230, 180, 0.92);
  text-shadow:
    0 0 4px rgba(0, 0, 0, 0.95),
    0 0 10px rgba(0, 0, 0, 0.7);
}

/* === Stats Bar === */
.stats-bar {
  position: relative;
  padding: 0;
  text-align: center;
  background: none;
  pointer-events: none;
  animation: fadeSlideDown 0.5s ease;
}

.stats-title-block {
  position: relative;
  text-align: center;
  pointer-events: none;
  padding: 0;
}

.stats-title-block[hidden] { display: none; }

.stats-title {
  color: rgba(103, 232, 249, 0.75);
  text-align: center;
  line-height: 1.5;
  font-size: clamp(0.75rem, 0.9rem, 1.5rem);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin-bottom: 14px;
}

.stats-panel {
  display: flex;
  flex-direction: column;
  width: 100%;
}

.stats-bar {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Horizontal stats on large screens (main column is wide) */
.stats-inner {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: 10px 26px;
  margin: 0;
}

.stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-width: 72px;
}

.stat-divider { display: none; }

.stat-number {
  font-family: var(--font-display, var(--font-body));
  font-size: clamp(1.05rem, 0.95rem + 0.5vw, 1.4rem);
  font-weight: 600;
  color: rgba(245, 249, 255, 0.96);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}

.stat-label {
  font-size: 10px;
  font-weight: 500;
  color: rgba(245, 249, 255, 0.5);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  white-space: nowrap;
}

@keyframes fadeSlideDown {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: translateY(0); }
}


/* === Globe === */
#globe-container {
  position: absolute;
  inset: 0;
  z-index: 1;
}

#globe-container::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at center, rgba(52,120,255,0.08), transparent 42%);
  pointer-events: none;
}

#globe-canvas {
  width: 100%;
  height: 100%;
  display: block;
}

/* === Tooltip === */
.tooltip {
  position: fixed;
  z-index: 200;
  pointer-events: none;
  background: rgba(13, 15, 24, 0.92);
  backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  padding: 8px 14px;
  font-size: var(--text-sm);
  color: #fff;
  opacity: 0;
  transition: opacity 150ms ease;
  white-space: nowrap;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.4);
  max-width: 280px;
}
.tooltip-main {
  display: flex;
  align-items: center;
  gap: 8px;
}
.tooltip.visible { opacity: 1; }
/* Sticky-pinned popup whose feature rotated to the far side of the globe.
   We keep the DOM mounted (so the click-to-pin user intent survives a
   drag) but hide it visually until the feature rotates back into view —
   prevents the user-reported flicker where the popup was torn down each
   time the dot crossed the horizon. */
.tooltip.hidden-far-side { opacity: 0; pointer-events: none; }
/* When a popup is pinned (sticky) we right-pad the layout so the close
   button has somewhere to sit, and the popup picks up a slightly stronger
   shadow to read as "this is now interactive". */
.tooltip.pinned { padding-right: 30px; }
/* Close (×) button. Hidden by default — only shows up when the popup is
   in pinned/sticky mode. 32px touch target on mobile. */
.tooltip-close {
  display: none;
  position: absolute;
  top: 4px;
  right: 4px;
  width: 24px;
  height: 24px;
  min-width: 24px;
  min-height: 24px;
  padding: 0;
  border: none;
  background: rgba(255,255,255,0.08);
  color: rgba(255,255,255,0.85);
  border-radius: 50%;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}
.tooltip.pinned .tooltip-close { display: block; }
.tooltip-close:hover, .tooltip-close:focus-visible {
  background: rgba(255,255,255,0.18);
  outline: none;
}
@media (max-width: 480px) {
  .tooltip-close { width: 32px; height: 32px; min-width: 32px; min-height: 32px; font-size: 20px; top: 4px; right: 4px; }
  .tooltip.pinned { padding-right: 38px; }
}
.tooltip-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.tooltip-info { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tooltip-city { font-weight: 600; font-size: var(--text-sm); }
.tooltip-detail { font-size: var(--text-xs); color: var(--color-text-muted); }

/* City popup thumbnail. Small, square, fixed size so the popup reserves
   the slot before the image decodes — no layout shift when the landmark
   loads or fails. There is intentionally no placeholder tile: if the
   renderer has no curated entry, the whole .tooltip-thumb wrapper is
   omitted; if a curated image 404s, the wrapper is removed at runtime
   via the <img onerror=...> handler. Either way, the popup never shows
   a hollow gray box. */
.tooltip-thumb {
  position: relative;
  display: inline-block;
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border-radius: 6px;
  overflow: hidden;
  background: rgba(0,0,0,0.4);
  box-shadow: 0 1px 4px rgba(0,0,0,0.35);
}
.tooltip-thumb-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
}
@media (max-width: 480px) {
  .tooltip-thumb { width: 40px; height: 40px; }
}
.trip-arrow {
  display: inline-block;
  margin: 0 4px;
  color: #67e8f9;
  font-weight: 500;
  opacity: 0.85;
}

/* === Right column controls === */
.right-top-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

.right-top-controls .ctrl-btn,
.right-top-controls .admin-menu-select-wrap {
  flex: 0 0 auto;
}

.right-spacer {
  flex: 1 1 auto;
  display: flex;
  align-items: flex-end;
  min-height: 120px;
}

.search-guide {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 14px;
  background: linear-gradient(180deg, rgba(10,14,24,0.38), rgba(10,14,24,0.18));
  color: rgba(255,255,255,0.58);
}

.search-guide span {
  display: block;
  margin-bottom: 6px;
  color: rgba(103, 232, 249, 0.75);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.search-guide p {
  max-width: none;
  font-size: 12px;
  line-height: 1.45;
}

.filter-block {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Two-column row for compact rail sections (Year + Type). Each child
   .filter-block takes half the rail width with the same gap the rail
   uses between sections. */
.rail-row {
  display: flex;
  flex-direction: row;
  gap: 14px;
  align-items: stretch;
}
.rail-row > .rail-section {
  flex: 1 1 0;
  min-width: 0;
}
/* When the rail is collapsed, fall back to icon-only behavior: let the
   children act as direct rail-section icons so they line up vertically
   (desktop) or wrap horizontally (mobile) like every other section. */
.layout-right.collapsed .rail-row {
  display: contents;
}

.filter-label,
.search-block .filter-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.45);
}

/* === Shared dark-select styling (Region, Year, Admin Menu) === */
.dark-select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  width: 100%;
  background: var(--select-bg);
  color: var(--select-text);
  border: 1px solid var(--select-border);
  border-radius: 10px;
  padding: 9px 32px 9px 12px;
  font-size: var(--text-sm);
  font-family: var(--font-body);
  font-weight: 500;
  cursor: pointer;
  outline: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none'><path d='M3 4.5l3 3 3-3' stroke='white' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 12px;
  transition: border-color 0.18s ease, background-color 0.18s ease, box-shadow 0.18s ease;
}

.dark-select:hover {
  background-color: var(--select-bg-hover);
  border-color: var(--select-border-hover);
}

.dark-select:focus {
  border-color: var(--select-focus);
  box-shadow: 0 0 0 3px rgba(103, 232, 249, 0.15);
}

.dark-select option,
.dark-select optgroup {
  background: var(--select-option-bg);
  color: var(--select-option-text);
}

/* === Admin Menu (legacy class names preserved) === */
.admin-menu-select-wrap {
  position: relative;
  display: block;
  width: 100%;
}

/* Make the Admin menu trigger at least as wide as the Region/Year
   selectors below it so "View / Edit Flights or Trips" and similar
   labels stop wrapping. The expanded rail width is driven by
   --col-right; we let the trigger fill the rail's interior. */
.rail-section-admin .dark-select-wrap,
.rail-section-admin .admin-menu-select-wrap {
  width: 100%;
}
.rail-section-admin .dark-select-trigger {
  min-width: 100%;
}
.rail-section-admin .dark-select-label {
  white-space: nowrap;
}
.rail-section-admin .dark-select-list {
  min-width: 100%;
  white-space: nowrap;
}
.rail-section-admin .dark-select-option {
  white-space: nowrap;
}

.admin-menu-select {
  /* Inherits from .dark-select; this class kept so existing JS still finds it */
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  border: 1px solid var(--select-border);
  background: var(--select-bg);
  color: var(--select-text);
  padding: 9px 36px 9px 14px;
  border-radius: 10px;
  font-size: var(--text-sm);
  font-weight: 600;
  outline: none;
  cursor: pointer;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none'><path d='M3 4.5l3 3 3-3' stroke='white' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 12px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.28);
  transition: border-color 0.18s ease, background-color 0.18s ease;
}

.admin-menu-select:hover {
  border-color: var(--select-border-hover);
  background-color: var(--select-bg-hover);
}

.admin-menu-select option {
  background: var(--select-option-bg);
  color: var(--select-option-text);
}

/* === Top toggle buttons === */
.toggle-btn,
.ctrl-btn {
  height: 36px;
  padding: 0 12px;
  min-width: 36px;
  border-radius: 10px;
  border: 1px solid var(--select-border);
  background: var(--select-bg);
  color: var(--select-text);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  backdrop-filter: blur(16px);
  transition: background 0.18s ease, border-color 0.18s ease, transform 0.12s ease;
}

.toggle-btn:hover,
.ctrl-btn:hover {
  border-color: var(--select-border-hover);
  background: var(--select-bg-hover);
}

.toggle-btn:active,
.ctrl-btn:active { transform: scale(0.97); }

/* === Search === */
.search-block {
  display: flex;
  flex-direction: column;
  gap: 6px;
  position: relative;
}

/* Floating bottom dock — single absolutely-positioned container anchored to
   the bottom-right corner of the main globe column. Lives inside .main-globe
   so it never overlaps the stats row (stats sit in .main-top, above
   .main-globe, which is `contain: paint`). Holds Search Insights (above) and
   the search input (below) as flex-column children so the two panels are
   guaranteed to render as a stacked pair with Insights directly above the
   search bar — no separate absolute positioning that could drift. */
.globe-bottom-dock {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 25;
  width: min(320px, calc(100% - 24px));
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* Cap total dock height to the globe column minus the stats row offset so
     an expanded Insights panel can never grow tall enough to overlap the
     stats bar above. */
  max-height: calc(100% - 24px);
  pointer-events: none;
}
.globe-bottom-dock > * { pointer-events: auto; }

body.embed-mode .globe-bottom-dock { display: none !important; }

/* Search input block — sits at the bottom of the shared dock. No own
   positioning; the dock controls placement. */
.globe-search-dock {
  padding: 8px 10px;
  background: rgba(10, 14, 24, 0.72);
  border: 1px solid var(--color-border);
  border-radius: 12px;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
  /* Order ensures the input sits below Insights regardless of source order. */
  order: 2;
  flex: 0 0 auto;
}

body.embed-mode .globe-search-dock { display: none !important; }

/* Search Insights — sits directly above the search input inside the same
   dock. Hidden by default and toggled from app.js (#search-facts hidden
   attribute when no active search). When visible, the panel takes remaining
   vertical space in the dock and scrolls internally so the dock's max-height
   cap keeps it from spilling over the stats bar above. */
.globe-search-facts {
  /* Order ensures Insights renders above the search input inside the dock. */
  order: 1;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
.globe-search-facts[hidden] { display: none !important; }

/* Collapsed state — keep only the header visible above the search dock so
   the user can re-expand without clearing the search. */
.globe-search-facts.is-collapsed {
  flex: 0 0 auto;
  overflow: hidden;
}
.globe-search-facts.is-collapsed .search-facts-body { display: none; }
.globe-search-facts.is-collapsed .ti-header { margin-bottom: 0; }

body.embed-mode .globe-search-facts { display: none !important; }

/* Sign-in CTA — sits at the bottom of the dock under the search input.
   Visible only when signed-out AND on the default/demo (no-slug) route.
   app.js toggles the [hidden] attribute; CSS handles the look. */
.globe-signin-cta {
  position: relative;
  order: 3;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  background: rgba(10, 14, 24, 0.78);
  border: 1px solid var(--color-border);
  border-radius: 12px;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
  color: rgba(255, 255, 255, 0.92);
  font-size: 13px;
  line-height: 1.35;
}
.globe-signin-cta[hidden] { display: none !important; }
.globe-signin-cta-close {
  position: absolute;
  top: 4px;
  right: 6px;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  background: transparent;
  color: rgba(255, 255, 255, 0.55);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  border-radius: 6px;
  transition: color 150ms ease, background 150ms ease;
}
.globe-signin-cta-close:hover,
.globe-signin-cta-close:focus-visible {
  color: #fff;
  background: rgba(255, 255, 255, 0.08);
  outline: none;
}
.globe-signin-cta-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.globe-signin-cta-text strong {
  font-size: 13px;
  font-weight: 600;
  color: #fff;
}
.globe-signin-cta-text span {
  color: rgba(255, 255, 255, 0.7);
  font-size: 12px;
}
.globe-signin-cta-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.globe-signin-cta-btn {
  flex: 1 1 auto;
  min-width: 0;
  padding: 7px 12px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease;
}
.globe-signin-cta-btn:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.28);
}
.globe-signin-cta-btn.primary {
  background: var(--color-accent, #448aff);
  border-color: transparent;
}
.globe-signin-cta-btn.primary:hover {
  background: var(--color-accent-hover, #5a99ff);
  filter: brightness(1.05);
}

body.embed-mode .globe-signin-cta { display: none !important; }

/* On mobile the bottom action bar already exposes Sign in, so the large
   signed-out CTA card just consumes vertical space. Hide it on small
   viewports (JS also enforces this — CSS is defense-in-depth). */
@media (max-width: 820px) {
  .globe-signin-cta { display: none !important; }
}

/* Toggle button in the Search Insights header. Subtle by default, with a
   rotating chevron that flips on collapse. */
.search-facts .ti-toggle {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 6px;
  border: 1px solid rgba(255,255,255,0.08);
  background: rgba(255,255,255,0.04);
  color: rgba(255,255,255,0.7);
  cursor: pointer;
  padding: 0;
  line-height: 1;
  transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
}
.search-facts .ti-toggle:hover {
  background: rgba(255,255,255,0.08);
  color: #fff;
  border-color: rgba(255,255,255,0.18);
}
.search-facts .ti-toggle:focus-visible {
  outline: 2px solid rgba(103, 232, 249, 0.6);
  outline-offset: 1px;
}
.search-facts .ti-toggle-icon {
  font-size: 11px;
  transform: rotate(0deg);
  transition: transform 150ms ease;
}
.search-facts.is-collapsed .ti-toggle-icon { transform: rotate(-90deg); }

@media (max-width: 900px) {
  .globe-bottom-dock {
    right: 8px;
    bottom: 8px;
    left: 8px;
    width: auto;
    max-width: none;
    max-height: calc(100% - 16px);
  }
  .globe-search-dock { padding: 6px 8px; }
}

.search-input-wrap {
  position: relative;
  display: flex;
}

.search-input {
  width: 100%;
  background: var(--select-bg);
  color: var(--select-text);
  border: 1px solid var(--select-border);
  border-radius: 10px;
  padding: 10px 36px 10px 12px;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  outline: none;
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.search-input::placeholder { color: rgba(255,255,255,0.4); }
.search-input:focus {
  border-color: var(--select-focus);
  box-shadow: 0 0 0 3px rgba(103, 232, 249, 0.15);
}

.search-clear {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 0;
  background: rgba(255,255,255,0.08);
  color: rgba(255,255,255,0.85);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
}
.search-clear:hover { background: rgba(255,255,255,0.16); }

.search-suggestions {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  right: 0;
  background: rgba(10, 14, 24, 0.96);
  border: 1px solid var(--select-border);
  border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.4);
  max-height: 220px;
  overflow-y: auto;
  z-index: 20;
}

.search-suggestion {
  padding: 8px 12px;
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--color-text);
  display: flex;
  justify-content: space-between;
  gap: 8px;
}
.search-suggestion:hover,
.search-suggestion.active { background: rgba(103, 232, 249, 0.12); color: #fff; }
.search-suggestion .suggestion-kind {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: rgba(255,255,255,0.45);
  align-self: center;
}

/* === Fun Facts panel(s) === */
.travel-insights,
.search-facts {
  position: relative;
  width: 100%;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  pointer-events: none;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 14px;
  background: linear-gradient(180deg, rgba(10,14,24,0.72), rgba(10,14,24,0.42));
  backdrop-filter: blur(12px);
  box-shadow: 0 8px 24px rgba(0,0,0,0.22);
}

/* Legacy "Fun Facts" header is now rendered inside the panel via
   .ti-header (see renderInsightCards in app.js) so we don't need a ::before
   pseudo-header any more. Keeping a no-op rule preserves the selector. */
.travel-insights::before {
  content: none;
}

.search-facts-title {
  color: rgba(103, 232, 249, 0.75);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  margin-bottom: 4px;
}

.travel-insight-line,
.search-facts-body > div {
  padding: 0;
  border: 0;
  background: transparent;
  color: rgba(255,255,255,0.82);
  font-size: 11px;
  line-height: 1.4;
  white-space: normal;
}

.travel-insight-line strong,
.search-facts-body strong {
  color: #fff;
  font-weight: 700;
}
.search-facts-body span.hl { color: #67e8f9; font-weight: 600; }

/* === Legend (legacy, hidden by default — region dropdown replaces it) === */
.legend { position: absolute; bottom: 16px; left: 16px; z-index: 100; }
.legend[hidden] { display: none; }
.legend-toggle {
  display: flex; align-items: center; gap: 6px;
  background: rgba(13, 15, 24, 0.8); backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 8px;
  padding: 8px 14px; color: var(--color-text-muted);
  font-size: var(--text-sm); font-family: var(--font-body);
  cursor: pointer; transition: all 180ms ease;
}
.legend-toggle:hover { color: #fff; border-color: rgba(255,255,255,0.15); }
.legend-items { display: none; flex-direction: column; gap: 4px; margin-top: 6px; background: rgba(13, 15, 24, 0.9); backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 8px; padding: 8px 10px; }
.legend.open .legend-items { display: flex; }
.legend-item { display: flex; align-items: center; gap: 8px; padding: 4px 6px; border-radius: 4px; cursor: pointer; transition: all 150ms ease; font-size: var(--text-sm); color: var(--color-text); user-select: none; }
.legend-item:hover { background: rgba(255, 255, 255, 0.05); }
.legend-item.dimmed { opacity: 0.3; }
.legend-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.legend-count { margin-left: auto; font-size: var(--text-xs); color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
.legend-actions { display: flex; gap: 6px; padding: 4px 6px 8px; border-bottom: 1px solid rgba(255,255,255,0.08); margin-bottom: 4px; }
.legend-actions button { flex: 1; border: 1px solid rgba(255,255,255,0.08); border-radius: 6px; background: rgba(255,255,255,0.04); color: rgba(255,255,255,0.72); font-size: 10px; padding: 4px 6px; cursor: pointer; }
.legend-actions button:hover { color: white; background: rgba(255,255,255,0.08); }

/* === Year filter (legacy — kept for app.js compatibility, hidden) === */
.year-filter {
  display: flex; gap: 4px; flex-wrap: wrap;
}
.year-filter[hidden] { display: none; }
.year-select {
  display: none;
  background: var(--select-bg);
  border: 1px solid var(--select-border);
  border-radius: 8px; padding: 6px 28px 6px 10px;
  color: #fff; font-size: var(--text-xs);
  font-family: var(--font-body); font-weight: 500;
  cursor: pointer; appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none'><path d='M3 4.5l3 3 3-3' stroke='white' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat; background-position: right 8px center; background-size: 12px;
}
.year-select option { background: var(--select-option-bg); color: #fff; }
.year-btn {
  background: var(--select-bg); border: 1px solid var(--select-border);
  border-radius: 6px; padding: 5px 10px; color: var(--color-text-muted);
  font-size: var(--text-xs); font-family: var(--font-body); font-weight: 500;
  cursor: pointer; transition: all 180ms ease;
}
.year-btn:hover { color: #fff; border-color: rgba(255,255,255,0.2); }
.year-btn.active { color: #fff; background: rgba(79, 152, 163, 0.25); border-color: rgba(79, 152, 163, 0.4); }

/* === Google Ads slot ===
   The ad shell lives at the bottom of the right column on desktop, and on
   mobile (single-column layout) it is reordered to sit at the very bottom of
   the page. The placeholder is purely cosmetic — it stays visible until a
   real AdSense publisher/slot ID is wired up. pointer-events:none on the
   placeholder prevents it from ever swallowing globe interactions. */
.ad-shell {
  width: 100%;
  min-height: 90px;
  margin-top: auto;
  border-radius: 12px;
  border: 1px solid rgba(255,255,255,0.06);
  background: rgba(10,14,24,0.42);
  overflow: hidden;
  padding: 8px;
  position: relative;
  flex-shrink: 0;
}
.ad-placeholder {
  position: absolute;
  inset: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border: 1px dashed rgba(255,255,255,0.12);
  border-radius: 10px;
  color: rgba(255,255,255,0.52);
  text-align: center;
  pointer-events: none;
}
.ad-placeholder span {
  color: rgba(255,255,255,0.76);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.ad-placeholder small {
  font-size: 11px;
}
.ad-slot {
  display: block !important;
  width: 100% !important;
  min-height: 90px !important;
}
/* Hide the AdSense <ins> when running with the placeholder/no real config so
   it doesn't reserve extra empty space below the dashed box. */
html[data-ads="placeholder"] .ad-slot { display: none !important; }

/* === Admin status pills (unchanged) === */
#admin-view .status-pill {
  display: inline-flex; align-items: center; padding: 2px 8px;
  border-radius: 999px; font-size: 11px; font-weight: 600;
}
#admin-view .status-pill.visited {
  color: var(--color-success); background: var(--color-success-bg);
  border: 1px solid rgba(105, 240, 174, 0.25);
}
#admin-view .status-pill.not-visited {
  color: var(--color-text-muted); background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.08);
}

.btn-signout { background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.08); }
.btn-signout:hover { background: rgba(255,255,255,0.12); }

.ui-separator {
  width: 1px;

  align-self: stretch;

  min-height: 14px;
  max-height: 24px;

  background: linear-gradient(
    to bottom,
    transparent,
    rgba(255,255,255,0.14),
    transparent
  );

  opacity: 0.9;
}

.ui-separator-horizontal {
  width: clamp(60px, 12vw, 100%);

  height: 1px;

  background: linear-gradient(
    to right,
    transparent,
    rgba(255,255,255,0.14),
    transparent
  );

  opacity: 0.9;
}

/* ============================================================
   Custom dark dropdown (replaces native browser-default popups)
   ============================================================ */
.dark-select-wrap {
  position: relative;
  width: 100%;
  display: block;
}

/* Hide the native select but keep it accessible to assistive tech and the
   existing app.js code that reads .value and listens for 'change'. */
.dark-select-wrap > select.dark-select {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip: rect(0 0 0 0);
  overflow: hidden;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
}

.dark-select-trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  background: var(--select-bg);
  color: var(--select-text);
  border: 1px solid var(--select-border);
  border-radius: 10px;
  padding: 9px 12px;
  font-size: var(--text-sm);
  font-family: var(--font-body);
  font-weight: 500;
  cursor: pointer;
  outline: none;
  text-align: left;
  transition: border-color 0.18s ease, background-color 0.18s ease, box-shadow 0.18s ease;
}
.dark-select-trigger:hover {
  background-color: var(--select-bg-hover);
  border-color: var(--select-border-hover);
}
.dark-select-wrap.open .dark-select-trigger,
.dark-select-trigger:focus-visible {
  border-color: var(--select-focus);
  box-shadow: 0 0 0 3px rgba(103, 232, 249, 0.15);
}
.dark-select-wrap.placeholder .dark-select-label {
  color: rgba(255,255,255,0.55);
  font-weight: 600;
}
.dark-select-label {
  flex: 1 1 auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dark-select-caret {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  color: rgba(255,255,255,0.78);
  transition: transform 180ms ease;
}
.dark-select-wrap.open .dark-select-caret { transform: rotate(180deg); }

.dark-select-list {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 50;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: rgba(10, 14, 24, 0.97);
  border: 1px solid var(--select-border);
  border-radius: 12px;
  box-shadow: 0 16px 40px rgba(0,0,0,0.45);
  backdrop-filter: blur(14px);
  max-height: 260px;
  overflow-y: auto;
  display: none;
}
.dark-select-wrap.open .dark-select-list { display: block; }

.dark-select-option {
  padding: 8px 10px;
  border-radius: 8px;
  color: var(--select-option-text);
  font-size: var(--text-sm);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  transition: background 0.12s ease, color 0.12s ease;
}
.dark-select-option:hover,
.dark-select-option:focus-visible {
  background: rgba(103, 232, 249, 0.14);
  color: #fff;
  outline: none;
}
.dark-select-option.selected {
  background: rgba(103, 232, 249, 0.22);
  color: #fff;
  font-weight: 600;
}
.dark-select-option.disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Non-selectable section header inside a custom dark dropdown. Used by the
   Admin menu to group items (Account / Add / Manage / Data / Share / Help)
   without depending on native <optgroup> styling, which can't be themed
   consistently across browsers. */
.dark-select-section {
  padding: 8px 10px 4px;
  margin-top: 2px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(103, 232, 249, 0.78);
  pointer-events: none;
  user-select: none;
}
.dark-select-section:first-child { margin-top: 0; }

/* ============================================================
   Subtle author credit (.app-credit) — centered under the globe
   column on every viewport. Lives inside .layout-main so it
   aligns to the globe column, not the whole page or right rail.
   ============================================================ */
.app-credit {
  font-family: var(--font-body);
  font-size: 11px;
  color: rgba(255, 255, 255, 0.42);
  letter-spacing: 0.02em;
  text-align: center;
  padding: 6px 12px 8px;
  background: transparent;
  pointer-events: none;
  user-select: text;
}
.app-credit .app-credit-sep {
  margin: 0 4px;
  color: rgba(255, 255, 255, 0.3);
}
.app-credit .app-credit-brand {
  color: rgba(255, 255, 255, 0.6);
  font-weight: 600;
}
.app-credit .app-credit-author {
  color: rgba(255, 255, 255, 0.55);
}
.app-credit .app-credit-tools {
  color: rgba(255, 255, 255, 0.5);
}
@media (max-width: 820px) {
  .app-credit {
    padding: 6px 16px 10px;
    font-size: 10px;
  }
}
/* Admin page reuses the existing .admin-footer chrome but the new wording
   gets a slightly lighter, condensed look so it blends with the dark UI. */
.app-credit-inline {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.5);
  letter-spacing: 0.02em;
}

/* Preset-specific attribution (e.g. NASA credit while the Night Lights
   preset is active). Painted into the existing .app-credit row by the
   renderer so it inherits center alignment + the muted styling and
   never blocks the right-rail controls. The link is the only
   clickable element — `pointer-events: auto` re-enables clicks on it
   despite the parent .app-credit disabling them globally. */
.app-credit .preset-credit {
  display: inline;
  color: rgba(255, 255, 255, 0.5);
}
.app-credit .preset-credit-link {
  color: rgba(180, 215, 255, 0.85);
  text-decoration: underline;
  text-decoration-color: rgba(180, 215, 255, 0.45);
  pointer-events: auto;
}
.app-credit .preset-credit-link:hover,
.app-credit .preset-credit-link:focus {
  color: rgba(220, 235, 255, 0.95);
  text-decoration-color: rgba(220, 235, 255, 0.7);
}
@media (max-width: 820px) {
  .app-credit .preset-credit {
    display: block;
    margin-top: 2px;
    font-size: 9px;
    line-height: 1.35;
  }
  .app-credit .preset-credit-sep {
    display: none;
  }
}
body.embed-mode .app-credit .preset-credit {
  color: rgba(255, 255, 255, 0.42);
}
body.embed-mode .app-credit .preset-credit-link {
  color: rgba(180, 215, 255, 0.7);
}

/* ============================================================
   Embed mode (?embed=main) — hide right rail, ads, and admin chrome
   so an iframe can show only the title/stats + globe column.
   ============================================================ */
body.embed-mode .layout {
  grid-template-columns: 1fr !important;
}
body.embed-mode .layout-right,
body.embed-mode #ga-toasts {
  display: none !important;
}
/* Subtle app credit stays visible inside the iframe so the embed always
   carries attribution back to MyTravelMap.app. Styled a touch quieter than
   the desktop credit so it doesn't compete with the embedder's own page. */
body.embed-mode .app-credit {
  padding: 4px 12px 6px;
  font-size: 10px;
  color: rgba(255, 255, 255, 0.38);
  background: transparent;
}
body.embed-mode .app-credit .app-credit-brand {
  color: rgba(255, 255, 255, 0.55);
}
body.embed-mode .app-credit .app-credit-author,
body.embed-mode .app-credit .app-credit-tools {
  color: rgba(255, 255, 255, 0.45);
}
/* Hide the right-rail ad shell but keep the dedicated embed ad shell visible
   under the globe. The embed shell is injected from the early-script only
   when embed mode is active, so on the normal page it stays off-DOM. */
body.embed-mode .ad-shell.right-ad-shell { display: none !important; }
/* Embed/iframe view: NO Google AdSense. The embed-ad-shell stays in the
   DOM (other selectors reference it) but is fully hidden so the sponsored
   area inside iframes contains only the affiliate stack. */
body.embed-mode .ad-shell.embed-ad-shell { display: none !important; }
body.embed-mode .layout-main {
  /* Add rows for the embed affiliate slot and the subtle app credit below
     the globe. The globe row keeps its existing 1fr so it still fills the
     available space; the affiliate slot and credit sit in auto-sized
     strips beneath. */
  grid-template-rows: auto 1fr auto auto;
}
/* Embed-mode affiliate slot: render the affiliate cards side-by-side and
   very small so the iframe sponsored strip stays under ~80px tall. The
   .affiliate-stack inside is forced into a horizontal flex layout with
   each card sized to share the row equally. */
body.embed-mode #embed-affiliate-slot {
  margin: 6px 12px 10px;
  padding: 0;
  max-height: 90px;
}
body.embed-mode #embed-affiliate-slot .affiliate-stack,
body.embed-mode #embed-affiliate-slot .affiliate-stack-side-by-side {
  display: flex;
  flex-direction: row;
  gap: 6px;
  align-items: stretch;
}
body.embed-mode #embed-affiliate-slot .affiliate-stack .affiliate-card,
body.embed-mode #embed-affiliate-slot .affiliate-card {
  flex: 1 1 0;
  min-width: 0;
  margin-top: 0;
  font-size: 11px;
}
body.embed-mode #embed-affiliate-slot .affiliate-card-link {
  padding: 4px 6px;
  gap: 4px;
}
body.embed-mode #embed-affiliate-slot .affiliate-card-title {
  font-size: 11px;
  line-height: 1.2;
}
body.embed-mode #embed-affiliate-slot .affiliate-card-sub,
body.embed-mode #embed-affiliate-slot .affiliate-card-brand {
  font-size: 9px;
}
body.embed-mode #embed-affiliate-slot .affiliate-card-icon {
  font-size: 14px;
}
body.embed-mode #embed-affiliate-slot .affiliate-card-disclosure {
  font-size: 9px;
  padding: 2px 6px 4px;
}
@media (max-width: 820px) {
  /* Mobile embed: tighten margins/font further; remain side-by-side. */
  body.embed-mode #embed-affiliate-slot {
    margin: 4px 8px 6px;
    max-height: 80px;
  }
  body.embed-mode #embed-affiliate-slot .affiliate-card-title {
    font-size: 10px;
  }
  body.embed-mode #embed-affiliate-slot .affiliate-card-sub,
  body.embed-mode #embed-affiliate-slot .affiliate-card-brand {
    font-size: 8px;
  }
}
/* The embed slot is meaningless outside embed mode. Belt-and-braces: it's
   already hidden by the `hidden` attribute and only injected on embed, but
   if someone forces it visible it won't bleed into the normal page. */
body:not(.embed-mode) .embed-ad-shell { display: none !important; }
/* Edge-to-edge inside the iframe so the embedder controls outer padding. */
body.embed-mode .layout {
  padding: 0;
  gap: 0;
}
body.embed-mode .layout-main {
  border-radius: 0;
  border: 0;
}

/* === Responsive — narrow viewports collapse to a single column === */
@media (max-width: 980px) {
  :root { --col-right: 280px; }
}

@media (max-width: 820px) {
  body { overflow: auto; }
  .layout {
    position: relative;
    inset: auto;
    grid-template-columns: 1fr;
    grid-template-rows: auto auto;
    gap: 8px;
    padding: 8px;
    min-height: 100dvh;
    transition: none;
  }
  /* On mobile, collapsed state collapses vertically (hide the rail but keep
     the toggle visible inline). */
  .layout[data-right-collapsed="true"] {
    grid-template-columns: 1fr;
  }
  /* Default globe view should fit inside the screen on mobile. The globe row
     is sized off dvh (Safari/iOS friendly) so the stats panel can sit above
     it without pushing the globe off-screen. With the surrounding panels
     tightened, the globe row can reclaim the freed vertical space. */
  .layout-main {
    grid-template-rows: auto min(74vh, 74dvh);
    /* On mobile the column flows vertically and grows past the viewport
       (teleported search dock + action bar + credit + ad shell are
       implicit grid rows below the globe). The base `overflow: hidden` —
       intended to clip the globe inside the rounded surface on desktop —
       was clipping the bottom of the ad shell on phones so the user could
       not scroll all the way down to the sponsored block. Let the page's
       own scroll handle it on mobile; `.main-globe` keeps its own
       `overflow: hidden` so the globe canvas is still clipped. */
    overflow: visible;
  }
  .main-globe {
    /* Hard ceiling so the canvas can never grow taller than the column it
       lives in — prevents zoom from pushing the globe over the stats. */
    max-height: min(74vh, 74dvh);
  }
  .layout-right {
    max-height: none;
    overflow: visible;
    padding-top: 12px;
  }
  .rail-collapse-btn {
    position: relative;
    top: auto;
    right: auto;
    align-self: flex-end;
    margin-bottom: 4px;
  }
  /* Collapsed rail on mobile lays icons out horizontally */
  .layout-right.collapsed {
    flex-direction: row;
    flex-wrap: wrap;
    padding: 8px;
    justify-content: flex-start;
  }
  .layout-right.collapsed .rail-collapse-btn {
    position: relative;
    left: auto;
    right: auto;
    transform: none;
    order: -1;
  }
  .stats-inner { gap: 6px 14px; }

  /* Tighten the top stats panel on mobile so it hugs the title + stat grid
     and leaves maximum vertical room for the globe below. The desktop values
     leave noticeable blank space on phones. */
  .main-top {
    padding: 10px 12px 8px;
    gap: 10px;
  }
  .globe-title-block { padding: 0; }
  .globe-title {
    font-size: clamp(1rem, 0.85rem + 1.2vw, 1.25rem);
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.15;
    color: rgba(245, 249, 255, 0.96);
  }
  .globe-subtitle {
    margin-top: 3px;
    line-height: 1.3;
    font-size: 12px;
    color: rgba(245, 249, 255, 0.6);
  }
  .stat { min-width: 56px; gap: 2px; }
  .stat-number {
    font-size: clamp(0.95rem, 0.82rem + 0.65vw, 1.15rem);
    font-weight: 600;
    color: rgba(245, 249, 255, 0.96);
    letter-spacing: -0.02em;
    line-height: 1.05;
  }
  .stat-label {
    letter-spacing: 0.08em;
    font-size: 9.5px;
    font-weight: 500;
    color: rgba(245, 249, 255, 0.52);
  }
  .stats-bar { animation: none; }

  /* On mobile/vertical, pin the Google Ads block to the very bottom of the
     page — below the right-column controls. The right rail is already the
     last grid row in single-column mode, and the ad shell is the last child
     within it, so it naturally lands at the bottom. */
  .ad-shell {
    margin-top: 8px;
    min-height: 100px;
    max-height: 140px;
  }

  /* When the right rail is collapsed on mobile, keep the Google Ads block
     visible at full width instead of folding it into an icon. Other rail
     sections still collapse to icons; the ad slot needs a real box for
     AdSense to render into. The shell stays inside the same flex row but
     forces a line-break (flex-basis:100%) so it sits below the icon strip,
     anchored at the bottom of the rail. */
  .layout-right.collapsed #right-ad-shell.rail-section {
    width: 100% !important;
    height: auto !important;
    min-height: 100px;
    flex: 0 0 100%;
    padding: 8px;
    border-radius: 12px;
    order: 999;
    display: block;
    overflow: hidden;
  }
  .layout-right.collapsed #right-ad-shell.rail-section::before {
    content: none;
  }
  .layout-right.collapsed #right-ad-shell.rail-section > * {
    display: revert !important;
  }
  .layout-right.collapsed #right-ad-shell .ad-placeholder {
    display: flex !important;
  }
}

@media (max-width: 520px) {
  .stats-inner { gap: 5px 10px; }
  .right-top-controls { gap: 6px; }
  .toggle-btn, .ctrl-btn { height: 34px; padding: 0 10px; font-size: 12px; }
  .stat { min-width: 52px; gap: 2px; }
  /* Hug the panel tighter on phones to maximise globe real estate, but keep
     a clear gap between title and stats so the header reads as two layers. */
  .main-top { padding: 8px 10px 6px; gap: 8px; }
  .globe-subtitle { margin-top: 2px; }
  /* On very narrow phones, give Year + Type their own full-width rows
     again so dropdown labels never feel cramped. */
  .rail-row { display: contents; }
  /* The stats and rail are now compact enough that the globe can take
     more of the viewport on small phones too. */
  .layout-main {
    grid-template-rows: auto min(70vh, 70dvh);
  }
  .main-globe {
    max-height: min(70vh, 70dvh);
  }
}


/* ============================================================
   In-globe admin (Login + Modals + Toasts)
   See globe-admin.js for behaviour.
   ============================================================ */
#globe-admin { font-family: var(--font-body); }

.ga-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.65);
  backdrop-filter: blur(6px);
  z-index: 2000;
  display: flex; align-items: center; justify-content: center;
  opacity: 0;
  transition: opacity 180ms ease;
  padding: 16px;
}
.ga-overlay.visible { opacity: 1; }
.ga-overlay[hidden] { display: none; }

.ga-modal {
  background: #0d0f18;
  color: #e0e0e6;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  width: 480px;
  max-width: 100%;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: 0 24px 60px rgba(0,0,0,0.55);
  transform: translateY(8px);
  transition: transform 180ms ease;
}
.ga-overlay.visible .ga-modal { transform: translateY(0); }
.ga-modal-lg { width: 640px; }

.ga-modal-header,
.ga-modal-footer {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px;
  padding: 14px 20px;
  border-bottom: 1px solid rgba(79, 152, 163, 0.25);
}
.ga-modal-footer {
  border-bottom: none;
  border-top: 1px solid rgba(79, 152, 163, 0.25);
  justify-content: flex-end;
}
.ga-modal-header h2 {
  font-family: var(--font-display);
  font-size: 16px; font-weight: 600; color: #fff;
}
.ga-modal-body { padding: 16px 20px; }

/* Modal close button. Previously the icon used a 25%-opacity teal which
   was effectively invisible on the dark #0d0f18 modal background — users
   on mobile reported they couldn't see the X. We bump the icon to a
   readable light grey (WCAG AA against the modal bg) and add a subtle
   border + background so the affordance reads as a tappable control
   without competing with the cinematic dark UI. The min-width/height
   guarantees a comfortable 36px touch target; the mobile media query
   below grows it further to the 44px Apple/Material recommendation. */
.ga-icon-btn {
  border: 1px solid rgba(255,255,255,0.14);
  background: rgba(255,255,255,0.04);
  color: rgba(224,224,230,0.88);
  padding: 6px;
  min-width: 36px;
  min-height: 36px;
  border-radius: 6px;
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: color 140ms ease, background 140ms ease, border-color 140ms ease;
}
.ga-icon-btn:hover,
.ga-icon-btn:focus-visible {
  color: #fff;
  background: rgba(255,255,255,0.10);
  border-color: rgba(255,255,255,0.28);
  outline: none;
}
.ga-icon-btn:active { background: rgba(255,255,255,0.16); }
@media (max-width: 820px) {
  .ga-icon-btn {
    min-width: 44px;
    min-height: 44px;
    padding: 8px;
  }
}

.ga-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px;
  font-size: 13px; font-weight: 500; font-family: var(--font-body);
  background: #181a27;
  color: #e0e0e6;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 6px;
  cursor: pointer;
  transition: all 180ms ease;
  white-space: nowrap;
}
.ga-btn:hover:not(:disabled) { border-color: rgba(255,255,255,0.18); background: #20222f; }
.ga-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.ga-btn-primary { background: #4f98a3; color: #fff; border-color: #4f98a3; }
.ga-btn-primary:hover:not(:disabled) { background: #5eadb9; border-color: #5eadb9; }
.ga-btn-secondary {
  background: transparent;
  border: 1px dashed rgba(255,255,255,0.18);
  color: rgba(255,255,255,0.75);
}

.ga-field { margin-bottom: 14px; display: flex; flex-direction: column; gap: 6px; }
.ga-field label {
  font-size: 11px; font-weight: 600;
  color: rgba(255,255,255,0.55);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.ga-input {
  width: 100%;
  padding: 8px 12px;
  background: #12141f;
  color: #e0e0e6;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 6px;
  font-size: 14px; font-family: var(--font-body);
  outline: none;
  transition: border-color 180ms ease;
}
.ga-input:focus { border-color: #4f98a3; }
select.ga-input {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b6d7b' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 32px;
}
select.ga-input option { background: #0d0f18; color: #e0e0e6; }

.ga-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@media (max-width: 480px) { .ga-row { grid-template-columns: 1fr; } }

/* Theme preset row in the in-globe Customize modal: keep the Apply
   button and the select on the SAME baseline. The select sits under a
   "Preset" label which would otherwise push it down a row from the
   button; we zero the wrap's margin-bottom and let it stretch so the
   button's bottom edge aligns with the select's bottom edge. Mirrors
   the Manage dropdown's chunky look and keeps a 44px minimum hit area
   on touch. */
/* Pull the in-globe preset row's baseline to the bottom so the labelled
   select and the bare Apply button share a bottom edge — same contract
   as the /admin surface above. */
.ga-row.ga-theme-preset-row { align-items: end; }
.ga-theme-preset-apply-wrap {
  /* .ga-field is flex-direction:column, so cross-axis is horizontal and
     main-axis is vertical. To pin the bare button to the bottom of the
     grid cell (so it shares a baseline with the select-with-label sibling)
     we need justify-content:flex-end on the column. The prior
     align-items:flex-end was a no-op for vertical alignment in a column —
     it only right-aligns children, which on a width:100% button is
     invisible. Net effect was the button floated to the top of the cell
     and left a visible gap under the dropdown.
     margin-bottom:0 strips the inherited 14px .ga-field bottom margin so
     the button's bottom edge actually lines up with the select's bottom
     edge instead of sitting 14px short of it. */
  justify-content: flex-end;
  margin-bottom: 0;
}
.ga-theme-preset-apply {
  width: 100%;
  min-height: 40px;
}
.ga-theme-preset-select {
  /* Match the Manage dropdown's chunkier sizing inside the modal context.
     .dark-select sets the dark surface + 10px radius + cyan focus ring;
     this nudges the vertical padding so it lines up with neighbouring
     .ga-input controls and stays comfortable on touch. */
  padding-top: 10px;
  padding-bottom: 10px;
  min-height: 40px;
}
@media (max-width: 480px) {
  .ga-theme-preset-apply { min-height: 44px; }
  .ga-theme-preset-select { min-height: 44px; }
  /* On narrow viewports the .ga-row collapses to single column. Drop
     the apply-wrap's bottom margin so the button sits snug under the
     select instead of leaving a gap. */
  .ga-theme-preset-apply-wrap { margin-bottom: 0; }
}

/* /admin Settings preset row — same layout contract as the in-globe
   Customize modal so the two surfaces feel identical:
   - dropdown + Apply on the same row at desktop widths
   - Apply button pinned to the bottom edge of the row so it shares a
     baseline with the labelled select
   - stack to single column on phones with no leftover margin
   The admin form uses .form-row + .form-group (different class names from
   the in-globe modal's .ga-row + .ga-field), so we duplicate the rules
   keyed on the admin selectors instead of relying on shared classes. */
#admin-view .settings-theme-preset-row {
  align-items: end;
}
#admin-view .settings-theme-preset-apply-wrap {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  margin-bottom: 0;
}
#admin-view .settings-theme-preset-apply-wrap .btn {
  width: 100%;
  min-height: 40px;
}
@media (max-width: 600px) {
  #admin-view .settings-theme-preset-apply-wrap .btn { min-height: 44px; }
  #admin-view .settings-theme-preset-apply-wrap { margin-bottom: 0; }
}

/* Theme preset preview — shared between in-globe Customize modal and
   /admin Settings. Lightweight SVG mini-globes (no Three.js) so a 6+
   preset gallery renders instantly without burning a mobile GPU.
   Hero shows the currently-selected preview at a larger size; tiles
   below let the user compare presets and pick before Apply. */
.ga-preset-preview-block {
  margin-top: 12px;
  padding: 12px;
  background: rgba(255,255,255,0.025);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 10px;
}
.ga-preset-preview-hero {
  /* Square globe preview — the upgraded presetPreviewSvg now mirrors the
     live globe at 1:1 aspect (dark disc + atmosphere halo + lat/long
     grid + continents + route arc + star field). 180px max keeps it from
     dominating the modal but still readable. */
  width: 100%;
  max-width: 180px;
  margin: 0 auto;
  border-radius: 8px;
  overflow: hidden;
  background: #000;
  aspect-ratio: 1 / 1;
  display: block;
}
.ga-preset-preview-hero svg.tg-preset-preview-svg {
  width: 100%;
  height: 100%;
  display: block;
}
.ga-preset-preview-caption {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  font-size: 12px;
  margin-top: 8px;
  margin-bottom: 8px;
  color: rgba(255,255,255,0.75);
}
.ga-preset-preview-caption strong { color: #fff; font-weight: 600; }
.ga-preset-preview-hint { flex-basis: 100%; font-size: 11px; }
.ga-preset-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 8px;
}
.ga-preset-tile {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  padding: 4px;
  background: rgba(255,255,255,0.02);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 6px;
  cursor: pointer;
  color: rgba(255,255,255,0.75);
  font: inherit;
  transition: border-color 0.15s ease, transform 0.1s ease;
}
.ga-preset-tile:hover { border-color: rgba(92,213,255,0.55); }
.ga-preset-tile:focus-visible {
  outline: 2px solid #5cd5ff;
  outline-offset: 2px;
}
.ga-preset-tile.is-active {
  border-color: #5cd5ff;
  box-shadow: 0 0 0 1px rgba(92,213,255,0.4);
}
.ga-preset-tile-thumb {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 4px;
  overflow: hidden;
  background: #000;
}
.ga-preset-tile-thumb svg.tg-preset-preview-svg {
  width: 100%;
  height: 100%;
  display: block;
}
.ga-preset-tile-label {
  font-size: 10.5px;
  line-height: 1.2;
  text-align: center;
  color: rgba(255,255,255,0.8);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
@media (max-width: 480px) {
  /* Mobile Customize: the preset preview is still available (same DOM as
     desktop), but tuned smaller so it doesn't crowd the modal. Hero
     shrinks to 132px (vs 180px desktop), gallery tiles drop to 68px so
     the row of thumbnails fits 3-4 per row on narrow phones, and the
     preview block has tighter padding. */
  .ga-preset-gallery { grid-template-columns: repeat(auto-fill, minmax(68px, 1fr)); gap: 6px; }
  .ga-preset-preview-hero { max-width: 132px; }
  .ga-preset-preview-block { padding: 10px; margin-top: 10px; }
  .ga-preset-preview-caption { font-size: 11.5px; margin-top: 6px; margin-bottom: 6px; }
  .ga-preset-tile-label { font-size: 9.5px; }
}

.ga-hint { font-size: 12px; color: rgba(255,255,255,0.55); margin-bottom: 12px; line-height: 1.5; }
.ga-muted { color: rgba(255,255,255,0.45); font-weight: 400; }
.ga-error { color: #ff6e6e; font-size: 12px; }

.ga-section {
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 8px;
  padding: 14px 14px 4px;
  margin-bottom: 12px;
  background: rgba(255,255,255,0.02);
}
.ga-section h3 {
  font-family: var(--font-display);
  font-size: 12px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.08em;
  margin-bottom: 10px;
  color: rgba(255,255,255,0.75);
}

.ga-advanced { margin-bottom: 12px; }
.ga-advanced summary {
  cursor: pointer; padding: 6px 0; font-size: 12px; color: rgba(255,255,255,0.55);
  list-style: none;
}
.ga-advanced summary::-webkit-details-marker { display: none; }
.ga-advanced summary::before { content: '▸'; margin-right: 6px; font-size: 10px; }
.ga-advanced[open] summary::before { content: '▾'; }

.ga-flight-legs,
.ga-trip-legs { display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px; }
.ga-leg-card {
  background: #12141f;
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 8px;
  padding: 12px;
}
.ga-leg-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.ga-leg-label {
  font-size: 11px; font-weight: 600; color: rgba(255,255,255,0.55);
  text-transform: uppercase; letter-spacing: 0.08em;
}
/* Trip multi-leg section header — sits between the destination block and the
   first leg card so the user understands the legs describe the route in. */
.ga-trip-legs-section { margin-bottom: 18px; }
.ga-trip-legs-header {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 8px;
}
.ga-trip-legs-title {
  font-size: 11px; font-weight: 600;
  color: rgba(255,255,255,0.55);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.ga-trip-legs-hint { flex: 1; min-width: 0; }
/* Header-row mini "+ Add leg" — always visible at the top of the Route
   section so the control is reachable on desktop even when the legs list
   pushes the bottom CTA below the fold. */
.ga-trip-add-leg-mini {
  flex: 0 0 auto;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 600;
  background: rgba(79, 152, 163, 0.18);
  border: 1px solid rgba(94, 173, 185, 0.6);
  color: #d6f1f6;
  border-radius: 6px;
  white-space: nowrap;
}
.ga-trip-add-leg-mini:hover:not(:disabled) {
  background: rgba(79, 152, 163, 0.32);
  border-color: #5eadb9;
  color: #fff;
}
/* "+ Add leg" — full-width, filled call-to-action so it stays unmistakable
   on the wider desktop Edit Trip modal where dashed/low-contrast borders
   blended with Associated Flights / Linked Trips lists underneath. */
.ga-trip-add-leg-btn {
  display: flex;
  width: 100%;
  justify-content: center;
  align-items: center;
  margin-top: 8px;
  padding: 11px 14px;
  background: #4f98a3;
  border: 1px solid #5eadb9;
  color: #ffffff;
  font-weight: 600;
  font-size: 14px;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(79, 152, 163, 0.25);
}
.ga-trip-add-leg-btn:hover:not(:disabled) {
  background: #5eadb9;
  border-color: #6fc0cc;
  color: #ffffff;
  box-shadow: 0 3px 12px rgba(94, 173, 185, 0.35);
}
.ga-trip-add-leg-btn span[aria-hidden="true"] {
  margin-right: 4px;
  font-size: 16px;
  line-height: 1;
}

.ga-color-pair { display: flex; gap: 8px; align-items: center; }
.ga-color-swatch {
  width: 44px; height: 36px; padding: 2px;
  background: #12141f; border: 1px solid rgba(255,255,255,0.1); border-radius: 6px; cursor: pointer;
  appearance: none; -webkit-appearance: none;
}
.ga-color-swatch::-webkit-color-swatch-wrapper { padding: 0; border: 0; }
.ga-color-swatch::-webkit-color-swatch { border: 0; border-radius: 4px; }
.ga-color-hex { flex: 1; font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 12px; text-transform: lowercase; }

.ga-autocomplete-wrap { position: relative; }
.ga-autocomplete-list {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 10;
  max-height: 220px; overflow-y: auto;
  background: #12141f;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 6px; margin-top: 2px;
  display: none;
  box-shadow: 0 12px 32px rgba(0,0,0,0.45);
}
.ga-autocomplete-list.open { display: block; }
.ga-ac-item { padding: 6px 12px; cursor: pointer; font-size: 13px; }
.ga-ac-item:hover, .ga-ac-item.highlighted { background: rgba(79, 152, 163, 0.18); }
.ga-ac-code { font-weight: 600; color: #fff; }
.ga-ac-detail { color: rgba(255,255,255,0.55); margin-left: 6px; }

/* Trip ↔ Flight association pickers used in #ga-trip-modal and
   #ga-flight-modal. List of existing records with checkboxes, plus a row
   of chips showing the current selection. */
.ga-tfl {
  margin-top: 6px;
  max-height: 180px;
  overflow-y: auto;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 6px;
  background: #12141f;
}
.ga-tfl .ga-tfl-empty {
  padding: 10px 12px;
  color: rgba(255,255,255,0.55);
  font-size: 13px;
}
.ga-tfl .ga-tfl-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  cursor: pointer;
  font-size: 13px;
  color: #fff;
}
.ga-tfl .ga-tfl-row:last-child { border-bottom: none; }
.ga-tfl .ga-tfl-row:hover { background: rgba(79, 152, 163, 0.10); }
.ga-tfl .ga-tfl-row input[type="checkbox"] { flex: 0 0 auto; accent-color: #5eadb9; }
.ga-tfl .ga-tfl-route {
  flex: 1 1 auto;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  letter-spacing: 0.02em;
}
.ga-tfl .ga-tfl-meta {
  color: rgba(255,255,255,0.55);
  font-size: 12px;
  white-space: nowrap;
}
.ga-tfs {
  margin-top: 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.ga-tfs .ga-tfs-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  background: rgba(79, 152, 163, 0.18);
  color: #b9e7ef;
  border-radius: 999px;
  font-size: 12px;
  line-height: 1.4;
}
.ga-tfs .ga-tfs-chip button {
  background: none;
  border: none;
  color: inherit;
  cursor: pointer;
  padding: 0;
  font-size: 14px;
  line-height: 1;
  opacity: 0.7;
}
.ga-tfs .ga-tfs-chip button:hover { opacity: 1; }
@media (max-width: 600px) {
  .ga-tfl .ga-tfl-row { flex-wrap: wrap; }
  .ga-tfl .ga-tfl-row .ga-tfl-meta { width: 100%; padding-left: 26px; }
}

.ga-geo-status { font-size: 12px; padding: 6px 10px; border-radius: 6px; margin: 4px 0 8px; min-height: 20px; }
.ga-geo-status.searching { color: #5eadb9; background: rgba(79,152,163,0.08); border: 1px solid rgba(79,152,163,0.2); }
.ga-geo-status.success   { color: #69f0ae; background: rgba(105,240,174,0.08); border: 1px solid rgba(105,240,174,0.2); }
.ga-geo-status.error     { color: #ff6e6e; background: rgba(255,110,110,0.08); border: 1px solid rgba(255,110,110,0.2); }

.ga-import-preview-box {
  padding: 10px 12px;
  background: #12141f;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 6px;
  font-size: 13px;
  line-height: 1.5;
}

.ga-toasts {
  position: fixed; bottom: 24px; right: 24px; z-index: 3000;
  display: flex; flex-direction: column; gap: 8px;
  pointer-events: none;
}
.ga-toast {
  pointer-events: auto;
  background: #12141f;
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: 8px;
  padding: 10px 16px;
  font-size: 13px;
  color: #e0e0e6;
  box-shadow: 0 12px 32px rgba(0,0,0,0.45);
  display: flex; align-items: center; gap: 10px;
  max-width: 380px;
  transition: opacity 250ms ease, transform 250ms ease;
}
.ga-toast-dot { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,0.4); flex-shrink: 0; }
.ga-toast.success { border-color: rgba(105,240,174,0.3); }
.ga-toast.success .ga-toast-dot { background: #69f0ae; }
.ga-toast.error   { border-color: rgba(255,64,129,0.4); }
.ga-toast.error   .ga-toast-dot { background: #ff4081; }
.ga-toast.info    { border-color: rgba(94,173,185,0.3); }
.ga-toast.info    .ga-toast-dot { background: #5eadb9; }

/* Disabled state for in-rail admin menu items handled via .dark-select-option.disabled
   which already exists. Add a stronger visual cue when fully disabled. */
.dark-select-option.disabled { color: rgba(255,255,255,0.4); }

/* === Help & FAQ modal === */
/* Tab strip inside the Help modal: switches between FAQ, Features, Privacy.
   Visual style matches the modal's dark/cyan palette and stays compact so
   it doesn't dominate the modal header. */
.ga-help-tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 14px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  flex-wrap: wrap;
}
.ga-help-tab {
  background: transparent;
  border: none;
  color: rgba(255,255,255,0.6);
  padding: 8px 12px;
  font-size: 13px;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 180ms ease, border-color 180ms ease;
}
.ga-help-tab:hover { color: rgba(255,255,255,0.85); }
.ga-help-tab.is-active {
  color: #67e8f9;
  border-bottom-color: #67e8f9;
}
.ga-help-panel { display: none; }
.ga-help-panel.is-active { display: block; }

/* Features & Release Notes panel */
.ga-features {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.ga-features-group h3 {
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 600;
  color: #e0e0e6;
  letter-spacing: 0.02em;
}
.ga-features-group ul {
  margin: 0;
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ga-features-group li {
  font-size: 13px;
  line-height: 1.5;
  color: rgba(255,255,255,0.78);
}
.ga-features-group code,
.ga-privacy code {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 12px;
  background: rgba(255,255,255,0.06);
  padding: 1px 5px;
  border-radius: 4px;
  color: rgba(255,255,255,0.92);
}

/* Privacy Policy panel */
.ga-privacy {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.ga-privacy section h3 {
  margin: 0 0 4px;
  font-size: 13px;
  font-weight: 600;
  color: #e0e0e6;
}
.ga-privacy section p {
  margin: 0;
  font-size: 13px;
  line-height: 1.55;
  color: rgba(255,255,255,0.78);
}
.ga-privacy-meta {
  font-size: 12px;
  color: rgba(255,255,255,0.5);
  font-style: italic;
  margin-top: 4px;
}

.ga-faq {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.ga-faq-item {
  background: #12141f;
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 8px;
  padding: 0 12px;
  transition: border-color 180ms ease, background 180ms ease;
}
.ga-faq-item[open] {
  border-color: rgba(103, 232, 249, 0.18);
  background: rgba(10, 20, 30, 0.7);
}
.ga-faq-item summary {
  cursor: pointer;
  list-style: none;
  padding: 10px 0;
  font-size: 13px;
  font-weight: 600;
  color: #e0e0e6;
  display: flex;
  align-items: center;
  gap: 8px;
}
.ga-faq-item summary::-webkit-details-marker { display: none; }
.ga-faq-item summary::before {
  content: '+';
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: rgba(103, 232, 249, 0.12);
  color: rgba(103, 232, 249, 0.9);
  font-size: 14px;
  font-weight: 700;
  flex-shrink: 0;
  transition: background 180ms ease, transform 180ms ease;
}
.ga-faq-item[open] summary::before {
  content: '−';
  background: rgba(103, 232, 249, 0.22);
}
.ga-faq-item p {
  margin: 0 0 12px;
  font-size: 13px;
  line-height: 1.55;
  color: rgba(255,255,255,0.78);
}
.ga-faq-item code {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 12px;
  background: rgba(255,255,255,0.06);
  padding: 1px 5px;
  border-radius: 4px;
  color: rgba(255,255,255,0.92);
}

/* ============================================================
   Sign-in modal (branded prompt shown before redirecting to
   Google OAuth). Matches the globe's dark/cyan palette so a user
   never feels yanked out of the app.
   ============================================================ */
.ga-modal.ga-signin { width: 420px; }
.ga-signin-illustration {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px 0 10px;
}
.ga-signin-logo {
  width: 84px;
  height: 84px;
  display: block;
  border-radius: 18px;
  object-fit: contain;
  box-shadow:
    0 0 0 1px rgba(103,232,249,0.18),
    0 0 24px rgba(103,232,249,0.22);
}
.ga-signin-lead {
  font-size: 14px;
  line-height: 1.5;
  color: rgba(255,255,255,0.86);
  margin: 4px 0 12px;
}
.ga-signin-bullets {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.ga-signin-bullets li {
  position: relative;
  padding-left: 22px;
  font-size: 13px;
  line-height: 1.45;
  color: rgba(255,255,255,0.78);
}
.ga-signin-bullets li::before {
  content: "✓";
  position: absolute;
  left: 0;
  top: 0;
  color: #67e8f9;
  font-weight: 700;
}
.ga-signin-note {
  margin: 4px 0 0;
  font-size: 11.5px;
  line-height: 1.45;
  color: rgba(255,255,255,0.55);
  font-style: italic;
}
.ga-btn-google {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: #fff;
  color: #1f2330;
  border-color: #fff;
}
.ga-btn-google:hover:not(:disabled) {
  background: #f1f3f4;
  border-color: #f1f3f4;
}
.ga-btn-google[data-pending="1"]::after {
  content: "";
  display: inline-block;
  width: 12px;
  height: 12px;
  border: 2px solid rgba(0,0,0,0.18);
  border-top-color: #4285F4;
  border-radius: 50%;
  margin-left: 6px;
  animation: ga-spin 0.7s linear infinite;
}
.ga-google-glyph { flex-shrink: 0; }
@keyframes ga-spin { to { transform: rotate(360deg); } }

/* ============================================================
   Right column polish — Fun Facts cards, sponsored ad card,
   and equal-size icons in collapsed mode (desktop + mobile tray).
   ============================================================ */

/* Fun Facts headline row + structured fact cards. The default
   <div class="travel-insight-line"> markup still renders fine
   (legacy fallback); the new card markup is preferred. */
.travel-insights,
.search-facts,
.ga-funfacts-body {
  pointer-events: auto;
}
.travel-insights .ti-header,
.search-facts .ti-header,
.ga-funfacts-body .ti-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}
.travel-insights .ti-title,
.search-facts .ti-title,
.ga-funfacts-body .ti-title {
  color: #67e8f9;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.travel-insights .ti-subtitle,
.search-facts .ti-subtitle,
.ga-funfacts-body .ti-subtitle {
  color: rgba(255,255,255,0.5);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: none;
}
.travel-insights .ti-cards,
.search-facts .ti-cards,
.ga-funfacts-body .ti-cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
}
.travel-insights .ti-card,
.search-facts .ti-card,
.ga-funfacts-body .ti-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px;
  border: 1px solid rgba(255,255,255,0.07);
  background: rgba(255,255,255,0.03);
  border-radius: 8px;
  min-width: 0;
}
.travel-insights .ti-card.span2,
.search-facts .ti-card.span2,
.ga-funfacts-body .ti-card.span2 { grid-column: 1 / -1; }
/* Interactive insight cards — focus/filter/open the related view on click. */
.search-facts .ti-card-action,
.ga-funfacts-body .ti-card-action {
  font: inherit;
  text-align: left;
  align-items: flex-start;
  cursor: pointer;
  width: 100%;
  transition: background 150ms ease, border-color 150ms ease;
}
.search-facts .ti-card-action::after,
.ga-funfacts-body .ti-card-action::after {
  content: '›';
  position: absolute;
  top: 6px;
  right: 9px;
  color: rgba(255,255,255,0.35);
  font-size: 13px;
  line-height: 1;
}
.search-facts .ti-card-action,
.ga-funfacts-body .ti-card-action { position: relative; }
.search-facts .ti-card-action:hover,
.search-facts .ti-card-action:focus-visible,
.ga-funfacts-body .ti-card-action:hover,
.ga-funfacts-body .ti-card-action:focus-visible {
  background: rgba(103, 232, 249, 0.12);
  border-color: rgba(103, 232, 249, 0.45);
  outline: none;
}
.travel-insights .ti-card-label,
.search-facts .ti-card-label,
.ga-funfacts-body .ti-card-label {
  font-size: 9.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.5);
  font-weight: 600;
}
.travel-insights .ti-card-value,
.search-facts .ti-card-value,
.ga-funfacts-body .ti-card-value {
  font-size: 13px;
  font-weight: 600;
  color: #fff;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.travel-insights .ti-card-detail,
.search-facts .ti-card-detail,
.ga-funfacts-body .ti-card-detail {
  font-size: 10.5px;
  color: rgba(255,255,255,0.55);
  line-height: 1.3;
}
.travel-insights .ti-card-value .hl,
.search-facts .ti-card-value .hl,
.ga-funfacts-body .ti-card-value .hl {
  color: #67e8f9;
}
.travel-insights .ti-empty,
.search-facts .ti-empty,
.ga-funfacts-body .ti-empty {
  font-size: 12px;
  color: rgba(255,255,255,0.6);
  padding: 4px 2px 0;
  line-height: 1.45;
}

/* Fun Facts modal: each section reuses the same card grid as desktop. */
.ga-funfacts-body .ga-funfacts-section + .ga-funfacts-section {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

/* Sponsored ad card — make the right-rail ad look intentional,
   labelled, and clearly separate from the actionable controls
   above/below it. */
.ad-shell.right-ad-shell {
  border: 1px solid rgba(255,255,255,0.08);
  background: linear-gradient(180deg, rgba(15,18,28,0.78), rgba(10,14,24,0.42));
  padding: 10px 10px 8px;
}
.ad-shell.right-ad-shell::before {
  content: "Sponsored";
  display: block;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.45);
  margin-bottom: 6px;
}
.ad-shell.right-ad-shell .ad-placeholder {
  position: relative;
  inset: auto;
  min-height: 80px;
}

/* Collapsed rail: enforce a uniform 40×40 tile and a single
   icon glyph size across every action so two icons can't render
   larger than the rest. The previous rule only set font-size for
   the ::before glyph; emoji presentation forms (🗺️ ✈️) can pick up
   the variation selector and render at a different metric than
   the rest. Pin the cap-height with line-height + max-width + a
   text-rendering nudge so all glyphs look square. */
.layout-right.collapsed {
  background: rgba(10,14,24,0.55);
  border: 1px solid rgba(255,255,255,0.08);
}
.layout-right.collapsed .rail-section {
  width: 40px !important;
  height: 40px !important;
  flex: 0 0 40px;
}
.layout-right.collapsed .rail-section::before {
  font-size: 18px !important;
  line-height: 1 !important;
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Force text-style emoji presentation where supported so the
     variation-selector emojis don't render at a larger metric
     than the rest. */
  font-variant-emoji: text;
  text-rendering: geometricPrecision;
}

/* Mobile tray (collapsed) — style the row of icons as a single
   rounded card with the collapse arrow visually separated, per
   the iPhone-style reference. Outer padding, gaps and button
   chrome are kept tight to maximise the globe area above. */
@media (max-width: 820px) {
  .layout-right {
    border-radius: 12px;
  }
  .layout-right.collapsed {
    gap: 6px;
    padding: 6px;
    align-items: center;
    background: rgba(13,16,28,0.92);
    border: 1px solid rgba(255,255,255,0.08);
    box-shadow: 0 6px 20px rgba(0,0,0,0.35);
  }
  .layout-right.collapsed .rail-collapse-btn {
    flex: 0 0 auto;
    margin-left: auto;
    height: 40px;
    min-width: 40px;
    border-radius: 10px;
    padding: 0 8px;
    background: rgba(255,255,255,0.05);
    border-color: rgba(255,255,255,0.12);
    order: 999;
    margin-bottom: 0;
  }
  .layout-right.collapsed .rail-section {
    width: 40px !important;
    height: 40px !important;
    flex: 0 0 40px;
    border-radius: 10px;
    background: rgba(255,255,255,0.04);
    border-color: rgba(255,255,255,0.1);
  }
  .layout-right.collapsed .rail-section::before {
    font-size: 18px !important;
  }
  /* Trim the inline ad shell on mobile so it doesn't leave empty space
     when the slot is short. The shell still renders, but the visible
     blank area shrinks. */
  .layout-right.collapsed #right-ad-shell.rail-section {
    margin-top: 2px;
    padding: 4px;
    min-height: 80px;
  }
  .ad-shell {
    margin-top: 4px;
    min-height: 80px;
    max-height: 120px;
  }
}

/* ============================================================
   Mobile bottom action bar
   ------------------------------------------------------------
   Compact icon-only nav fixed to the bottom of the viewport on
   phones. Replaces the right rail on mobile so users don't have
   to expand/collapse it to reach common actions. Hidden on
   desktop and in embed mode; right rail is hidden on mobile.
   ============================================================ */
.mobile-action-bar { display: none; }

@media (max-width: 820px) {
  /* Hide the desktop right rail on mobile — actions move to the inline bar.
     Keep the element in the DOM (display:none) so the AdSense loader's
     offscreen check naturally skips the embedded ad slot. */
  .layout-right { display: none !important; }
  /* Restore a clean single-column layout for the globe column. */
  .layout {
    grid-template-columns: 1fr !important;
    grid-template-rows: auto auto;
  }
  /* No fixed-bottom action bar anymore — it sits inline in the column. */
  body { padding-bottom: 0; }

  /* Inline (non-fixed) mobile action bar — placed by JS inside the main
     column directly below the search box, above the credit row. */
  .mobile-action-bar {
    display: flex;
    justify-content: space-around;
    align-items: stretch;
    gap: 6px;
    position: static;
    left: auto;
    right: auto;
    bottom: auto;
    z-index: auto;
    margin: 6px 8px 4px;
    padding: 8px;
    background: rgba(13, 16, 28, 0.78);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 14px;
    box-shadow: 0 4px 14px rgba(0,0,0,0.25);
  }
  .mab-btn {
    position: relative;
    flex: 1 1 0;
    min-width: 0;
    max-width: 80px;
    height: 54px;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    color: rgba(255, 255, 255, 0.85);
    cursor: pointer;
    transition: background 120ms ease, transform 120ms ease;
    padding: 4px 2px;
  }
  .mab-btn:hover,
  .mab-btn:focus-visible { background: rgba(255, 255, 255, 0.08); outline: none; }
  .mab-btn:active { transform: scale(0.96); }
  .mab-btn svg { flex: 0 0 auto; }
  .mab-btn .mab-label {
    font-family: var(--font-body);
    font-size: 10px;
    line-height: 1.1;
    font-weight: 500;
    letter-spacing: 0.01em;
    color: rgba(255, 255, 255, 0.75);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
  }
  .mab-btn .mab-lock {
    display: none;
    position: absolute;
    right: 4px;
    top: 2px;
    font-size: 10px;
    line-height: 1;
    color: rgba(255, 255, 255, 0.6);
  }
  .mab-btn.is-locked {
    color: rgba(255, 255, 255, 0.45);
    background: rgba(255, 255, 255, 0.02);
  }
  .mab-btn.is-locked .mab-label { color: rgba(255, 255, 255, 0.45); }
  .mab-btn.is-locked .mab-lock { display: block; }

  /* Auth icon + label: show the right one for the current state. */
  #mab-auth .mab-icon-signout,
  #mab-auth .mab-label-signout { display: none; }
  .mobile-action-bar[data-auth="user"] #mab-auth .mab-icon-signin,
  .mobile-action-bar[data-auth="user"] #mab-auth .mab-label-signin { display: none; }
  .mobile-action-bar[data-auth="user"] #mab-auth .mab-icon-signout { display: inline-block; }
  .mobile-action-bar[data-auth="user"] #mab-auth .mab-label-signout { display: inline-block; }

  /* Subtle colored accents on each action icon. SVGs use stroke:currentColor,
     so setting `color` tints the icon while the label stays neutral white.
     A faint matching drop-shadow gives the icon a premium glow without
     turning the bar garish. Locked buttons override these to grey. */
  #mab-auth        { --mab-accent: #a78bfa; }   /* violet — auth */
  #mab-add-flight  { --mab-accent: #60a5fa; }   /* sky blue — flight */
  #mab-add-trip    { --mab-accent: #fb923c; }   /* warm orange — trip pin */
  #mab-manage      { --mab-accent: #f472b6; }   /* pink — modify */
  #mab-more        { --mab-accent: #d4d4d8; }   /* neutral — overflow */

  .mab-btn[id^="mab-"] svg {
    color: var(--mab-accent, rgba(255,255,255,0.85));
    filter: drop-shadow(0 0 4px color-mix(in srgb, var(--mab-accent, #ffffff) 40%, transparent));
    transition: color 150ms ease, filter 150ms ease;
  }
  .mab-btn[id^="mab-"]:hover svg,
  .mab-btn[id^="mab-"]:focus-visible svg {
    filter: drop-shadow(0 0 6px color-mix(in srgb, var(--mab-accent, #ffffff) 60%, transparent));
  }
  /* Locked buttons must not look like an active accent. */
  .mab-btn.is-locked svg {
    color: rgba(255, 255, 255, 0.4) !important;
    filter: none !important;
  }

  /* Inline mobile bar already has comfortable inner padding above. */
}

body.embed-mode .mobile-action-bar { display: none !important; }

/* ============================================================
   Mobile top-right floating controls
   ------------------------------------------------------------
   Holds the Map / Satellite toggle (previously a tile in the
   bottom action bar). Placed where the old dark/light theme
   selector used to sit so the corner stays familiar. Hidden on
   desktop (right rail has the same control). Hidden in embed
   mode (controls are deliberately stripped). Hidden during the
   auth-loading flash window (see auth-loading rules below).
   ============================================================ */
.mobile-top-right-controls { display: none; }
body.embed-mode .mobile-top-right-controls { display: none !important; }

@media (max-width: 820px) {
  .mobile-top-right-controls {
    display: flex;
    gap: 6px;
    position: fixed;
    top: max(8px, env(safe-area-inset-top, 0px));
    right: max(8px, env(safe-area-inset-right, 0px));
    z-index: 65;
    pointer-events: auto;
  }
  .mtr-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 6px 10px;
    background: rgba(13, 16, 28, 0.72);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 999px;
    color: rgba(255, 255, 255, 0.88);
    font-family: var(--font-body);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.01em;
    cursor: pointer;
    transition: background 120ms ease, transform 120ms ease, color 120ms ease;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
  }
  .mtr-btn svg { color: #6ee7ff; flex: 0 0 auto; }
  .mtr-btn:hover,
  .mtr-btn:focus-visible {
    background: rgba(255, 255, 255, 0.08);
    outline: none;
    color: #fff;
  }
  .mtr-btn:active { transform: scale(0.97); }
  .mtr-label {
    line-height: 1;
    white-space: nowrap;
  }
}

/* ============================================================
   Mobile "More" sheet
   ------------------------------------------------------------
   List-style picker for the secondary actions hidden behind the
   More button in the mobile action bar. Visually consistent with
   the existing ga-modal but with vertical list rows tuned for
   thumb tapping.
   ============================================================ */
.ga-sheet-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 0;
  margin: 0;
  list-style: none;
}
.ga-sheet-item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 12px;
  color: rgba(255, 255, 255, 0.92);
  cursor: pointer;
  text-align: left;
  font-family: var(--font-body);
  transition: background 120ms ease, transform 120ms ease;
}
.ga-sheet-item:hover,
.ga-sheet-item:focus-visible {
  background: rgba(255, 255, 255, 0.08);
  outline: none;
}
.ga-sheet-item:active { transform: scale(0.99); }
.ga-sheet-item .ga-sheet-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: rgba(110, 231, 255, 0.10);
  color: #6ee7ff;
  flex: 0 0 auto;
}
.ga-sheet-item .ga-sheet-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.ga-sheet-item .ga-sheet-title {
  font-size: 14px;
  font-weight: 600;
  color: #fff;
}
.ga-sheet-item .ga-sheet-sub {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.6);
}
.ga-sheet-item .ga-sheet-lock {
  margin-left: auto;
  display: none;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.55);
}
.ga-sheet-item.is-locked {
  opacity: 0.55;
  cursor: not-allowed;
}
.ga-sheet-item.is-locked .ga-sheet-lock { display: inline; }
.ga-modal-sheet { max-width: 460px; }
/* Desktop-version hint inside the mobile More sheet. Sits below the action
   list, visually quieter than a sheet item so it reads as guidance rather
   than another tappable row. Only the More sheet renders this element so
   no media query is needed — the desktop UI never opens this modal. */
.ga-more-tip {
  margin: 14px 4px 0;
  padding: 10px 12px;
  border-radius: 10px;
  background: rgba(110, 231, 255, 0.06);
  border: 1px solid rgba(110, 231, 255, 0.14);
  color: rgba(255, 255, 255, 0.72);
  font-size: 12px;
  line-height: 1.5;
}

/* ============================================================
   Modify (manage) modal — search list with tabs
   ============================================================ */
.ga-modal-wide { max-width: 720px; width: 100%; }
.ga-manage-body { display: flex; flex-direction: column; gap: 12px; }
.ga-manage-tabs {
  display: inline-flex;
  gap: 4px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 12px;
  padding: 4px;
  align-self: flex-start;
}
.ga-manage-tab {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 6px 14px;
  border-radius: 10px;
  color: rgba(255, 255, 255, 0.72);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  letter-spacing: 0.01em;
  transition: background 120ms ease, color 120ms ease;
}
.ga-manage-tab:hover { color: #fff; }
.ga-manage-tab.is-active {
  background: rgba(110, 231, 255, 0.16);
  color: #6ee7ff;
}
.ga-manage-search-field { margin: 0; }
.ga-manage-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 60vh;
  overflow-y: auto;
  padding-right: 2px;
}
.ga-manage-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 10px;
}
.ga-manage-row-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}
.ga-manage-row-title {
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ga-manage-row-sub {
  color: rgba(255, 255, 255, 0.6);
  font-size: 12px;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ga-manage-row-actions {
  display: flex;
  gap: 6px;
  flex: 0 0 auto;
}
.ga-manage-action {
  appearance: none;
  border: 1px solid rgba(255, 255, 255, 0.10);
  background: rgba(255, 255, 255, 0.05);
  color: #fff;
  padding: 6px 10px;
  border-radius: 8px;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.ga-manage-action:hover { background: rgba(255, 255, 255, 0.10); }
.ga-manage-action.is-danger { color: #fda4af; }
.ga-manage-action.is-danger:hover { background: rgba(239, 68, 68, 0.15); border-color: rgba(239, 68, 68, 0.35); color: #fff; }

@media (max-width: 520px) {
  .ga-manage-row { flex-direction: column; align-items: stretch; }
  .ga-manage-row-actions { justify-content: flex-end; }
}

/* Fun Facts modal — section spacing for the live insight cards. */
.ga-funfacts-section + .ga-funfacts-section {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

/* ============================================================
   Rail-expanded layout overrides
   ------------------------------------------------------------
   The right rail no longer collapses. The collapse handle is
   kept in the DOM (so existing JS that queries it doesn't throw)
   but it is visually hidden everywhere.
   ============================================================ */
.rail-collapse-btn { display: none !important; }

/* ============================================================
   Mobile: keep the right rail hidden but surface the ad slot
   below the credit and reduce the globe height to make room.
   The ad shell is moved out of .layout-right and into the main
   column by JS on mobile (see public/app.js — wireMobileAdShell).
   ============================================================ */
@media (max-width: 820px) {
  /* Mobile: stats row + globe row + inline children flow below.
     Globe is sized to about half the viewport so search, action bar,
     credit and ad shell remain visible without scrolling on a typical
     phone. */
  .layout-main {
    grid-template-rows: auto min(48vh, 48dvh) !important;
  }
  .main-globe {
    max-height: min(48vh, 48dvh) !important;
  }
  /* On mobile, the floating bottom-dock is taken out of the globe overlay
     (positioned absolute by default) and rendered inline below the globe.
     The teleport script in index.html moves it to be a sibling of
     .main-globe, so target it from .layout-main directly. */
  .layout-main > .globe-bottom-dock,
  .main-globe .globe-bottom-dock {
    position: static;
    inset: auto;
    width: auto;
    max-width: none;
    max-height: none;
    margin: 6px 8px 0;
    padding: 0;
    pointer-events: auto;
  }
  /* Search dock sits inline directly below the globe. */
  .globe-bottom-dock { gap: 6px; }
  .globe-search-dock {
    width: auto;
  }
  /* On mobile, suggestions drop down below the input (inline layout). */
  .layout-main > .globe-bottom-dock .search-suggestions {
    bottom: auto;
    top: calc(100% + 4px);
  }
  /* Hide overlay-only search insights on mobile to keep the layout compact;
     Fun Facts modal already surfaces the same content. */
  .globe-search-facts { display: none !important; }
  /* The ad shell, when teleported into the main column, sits below the
     credit. Constrain it visually so it doesn't dominate the page. */
  .layout-main #right-ad-shell.mobile-ad-shell {
    display: block;
    width: 100%;
    margin: 8px 0 4px;
    padding: 8px;
    min-height: 100px;
    /* Explicitly unset the base mobile .ad-shell caps (max-height 120px +
       overflow hidden) so the affiliate companion card (rendered ABOVE
       the AdSense ins) is not clipped on phones — the whole sponsored
       block was previously cut off at 120px and unreachable by scroll. */
    max-height: none !important;
    overflow: visible !important;
    border-radius: 12px;
    background: rgba(13,16,28,0.92);
    border: 1px solid rgba(255,255,255,0.08);
  }
  .layout-main #right-ad-shell.mobile-ad-shell::before { content: none; }
  /* Compact affiliate card variant when it lives in the mobile ad shell —
     keep it tight so the AdSense <ins> below still gets the bulk of the
     space and the whole block stays under ~220 px tall. */
  .layout-main #right-ad-shell.mobile-ad-shell #right-ad-affiliate-slot .affiliate-card {
    margin: 0 0 6px;
  }
}
@media (max-width: 520px) {
  .layout-main {
    grid-template-rows: auto min(54vh, 54dvh) !important;
  }
  .main-globe {
    max-height: min(54vh, 54dvh) !important;
  }
}

/* ============================================================
   Mobile hint banner
   ------------------------------------------------------------
   Subtle one-line tip shown above the mobile action bar. Used
   for the signed-out sign-in message and the signed-in empty-
   data nudge. Desktop hides it entirely.
   ============================================================ */
.mobile-hint { display: none; }

@media (max-width: 820px) {
  .mobile-hint {
    display: none;
    position: static;
    left: auto;
    right: auto;
    bottom: auto;
    margin: 4px 6px 2px;
    z-index: auto;
    padding: 8px 28px 8px 12px;
    background: rgba(13, 16, 28, 0.92);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 10px;
    color: rgba(255, 255, 255, 0.85);
    font-family: var(--font-body);
    font-size: 12px;
    line-height: 1.3;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
  }
  .mobile-hint:not([hidden]) { display: block; }
  .mobile-hint-text { color: rgba(255, 255, 255, 0.85); }
  .mobile-hint-close {
    position: absolute;
    top: 4px;
    right: 6px;
    background: transparent;
    border: 0;
    color: rgba(255, 255, 255, 0.7);
    font-size: 16px;
    line-height: 1;
    padding: 2px 6px;
    cursor: pointer;
  }
  .mobile-hint-close:hover,
  .mobile-hint-close:focus-visible { color: #ffffff; outline: none; }
}
body.embed-mode .mobile-hint { display: none !important; }

/* ============================================================
   Auth-loading state
   ------------------------------------------------------------
   While a signed-in visitor on / is being redirected to their
   own /u/:slug, hide stale title/stats/credit so the user does
   not see the demo flash before the redirect lands.
   ============================================================ */
body.auth-loading .globe-title-block,
body.auth-loading .stats-panel,
body.auth-loading .stat,
body.auth-loading .stat-divider,
body.auth-loading .app-credit,
body.auth-loading #globe-search-dock,
body.auth-loading #search-facts,
body.auth-loading #mobile-action-bar,
body.auth-loading #mobile-hint,
body.auth-loading #mobile-top-right-controls,
html.auth-loading .globe-title-block,
html.auth-loading .stats-panel,
html.auth-loading #globe-search-dock,
html.auth-loading #mobile-action-bar,
html.auth-loading #mobile-hint,
html.auth-loading #mobile-top-right-controls {
  visibility: hidden !important;
}
/* Cover the 3D globe canvas itself while auth is resolving — otherwise the
   demo/previous-slug arcs and country labels paint behind the "Loading…"
   overlay and the user still sees a flash of foreign data. */
body.auth-loading #globe-canvas,
body.auth-loading .country-labels,
html.auth-loading #globe-canvas,
html.auth-loading .country-labels {
  visibility: hidden !important;
}
body.auth-loading .main-globe::after,
html.auth-loading .main-globe::after {
  content: 'Loading your travel map…';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 255, 255, 0.55);
  font-family: var(--font-body);
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  pointer-events: none;
  z-index: 40;
  background: radial-gradient(circle at center,
    rgba(15, 18, 32, 0.85) 0%,
    rgba(8, 10, 18, 0.95) 70%);
}

/* ============================================================
   Mobile: prevent iOS Safari focus-zoom on text inputs.
   Safari zooms the viewport when a tapped input's computed
   font-size is < 16px. Bump every typing surface to 16px on
   small screens — visual design is unchanged at this scale.
   ============================================================ */
@media (max-width: 820px) {
  .search-input,
  .ga-input,
  input[type="text"],
  input[type="search"],
  input[type="email"],
  input[type="password"],
  input[type="number"],
  input[type="tel"],
  input[type="url"],
  input[type="date"],
  input[type="time"],
  textarea,
  select {
    font-size: 16px !important;
  }
}

/* ============================================================
   Mobile: shorten the globe section and bring search + actions
   closer to the globe so the credit + ad shell come into view
   without scrolling. The reduced FIT_RADIUS in app.js renders
   the globe noticeably larger inside this tighter row, so the
   user gets a bigger globe and less empty background at once.
   Order preserved: stats → globe → search → action row →
   credit → ad shell.
   ============================================================ */
@media (max-width: 820px) {
  .layout { padding: 4px !important; gap: 4px !important; }
  .layout-main {
    /* Tightened from 78dvh → 60dvh: the globe now fills its row instead
       of floating in a tall starfield, and the action row + ad shell are
       visible without scrolling on a typical phone. */
    grid-template-rows: auto min(60vh, 60dvh) !important;
  }
  .main-globe {
    max-height: min(60vh, 60dvh) !important;
    padding: 0 !important;
  }
  #globe-container { inset: 0 !important; }
  #globe-container::after { display: none; }

  /* Pull the search dock right up against the globe (was 6px top, 8px sides). */
  .layout-main > .globe-bottom-dock,
  .main-globe .globe-bottom-dock {
    margin: 2px 6px 0 !important;
  }
  /* Action bar sits flush below the search; trim its outer margin and the
     box padding so it doesn't add a tall band of background. */
  .mobile-action-bar {
    margin: 4px 6px 2px !important;
    padding: 6px !important;
  }
  /* Credit row used 6px 16px 10px — strip top/bottom to a hair. */
  .app-credit { padding: 2px 16px 4px !important; }
  /* Ad shell: shorter so it comes into view above the fold on phones. The
     max-height cap is removed because the affiliate companion now lives
     ABOVE the AdSense <ins> in the same shell and needs vertical room. */
  .layout-main #right-ad-shell.mobile-ad-shell {
    margin: 4px 0 4px !important;
    min-height: 70px !important;
    padding: 6px !important;
  }
}
@media (max-width: 520px) {
  .layout-main {
    grid-template-rows: auto min(58vh, 58dvh) !important;
  }
  .main-globe {
    max-height: min(58vh, 58dvh) !important;
  }
}

/* ============================================================
   Affiliate cards (GetYourGuide "Tours & activities")
   ============================================================
   Rendered by public/affiliate.js into the Search Insights panel
   (compact variant) and the mobile "More" sheet (standard variant).
   Hidden automatically when affiliate-offers.json is disabled or
   has no partnerId — see public/content/AFFILIATE_README.md. */
.affiliate-card {
  margin-top: 8px;
  border-radius: 10px;
  background: linear-gradient(135deg, rgba(79, 152, 163, 0.25), rgba(255, 200, 70, 0.08));
  border: 1px solid rgba(79, 152, 163, 0.35);
  overflow: hidden;
}
.affiliate-card-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  color: inherit;
  text-decoration: none;
  transition: background 0.15s ease;
}
.affiliate-card-link:hover,
.affiliate-card-link:focus-visible {
  background: rgba(79, 152, 163, 0.25);
  text-decoration: none;
  outline: none;
}
.affiliate-card-icon {
  font-size: 22px;
  line-height: 1;
  flex: 0 0 auto;
}
.affiliate-card-body {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1 1 auto;
}
.affiliate-card-title {
  font-weight: 600;
  font-size: 14px;
}
.affiliate-card-sub {
  font-size: 12px;
  opacity: 0.8;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.affiliate-card-brand {
  font-size: 11px;
  opacity: 0.6;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.affiliate-card-chev {
  font-size: 18px;
  opacity: 0.7;
  flex: 0 0 auto;
}
.affiliate-card-disclosure {
  padding: 6px 12px 8px;
  font-size: 11px;
  opacity: 0.7;
  line-height: 1.35;
  border-top: 1px dashed rgba(79, 152, 163, 0.25);
}
.affiliate-card-compact {
  margin-top: 10px;
}
.affiliate-card-compact .affiliate-card-link {
  padding: 8px 10px;
}
.affiliate-card-compact .affiliate-card-disclosure {
  padding: 4px 10px 6px;
  font-size: 10px;
}

/* Affiliate card slot living inside the right-rail .ad-shell. The shell's
   .ad-placeholder is absolutely positioned and only covers the AdSense
   <ins> area, so the affiliate card flows below it naturally. We push it
   slightly down so it isn't hugged against the AdSense unit. */
#right-ad-affiliate-slot:empty,
#embed-affiliate-slot:empty {
  display: none;
}
#right-ad-affiliate-slot .affiliate-card,
#embed-affiliate-slot .affiliate-card {
  margin-top: 6px;
}

/* Tooltip affiliate link. The tooltip itself stays pointer-events:none for
   the body text, but the affiliate link inside re-enables interactions so a
   user can click through. The link is intentionally small and uses an
   amber accent so the sponsored nature is obvious at a glance. */
.tooltip-affiliate {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: 8px;
  padding: 2px 6px;
  border-radius: 999px;
  background: rgba(79, 152, 163, 0.25);
  border: 1px solid rgba(79, 152, 163, 0.45);
  color: #ffd494;
  font-size: 11px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.tooltip-affiliate:hover,
.tooltip-affiliate:focus-visible {
  background: rgba(79, 152, 163, 0.25);
  text-decoration: none;
  outline: none;
}
.tooltip-affiliate-icon { font-size: 12px; }
.tooltip-affiliate-disc {
  font-size: 9px;
  opacity: 0.6;
  display: block;
  white-space: nowrap;
  margin-top: 2px;
  pointer-events: none;
}

/* Stack of affiliate cards (one per configured provider) — used in the
   right-rail, embed, More sheet, and search insights placements. */
.affiliate-stack {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.affiliate-stack .affiliate-card { margin-top: 0; }

/* Stack of tooltip-sized affiliate links. Providers stack vertically BELOW
   the original tooltip text — each link keeps its own pill shape, and a
   single small disclosure under the first pill applies to all providers
   so we don't repeat the same line per provider. */
.tooltip-affiliate-stack {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  pointer-events: auto;
}
.tooltip-affiliate-stack .tooltip-affiliate {
  margin-left: 0;
}

/* --- City popup: years-visited chip row ----------------------------------
   Sits between the city/airport headline and the affiliate pills (when
   present). Chips are real <button>s so they are keyboard-focusable and
   announced by screen readers; the parent .tooltip switches to
   pointer-events:auto when this row is rendered.                       */
.tooltip-years {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  width: 100%;
  pointer-events: auto;
}
.tooltip-years-label {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.tooltip-years-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-width: 100%;
}
.tooltip-year-chip {
  font: inherit;
  font-size: var(--text-xs);
  line-height: 1;
  padding: 4px 8px;
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,0.18);
  background: rgba(255,255,255,0.06);
  color: #fff;
  cursor: pointer;
  min-height: 24px;
  min-width: 40px;
}
.tooltip-year-chip:hover,
.tooltip-year-chip:focus-visible {
  background: rgba(103, 232, 249, 0.18);
  border-color: rgba(103, 232, 249, 0.55);
  outline: none;
}
.tooltip-year-more {
  font: inherit;
  font-size: var(--text-xs);
  line-height: 1;
  padding: 4px 8px;
  border-radius: 999px;
  border: 1px dashed rgba(255,255,255,0.28);
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  min-height: 24px;
}
.tooltip-year-more:hover,
.tooltip-year-more:focus-visible {
  color: #fff;
  border-color: rgba(255,255,255,0.5);
  outline: none;
}

/* Memory-label chip row — same layout as the years row, distinct accent so
   it reads as a different action (opens the memory's itinerary view). */
.tooltip-memories {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  width: 100%;
  pointer-events: auto;
}
.tooltip-memory-chip {
  font: inherit;
  font-size: var(--text-xs);
  line-height: 1;
  padding: 4px 8px;
  border-radius: 999px;
  border: 1px solid rgba(244, 114, 182, 0.45);
  background: rgba(244, 114, 182, 0.12);
  color: #fff;
  cursor: pointer;
  min-height: 24px;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tooltip-memory-chip:hover,
.tooltip-memory-chip:focus-visible {
  background: rgba(244, 114, 182, 0.28);
  border-color: rgba(244, 114, 182, 0.8);
  outline: none;
}

/* --- City / Year detail modal --------------------------------------------
   Reuses the existing .ga-overlay / .ga-modal chrome so global ESC and
   outside-click handlers in globe-admin.js apply without any rewiring. */
.cy-modal-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.cy-visit {
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 10px;
  padding: 12px 14px;
  background: rgba(255,255,255,0.03);
}
.cy-visit + .cy-visit { margin-top: 0; }
.cy-visit-title {
  font-weight: 600;
  font-size: var(--text-sm);
  margin: 0 0 4px 0;
}
.cy-visit-dates {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin: 0 0 8px 0;
}
.cy-visit-section {
  margin: 6px 0;
}
.cy-visit-section h4 {
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
  margin: 0 0 4px 0;
}
.cy-visit-section ul {
  margin: 0;
  padding-left: 18px;
  font-size: var(--text-sm);
}
.cy-tag {
  display: inline-block;
  font-size: var(--text-xs);
  padding: 2px 8px;
  margin: 0 4px 4px 0;
  border-radius: 999px;
  background: rgba(103, 232, 249, 0.12);
  color: #67e8f9;
  border: 1px solid rgba(103, 232, 249, 0.3);
}
.cy-meta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin: 0 0 6px 0;
}
.cy-meta-row strong {
  color: #fff;
  font-weight: 600;
  margin-right: 4px;
}
.cy-empty {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* ============================================================================
 * Early Access banners + upgrade modal (PR: early-access-approval-quota)
 * ----------------------------------------------------------------------------
 * .ea-banner-pending  -> shown to signed-in users with approval_status='pending'
 * .ea-banner-empty    -> approved user with 0 trips and 0 flights
 * .ea-banner-quota    -> approved user who has hit FREE_ITEM_QUOTA (50)
 * Banners share a stacked layout. They reuse the dark glass aesthetic from
 * .globe-signin-cta so they sit naturally next to it.
 * ========================================================================== */
.ea-banner {
  position: relative;
  order: 3;
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 14px;
  background: rgba(10, 14, 24, 0.82);
  border: 1px solid var(--color-border);
  border-radius: 12px;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
  color: rgba(255, 255, 255, 0.92);
  font-size: 13px;
  line-height: 1.4;
  margin-top: 10px;
}
.ea-banner[hidden] { display: none !important; }
.ea-banner-icon {
  flex: 0 0 auto;
  font-size: 22px;
  line-height: 1;
  filter: drop-shadow(0 0 6px rgba(92, 213, 255, 0.35));
}
.ea-banner-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ea-banner-body strong { color: #fff; font-weight: 600; }
.ea-banner-body span { color: rgba(255, 255, 255, 0.72); font-size: 12px; }
.ea-banner-body a { color: #5cd5ff; text-decoration: underline; text-underline-offset: 2px; }
.ea-banner-pending { border-color: rgba(180, 140, 255, 0.35); }
.ea-banner-empty   { border-color: rgba(92, 213, 255, 0.35); }
.ea-banner-quota   { border-color: rgba(255, 108, 200, 0.42); }
body.embed-mode .ea-banner { display: none !important; }

/* Upgrade modal (paywall placeholder until a real payment provider is wired). */
.ea-upgrade-overlay {
  position: fixed; inset: 0;
  background: rgba(2, 4, 10, 0.74);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  z-index: 1100;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
}
.ea-upgrade-overlay[hidden] { display: none !important; }
.ea-upgrade-card {
  width: 100%; max-width: 480px;
  background: #0a0f1d;
  border: 1px solid #1a2547;
  border-radius: 18px;
  padding: 28px;
  color: #e8ecf5;
  box-shadow: 0 30px 80px rgba(0,0,0,0.6);
}
.ea-upgrade-eyebrow {
  font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase;
  color: #ff6cc8; font-weight: 700;
}
.ea-upgrade-title {
  margin: 8px 0 4px 0; font-size: 22px; line-height: 1.2; color: #fff; font-weight: 800;
}
.ea-upgrade-lead { font-size: 14px; color: #cdd5e8; line-height: 1.6; margin: 0 0 18px 0; }
.ea-upgrade-plans { display: flex; gap: 12px; margin-bottom: 18px; }
.ea-upgrade-plan {
  flex: 1; padding: 14px; background: #0e1530; border: 1px solid #1a2547;
  border-radius: 12px; text-align: left;
}
.ea-upgrade-plan-name { font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; color: #8b97b8; }
.ea-upgrade-plan-price { margin-top: 6px; font-size: 22px; color: #fff; font-weight: 800; }
.ea-upgrade-plan-period { font-size: 12px; color: #6b7798; }
.ea-upgrade-actions { display: flex; gap: 10px; justify-content: flex-end; }
.ea-upgrade-actions button {
  padding: 10px 16px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.18);
  background: rgba(255,255,255,0.06); color: #fff; font-weight: 600; cursor: pointer;
}
.ea-upgrade-actions .primary {
  background: linear-gradient(90deg,#5cd5ff,#b48cff,#ff6cc8); color: #05070d; border: none;
}
.ea-upgrade-note { margin-top: 12px; font-size: 12px; color: #6b7798; line-height: 1.5; }

/* Early Access admin panel listings (admin.html). Scoped to the panel so
   it doesn't bleed into the rest of the admin app.
   The panel itself needs an internal scroll envelope so long lists of
   pending/approved profiles + invitations don't push the page off-screen
   on small viewports (the rest of the admin app uses the same approach via
   .table-container and .settings-wrap). */
#admin-view #panel-earlyaccess {
  max-height: calc(100dvh - 320px);
  overflow-y: auto;
  overflow-x: hidden;
  padding: 0 24px 24px;
  scrollbar-width: thin;
  scrollbar-color: var(--color-border-hover) transparent;
  -webkit-overflow-scrolling: touch;
}
#admin-view #panel-earlyaccess::-webkit-scrollbar { width: 8px; }
#admin-view #panel-earlyaccess::-webkit-scrollbar-track { background: transparent; }
#admin-view #panel-earlyaccess::-webkit-scrollbar-thumb {
  background: var(--color-border-hover);
  border-radius: 4px;
}
@media (max-height: 800px) {
  #admin-view #panel-earlyaccess { max-height: calc(100dvh - 260px); }
}
@media (max-width: 720px) {
  #admin-view #panel-earlyaccess {
    padding: 0 14px 20px;
    max-height: calc(100dvh - 240px);
  }
}
#panel-earlyaccess .ea-admin-list { display: flex; flex-direction: column; gap: 8px; }
#panel-earlyaccess .ea-row {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px;
  background: rgba(255,255,255,0.02);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 10px;
  flex-wrap: wrap;
}
#panel-earlyaccess .ea-row .ea-row-meta { flex: 1; min-width: 220px; font-size: 13px; }
#panel-earlyaccess .ea-row .ea-row-meta strong { color: #fff; }
#panel-earlyaccess .ea-row .ea-row-meta .muted { color: rgba(255,255,255,0.55); font-size: 12px; display: block; margin-top: 2px; }
#panel-earlyaccess .ea-row .ea-row-actions { display: flex; gap: 6px; flex-wrap: wrap; }
#panel-earlyaccess .ea-row .ea-pill {
  display:inline-block;padding:2px 8px;border-radius:999px;
  font-size:11px;letter-spacing:0.06em;text-transform:uppercase;font-weight:600;
}
#panel-earlyaccess .ea-pill.pending  { background:rgba(180,140,255,0.18); color:#d2b6ff; }
#panel-earlyaccess .ea-pill.approved { background:rgba(92,213,255,0.18); color:#9be7ff; }
#panel-earlyaccess .ea-pill.rejected { background:rgba(255,108,200,0.18); color:#ffb5e0; }
#panel-earlyaccess .ea-pill.free     { background:rgba(255,255,255,0.10); color:#fff; }

/* Admin Users tab — scroll envelope + card list. Mirrors the Early Access
   panel so behavior is consistent across desktop and mobile. */
#admin-view #panel-users {
  max-height: calc(100dvh - 320px);
  overflow-y: auto;
  overflow-x: hidden;
  padding: 0 24px 24px;
  scrollbar-width: thin;
  scrollbar-color: var(--color-border-hover) transparent;
  -webkit-overflow-scrolling: touch;
}
#admin-view #panel-users::-webkit-scrollbar { width: 8px; }
#admin-view #panel-users::-webkit-scrollbar-track { background: transparent; }
#admin-view #panel-users::-webkit-scrollbar-thumb {
  background: var(--color-border-hover);
  border-radius: 4px;
}
@media (max-height: 800px) {
  #admin-view #panel-users { max-height: calc(100dvh - 260px); }
}
@media (max-width: 720px) {
  #admin-view #panel-users {
    padding: 0 14px 20px;
    max-height: calc(100dvh - 240px);
  }
}
/* Dense Excel-style users table — admin/superadmin Users tab.
   The previous card grid was easy on the eye but burned vertical space;
   admin asked for tight rows so a long allowlist scans in one glance.
   Sticky header keeps column meaning visible while scrolling.
   On narrow viewports the table itself scrolls horizontally so every
   action stays reachable instead of being hidden by a card-fallback. */
#panel-users .users-admin-list { display: block; }
#panel-users .users-table-scroll {
  /* Both axes scroll: horizontal for narrow viewports where the dense
     column count overflows, vertical so a long allowlist doesn't push
     the admin footer off the page. max-height is calc()d off the viewport
     minus the rest of the admin chrome (tabs + page padding + summary +
     refresh toolbar) so the table fills the visible area without pulling
     the page itself into a second scroll. min-height keeps the empty/
     loading state from collapsing to a hairline. */
  overflow: auto;
  max-height: calc(100vh - 280px);
  min-height: 240px;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 8px;
  background: rgba(255,255,255,0.015);
  max-width: 100%;
  -webkit-overflow-scrolling: touch;
  /* When the table content is taller than max-height, overscroll-contain
     keeps the page itself from rubber-banding behind the scroll. */
  overscroll-behavior: contain;
}
@media (max-width: 720px) {
  #panel-users .users-table-scroll {
    /* Phones have a smaller chrome budget — give the table more room. */
    max-height: calc(100vh - 220px);
  }
}
#panel-users table.users-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  line-height: 1.25;
  color: var(--color-text);
  table-layout: auto;
}
#panel-users table.users-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: #15182a;
  color: rgba(255,255,255,0.78);
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 6px 8px;
  text-align: left;
  border-bottom: 1px solid rgba(255,255,255,0.12);
  white-space: nowrap;
}
#panel-users table.users-table tbody td {
  padding: 4px 8px;
  border-bottom: 1px solid rgba(255,255,255,0.05);
  vertical-align: middle;
  white-space: nowrap;
}
#panel-users table.users-table tbody tr:nth-child(even) td {
  background: rgba(255,255,255,0.015);
}
#panel-users table.users-table tbody tr:hover td {
  background: rgba(80,170,255,0.06);
}
#panel-users .ucol-name     { min-width: 150px; }
#panel-users .ucol-email    { min-width: 200px; }
#panel-users .ucol-joined   { width: 1%; }
#panel-users .ucol-num      { width: 1%; text-align: right; }
#panel-users th.ucol-num    { text-align: right; }
#panel-users .ucol-actions  { width: 1%; }
#panel-users .ucell-name {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  max-width: 280px;
}
#panel-users .ucell-name-text {
  font-weight: 600; color: #fff;
  overflow: hidden; text-overflow: ellipsis;
  max-width: 180px; white-space: nowrap;
}
#panel-users .ucell-pills { display: inline-flex; gap: 3px; flex-wrap: wrap; }
#panel-users .ucell-email {
  color: rgba(255,255,255,0.75);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  overflow: hidden; text-overflow: ellipsis;
  max-width: 280px;
}
#panel-users .ucell-email .ucell-sub {
  color: rgba(255,255,255,0.45);
  margin-left: 4px;
}
#panel-users .ucell-num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
  color: rgba(255,255,255,0.85);
}
#panel-users .ucell-actions {
  display: inline-flex; gap: 3px; align-items: center; flex-wrap: nowrap;
}
#panel-users .ucell-limit-input {
  width: 56px; padding: 2px 5px; border-radius: 4px;
  border: 1px solid var(--color-border);
  background: var(--color-surface-2); color: var(--color-text);
  font-size: 11px; line-height: 1.2;
  font-variant-numeric: tabular-nums;
}
#panel-users .btn.btn-xs {
  padding: 2px 6px;
  font-size: 11px;
  line-height: 1.2;
  border-radius: 4px;
  min-width: 22px;
}
#panel-users .ea-pill {
  display:inline-block; padding:1px 5px; border-radius: 3px;
  font-size:9px; letter-spacing:0.04em; text-transform:uppercase; font-weight:700;
  line-height: 1.3;
}
#panel-users .ea-pill.pending  { background:rgba(180,140,255,0.18); color:#d2b6ff; }
#panel-users .ea-pill.approved { background:rgba(92,213,255,0.18); color:#9be7ff; }
#panel-users .ea-pill.rejected { background:rgba(255,108,200,0.18); color:#ffb5e0; }
#panel-users .ea-pill.free     { background:rgba(255,255,255,0.10); color:#fff; }
#panel-users .ea-pill.admin    { background:rgba(255,215,64,0.18); color:#ffe28a; }
@media (max-width: 720px) {
  #panel-users table.users-table { font-size: 11px; }
  #panel-users .ucell-name-text { max-width: 120px; }
  #panel-users .ucell-email { max-width: 180px; }
  #panel-users .ucell-limit-input { width: 48px; }
}

/* Per-row Actions dropdown in the Users table. Replaces the row of
   four tiny icon buttons (✓ ✗ ⊙ ∞) the user couldn't easily scan or
   tap. Built on <details>/<summary> so it works without JS for open/
   close state and is keyboard-accessible out of the box. The menu
   floats absolutely above the table so it doesn't push row height
   around; the parent <td> is position:relative for that anchoring. */
#panel-users td.ucol-actions { position: relative; }
#panel-users .user-actions-menu { position: relative; display: inline-block; }
#panel-users .user-actions-menu > summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
#panel-users .user-actions-menu > summary::-webkit-details-marker { display: none; }
#panel-users .user-actions-menu .user-actions-list {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  z-index: 5;
  min-width: 180px;
  background: #15182a;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 6px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.5);
  padding: 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
#panel-users .user-actions-menu .user-action-item {
  background: transparent;
  border: 0;
  color: rgba(255,255,255,0.9);
  text-align: left;
  padding: 7px 10px;
  font-size: 12px;
  border-radius: 4px;
  cursor: pointer;
}
#panel-users .user-actions-menu .user-action-item:hover,
#panel-users .user-actions-menu .user-action-item:focus-visible {
  background: rgba(92,213,255,0.14);
  color: #fff;
  outline: none;
}
#panel-users .user-actions-menu .user-action-approve { color: #9be7ff; }
#panel-users .user-actions-menu .user-action-reject  { color: #ffb5e0; }
@media (max-width: 720px) {
  /* Anchor the dropdown to the right edge of the table on narrow
     screens so it doesn't overflow the scrollable wrapper. */
  #panel-users .user-actions-menu .user-actions-list {
    min-width: 160px;
  }
}
